Skip to content

Commit dce920c

Browse files
committed
refactor: improve Claude Code provider implementation
- Remove unnecessary type assertion for better type safety - Add helper function for content text extraction - Improve buffer trimming consistency - Enhance error logging with data preview for debugging - Replace setTimeout with setImmediate in tests for better reliability Addresses review feedback to improve code quality and maintainability.
1 parent c02fd53 commit dce920c

File tree

2 files changed

+41
-32
lines changed

2 files changed

+41
-32
lines changed

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

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,12 @@ describe("ClaudeCodeHandler", () => {
8585
}
8686

8787
// Emit the thinking response and wait for processing
88-
setTimeout(() => {
88+
setImmediate(() => {
8989
mockProcess.stdout.emit("data", JSON.stringify(thinkingResponse) + "\n")
90-
}, 10)
91-
92-
// Emit process close after data
93-
setTimeout(() => {
94-
mockProcess.emit("close", 0)
95-
}, 50)
90+
setImmediate(() => {
91+
mockProcess.emit("close", 0)
92+
})
93+
})
9694

9795
// Get the result
9896
const result = await streamGenerator.next()
@@ -141,14 +139,12 @@ describe("ClaudeCodeHandler", () => {
141139
}
142140

143141
// Emit the mixed response and wait for processing
144-
setTimeout(() => {
142+
setImmediate(() => {
145143
mockProcess.stdout.emit("data", JSON.stringify(mixedResponse) + "\n")
146-
}, 10)
147-
148-
// Emit process close after data
149-
setTimeout(() => {
150-
mockProcess.emit("close", 0)
151-
}, 50)
144+
setImmediate(() => {
145+
mockProcess.emit("close", 0)
146+
})
147+
})
152148

153149
// Get the first result (thinking)
154150
const thinkingResult = await streamGenerator.next()
@@ -200,14 +196,12 @@ describe("ClaudeCodeHandler", () => {
200196
}
201197

202198
// Emit the error response and wait for processing
203-
setTimeout(() => {
199+
setImmediate(() => {
204200
mockProcess.stdout.emit("data", JSON.stringify(errorResponse) + "\n")
205-
}, 10)
206-
207-
// Emit process close after data
208-
setTimeout(() => {
209-
mockProcess.emit("close", 0)
210-
}, 50)
201+
setImmediate(() => {
202+
mockProcess.emit("close", 0)
203+
})
204+
})
211205

212206
// Should throw error with thinking content
213207
await expect(streamGenerator.next()).rejects.toThrow("This is an error scenario")

src/api/providers/claude-code.ts

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
3939

4040
// Process complete lines
4141
for (const line of lines) {
42-
if (line.trim() !== "") {
43-
dataQueue.push(line.trim())
42+
const trimmedLine = line.trim()
43+
if (trimmedLine !== "") {
44+
dataQueue.push(trimmedLine)
4445
}
4546
}
4647
})
@@ -52,8 +53,9 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
5253
claudeProcess.on("close", (code) => {
5354
exitCode = code
5455
// Process any remaining data in buffer
55-
if (buffer.trim()) {
56-
dataQueue.push(buffer.trim())
56+
const trimmedBuffer = buffer.trim()
57+
if (trimmedBuffer) {
58+
dataQueue.push(trimmedBuffer)
5759
buffer = ""
5860
}
5961
})
@@ -115,11 +117,7 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
115117
if (message.stop_reason !== null && message.stop_reason !== "tool_use") {
116118
const firstContent = message.content[0]
117119
const errorMessage =
118-
(firstContent?.type === "text"
119-
? firstContent.text
120-
: firstContent?.type === "thinking"
121-
? firstContent.thinking
122-
: undefined) ||
120+
this.getContentText(firstContent) ||
123121
t("common:errors.claudeCode.stoppedWithReason", { reason: message.stop_reason })
124122

125123
if (errorMessage.includes("Invalid model name")) {
@@ -141,7 +139,7 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
141139
text: content.thinking,
142140
}
143141
} else {
144-
console.warn("Unsupported content type:", (content as any).type)
142+
console.warn("Unsupported content type:", content)
145143
}
146144
}
147145

@@ -181,12 +179,29 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
181179
}
182180
}
183181

182+
private getContentText(content: any): string | undefined {
183+
if (!content) return undefined
184+
switch (content.type) {
185+
case "text":
186+
return content.text
187+
case "thinking":
188+
return content.thinking
189+
default:
190+
return undefined
191+
}
192+
}
193+
184194
// TODO: Validate instead of parsing
185195
private attemptParseChunk(data: string): ClaudeCodeMessage | null {
186196
try {
187197
return JSON.parse(data)
188198
} catch (error) {
189-
console.error("Error parsing chunk:", error)
199+
console.error(
200+
"Error parsing chunk:",
201+
error,
202+
"Data:",
203+
data.substring(0, 100) + (data.length > 100 ? "..." : ""),
204+
)
190205
return null
191206
}
192207
}

0 commit comments

Comments
 (0)