Responses
Call the node's model with the OpenAI Responses API, statelessly, from the OpenAI SDKs, the OpenAI Agents SDK and Codex.
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
tsimport 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);
pythonimport osfrom openai import OpenAIclient = OpenAI(api_key=os.environ["B3OS_API_KEY"], base_url="https://beta-api.b3os.org/v1/infer")response = client.responses.create( model=os.environ["B3OS_MODEL"], instructions="You are a concise assistant.", input="Name one river in Oregon.",)print(response.output_text, response.usage)
bashcurl -s https://beta-api.b3os.org/v1/infer/responses \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H "Content-Type: application/json" \ -d "{\"model\": \"$B3OS_MODEL\", \"instructions\": \"You are a concise assistant.\", \"input\": \"Name one river in Oregon.\"}"
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.
pythontools = [{ "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:
bashcurl -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"}]}'
json{ "error": { "code": "invalid_request", "message": "no function_call with call_id \"call_weather_1\" comes before this output", "param": "input.1.call_id", "type": "invalid_request_error" }}
httpserver-timing: total;dur=0.096x-request-id: req_01m69wb5cmmj4qm430szmt6h0x
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.
pythonimport 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:
tomlmodel = "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."}
Opens the dashboard playground with this request filled in (
POST /v1/infer/responses
). The link carries no key.