Skip to main content
LangChain and LangGraph work against FlexAI’s OpenAI-compatible API today: ChatOpenAI takes a base_url, so you set it to https://api.flex.ai/v1, pass your FlexAI key, name a served model, and every LangChain surface — chat, streaming, tool calling, agents, structured output — runs unchanged. There is no package to install beyond LangChain’s own. The headline for agent builders: every model in the table below completed a two-tool LangGraph agent loop against production as written here, and a model that failed any check does not appear on this page.
New to FlexAI? Create an account, add billing, and create an sk-… key on the dashboard’s API Keys page — the quickstart walks through it. Export the key as FLEXAI_API_KEY for the snippets on this page.

Connect

That is the whole integration. model is an id that GET /v1/models returns right now — the serving subset of the catalog; a model offered for dedicated deployment or not currently serving is absent from that list and fails immediately. Ids are case sensitive. max_tokens=4096 matters as soon as tools are involved; what to plan for explains why. If you would rather not pass the URL and key in code, LangChain reads the OpenAI SDK’s environment variables:
and ChatOpenAI(model="DeepSeek-V4-Flash-0731", max_tokens=4096) connects the same way.

Stream

Streaming works with .stream() and .astream(). Set stream_usage=True so token usage arrives at the end of the stream — that is the stream_options.include_usage flag the streaming guide asks for, and it keeps your own accounting in step with your bill. Usage rides on its own chunk after the last piece of text (LangChain then emits one empty closing chunk), so add the chunks together and read usage_metadata off the total rather than off the last object:
The Streaming column in the table passes only when this snippet streamed text in more than one chunk and usage arrived after the last text chunk.

Call tools

Define tools with @tool, bind them, and feed results back as ToolMessages. The round trip below is the pattern LangGraph automates in the next section.
Two details of the gateway make this loop simpler than on some providers. An assistant turn that is a pure tool call may carry content as null, omitted, or "" — the gateway accepts all three and normalizes the empty string, so whatever your LangChain version serializes is fine. A user message directly after a tool message is the other one, and here the honest answer is narrower: the gateway does bridge that gap, but only for the specific models whose chat template is known to insist on strict role alternation, and that list is short. LangGraph’s own loop does not produce the shape — it sends the tool result and lets the model answer — so most agents never hit it. If you are building a multi-turn chat where the user types again straight after a tool result and you see a 400 about message ordering, insert an assistant turn between them. This page’s probe does not measure that case, so the table says nothing about it. To force a tool call, bind with tool_choice="any" (LangChain’s name for OpenAI’s "required"). Every model in the table honored it in the last run, with one thing worth knowing about how that was measured: the probe binds a single tool, and for a single-tool request the gateway can rewrite a forced call into a named one, which the serving stack enforces more reliably. On gpt-oss-120b that rewrite is what the column is reporting — with two or more tools bound, generic required is not enforced at the engine, and a turn where the model answers in prose instead of calling a tool comes back as a 4xx. If you need a forced call across several bound tools on that model, name the tool you want rather than relying on required. Leave tool_choice at its default when you want the model to decide.

Run a LangGraph agent

create_agent (LangChain 1.x, built on LangGraph) gives you the ReAct loop — model turn, tool calls, tool results, repeat until the model answers. With two tools the model has to chain them:
state["messages"] holds the full trace — the two tool calls, their results and the final answer — so you can log or replay it. The Agent loop column in the table passes only when that trace shows the chain in order: one turn calling get_weather, its result, a later turn calling convert_c_to_f with the temperature the weather tool returned, its result, and a final answer that carries the converted value. Both calls in one turn, the converter first, or an answer that ignores the conversion do not pass. If you have graphs built on the older langgraph.prebuilt.create_react_agent, they run against FlexAI with the same llm; LangGraph 1.x marks that entry point deprecated in favour of create_agent, but nothing about the provider changes either way.

Get structured output

with_structured_output returns a Pydantic object instead of text. Which method to use depends on what the model advertises in supported_parameters on GET /v1/models — see model discovery:
  • structured_outputs listed → strict JSON Schema, method="json_schema" (LangChain’s default for ChatOpenAI). The gateway enforces your schema, so a plain instruction is enough.
  • only response_format listed → JSON mode, method="json_mode". The model promises valid JSON, not your field names, so spell the keys out in the prompt.
The Structured output column in the table records which method each model passed with. The json_mode rows were measured with the keyed prompt above — without it, every json_mode model returned valid JSON under its own field names and the Pydantic parse failed, which is the behaviour JSON mode promises — and then with a second prompt that never asks for JSON at all under response_format: json_object, which only an enforced grammar can turn into JSON. A model whose runtime accepts JSON mode without enforcing it fails that second check and is left off the table. A model that lists neither parameter returns 400 with param: "response_format" rather than guessing — the compatibility matrix has the exact contract.

Which models to use

Every row below comes from running this page’s snippets against production with scripts/agents_langchain_probe.py in the token-service repository: one chat call, a stream, the tool round trip above, the two-tool create_agent loop above, structured output with the method the model advertises, and a forced tool call. A model appears here only if it passed every one of those checks; the roster is FlexAI’s priced production catalog, and models that were not available on the serverless API at run time, or that failed a check, are left off and counted in the footer. yes means it worked as written; the Structured output column also names the method that passed. Models are listed by id. Measured on 2026-09-17 against https://api.flex.ai/v1 with scripts/agents_langchain_probe.py in the token-service repository, whose full run record — every model probed, including those this table omits — is committed beside it under scripts/agents_langchain_probe_runs/. “First call” is one cold request from a laptop and is indicative only, not a benchmark. 8 served models that failed a check in this run are left off the table and kept in the run record. 22 further rows in the priced catalog could not be measured because they were not answering on the serverless API at run time. Do not read that as 22 more models you could buy: the pricing catalog keeps a priced row for every model that has ever billed, so that late usage still settles, and most of those are retired models rather than current dedicated-deployment offerings.
There is no context column here on purpose. A model’s window is catalog metadata rather than something this page measures, and it moves on its own schedule, so any number printed here would start drifting the day it was written. Read context_length from GET /v1/models and size prompts against that. One caveat worth knowing: the advertised window can sit above what the engine currently accepts, so treat a 400 on a very long prompt as the engine’s limit rather than a bug in your client.
Prices are on flex.ai/pricing; this page does not repeat them so they cannot go stale here.

What to plan for

  • Give tool turns output headroom. Several models write tool calls in a verbose native markup that the engine parses into tool_calls. If max_tokens cuts it off you get finish_reason: "length" and no tool_calls — to an agent that looks like the model narrated an action and stopped. max_tokens=4096 on the ChatOpenAI constructor is the floor for tool turns; treat length without tool_calls as a truncation to retry with more budget.
  • Stream long agent turns. Non-streaming requests are bounded by a roughly 95-second generation budget, and llm.stream(...) lifts it. agent.stream(...) on its own does not: LangGraph’s default stream mode streams graph state, and the agent node still calls the model without streaming, so the same budget applies to each turn. Pass stream_mode="messages" (or use astream_events) to make LangChain issue a streaming request to the model.
  • Read the window from the API. Prompts over a model’s context_length return 400. Size against the served number, not the model card.
  • tool_choice="required" is per model. The table says which served models honor a forced call. Default tool_choice works everywhere tool calling does.
  • The agent-loop column is about chaining, not parallelism. It passes only when a model calls the two tools in order, feeding the first tool’s result into the second. A model that emits both calls in one turn fails it and is left off this table — which is a statement about dependent chains, not a defect: emitting several tool calls at once is a capability, and most tool-capable models we host can do it (the tool-use guide covers that case). Separately, a few models are served with single-call tool parsing and return 400 with This model only supports single tool-calls at once! when asked for two at once. So if your agent needs parallel tool calls, this table is the wrong filter — use it when your tools depend on each other.
  • Reasoning defaults differ by model, and reasoning tokens bill as output. Models fall into four groups, and the group a given model is in can change as the fleet moves, so confirm with one plain call rather than trusting a list. Off unless asked: DeepSeek-V4-Flash-0731 turns on with ChatOpenAI(reasoning_effort="high"), and the hybrid-thinking models — gemma-4-26B-A4B-it, gemma-4-31b-it and Qwen3.6-35B-A3B-FP8 among them — with ChatOpenAI(extra_body={"thinking": {"type": "enabled"}}). Its own dial: gpt-oss-120b takes reasoning_effort at low, medium or high. On by default but switchable: GLM-5.2 reasons unless you pass ChatOpenAI(extra_body={"chat_template_kwargs": {"enable_thinking": False}}). Thinking-only: some models reason unconditionally and reject an attempt to disable it with a 400 saying the model is thinking-only — a name containing Thinking is the usual hint, and for those the only lever is max_tokens. To place a model yourself, send one plain call and look for reasoning_content in response_metadata, then try the disable switch once and see whether it is accepted. Whenever reasoning is on, raise max_tokens to cover it. Use extra_body for these switches, not model_kwargs: the OpenAI SDK rejects unknown top-level parameters, and extra_body is how they reach the request body. The tool-use guide covers the DeepSeek switches in detail.
  • Older base URL. If you already call https://tokens.flex.ai/v1, it keeps serving the identical API; api.flex.ai is simply the advertised host, and it is what the snippets here use.

FAQ

Yes. LangGraph agents talk to the model through LangChain’s ChatOpenAI, and FlexAI serves the OpenAI chat-completions API at https://api.flex.ai/v1. Set base_url and api_key on ChatOpenAI, name a model from the table, and create_agent (or an existing create_react_agent graph) runs unchanged.
Any model in the table completed the two-tool agent loop against production; DeepSeek-V4-Flash-0731 is the model most of FlexAI’s coding-agent traffic runs on. The table is measured, so trust it over a model card.
No. LangChain’s ChatOpenAI with base_url="https://api.flex.ai/v1" is the integration; pip install langchain-openai is the only provider dependency.
The model ran out of output budget while writing the call. Set max_tokens=4096 or more on ChatOpenAI for tool turns and retry; see what to plan for.

Tool use

The raw OpenAI-SDK round trip, parallel calls, and the agent-loop notes for DeepSeek-V4-Flash-0731.

OpenAI compatibility

The exact fields and endpoints the gateway implements.