diff --git a/apps/web/.env.example b/apps/web/.env.example index e2d571cee8..edf5d1e54e 100644 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -85,6 +85,14 @@ LOG_ZOD_ERRORS=true # ECONOMY_LLM_MODEL=anthropic/claude-haiku-4.5 # AI_GATEWAY_API_KEY= +# --- Azure OpenAI --- +# DEFAULT_LLM_PROVIDER=azure +# DEFAULT_LLM_MODEL=gpt-5.1 +# ECONOMY_LLM_PROVIDER=azure +# ECONOMY_LLM_MODEL=gpt-5-mini +# AZURE_BASE_URL= +# AZURE_API_KEY= + # --- Groq --- # DEFAULT_LLM_PROVIDER=groq # DEFAULT_LLM_MODEL=llama-3.3-70b-versatile diff --git a/apps/web/env.ts b/apps/web/env.ts index d81c590990..9c7a8fa4f2 100644 --- a/apps/web/env.ts +++ b/apps/web/env.ts @@ -11,6 +11,7 @@ const llmProviderEnum = z.enum([ "openrouter", "groq", "aigateway", + "azure", "ollama", ]); @@ -58,6 +59,8 @@ export const env = createEnv({ GROQ_API_KEY: z.string().optional(), OPENROUTER_API_KEY: z.string().optional(), AI_GATEWAY_API_KEY: z.string().optional(), + AZURE_BASE_URL: z.string().optional(), + AZURE_API_KEY: z.string().optional(), OLLAMA_BASE_URL: z.string().optional(), OPENAI_ZERO_DATA_RETENTION: z.coerce.boolean().optional().default(false), diff --git a/apps/web/package.json b/apps/web/package.json index d17f8f5987..8a5135efcf 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -18,6 +18,7 @@ "dependencies": { "@ai-sdk/amazon-bedrock": "3.0.67", "@ai-sdk/anthropic": "2.0.53", + "@ai-sdk/azure": "2.0.82", "@ai-sdk/gateway": "2.0.18", "@ai-sdk/google": "2.0.44", "@ai-sdk/groq": "2.0.32", diff --git a/apps/web/utils/llms/config.ts b/apps/web/utils/llms/config.ts index e4264c4a66..c33a810574 100644 --- a/apps/web/utils/llms/config.ts +++ b/apps/web/utils/llms/config.ts @@ -12,6 +12,7 @@ export const Provider = { GROQ: "groq", OPENROUTER: "openrouter", AI_GATEWAY: "aigateway", + AZURE: "azure", ...(supportsOllama ? { OLLAMA: "ollama" } : {}), }; diff --git a/apps/web/utils/llms/model.ts b/apps/web/utils/llms/model.ts index d27072ec45..04a5d362b0 100644 --- a/apps/web/utils/llms/model.ts +++ b/apps/web/utils/llms/model.ts @@ -6,6 +6,7 @@ import { createGoogleGenerativeAI } from "@ai-sdk/google"; import { createGroq } from "@ai-sdk/groq"; import { createOpenRouter } from "@openrouter/ai-sdk-provider"; import { createGateway } from "@ai-sdk/gateway"; +import { createAzure } from "@ai-sdk/azure"; // import { createOllama } from "ollama-ai-provider"; import { env } from "@/env"; import { Provider } from "@/utils/llms/config"; @@ -138,6 +139,18 @@ function selectModel( backupModel: getBackupModel(aiApiKey), }; } + case Provider.AZURE: { + const modelName = aiModel || "gpt-5.1"; + return { + provider: Provider.AZURE, + modelName, + model: createAzure({ + baseURL: env.AZURE_BASE_URL, + apiKey: aiApiKey || env.AZURE_API_KEY, + })(modelName), + backupModel: getBackupModel(aiApiKey), + }; + } case Provider.OLLAMA: { throw new Error( "Ollama is not supported. Revert to version v1.7.28 or older to use it.", @@ -343,6 +356,7 @@ function getProviderApiKey(provider: string) { [Provider.GROQ]: env.GROQ_API_KEY, [Provider.OPENROUTER]: env.OPENROUTER_API_KEY, [Provider.AI_GATEWAY]: env.AI_GATEWAY_API_KEY, + [Provider.AZURE]: env.AZURE_API_KEY, }; return providerApiKeys[provider]; diff --git a/docs/hosting/environment-variables.md b/docs/hosting/environment-variables.md index 2442db3033..dab270badc 100644 --- a/docs/hosting/environment-variables.md +++ b/docs/hosting/environment-variables.md @@ -36,7 +36,7 @@ cp apps/web/.env.example apps/web/.env | `UPSTASH_REDIS_TOKEN` | No* | Upstash Redis token (*required if not using Docker Compose) | — | | `REDIS_URL` | No | Alternative Redis URL (for subscriptions) | — | | **LLM Provider Selection** |||| -| `DEFAULT_LLM_PROVIDER` | No | Primary LLM provider (`anthropic`, `google`, `openai`, `bedrock`, `openrouter`, `groq`, `aigateway`, `ollama`) | `anthropic` | +| `DEFAULT_LLM_PROVIDER` | No | Primary LLM provider (`anthropic`, `google`, `openai`, `bedrock`, `openrouter`, `groq`, `aigateway`, `azure`, `ollama`) | `anthropic` | | `DEFAULT_LLM_MODEL` | No | Model to use with default provider | Provider default | | `DEFAULT_OPENROUTER_PROVIDERS` | No | Comma-separated list of OpenRouter providers | — | | `ECONOMY_LLM_PROVIDER` | No | Provider for cheaper operations | — | @@ -54,6 +54,9 @@ cp apps/web/.env.example apps/web/.env | `GROQ_API_KEY` | No | Groq API key | — | | `OPENROUTER_API_KEY` | No | OpenRouter API key | — | | `AI_GATEWAY_API_KEY` | No | AI Gateway API key | — | +| **Azure OpenAI** |||| +| `AZURE_BASE_URL` | No | Azure OpenAI base URL (e.g., `https://your-resource.openai.azure.com/openai/v1/`) | — | +| `AZURE_API_KEY` | No | Azure OpenAI API key | — | | **AWS Bedrock** |||| | `BEDROCK_ACCESS_KEY` | No | AWS access key for Bedrock. See [AI SDK Bedrock documentation](https://ai-sdk.dev/providers/ai-sdk-providers/amazon-bedrock). | — | | `BEDROCK_SECRET_KEY` | No | AWS secret key for Bedrock | — | diff --git a/packages/cli/src/main.ts b/packages/cli/src/main.ts index 1cf09e8ab6..71dca5c030 100644 --- a/packages/cli/src/main.ts +++ b/packages/cli/src/main.ts @@ -388,6 +388,7 @@ Full guide: https://docs.getinboxzero.com/self-hosting/microsoft-oauth`, label: "Vercel AI Gateway", hint: "access multiple models", }, + { value: "azure", label: "Azure OpenAI" }, { value: "bedrock", label: "AWS Bedrock" }, { value: "groq", label: "Groq", hint: "fast inference" }, ], @@ -415,6 +416,7 @@ Full guide: https://docs.getinboxzero.com/self-hosting/microsoft-oauth`, default: "anthropic/claude-sonnet-4.5", economy: "anthropic/claude-haiku-4.5", }, + azure: { default: "gpt-5.1", economy: "gpt-5.1-mini" }, bedrock: { default: "global.anthropic.claude-sonnet-4-5-20250929-v1:0", economy: "global.anthropic.claude-haiku-4-5-20251001-v1:0", @@ -467,6 +469,37 @@ Full guide: https://docs.getinboxzero.com/self-hosting/microsoft-oauth`, env.BEDROCK_ACCESS_KEY = bedrockCreds.accessKey; env.BEDROCK_SECRET_KEY = bedrockCreds.secretKey; env.BEDROCK_REGION = bedrockCreds.region || "us-west-2"; + } else if (llmProvider === "azure") { + // Handle Azure separately (needs BASE_URL + API_KEY) + p.log.info( + "Get your Azure OpenAI credentials from the Azure Portal:\nhttps://portal.azure.com/", + ); + + const azureCreds = await p.group( + { + baseUrl: () => + p.text({ + message: "Azure Base URL", + placeholder: "https://your-resource.openai.azure.com/openai/v1/", + validate: (v) => (!v ? "Base URL is required" : undefined), + }), + apiKey: () => + p.text({ + message: "Azure API Key", + placeholder: "your-api-key", + validate: (v) => (!v ? "API key is required" : undefined), + }), + }, + { + onCancel: () => { + p.cancel("Setup cancelled."); + process.exit(0); + }, + }, + ); + + env.AZURE_BASE_URL = azureCreds.baseUrl; + env.AZURE_API_KEY = azureCreds.apiKey; } else { const llmLinks: Record = { anthropic: "https://console.anthropic.com/settings/keys", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 20288354e6..c6880d3124 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -103,6 +103,9 @@ importers: '@ai-sdk/anthropic': specifier: 2.0.53 version: 2.0.53(zod@3.25.46) + '@ai-sdk/azure': + specifier: 2.0.82 + version: 2.0.82(zod@3.25.46) '@ai-sdk/gateway': specifier: 2.0.18 version: 2.0.18(zod@3.25.46) @@ -823,6 +826,12 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/azure@2.0.82': + resolution: {integrity: sha512-Bpab51ETBB4adZC1xGMYsryL/CB8j1sA+t5aDqhRv3t3WRLTxhaBDcFKtQTIuxiEQTFosz9Q2xQqdfBvQm5jHw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/gateway@2.0.18': resolution: {integrity: sha512-sDQcW+6ck2m0pTIHW6BPHD7S125WD3qNkx/B8sEzJp/hurocmJ5Cni0ybExg6sQMGo+fr/GWOwpHF1cmCdg5rQ==} engines: {node: '>=18'} @@ -853,6 +862,12 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/openai@2.0.80': + resolution: {integrity: sha512-tNHuraF11db+8xJEDBoU9E3vMcpnHFKRhnLQ3DQX2LnEzfPB9DksZ8rE+yVuDN1WRW9cm2OWAhgHFgVKs7ICuw==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + '@ai-sdk/provider-utils@2.2.8': resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==} engines: {node: '>=18'} @@ -12613,6 +12628,13 @@ snapshots: '@ai-sdk/provider-utils': 3.0.18(zod@3.25.46) zod: 3.25.46 + '@ai-sdk/azure@2.0.82(zod@3.25.46)': + dependencies: + '@ai-sdk/openai': 2.0.80(zod@3.25.46) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.46) + zod: 3.25.46 + '@ai-sdk/gateway@2.0.18(zod@3.25.46)': dependencies: '@ai-sdk/provider': 2.0.0 @@ -12645,6 +12667,12 @@ snapshots: '@ai-sdk/provider-utils': 3.0.18(zod@3.25.46) zod: 3.25.46 + '@ai-sdk/openai@2.0.80(zod@3.25.46)': + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.18(zod@3.25.46) + zod: 3.25.46 + '@ai-sdk/provider-utils@2.2.8(zod@3.25.46)': dependencies: '@ai-sdk/provider': 1.1.3 diff --git a/version.txt b/version.txt index 649e8a7fbb..465d2da9d2 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v2.21.59 \ No newline at end of file +v2.21.60 \ No newline at end of file