diff --git a/app/components/AiConfigForm.vue b/app/components/AiConfigForm.vue index 54d826f9..6b0730e3 100644 --- a/app/components/AiConfigForm.vue +++ b/app/components/AiConfigForm.vue @@ -130,7 +130,8 @@ function pickProvider(key: string) { const canSave = computed(() => { if (!form.value.name.trim()) return false if (!form.value.model.trim()) return false - if (!isEdit.value && !form.value.apiKey) return false + // API key required for cloud providers, but optional for custom endpoints (Ollama, etc.) + if (!isEdit.value && !form.value.apiKey && !isCustomProvider.value) return false if (isCustomProvider.value && !form.value.baseUrl) return false return true }) @@ -148,7 +149,7 @@ async function handleSave() { outputPricePer1m: form.value.outputPricePer1m, } if (isCustomProvider.value) body.baseUrl = form.value.baseUrl - if (form.value.apiKey) body.apiKey = form.value.apiKey + if (form.value.apiKey !== undefined) body.apiKey = form.value.apiKey if (isEdit.value && props.config) { await $fetch(`/api/ai-config/${props.config.id}`, { diff --git a/package-lock.json b/package-lock.json index 280e1610..b643ed36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@ai-sdk/anthropic": "^3.0.74", "@ai-sdk/google": "^3.0.67", + "@ai-sdk/mistral": "^3.0.40", "@ai-sdk/openai": "^3.0.58", "@aws-sdk/client-s3": "^3.1041.0", "@better-auth/sso": "^1.6.16", @@ -106,6 +107,39 @@ "zod": "^3.25.76 || ^4.1.8" } }, + "node_modules/@ai-sdk/mistral": { + "version": "3.0.40", + "resolved": "https://registry.npmjs.org/@ai-sdk/mistral/-/mistral-3.0.40.tgz", + "integrity": "sha512-HzCV9jFsb04kpL/N+G7SCjFKJA0Q33p4Hc+1quYGGD8Ta37Pe5bzk9AjxaS8OYd//jmcny16yKhJ+e+h+P0AJg==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "3.0.10", + "@ai-sdk/provider-utils": "4.0.30" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/mistral/node_modules/@ai-sdk/provider-utils": { + "version": "4.0.30", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.30.tgz", + "integrity": "sha512-VO7I+vPffqI5sMnPoUq5DCSqKIgQIk/naJWRdQVpz2ma2zoprC/lqiJiUEl2s6DfvTD76TbhD3q39ROjlA6rGw==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "3.0.10", + "@standard-schema/spec": "^1.1.0", + "eventsource-parser": "^3.0.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, "node_modules/@ai-sdk/openai": { "version": "3.0.69", "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-3.0.69.tgz", diff --git a/package.json b/package.json index 6f3635bc..e5b3c2d4 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "dependencies": { "@ai-sdk/anthropic": "^3.0.74", "@ai-sdk/google": "^3.0.67", + "@ai-sdk/mistral": "^3.0.40", "@ai-sdk/openai": "^3.0.58", "@aws-sdk/client-s3": "^3.1041.0", "@better-auth/sso": "^1.6.16", diff --git a/server/utils/ai/provider.ts b/server/utils/ai/provider.ts index 0f385cfa..43352175 100644 --- a/server/utils/ai/provider.ts +++ b/server/utils/ai/provider.ts @@ -1,18 +1,19 @@ /** * AI Provider Abstraction Layer * - * Supports OpenAI, Anthropic, and custom OpenAI-compatible endpoints. + * Supports OpenAI, Anthropic, Google, Mistral, and custom OpenAI-compatible endpoints. * Credentials are decrypted per-request from the organization's AI config. * Never logs or stores raw API keys — only encrypted values in the database. */ import { createOpenAI } from '@ai-sdk/openai' import { createAnthropic } from '@ai-sdk/anthropic' import { createGoogleGenerativeAI } from '@ai-sdk/google' +import { createMistral } from '@ai-sdk/mistral' import { generateObject } from 'ai' import type { z } from 'zod' import { decrypt } from '../encryption' -export type SupportedProvider = 'openai' | 'anthropic' | 'google' | 'openai_compatible' +export type SupportedProvider = 'openai' | 'anthropic' | 'google' | 'mistral' | 'openai_compatible' export interface ProviderConfig { provider: SupportedProvider @@ -99,6 +100,20 @@ export const PROVIDER_REGISTRY: Record