mirror of
https://github.com/openai/codex.git
synced 2026-10-21 07:42:01 +00:00
Annotate Responses Lite base instructions (#40296)
## What changed - Build Responses Lite base instructions through a dedicated context fragment so the developer message carries the `model.base_instructions` content kind. - Apply provider metadata cleanup after adding the base-instructions message, preserving the existing behavior for non-OpenAI providers. ## Testing - Extend Responses Lite request coverage to verify the annotation is present for OpenAI requests and omitted for a third-party provider. GitOrigin-RevId: b0317f92dfb9570860688b49e3a7668da516ee8c
This commit is contained in:
+17
-22
@@ -78,7 +78,6 @@ use codex_protocol::auth::AuthMode;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
|
||||
use codex_protocol::config_types::Verbosity as VerbosityConfig;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::openai_models::ModelInfo;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
@@ -115,6 +114,8 @@ use crate::attestation::X_OAI_ATTESTATION_HEADER;
|
||||
use crate::client_common::Prompt;
|
||||
use crate::client_common::ResponseEvent;
|
||||
use crate::client_common::ResponseStream;
|
||||
use crate::context::BaseInstructionsFragment;
|
||||
use crate::context::ContextualUserFragment;
|
||||
use crate::feedback_tags;
|
||||
use crate::responses_metadata::CodexResponsesMetadata;
|
||||
use crate::responses_metadata::subagent_header_value;
|
||||
@@ -875,18 +876,6 @@ impl ModelClient {
|
||||
) -> Result<ResponsesApiRequest> {
|
||||
let mut input = prompt.get_formatted_input_for_request(model_info.use_responses_lite);
|
||||
let is_openai = self.state.provider.info().is_openai();
|
||||
if !is_openai {
|
||||
for item in &mut input {
|
||||
item.clear_internal_chat_message_metadata_passthrough();
|
||||
if let ResponseItem::FunctionCall {
|
||||
encrypted_function_args,
|
||||
..
|
||||
} = item
|
||||
{
|
||||
*encrypted_function_args = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
let (instructions, tools) = if model_info.use_responses_lite {
|
||||
let tools = if self.state.provider.capabilities().namespace_tools {
|
||||
create_tools_json_for_responses_lite(&prompt.tools)?
|
||||
@@ -899,15 +888,9 @@ impl ModelClient {
|
||||
tools,
|
||||
}];
|
||||
if !prompt.base_instructions.text.is_empty() {
|
||||
prefix.push(ResponseItem::Message {
|
||||
id: None,
|
||||
role: "developer".to_string(),
|
||||
content: vec![ContentItem::InputText {
|
||||
text: prompt.base_instructions.text.clone(),
|
||||
}],
|
||||
phase: None,
|
||||
internal_chat_message_metadata_passthrough: None,
|
||||
});
|
||||
prefix.push(ContextualUserFragment::into(BaseInstructionsFragment(
|
||||
prompt.base_instructions.text.clone(),
|
||||
)));
|
||||
}
|
||||
input.splice(0..0, prefix);
|
||||
(String::new(), None)
|
||||
@@ -917,6 +900,18 @@ impl ModelClient {
|
||||
Some(create_tools_raw_json_for_responses_api(&prompt.tools)?.into()),
|
||||
)
|
||||
};
|
||||
if !is_openai {
|
||||
for item in &mut input {
|
||||
item.clear_internal_chat_message_metadata_passthrough();
|
||||
if let ResponseItem::FunctionCall {
|
||||
encrypted_function_args,
|
||||
..
|
||||
} = item
|
||||
{
|
||||
*encrypted_function_args = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
let reasoning = Self::build_reasoning(model_info, effort, summary);
|
||||
let stream_options = (self.state.concurrent_reasoning_summaries_enabled
|
||||
&& is_openai
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
use super::ContextualUserFragment;
|
||||
use codex_protocol::models::ContentItemKind;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) struct BaseInstructionsFragment(pub(crate) String);
|
||||
|
||||
impl ContextualUserFragment for BaseInstructionsFragment {
|
||||
fn content_kind(&self) -> ContentItemKind {
|
||||
ContentItemKind("model.base_instructions".to_string())
|
||||
}
|
||||
|
||||
fn role(&self) -> &'static str {
|
||||
"developer"
|
||||
}
|
||||
|
||||
fn requires_separate_message(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn markers(&self) -> (&'static str, &'static str) {
|
||||
Self::type_markers()
|
||||
}
|
||||
|
||||
fn type_markers() -> (&'static str, &'static str) {
|
||||
("", "")
|
||||
}
|
||||
|
||||
fn body(&self) -> String {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
mod approved_command_prefix_saved;
|
||||
mod apps_instructions;
|
||||
mod available_plugins_instructions;
|
||||
mod base_instructions;
|
||||
mod compaction_summary;
|
||||
mod contextual_user_message;
|
||||
mod current_time_reminder;
|
||||
@@ -47,6 +48,7 @@ pub(crate) use approved_command_prefix_saved::APPROVED_COMMAND_PREFIX_SAVED_MESS
|
||||
pub(crate) use approved_command_prefix_saved::ApprovedCommandPrefixSaved;
|
||||
pub(crate) use apps_instructions::AppsInstructions;
|
||||
pub(crate) use available_plugins_instructions::AvailablePluginsInstructions;
|
||||
pub(crate) use base_instructions::BaseInstructionsFragment;
|
||||
pub(crate) use codex_context_fragments::AdditionalContextDeveloperFragment;
|
||||
pub(crate) use codex_context_fragments::AdditionalContextUserFragment;
|
||||
pub use codex_context_fragments::ContextualUserFragment;
|
||||
|
||||
@@ -109,6 +109,7 @@ async fn responses_lite_uses_input_items_for_instructions_and_tools() -> Result<
|
||||
})
|
||||
.with_config(|config| {
|
||||
config.base_instructions = Some("test instructions".to_string());
|
||||
config.code_mode.disable_in_process_fallback = true;
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
|
||||
@@ -132,6 +133,9 @@ async fn responses_lite_uses_input_items_for_instructions_and_tools() -> Result<
|
||||
"type": "input_text",
|
||||
"text": "test instructions",
|
||||
}],
|
||||
"internal_chat_message_metadata_passthrough": {
|
||||
"content_item_kinds": ["model.base_instructions"],
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
@@ -185,6 +189,7 @@ async fn responses_lite_includes_tool_namespaces_info_when_enabled() -> Result<(
|
||||
model_info.supports_search_tool = false;
|
||||
})
|
||||
.with_config(|config| {
|
||||
config.code_mode.disable_in_process_fallback = true;
|
||||
config.tool_registry.turn_metadata_includes_tool_info = true;
|
||||
});
|
||||
let test = builder.build_with_auto_env(&server).await?;
|
||||
@@ -456,6 +461,7 @@ async fn responses_lite_does_not_expose_standalone_web_search_for_bedrock_provid
|
||||
);
|
||||
let body = request.body_json();
|
||||
assert!(body.get("tools").is_none());
|
||||
assert!(!request.has_content_kinds(&["model.base_instructions"]));
|
||||
let tools = additional_tools(&body)?;
|
||||
assert!(!has_namespaced_tool(tools, "web", "run"));
|
||||
assert!(!has_hosted_tool(tools, "web_search"));
|
||||
|
||||
Reference in New Issue
Block a user