Files
moreminimore-vibe/src/ipc/utils/llm_engine_provider.ts
Kunthawat Greethong 29248688f3 feat: rebrand from Dyad to MoreMinimore
- Update package.json description to reflect new branding
- Add fix_chat_input function to update Pro URL references
- Rename all Dyad-related functions and tags to MoreMinimore
- Update test files to use new function names
- Remove Pro restrictions from Annotator component
- Update branding text throughout the application
2025-12-19 17:26:32 +07:00

212 lines
6.5 KiB
TypeScript

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<string, string>;
/**
Optional custom url query parameters to include in request urls.
*/
queryParams?: Record<string, string>;
/**
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<string, number>();
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<string, string>;
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;
}