mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
feat: Add comprehensive error logging to Roo Cloud provider (#9098)
feat: add comprehensive error logging to Roo Cloud provider - Add detailed error logging in handleOpenAIError() to capture error details before transformation - Enhanced getRooModels() to log HTTP response details on failed requests - Added error context logging to RooHandler streaming and model loading - All existing tests passing (48 total)
This commit is contained in:
parent
2abdad6d55
commit
247b06186f
3 changed files with 98 additions and 51 deletions
|
|
@ -14,6 +14,11 @@ import { DEFAULT_HEADERS } from "../constants"
|
|||
* @throws Will throw an error if the request fails or the response is not as expected.
|
||||
*/
|
||||
export async function getRooModels(baseUrl: string, apiKey?: string): Promise<ModelRecord> {
|
||||
// Construct the models endpoint URL early so it's available in catch block for logging
|
||||
// Strip trailing /v1 or /v1/ to avoid /v1/v1/models
|
||||
const normalizedBase = baseUrl.replace(/\/?v1\/?$/, "")
|
||||
const url = `${normalizedBase}/v1/models`
|
||||
|
||||
try {
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/json",
|
||||
|
|
@ -24,11 +29,6 @@ export async function getRooModels(baseUrl: string, apiKey?: string): Promise<Mo
|
|||
headers["Authorization"] = `Bearer ${apiKey}`
|
||||
}
|
||||
|
||||
// Construct the models endpoint URL
|
||||
// Strip trailing /v1 or /v1/ to avoid /v1/v1/models
|
||||
const normalizedBase = baseUrl.replace(/\/?v1\/?$/, "")
|
||||
const url = `${normalizedBase}/v1/models`
|
||||
|
||||
// Use fetch with AbortController for better timeout handling
|
||||
const controller = new AbortController()
|
||||
const timeoutId = setTimeout(() => controller.abort(), 10000)
|
||||
|
|
@ -40,6 +40,21 @@ export async function getRooModels(baseUrl: string, apiKey?: string): Promise<Mo
|
|||
})
|
||||
|
||||
if (!response.ok) {
|
||||
// Log detailed error information
|
||||
let errorBody = ""
|
||||
try {
|
||||
errorBody = await response.text()
|
||||
} catch {
|
||||
errorBody = "(unable to read response body)"
|
||||
}
|
||||
|
||||
console.error(`[getRooModels] HTTP error:`, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
url,
|
||||
body: errorBody,
|
||||
})
|
||||
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
||||
}
|
||||
|
||||
|
|
@ -105,7 +120,14 @@ export async function getRooModels(baseUrl: string, apiKey?: string): Promise<Mo
|
|||
clearTimeout(timeoutId)
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("Error fetching Roo Code Cloud models:", error.message ? error.message : error)
|
||||
// Enhanced error logging
|
||||
console.error("[getRooModels] Error fetching Roo Code Cloud models:", {
|
||||
message: error.message || String(error),
|
||||
name: error.name,
|
||||
stack: error.stack,
|
||||
url,
|
||||
hasApiKey: Boolean(apiKey),
|
||||
})
|
||||
|
||||
// Handle abort/timeout
|
||||
if (error.name === "AbortError") {
|
||||
|
|
|
|||
|
|
@ -115,61 +115,72 @@ export class RooHandler extends BaseOpenAiCompatibleProvider<string> {
|
|||
messages: Anthropic.Messages.MessageParam[],
|
||||
metadata?: ApiHandlerCreateMessageMetadata,
|
||||
): ApiStream {
|
||||
const stream = await this.createStream(
|
||||
systemPrompt,
|
||||
messages,
|
||||
metadata,
|
||||
metadata?.taskId ? { headers: { "X-Roo-Task-ID": metadata.taskId } } : undefined,
|
||||
)
|
||||
try {
|
||||
const stream = await this.createStream(
|
||||
systemPrompt,
|
||||
messages,
|
||||
metadata,
|
||||
metadata?.taskId ? { headers: { "X-Roo-Task-ID": metadata.taskId } } : undefined,
|
||||
)
|
||||
|
||||
let lastUsage: RooUsage | undefined = undefined
|
||||
let lastUsage: RooUsage | undefined = undefined
|
||||
|
||||
for await (const chunk of stream) {
|
||||
const delta = chunk.choices[0]?.delta
|
||||
for await (const chunk of stream) {
|
||||
const delta = chunk.choices[0]?.delta
|
||||
|
||||
if (delta) {
|
||||
// Check for reasoning content (similar to OpenRouter)
|
||||
if ("reasoning" in delta && delta.reasoning && typeof delta.reasoning === "string") {
|
||||
yield {
|
||||
type: "reasoning",
|
||||
text: delta.reasoning,
|
||||
if (delta) {
|
||||
// Check for reasoning content (similar to OpenRouter)
|
||||
if ("reasoning" in delta && delta.reasoning && typeof delta.reasoning === "string") {
|
||||
yield {
|
||||
type: "reasoning",
|
||||
text: delta.reasoning,
|
||||
}
|
||||
}
|
||||
|
||||
// Also check for reasoning_content for backward compatibility
|
||||
if ("reasoning_content" in delta && typeof delta.reasoning_content === "string") {
|
||||
yield {
|
||||
type: "reasoning",
|
||||
text: delta.reasoning_content,
|
||||
}
|
||||
}
|
||||
|
||||
if (delta.content) {
|
||||
yield {
|
||||
type: "text",
|
||||
text: delta.content,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also check for reasoning_content for backward compatibility
|
||||
if ("reasoning_content" in delta && typeof delta.reasoning_content === "string") {
|
||||
yield {
|
||||
type: "reasoning",
|
||||
text: delta.reasoning_content,
|
||||
}
|
||||
}
|
||||
|
||||
if (delta.content) {
|
||||
yield {
|
||||
type: "text",
|
||||
text: delta.content,
|
||||
}
|
||||
if (chunk.usage) {
|
||||
lastUsage = chunk.usage as RooUsage
|
||||
}
|
||||
}
|
||||
|
||||
if (chunk.usage) {
|
||||
lastUsage = chunk.usage as RooUsage
|
||||
}
|
||||
}
|
||||
if (lastUsage) {
|
||||
// Check if the current model is marked as free
|
||||
const model = this.getModel()
|
||||
const isFreeModel = model.info.isFree ?? false
|
||||
|
||||
if (lastUsage) {
|
||||
// Check if the current model is marked as free
|
||||
const model = this.getModel()
|
||||
const isFreeModel = model.info.isFree ?? false
|
||||
|
||||
yield {
|
||||
type: "usage",
|
||||
inputTokens: lastUsage.prompt_tokens || 0,
|
||||
outputTokens: lastUsage.completion_tokens || 0,
|
||||
cacheWriteTokens: lastUsage.cache_creation_input_tokens,
|
||||
cacheReadTokens: lastUsage.prompt_tokens_details?.cached_tokens,
|
||||
totalCost: isFreeModel ? 0 : (lastUsage.cost ?? 0),
|
||||
yield {
|
||||
type: "usage",
|
||||
inputTokens: lastUsage.prompt_tokens || 0,
|
||||
outputTokens: lastUsage.completion_tokens || 0,
|
||||
cacheWriteTokens: lastUsage.cache_creation_input_tokens,
|
||||
cacheReadTokens: lastUsage.prompt_tokens_details?.cached_tokens,
|
||||
totalCost: isFreeModel ? 0 : (lastUsage.cost ?? 0),
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// Log streaming errors with context
|
||||
console.error("[RooHandler] Error during message streaming:", {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
modelId: this.options.apiModelId,
|
||||
hasTaskId: Boolean(metadata?.taskId),
|
||||
})
|
||||
throw error
|
||||
}
|
||||
}
|
||||
override async completePrompt(prompt: string): Promise<string> {
|
||||
|
|
@ -187,7 +198,13 @@ export class RooHandler extends BaseOpenAiCompatibleProvider<string> {
|
|||
apiKey,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("[RooHandler] Error loading dynamic models:", error)
|
||||
// Enhanced error logging with more context
|
||||
console.error("[RooHandler] Error loading dynamic models:", {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
baseURL,
|
||||
hasApiKey: Boolean(apiKey),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,13 @@ export function handleOpenAIError(error: unknown, providerName: string): Error {
|
|||
if (error instanceof Error) {
|
||||
const msg = error.message || ""
|
||||
|
||||
// Log the original error details for debugging
|
||||
console.error(`[${providerName}] API error:`, {
|
||||
message: msg,
|
||||
name: error.name,
|
||||
stack: error.stack,
|
||||
})
|
||||
|
||||
// Invalid character/ByteString conversion error in API key
|
||||
if (msg.includes("Cannot convert argument to a ByteString")) {
|
||||
return new Error(i18n.t("common:errors.api.invalidKeyInvalidChars"))
|
||||
|
|
@ -25,5 +32,6 @@ export function handleOpenAIError(error: unknown, providerName: string): Error {
|
|||
}
|
||||
|
||||
// Non-Error: wrap with provider-specific prefix
|
||||
console.error(`[${providerName}] Non-Error exception:`, error)
|
||||
return new Error(`${providerName} completion error: ${String(error)}`)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue