Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions service/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ func ClaudeToOpenAIRequest(claudeRequest dto.ClaudeRequest, info *relaycommon.Re

for _, mediaMsg := range contents {
switch mediaMsg.Type {
case "thinking":
if mediaMsg.Thinking != nil {
openAIMessage.ReasoningContent = mediaMsg.Thinking
}
Comment on lines +152 to +155

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reasoning-only Claude messages can still be dropped.

This mapping is correct, but the append guard at Line 213 only keeps messages with content/tool calls. If a message has only thinking, ReasoningContent is lost.

Proposed fix
-       if len(openAIMessage.ParseContent()) > 0 || len(openAIMessage.ToolCalls) > 0 {
+       if len(openAIMessage.ParseContent()) > 0 || len(openAIMessage.ToolCalls) > 0 || openAIMessage.ReasoningContent != nil {
            openAIMessages = append(openAIMessages, openAIMessage)
        }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@service/convert.go` around lines 152 - 155, The code maps Claude "thinking"
into openAIMessage.ReasoningContent but the append/filter that pushes
openAIMessage into the messages slice (the guard around the append near the
current append location) only checks for Content or ToolCall and therefore drops
messages that only contain ReasoningContent; update that append guard to also
accept messages where openAIMessage.ReasoningContent is non-nil/non-empty (or
mediaMsg.Thinking was set) so reasoning-only messages are preserved when
appending the openAIMessage.

case "text", "input_text":
message := dto.MediaContent{
Type: "text",
Expand Down
1 change: 1 addition & 0 deletions web/classic/src/helpers/utils.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ export const formatMessageForAPI = (message) => {
return {
role: message.role,
content: message.content,
reasoning_content: message.reasoningContent || undefined,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const useSyncMessageAndCustomBody = (
id: msg.id,
role: msg.role,
content: msg.content,
reasoning_content: msg.reasoningContent || undefined,
})),
);
}, []);
Expand Down Expand Up @@ -77,6 +78,7 @@ export const useSyncMessageAndCustomBody = (
customPayload.messages = message.map((msg) => ({
role: msg.role,
content: msg.content,
reasoning_content: msg.reasoningContent || undefined,
}));

const newCustomBody = JSON.stringify(customPayload, null, 2);
Expand Down
1 change: 1 addition & 0 deletions web/default/src/features/playground/lib/message-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export function formatMessageForAPI(message: Message): ChatCompletionMessage {
return {
role: message.from,
content: currentVersion.content,
reasoning_content: message.reasoning?.content,
}
}

Expand Down
1 change: 1 addition & 0 deletions web/default/src/features/playground/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export interface Message {
export interface ChatCompletionMessage {
role: MessageRole
content: string | ContentPart[]
reasoning_content?: string
}

export interface ContentPart {
Expand Down