> ## Documentation Index
> Fetch the complete documentation index at: https://docs.beta.b3os.org/llms.txt
> Use this file to discover all available pages before exploring further.

# 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](/compat/openai)).

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`](/errors#unsupported_parameter).

## A response

**TypeScript**

```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);
    ```
  **Python**

```python
    import os
    from openai import OpenAI

    client = 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)
    ```
  **curl**

```bash
    curl -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.

```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:

**Request**

```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"}]}'
```
**Response 400**

```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"
  }
}
```
**Server-Timing**

```http
server-timing: total;dur=0.096
x-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`](/errors#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 asyncio
import os

from agents import Agent, OpenAIResponsesModel, Runner, function_tool, set_tracing_disabled
from openai import AsyncOpenAI

client = AsyncOpenAI(api_key=os.environ["B3OS_API_KEY"], base_url="https://beta-api.b3os.org/v1/infer")
set_tracing_disabled(True)


@function_tool
def 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`](/errors#rate_limited) with `Retry-After`; a prompt longer than the context answers `400` [`context_length_exceeded`](/errors#context_length_exceeded). See [Infer limits](/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](https://beta.b3os.org/app/playground?op=inferCreateResponse&body=eyJtb2RlbCI6ImIzb3MvcXdlbjMuNS05YiIsImluc3RydWN0aW9ucyI6IllvdSBhcmUgYSBjb25jaXNlIGFzc2lzdGFudC4iLCJpbnB1dCI6Ik5hbWUgb25lIHJpdmVyIGluIE9yZWdvbi4ifQ)

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