Skip to content

Commit 2ddab16

Browse files
hannesrudolphdaniel-lxs
authored andcommitted
fix: resolve Claude Code provider JSON parsing and reasoning block display (RooCodeInc#5049)
Co-authored-by: Daniel Riccio <[email protected]>
1 parent 8221f12 commit 2ddab16

File tree

3 files changed

+3
-159
lines changed

3 files changed

+3
-159
lines changed

src/api/providers/__tests__/claude-code.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,4 +500,4 @@ describe("ClaudeCodeHandler", () => {
500500

501501
consoleSpy.mockRestore()
502502
})
503-
})
503+
})

src/api/providers/claude-code.ts

Lines changed: 1 addition & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ import { claudeCodeDefaultModelId, type ClaudeCodeModelId, claudeCodeModels } fr
33
import { type ApiHandler } from ".."
44
import { ApiStreamUsageChunk, type ApiStream } from "../transform/stream"
55
import { runClaudeCode } from "../../integrations/claude-code/run"
6-
<<<<<<< HEAD
76
import { filterMessagesForClaudeCode } from "../../integrations/claude-code/message-filter"
8-
=======
9-
import { ClaudeCodeMessage } from "../../integrations/claude-code/types"
10-
>>>>>>> 4fa735de3 (feat: add Claude Code provider for local CLI integration (#4864))
117
import { BaseProvider } from "./base-provider"
128
import { t } from "../../i18n"
139
import { ApiHandlerOptions } from "../../shared/api"
@@ -21,51 +17,16 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
2117
}
2218

2319
override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
24-
<<<<<<< HEAD
2520
// Filter out image blocks since Claude Code doesn't support them
2621
const filteredMessages = filterMessagesForClaudeCode(messages)
2722

2823
const claudeProcess = runClaudeCode({
2924
systemPrompt,
3025
messages: filteredMessages,
31-
=======
32-
const claudeProcess = runClaudeCode({
33-
systemPrompt,
34-
messages,
35-
>>>>>>> 4fa735de3 (feat: add Claude Code provider for local CLI integration (#4864))
3626
path: this.options.claudeCodePath,
3727
modelId: this.getModel().id,
3828
})
3929

40-
<<<<<<< HEAD
41-
=======
42-
const dataQueue: string[] = []
43-
let processError = null
44-
let errorOutput = ""
45-
let exitCode: number | null = null
46-
47-
claudeProcess.stdout.on("data", (data) => {
48-
const output = data.toString()
49-
const lines = output.split("\n").filter((line: string) => line.trim() !== "")
50-
51-
for (const line of lines) {
52-
dataQueue.push(line)
53-
}
54-
})
55-
56-
claudeProcess.stderr.on("data", (data) => {
57-
errorOutput += data.toString()
58-
})
59-
60-
claudeProcess.on("close", (code) => {
61-
exitCode = code
62-
})
63-
64-
claudeProcess.on("error", (error) => {
65-
processError = error
66-
})
67-
68-
>>>>>>> 4fa735de3 (feat: add Claude Code provider for local CLI integration (#4864))
6930
// Usage is included with assistant messages,
7031
// but cost is included in the result chunk
7132
let usage: ApiStreamUsageChunk = {
@@ -76,62 +37,27 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
7637
cacheWriteTokens: 0,
7738
}
7839

79-
<<<<<<< HEAD
8040
let isPaidUsage = true
8141

8242
for await (const chunk of claudeProcess) {
8343
if (typeof chunk === "string") {
8444
yield {
8545
type: "text",
8646
text: chunk,
87-
=======
88-
while (exitCode !== 0 || dataQueue.length > 0) {
89-
if (dataQueue.length === 0) {
90-
await new Promise((resolve) => setImmediate(resolve))
91-
}
92-
93-
if (exitCode !== null && exitCode !== 0) {
94-
if (errorOutput) {
95-
throw new Error(
96-
t("common:errors.claudeCode.processExitedWithError", {
97-
exitCode,
98-
output: errorOutput.trim(),
99-
}),
100-
)
101-
}
102-
throw new Error(t("common:errors.claudeCode.processExited", { exitCode }))
103-
}
104-
105-
const data = dataQueue.shift()
106-
if (!data) {
107-
continue
108-
}
109-
110-
const chunk = this.attemptParseChunk(data)
111-
112-
if (!chunk) {
113-
yield {
114-
type: "text",
115-
text: data || "",
116-
>>>>>>> 4fa735de3 (feat: add Claude Code provider for local CLI integration (#4864))
11747
}
11848

11949
continue
12050
}
12151

12252
if (chunk.type === "system" && chunk.subtype === "init") {
123-
<<<<<<< HEAD
12453
// Based on my tests, subscription usage sets the `apiKeySource` to "none"
12554
isPaidUsage = chunk.apiKeySource !== "none"
126-
=======
127-
>>>>>>> 4fa735de3 (feat: add Claude Code provider for local CLI integration (#4864))
12855
continue
12956
}
13057

13158
if (chunk.type === "assistant" && "message" in chunk) {
13259
const message = chunk.message
13360

134-
<<<<<<< HEAD
13561
if (message.stop_reason !== null) {
13662
const content = "text" in message.content[0] ? message.content[0] : undefined
13763

@@ -179,28 +105,6 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
179105
case "tool_use":
180106
console.error(`tool_use is not supported yet. Received: ${JSON.stringify(content)}`)
181107
break
182-
=======
183-
if (message.stop_reason !== null && message.stop_reason !== "tool_use") {
184-
const errorMessage =
185-
message.content[0]?.text ||
186-
t("common:errors.claudeCode.stoppedWithReason", { reason: message.stop_reason })
187-
188-
if (errorMessage.includes("Invalid model name")) {
189-
throw new Error(errorMessage + `\n\n${t("common:errors.claudeCode.apiKeyModelPlanMismatch")}`)
190-
}
191-
192-
throw new Error(errorMessage)
193-
}
194-
195-
for (const content of message.content) {
196-
if (content.type === "text") {
197-
yield {
198-
type: "text",
199-
text: content.text,
200-
}
201-
} else {
202-
console.warn("Unsupported content type:", content.type)
203-
>>>>>>> 4fa735de3 (feat: add Claude Code provider for local CLI integration (#4864))
204108
}
205109
}
206110

@@ -214,23 +118,10 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
214118
}
215119

216120
if (chunk.type === "result" && "result" in chunk) {
217-
<<<<<<< HEAD
218121
usage.totalCost = isPaidUsage ? chunk.total_cost_usd : 0
219122

220123
yield usage
221124
}
222-
=======
223-
// Only use the cost from the CLI if provided
224-
// Don't calculate cost as it may be $0 for subscription users
225-
usage.totalCost = chunk.cost_usd ?? 0
226-
227-
yield usage
228-
}
229-
230-
if (processError) {
231-
throw processError
232-
}
233-
>>>>>>> 4fa735de3 (feat: add Claude Code provider for local CLI integration (#4864))
234125
}
235126
}
236127

@@ -247,20 +138,11 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
247138
}
248139
}
249140

250-
<<<<<<< HEAD
251141
private attemptParse(str: string) {
252142
try {
253143
return JSON.parse(str)
254144
} catch (err) {
255-
=======
256-
// TODO: Validate instead of parsing
257-
private attemptParseChunk(data: string): ClaudeCodeMessage | null {
258-
try {
259-
return JSON.parse(data)
260-
} catch (error) {
261-
console.error("Error parsing chunk:", error)
262-
>>>>>>> 4fa735de3 (feat: add Claude Code provider for local CLI integration (#4864))
263145
return null
264146
}
265147
}
266-
}
148+
}
Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,17 @@
1-
<<<<<<< HEAD
21
import type { Anthropic } from "@anthropic-ai/sdk"
32

4-
=======
5-
>>>>>>> 4fa735de3 (feat: add Claude Code provider for local CLI integration (#4864))
63
type InitMessage = {
74
type: "system"
85
subtype: "init"
96
session_id: string
107
tools: string[]
118
mcp_servers: string[]
12-
<<<<<<< HEAD
139
apiKeySource: "none" | "/login managed key" | string
14-
=======
15-
}
16-
17-
type ClaudeCodeContent = {
18-
type: "text"
19-
text: string
20-
>>>>>>> 4fa735de3 (feat: add Claude Code provider for local CLI integration (#4864))
2110
}
2211

2312
type AssistantMessage = {
2413
type: "assistant"
25-
<<<<<<< HEAD
2614
message: Anthropic.Messages.Message
27-
=======
28-
message: {
29-
id: string
30-
type: "message"
31-
role: "assistant"
32-
model: string
33-
content: ClaudeCodeContent[]
34-
stop_reason: string | null
35-
stop_sequence: null
36-
usage: {
37-
input_tokens: number
38-
cache_creation_input_tokens?: number
39-
cache_read_input_tokens?: number
40-
output_tokens: number
41-
service_tier: "standard"
42-
}
43-
}
44-
>>>>>>> 4fa735de3 (feat: add Claude Code provider for local CLI integration (#4864))
4515
session_id: string
4616
}
4717

@@ -52,21 +22,13 @@ type ErrorMessage = {
5222
type ResultMessage = {
5323
type: "result"
5424
subtype: "success"
55-
<<<<<<< HEAD
5625
total_cost_usd: number
57-
=======
58-
cost_usd: number
59-
>>>>>>> 4fa735de3 (feat: add Claude Code provider for local CLI integration (#4864))
6026
is_error: boolean
6127
duration_ms: number
6228
duration_api_ms: number
6329
num_turns: number
6430
result: string
65-
<<<<<<< HEAD
66-
=======
67-
total_cost: number
68-
>>>>>>> 4fa735de3 (feat: add Claude Code provider for local CLI integration (#4864))
6931
session_id: string
7032
}
7133

72-
export type ClaudeCodeMessage = InitMessage | AssistantMessage | ErrorMessage | ResultMessage
34+
export type ClaudeCodeMessage = InitMessage | AssistantMessage | ErrorMessage | ResultMessage

0 commit comments

Comments
 (0)