diff --git a/internal/providers/openai_gemini.go b/internal/providers/openai_gemini.go index 20388e7e..30ee3621 100644 --- a/internal/providers/openai_gemini.go +++ b/internal/providers/openai_gemini.go @@ -1,20 +1,11 @@ package providers -import ( - "encoding/json" - "fmt" - "strings" -) - -// collapseToolCallsWithoutSig rewrites assistant messages whose tool_calls lack -// thought_signature (required by Gemini 2.5+) into plain text. This handles old -// session history stored before the thought_signature capture fix. -// Tool call cycles (assistant + tool results) are collapsed into: -// - assistant: original content + summary -// - user: tool result content +// collapseToolCallsWithoutSig strips tool_call cycles that lack thought_signature +// (required by Gemini 2.5+). Old session history stored before the thought_signature +// capture fix doesn't have it, and Gemini rejects those messages with HTTP 400. // -// The collapsed format deliberately avoids looking like a tool call invocation -// to prevent Gemini from imitating the pattern instead of using structured tool_calls. +// The assistant's original text content (if any) is preserved; only the tool_calls +// and their corresponding tool-result messages are dropped. func collapseToolCallsWithoutSig(msgs []Message) []Message { // Collect tool_call IDs that need collapsing. collapseIDs := make(map[string]bool) @@ -24,7 +15,6 @@ func collapseToolCallsWithoutSig(msgs []Message) []Message { } for _, tc := range m.ToolCalls { if tc.Metadata["thought_signature"] == "" { - // If any tool_call in the message is missing sig, collapse all of them. for _, tc2 := range m.ToolCalls { collapseIDs[tc2.ID] = true } @@ -40,49 +30,23 @@ func collapseToolCallsWithoutSig(msgs []Message) []Message { for i := 0; i < len(msgs); i++ { m := msgs[i] - // Collapse assistant with tool_calls → plain text assistant + // Strip tool_calls from assistant message, keep original content only. if m.Role == "assistant" && len(m.ToolCalls) > 0 && collapseIDs[m.ToolCalls[0].ID] { - var sb strings.Builder if m.Content != "" { - sb.WriteString(m.Content) - sb.WriteString("\n\n") + result = append(result, Message{ + Role: "assistant", + Content: m.Content, + }) } - for _, tc := range m.ToolCalls { - argsJSON, _ := json.Marshal(tc.Arguments) - fmt.Fprintf(&sb, "(Used tool \"%s\" with %s)\n", tc.Name, string(argsJSON)) - } - result = append(result, Message{ - Role: "assistant", - Content: strings.TrimSpace(sb.String()), - }) - // Collect consecutive tool results → one user message - var toolBuf strings.Builder + // Skip consecutive tool results belonging to these tool_calls. for i+1 < len(msgs) && msgs[i+1].Role == "tool" && collapseIDs[msgs[i+1].ToolCallID] { i++ - toolMsg := msgs[i] - toolName := toolMsg.ToolCallID - for _, tc := range m.ToolCalls { - if tc.ID == toolMsg.ToolCallID { - toolName = tc.Name - break - } - } - if toolBuf.Len() > 0 { - toolBuf.WriteString("\n\n") - } - fmt.Fprintf(&toolBuf, "Result from %s: %s", toolName, toolMsg.Content) - } - if toolBuf.Len() > 0 { - result = append(result, Message{ - Role: "user", - Content: toolBuf.String(), - }) } continue } - // Skip orphaned tool results whose assistant was already collapsed + // Skip orphaned tool results whose assistant was already collapsed. if m.Role == "tool" && collapseIDs[m.ToolCallID] { continue } diff --git a/ui/web/src/pages/traces/trace-detail-dialog.tsx b/ui/web/src/pages/traces/trace-detail-dialog.tsx index 5ab253ff..d36ce0fd 100644 --- a/ui/web/src/pages/traces/trace-detail-dialog.tsx +++ b/ui/web/src/pages/traces/trace-detail-dialog.tsx @@ -132,14 +132,14 @@ export function TraceDetailDialog({ traceId, onClose, getTrace, onNavigateTrace {trace.input_preview && (

Input

-

{trace.input_preview}

+
{trace.input_preview}
)} {trace.output_preview && (

Output

-

{trace.output_preview}

+
{trace.output_preview}
)} @@ -255,7 +255,7 @@ function SpanTreeNode({ node, depth }: { node: SpanNode; depth: number }) { {span.input_preview && (

Input:

-
+                
                   {span.input_preview}
                 
@@ -263,7 +263,7 @@ function SpanTreeNode({ node, depth }: { node: SpanNode; depth: number }) { {span.output_preview && (

Output:

-
+                
                   {span.output_preview}