Skip to content

Commit c34ffa8

Browse files
authored
Add Timeout & Graceful Failure Handling for SQL Tools Service getNextMessage Calls (#19439)
* Add timeout to chat getNextMessage call * Switch link
1 parent 8e0a566 commit c34ffa8

File tree

2 files changed

+53
-9
lines changed

2 files changed

+53
-9
lines changed

src/chat/chatAgentRequestHandler.ts

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ export const createSqlAgentRequestHandler = (
288288

289289
// Process tool calls and get the result
290290
const result = await processToolCalls(
291+
stream,
291292
sqlTools,
292293
conversationUri,
293294
replyText,
@@ -363,7 +364,15 @@ export const createSqlAgentRequestHandler = (
363364
return { metadata: { command: "", correlationId: correlationId } };
364365
};
365366

367+
async function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
368+
const timeout = new Promise<never>((_, reject) =>
369+
setTimeout(() => reject(new Error("Operation timed out")), ms),
370+
);
371+
return Promise.race([promise, timeout]);
372+
}
373+
366374
async function processToolCalls(
375+
stream: vscode.ChatResponseStream,
367376
sqlTools: { tool: LanguageModelChatTool; parameters: string }[],
368377
conversationUri: string,
369378
replyText: string,
@@ -387,19 +396,53 @@ export const createSqlAgentRequestHandler = (
387396

388397
let result: GetNextMessageResponse;
389398
for (const toolCall of sqlTools) {
390-
result = await copilotService.getNextMessage(
391-
conversationUri,
392-
replyText,
393-
toolCall.tool,
394-
toolCall.parameters,
395-
);
399+
try {
400+
result = await withTimeout(
401+
copilotService.getNextMessage(
402+
conversationUri,
403+
replyText,
404+
toolCall.tool,
405+
toolCall.parameters,
406+
),
407+
60000, // timeout in milliseconds
408+
);
396409

397-
if (result.messageType === MessageType.Complete) {
398-
break;
410+
if (result.messageType === MessageType.Complete) {
411+
break;
412+
}
413+
} catch (error) {
414+
console.error(`Tool call failed or timed out:`, error);
415+
416+
// Log telemetry for the unhandled error
417+
sendErrorEvent(
418+
TelemetryViews.MssqlCopilot,
419+
TelemetryActions.Error,
420+
error instanceof Error ? error : new Error("Unknown tool call error"),
421+
true,
422+
undefined,
423+
undefined,
424+
{
425+
correlationId: correlationId,
426+
toolName: toolCall?.tool?.functionName || "Unknown",
427+
},
428+
);
429+
430+
// Gracefully warn the user in markdown
431+
stream.markdown(
432+
"⚠️ This message couldn't be processed. If this issue persists, please check the logs and [open an issue](https://aka.ms/vscode-mssql-copilot-feedback) on GitHub for this Preview release.",
433+
);
434+
435+
result = undefined;
436+
437+
break; // Exit the loop if a tool call fails or times out
399438
}
400439
}
401440

402-
return result!;
441+
if (!result) {
442+
throw new Error("All tool calls failed or timed out.");
443+
}
444+
445+
return result;
403446
}
404447

405448
function prepareRequestMessages(

src/sharedInterfaces/telemetry.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ export enum TelemetryActions {
8888
AnalyzeQueryPerformance = "AnalyzeQueryPerformance",
8989
Error = "Error",
9090
ToolCall = "ToolCall",
91+
ToolCallFailure = "ToolCallFailure",
9192
Feedback = "Feedback",
9293
ChatWithDatabase = "ChatWithDatabase",
9394
StartConversation = "StartConversation",

0 commit comments

Comments
 (0)