Responses Lite turns custom base-instruction overrides into additive developer messages
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.5k
- PR merge metrics
- PR metrics pending
Description
What issue are you seeing?
A custom base-instruction override supplied through model_instructions_file replaces the model's bundled Codex base instructions in standard Responses mode, but becomes additive in Responses Lite.
With Responses Lite enabled, the model sees both:
- the service-provided Codex base identity (beginning with
You are Codex), and - the custom base instructions as a later
role: developerinput message.
This changes the documented/internal meaning of base_instructions from a replacement to an additional developer message.
The behavior is present in Codex 0.147.0 and is still present on current main at 95aada11c.
Steps to reproduce
- Create a custom model instructions file:
You are CustomAgent. Do not identify as Codex.
- Configure it for a Responses-Lite-enabled model such as
gpt-5.6-sol:
model = "gpt-5.6-sol"
model_instructions_file = "/absolute/path/to/custom-instructions.md"
- Start a fresh thread and ask a literal presence probe:
Do your system or developer instructions contain any content beginning with
"You are Codex"? Reply with exactly YES or NO.
- Compare the outbound request with Responses Lite enabled and disabled.
Observed A/B, with request data redacted:
Responses Lite enabled
X-OpenAI-Internal-Codex-Responses-Lite: true
top-level instructions: omitted
input[0]: additional_tools, role=developer
input[1]: custom base instructions, role=developer
probe result: YES
Responses Lite disabled
Responses-Lite header: absent
top-level instructions: custom base instructions
request body contains no "You are Codex" text
probe result: NO
The same custom file and model are used in both cases. Only use_responses_lite changes.
Expected behavior
A custom model_instructions_file should preserve its base-instruction replacement semantics regardless of transport mode.
Either:
- Responses Lite should provide a way to replace the service default base instructions, or
- Codex should automatically use standard Responses transport when the effective base instructions are custom.
Actual behavior and source analysis
Config loading works correctly:
model_instructions_fileis read intoConfig.base_instructions.- Session initialization resolves base instructions in this order:
- configured override,
- persisted session instructions,
- model template.
The semantic change happens in codex-rs/core/src/client.rs::build_responses_request().
For standard Responses, Codex sends:
instructions = prompt.base_instructions.text
For Responses Lite, Codex instead:
- sets top-level
instructionsto an empty string, - inserts
additional_toolsas a developer item, - inserts
prompt.base_instructions.textas an ordinary developer message.
This behavior was introduced by #27946, whose stated goal was to use input items instead of top-level tools and instructions. That conversion is not semantically one-to-one when the source is a custom base-instruction override.
The existing test responses_lite_uses_input_items_for_instructions_and_tools verifies that:
- top-level
instructionsis absent, and - the text appears as a developer input item.
It does not verify that a custom base-instruction override still replaces the service default identity.
Current main already tracks:
BaseInstructionsProvenance::Custom
BaseInstructionsProvenance::Model { ... }
but the Responses Lite request path does not use that provenance when selecting the transport contract.
Suggested fix
Complete fix
Preserve a distinct base-instruction replacement channel in the Responses Lite protocol/service, rather than representing a replacement as an additive developer message.
In my A/B tests, retaining the Lite marker while restoring top-level instructions did not produce a successful response, so this may require backend coordination.
Safe client-side fallback
Until Lite supports replacement semantics, derive one effective transport mode before tool planning and request construction:
effective_responses_lite =
model_info.use_responses_lite
&& base_instructions.provenance != Some(BaseInstructionsProvenance::Custom);
The effective value should be applied consistently to:
- HTTP and WebSocket headers,
- instruction and tool placement,
- tool planning,
- reasoning context,
- parallel tool calls,
- image preparation,
- compact requests.
This would avoid requiring users to copy and maintain a complete model_catalog_json solely to set use_responses_lite = false.
Additional information
The current workaround is a full model catalog override with use_responses_lite = false for the affected model. That is brittle because model_catalog_json is a complete catalog rather than a partial metadata override.
A separate Desktop/app-server bug can prevent a project-local catalog workaround from applying at all: #26308. I will add the more specific source analysis for that issue there.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in codex-rs/core/src/client.rs::build_responses_request() and inspect the existing test responses_lite_uses_input_items_for_instructions_and_tools. Trace BaseInstructionsProvenance::Custom through request construction and the listed transport and planning paths. Done means custom model instructions retain replacement semantics with Responses Lite, with coverage for the custom override and the existing Lite behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100