From a596ffc313eb640ce289722a3933e457f7772dae Mon Sep 17 00:00:00 2001 From: Logan Yang Date: Tue, 11 Feb 2025 15:35:57 -0800 Subject: [PATCH] Add openai o-series support (#1238) --- package-lock.json | 85 ++++++++++++++++--- package.json | 2 +- src/LLMProviders/chainManager.ts | 23 ++--- src/LLMProviders/chainRunner.ts | 8 +- src/LLMProviders/chatModelManager.ts | 62 ++++++++------ src/LLMProviders/embeddingManager.ts | 18 ++-- src/aiParams.ts | 1 + src/chainUtils.ts | 1 - src/constants.ts | 14 +-- src/settings/v2/components/ModelAddDialog.tsx | 52 ++++++------ src/utils.ts | 18 ++++ 11 files changed, 188 insertions(+), 96 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0d181d90..72092f9e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@langchain/core": "^0.3.27", "@langchain/google-genai": "^0.1.6", "@langchain/groq": "^0.1.2", - "@langchain/openai": "^0.3.14", + "@langchain/openai": "^0.4.2", "@orama/orama": "^3.0.0-rc-2", "@radix-ui/react-checkbox": "^1.1.3", "@radix-ui/react-collapsible": "^1.1.2", @@ -4257,6 +4257,24 @@ } } }, + "node_modules/@langchain/community/node_modules/@langchain/openai": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.3.17.tgz", + "integrity": "sha512-uw4po32OKptVjq+CYHrumgbfh4NuD7LqyE+ZgqY9I/LrLc6bHLMc+sisHmI17vgek0K/yqtarI0alPJbzrwyag==", + "license": "MIT", + "dependencies": { + "js-tiktoken": "^1.0.12", + "openai": "^4.77.0", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.3.29 <0.4.0" + } + }, "node_modules/@langchain/community/node_modules/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", @@ -4270,16 +4288,17 @@ } }, "node_modules/@langchain/core": { - "version": "0.3.27", - "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.27.tgz", - "integrity": "sha512-jtJKbJWB1NPU1YvtrExOB2rumvUFgkJwlWGxyjSIV9A6zcLVmUbcZGV8fCSuXgl5bbzOIQLJ1xcLYQmbW9TkTg==", + "version": "0.3.39", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.39.tgz", + "integrity": "sha512-muXs4asy1A7qDtcdznxqyBfxf4N6qxofY/S0c95vbsWa0r9YAE2PttHIjcuxSy1q2jUiTkpCcgFEjNJRQRVhEw==", + "license": "MIT", "dependencies": { "@cfworker/json-schema": "^4.0.2", "ansi-styles": "^5.0.0", "camelcase": "6", "decamelize": "1.2.0", "js-tiktoken": "^1.0.12", - "langsmith": "^0.2.8", + "langsmith": ">=0.2.8 <0.4.0", "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", @@ -4357,6 +4376,24 @@ "@langchain/core": ">=0.2.21 <0.4.0" } }, + "node_modules/@langchain/groq/node_modules/@langchain/openai": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.3.17.tgz", + "integrity": "sha512-uw4po32OKptVjq+CYHrumgbfh4NuD7LqyE+ZgqY9I/LrLc6bHLMc+sisHmI17vgek0K/yqtarI0alPJbzrwyag==", + "license": "MIT", + "dependencies": { + "js-tiktoken": "^1.0.12", + "openai": "^4.77.0", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.3.29 <0.4.0" + } + }, "node_modules/@langchain/ollama": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@langchain/ollama/-/ollama-0.1.1.tgz", @@ -4387,13 +4424,13 @@ } }, "node_modules/@langchain/openai": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.3.14.tgz", - "integrity": "sha512-lNWjUo1tbvsss45IF7UQtMu1NJ6oUKvhgPYWXnX9f/d6OmuLu7D99HQ3Y88vLcUo9XjjOy417olYHignMduMjA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.4.2.tgz", + "integrity": "sha512-Cuj7qbVcycALTP0aqZuPpEc7As8cwiGaU21MhXRyZFs+dnWxKYxZ1Q1z4kcx6cYkq/I+CNwwmk+sP+YruU73Aw==", "license": "MIT", "dependencies": { "js-tiktoken": "^1.0.12", - "openai": "^4.71.0", + "openai": "^4.77.0", "zod": "^3.22.4", "zod-to-json-schema": "^3.22.3" }, @@ -4401,7 +4438,7 @@ "node": ">=18" }, "peerDependencies": { - "@langchain/core": ">=0.2.26 <0.4.0" + "@langchain/core": ">=0.3.29 <0.4.0" } }, "node_modules/@langchain/textsplitters": { @@ -14792,6 +14829,24 @@ } } }, + "node_modules/langchain/node_modules/@langchain/openai": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.3.17.tgz", + "integrity": "sha512-uw4po32OKptVjq+CYHrumgbfh4NuD7LqyE+ZgqY9I/LrLc6bHLMc+sisHmI17vgek0K/yqtarI0alPJbzrwyag==", + "license": "MIT", + "dependencies": { + "js-tiktoken": "^1.0.12", + "openai": "^4.77.0", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@langchain/core": ">=0.3.29 <0.4.0" + } + }, "node_modules/langchain/node_modules/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", @@ -17036,9 +17091,9 @@ "integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==" }, "node_modules/openai": { - "version": "4.73.1", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.73.1.tgz", - "integrity": "sha512-nWImDJBcUsqrhy7yJScXB4+iqjzbUEgzfA3un/6UnHFdwWhjX24oztj69Ped/njABfOdLcO/F7CeWTI5dt8Xmg==", + "version": "4.83.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.83.0.tgz", + "integrity": "sha512-fmTsqud0uTtRKsPC7L8Lu55dkaTwYucqncDHzVvO64DKOpNTuiYwjbR/nVgpapXuYy8xSnhQQPUm+3jQaxICgw==", "license": "Apache-2.0", "dependencies": { "@types/node": "^18.11.18", @@ -17053,9 +17108,13 @@ "openai": "bin/cli" }, "peerDependencies": { + "ws": "^8.18.0", "zod": "^3.23.8" }, "peerDependenciesMeta": { + "ws": { + "optional": true + }, "zod": { "optional": true } diff --git a/package.json b/package.json index 57547dfb..bac8e67d 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "@langchain/core": "^0.3.27", "@langchain/google-genai": "^0.1.6", "@langchain/groq": "^0.1.2", - "@langchain/openai": "^0.3.14", + "@langchain/openai": "^0.4.2", "@orama/orama": "^3.0.0-rc-2", "@radix-ui/react-checkbox": "^1.1.3", "@radix-ui/react-collapsible": "^1.1.2", diff --git a/src/LLMProviders/chainManager.ts b/src/LLMProviders/chainManager.ts index 98fbeab1..d7155b76 100644 --- a/src/LLMProviders/chainManager.ts +++ b/src/LLMProviders/chainManager.ts @@ -7,23 +7,19 @@ import { subscribeToModelKeyChange, } from "@/aiParams"; import ChainFactory, { ChainType, Document } from "@/chainFactory"; -import { - AI_SENDER, - BUILTIN_CHAT_MODELS, - USER_SENDER, - VAULT_VECTOR_STORE_STRATEGY, -} from "@/constants"; +import { BUILTIN_CHAT_MODELS, USER_SENDER, VAULT_VECTOR_STORE_STRATEGY } from "@/constants"; import { ChainRunner, CopilotPlusChainRunner, LLMChainRunner, VaultQAChainRunner, } from "@/LLMProviders/chainRunner"; +import { logError, logInfo } from "@/logger"; import { HybridRetriever } from "@/search/hybridRetriever"; import VectorStoreManager from "@/search/vectorStoreManager"; import { getSettings, getSystemPrompt, subscribeToSettingsChange } from "@/settings/model"; import { ChatMessage } from "@/sharedState"; -import { findCustomModel, isSupportedChain } from "@/utils"; +import { findCustomModel, isOSeriesModel, isSupportedChain } from "@/utils"; import { ChatPromptTemplate, HumanMessagePromptTemplate, @@ -34,7 +30,6 @@ import { App, Notice } from "obsidian"; import ChatModelManager from "./chatModelManager"; import MemoryManager from "./memoryManager"; import PromptManager from "./promptManager"; -import { logError, logInfo } from "@/logger"; export default class ChainManager { private static chain: RunnableSequence; @@ -247,21 +242,19 @@ export default class ChainManager { this.validateChainInitialization(); const chatModel = this.chatModelManager.getChatModel(); - const modelName = (chatModel as any).modelName || (chatModel as any).model || ""; - const isO1Model = modelName.startsWith("o1"); // Handle ignoreSystemMessage - if (ignoreSystemMessage || isO1Model) { + if (ignoreSystemMessage || isOSeriesModel(chatModel)) { let effectivePrompt = ChatPromptTemplate.fromMessages([ new MessagesPlaceholder("history"), HumanMessagePromptTemplate.fromTemplate("{input}"), ]); - // TODO: hack for o1 models, to be removed when they support system prompt - if (isO1Model) { - // Temporary fix:for o1-xx model need to covert systemMessage to aiMessage + // TODO: hack for o-series models, to be removed when langchainjs supports system prompt + // https://github.com/langchain-ai/langchain/issues/28895 + if (isOSeriesModel(chatModel)) { effectivePrompt = ChatPromptTemplate.fromMessages([ - [AI_SENDER, getSystemPrompt() || ""], + [USER_SENDER, getSystemPrompt() || ""], effectivePrompt, ]); } diff --git a/src/LLMProviders/chainRunner.ts b/src/LLMProviders/chainRunner.ts index bf534cdf..f8bf0c7d 100644 --- a/src/LLMProviders/chainRunner.ts +++ b/src/LLMProviders/chainRunner.ts @@ -17,6 +17,7 @@ import { extractYoutubeUrl, formatDateTime, getApiErrorMessage, + getMessageRole, ImageContent, ImageProcessor, MessageContent, @@ -357,10 +358,13 @@ class CopilotPlusChainRunner extends BaseChainRunner { "\n\nThe following is the relevant conversation history. Use this context to maintain consistency in your responses:"; } - // Add the combined system message + // Get chat model for role determination for O-series models + const chatModel = this.chainManager.chatModelManager.getChatModel(); + + // Add the combined system message with appropriate role if (fullSystemMessage) { messages.push({ - role: "system", + role: getMessageRole(chatModel), content: `${fullSystemMessage}\nIMPORTANT: Maintain consistency with previous responses in the conversation. If you've provided information about a person or topic before, use that same information in follow-up questions.`, }); } diff --git a/src/LLMProviders/chatModelManager.ts b/src/LLMProviders/chatModelManager.ts index e9f9af02..4d08e3f4 100644 --- a/src/LLMProviders/chatModelManager.ts +++ b/src/LLMProviders/chatModelManager.ts @@ -2,7 +2,7 @@ import { CustomModel, getModelKey, ModelConfig, setModelKey } from "@/aiParams"; import { BREVILABS_API_BASE_URL, BUILTIN_CHAT_MODELS, ChatModelProviders } from "@/constants"; import { getDecryptedKey } from "@/encryptionService"; import { getModelKeyFromModel, getSettings, subscribeToSettingsChange } from "@/settings/model"; -import { err2String, safeFetch } from "@/utils"; +import { err2String, isOSeriesModel, safeFetch } from "@/utils"; import { HarmBlockThreshold, HarmCategory } from "@google/generative-ai"; import { ChatAnthropic } from "@langchain/anthropic"; import { ChatCohere } from "@langchain/cohere"; @@ -75,13 +75,11 @@ export default class ChatModelManager { private async getModelConfig(customModel: CustomModel): Promise { const settings = getSettings(); - // Check if the model starts with "o1" const modelName = customModel.name; - const isO1Model = modelName.startsWith("o1"); - const baseConfig: ModelConfig = { + const isOSeries = isOSeriesModel(modelName); + const baseConfig: Omit = { modelName: modelName, temperature: customModel.temperature ?? settings.temperature, - maxTokens: customModel.maxTokens ?? settings.maxTokens, streaming: customModel.stream ?? true, maxRetries: 3, maxConcurrency: 3, @@ -97,10 +95,9 @@ export default class ChatModelManager { configuration: { baseURL: customModel.baseUrl, fetch: customModel.enableCors ? safeFetch : undefined, + organization: await getDecryptedKey(customModel.openAIOrgId || settings.openAIOrgId), }, - // @ts-ignore - openAIOrgId: getDecryptedKey(customModel.openAIOrgId || settings.openAIOrgId), - ...this.handleOpenAIExtraArgs(isO1Model, settings.maxTokens, settings.temperature), + ...this.handleOpenAIExtraArgs(isOSeries, settings.maxTokens, settings.temperature), }, [ChatModelProviders.ANTHROPIC]: { anthropicApiKey: await getDecryptedKey(customModel.apiKey || settings.anthropicApiKey), @@ -113,17 +110,18 @@ export default class ChatModelManager { }, }, [ChatModelProviders.AZURE_OPENAI]: { - azureOpenAIApiKey: await getDecryptedKey(customModel.apiKey || settings.azureOpenAIApiKey), - azureOpenAIApiInstanceName: - customModel.azureOpenAIApiInstanceName || settings.azureOpenAIApiInstanceName, - azureOpenAIApiDeploymentName: - customModel.azureOpenAIApiDeploymentName || settings.azureOpenAIApiDeploymentName, - azureOpenAIApiVersion: customModel.azureOpenAIApiVersion || settings.azureOpenAIApiVersion, + modelName: modelName, + openAIApiKey: await getDecryptedKey(customModel.apiKey || settings.azureOpenAIApiKey), configuration: { - baseURL: customModel.baseUrl, + baseURL: + customModel.baseUrl || + `https://${customModel.azureOpenAIApiInstanceName || settings.azureOpenAIApiInstanceName}.openai.azure.com/openai/deployments/${customModel.azureOpenAIApiDeploymentName || settings.azureOpenAIApiDeploymentName}`, + defaultQuery: { + "api-version": customModel.azureOpenAIApiVersion || settings.azureOpenAIApiVersion, + }, fetch: customModel.enableCors ? safeFetch : undefined, }, - ...this.handleOpenAIExtraArgs(isO1Model, settings.maxTokens, settings.temperature), + ...this.handleOpenAIExtraArgs(isOSeries, settings.maxTokens, settings.temperature), }, [ChatModelProviders.COHEREAI]: { apiKey: await getDecryptedKey(customModel.apiKey || settings.cohereApiKey), @@ -186,9 +184,9 @@ export default class ChatModelManager { configuration: { baseURL: customModel.baseUrl, fetch: customModel.enableCors ? safeFetch : undefined, - dangerouslyAllowBrowser: true, + defaultHeaders: { "dangerously-allow-browser": "true" }, }, - ...this.handleOpenAIExtraArgs(isO1Model, settings.maxTokens, settings.temperature), + ...this.handleOpenAIExtraArgs(isOSeries, settings.maxTokens, settings.temperature), }, [ChatModelProviders.COPILOT_PLUS]: { modelName: modelName, @@ -203,11 +201,22 @@ export default class ChatModelManager { const selectedProviderConfig = providerConfig[customModel.provider as keyof typeof providerConfig] || {}; - return { ...baseConfig, ...selectedProviderConfig }; + // Add token configuration separately to ensure they don't conflict + const tokenConfig = this.handleOpenAIExtraArgs( + isOSeries, + customModel.maxTokens ?? settings.maxTokens, + customModel.temperature ?? settings.temperature + ); + + return { + ...baseConfig, + ...selectedProviderConfig, + ...tokenConfig, + }; } - private handleOpenAIExtraArgs(isO1Model: boolean, maxTokens: number, temperature: number) { - return isO1Model + private handleOpenAIExtraArgs(isOSeriesModel: boolean, maxTokens: number, temperature: number) { + const config = isOSeriesModel ? { maxCompletionTokens: maxTokens, temperature: 1, @@ -216,6 +225,7 @@ export default class ChatModelManager { maxTokens: maxTokens, temperature: temperature, }; + return config; } // Build a map of modelKey to model config @@ -327,10 +337,14 @@ export default class ChatModelManager { const modelToTest = { ...model, enableCors }; const modelConfig = await this.getModelConfig(modelToTest); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { streaming, temperature, ...pingConfig } = modelConfig; - pingConfig.maxTokens = 10; + const { streaming, maxTokens, maxCompletionTokens, ...pingConfig } = modelConfig; + const isOSeries = isOSeriesModel(modelToTest.name); + const tokenConfig = this.handleOpenAIExtraArgs(isOSeries, 10, modelConfig.temperature); - const testModel = new (this.getProviderConstructor(modelToTest))(pingConfig); + const testModel = new (this.getProviderConstructor(modelToTest))({ + ...pingConfig, + ...tokenConfig, + }); await testModel.invoke([{ role: "user", content: "hello" }], { timeout: 3000, }); diff --git a/src/LLMProviders/embeddingManager.ts b/src/LLMProviders/embeddingManager.ts index 8716ddaf..b98143d8 100644 --- a/src/LLMProviders/embeddingManager.ts +++ b/src/LLMProviders/embeddingManager.ts @@ -208,7 +208,7 @@ export default class EmbeddingManager { }, [EmbeddingModelProviders.OPENAI]: { modelName, - openAIApiKey: await getDecryptedKey(customModel.apiKey || settings.openAIApiKey), + apiKey: await getDecryptedKey(customModel.apiKey || settings.openAIApiKey), timeout: 10000, configuration: { baseURL: customModel.baseUrl, @@ -224,15 +224,15 @@ export default class EmbeddingManager { apiKey: await getDecryptedKey(settings.googleApiKey), }, [EmbeddingModelProviders.AZURE_OPENAI]: { - azureOpenAIApiKey: await getDecryptedKey(customModel.apiKey || settings.azureOpenAIApiKey), - azureOpenAIApiInstanceName: - customModel.azureOpenAIApiInstanceName || settings.azureOpenAIApiInstanceName, - azureOpenAIApiDeploymentName: - customModel.azureOpenAIApiEmbeddingDeploymentName || - settings.azureOpenAIApiEmbeddingDeploymentName, - azureOpenAIApiVersion: customModel.azureOpenAIApiVersion || settings.azureOpenAIApiVersion, + modelName, + openAIApiKey: await getDecryptedKey(customModel.apiKey || settings.azureOpenAIApiKey), configuration: { - baseURL: customModel.baseUrl, + baseURL: + customModel.baseUrl || + `https://${customModel.azureOpenAIApiInstanceName || settings.azureOpenAIApiInstanceName}.openai.azure.com/openai/deployments/${customModel.azureOpenAIApiEmbeddingDeploymentName || settings.azureOpenAIApiEmbeddingDeploymentName}`, + defaultQuery: { + "api-version": customModel.azureOpenAIApiVersion || settings.azureOpenAIApiVersion, + }, fetch: customModel.enableCors ? safeFetch : undefined, }, }, diff --git a/src/aiParams.ts b/src/aiParams.ts index 723b86a8..16d0b79e 100644 --- a/src/aiParams.ts +++ b/src/aiParams.ts @@ -40,6 +40,7 @@ export interface ModelConfig { maxRetries: number; maxConcurrency: number; maxTokens?: number; + maxCompletionTokens?: number; openAIApiKey?: string; openAIOrgId?: string; anthropicApiKey?: string; diff --git a/src/chainUtils.ts b/src/chainUtils.ts index a066fa0f..3506981e 100644 --- a/src/chainUtils.ts +++ b/src/chainUtils.ts @@ -23,7 +23,6 @@ export async function getStandaloneQuestion( const response = await ChatModelManager.getInstance() .getChatModel() .invoke([ - { role: "system", content: condenseQuestionTemplate }, { role: "user", content: condenseQuestionTemplate diff --git a/src/constants.ts b/src/constants.ts index 31806f2c..4e5eb40c 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -43,7 +43,8 @@ export enum ChatModels { COPILOT_PLUS_FLASH = "copilot-plus-flash", GPT_4o = "gpt-4o", GPT_4o_mini = "gpt-4o-mini", - GPT_4_TURBO = "gpt-4-turbo", + O1_mini = "o1-mini", + O3_mini = "o3-mini", GEMINI_PRO = "gemini-1.5-pro", GEMINI_FLASH = "gemini-1.5-flash", AZURE_OPENAI = "azure-openai", @@ -93,7 +94,13 @@ export const BUILTIN_CHAT_MODELS: CustomModel[] = [ core: true, }, { - name: ChatModels.GPT_4_TURBO, + name: ChatModels.O1_mini, + provider: ChatModelProviders.OPENAI, + enabled: true, + isBuiltIn: true, + }, + { + name: ChatModels.O3_mini, provider: ChatModelProviders.OPENAI, enabled: true, isBuiltIn: true, @@ -147,15 +154,12 @@ export enum EmbeddingModelProviders { OPENAI = "openai", COHEREAI = "cohereai", GOOGLE = "google", - // AZURE_OPENAI = "azure_openai", AZURE_OPENAI = "azure openai", OLLAMA = "ollama", LM_STUDIO = "lm-studio", OPENAI_FORMAT = "3rd party (openai-format)", COPILOT_PLUS = "copilot-plus", COPILOT_PLUS_JINA = "copilot-plus-jina", - // HUGGINGFACE = "huggingface", - // VOYAGEAI = "voyageai", } export enum EmbeddingModels { diff --git a/src/settings/v2/components/ModelAddDialog.tsx b/src/settings/v2/components/ModelAddDialog.tsx index e03d086b..297b3ba2 100644 --- a/src/settings/v2/components/ModelAddDialog.tsx +++ b/src/settings/v2/components/ModelAddDialog.tsx @@ -1,6 +1,24 @@ -import React, { useState } from "react"; -import { useTab } from "@/contexts/TabContext"; -import { getSettings } from "@/settings/model"; +import { CustomModel } from "@/aiParams"; +import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { PasswordInput } from "@/components/ui/password-input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import { ChatModelProviders, DisplayKeyProviders, @@ -9,30 +27,12 @@ import { ProviderMetadata, ProviderSettingsKeyMap, } from "@/constants"; -import { CustomModel } from "@/aiParams"; +import { useTab } from "@/contexts/TabContext"; +import { getSettings } from "@/settings/model"; import { err2String, getProviderInfo, getProviderLabel, omit } from "@/utils"; -import { Notice } from "obsidian"; -import { Label } from "@/components/ui/label"; -import { Input } from "@/components/ui/input"; -import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; -import { Button } from "@/components/ui/button"; import { ChevronDown, Loader2 } from "lucide-react"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { PasswordInput } from "@/components/ui/password-input"; -import { Checkbox } from "@/components/ui/checkbox"; +import { Notice } from "obsidian"; +import React, { useState } from "react"; interface FormFieldProps { label: string; @@ -426,7 +426,7 @@ export const ModelAddDialog: React.FC = ({ {Object.values( isEmbeddingModel ? omit(EmbeddingModelProviders, ["COPILOT_PLUS", "COPILOT_PLUS_JINA"]) - : ChatModelProviders + : omit(ChatModelProviders, ["COPILOT_PLUS"]) ).map((provider) => ( {getProviderLabel(provider)} diff --git a/src/utils.ts b/src/utils.ts index ced8dac6..0168662b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -7,6 +7,7 @@ import { USER_SENDER, } from "@/constants"; import { ChatMessage } from "@/sharedState"; +import { BaseChatModel } from "@langchain/core/language_models/chat_models"; import { MemoryVariables } from "@langchain/core/memory"; import { RunnableSequence } from "@langchain/core/runnables"; import { BaseChain, RetrievalQAChain } from "langchain/chains"; @@ -867,3 +868,20 @@ export async function checkLatestVersion(): Promise<{ return { version: null, error: errorMessage }; } } + +export function isOSeriesModel(model: BaseChatModel | string): boolean { + if (typeof model === "string") { + return model.startsWith("o1") || model.startsWith("o3"); + } + + // For BaseChatModel instances + const modelName = (model as any).modelName || (model as any).model || ""; + return modelName.startsWith("o1") || modelName.startsWith("o3"); +} + +export function getMessageRole( + model: BaseChatModel | string, + defaultRole: "system" | "human" = "system" +): "system" | "human" { + return isOSeriesModel(model) ? "human" : defaultRole; +}