mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
opt: Enhance OpenAiHandler to diagnose request issues and handle empty streams (#1702)
This commit is contained in:
parent
10cde3671e
commit
ea2f4080f8
2 changed files with 86 additions and 21 deletions
5
.changeset/forty-timers-kick.md
Normal file
5
.changeset/forty-timers-kick.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
opt: Enhance OpenAiHandler to diagnose request issues and handle empty streams
|
||||
|
|
@ -28,6 +28,65 @@ export class OpenAiHandler implements ApiHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private async diagnoseRequestProblem(
|
||||
modelId: string,
|
||||
messages: OpenAI.Chat.ChatCompletionMessageParam[],
|
||||
apiKey: string,
|
||||
baseURL: string,
|
||||
) {
|
||||
const url = `${baseURL}/chat/completions`
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: modelId,
|
||||
messages: messages,
|
||||
temperature: 0,
|
||||
stream: true,
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
return `HTTP error! status: ${response.status}, statusText: ${response.statusText}`
|
||||
}
|
||||
|
||||
const responseData = await response.json()
|
||||
return responseData
|
||||
} catch (error) {
|
||||
return error instanceof Error ? error.message : String(error)
|
||||
}
|
||||
}
|
||||
|
||||
private async *handleChunk(chunk: OpenAI.Chat.Completions.ChatCompletionChunk): ApiStream {
|
||||
const delta = chunk.choices[0]?.delta
|
||||
if (delta?.content) {
|
||||
yield {
|
||||
type: "text",
|
||||
text: delta.content,
|
||||
}
|
||||
}
|
||||
|
||||
if (delta && "reasoning_content" in delta && delta.reasoning_content) {
|
||||
yield {
|
||||
type: "reasoning",
|
||||
reasoning: (delta.reasoning_content as string | undefined) || "",
|
||||
}
|
||||
}
|
||||
|
||||
if (chunk.usage) {
|
||||
yield {
|
||||
type: "usage",
|
||||
inputTokens: chunk.usage.prompt_tokens || 0,
|
||||
outputTokens: chunk.usage.completion_tokens || 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@withRetry()
|
||||
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
|
||||
const modelId = this.options.openAiModelId ?? ""
|
||||
|
|
@ -49,29 +108,30 @@ export class OpenAiHandler implements ApiHandler {
|
|||
stream: true,
|
||||
stream_options: { include_usage: true },
|
||||
})
|
||||
for await (const chunk of stream) {
|
||||
const delta = chunk.choices[0]?.delta
|
||||
if (delta?.content) {
|
||||
yield {
|
||||
type: "text",
|
||||
text: delta.content,
|
||||
}
|
||||
}
|
||||
|
||||
if (delta && "reasoning_content" in delta && delta.reasoning_content) {
|
||||
yield {
|
||||
type: "reasoning",
|
||||
reasoning: (delta.reasoning_content as string | undefined) || "",
|
||||
}
|
||||
}
|
||||
const [validationStream, contentStream] = stream.tee()
|
||||
|
||||
if (chunk.usage) {
|
||||
yield {
|
||||
type: "usage",
|
||||
inputTokens: chunk.usage.prompt_tokens || 0,
|
||||
outputTokens: chunk.usage.completion_tokens || 0,
|
||||
}
|
||||
}
|
||||
// Check the first chunk to detect potential stream issues early
|
||||
// This helps to provide better error messages for cases like:
|
||||
// https://github.com/cline/cline/issues/1662
|
||||
// where the stream appears valid but contains no actual data
|
||||
const firstChunk = await validationStream[Symbol.asyncIterator]().next()
|
||||
if (firstChunk.done || !firstChunk.value) {
|
||||
// Make an additional request to get detailed error information
|
||||
// This gives us more context about what went wrong with the API call
|
||||
const errorResponse = await this.diagnoseRequestProblem(
|
||||
modelId,
|
||||
openAiMessages,
|
||||
this.client.apiKey,
|
||||
this.client.baseURL,
|
||||
)
|
||||
throw new Error(`Stream empty. Error details: ${JSON.stringify(errorResponse)}`)
|
||||
}
|
||||
|
||||
yield* this.handleChunk(firstChunk.value)
|
||||
|
||||
for await (const chunk of contentStream) {
|
||||
yield* this.handleChunk(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue