diff --git a/.env.example b/.env.example index e411dcee..22bdcc18 100644 --- a/.env.example +++ b/.env.example @@ -15,5 +15,10 @@ OPENAI_API_KEY= # You only need this environment variable set if you want to use Claude models ANTHROPIC_API_KEY= +# Get your OpenRouter API Key in your account settings - +# https://openrouter.ai/settings/keys +# You only need this environment variable set if you want to use OpenRouter models +OPEN_ROUTER_API_KEY= + # Include this environment variable if you want more logging for debugging locally VITE_LOG_LEVEL=debug \ No newline at end of file diff --git a/app/lib/.server/llm/api-key.ts b/app/lib/.server/llm/api-key.ts index 8bc76856..1f656cfa 100644 --- a/app/lib/.server/llm/api-key.ts +++ b/app/lib/.server/llm/api-key.ts @@ -14,6 +14,8 @@ export function getAPIKey(cloudflareEnv: Env, provider: string) { return env.OPENAI_API_KEY || cloudflareEnv.OPENAI_API_KEY; case 'Groq': return env.GROQ_API_KEY || cloudflareEnv.GROQ_API_KEY; + case 'OpenRouter': + return env.OPEN_ROUTER_API_KEY || cloudflareEnv.OPEN_ROUTER_API_KEY; default: return ""; } diff --git a/app/lib/.server/llm/model.ts b/app/lib/.server/llm/model.ts index 0aaa37dc..9b56a6dd 100644 --- a/app/lib/.server/llm/model.ts +++ b/app/lib/.server/llm/model.ts @@ -4,6 +4,7 @@ import { getAPIKey } from '~/lib/.server/llm/api-key'; import { createAnthropic } from '@ai-sdk/anthropic'; import { createOpenAI } from '@ai-sdk/openai'; import { ollama } from 'ollama-ai-provider'; +import { createOpenRouter } from "@openrouter/ai-sdk-provider"; export function getAnthropicModel(apiKey: string, model: string) { const anthropic = createAnthropic({ @@ -34,6 +35,14 @@ export function getOllamaModel(model: string) { return ollama(model); } +export function getOpenRouterModel(apiKey: string, model: string) { + const openRouter = createOpenRouter({ + apiKey + }); + + return openRouter.chat(model); +} + export function getModel(provider: string, model: string, env: Env) { const apiKey = getAPIKey(env, provider); @@ -44,6 +53,8 @@ export function getModel(provider: string, model: string, env: Env) { return getOpenAIModel(apiKey, model); case 'Groq': return getGroqModel(apiKey, model); + case 'OpenRouter': + return getOpenRouterModel(apiKey, model); default: return getOllamaModel(model); } diff --git a/app/utils/constants.ts b/app/utils/constants.ts index fbbe3267..4ff6a46f 100644 --- a/app/utils/constants.ts +++ b/app/utils/constants.ts @@ -17,6 +17,12 @@ export const MODEL_LIST = [ { name: 'codellama:34b', label: 'Code Llama 34b', provider: 'Ollama' }, { name: 'codellama:13b', label: 'Code Llama 13b', provider: 'Ollama' }, { name: 'codellama:7b', label: 'Code Llama 7b', provider: 'Ollama' }, + { name: 'deepseek/deepseek-coder', label: 'Deepseek-Coder V2 236B (OpenRouter)', provider: 'OpenRouter' }, + { name: 'google/gemini-flash-1.5', label: 'Google Gemini Flash 1.5 (OpenRouter)', provider: 'OpenRouter' }, + { name: 'google/gemini-pro-1.5', label: 'Google Gemini Pro 1.5 (OpenRouter)', provider: 'OpenRouter' }, + { name: 'mistralai/mistral-nemo', label: 'OpenRouter Mistral Nemo (OpenRouter)', provider: 'OpenRouter' }, + { name: 'qwen/qwen-110b-chat', label: 'OpenRouter Qwen 110b Chat (OpenRouter)', provider: 'OpenRouter' }, + { name: 'cohere/command', label: 'Cohere Command (OpenRouter)', provider: 'OpenRouter' }, { name: 'llama-3.1-70b-versatile', label: 'Llama 3.1 70b (Groq)', provider: 'Groq' }, { name: 'llama-3.1-8b-instant', label: 'Llama 3.1 8b (Groq)', provider: 'Groq' }, { name: 'llama-3.2-11b-vision-preview', label: 'Llama 3.2 11b (Groq)', provider: 'Groq' }, diff --git a/package.json b/package.json index cb712276..6cc49c7e 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@iconify-json/svg-spinners": "^1.1.2", "@lezer/highlight": "^1.2.0", "@nanostores/react": "^0.7.2", + "@openrouter/ai-sdk-provider": "^0.0.5", "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1", "@remix-run/cloudflare": "^2.10.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a7aea68a..75f0b99c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,9 @@ importers: '@nanostores/react': specifier: ^0.7.2 version: 0.7.2(nanostores@0.10.3)(react@18.3.1) + '@openrouter/ai-sdk-provider': + specifier: ^0.0.5 + version: 0.0.5(zod@3.23.8) '@radix-ui/react-dialog': specifier: ^1.1.1 version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -249,6 +252,15 @@ packages: peerDependencies: zod: ^3.0.0 + '@ai-sdk/provider-utils@1.0.2': + resolution: {integrity: sha512-57f6O4OFVNEpI8Z8o+K40tIB3YQiTw+VCql/qrAO9Utq7Ti1o6+X9tvm177DlZJL7ft0Rwzvgy48S9YhrEKgmA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + peerDependenciesMeta: + zod: + optional: true + '@ai-sdk/provider-utils@1.0.20': resolution: {integrity: sha512-ngg/RGpnA00eNOWEtXHenpX1MsM2QshQh4QJFjUfwcqHpM5kTfG7je7Rc3HcEDP+OkRVv2GF+X4fC1Vfcnl8Ow==} engines: {node: '>=18'} @@ -267,6 +279,10 @@ packages: zod: optional: true + '@ai-sdk/provider@0.0.12': + resolution: {integrity: sha512-oOwPQD8i2Ynpn22cur4sk26FW3mSy6t6/X/K1Ay2yGBKYiSpRyLfObhOrZEGsXDx+3euKy4nEZ193R36NM+tpQ==} + engines: {node: '>=18'} + '@ai-sdk/provider@0.0.17': resolution: {integrity: sha512-f9j+P5yYRkqKFHxvWae5FI0j6nqROPCoPnMkpc2hc2vC7vKjqzrxBJucD8rpSaUjqiBnY/QuRJ0QeV717Uz5tg==} engines: {node: '>=18'} @@ -1196,6 +1212,12 @@ packages: resolution: {integrity: sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@openrouter/ai-sdk-provider@0.0.5': + resolution: {integrity: sha512-AfxXQhISpxQSeUjU/4jo9waM5GRNX6eIkfTFS9l7vHkD1TKDP81Y/dXrE0ttJeN/Kap3tPF3Jwh49me0gWwjSw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + '@opentelemetry/api@1.9.0': resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} @@ -5331,6 +5353,15 @@ snapshots: '@ai-sdk/provider-utils': 1.0.20(zod@3.23.8) zod: 3.23.8 + '@ai-sdk/provider-utils@1.0.2(zod@3.23.8)': + dependencies: + '@ai-sdk/provider': 0.0.12 + eventsource-parser: 1.1.2 + nanoid: 3.3.6 + secure-json-parse: 2.7.0 + optionalDependencies: + zod: 3.23.8 + '@ai-sdk/provider-utils@1.0.20(zod@3.23.8)': dependencies: '@ai-sdk/provider': 0.0.24 @@ -5349,6 +5380,10 @@ snapshots: optionalDependencies: zod: 3.23.8 + '@ai-sdk/provider@0.0.12': + dependencies: + json-schema: 0.4.0 + '@ai-sdk/provider@0.0.17': dependencies: json-schema: 0.4.0 @@ -6288,6 +6323,12 @@ snapshots: dependencies: which: 3.0.1 + '@openrouter/ai-sdk-provider@0.0.5(zod@3.23.8)': + dependencies: + '@ai-sdk/provider': 0.0.12 + '@ai-sdk/provider-utils': 1.0.2(zod@3.23.8) + zod: 3.23.8 + '@opentelemetry/api@1.9.0': {} '@pkgjs/parseargs@0.11.0': @@ -7194,7 +7235,7 @@ snapshots: '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.24.7) '@vanilla-extract/babel-plugin-debug-ids': 1.0.6 '@vanilla-extract/css': 1.15.3 - esbuild: 0.17.6 + esbuild: 0.17.19 eval: 0.1.8 find-up: 5.0.0 javascript-stringify: 2.1.0 @@ -9734,7 +9775,7 @@ snapshots: acorn: 8.12.0 pathe: 1.1.2 pkg-types: 1.1.1 - ufo: 1.5.3 + ufo: 1.5.4 modern-ahocorasick@1.0.1: {} diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts index 28877fd9..41af2144 100644 --- a/worker-configuration.d.ts +++ b/worker-configuration.d.ts @@ -2,4 +2,5 @@ interface Env { ANTHROPIC_API_KEY: string; OPENAI_API_KEY: string; GROQ_API_KEY: string; + OPEN_ROUTER_API_KEY: string; }