This commit is contained in:
Douwe Osinga
2025-10-08 10:56:47 -04:00
parent 963f1f3e18
commit b2bc02862a
3 changed files with 35 additions and 32 deletions
+16 -23
View File
@@ -13,6 +13,12 @@ use utoipa::ToSchema;
use crate::conversation::tool_result_serde;
use crate::utils::sanitize_unicode_tags;
#[derive(ToSchema)]
pub enum ToolCallResult<T> {
Success { value: T },
Error { error: String },
}
/// Custom deserializer for MessageContent that sanitizes Unicode Tags in text content
fn deserialize_sanitized_content<'de, D>(deserializer: D) -> Result<Vec<MessageContent>, D::Error>
where
@@ -39,44 +45,28 @@ where
Ok(content)
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
#[serde(tag = "status", rename_all = "lowercase")]
pub enum ToolCallResult<T> {
Success { value: T },
Error { error: String },
}
impl<T> From<ToolResult<T>> for ToolCallResult<T> {
fn from(result: ToolResult<T>) -> Self {
match result {
Ok(value) => ToolCallResult::Success { value },
Err(error) => ToolCallResult::Error {
error: error.to_string(),
},
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[derive(ToSchema)]
pub struct ToolRequest {
pub id: String,
pub tool_call: ToolCallResult<CallToolRequestParam>,
#[serde(with = "tool_result_serde")]
#[schema(value_type = Object)]
pub tool_call: ToolResult<CallToolRequestParam>,
}
impl ToolRequest {
pub fn to_readable_string(&self) -> String {
match &self.tool_call {
ToolCallResult::Success { value } => {
Ok(tool_call) => {
format!(
"Tool: {}, Args: {}",
value.name,
serde_json::to_string_pretty(&value.arguments)
tool_call.name,
serde_json::to_string_pretty(&tool_call.arguments)
.unwrap_or_else(|_| "<<invalid json>>".to_string())
)
}
ToolCallResult::Error { error } => format!("Invalid tool call: {}", error),
Err(e) => format!("Invalid tool call: {}", e),
}
}
}
@@ -87,6 +77,7 @@ impl ToolRequest {
pub struct ToolResponse {
pub id: String,
#[serde(with = "tool_result_serde")]
#[schema(value_type = Object)]
pub tool_result: ToolResult<Vec<Content>>,
}
@@ -115,6 +106,8 @@ pub struct RedactedThinkingContent {
#[serde(rename_all = "camelCase")]
pub struct FrontendToolRequest {
pub id: String,
#[serde(with = "tool_result_serde")]
#[schema(value_type = Object)]
pub tool_call: ToolResult<CallToolRequestParam>,
}
+3 -3
View File
@@ -2843,7 +2843,7 @@
"type": "string"
},
"toolCall": {
"$ref": "#/components/schemas/ToolResult"
"type": "object"
}
}
},
@@ -4439,7 +4439,7 @@
"type": "string"
},
"toolCall": {
"$ref": "#/components/schemas/ToolResult"
"type": "object"
}
}
},
@@ -4454,7 +4454,7 @@
"type": "string"
},
"toolResult": {
"$ref": "#/components/schemas/ToolResult"
"type": "object"
}
}
},
@@ -12,7 +12,7 @@ import { ChevronRight, FlaskConical } from 'lucide-react';
import { TooltipWrapper } from './settings/providers/subcomponents/buttons/TooltipWrapper';
import MCPUIResourceRenderer from './MCPUIResourceRenderer';
import { isUIResource } from '@mcp-ui/client';
import { Content } from '../api';
import { Content, RawResource } from '../api';
interface ToolCallWithResponseProps {
isCancelledMessage: boolean;
@@ -23,6 +23,17 @@ interface ToolCallWithResponseProps {
append?: (value: string) => void; // Function to append messages to the chat
}
function getToolResultValue(toolResult: Record<string, unknown>): Content[] | null {
if ('value' in toolResult && Array.isArray(toolResult.value)) {
return toolResult.value as Content[];
}
return null;
}
function isRawResource(content: Content): content is RawResource {
return 'uri' in content && 'name' in content;
}
export default function ToolCallWithResponse({
isCancelledMessage,
toolRequest,
@@ -55,11 +66,10 @@ export default function ToolCallWithResponse({
</div>
{/* MCP UI — Inline */}
{toolResponse?.toolResult &&
Array.isArray((toolResponse.toolResult as any).value) &&
(toolResponse.toolResult as any).value.map((content: Content, index: number) => {
if (isUIResource(content)) {
getToolResultValue(toolResponse.toolResult)?.map((content: Content, index: number) => {
if (isRawResource(content) && isUIResource(content)) {
return (
<div key={`${index}`} className="mt-3">
<div key={index} className="mt-3">
<MCPUIResourceRenderer content={content} appendPromptToChat={append} />
<div className="mt-3 p-4 py-3 border border-borderSubtle rounded-lg bg-background-muted flex items-center">
<FlaskConical className="mr-2" size={20} />
@@ -72,7 +82,7 @@ export default function ToolCallWithResponse({
} else {
return null;
}
})}
})}{' '}
</>
);
}