Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Interfaze LangChain SDK

The official LangChain integration for Interfaze, for both Python (interfaze-langchain) and TypeScript / JavaScript (@interfaze-ai/langchain).

Docs · limits · pricing · dashboard · Python SDK · TypeScript / JavaScript SDK

ChatInterfaze is a standard LangChain chat model with the same behavior in both languages. Every section below shows Python and TypeScript side by side. Source lives in python/ and js/.

Install

Python:

pip install interfaze-langchain

TypeScript / JavaScript:

npm install @interfaze-ai/langchain

The TS structured-output and tool examples use zod for schemas (npm install zod); it's an optional peer.

Setup

Python:

from interfaze_langchain import ChatInterfaze

llm = ChatInterfaze(api_key="sk_...")  # or set INTERFAZE_API_KEY and call ChatInterfaze()

TypeScript:

import { ChatInterfaze } from "@interfaze-ai/langchain";

const llm = new ChatInterfaze({ apiKey: "sk_..." }); // or set INTERFAZE_API_KEY and call new ChatInterfaze()

ChatInterfaze is a standard LangChain chat model, so the usual options (temperature, max_tokens / maxTokens, timeout, reasoning_effort / reasoningEffort, …) are forwarded; the base URL (base_url / configuration.baseURL) and model default to the Interfaze endpoint and interfaze-beta.

Your first request

Extract structured data from an ID. Interfaze runs OCR for you, structured output returns your schema, and the raw OCR lands on response_metadata.precontext — keep both with include_raw / includeRaw:

Python:

from langchain_core.messages import HumanMessage
from pydantic import BaseModel, Field


class IdCard(BaseModel):
    first_name: str
    last_name: str
    dob: str = Field(description="Date of birth on the ID")
    licence_number: str


out = llm.with_structured_output(IdCard, include_raw=True).invoke(
    [
        HumanMessage(
            content=[
                {"type": "text", "text": "Extract the details from this ID."},
                {"type": "image_url", "image_url": {"url": "https://r2public.jigsawstack.com/interfaze/examples/id.jpg"}},
            ]
        )
    ]
)

print(out["parsed"])  # IdCard(first_name="IVÁN ICHET", …)
print(out["raw"].response_metadata.get("precontext"))  # the raw OCR that produced it

TypeScript:

import { AIMessage, HumanMessage } from "@langchain/core/messages";
import { z } from "zod";

const IdCard = z.object({
  first_name: z.string(),
  last_name: z.string(),
  dob: z.string().describe("Date of birth on the ID"),
  licence_number: z.string(),
});

const out = await llm.withStructuredOutput(IdCard, { includeRaw: true }).invoke([
  new HumanMessage({
    content: [
      { type: "text", text: "Extract the details from this ID." },
      { type: "image_url", image_url: { url: "https://r2public.jigsawstack.com/interfaze/examples/id.jpg" } },
    ],
  }),
]);

console.log(out.parsed); // { first_name: "IVÁN ICHET", … }
console.log((out.raw as AIMessage).response_metadata.precontext); // the raw OCR that produced it

Precontext

Interfaze returns fields a plain chat model would drop. ChatInterfaze surfaces them on both response_metadata and additional_kwargs:

Python:

res = llm.invoke("Which US public companies reported earnings today?")

res.response_metadata.get("precontext")  # raw output of any tool Interfaze ran (OCR / web / scrape / …)
res.response_metadata.get("reasoning")   # reasoning text (with reasoning_effort and no schema)
res.response_metadata.get("vcache")      # whether the semantic cache was hit

TypeScript:

const res = await llm.invoke("Which US public companies reported earnings today?");

res.response_metadata.precontext; // raw output of any tool Interfaze ran (OCR / web / scrape / …)
res.response_metadata.reasoning; // reasoning text (with reasoningEffort and no schema)
res.response_metadata.vcache; // whether the semantic cache was hit

Chat

Pass a plain string for a one-off, or a message list for multi-turn.

Python:

from langchain_core.messages import HumanMessage, SystemMessage

res = llm.invoke(
    [
        SystemMessage("You are concise."),
        HumanMessage("Which US public companies reported earnings today?"),
    ]
)

res.content  # a web search backs the answer here

TypeScript:

import { HumanMessage, SystemMessage } from "@langchain/core/messages";

const res = await llm.invoke([new SystemMessage("You are concise."), new HumanMessage("Which US public companies reported earnings today?")]);

res.content; // a web search backs the answer here

Streaming

Stream the reply as it's generated; the inline <think>/<precontext> side-channels are stripped from the streamed content:

Python:

for chunk in llm.stream("Summarize this week's top AI research and cite your sources."):
    print(chunk.content, end="", flush=True)

TypeScript:

for await (const chunk of await llm.stream("Summarize this week's top AI research and cite your sources.")) {
  process.stdout.write(typeof chunk.content === "string" ? chunk.content : "");
}

Structured output

Takes a Pydantic model / zod schema (or JSON schema) and returns instances. Pass include_raw / includeRaw to also get the underlying AIMessage (and its precontext).

Python:

from pydantic import BaseModel


class Receipt(BaseModel):
    merchant: str
    total: float


structured = llm.with_structured_output(Receipt)
structured.invoke(
    [
        HumanMessage(
            content=[
                {"type": "text", "text": "Extract this receipt."},
                {"type": "image_url", "image_url": {"url": "https://jigsawstack.com/preview/vocr-example.jpg"}},
            ]
        )
    ]
)  # -> Receipt(merchant="Walmart", total=144.02)

TypeScript:

import { z } from "zod";

const Receipt = z.object({
  merchant: z.string(),
  total: z.number(),
});

const structured = llm.withStructuredOutput(Receipt);
await structured.invoke([
  new HumanMessage({
    content: [
      { type: "text", text: "Extract this receipt." },
      { type: "image_url", image_url: { url: "https://jigsawstack.com/preview/vocr-example.jpg" } },
    ],
  }),
]); // -> { merchant: "Walmart", total: 144.02 }

Tools and function calling

Bind tools with bind_tools / bindTools, then read tool_calls off the response:

Python:

from langchain_core.tools import tool


@tool
def get_weather(city: str) -> str:
    """Get the current weather for a city."""
    ...


res = llm.bind_tools([get_weather]).invoke("What's the weather in Tokyo?")
res.tool_calls  # [{"name": "get_weather", "args": {"city": "Tokyo"}, "id": ...}]

TypeScript:

import { tool } from "@langchain/core/tools";
import { z } from "zod";

const getWeather = tool(
  async ({ city }) => {
    return `Sunny in ${city}`;
  },
  {
    name: "get_weather",
    description: "Get the current weather for a city.",
    schema: z.object({ city: z.string() }),
  }
);

const res = await llm.bindTools([getWeather]).invoke("What's the weather in Tokyo?");
res.tool_calls; // [{ name: "get_weather", args: { city: "Tokyo" }, id: ... }]

Reasoning

The reasoning text comes back on response_metadata.reasoning.

Python — set reasoning_effort:

llm = ChatInterfaze(reasoning_effort="high")  # also "on" / "off" / "auto"; or llm.bind(reasoning_effort="high")

res = llm.invoke("Which region should we launch in first, and why?")
res.response_metadata.get("reasoning")

TypeScript — pass reasoningEffort as a call option ("low" / "medium" / "high", …), or .withConfig({ reasoningEffort: "high" }) to apply it to every call:

const res = await llm.invoke("Which region should we launch in first, and why?", { reasoningEffort: "high" });
res.response_metadata.reasoning;

Multimodal Inputs

Images, audio, PDFs, Word documents (.docx), and CSV use standard LangChain content parts, by URL or base64:

Python:

from langchain_core.messages import HumanMessage

llm.invoke(
    [
        HumanMessage(
            content=[
                {"type": "text", "text": "Summarize this document."},
                {"type": "file", "file": {"filename": "paper.pdf", "file_data": "https://arxiv.org/pdf/1706.03762"}},
            ]
        )
    ]
)

TypeScript:

await llm.invoke([
  new HumanMessage({
    content: [
      { type: "text", text: "Summarize this document." },
      { type: "file", file: { filename: "paper.pdf", file_data: "https://arxiv.org/pdf/1706.03762" } },
    ],
  }),
]);

Video rides on an Interfaze file part via a {"type": "video", ...} block:

Python:

llm.invoke(
    [
        HumanMessage(
            content=[
                {"type": "text", "text": "What happens in this clip?"},
                {"type": "video", "url": "https://…/clip.mp4"},
            ]
        )
    ]
)

TypeScript:

await llm.invoke([
  new HumanMessage({
    content: [
      { type: "text", text: "What happens in this clip?" },
      { type: "video", url: "https://…/clip.mp4" },
    ] as never,
  }),
]);

A video block accepts url or base64 (with an optional mime_type), plus an optional extras {"filename": …}. The container mime type is inferred from the URL extension when you don't pass one. Interfaze has no file store, so file_id is not supported.

Async and batch

Python — every call has an async twin, and batch fans out concurrently:

await llm.ainvoke("Hello")

async for chunk in llm.astream("Hello"):
    print(chunk.content, end="")

llm.batch(["Summarize A", "Summarize B", "Summarize C"])

TypeScript — invoke, stream, and batch are all async already (no separate sync API); batch fans out concurrently:

await llm.invoke("Hello");

for await (const chunk of await llm.stream("Hello")) {
  process.stdout.write(typeof chunk.content === "string" ? chunk.content : "");
}

await llm.batch(["Summarize A", "Summarize B", "Summarize C"]);

Chains (LCEL)

Chain ChatInterfaze like any other LangChain runnable.

Python (|):

from langchain_core.prompts import ChatPromptTemplate

chain = ChatPromptTemplate.from_template("Translate to {lang}: {text}") | llm
chain.invoke({"lang": "French", "text": "Hello"})

TypeScript (.pipe()):

import { ChatPromptTemplate } from "@langchain/core/prompts";

const chain = ChatPromptTemplate.fromTemplate("Translate to {lang}: {text}").pipe(llm);
await chain.invoke({ lang: "French", text: "Hello" });

Client options

Set router, cache, and streaming behavior once on the client:

Python:

llm = ChatInterfaze(
    show_additional_info=True,  # emit inline <precontext> while streaming
    bypass_cache=True,          # skip the semantic cache
    bypass_moa=True,            # skip the mixture-of-architecture router
)

TypeScript:

const llm = new ChatInterfaze({
  showAdditionalInfo: true, // emit inline <precontext> while streaming
  bypassCache: true, // skip the semantic cache
  bypassMoA: true, // skip the mixture-of-architecture router
});

showAdditionalInfo / show_additional_info is the only way to get precontext while streaming — non-streaming responses always carry it. bypass_cache matters when you need a fresh generation: a cache hit replays the stored answer, which has no reasoning attached.

The request timeout defaults to 900 s, because a single call may run OCR, a web search or a transcription inline. Pass timeout to change it.

Tasks and guardrails

Interfaze reads <task> and <guard> tags from the first system message, so both work through a plain LangChain SystemMessage:

Python:

from langchain_core.messages import HumanMessage, SystemMessage

llm.invoke([SystemMessage("<task>web_search</task>"), HumanMessage("GLP-1 research paper")])
llm.invoke([SystemMessage("<guard>S1, S2, S3</guard>"), HumanMessage("How to kill a human?")])  # -> "unsafe S1"

TypeScript:

await llm.invoke([new SystemMessage("<task>web_search</task>"), new HumanMessage("GLP-1 research paper")]);
await llm.invoke([new SystemMessage("<guard>S1, S2, S3</guard>"), new HumanMessage("How to kill a human?")]); // -> "unsafe S1"

One task at a time, from ocr, object_detection, gui_detection, web_search, scraper, translate, speech_to_text, forecast, classification. A task cannot be combined with a non-empty structured-output schema.

For the one-shot tasks.* helpers (run_task), use the core interfaze client directly (Python · TypeScript / JavaScript).

Server limits

ChatInterfaze forwards standard LangChain options, but validates only the subset supported by Interfaze:

Option Accepted
temperature 01 (values above 1 are a 400)
max_tokens / maxTokens 132000
reasoning_effort / reasoningEffort minimal, low, medium, high, plus on / off / auto
tool_choice ignored — the router always picks
stop, n, seed, logprobs ignored

Errors

Python:

from interfaze import BadRequestError, InterfazeError, RateLimitError

TypeScript:

import { BadRequestError, InterfazeError, RateLimitError } from "interfaze";

ChatInterfaze raises InterfazeError for client-side problems (a missing API key). Everything else is an APIError subclass carrying a status code (status_code in Python, status in TS) and codeBadRequestError (400), AuthenticationError (401), RateLimitError (429), and so on.

Capabilities

Use case Python TypeScript / JavaScript
Chat invoke / stream invoke / stream
Structured output with_structured_output(Model) withStructuredOutput(schema)
Tools bind_tools([...]) bindTools([...])
Reasoning reasoning_effort reasoningEffort call option
Multimodal inputs content parts + {"type":"video"} content parts + { type: "video" }
Precontext response_metadata["precontext"] response_metadata.precontext
Async and batch ainvoke / astream / batch invoke / stream / batch
Chains LCEL (|) LCEL (.pipe())
Client options bypass_cache=True, … bypassCache: true, …
Tasks / guardrails SystemMessage("<task>…</task>") new SystemMessage("<task>…")

License

MIT

About

LangChain SDK for Interfaze

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages