parseToolRole tries SDK parts first, then falls back to legacy tool result rows. Unlike assistant/user roles, tool messages don't need the isFantasyEnvelopeFormat heuristic: legacy tool result rows have no "type" field (just tool_call_id, tool_name, result), so hasToolResultType reliably rejects the
(raw pqtype.NullRawMessage)
| 423 | // result rows have no "type" field (just tool_call_id, tool_name, |
| 424 | // result), so hasToolResultType reliably rejects them. |
| 425 | func parseToolRole(raw pqtype.NullRawMessage) ([]codersdk.ChatMessagePart, error) { |
| 426 | // Try SDK parts. |
| 427 | var parts []codersdk.ChatMessagePart |
| 428 | if err := json.Unmarshal(raw.RawMessage, &parts); err == nil && hasToolResultType(parts) { |
| 429 | return parts, nil |
| 430 | } |
| 431 | |
| 432 | // Fall back to legacy tool result rows. |
| 433 | rows, err := parseToolResultRows(raw) |
| 434 | if err != nil { |
| 435 | return nil, err |
| 436 | } |
| 437 | parts = make([]codersdk.ChatMessagePart, 0, len(rows)) |
| 438 | for _, row := range rows { |
| 439 | part := codersdk.ChatMessageToolResult(row.ToolCallID, row.ToolName, row.Result, row.IsError, row.IsMedia) |
| 440 | part.ProviderExecuted = row.ProviderExecuted |
| 441 | part.ProviderMetadata = row.ProviderMetadata |
| 442 | parts = append(parts, part) |
| 443 | } |
| 444 | return parts, nil |
| 445 | } |
| 446 | |
| 447 | // parseUserRole uses a structural heuristic to distinguish legacy |
| 448 | // fantasy envelope from new SDK parts. |
no test coverage detected