mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
improvement(providers): added enhanced error logging
This commit is contained in:
@@ -12,7 +12,16 @@ export const dynamic = 'force-dynamic'
|
||||
* Server-side proxy for provider requests
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = crypto.randomUUID().slice(0, 8)
|
||||
const startTime = Date.now()
|
||||
|
||||
try {
|
||||
logger.info(`[${requestId}] Provider API request started`, {
|
||||
timestamp: new Date().toISOString(),
|
||||
userAgent: request.headers.get('User-Agent'),
|
||||
contentType: request.headers.get('Content-Type'),
|
||||
})
|
||||
|
||||
const body = await request.json()
|
||||
const {
|
||||
provider,
|
||||
@@ -29,17 +38,44 @@ export async function POST(request: NextRequest) {
|
||||
messages,
|
||||
} = body
|
||||
|
||||
logger.info(`[${requestId}] Provider request details`, {
|
||||
provider,
|
||||
model,
|
||||
hasSystemPrompt: !!systemPrompt,
|
||||
hasContext: !!context,
|
||||
hasTools: !!tools?.length,
|
||||
toolCount: tools?.length || 0,
|
||||
hasApiKey: !!apiKey,
|
||||
hasResponseFormat: !!responseFormat,
|
||||
workflowId,
|
||||
stream: !!stream,
|
||||
hasMessages: !!messages?.length,
|
||||
messageCount: messages?.length || 0,
|
||||
})
|
||||
|
||||
let finalApiKey: string
|
||||
try {
|
||||
finalApiKey = getApiKey(provider, model, apiKey)
|
||||
} catch (error) {
|
||||
logger.error('Failed to get API key:', error)
|
||||
logger.error(`[${requestId}] Failed to get API key:`, {
|
||||
provider,
|
||||
model,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
hasProvidedApiKey: !!apiKey,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{ error: error instanceof Error ? error.message : 'API key error' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Executing provider request`, {
|
||||
provider,
|
||||
model,
|
||||
workflowId,
|
||||
hasApiKey: !!finalApiKey,
|
||||
})
|
||||
|
||||
// Execute provider request directly with the managed key
|
||||
const response = await executeProviderRequest(provider, {
|
||||
model,
|
||||
@@ -55,6 +91,20 @@ export async function POST(request: NextRequest) {
|
||||
messages,
|
||||
})
|
||||
|
||||
const executionTime = Date.now() - startTime
|
||||
logger.info(`[${requestId}] Provider request completed successfully`, {
|
||||
provider,
|
||||
model,
|
||||
workflowId,
|
||||
executionTime,
|
||||
responseType:
|
||||
response instanceof ReadableStream
|
||||
? 'stream'
|
||||
: response && typeof response === 'object' && 'stream' in response
|
||||
? 'streaming-execution'
|
||||
: 'json',
|
||||
})
|
||||
|
||||
// Check if the response is a StreamingExecution
|
||||
if (
|
||||
response &&
|
||||
@@ -63,7 +113,7 @@ export async function POST(request: NextRequest) {
|
||||
'execution' in response
|
||||
) {
|
||||
const streamingExec = response as StreamingExecution
|
||||
logger.info('Received StreamingExecution from provider')
|
||||
logger.info(`[${requestId}] Received StreamingExecution from provider`)
|
||||
|
||||
// Extract the stream and execution data
|
||||
const stream = streamingExec.stream
|
||||
@@ -110,7 +160,7 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
executionDataHeader = JSON.stringify(safeExecutionData)
|
||||
} catch (error) {
|
||||
logger.error('Failed to serialize execution data:', error)
|
||||
logger.error(`[${requestId}] Failed to serialize execution data:`, error)
|
||||
executionDataHeader = JSON.stringify({
|
||||
success: executionData.success,
|
||||
error: 'Failed to serialize full execution data',
|
||||
@@ -130,7 +180,7 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
// Check if the response is a ReadableStream for streaming
|
||||
if (response instanceof ReadableStream) {
|
||||
logger.info('Streaming response from provider')
|
||||
logger.info(`[${requestId}] Streaming response from provider`)
|
||||
return new Response(response, {
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
@@ -143,7 +193,15 @@ export async function POST(request: NextRequest) {
|
||||
// Return regular JSON response for non-streaming
|
||||
return NextResponse.json(response)
|
||||
} catch (error) {
|
||||
logger.error('Provider request failed:', error)
|
||||
const executionTime = Date.now() - startTime
|
||||
logger.error(`[${requestId}] Provider request failed:`, {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
errorName: error instanceof Error ? error.name : 'Unknown',
|
||||
errorStack: error instanceof Error ? error.stack : undefined,
|
||||
executionTime,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: error instanceof Error ? error.message : String(error) },
|
||||
{ status: 500 }
|
||||
|
||||
@@ -344,25 +344,58 @@ export class AgentBlockHandler implements BlockHandler {
|
||||
const url = new URL('/api/providers', baseUrl)
|
||||
|
||||
try {
|
||||
logger.info(`Making provider request to: ${url.toString()}`, {
|
||||
workflowId: context.workflowId,
|
||||
blockId: block.id,
|
||||
provider: providerId,
|
||||
model,
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
|
||||
const response = await fetch(url.toString(), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(providerRequest),
|
||||
// Add timeout and signal for better error handling
|
||||
signal: AbortSignal.timeout(120000), // 2 minute timeout
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
// Try to extract a helpful error message
|
||||
let errorMessage = `Provider API request failed with status ${response.status}`
|
||||
let errorDetails = null
|
||||
|
||||
try {
|
||||
const errorData = await response.json()
|
||||
if (errorData.error) {
|
||||
errorMessage = errorData.error
|
||||
errorDetails = errorData
|
||||
}
|
||||
} catch (_e) {
|
||||
// If JSON parsing fails, use the original error message
|
||||
// If JSON parsing fails, try to get text response
|
||||
try {
|
||||
const textError = await response.text()
|
||||
if (textError) {
|
||||
errorDetails = { textResponse: textError }
|
||||
}
|
||||
} catch (_textError) {
|
||||
// If text parsing also fails, use the original error message
|
||||
}
|
||||
}
|
||||
|
||||
logger.error('Provider API request failed', {
|
||||
workflowId: context.workflowId,
|
||||
blockId: block.id,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
url: url.toString(),
|
||||
errorMessage,
|
||||
errorDetails,
|
||||
headers: Object.fromEntries(response.headers.entries()),
|
||||
})
|
||||
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
@@ -579,6 +612,50 @@ export class AgentBlockHandler implements BlockHandler {
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error executing provider request:', { error })
|
||||
|
||||
// Enhanced error logging for different error types
|
||||
if (error instanceof Error) {
|
||||
logger.error('Provider request error details', {
|
||||
workflowId: context.workflowId,
|
||||
blockId: block.id,
|
||||
errorName: error.name,
|
||||
errorMessage: error.message,
|
||||
errorStack: error.stack,
|
||||
url: url.toString(),
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
|
||||
// Check for specific error types
|
||||
if (error.name === 'AbortError') {
|
||||
logger.error('Request timed out after 2 minutes', {
|
||||
workflowId: context.workflowId,
|
||||
blockId: block.id,
|
||||
url: url.toString(),
|
||||
})
|
||||
throw new Error('Provider request timed out - the API took too long to respond')
|
||||
}
|
||||
if (error.name === 'TypeError' && error.message.includes('fetch')) {
|
||||
logger.error('Network fetch error - possible connectivity issue', {
|
||||
workflowId: context.workflowId,
|
||||
blockId: block.id,
|
||||
url: url.toString(),
|
||||
errorMessage: error.message,
|
||||
})
|
||||
throw new Error(
|
||||
'Network error - unable to connect to provider API. Please check your internet connection.'
|
||||
)
|
||||
}
|
||||
if (error.message.includes('ENOTFOUND') || error.message.includes('ECONNREFUSED')) {
|
||||
logger.error('DNS/Connection error', {
|
||||
workflowId: context.workflowId,
|
||||
blockId: block.id,
|
||||
url: url.toString(),
|
||||
errorMessage: error.message,
|
||||
})
|
||||
throw new Error('Unable to connect to server - DNS or connection issue')
|
||||
}
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user