Download codex-rs/core/src/tools/context_tests.rs from SaylorTwift/codex: direct link, hf CLI and curl.
- Browser
- Download file 18.7 kB
-
https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/core/src/tools/context_tests.rs
- Command line
-
hf download hf://SaylorTwift/codex/codex-rs/core/src/tools/context_tests.rs
-
curl -L -o context_tests.rs https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/core/src/tools/context_tests.rs
18.7 kB
| use super::*; | |
| use codex_protocol::models::DEFAULT_IMAGE_DETAIL; | |
| use codex_protocol::models::ImageReference; | |
| use codex_protocol::models::SearchToolCallParams; | |
| use core_test_support::assert_regex_match; | |
| use pretty_assertions::assert_eq; | |
| use serde_json::json; | |
| fn custom_tool_calls_should_roundtrip_as_custom_outputs() { | |
| let payload = ToolPayload::Custom { | |
| input: "patch".to_string(), | |
| }; | |
| let response = FunctionToolOutput::from_text("patched".to_string(), Some(true)) | |
| .to_response_item("call-42", &payload); | |
| match response { | |
| ResponseInputItem::CustomToolCallOutput { | |
| call_id, output, .. | |
| } => { | |
| assert_eq!(call_id, "call-42"); | |
| assert_eq!(output.content_items(), None); | |
| assert_eq!(output.body.to_text().as_deref(), Some("patched")); | |
| assert_eq!(output.success, Some(true)); | |
| } | |
| other => panic!("expected CustomToolCallOutput, got {other:?}"), | |
| } | |
| } | |
| fn function_payloads_remain_function_outputs() { | |
| let payload = ToolPayload::Function { | |
| arguments: "{}".to_string(), | |
| }; | |
| let response = FunctionToolOutput::from_text("ok".to_string(), Some(true)) | |
| .to_response_item("fn-1", &payload); | |
| match response { | |
| ResponseInputItem::FunctionCallOutput { call_id, output } => { | |
| assert_eq!(call_id, "fn-1"); | |
| assert_eq!(output.content_items(), None); | |
| assert_eq!(output.body.to_text().as_deref(), Some("ok")); | |
| assert_eq!(output.success, Some(true)); | |
| } | |
| other => panic!("expected FunctionCallOutput, got {other:?}"), | |
| } | |
| } | |
| fn mcp_code_mode_result_omits_private_metadata() { | |
| let output = CallToolResult { | |
| content: vec![serde_json::json!({ | |
| "type": "text", | |
| "text": "ignored", | |
| })], | |
| structured_content: Some(serde_json::json!({ | |
| "threadId": "thread_123", | |
| "content": "done", | |
| })), | |
| is_error: Some(false), | |
| meta: Some(serde_json::json!({ | |
| "source": "mcp", | |
| })), | |
| }; | |
| let result = output.code_mode_result(&ToolPayload::Function { | |
| arguments: "{}".to_string(), | |
| }); | |
| assert_eq!( | |
| result, | |
| serde_json::json!({ | |
| "content": [{ | |
| "type": "text", | |
| "text": "ignored", | |
| }], | |
| "structuredContent": { | |
| "threadId": "thread_123", | |
| "content": "done", | |
| }, | |
| "isError": false, | |
| }) | |
| ); | |
| assert_eq!(output.meta, Some(serde_json::json!({ "source": "mcp" }))); | |
| } | |
| fn mcp_tool_output_response_item_includes_wall_time() { | |
| let output = McpToolOutput { | |
| result: CallToolResult { | |
| content: vec![serde_json::json!({ | |
| "type": "text", | |
| "text": "done", | |
| })], | |
| structured_content: None, | |
| is_error: Some(false), | |
| meta: None, | |
| }, | |
| tool_input: json!({}), | |
| result_metadata_capture_allowed: false, | |
| wall_time: std::time::Duration::from_millis(1250), | |
| original_image_detail_supported: false, | |
| truncation_policy: TruncationPolicy::Bytes(1024), | |
| }; | |
| let response = output.to_response_item( | |
| "mcp-call-1", | |
| &ToolPayload::Function { | |
| arguments: "{}".to_string(), | |
| }, | |
| ); | |
| assert_eq!( | |
| response, | |
| ResponseInputItem::FunctionCallOutput { | |
| call_id: "mcp-call-1".to_string(), | |
| output: FunctionCallOutputPayload { | |
| body: FunctionCallOutputBody::ContentItems(vec![ | |
| FunctionCallOutputContentItem::InputText { | |
| text: "Wall time: 1.2500 seconds\nOutput:".to_string(), | |
| }, | |
| FunctionCallOutputContentItem::InputText { | |
| text: "done".to_string(), | |
| }, | |
| ]), | |
| success: Some(true), | |
| }, | |
| } | |
| ); | |
| } | |
| fn mcp_tool_output_response_item_truncates_large_structured_content() { | |
| let output = McpToolOutput { | |
| result: CallToolResult { | |
| content: vec![serde_json::json!({ | |
| "type": "text", | |
| "text": "ignored when structured content is present", | |
| })], | |
| structured_content: Some(serde_json::json!({ | |
| "items": "large structured value ".repeat(1_000), | |
| })), | |
| is_error: Some(false), | |
| meta: None, | |
| }, | |
| tool_input: json!({}), | |
| result_metadata_capture_allowed: false, | |
| wall_time: std::time::Duration::from_millis(1250), | |
| original_image_detail_supported: false, | |
| truncation_policy: TruncationPolicy::Bytes(128), | |
| }; | |
| assert_eq!( | |
| output.log_output(), | |
| format!( | |
| "Wall time: 1.2500 seconds\nOutput:\n{}", | |
| json!({"items": "large structured value ".repeat(1_000)}) | |
| ) | |
| ); | |
| let response = output.to_response_item( | |
| "mcp-call-large", | |
| &ToolPayload::Function { | |
| arguments: "{}".to_string(), | |
| }, | |
| ); | |
| match response { | |
| ResponseInputItem::FunctionCallOutput { call_id, output } => { | |
| assert_eq!(call_id, "mcp-call-large"); | |
| assert_eq!(output.success, Some(true)); | |
| let text = output | |
| .body | |
| .to_text() | |
| .expect("MCP output should serialize as text"); | |
| assert!(text.starts_with("Wall time: 1.2500 seconds\nOutput:\n")); | |
| assert!(text.contains("chars truncated")); | |
| assert!(!text.contains("ignored when structured content is present")); | |
| } | |
| other => panic!("expected FunctionCallOutput, got {other:?}"), | |
| } | |
| } | |
| fn mcp_tool_output_response_item_preserves_content_items() { | |
| let image_url = "data:image/png;base64,AAA"; | |
| let output = McpToolOutput { | |
| result: CallToolResult { | |
| content: vec![serde_json::json!({ | |
| "type": "image", | |
| "mimeType": "image/png", | |
| "data": "AAA", | |
| })], | |
| structured_content: None, | |
| is_error: Some(false), | |
| meta: None, | |
| }, | |
| tool_input: json!({}), | |
| result_metadata_capture_allowed: false, | |
| wall_time: std::time::Duration::from_millis(500), | |
| original_image_detail_supported: false, | |
| truncation_policy: TruncationPolicy::Bytes(1024), | |
| }; | |
| let response = output.to_response_item( | |
| "mcp-call-2", | |
| &ToolPayload::Function { | |
| arguments: "{}".to_string(), | |
| }, | |
| ); | |
| match response { | |
| ResponseInputItem::FunctionCallOutput { output, .. } => { | |
| assert_eq!( | |
| output.content_items(), | |
| Some( | |
| vec![ | |
| FunctionCallOutputContentItem::InputText { | |
| text: "Wall time: 0.5000 seconds\nOutput:".to_string(), | |
| }, | |
| FunctionCallOutputContentItem::InputImage { | |
| image: ImageReference::Inline { | |
| image_url: image_url.to_string() | |
| }, | |
| detail: Some(DEFAULT_IMAGE_DETAIL), | |
| }, | |
| ] | |
| .as_slice() | |
| ) | |
| ); | |
| assert_eq!( | |
| output.body.to_text().as_deref(), | |
| Some("Wall time: 0.5000 seconds\nOutput:") | |
| ); | |
| } | |
| other => panic!("expected FunctionCallOutput, got {other:?}"), | |
| } | |
| } | |
| fn mcp_tool_output_code_mode_result_preserves_content_without_private_metadata( | |
| truncation_policy: TruncationPolicy, | |
| ) { | |
| let large_content = "large structured value ".repeat(1_000); | |
| let output = McpToolOutput { | |
| result: CallToolResult { | |
| content: vec![serde_json::json!({ | |
| "type": "text", | |
| "text": "ignored", | |
| })], | |
| structured_content: Some(serde_json::json!({ | |
| "content": large_content, | |
| })), | |
| is_error: Some(false), | |
| meta: Some(serde_json::json!({ | |
| "hive_dispatch_id": "private-dispatch-id", | |
| })), | |
| }, | |
| tool_input: json!({}), | |
| result_metadata_capture_allowed: false, | |
| wall_time: std::time::Duration::from_millis(1250), | |
| original_image_detail_supported: false, | |
| truncation_policy, | |
| }; | |
| let payload = ToolPayload::Function { | |
| arguments: "{}".to_string(), | |
| }; | |
| let result = output.code_mode_result(&payload); | |
| assert_eq!( | |
| result, | |
| serde_json::json!({ | |
| "content": [{ | |
| "type": "text", | |
| "text": "ignored", | |
| }], | |
| "structuredContent": { | |
| "content": "large structured value ".repeat(1_000), | |
| }, | |
| "isError": false, | |
| }) | |
| ); | |
| assert_eq!( | |
| output.result.meta, | |
| Some(serde_json::json!({ "hive_dispatch_id": "private-dispatch-id" })) | |
| ); | |
| } | |
| fn custom_tool_calls_can_derive_text_from_content_items() { | |
| let payload = ToolPayload::Custom { | |
| input: "patch".to_string(), | |
| }; | |
| let response = FunctionToolOutput::from_content( | |
| vec![ | |
| FunctionCallOutputContentItem::InputText { | |
| text: "line 1".to_string(), | |
| }, | |
| FunctionCallOutputContentItem::InputImage { | |
| image: ImageReference::Inline { | |
| image_url: "data:image/png;base64,AAA".to_string(), | |
| }, | |
| detail: Some(DEFAULT_IMAGE_DETAIL), | |
| }, | |
| FunctionCallOutputContentItem::InputText { | |
| text: "line 2".to_string(), | |
| }, | |
| ], | |
| Some(true), | |
| ) | |
| .to_response_item("call-99", &payload); | |
| match response { | |
| ResponseInputItem::CustomToolCallOutput { | |
| call_id, output, .. | |
| } => { | |
| let expected = vec![ | |
| FunctionCallOutputContentItem::InputText { | |
| text: "line 1".to_string(), | |
| }, | |
| FunctionCallOutputContentItem::InputImage { | |
| image: ImageReference::Inline { | |
| image_url: "data:image/png;base64,AAA".to_string(), | |
| }, | |
| detail: Some(DEFAULT_IMAGE_DETAIL), | |
| }, | |
| FunctionCallOutputContentItem::InputText { | |
| text: "line 2".to_string(), | |
| }, | |
| ]; | |
| assert_eq!(call_id, "call-99"); | |
| assert_eq!(output.content_items(), Some(expected.as_slice())); | |
| assert_eq!(output.body.to_text().as_deref(), Some("line 1\nline 2")); | |
| assert_eq!(output.success, Some(true)); | |
| } | |
| other => panic!("expected CustomToolCallOutput, got {other:?}"), | |
| } | |
| } | |
| fn tool_search_payloads_roundtrip_as_tool_search_outputs() { | |
| let payload = ToolPayload::ToolSearch { | |
| arguments: SearchToolCallParams { | |
| query: "calendar".to_string(), | |
| limit: None, | |
| }, | |
| }; | |
| let response = ToolSearchOutput { | |
| tools: vec![LoadableToolSpec::Function(codex_tools::ResponsesApiTool { | |
| name: "create_event".to_string(), | |
| description: String::new(), | |
| strict: false, | |
| defer_loading: Some(true), | |
| parameters: codex_tools::JsonSchema::object( | |
| /*properties*/ Default::default(), | |
| /*required*/ None, | |
| /*additional_properties*/ None, | |
| ), | |
| output_schema: None, | |
| })], | |
| } | |
| .to_response_item("search-1", &payload); | |
| match response { | |
| ResponseInputItem::ToolSearchOutput { | |
| call_id, | |
| status, | |
| execution, | |
| tools, | |
| } => { | |
| assert_eq!(call_id, "search-1"); | |
| assert_eq!(status, "completed"); | |
| assert_eq!(execution, "client"); | |
| assert_eq!( | |
| tools, | |
| vec![json!({ | |
| "type": "function", | |
| "name": "create_event", | |
| "description": "", | |
| "strict": false, | |
| "defer_loading": true, | |
| "parameters": { | |
| "type": "object", | |
| "properties": {} | |
| } | |
| })] | |
| ); | |
| } | |
| other => panic!("expected ToolSearchOutput, got {other:?}"), | |
| } | |
| } | |
| fn log_preview_uses_content_items_when_plain_text_is_missing() { | |
| let output = FunctionToolOutput::from_content( | |
| vec![FunctionCallOutputContentItem::InputText { | |
| text: "preview".to_string(), | |
| }], | |
| Some(true), | |
| ); | |
| assert_eq!(output.log_output(), "preview"); | |
| assert_eq!( | |
| function_call_output_content_items_to_text(&output.body), | |
| Some("preview".to_string()) | |
| ); | |
| } | |
| fn exec_command_tool_output_formats_truncated_response() { | |
| let payload = ToolPayload::Function { | |
| arguments: "{}".to_string(), | |
| }; | |
| let output = ExecCommandToolOutput { | |
| event_call_id: "call-42".to_string(), | |
| chunk_id: "abc123".to_string(), | |
| wall_time: std::time::Duration::from_millis(1250), | |
| raw_output: b"token one token two token three token four token five".to_vec(), | |
| truncation_policy: TruncationPolicy::Tokens(10_000), | |
| max_output_tokens: Some(4), | |
| process_id: None, | |
| exit_code: Some(0), | |
| original_token_count: Some(10), | |
| output_omitted_bytes: None, | |
| hook_command: None, | |
| }; | |
| assert_eq!( | |
| output.log_output(), | |
| "Chunk ID: abc123\nWall time: 1.2500 seconds\nProcess exited with code 0\nOriginal token count: 10\nOutput:\ntoken one token two token three token four token five" | |
| ); | |
| let response = output.to_response_item("call-42", &payload); | |
| match response { | |
| ResponseInputItem::FunctionCallOutput { call_id, output } => { | |
| assert_eq!(call_id, "call-42"); | |
| assert_eq!(output.success, Some(true)); | |
| let text = output | |
| .body | |
| .to_text() | |
| .expect("exec output should serialize as text"); | |
| assert_regex_match( | |
| r#"(?sx) | |
| ^Chunk\ ID:\ abc123 | |
| \nWall\ time:\ \d+\.\d{4}\ seconds | |
| \nProcess\ exited\ with\ code\ 0 | |
| \nOriginal\ token\ count:\ 10 | |
| \nOutput: | |
| \n.*tokens\ truncated.* | |
| $"#, | |
| &text, | |
| ); | |
| } | |
| other => panic!("expected FunctionCallOutput, got {other:?}"), | |
| } | |
| } | |
| fn exec_command_tool_output_reserves_metadata_budget_and_preserves_policy_units() { | |
| let payload = ToolPayload::Function { | |
| arguments: "{}".to_string(), | |
| }; | |
| let raw_output = (1..=150) | |
| .map(|line| format!("{line}\n")) | |
| .collect::<String>() | |
| .into_bytes(); | |
| for (policy, marker) in [ | |
| (TruncationPolicy::Bytes(200), "chars truncated"), | |
| (TruncationPolicy::Tokens(50), "tokens truncated"), | |
| ] { | |
| let response = ExecCommandToolOutput { | |
| event_call_id: "call-42".to_string(), | |
| chunk_id: "abc123".to_string(), | |
| wall_time: std::time::Duration::from_millis(/*millis*/ 1250), | |
| raw_output: raw_output.clone(), | |
| truncation_policy: policy, | |
| max_output_tokens: None, | |
| process_id: None, | |
| exit_code: Some(0), | |
| original_token_count: Some(123), | |
| output_omitted_bytes: None, | |
| hook_command: None, | |
| } | |
| .to_response_item("call-42", &payload); | |
| let ResponseInputItem::FunctionCallOutput { output, .. } = response else { | |
| panic!("expected FunctionCallOutput"); | |
| }; | |
| let text = output | |
| .body | |
| .to_text() | |
| .expect("exec output should serialize as text"); | |
| assert!(text.len() <= (policy * 1.2).byte_budget()); | |
| assert_eq!(text.matches(marker).count(), 1); | |
| assert!(text.contains("Original token count: 123")); | |
| assert!(text.contains("Total output lines: 150")); | |
| assert!(text.contains("\n1\n2\n3\n")); | |
| assert!(text.ends_with("149\n150\n")); | |
| } | |
| } | |
| fn exec_command_tool_output_preserves_omission_metadata_when_truncated() { | |
| let payload = ToolPayload::Function { | |
| arguments: "{}".to_string(), | |
| }; | |
| let marker = format_output_omission_marker(/*omitted_bytes*/ 123_456); | |
| let raw_output = format!( | |
| "HEAD-{}\n{marker}\nTAIL-{}", | |
| "a".repeat(/*n*/ 100), | |
| "z".repeat(/*n*/ 100) | |
| ) | |
| .into_bytes(); | |
| let mut output = ExecCommandToolOutput { | |
| event_call_id: "call-omitted".to_string(), | |
| chunk_id: "abc123".to_string(), | |
| wall_time: std::time::Duration::from_millis(/*millis*/ 1250), | |
| raw_output, | |
| truncation_policy: TruncationPolicy::Tokens(10_000), | |
| max_output_tokens: Some(4), | |
| process_id: None, | |
| exit_code: Some(0), | |
| original_token_count: Some(42_000), | |
| output_omitted_bytes: NonZeroUsize::new(/*n*/ 123_456), | |
| hook_command: None, | |
| }; | |
| let expected_header = "Chunk ID: abc123\nWall time: 1.2500 seconds\nProcess exited with code 0\nOriginal token count: 42000\nOutput:\n"; | |
| assert_eq!( | |
| output.log_output(), | |
| format!( | |
| "{expected_header}{}", | |
| String::from_utf8_lossy(&output.raw_output) | |
| ) | |
| ); | |
| let response = output.to_response_item("call-omitted", &payload); | |
| // Collection may report omitted bytes without including the marker in its text. | |
| output.raw_output = b"remaining output".to_vec(); | |
| assert_eq!( | |
| output.log_output(), | |
| format!("{expected_header}{marker}\nremaining output") | |
| ); | |
| let ResponseInputItem::FunctionCallOutput { output, .. } = response else { | |
| panic!("expected FunctionCallOutput"); | |
| }; | |
| let text = output | |
| .body | |
| .to_text() | |
| .expect("exec output should serialize as text"); | |
| assert!(text.contains("Original token count: 42000")); | |
| assert!(text.contains("Warning: truncated output (original token count: 42000)")); | |
| assert_eq!(text.matches(&marker).count(), 1); | |
| } | |