POST /v1/infer/responses is the OpenAI Responses API on the node's model, with the same base URL as chat completions, https://beta-api.b3os.org/v1/infer. Set B3OS_MODEL to a chat model from GET /v1/infer/models first (how).

The node stores nothing. Send the whole conversation as input items on every turn; previous_response_id, conversation, stored prompts and background mode are refused with 400 unsupported_parameter.

A response

ts
import OpenAI from "openai";const client = new OpenAI({ apiKey: process.env.B3OS_API_KEY, baseURL: "https://beta-api.b3os.org/v1/infer" });const response = await client.responses.create({ model: process.env.B3OS_MODEL!, instructions: "You are a concise assistant.", input: "Name one river in Oregon.",});console.log(response.output_text, response.usage);

stream: true sends the Responses event stream: response.created, the output item, content part and response.output_text.delta events, response.function_call_arguments.delta for tool calls, and response.completed (or response.incomplete at max_output_tokens). The SDKs' responses.stream(...) helpers read it.

Function tools, statelessly

Each turn sends the conversation so far: the earlier input, the output items the model returned, and a function_call_output for every call.

python
tools = [{ "type": "function", "name": "get_weather", "description": "Current weather for a city.", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},}]question = {"role": "user", "content": "What is the weather in Eugene, Oregon? Use the tool."}first = client.responses.create(model=os.environ["B3OS_MODEL"], input=[question], tools=tools)call = next(o for o in first.output if o.type == "function_call")follow = [ question, *[o.model_dump(exclude_unset=True) for o in first.output], {"type": "function_call_output", "call_id": call.call_id, "output": "52F and raining"},]answer = client.responses.create(model=os.environ["B3OS_MODEL"], input=follow, tools=tools)print(answer.output_text)

An output sent without the call it answers (as a client that expects the server to remember the last response would send it) is refused, naming the item:

bash
curl -s -X POST 'https://beta-api.b3os.org/v1/infer/responses' \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H 'content-type: application/json' \ -d '{"model":"b3os/qwen3.5-9b","input":[{"role":"user","content":"What is the weather in Eugene, Oregon? Use the tool."},{"type":"function_call_output","call_id":"call_weather_1","output":"52F and raining"}]}'

Captured 2026-10-31 from commit 6bd0d80a , label dev (recorded on a dev node before this reached production). Case infer/responses-unanswered-output .

tool_choice takes none, auto, required or a named function, and text.format takes json_schema for structured output. A tool's strict reaches the model's runtime only when you set it, and the response's tools echo what was applied: your value, or null when unset. OpenAI defaults it to true; here an unset strict leaves auto tool calls unconstrained, so set "strict": true when the arguments must match the schema. reasoning.effort from low up turns the model's reasoning on; it comes back as reasoning items, which go back with their turn. Tools OpenAI runs on its side (web search, file search, computer use, code interpreter, MCP, image generation, shell) are refused with 400 unsupported_parameter.

OpenAI Agents SDK

The Agents SDK uses Responses by default. Give it a client pointed at the node and a model object: a model string with a slash (b3os/...) is read as a provider prefix and fails with Unknown prefix: b3os. Turn tracing off, or the SDK sends traces to OpenAI.

python
import asyncioimport osfrom agents import Agent, OpenAIResponsesModel, Runner, function_tool, set_tracing_disabledfrom openai import AsyncOpenAIclient = AsyncOpenAI(api_key=os.environ["B3OS_API_KEY"], base_url="https://beta-api.b3os.org/v1/infer")set_tracing_disabled(True)@function_tooldef get_weather(city: str) -> str: """Current weather for a city.""" return "52F and raining"agent = Agent( name="Weather", instructions="Answer with the tool's result.", model=OpenAIResponsesModel(model=os.environ["B3OS_MODEL"], openai_client=client), tools=[get_weather],)print(asyncio.run(Runner.run(agent, "What is the weather in Eugene, Oregon? Use the tool.")).final_output)

Codex

Codex talks to a custom provider over Responses. Add the node as a provider in ~/.codex/config.toml, with your key in B3OS_API_KEY:

toml
model = "b3os/qwen3.5-9b"model_provider = "b3os"[model_providers.b3os]name = "B3OS"base_url = "https://beta-api.b3os.org/v1/infer"env_key = "B3OS_API_KEY"wire_api = "responses"supports_websockets = false

supports_websockets = false keeps Codex on HTTPS, the only transport the node serves for Responses: a WebSocket upgrade on /v1/infer/responses is answered at once with 405 (Allow: POST). Inside a B3OS sandbox, set base_url = "http://127.0.0.1:7700/v1/infer".

Limits

Responses share chat completions' admission and limits: the estimated input plus max_output_tokens counts against your organization's tokens per minute for the model, with requests per minute and requests in flight. Going over answers 429 rate_limited with Retry-After; a prompt longer than the context answers 400 context_length_exceeded. See Infer limits. Errors use OpenAI's shape.

json
{ "model": "b3os/qwen3.5-9b", "instructions": "You are a concise assistant.", "input": "Name one river in Oregon."}

Try it in the playground

Opens the dashboard playground with this request filled in ( POST /v1/infer/responses ). The link carries no key.

Ask a question... ⌘I