Skip to content

DocsUse it from

2 min read · updated

Frameworks

LangChain, the Vercel AI SDK, n8n, TanStack AI and Pipecat, each checked against a running server: what to set, and what to know.

Five third-party integrations of TypeSafe’s wire format were run against a decisio server (Gemma 4 31B, decisio 0.9.0, from a Mac through an SSH tunnel, 2026-10-08); every check passed. They are plumbing checks: the client sends a request, the server answers, the client parses it. They say nothing about accuracy, and each is written and maintained by someone else.

Figure · Frameworks

Five integrations, each checked against a running server.

They are plumbing checks of the wire format, not of accuracy.

Source: the README's "Works with" table, and each integration's README in decisio-examples.

Every integration needs the same three things, in its own spelling:

base URL http://127.0.0.1:8000 (the Vercel AI SDK's provider wants http://127.0.0.1:8000/v1)
API key any string (every integration requires one; the server ignores it)
model decisio-gemma-4-31b-it-letters (the name GET /v1/models lists; the server ignores it)

A shape to fill in, not a command to run.

WordMeaning
integrationa package written and maintained by someone else that speaks System One
plumbing checka run that sends requests and parses answers, to see that the pieces fit; it does not measure accuracy
abstentiona server run with --abstain-option can answer the best of the other options when its “can’t tell” option is the likeliest; most of the integrations drop the two fields that say so (n8n with Simplify Output off, and Pipecat’s client, pass them through), and that was emulated, not seen on a real abstaining server

Python 3.12; langchain-typesafe, 0.0.1a3: 42 of 42 checks passed against a running server (the check and its record). It uses TypeSafeClassifier.

Base URL
no /v1: the class appends it; the base_url argument or TYPESAFE_BASE_URL
API key
required: a dummy one works (ValueError without)
If the server answers 422
TypeSafeUnprocessableEntityError, status 422, the server's detail on its body; not retried
Timeout and retries
30 seconds, no retries
from langchain_typesafe import TypeSafeClassifier
classifier = TypeSafeClassifier(base_url="http://127.0.0.1:8000", api_key="unused")

A shape to fill in, not a command to run.

Nothing broke. The class marks itself beta and logs a warning when built. It checks a question before sending (a choice with no options never reaches the server), and it drops the abstention fields, so a caller cannot tell that a server abstained.

Node 22; ai + @ai-sdk/typesafe-ai, ai 7.0.131, @ai-sdk/typesafe-ai 3.0.15: 42 of 42 checks passed against a running server (the check and its record). It uses createTypeSafeAi(...).decisionModel(...) with experimental_decide.

Base URL
must end in /v1 (http://host:port/v1); TYPESAFE_AI_BASE_URL is read when the provider is created
API key
required: apiKey or TYPESAFE_AI_API_KEY (LoadAPIKeyError without)
If the server answers 422
APICallError, statusCode 422, not retryable; the message is the server's detail as JSON
Timeout and retries
none set (Node's fetch defaults apply); maxRetries 2 for retryable failures
import { createTypeSafeAi } from "@ai-sdk/typesafe-ai";
const typesafe = createTypeSafeAi({ baseURL: "http://127.0.0.1:8000/v1", apiKey: "unused" });
const model = typesafe.decisionModel("decisio-gemma-4-31b-it-letters");

A shape to fill in, not a command to run.

The one real difference from the other integrations is that baseURL needs /v1; without it the call fails loudly with a 404. The decision function throws if the chosen option is not the most probable one, which a server run with --abstain-option can produce; that case was emulated, not seen on a real abstaining server.

Node 22 (n8n 2.42 and later need Node 24 and were not run); n8n-nodes-jev, n8n 2.35.7, n8n-nodes-jev 0.2.3: 44 of 44 checks passed against a running server (the check and its record). It uses the community node's credential and its decision nodes.

Base URL
the credential's Base URL, with no /v1; its default is the hosted address, so a self-hosted server changes it
API key
required in the credential: any string
If the server answers 422
a NodeApiError with httpCode 422 and the server's detail; with Continue on Error the detail is only in the item's error object
Timeout and retries
none unless added under Options; retries only on 429 and 529

Nothing broke. Simplify Output is on by default and returns one flat value per question, so probabilities, legend and usage are dropped; turn it off to get the server's response unchanged.

Node 22; @tanstack/ai + @tanstack/ai-typesafe, @tanstack/ai 0.65.1, @tanstack/ai-typesafe 0.1.8: 39 of 39 checks passed against a running server (the check and its record). It uses createTypesafeDecider(...) with decide().

Base URL
no /v1: the adapter posts to {baseURL}/v1/systemone; there is no environment variable for it
API key
required: createTypesafeDecider takes it directly, typesafeDecider reads TYPESAFE_API_KEY
If the server answers 422
an Error whose message starts "TypeSafe evaluate request failed: 422 Unprocessable Entity" and carries the body; not retried
Timeout and retries
none set (Node's fetch defaults apply); no retries
import { createTypesafeDecider } from "@tanstack/ai-typesafe";
const adapter = createTypesafeDecider("decisio-gemma-4-31b-it-letters", "unused", { baseURL: "http://127.0.0.1:8000" });

A shape to fill in, not a command to run.

Nothing broke. A base URL ending in /v1 posts to /v1/v1/systemone and fails with a 404, the opposite of the Vercel AI SDK's provider. The adapter drops the abstention fields and the x-decisio response headers.

Python 3.12; pipecat-ai[jev], pipecat-ai 1.12.0: 39 of 39 checks passed against a running server (the check and its record). It uses JevClient and JevClassifier in pipecat.classifiers.jev.

Base URL
no /v1; always pass base_url, because the default is the hosted address
API key
required: JevClient(api_key="") raises ValueError
If the server answers 422
ClassifierError "HTTP 422"; the server's detail is discarded
Timeout and retries
10 seconds; 3 retries, on 429 and 529 only
from pipecat.classifiers.jev import JevClient
client = JevClient(api_key="unused", base_url="http://127.0.0.1:8000")

A shape to fill in, not a command to run.

Nothing broke. A 422 loses its reason (only "HTTP 422" is raised), the default timeout is short at 10 seconds, and the classifier's results leave out the abstention fields.