Skip to content

04 — Frameworks

Markdown

LangChain

LangChain's ChatOpenAI takes a base URL, so Inferit works as its model in Python and in JavaScript. Chains, agents and tools built on it run unchanged.

1. Get an API key

Create a key in the Buy workspace (Quickstart, steps 1 to 6) and export it. The key spends your prepaid balance: test ITC on the testnet, a test token with no monetary value.

bash
export INFERIT_API_KEY='ik_...'

The base URL is https://api-production-c74b9.up.railway.app/v1. Any OpenAI-compatible client works with it (chat completions, streaming included).

2. Python

langchain-openai's ChatOpenAI with Inferit's base URL. model_name in the reply's metadata is the model that answered.

chat.py
# pip install langchain-openai
import os
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    base_url="https://api-production-c74b9.up.railway.app/v1",
    api_key=os.environ["INFERIT_API_KEY"],
    model="qwen/qwen-2.5-14b-instruct",  # "auto": Inferit picks the model
)

reply = llm.invoke("Say hello in five words.")
print(reply.content)
print(reply.response_metadata.get("model_name"))  # the model that answered

3. JavaScript

@langchain/openai takes the base URL under configuration, which it passes to the OpenAI client.

chat.mjs
// npm i @langchain/openai @langchain/core
import { ChatOpenAI } from '@langchain/openai';

const llm = new ChatOpenAI({
  model: 'qwen/qwen-2.5-14b-instruct', // "auto": Inferit picks the model
  apiKey: process.env.INFERIT_API_KEY,
  configuration: { baseURL: 'https://api-production-c74b9.up.railway.app/v1' },
});

const reply = await llm.invoke('Say hello in five words.');
console.log(reply.content);
console.log(reply.response_metadata?.model_name); // the model that answered

Model auto, or a named model

With "auto" as the model, Inferit picks the model: the best value on a published quality ranking for this request, at that model's cheapest healthy seller (Automatic routing). The response names the model that answered, in its model field and the x-inferit-model header.

Auto needs a ranked model with a live seller. GET /v1/routing lists the models it can choose from now in candidates_now; while that list is empty, a request with auto answers 503 no_eligible_model. Then name a catalog model with a live seller instead, such as qwen/qwen-2.5-14b-instruct, and Inferit picks the seller. The samples on this page send auto while it has a candidate, and that model otherwise.

Pay per request with x402 (JavaScript)

With no API key, LangChain JS can pay for each request with x402: give the OpenAI client a fetch that answers the API's 402 with the standard x402 v2 client. The OpenAI client always sends an Authorization header, so the wrapper removes it first; with it, the API would read the request as an API-key call. Each payment is at least 0.01 ITC, is credited to the paying address's balance, and each request is charged its exact cost (agents.md, API reference). The client caps one payment at 1 ITC; that is a setting in your client, not a rule of the contract.

chat-x402.mts
// npm i @langchain/openai @langchain/core @x402/fetch @x402/evm viem
import { ChatOpenAI } from '@langchain/openai';
import { wrapFetchWithPayment, x402Client } from '@x402/fetch';
import { ExactEvmScheme } from '@x402/evm/exact/client';
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';

const account = privateKeyToAccount((process.env.AGENT_KEY ?? generatePrivateKey()) as `0x${string}`);

// Testnet only: Inferit's faucet gives this address 1,000 free test ITC (the claim costs nothing).
await fetch('https://api-production-c74b9.up.railway.app/v1/faucet', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ address: account.address }),
});

// The standard x402 v2 client pays only in tokens it was told about: allow ITC,
// and cap one payment at 1 ITC (1000000 base units, 6 decimals).
const ITC = { network: 'eip155:1874', asset: '0x2E672dFE33EA977FD064E01aDe7d8c73B3Be7fBB' } as const;
const client = new x402Client()
  .register('eip155:*', new ExactEvmScheme(account))
  .setSpendControls({ allowedAssets: [{ ...ITC, maxAmountPerPayment: '1000000' }] });
const paidFetch = wrapFetchWithPayment(fetch, client);

// The OpenAI client always sends an Authorization header. Drop it, so the API
// answers with a 402 quote, which paidFetch pays and retries with PAYMENT-SIGNATURE.
const x402Fetch: typeof fetch = (input, init = {}) => {
  const headers = new Headers(init.headers);
  headers.delete('authorization');
  return paidFetch(input, { ...init, headers });
};

const llm = new ChatOpenAI({
  model: 'qwen/qwen-2.5-14b-instruct',
  apiKey: 'x402', // never sent: x402Fetch removes it
  maxTokens: 64, // the quote is the worst case for this body: a small limit keeps it small
  configuration: { baseURL: 'https://api-production-c74b9.up.railway.app/v1', fetch: x402Fetch },
});
console.log((await llm.invoke('Say hello in five words.')).content);

The x402 sample is JavaScript only. From Python, the exchange can be signed by hand, as in the Python sample on Agents; or use an API key.

Checked

The Python and JavaScript samples (with an API key) ran against the hosted testnet API on 2026-10-13, with a named model (auto had no candidate that day).

The x402 variant was run up to the API's 402 quote: the request reached the API with no Authorization header and the x402 client read the quote. The paid retry is the same exchange as on Agents.