mirror of
https://github.com/openai/codex.git
synced 2026-10-20 00:18:49 +00:00
Gate content item kinds behind a feature flag (#40450)
## What changed - Add the under-development `content_item_kinds` feature, disabled by default. - Strip content item classifications from Responses input when the feature is disabled while preserving other passthrough metadata. ## Testing - Add coverage that disables the feature and verifies requests omit content item kinds without removing the turn ID. GitOrigin-RevId: f938feb2895d766d979b1666fc7b97fd128fe8a7
This commit is contained in:
@@ -652,6 +652,9 @@
|
||||
"connectors": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"content_item_kinds": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"current_time_reminder": {
|
||||
"$ref": "#/definitions/FeatureToml_for_CurrentTimeReminderConfigToml"
|
||||
},
|
||||
@@ -5628,6 +5631,9 @@
|
||||
"connectors": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"content_item_kinds": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"current_time_reminder": {
|
||||
"$ref": "#/definitions/FeatureToml_for_CurrentTimeReminderConfigToml"
|
||||
},
|
||||
|
||||
@@ -208,6 +208,7 @@ struct ModelClientState {
|
||||
session_source: SessionSource,
|
||||
originator: String,
|
||||
model_verbosity: Option<VerbosityConfig>,
|
||||
content_item_kinds_enabled: bool,
|
||||
enable_request_compression: bool,
|
||||
include_timing_metrics: bool,
|
||||
beta_features_header: Option<String>,
|
||||
@@ -436,6 +437,7 @@ impl ModelClient {
|
||||
session_source: SessionSource,
|
||||
originator: String,
|
||||
model_verbosity: Option<VerbosityConfig>,
|
||||
content_item_kinds_enabled: bool,
|
||||
enable_request_compression: bool,
|
||||
include_timing_metrics: bool,
|
||||
beta_features_header: Option<String>,
|
||||
@@ -459,6 +461,7 @@ impl ModelClient {
|
||||
session_source,
|
||||
originator,
|
||||
model_verbosity,
|
||||
content_item_kinds_enabled,
|
||||
enable_request_compression,
|
||||
include_timing_metrics,
|
||||
beta_features_header,
|
||||
@@ -963,6 +966,9 @@ impl ModelClient {
|
||||
if item.id().is_some_and(|id| !id.is_prefixed()) {
|
||||
item.set_id(/*new_id*/ None);
|
||||
}
|
||||
if !self.state.content_item_kinds_enabled {
|
||||
item.clear_content_item_kinds();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ fn test_model_client_with_thread_id(
|
||||
session_source,
|
||||
"test_originator".to_string(),
|
||||
/*model_verbosity*/ None,
|
||||
/*content_item_kinds_enabled*/ true,
|
||||
/*enable_request_compression*/ false,
|
||||
/*include_timing_metrics*/ false,
|
||||
/*beta_features_header*/ None,
|
||||
@@ -155,6 +156,7 @@ async fn compact_uses_bearer_after_agent_identity_session_fallback() -> anyhow::
|
||||
SessionSource::Cli,
|
||||
"test_originator".to_string(),
|
||||
/*model_verbosity*/ None,
|
||||
/*content_item_kinds_enabled*/ true,
|
||||
/*enable_request_compression*/ false,
|
||||
/*include_timing_metrics*/ false,
|
||||
/*beta_features_header*/ None,
|
||||
@@ -955,6 +957,7 @@ fn model_client_with_counting_attestation(
|
||||
SessionSource::Exec,
|
||||
"test_originator".to_string(),
|
||||
/*model_verbosity*/ None,
|
||||
/*content_item_kinds_enabled*/ true,
|
||||
/*enable_request_compression*/ false,
|
||||
/*include_timing_metrics*/ false,
|
||||
/*beta_features_header*/ None,
|
||||
|
||||
@@ -1432,6 +1432,7 @@ impl Session {
|
||||
session_configuration.session_source.clone(),
|
||||
session_configuration.originator.clone(),
|
||||
config.model_verbosity,
|
||||
config.features.enabled(Feature::ContentItemKinds),
|
||||
config.features.enabled(Feature::EnableRequestCompression),
|
||||
config.features.enabled(Feature::RuntimeMetrics),
|
||||
Self::build_model_client_beta_features_header(config.as_ref()),
|
||||
|
||||
@@ -690,6 +690,7 @@ fn test_model_client_session() -> crate::client::ModelClientSession {
|
||||
codex_protocol::protocol::SessionSource::Exec,
|
||||
"test_originator".to_string(),
|
||||
/*model_verbosity*/ None,
|
||||
/*content_item_kinds_enabled*/ true,
|
||||
/*enable_request_compression*/ false,
|
||||
/*include_timing_metrics*/ false,
|
||||
/*beta_features_header*/ None,
|
||||
@@ -5991,6 +5992,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
||||
session_configuration.session_source.clone(),
|
||||
session_configuration.originator.clone(),
|
||||
config.model_verbosity,
|
||||
config.features.enabled(Feature::ContentItemKinds),
|
||||
config.features.enabled(Feature::EnableRequestCompression),
|
||||
config.features.enabled(Feature::RuntimeMetrics),
|
||||
Session::build_model_client_beta_features_header(config.as_ref()),
|
||||
@@ -8200,6 +8202,7 @@ where
|
||||
session_configuration.session_source.clone(),
|
||||
session_configuration.originator.clone(),
|
||||
config.model_verbosity,
|
||||
config.features.enabled(Feature::ContentItemKinds),
|
||||
config.features.enabled(Feature::EnableRequestCompression),
|
||||
config.features.enabled(Feature::RuntimeMetrics),
|
||||
Session::build_model_client_beta_features_header(config.as_ref()),
|
||||
|
||||
@@ -1339,6 +1339,10 @@ pub fn test_codex() -> TestCodexBuilder {
|
||||
.features
|
||||
.disable(Feature::ShellSnapshot)
|
||||
.expect("test config should allow ShellSnapshot override");
|
||||
config
|
||||
.features
|
||||
.enable(Feature::ContentItemKinds)
|
||||
.expect("test config should allow ContentItemKinds override");
|
||||
})],
|
||||
auth: CodexAuth::from_api_key("dummy"),
|
||||
pre_build_hooks: vec![],
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::sync::Arc;
|
||||
use codex_core::ModelClient;
|
||||
use codex_core::Prompt;
|
||||
use codex_core::ResponseEvent;
|
||||
use codex_features::Feature;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::auth::AgentIdentityAuthPolicy;
|
||||
use codex_model_provider_info::ModelProviderInfo;
|
||||
@@ -128,6 +129,7 @@ async fn responses_stream_includes_subagent_header_on_review() {
|
||||
session_source.clone(),
|
||||
"test_originator".to_string(),
|
||||
config.model_verbosity,
|
||||
config.features.enabled(Feature::ContentItemKinds),
|
||||
/*enable_request_compression*/ false,
|
||||
/*include_timing_metrics*/ false,
|
||||
/*beta_features_header*/ None,
|
||||
@@ -264,6 +266,7 @@ async fn responses_stream_includes_subagent_header_on_other() {
|
||||
session_source.clone(),
|
||||
"test_originator".to_string(),
|
||||
config.model_verbosity,
|
||||
config.features.enabled(Feature::ContentItemKinds),
|
||||
/*enable_request_compression*/ false,
|
||||
/*include_timing_metrics*/ false,
|
||||
/*beta_features_header*/ None,
|
||||
@@ -385,6 +388,7 @@ async fn responses_respects_model_info_overrides_from_config() {
|
||||
session_source.clone(),
|
||||
"test_originator".to_string(),
|
||||
config.model_verbosity,
|
||||
config.features.enabled(Feature::ContentItemKinds),
|
||||
/*enable_request_compression*/ false,
|
||||
/*include_timing_metrics*/ false,
|
||||
/*beta_features_header*/ None,
|
||||
|
||||
@@ -1375,6 +1375,10 @@ async fn fork_injects_changed_agents_md_once() -> Result<()> {
|
||||
fork_config.model_provider = parent.config.model_provider.clone();
|
||||
fork_config.model_catalog = parent.config.model_catalog.clone();
|
||||
fork_config.codex_self_exe = parent.config.codex_self_exe.clone();
|
||||
fork_config
|
||||
.features
|
||||
.enable(Feature::ContentItemKinds)
|
||||
.expect("test config should allow ContentItemKinds override");
|
||||
let forked = parent
|
||||
.thread_manager
|
||||
.fork_thread(
|
||||
|
||||
@@ -1524,6 +1524,7 @@ async fn send_request_with_provider(provider: ModelProviderInfo) {
|
||||
SessionSource::Exec,
|
||||
"test_originator".to_string(),
|
||||
config.model_verbosity,
|
||||
config.features.enabled(Feature::ContentItemKinds),
|
||||
/*enable_request_compression*/ false,
|
||||
/*include_timing_metrics*/ false,
|
||||
/*beta_features_header*/ None,
|
||||
@@ -3009,6 +3010,7 @@ async fn azure_responses_request_does_not_store_and_preserves_prefixed_item_ids(
|
||||
SessionSource::Exec,
|
||||
"test_originator".to_string(),
|
||||
config.model_verbosity,
|
||||
config.features.enabled(Feature::ContentItemKinds),
|
||||
/*enable_request_compression*/ false,
|
||||
/*include_timing_metrics*/ false,
|
||||
/*beta_features_header*/ None,
|
||||
|
||||
@@ -2514,6 +2514,7 @@ async fn websocket_harness_with_provider_options_and_auth(
|
||||
SessionSource::Exec,
|
||||
"test_originator".to_string(),
|
||||
config.model_verbosity,
|
||||
config.features.enabled(Feature::ContentItemKinds),
|
||||
/*enable_request_compression*/ false,
|
||||
runtime_metrics_enabled,
|
||||
/*beta_features_header*/ None,
|
||||
|
||||
@@ -215,3 +215,39 @@ async fn first_request_item_types_roles_and_content_annotations() -> Result<()>
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn content_item_kinds_are_omitted_when_feature_disabled() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let server = start_mock_server().await;
|
||||
let response = mount_sse_once(
|
||||
&server,
|
||||
sse(vec![ev_response_created("resp-1"), ev_completed("resp-1")]),
|
||||
)
|
||||
.await;
|
||||
let test = test_codex()
|
||||
.with_config(|config| {
|
||||
config.developer_instructions = Some("Keep other metadata intact.".into());
|
||||
config
|
||||
.features
|
||||
.disable(Feature::ContentItemKinds)
|
||||
.expect("test config should allow ContentItemKinds override");
|
||||
})
|
||||
.build_with_auto_env(&server)
|
||||
.await?;
|
||||
|
||||
test.submit_text_turn("inspect request metadata").await?;
|
||||
|
||||
let input = response.single_request().input();
|
||||
assert!(input.iter().all(|item| {
|
||||
item.pointer("/internal_chat_message_metadata_passthrough/content_item_kinds")
|
||||
.is_none()
|
||||
}));
|
||||
assert!(input.iter().any(|item| {
|
||||
item.pointer("/internal_chat_message_metadata_passthrough/turn_id")
|
||||
.is_some()
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -99,6 +99,8 @@ pub enum Feature {
|
||||
SecretAuthStorage,
|
||||
|
||||
// Experimental
|
||||
/// Send per-content-entry classifications in internal Responses metadata.
|
||||
ContentItemKinds,
|
||||
/// Record model-attempted tool calls in internal Responses metadata.
|
||||
ExecutedToolCallMetadata,
|
||||
/// Enable JavaScript code mode backed by the standalone host process.
|
||||
@@ -905,6 +907,12 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
stage: Stage::Removed,
|
||||
default_enabled: false,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::ContentItemKinds,
|
||||
key: "content_item_kinds",
|
||||
stage: Stage::UnderDevelopment,
|
||||
default_enabled: false,
|
||||
},
|
||||
FeatureSpec {
|
||||
id: Feature::ExecutedToolCallMetadata,
|
||||
key: "executed_tool_call_metadata",
|
||||
|
||||
@@ -257,6 +257,7 @@ impl MemoryStartupContext {
|
||||
session_source.clone(),
|
||||
config_snapshot.originator,
|
||||
config.model_verbosity,
|
||||
config.features.enabled(Feature::ContentItemKinds),
|
||||
config.features.enabled(Feature::EnableRequestCompression),
|
||||
config.features.enabled(Feature::RuntimeMetrics),
|
||||
/*beta_features_header*/ None,
|
||||
|
||||
@@ -1316,6 +1316,21 @@ impl ResponseItem {
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes content item classifications while preserving other passthrough metadata.
|
||||
pub fn clear_content_item_kinds(&mut self) {
|
||||
let Some(metadata) = self.internal_chat_message_metadata_passthrough_mut() else {
|
||||
return;
|
||||
};
|
||||
let Some(metadata_value) = metadata else {
|
||||
return;
|
||||
};
|
||||
|
||||
metadata_value.content_item_kinds = None;
|
||||
if metadata_value == &InternalChatMessageMetadataPassthrough::default() {
|
||||
*metadata = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn internal_chat_message_metadata_passthrough(
|
||||
&self,
|
||||
) -> Option<&InternalChatMessageMetadataPassthrough> {
|
||||
|
||||
Reference in New Issue
Block a user