Repository navigation
fix(evals): format JSON tool results + show csp #4670
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
eae5c76
c93cd7e
8a210a5
216482e
1aaad9e
5dd1971
4cd401b
af4d54a
8cd2358
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| "@mcpjam/chat-ui": patch | ||
| "@mcpjam/inspector": patch | ||
| --- | ||
|
|
||
| Render eval tool calls with the shared playground card, including Input, Result, and truthful recorded widget diagnostics, without duplicate result rows or live-only controls on frozen screenshots. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,7 +15,10 @@ function isUIResourceShape(value: unknown): boolean { | |
| return false; | ||
| } | ||
| import { buildPersistedExecutionReplay } from "./persisted-execution-replay"; | ||
| import { extractTextFromToolResult } from "./tool-result-text"; | ||
| import { | ||
| extractDisplayFromToolResult, | ||
| extractTextFromToolResult, | ||
| } from "./tool-result-text"; | ||
| import { getToolServerId } from "./tool-server"; | ||
| import { detectUIType, getUIResourceUri, UIType } from "./widget-detection"; | ||
| import { | ||
|
|
@@ -129,8 +132,7 @@ export function snapshotsToTraceWidgetSnapshots( | |
| widgetHtmlUrl: snap.widgetHtmlUrl, | ||
| toolOutput: snap.toolOutput, | ||
| injectedOpenAiCompat: snap.injectedOpenAiCompat, | ||
| injectedOpenAiCompatCapabilities: | ||
| snap.injectedOpenAiCompatCapabilities, | ||
| injectedOpenAiCompatCapabilities: snap.injectedOpenAiCompatCapabilities, | ||
| }; | ||
| }); | ||
| } | ||
|
|
@@ -176,8 +178,7 @@ export function buildToolRenderOverridesFromSnapshots( | |
| widgetPermissive: snap.widgetPermissive, | ||
| prefersBorder: snap.prefersBorder, | ||
| injectedOpenAiCompat: snap.injectedOpenAiCompat, | ||
| injectedOpenAiCompatCapabilities: | ||
| snap.injectedOpenAiCompatCapabilities, | ||
| injectedOpenAiCompatCapabilities: snap.injectedOpenAiCompatCapabilities, | ||
| }); | ||
| overrides[snap.toolCallId] = replay.renderOverride; | ||
| } | ||
|
|
@@ -211,8 +212,16 @@ export interface AdaptedTraceResult { | |
| sourceMessageIndexToFocusUiMessageId: Record<number, string>; | ||
| } | ||
|
|
||
| type ToolResultDisplay = "sibling-text" | "attached-to-tool"; | ||
| type ToolResultDisplay = "sibling-text" | "attached-to-tool" | "tool-card"; | ||
| type TraceDisplayMode = "markdown" | "json-markdown"; | ||
| type TraceDisplayAttachment = | ||
| | { kind: "text"; text: string; mode: "markdown" } | ||
| | { | ||
| kind: "json"; | ||
| value: unknown; | ||
| text: string; | ||
| mode: "json-markdown"; | ||
| }; | ||
|
|
||
| interface TraceToolResultEntry { | ||
| part: TraceContentPart; | ||
|
|
@@ -531,8 +540,7 @@ function createReplayOverride( | |
| widgetPermissive: snapshot.widgetPermissive, | ||
| prefersBorder: snapshot.prefersBorder, | ||
| injectedOpenAiCompat: snapshot.injectedOpenAiCompat, | ||
| injectedOpenAiCompatCapabilities: | ||
| snapshot.injectedOpenAiCompatCapabilities, | ||
| injectedOpenAiCompatCapabilities: snapshot.injectedOpenAiCompatCapabilities, | ||
| }).renderOverride; | ||
| } | ||
|
|
||
|
|
@@ -547,23 +555,31 @@ function createLiveSnapshotOverride(snapshot: TraceWidgetSnapshot) { | |
| widgetPermissive: snapshot.widgetPermissive, | ||
| prefersBorder: snapshot.prefersBorder, | ||
| injectedOpenAiCompat: snapshot.injectedOpenAiCompat, | ||
| injectedOpenAiCompatCapabilities: | ||
| snapshot.injectedOpenAiCompatCapabilities, | ||
| injectedOpenAiCompatCapabilities: snapshot.injectedOpenAiCompatCapabilities, | ||
| } satisfies ToolRenderOverride; | ||
| } | ||
|
|
||
| function getTraceDisplayAttachment(params: { | ||
| displayedOutput: unknown; | ||
| adaptedOutput: unknown; | ||
| canReplayWidget: boolean; | ||
| }): { text: string; mode: TraceDisplayMode } | null { | ||
| const extractedText = extractTextFromToolResult(params.displayedOutput); | ||
| if (extractedText) { | ||
| }): TraceDisplayAttachment | null { | ||
| const display = extractDisplayFromToolResult(params.displayedOutput); | ||
| if (display?.kind === "text") { | ||
| return { | ||
| text: extractedText, | ||
| kind: "text", | ||
| text: display.text, | ||
| mode: "markdown", | ||
| }; | ||
| } | ||
| if (display?.kind === "json") { | ||
| return { | ||
| kind: "json", | ||
| value: display.value, | ||
| text: toMarkdownJson(display.value) ?? "", | ||
| mode: "json-markdown", | ||
| }; | ||
| } | ||
|
|
||
| if (params.canReplayWidget) { | ||
| return null; | ||
|
|
@@ -575,6 +591,8 @@ function getTraceDisplayAttachment(params: { | |
| } | ||
|
|
||
| return { | ||
| kind: "json", | ||
| value: params.adaptedOutput, | ||
| text: jsonMarkdown, | ||
| mode: "json-markdown", | ||
| }; | ||
|
|
@@ -648,7 +666,7 @@ function buildToolParts(params: { | |
| } | ||
|
|
||
| const traceDisplayAttachment = | ||
| !isError && params.matchedResult | ||
| params.toolResultDisplay !== "tool-card" && !isError && params.matchedResult | ||
| ? getTraceDisplayAttachment({ | ||
| displayedOutput, | ||
| adaptedOutput, | ||
|
|
@@ -695,26 +713,38 @@ function buildToolParts(params: { | |
| parts.push(toolPart); | ||
|
|
||
| if (isError) { | ||
| parts.push({ | ||
| type: "text", | ||
| text: `Tool error: ${errorText}`, | ||
| }); | ||
| if (params.toolResultDisplay !== "tool-card") { | ||
| parts.push({ | ||
| type: "text", | ||
| text: `Tool error: ${errorText}`, | ||
| }); | ||
| } | ||
| return parts; | ||
| } | ||
|
|
||
| if (!params.matchedResult) { | ||
| return parts; | ||
| } | ||
|
|
||
| if (params.toolResultDisplay === "attached-to-tool") { | ||
| if ( | ||
| params.toolResultDisplay === "attached-to-tool" || | ||
| params.toolResultDisplay === "tool-card" | ||
| ) { | ||
|
Comment on lines
+729
to
+732
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Preserve parsed The widget case in At Line 731, 🤖 Prompt for AI Agents |
||
| return parts; | ||
| } | ||
|
|
||
| if (traceDisplayAttachment) { | ||
| parts.push({ | ||
| type: "text", | ||
| text: traceDisplayAttachment.text, | ||
| }); | ||
| if (traceDisplayAttachment.kind === "json") { | ||
| parts.push({ | ||
| type: "data-result", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: In the default sibling-text mode, JSON results are rendered twice: the tool card still shows Prompt for AI agents |
||
| data: traceDisplayAttachment.value, | ||
| } as any); | ||
| } else { | ||
| parts.push({ | ||
| type: "text", | ||
| text: traceDisplayAttachment.text, | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| return parts; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.