mirror of
https://github.com/openai/codex.git
synced 2026-05-02 04:11:39 +03:00
Reuse McpToolOutput in McpHandler (#14229)
We already have a type to represent the MCP tool output, reuse it instead of the custom McpHandlerOutput
This commit is contained in:
committed by
Michael Bolin
parent
52a7f4b68b
commit
c4d35084f5
@@ -206,7 +206,7 @@ pub enum ResponseInputItem {
|
||||
},
|
||||
McpToolCallOutput {
|
||||
call_id: String,
|
||||
result: Result<CallToolResult, String>,
|
||||
output: McpToolOutput,
|
||||
},
|
||||
CustomToolCallOutput {
|
||||
call_id: String,
|
||||
@@ -843,14 +843,8 @@ impl From<ResponseInputItem> for ResponseItem {
|
||||
ResponseInputItem::FunctionCallOutput { call_id, output } => {
|
||||
Self::FunctionCallOutput { call_id, output }
|
||||
}
|
||||
ResponseInputItem::McpToolCallOutput { call_id, result } => {
|
||||
let output = match result {
|
||||
Ok(result) => FunctionCallOutputPayload::from(&result),
|
||||
Err(tool_call_err) => FunctionCallOutputPayload {
|
||||
body: FunctionCallOutputBody::Text(format!("err: {tool_call_err:?}")),
|
||||
success: Some(false),
|
||||
},
|
||||
};
|
||||
ResponseInputItem::McpToolCallOutput { call_id, output } => {
|
||||
let output = output.into_function_call_output_payload();
|
||||
Self::FunctionCallOutput { call_id, output }
|
||||
}
|
||||
ResponseInputItem::CustomToolCallOutput { call_id, output } => {
|
||||
@@ -1190,25 +1184,59 @@ impl<'de> Deserialize<'de> for FunctionCallOutputPayload {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&CallToolResult> for FunctionCallOutputPayload {
|
||||
fn from(call_tool_result: &CallToolResult) -> Self {
|
||||
let CallToolResult {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct McpToolOutput {
|
||||
pub content: Vec<serde_json::Value>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub structured_content: Option<serde_json::Value>,
|
||||
pub success: bool,
|
||||
}
|
||||
|
||||
impl McpToolOutput {
|
||||
pub fn from_result(result: Result<CallToolResult, String>) -> Self {
|
||||
match result {
|
||||
Ok(result) => Self::from(&result),
|
||||
Err(error) => Self::from_error_text(error),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_error_text(text: String) -> Self {
|
||||
Self {
|
||||
content: vec![serde_json::json!({
|
||||
"type": "text",
|
||||
"text": text,
|
||||
})],
|
||||
structured_content: None,
|
||||
success: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_call_tool_result(self) -> CallToolResult {
|
||||
let Self {
|
||||
content,
|
||||
structured_content,
|
||||
is_error,
|
||||
meta: _,
|
||||
} = call_tool_result;
|
||||
success,
|
||||
} = self;
|
||||
|
||||
let is_success = is_error != &Some(true);
|
||||
CallToolResult {
|
||||
content,
|
||||
structured_content,
|
||||
is_error: Some(!success),
|
||||
meta: None,
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(structured_content) = structured_content
|
||||
pub fn as_function_call_output_payload(&self) -> FunctionCallOutputPayload {
|
||||
if let Some(structured_content) = &self.structured_content
|
||||
&& !structured_content.is_null()
|
||||
{
|
||||
match serde_json::to_string(structured_content) {
|
||||
Ok(serialized_structured_content) => {
|
||||
return FunctionCallOutputPayload {
|
||||
body: FunctionCallOutputBody::Text(serialized_structured_content),
|
||||
success: Some(is_success),
|
||||
success: Some(self.success),
|
||||
};
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -1220,7 +1248,7 @@ impl From<&CallToolResult> for FunctionCallOutputPayload {
|
||||
}
|
||||
}
|
||||
|
||||
let serialized_content = match serde_json::to_string(content) {
|
||||
let serialized_content = match serde_json::to_string(&self.content) {
|
||||
Ok(serialized_content) => serialized_content,
|
||||
Err(err) => {
|
||||
return FunctionCallOutputPayload {
|
||||
@@ -1230,7 +1258,7 @@ impl From<&CallToolResult> for FunctionCallOutputPayload {
|
||||
}
|
||||
};
|
||||
|
||||
let content_items = convert_mcp_content_to_items(content);
|
||||
let content_items = convert_mcp_content_to_items(&self.content);
|
||||
|
||||
let body = match content_items {
|
||||
Some(content_items) => FunctionCallOutputBody::ContentItems(content_items),
|
||||
@@ -1239,7 +1267,28 @@ impl From<&CallToolResult> for FunctionCallOutputPayload {
|
||||
|
||||
FunctionCallOutputPayload {
|
||||
body,
|
||||
success: Some(is_success),
|
||||
success: Some(self.success),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_function_call_output_payload(self) -> FunctionCallOutputPayload {
|
||||
self.as_function_call_output_payload()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&CallToolResult> for McpToolOutput {
|
||||
fn from(call_tool_result: &CallToolResult) -> Self {
|
||||
let CallToolResult {
|
||||
content,
|
||||
structured_content,
|
||||
is_error,
|
||||
meta: _,
|
||||
} = call_tool_result;
|
||||
|
||||
Self {
|
||||
content: content.clone(),
|
||||
structured_content: structured_content.clone(),
|
||||
success: is_error != &Some(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1833,7 +1882,7 @@ mod tests {
|
||||
meta: None,
|
||||
};
|
||||
|
||||
let payload = FunctionCallOutputPayload::from(&call_tool_result);
|
||||
let payload = McpToolOutput::from(&call_tool_result).into_function_call_output_payload();
|
||||
assert_eq!(payload.success, Some(true));
|
||||
let Some(items) = payload.content_items() else {
|
||||
panic!("expected content items");
|
||||
@@ -1900,7 +1949,7 @@ mod tests {
|
||||
meta: None,
|
||||
};
|
||||
|
||||
let payload = FunctionCallOutputPayload::from(&call_tool_result);
|
||||
let payload = McpToolOutput::from(&call_tool_result).into_function_call_output_payload();
|
||||
let Some(items) = payload.content_items() else {
|
||||
panic!("expected content items");
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user