https://www.boundaryml.com logo
Using Codex models with OpenAI Responses API
# help
t
Sup BAML! Usually this is Anders but I've been tasked with implementing all our LLM providers so I've been working heavily with the BAML client registry šŸ™‚ I'm trying to make it so we can leverage the OpenAI ChatGPT Plus/Pro subscription in our TUI. I'm getting an oauth token from OpenAI. Then I need to call the codex model specifically using the Responses API. I'm trying to do it like so:
Copy code
const bamlProvider = (providerId === 'openai' && auth?.type === 'oauth')
    ? 'openai-responses' as const
    : def.bamlProvider

  // The options passed to BAML:
  cr.addLlmClient('ChatClientNoRetryAnthropicOnly', 'openai-responses', {
    model: modelId,
    api_key: auth.accessToken,
    base_url: 'https://chatgpt.com/backend-api/codex',
    headers: {
      'ChatGPT-Account-Id': auth.accountId,
    },
  })
But I keep getting this error back from OpenAI:
Copy code
Request failed with status code: 400 Bad Request, 
{"detail":"Instructions are required"}
I thought the BAML openai-response provider would properly map system instructions to the right parameters. But is something different with the codex endpoint? Do I need to use the Modular API to manually construct the proper request here?
g
Hi Tom! Let me try to debug this with you. Does that same config work if you point it to other openai models that use the responses API? \
t
So if someone uses an API key instead of Oauth I actually just use the normal chat completions API. But I can try to just hit responses API with non-codex models to see
- Provider: openai-responses - Endpoint: default, no base URL override - Auth: OAuth access token as api_key - Model: gpt-5.2 - Headers: ChatGPT-Account-Id Error: 401 Unauthorized { "error": { "message": "You have insufficient permissions for this operation. Missing scopes: api.responses.write. Check that you have the correct role in your organization (Reader, Writer, Owner) and project (Member, Owner), and if you're using a restricted API key, that it has the necessary scopes.", "type": "invalid_request_error", "param": null } }
I believe with the ChatGPT OAuth token, you can only call Codex models on the Codex endpoint
g
Do you know if the API behind the endpoint you're using is documented somewhere? I might try using Modular API to test some hypotheses about it - for example does it start giving you good responses if you pass something via the
instructions
field, which isn't part of the request we build via BAML. When you make the prior request (the one that fails with 400), can you grab a screenshot of the request preview from the Playground for me?
t
It's not super well documented but if you look at other open source tools this is how they all do it. OpenAI does mention it in their blog here (https://openai.com/index/unrolling-the-codex-agent-loop/): > Model inference > The Codex CLI sends HTTP requests to the Responses API⁠(opens in a new window) to run model inference. We’ll examine how information flows through Codex, which uses the Responses API to drive the agent loop. > > The Responses API endpoint that the Codex CLI uses is configurable⁠(opens in a new window), so it can be used with any endpoint that implements the Responses API⁠(opens in a new window): > > When using ChatGPT login⁠(opens in a new window) with the Codex CLI, it uses https://chatgpt.com/backend-api/codex/responses as the endpoint > When using API-key authentication⁠(opens in a new window) with OpenAI hosted models, it uses https://api.openai.com/v1/responses as the endpoint
And unlike Anthropic they have given their blessing for 3rd party tools to use their subscriptions to hit the API
There's more info on the specific instructions param that is causing the error when using BAML openai-responses provider in the blog I linked: > The Responses API⁠(opens in a new window) takes a JSON payload with many parameters. We’ll focus on these three: > > instructions⁠(opens in a new window): system (or developer) message inserted into the model’s context > tools⁠(opens in a new window): a list of tools the model may call while generating a response > input⁠(opens in a new window): a list of text, image, or file inputs to the model > In Codex, the instructions field is read from the model_instructions_file⁠(opens in a new window) in ~/.codex/config.toml, if specified; otherwise, the base_instructions associated with a model⁠(opens in a new window) are used. Model-specific instructions live in the Codex repo and are bundled into the CLI (e.g., gpt-5.2-codex_prompt.md⁠(opens in a new window)).
g
Huh, interesting.
Are you open to giving it a try with Modular API?
t
Yeah I'll implement that now
Yep I got it to work with my own implementation using modular API
g
Nice nice. Is that a good solution for you, or now that you see how instructions works, do you think this is something BAML should natively support?
t
Works for us fine, really up to you. Would be nice to support for people that want to use the OAuth versions in their agents. Otherwise they will have to hack together a lot. Would also need to probably add in docs that people need to grab their OAuth token and account ID from OpenAI and store them to use properly. OpenAI has "blessed" this as being supported for 3rd party tools, so unlike Anthropic, I do think it coud make sense to add
I can send my implementation reference here if you want
b
that would be fantastic, if you could! šŸ™
a
one problem with this approach is it that requests don't get traced in the dashboard - unsure if possible with the modular api
v
Has this feature been added? I'm also encountering the same issue, and I'm hesitant about whether to use the modular API to implement it
## Workaround:
instructions
as a Passthrough Option BAML forwards unrecognized options directly to the request body. Adding a static
instructions
field in client options bypasses the error:
Copy code
baml
client<llm> MyClient {
  provider "openai-responses"
  options {
    model "gpt-5.4"
    base_url "https://your.example.com/v1"
    api_key env.YOUR_API_KEY
    instructions "Follow the user's instructions carefully."
    reasoning {
      effort "medium"
    }
  }
}
The prompt's system role content is still sent as part of
input
messages, so the model receives the full context. The static
instructions
field simply satisfies the endpoint's requirement.
t
Hi, has anyone got this error using codex with the workaround above by @vEth. Is this fixable? > ---ERROR (Unspecified error code: 2)--- > Failed to parse into a responses API stream event: > {"type":"response.completed","response":{ > "id":"resp_xxxxxxxx", > "object":"response","created_at":1779867380,"status":"completed", > ...... > "tool_choice":"auto", > "tool_usage":{ > "image_gen":{"input_tokens":0,"input_tokens_details":{...}, > "output_tokens":0,"output_tokens_details":{...}, > "total_tokens":0}, > "web_search":{"num_requests":0} > }, > "tools":[],"top_logprobs":0,"top_p":0.98,"truncation":"disabled", > "usage":{"input_tokens":45,"input_tokens_details":{"cached_tokens":0}, > "output_tokens":9,"output_tokens_details":{"reasoning_tokens":0}, > "total_tokens":54}, > "user":null,"metadata":{} > },"sequence_number":12} > > Caused by: > missing field output > > Traceback (most recent call last): > ... > baml_py._impl.BamlClientHttpError: BamlClientHttpError( > client_name=ChatGPTCodex, > message=Failed to parse into a responses API stream event: {…payload above…} > > Caused by: > missing field output, > status_code=2, > detailed_message=LLM client "ChatGPTCodex" failed with status code: > Unspecified error code: 2 > Message: Failed to parse into a responses API stream event: {…payload above…} > )
7 Views