mirror of
https://github.com/openai/codex.git
synced 2026-11-02 19:43:24 +00:00
mandate metadata_id and use constructor
This commit is contained in:
@@ -1964,10 +1964,7 @@
|
||||
"properties": {
|
||||
"metadata_id": {
|
||||
"description": "Client-visible metadata ID generated by Codex for this item.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
"type": "string"
|
||||
},
|
||||
"user_message_type": {
|
||||
"anyOf": [
|
||||
@@ -1980,6 +1977,9 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metadata_id"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ResponsesApiWebSearchAction": {
|
||||
|
||||
@@ -10687,10 +10687,7 @@
|
||||
"properties": {
|
||||
"metadata_id": {
|
||||
"description": "Client-visible metadata ID generated by Codex for this item.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
"type": "string"
|
||||
},
|
||||
"user_message_type": {
|
||||
"anyOf": [
|
||||
@@ -10703,6 +10700,9 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metadata_id"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ResponsesApiWebSearchAction": {
|
||||
|
||||
@@ -7455,10 +7455,7 @@
|
||||
"properties": {
|
||||
"metadata_id": {
|
||||
"description": "Client-visible metadata ID generated by Codex for this item.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
"type": "string"
|
||||
},
|
||||
"user_message_type": {
|
||||
"anyOf": [
|
||||
@@ -7471,6 +7468,9 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metadata_id"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ResponsesApiWebSearchAction": {
|
||||
|
||||
+4
-4
@@ -812,10 +812,7 @@
|
||||
"properties": {
|
||||
"metadata_id": {
|
||||
"description": "Client-visible metadata ID generated by Codex for this item.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
"type": "string"
|
||||
},
|
||||
"user_message_type": {
|
||||
"anyOf": [
|
||||
@@ -828,6 +825,9 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metadata_id"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ResponsesApiWebSearchAction": {
|
||||
|
||||
@@ -878,10 +878,7 @@
|
||||
"properties": {
|
||||
"metadata_id": {
|
||||
"description": "Client-visible metadata ID generated by Codex for this item.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
"type": "string"
|
||||
},
|
||||
"user_message_type": {
|
||||
"anyOf": [
|
||||
@@ -894,6 +891,9 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metadata_id"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ResponsesApiWebSearchAction": {
|
||||
|
||||
@@ -7,4 +7,4 @@ export type ResponseItemMessageMetadata = { user_message_type?: UserMessageType,
|
||||
/**
|
||||
* Client-visible metadata ID generated by Codex for this item.
|
||||
*/
|
||||
metadata_id?: string, };
|
||||
metadata_id: string, };
|
||||
|
||||
@@ -202,7 +202,7 @@ fn reserializes_shell_outputs_for_function_and_custom_tool_calls() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn formatted_input_does_not_generate_message_metadata_id_when_disabled() {
|
||||
fn formatted_input_preserves_message_metadata_id_when_disabled() {
|
||||
let prompt = Prompt {
|
||||
input: vec![ResponseItem::Message {
|
||||
id: Some("msg_123".to_string()),
|
||||
@@ -212,7 +212,7 @@ fn formatted_input_does_not_generate_message_metadata_id_when_disabled() {
|
||||
}],
|
||||
metadata: Some(ResponseItemMessageMetadata {
|
||||
user_message_type: Some(UserMessageType::Prompt),
|
||||
metadata_id: None,
|
||||
metadata_id: "2585a800-7d93-4f52-8648-d9cb39f413d2".to_string(),
|
||||
}),
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
@@ -227,7 +227,10 @@ fn formatted_input_does_not_generate_message_metadata_id_when_disabled() {
|
||||
ResponseItem::Message { metadata, .. } => {
|
||||
let metadata = metadata.as_ref().expect("metadata should be present");
|
||||
assert_eq!(metadata.user_message_type, Some(UserMessageType::Prompt));
|
||||
assert_eq!(metadata.metadata_id, None);
|
||||
assert_eq!(
|
||||
metadata.metadata_id,
|
||||
"2585a800-7d93-4f52-8648-d9cb39f413d2".to_string()
|
||||
);
|
||||
}
|
||||
other => panic!("expected message item, got {other:?}"),
|
||||
}
|
||||
@@ -244,7 +247,7 @@ fn formatted_input_preserves_existing_message_metadata_id() {
|
||||
}],
|
||||
metadata: Some(ResponseItemMessageMetadata {
|
||||
user_message_type: Some(UserMessageType::Prompt),
|
||||
metadata_id: Some("2585a800-7d93-4f52-8648-d9cb39f413d2".to_string()),
|
||||
metadata_id: "2585a800-7d93-4f52-8648-d9cb39f413d2".to_string(),
|
||||
}),
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
@@ -260,7 +263,7 @@ fn formatted_input_preserves_existing_message_metadata_id() {
|
||||
let metadata = metadata.as_ref().expect("metadata should be present");
|
||||
assert_eq!(metadata.user_message_type, Some(UserMessageType::Prompt));
|
||||
assert_eq!(
|
||||
metadata.metadata_id.as_deref(),
|
||||
Some(metadata.metadata_id.as_str()),
|
||||
Some("2585a800-7d93-4f52-8648-d9cb39f413d2")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -330,6 +330,7 @@ use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::DeveloperInstructions;
|
||||
use codex_protocol::models::ResponseInputItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::models::ResponseItemMessageMetadata;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
use codex_protocol::protocol::CodexErrorInfo;
|
||||
use codex_protocol::protocol::InitialHistory;
|
||||
@@ -3298,7 +3299,26 @@ impl Session {
|
||||
items
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(ResponseItem::with_generated_metadata_id)
|
||||
.map(|item| match item {
|
||||
ResponseItem::Message {
|
||||
id,
|
||||
role,
|
||||
content,
|
||||
metadata,
|
||||
end_turn,
|
||||
phase,
|
||||
} => ResponseItem::Message {
|
||||
id,
|
||||
role,
|
||||
content,
|
||||
metadata: Some(
|
||||
metadata.unwrap_or_else(|| ResponseItemMessageMetadata::new(None)),
|
||||
),
|
||||
end_turn,
|
||||
phase,
|
||||
},
|
||||
other => other,
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
items.to_vec()
|
||||
|
||||
@@ -4438,7 +4438,7 @@ async fn record_into_history_generates_message_metadata_id_when_item_metadata_en
|
||||
|
||||
let metadata_id = metadata
|
||||
.as_ref()
|
||||
.and_then(|metadata| metadata.metadata_id.as_deref())
|
||||
.map(|metadata| metadata.metadata_id.as_str())
|
||||
.expect("metadata_id should be generated when item metadata is enabled");
|
||||
uuid::Uuid::parse_str(metadata_id).expect("metadata_id should be valid");
|
||||
}
|
||||
|
||||
@@ -269,54 +269,24 @@ pub enum UserMessageType {
|
||||
PromptQueued,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema, TS)]
|
||||
pub struct ResponseItemMessageMetadata {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub user_message_type: Option<UserMessageType>,
|
||||
/// Client-visible metadata ID generated by Codex for this item.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub metadata_id: Option<String>,
|
||||
pub metadata_id: String,
|
||||
}
|
||||
|
||||
impl ResponseItem {
|
||||
/// Ensures message metadata includes a generated metadata ID.
|
||||
pub fn with_generated_metadata_id(self) -> Self {
|
||||
match self {
|
||||
ResponseItem::Message {
|
||||
id,
|
||||
role,
|
||||
content,
|
||||
metadata,
|
||||
end_turn,
|
||||
phase,
|
||||
} => {
|
||||
let metadata = ensure_generated_metadata_id(metadata);
|
||||
ResponseItem::Message {
|
||||
id,
|
||||
role,
|
||||
content,
|
||||
metadata,
|
||||
end_turn,
|
||||
phase,
|
||||
}
|
||||
}
|
||||
other => other,
|
||||
impl ResponseItemMessageMetadata {
|
||||
pub fn new(user_message_type: Option<UserMessageType>) -> Self {
|
||||
Self {
|
||||
user_message_type,
|
||||
metadata_id: uuid::Uuid::new_v4().to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_generated_metadata_id(
|
||||
metadata: Option<ResponseItemMessageMetadata>,
|
||||
) -> Option<ResponseItemMessageMetadata> {
|
||||
let mut metadata = metadata.unwrap_or_default();
|
||||
if metadata.metadata_id.is_none() {
|
||||
metadata.metadata_id = Some(uuid::Uuid::new_v4().to_string());
|
||||
}
|
||||
Some(metadata)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema, TS)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum ContentItem {
|
||||
@@ -3020,49 +2990,29 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generates_metadata_id_for_message_items() -> Result<()> {
|
||||
let item = ResponseItem::Message {
|
||||
id: Some("msg_123".to_string()),
|
||||
role: "assistant".to_string(),
|
||||
content: vec![ContentItem::OutputText {
|
||||
text: "hello".to_string(),
|
||||
}],
|
||||
metadata: Some(ResponseItemMessageMetadata {
|
||||
user_message_type: Some(UserMessageType::Prompt),
|
||||
metadata_id: None,
|
||||
}),
|
||||
end_turn: None,
|
||||
phase: None,
|
||||
};
|
||||
|
||||
let surfaced = item.with_generated_metadata_id();
|
||||
let serialized = serde_json::to_value(&surfaced)?;
|
||||
let metadata = serialized
|
||||
.get("metadata")
|
||||
.and_then(serde_json::Value::as_object)
|
||||
.expect("metadata should be present");
|
||||
fn response_item_message_metadata_serializes_required_metadata_id() -> Result<()> {
|
||||
let metadata = ResponseItemMessageMetadata::new(Some(UserMessageType::Prompt));
|
||||
let serialized = serde_json::to_value(&metadata)?;
|
||||
let metadata = serialized.as_object().expect("metadata should be present");
|
||||
let metadata_id = metadata
|
||||
.get("metadata_id")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.expect("metadata_id should be present");
|
||||
|
||||
uuid::Uuid::parse_str(metadata_id).expect("metadata_id should be valid");
|
||||
|
||||
assert_eq!(serialized.get("type"), Some(&serde_json::json!("message")));
|
||||
assert_eq!(
|
||||
serialized.get("role"),
|
||||
Some(&serde_json::json!("assistant"))
|
||||
);
|
||||
assert_eq!(
|
||||
serialized.get("content"),
|
||||
Some(&serde_json::json!([{ "type": "output_text", "text": "hello" }]))
|
||||
);
|
||||
assert_eq!(
|
||||
metadata.get("user_message_type"),
|
||||
Some(&serde_json::json!("prompt"))
|
||||
);
|
||||
assert_ne!(metadata_id, "msg_123");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn response_item_message_metadata_new_generates_metadata_id() {
|
||||
let metadata = ResponseItemMessageMetadata::new(Some(UserMessageType::Prompt));
|
||||
|
||||
assert_eq!(metadata.user_message_type, Some(UserMessageType::Prompt));
|
||||
uuid::Uuid::parse_str(&metadata.metadata_id).expect("metadata_id should be valid");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user