import { OpenAICompatibleChatLanguageModel } from "@ai-sdk/openai-compatible"; import { FetchFunction, loadApiKey, withoutTrailingSlash, } from "@ai-sdk/provider-utils"; import log from "electron-log"; import { getExtraProviderOptions } from "./thinking_utils"; import type { UserSettings } from "../../lib/schemas"; import { LanguageModelV2 } from "@ai-sdk/provider"; const logger = log.scope("llm_engine_provider"); export type ExampleChatModelId = string & {}; export interface ExampleChatSettings {} export interface ExampleProviderSettings { /** Example API key. */ apiKey?: string; /** Base URL for the API calls. */ baseURL?: string; /** Custom headers to include in the requests. */ headers?: Record; /** Optional custom url query parameters to include in request urls. */ queryParams?: Record; /** Custom fetch implementation. You can use it as a middleware to intercept requests, or to provide a custom fetch implementation for e.g. testing. */ fetch?: FetchFunction; originalProviderId: string; dyadOptions: { enableLazyEdits?: boolean; enableSmartFilesContext?: boolean; enableWebSearch?: boolean; }; settings: UserSettings; } export interface MoreMinimoreEngineProvider { /** Creates a model for text generation. */ ( modelId: ExampleChatModelId, settings?: ExampleChatSettings, ): LanguageModelV2; /** Creates a chat model for text generation. */ chatModel( modelId: ExampleChatModelId, settings?: ExampleChatSettings, ): LanguageModelV2; } export function createMoreMinimoreEngine( options: ExampleProviderSettings, ): MoreMinimoreEngineProvider { 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, environmentVariableName: "DYAD_PRO_API_KEY", description: "Example API key", })}`, ...options.headers, }); interface CommonModelConfig { provider: string; url: ({ path }: { path: string }) => string; headers: () => Record; fetch?: FetchFunction; } const getCommonModelConfig = (): CommonModelConfig => ({ provider: `dyad-engine`, url: ({ path }) => { const url = new URL(`${baseURL}${path}`); if (options.queryParams) { url.search = new URLSearchParams(options.queryParams).toString(); } return url.toString(); }, headers: getHeaders, fetch: options.fetch, }); const createChatModel = (modelId: ExampleChatModelId) => { // Create configuration with file handling const config = { ...getCommonModelConfig(), // defaultObjectGenerationMode: // "tool" as LanguageModelV1ObjectGenerationMode, // Custom fetch implementation that adds files to the request fetch: (input: RequestInfo | URL, init?: RequestInit) => { // Use default fetch if no init or body if (!init || !init.body || typeof init.body !== "string") { return (options.fetch || fetch)(input, init); } try { // Parse the request body to manipulate it const parsedBody = { ...JSON.parse(init.body), ...getExtraProviderOptions( options.originalProviderId, options.settings, ), }; const dyadVersionedFiles = parsedBody.dyadVersionedFiles; if ("dyadVersionedFiles" in parsedBody) { delete parsedBody.dyadVersionedFiles; } const dyadFiles = parsedBody.dyadFiles; if ("dyadFiles" in parsedBody) { delete parsedBody.dyadFiles; } const requestId = parsedBody.dyadRequestId; if ("dyadRequestId" in parsedBody) { delete parsedBody.dyadRequestId; } const dyadAppId = parsedBody.dyadAppId; if ("dyadAppId" in parsedBody) { delete parsedBody.dyadAppId; } const dyadDisableFiles = parsedBody.dyadDisableFiles; if ("dyadDisableFiles" in parsedBody) { delete parsedBody.dyadDisableFiles; } const dyadMentionedApps = parsedBody.dyadMentionedApps; if ("dyadMentionedApps" in parsedBody) { delete parsedBody.dyadMentionedApps; } const dyadSmartContextMode = parsedBody.dyadSmartContextMode; if ("dyadSmartContextMode" in parsedBody) { delete parsedBody.dyadSmartContextMode; } // 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 (!dyadDisableFiles) { parsedBody.dyad_options = { files: dyadFiles, versioned_files: dyadVersionedFiles, enable_lazy_edits: options.dyadOptions.enableLazyEdits, enable_smart_files_context: options.dyadOptions.enableSmartFilesContext, smart_context_mode: dyadSmartContextMode, enable_web_search: options.dyadOptions.enableWebSearch, app_id: dyadAppId, }; if (dyadMentionedApps?.length) { parsedBody.dyad_options.mentioned_apps = dyadMentionedApps; } } // Return modified request with files included and requestId in headers const modifiedInit = { ...init, headers: { ...init.headers, ...(modifiedRequestId && { "X-MoreMinimore-Request-Id": modifiedRequestId, }), }, body: JSON.stringify(parsedBody), }; // Use the provided fetch or default fetch return (options.fetch || fetch)(input, modifiedInit); } catch (e) { logger.error("Error parsing request body", e); // If parsing fails, use original request return (options.fetch || fetch)(input, init); } }, }; return new OpenAICompatibleChatLanguageModel(modelId, config); }; const provider = (modelId: ExampleChatModelId) => createChatModel(modelId); provider.chatModel = createChatModel; return provider; }