diff --git a/package-lock.json b/package-lock.json index e6284ccd..65d806cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@langchain/cohere": "^0.0.2", "@langchain/community": "^0.0.16", "@langchain/google-genai": "^0.0.7", + "@langchain/groq": "^0.0.9", "@tabler/icons-react": "^2.14.0", "@types/pouchdb": "^6.4.0", "axios": "^1.3.4", @@ -2249,20 +2250,22 @@ } }, "node_modules/@langchain/core": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.1.10.tgz", - "integrity": "sha512-vSFPtTkpQ9NT3qqvRh9FN4A16m89npj9KhzZgIh976bseMfI87jS+GK6Rih3qVcB8wsStqp80E5A36ce2egl8Q==", + "version": "0.1.62", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.1.62.tgz", + "integrity": "sha512-PsBM/x0wxVvVPawKak2MFjxEneogipf212yO+rLf+2M3RDepCpYCdn1NBWgfmC+AWpmBYx/8zM7Y4QW7Kg2Wjw==", "dependencies": { "ansi-styles": "^5.0.0", "camelcase": "6", "decamelize": "1.2.0", "js-tiktoken": "^1.0.8", - "langsmith": "~0.0.48", + "langsmith": "~0.1.7", "ml-distance": "^4.0.0", + "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", "uuid": "^9.0.0", - "zod": "^3.22.3" + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" }, "engines": { "node": ">=18" @@ -2290,6 +2293,42 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@langchain/core/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@langchain/core/node_modules/langsmith": { + "version": "0.1.23", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.1.23.tgz", + "integrity": "sha512-lc6BpC82zpHNDW5qNO/+eQrN9YNK7ElZIU0GIHZrdKTclaqAXYkJB2C3hM2sxdkdp0HbFYOLa0PxxZLzNkMloQ==", + "dependencies": { + "@types/uuid": "^9.0.1", + "commander": "^10.0.1", + "p-queue": "^6.6.2", + "p-retry": "4", + "uuid": "^9.0.0" + }, + "peerDependencies": { + "openai": "*" + }, + "peerDependenciesMeta": { + "openai": { + "optional": true + } + } + }, + "node_modules/@langchain/core/node_modules/zod-to-json-schema": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.23.0.tgz", + "integrity": "sha512-az0uJ243PxsRIa2x1WmNE/pnuA05gUq/JB8Lwe1EDCCL/Fz9MgjYQ0fPlyc2Tcv6aF2ZA7WM5TWaRZVEFaAIag==", + "peerDependencies": { + "zod": "^3.23.3" + } + }, "node_modules/@langchain/google-genai": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/@langchain/google-genai/-/google-genai-0.0.7.tgz", @@ -2302,21 +2341,52 @@ "node": ">=18" } }, - "node_modules/@langchain/openai": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.0.10.tgz", - "integrity": "sha512-74BBDsuLHT+jNlDviHPUemqiG2nm1AF8i9ta0zfkwjE9JtNITL5ZVhoCSczklipx5LzO+6etVuhjdhXcwNOfBA==", + "node_modules/@langchain/groq": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@langchain/groq/-/groq-0.0.9.tgz", + "integrity": "sha512-/QGGgazYdxlN8FCmPfEVDO9Hg55POvQdnoou+b3lsugmwP1TYPRtqLW6JY7Atb36X4vjEJwiMCnntDXdT7vgaw==", "dependencies": { - "@langchain/core": "~0.1.9", - "js-tiktoken": "^1.0.7", - "openai": "^4.19.0", - "zod": "^3.22.3", - "zod-to-json-schema": "3.20.3" + "@langchain/core": "~0.1.56", + "@langchain/openai": "~0.0.28", + "groq-sdk": "^0.3.2", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.5" }, "engines": { "node": ">=18" } }, + "node_modules/@langchain/groq/node_modules/zod-to-json-schema": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.23.0.tgz", + "integrity": "sha512-az0uJ243PxsRIa2x1WmNE/pnuA05gUq/JB8Lwe1EDCCL/Fz9MgjYQ0fPlyc2Tcv6aF2ZA7WM5TWaRZVEFaAIag==", + "peerDependencies": { + "zod": "^3.23.3" + } + }, + "node_modules/@langchain/openai": { + "version": "0.0.28", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.0.28.tgz", + "integrity": "sha512-2s1RA3/eAnz4ahdzsMPBna9hfAqpFNlWdHiPxVGZ5yrhXsbLWWoPcF+22LCk9t0HJKtazi2GCIWc0HVXH9Abig==", + "dependencies": { + "@langchain/core": "~0.1.56", + "js-tiktoken": "^1.0.7", + "openai": "^4.32.1", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@langchain/openai/node_modules/zod-to-json-schema": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.23.0.tgz", + "integrity": "sha512-az0uJ243PxsRIa2x1WmNE/pnuA05gUq/JB8Lwe1EDCCL/Fz9MgjYQ0fPlyc2Tcv6aF2ZA7WM5TWaRZVEFaAIag==", + "peerDependencies": { + "zod": "^3.23.3" + } + }, "node_modules/@next/env": { "version": "14.0.4", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.4.tgz", @@ -6164,6 +6234,30 @@ "dev": true, "peer": true }, + "node_modules/groq-sdk": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/groq-sdk/-/groq-sdk-0.3.3.tgz", + "integrity": "sha512-wdOeZ2QymPjjP3tmFpUAnfMisoLbt7xF2MfpROeFAngcqWbfTyB9j9pMWSEAMF/E4gZx8f2Y+5zswO0q92CSxA==", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "digest-fetch": "^1.3.0", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7", + "web-streams-polyfill": "^3.2.1" + } + }, + "node_modules/groq-sdk/node_modules/@types/node": { + "version": "18.19.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz", + "integrity": "sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -9713,6 +9807,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "bin": { + "mustache": "bin/mustache" + } + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -10085,15 +10187,14 @@ "integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==" }, "node_modules/openai": { - "version": "4.24.1", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.24.1.tgz", - "integrity": "sha512-ezm/O3eiZMnyBqirUnWm9N6INJU1WhNtz+nK/Zj/2oyKvRz9pgpViDxa5wYOtyGYXPn1sIKBV0I/S4BDhtydqw==", + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.43.0.tgz", + "integrity": "sha512-4SMUB/XiqnO5IrEcdzEGGTcHoeXq7D/k82v36zoqSitrMUjenZXGH5JysIH7aF7Wr+gjvq0dT2mV6wLVKA7Seg==", "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", - "digest-fetch": "^1.3.0", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7", @@ -10104,9 +10205,9 @@ } }, "node_modules/openai/node_modules/@types/node": { - "version": "18.19.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.3.tgz", - "integrity": "sha512-k5fggr14DwAytoA/t8rPrIz++lXK7/DqckthCmoZOKNsEbJkId4Z//BqgApXBUGrGddrigYa1oqheo/7YmW4rg==", + "version": "18.19.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz", + "integrity": "sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==", "dependencies": { "undici-types": "~5.26.4" } @@ -12500,9 +12601,9 @@ } }, "node_modules/zod": { - "version": "3.22.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", - "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 6e97bbbe..5e975a0d 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@langchain/cohere": "^0.0.2", "@langchain/community": "^0.0.16", "@langchain/google-genai": "^0.0.7", + "@langchain/groq": "^0.0.9", "@tabler/icons-react": "^2.14.0", "@types/pouchdb": "^6.4.0", "axios": "^1.3.4", diff --git a/src/LLMProviders/chatModelManager.ts b/src/LLMProviders/chatModelManager.ts index 3eaae4a4..f50d5e76 100644 --- a/src/LLMProviders/chatModelManager.ts +++ b/src/LLMProviders/chatModelManager.ts @@ -9,6 +9,7 @@ import { OPENAI_MODELS, OPENROUTERAI_MODELS, PROXY_SERVER_PORT, + GROQ_MODELS, } from '@/constants'; import EncryptionService from '@/encryptionService'; import { ProxyChatOpenAI } from '@/langchainWrappers'; @@ -18,6 +19,7 @@ import { ChatOllama } from "@langchain/community/chat_models/ollama"; import { ChatGoogleGenerativeAI } from "@langchain/google-genai"; import { BaseChatModel } from 'langchain/chat_models/base'; import { ChatOpenAI } from 'langchain/chat_models/openai'; +import { ChatGroq } from "@langchain/groq"; import { Notice } from 'obsidian'; export default class ChatModelManager { @@ -100,6 +102,10 @@ export default class ChatModelManager { ...(params.ollamaBaseUrl ? { baseUrl: params.ollamaBaseUrl } : {}), modelName: params.ollamaModel, }, + [ModelProviders.GROQ]: { + apiKey: decrypt(params.groqApiKey), + modelName: params.groqModel, + }, }; return { ...baseConfig, ...(providerConfig[chatModelProvider as keyof typeof providerConfig] || {}) }; @@ -156,6 +162,12 @@ export default class ChatModelManager { constructor: ProxyChatOpenAI, vendor: ModelProviders.LM_STUDIO, }, + { + models: GROQ_MODELS, + apiKey: this.langChainParams.groqApiKey, + constructor: ChatGroq, + vendor: ModelProviders.GROQ, + }, ]; modelConfigurations.forEach(({ models, apiKey, constructor, vendor }) => { @@ -175,7 +187,7 @@ export default class ChatModelManager { setChatModel(modelDisplayName: string): void { if (!ChatModelManager.modelMap.hasOwnProperty(modelDisplayName)) { - throw new Error(`No model found for: ${modelDisplayName}`); + throw new Error(`No model found for: ${modelDisplayName}`); } // MUST update it since chatModelManager is a singleton. this.langChainParams.model = getModelName(modelDisplayName); @@ -190,12 +202,11 @@ export default class ChatModelManager { } const modelConfig = this.getModelConfig(selectedModel.vendor); - + new Notice(`Setting model: ${modelDisplayName}`); try { const newModelInstance = new selectedModel.AIConstructor({ ...modelConfig, }); - // Set the new model ChatModelManager.chatModel = newModelInstance; } catch (error) { diff --git a/src/aiParams.ts b/src/aiParams.ts index f137c7ca..e9a746a1 100644 --- a/src/aiParams.ts +++ b/src/aiParams.ts @@ -25,6 +25,8 @@ export interface ModelConfig { baseUrl?: string; openRouterModel?: string; lmStudioBaseUrl?: string; + groqApiKey?: string; + groqModel?: string; } export interface LangChainParams { @@ -58,6 +60,8 @@ export interface LangChainParams { openAIProxyModelName?: string; openAIEmbeddingProxyBaseUrl?: string; openAIEmbeddingProxyModelName?: string; + groqApiKey: string; + groqModel: string; } export interface SetChainOptions { diff --git a/src/components/ChatComponents/ChatIcons.tsx b/src/components/ChatComponents/ChatIcons.tsx index 0d9c602d..9d4da0dd 100644 --- a/src/components/ChatComponents/ChatIcons.tsx +++ b/src/components/ChatComponents/ChatIcons.tsx @@ -195,6 +195,9 @@ const ChatIcons: React.FC = ({ + Model Selection diff --git a/src/constants.ts b/src/constants.ts index a0884df8..46907aaa 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -15,6 +15,7 @@ export enum ChatModels { GPT_4_32K = "gpt-4-32k", GEMINI_PRO = "gemini-pro", OLLAMA = "ollama", + Groq = "llama3-70b-8192", } export enum ChatModelDisplayNames { @@ -30,6 +31,7 @@ export enum ChatModelDisplayNames { OPENROUTERAI = "OPENROUTER.AI", OLLAMA = "OLLAMA (LOCAL)", LM_STUDIO = "LM STUDIO (LOCAL)", + GROQ = "Groq", } export const OPENAI_MODELS = new Set([ @@ -67,6 +69,8 @@ export const DISPLAY_NAME_TO_MODEL: Record = { [ChatModelDisplayNames.GEMINI_PRO]: ChatModels.GEMINI_PRO, }; +export const GROQ_MODELS = new Set([ChatModelDisplayNames.GROQ]); + // Model Providers export enum ModelProviders { OPENAI = "openai", @@ -78,6 +82,7 @@ export enum ModelProviders { OPENROUTERAI = "openrouterai", LM_STUDIO = "lm_studio", OLLAMA = "ollama", + GROQ = "groq", } export const VENDOR_MODELS: Record> = { @@ -88,6 +93,7 @@ export const VENDOR_MODELS: Record> = { [ModelProviders.OPENROUTERAI]: OPENROUTERAI_MODELS, [ModelProviders.OLLAMA]: OLLAMA_MODELS, [ModelProviders.LM_STUDIO]: LM_STUDIO_MODELS, + [ModelProviders.GROQ]: GROQ_MODELS, }; export const EMBEDDING_PROVIDERS = [ @@ -174,4 +180,6 @@ export const DEFAULT_SETTINGS: CopilotSettings = { debug: false, enableEncryption: false, maxSourceChunks: 3, + groqModel: "llama3-70b-8192", + groqApiKey: "", }; diff --git a/src/main.ts b/src/main.ts index 2f8c1fd1..da5e6223 100644 --- a/src/main.ts +++ b/src/main.ts @@ -749,6 +749,8 @@ export default class CopilotPlugin extends Plugin { ollamaModel, ollamaBaseUrl, lmStudioBaseUrl, + groqApiKey, + groqModel, } = sanitizeSettings(this.settings); return { openAIApiKey, @@ -757,6 +759,8 @@ export default class CopilotPlugin extends Plugin { cohereApiKey, anthropicApiKey, anthropicModel: anthropicModel || DEFAULT_SETTINGS.anthropicModel, + groqApiKey, + groqModel, azureOpenAIApiKey, azureOpenAIApiInstanceName, azureOpenAIApiDeploymentName, diff --git a/src/settings/SettingsPage.tsx b/src/settings/SettingsPage.tsx index adf76346..c8429db6 100644 --- a/src/settings/SettingsPage.tsx +++ b/src/settings/SettingsPage.tsx @@ -42,6 +42,8 @@ export interface CopilotSettings { enableEncryption: boolean; maxSourceChunks: number; qaExclusionPaths: string; + groqModel: string; + groqApiKey: string; } export class CopilotSettingTab extends PluginSettingTab { diff --git a/src/settings/components/ApiSettings.tsx b/src/settings/components/ApiSettings.tsx index 00936b9f..2ce950fc 100644 --- a/src/settings/components/ApiSettings.tsx +++ b/src/settings/components/ApiSettings.tsx @@ -28,6 +28,10 @@ interface ApiSettingsProps { setAzureOpenAIApiVersion: (value: string) => void; azureOpenAIApiEmbeddingDeploymentName: string; setAzureOpenAIApiEmbeddingDeploymentName: (value: string) => void; + groqApiKey: string; + setGroqApiKey: (value: string) => void; + groqModel: string; + setGroqModel: (value: string) => void; } const ApiSettings: React.FC = ({ @@ -55,6 +59,10 @@ const ApiSettings: React.FC = ({ setAzureOpenAIApiVersion, azureOpenAIApiEmbeddingDeploymentName, setAzureOpenAIApiEmbeddingDeploymentName, + groqApiKey, + setGroqApiKey, + groqModel, + setGroqModel, }) => { return (
@@ -214,6 +222,32 @@ const ApiSettings: React.FC = ({ />
+ + +
+ + +

+ If you have Groq API access, you can get the API key {' '} + + here + . +
+ Your API key is stored locally and is only used to make requests to Groq's services. +

+
+
); }; diff --git a/src/settings/components/SettingsMain.tsx b/src/settings/components/SettingsMain.tsx index fb25a3c1..a468a16e 100644 --- a/src/settings/components/SettingsMain.tsx +++ b/src/settings/components/SettingsMain.tsx @@ -37,6 +37,9 @@ export default function SettingsMain({ plugin, reloadPlugin }: SettingsMainProps const [azureOpenAIApiVersion, setAzureOpenAIApiVersion] = useState(plugin.settings.azureOpenAIApiVersion); const [azureOpenAIApiEmbeddingDeploymentName, setAzureOpenAIApiEmbeddingDeploymentName] = useState(plugin.settings.azureOpenAIApiEmbeddingDeploymentName); + const [groqApiKey, setGroqApiKey] = useState(plugin.settings.groqApiKey); + const [groqModel, setGroqModel] = useState(plugin.settings.groqModel); + // QA settings const [embeddingModel, setEmbeddingModel] = useState(plugin.settings.embeddingModel); const [cohereApiKey, setCohereApiKey] = useState(plugin.settings.cohereApiKey); @@ -78,6 +81,8 @@ export default function SettingsMain({ plugin, reloadPlugin }: SettingsMainProps plugin.settings.azureOpenAIApiDeploymentName = azureOpenAIApiDeploymentName; plugin.settings.azureOpenAIApiVersion = azureOpenAIApiVersion; plugin.settings.azureOpenAIApiEmbeddingDeploymentName = azureOpenAIApiEmbeddingDeploymentName; + plugin.settings.groqApiKey = groqApiKey; + plugin.settings.groqModel = groqModel; // QA settings plugin.settings.embeddingModel = embeddingModel; @@ -207,6 +212,10 @@ export default function SettingsMain({ plugin, reloadPlugin }: SettingsMainProps setAzureOpenAIApiVersion={setAzureOpenAIApiVersion} azureOpenAIApiEmbeddingDeploymentName={azureOpenAIApiEmbeddingDeploymentName} setAzureOpenAIApiEmbeddingDeploymentName={setAzureOpenAIApiEmbeddingDeploymentName} + groqApiKey={groqApiKey} + setGroqApiKey={setGroqApiKey} + groqModel={groqModel} + setGroqModel={setGroqModel} />