From cb60a0562b0cedc05dd8f814775d7b8ab65a3bf8 Mon Sep 17 00:00:00 2001 From: Will Chen Date: Thu, 17 Jul 2025 14:16:23 -0700 Subject: [PATCH] Send request id for LLM engine calls (#659) --- src/ipc/handlers/chat_stream_handlers.ts | 10 +++++++- src/ipc/utils/llm_engine_provider.ts | 30 ++++++++++++++++++++---- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/ipc/handlers/chat_stream_handlers.ts b/src/ipc/handlers/chat_stream_handlers.ts index 99f1444..f53a234 100644 --- a/src/ipc/handlers/chat_stream_handlers.ts +++ b/src/ipc/handlers/chat_stream_handlers.ts @@ -1,3 +1,4 @@ +import { v4 as uuidv4 } from "uuid"; import { ipcMain } from "electron"; import { CoreMessage, @@ -534,12 +535,16 @@ This conversation includes one or more image attachments. When the user uploads chatMessages: CoreMessage[]; modelClient: ModelClient; }) => { + const dyadRequestId = uuidv4(); return streamText({ maxTokens: await getMaxTokens(settings.selectedModel), temperature: 0, maxRetries: 2, model: modelClient.model, providerOptions: { + "dyad-engine": { + dyadRequestId, + }, "dyad-gateway": getExtraProviderOptions( modelClient.builtinProviderId, settings, @@ -560,9 +565,12 @@ This conversation includes one or more image attachments. When the user uploads errorMessage += "\n\nDetails: " + responseBody; } const message = errorMessage || JSON.stringify(error); + const requestIdPrefix = isEngineEnabled + ? `[Request ID: ${dyadRequestId}] ` + : ""; event.sender.send( "chat:response:error", - `Sorry, there was an error from the AI: ${message}`, + `Sorry, there was an error from the AI: ${requestIdPrefix}${message}`, ); // Clean up the abort controller activeStreams.delete(req.chatId); diff --git a/src/ipc/utils/llm_engine_provider.ts b/src/ipc/utils/llm_engine_provider.ts index e3ad6b6..8b62cc2 100644 --- a/src/ipc/utils/llm_engine_provider.ts +++ b/src/ipc/utils/llm_engine_provider.ts @@ -75,6 +75,10 @@ export function createDyadEngine( ): DyadEngineProvider { const baseURL = withoutTrailingSlash(options.baseURL); logger.info("creating dyad engine with baseURL", baseURL); + + // Track request ID attempts + const requestIdAttempts = new Map(); + const getHeaders = () => ({ Authorization: `Bearer ${loadApiKey({ apiKey: options.apiKey, @@ -91,8 +95,8 @@ export function createDyadEngine( fetch?: FetchFunction; } - const getCommonModelConfig = (modelType: string): CommonModelConfig => ({ - provider: `example.${modelType}`, + const getCommonModelConfig = (): CommonModelConfig => ({ + provider: `dyad-engine`, url: ({ path }) => { const url = new URL(`${baseURL}${path}`); if (options.queryParams) { @@ -113,7 +117,7 @@ export function createDyadEngine( // Create configuration with file handling const config = { - ...getCommonModelConfig("chat"), + ...getCommonModelConfig(), defaultObjectGenerationMode: "tool" as LanguageModelV1ObjectGenerationMode, // Custom fetch implementation that adds files to the request @@ -132,6 +136,18 @@ export function createDyadEngine( options.settings, ), }; + const requestId = parsedBody.dyadRequestId; + if ("dyadRequestId" in parsedBody) { + delete parsedBody.dyadRequestId; + } + + // Track and modify requestId with attempt number + let modifiedRequestId = requestId; + if (requestId) { + const currentAttempt = (requestIdAttempts.get(requestId) || 0) + 1; + requestIdAttempts.set(requestId, currentAttempt); + modifiedRequestId = `${requestId}:attempt-${currentAttempt}`; + } // Add files to the request if they exist if (files?.length) { @@ -143,9 +159,15 @@ export function createDyadEngine( }; } - // Return modified request with files included + // Return modified request with files included and requestId in headers const modifiedInit = { ...init, + headers: { + ...init.headers, + ...(modifiedRequestId && { + "X-Dyad-Request-Id": modifiedRequestId, + }), + }, body: JSON.stringify(parsedBody), };