mirror of
https://github.com/openai/codex.git
synced 2026-05-23 12:34:25 +00:00
preserve typed outputs for post-tool rewrites
This commit is contained in:
@@ -179,6 +179,7 @@ impl ToolCallRuntime {
|
||||
message: Self::abort_message(call, secs),
|
||||
}),
|
||||
post_tool_use_payload: None,
|
||||
model_visible_override: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,7 @@ pub(crate) struct AnyToolResult {
|
||||
pub(crate) payload: ToolPayload,
|
||||
pub(crate) result: Box<dyn ToolOutput>,
|
||||
pub(crate) post_tool_use_payload: Option<PostToolUsePayload>,
|
||||
pub(crate) model_visible_override: Option<FunctionToolOutput>,
|
||||
}
|
||||
|
||||
impl AnyToolResult {
|
||||
@@ -117,9 +118,13 @@ impl AnyToolResult {
|
||||
call_id,
|
||||
payload,
|
||||
result,
|
||||
model_visible_override,
|
||||
..
|
||||
} = self;
|
||||
result.to_response_item(&call_id, &payload)
|
||||
model_visible_override.map_or_else(
|
||||
|| result.to_response_item(&call_id, &payload),
|
||||
|override_output| override_output.to_response_item(&call_id, &payload),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn code_mode_result(self) -> serde_json::Value {
|
||||
@@ -207,6 +212,7 @@ where
|
||||
payload,
|
||||
result: Box::new(output),
|
||||
post_tool_use_payload,
|
||||
model_visible_override: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -481,7 +487,7 @@ impl ToolRegistry {
|
||||
} else if let Some(updated_tool_output) = &outcome.updated_tool_output {
|
||||
let mut guard = response_cell.lock().await;
|
||||
if let Some(result) = guard.as_mut() {
|
||||
result.result = Box::new(FunctionToolOutput::from_text(
|
||||
result.model_visible_override = Some(FunctionToolOutput::from_text(
|
||||
post_tool_use_output_to_model_text(updated_tool_output),
|
||||
Some(true),
|
||||
));
|
||||
@@ -511,6 +517,10 @@ impl ToolRegistry {
|
||||
&result.call_id,
|
||||
&result.payload,
|
||||
result.result.as_ref(),
|
||||
result
|
||||
.model_visible_override
|
||||
.as_ref()
|
||||
.map(|override_output| override_output as &dyn ToolOutput),
|
||||
);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
use super::*;
|
||||
use crate::tools::context::McpToolOutput;
|
||||
use codex_protocol::mcp::CallToolResult;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Default)]
|
||||
struct TestHandler;
|
||||
@@ -53,3 +57,55 @@ fn handler_looks_up_namespaced_aliases_explicitly() {
|
||||
.is_some_and(|handler| Arc::ptr_eq(handler, &namespaced_handler))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn model_visible_override_does_not_replace_typed_tool_output() {
|
||||
let result = mcp_result_with_model_visible_override();
|
||||
|
||||
match result.into_response() {
|
||||
ResponseInputItem::FunctionCallOutput { call_id, output } => {
|
||||
assert_eq!(call_id, "mcp-call-1");
|
||||
assert_eq!(output.body.to_text().as_deref(), Some("[redacted]"));
|
||||
}
|
||||
other => panic!("expected FunctionCallOutput, got {other:?}"),
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
mcp_result_with_model_visible_override().code_mode_result(),
|
||||
json!({
|
||||
"content": [],
|
||||
"structuredContent": {
|
||||
"echo": "original",
|
||||
},
|
||||
"isError": false,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
fn mcp_result_with_model_visible_override() -> AnyToolResult {
|
||||
AnyToolResult {
|
||||
call_id: "mcp-call-1".to_string(),
|
||||
payload: ToolPayload::Mcp {
|
||||
server: "memory".to_string(),
|
||||
tool: "lookup".to_string(),
|
||||
raw_arguments: "{}".to_string(),
|
||||
},
|
||||
result: Box::new(McpToolOutput {
|
||||
result: CallToolResult {
|
||||
content: Vec::new(),
|
||||
structured_content: Some(json!({ "echo": "original" })),
|
||||
is_error: Some(false),
|
||||
meta: None,
|
||||
},
|
||||
tool_input: json!({}),
|
||||
wall_time: Duration::ZERO,
|
||||
original_image_detail_supported: false,
|
||||
truncation_policy: codex_utils_output_truncation::TruncationPolicy::Bytes(1024),
|
||||
}),
|
||||
post_tool_use_payload: None,
|
||||
model_visible_override: Some(FunctionToolOutput::from_text(
|
||||
"[redacted]".to_string(),
|
||||
Some(true),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,12 +37,14 @@ impl ToolDispatchTrace {
|
||||
call_id: &str,
|
||||
payload: &ToolPayload,
|
||||
result: &dyn ToolOutput,
|
||||
model_visible_override: Option<&dyn ToolOutput>,
|
||||
) {
|
||||
if !self.context.is_enabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(result_payload) = tool_dispatch_result(invocation, call_id, payload, result)
|
||||
let Some(result_payload) =
|
||||
tool_dispatch_result(invocation, call_id, payload, result, model_visible_override)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
@@ -89,10 +91,13 @@ fn tool_dispatch_result(
|
||||
call_id: &str,
|
||||
payload: &ToolPayload,
|
||||
result: &dyn ToolOutput,
|
||||
model_visible_override: Option<&dyn ToolOutput>,
|
||||
) -> Option<ToolDispatchResult> {
|
||||
match invocation.source {
|
||||
ToolCallSource::Direct => Some(ToolDispatchResult::DirectResponse {
|
||||
response_item: result.to_response_item(call_id, payload),
|
||||
response_item: model_visible_override
|
||||
.unwrap_or(result)
|
||||
.to_response_item(call_id, payload),
|
||||
}),
|
||||
ToolCallSource::CodeMode { .. } => Some(ToolDispatchResult::CodeModeResponse {
|
||||
value: result.code_mode_result(payload),
|
||||
|
||||
Reference in New Issue
Block a user