Enhance Azure configuration handling and UI updates (#1289)

# Changes 

- Update Azure configuration components to manage API key and resource
name settings.
- Improve visibility of conThis pull request introduces a redesigned
Azure provider settings UI and refactors the logic for configuring Azure
OpenAI credentials, both in the frontend and supporting hooks. The main
changes focus on making the Azure configuration experience clearer and
more robust by supporting both environment variable and saved settings,
improving status indicators, and updating the logic for determining
provider readiness.

**Azure Provider UI and Logic Improvements**

* Added a new `AzureConfiguration` component that provides a dedicated
form for entering and saving Azure resource name and API key, with clear
status indicators and error handling. The UI now explains precedence
between saved settings and environment variables, and guides users
through configuration.
(`src/components/settings/AzureConfiguration.tsx`)
* Updated the main provider settings page and API key configuration
logic to pass Azure-specific settings and update functions to the new
component, ensuring seamless integration and correct state management.
(`src/components/settings/ApiKeyConfiguration.tsx`,
`src/components/settings/ProviderSettingsPage.tsx`)
[[1]](diffhunk://#diff-2104fb487cda3768cc5777889100e882f51e7fb3e13abe3cc89cf8ed1444300aR35)
[[2]](diffhunk://#diff-2104fb487cda3768cc5777889100e882f51e7fb3e13abe3cc89cf8ed1444300aR51-R61)
[[3]](diffhunk://#diff-9140e707ebb56ffed3272b4661ea1e6d8388ee604a8535c58e8a1564d280057cR297)
* Refactored the logic for determining whether Azure is configured to
check both saved settings and environment variables, ensuring accurate
status display and enabling fallback to environment variables if no
settings are saved. (`src/components/settings/ProviderSettingsPage.tsx`,
`src/hooks/useLanguageModelProviders.ts`)
[[1]](diffhunk://#diff-9140e707ebb56ffed3272b4661ea1e6d8388ee604a8535c58e8a1564d280057cL72-R99)
[[2]](diffhunk://#diff-9ac9e279a0cda34a0bc519348d5474b2e355b0828a678495be3af1e8984b5be5R35-R48)
* Updated the Azure provider E2E test to verify the new UI elements,
status indicators, and guidance, ensuring the test matches the new
configuration flow and messaging.
(`e2e-tests/azure_provider_settings.spec.ts`)

**Supporting Type and Import Updates**

* Added and updated type imports for `AzureProviderSetting` and
`VertexProviderSetting` where needed to support the new logic and UI.
(`src/components/settings/ProviderSettingsPage.tsx`,
`src/hooks/useLanguageModelProviders.ts`,
`src/ipc/utils/get_model_client.ts`)
[[1]](diffhunk://#diff-9140e707ebb56ffed3272b4661ea1e6d8388ee604a8535c58e8a1564d280057cL14-R14)
[[2]](diffhunk://#diff-9ac9e279a0cda34a0bc519348d5474b2e355b0828a678495be3af1e8984b5be5L5-R5)
[[3]](diffhunk://#diff-3cd526c6c10413c1387bfef450e48b880ba6f54865e96046044586ff4192bcceR15)
* Changed Azure model client import to use `createAzure` for consistency
and future extensibility. (`src/ipc/utils/get_model_client.ts`)
[Copilot is generating a summary...]figuration status and error handling
in the UI.
- Refactor environment variable checks to prioritize saved settings.
- Add support for Azure provider settings in the schema.
- Modify tests to reflect changes in Azure configuration requirements.

# Changes in short

- **Azure settings panel**
  - Replaced with a full form that:
    - Persists API key and resource name  
    - Surfaces save state  
    - Keeps the environment-variable helper  
  - *(src/components/settings/AzureConfiguration.tsx:23-214)*

- **Settings stack workflow**
  - Threaded the new Azure workflow:
    - Config shim now passes `updateSettings`  
    - Provider status checks prefer saved Azure values before env vars  
- *(src/components/settings/ApiKeyConfiguration.tsx:40-55,
src/components/settings/ProviderSettingsPage.tsx:60-105)*

- **Provider detection**
  - Azure treated like other saved credentials by:
    - Looking for both stored fields, or  
    - The pair of env vars  
  - *(src/hooks/useLanguageModelProviders.ts:5-57)*

- **Back-end model creation**
  - Reads saved Azure credentials (falling back to env vars)  
  - Builds the client via `createAzure`  
  - *(src/ipc/utils/get_model_client.ts:316-369)*

- **Provider schema support**
  - Extended so Azure can store its resource name alongside the secret  
  - *(src/lib/schemas.ts:82-109)*  

- **E2E tests**
  - Updated Azure Playwright spec to cover the new UI  
  - *(e2e-tests/azure_provider_settings.spec.ts:4-50)*

Issues resolved: #1275
This commit is contained in:
Md Rakibul Islam Rocky
2025-09-30 23:53:52 +03:00
committed by GitHub
parent 39266416c7
commit 29d8421ce9
8 changed files with 361 additions and 120 deletions

1
.gitignore vendored
View File

@@ -101,3 +101,4 @@ out/
sqlite.db
userData/
.env.local
.idea/

View File

@@ -17,27 +17,45 @@ testWithPo("Azure provider settings UI", async ({ po }) => {
timeout: 5000,
});
// Check that Azure-specific UI is displayed
await expect(po.page.getByText("Azure OpenAI Configuration")).toBeVisible();
await expect(po.page.getByText("AZURE_API_KEY")).toBeVisible();
await expect(po.page.getByText("AZURE_RESOURCE_NAME")).toBeVisible();
// Check environment variable status indicators exist
// Confirm the new configuration form is rendered
await expect(
po.page.getByText("Environment Variables Configuration"),
po.page.getByText("Azure OpenAI Configuration Required"),
).toBeVisible();
await expect(po.page.getByLabel("Resource Name")).toBeVisible();
await expect(po.page.getByLabel("API Key")).toBeVisible();
await expect(
po.page.getByRole("button", { name: "Save Settings" }),
).toBeVisible();
// Check setup instructions are present
await expect(po.page.getByText("How to configure:")).toBeVisible();
// Environment variable helper section should still be available
await expect(
po.page.getByText("Get your API key from the Azure portal"),
).toBeVisible();
await expect(po.page.getByText("Find your resource name")).toBeVisible();
await expect(
po.page.getByText("Set these environment variables before starting Dyad"),
po.page.getByText("Environment Variables (optional)"),
).toBeVisible();
// Check that status indicators show "Not Set" (since no env vars are configured in test)
const statusElements = po.page.locator(".bg-red-100, .bg-red-800\\/20");
await expect(statusElements.first()).toBeVisible();
// FIX: disambiguate text matches to avoid strict mode violation
await expect(
po.page.getByText("AZURE_API_KEY", { exact: true }),
).toBeVisible();
await expect(
po.page.getByText("AZURE_RESOURCE_NAME", { exact: true }),
).toBeVisible();
// Since no env vars are configured in the test run, both should read "Not Set"
await expect(
po.page
.getByTestId("azure-api-key-status")
.getByText("Not Set", { exact: true }),
).toBeVisible();
await expect(
po.page
.getByTestId("azure-resource-name-status")
.getByText("Not Set", { exact: true }),
).toBeVisible();
// The guidance text should explain precedence between saved settings and environment variables
await expect(
po.page.getByText(
"Values saved in Settings take precedence over environment variables.",
),
).toBeVisible();
});

View File

@@ -32,6 +32,7 @@ interface ApiKeyConfigurationProps {
onSaveKey: () => Promise<void>;
onDeleteKey: () => Promise<void>;
isDyad: boolean;
updateSettings: (settings: Partial<UserSettings>) => Promise<UserSettings>;
}
export function ApiKeyConfiguration({
@@ -47,10 +48,17 @@ export function ApiKeyConfiguration({
onSaveKey,
onDeleteKey,
isDyad,
updateSettings,
}: ApiKeyConfigurationProps) {
// Special handling for Azure OpenAI which requires environment variables
if (provider === "azure") {
return <AzureConfiguration envVars={envVars} />;
return (
<AzureConfiguration
settings={settings}
envVars={envVars}
updateSettings={updateSettings}
/>
);
}
// Special handling for Google Vertex AI which uses service account credentials
if (provider === "vertex") {

View File

@@ -1,3 +1,6 @@
import { useEffect, useMemo, useState } from "react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import {
Accordion,
@@ -5,111 +8,264 @@ import {
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { Info, KeyRound } from "lucide-react";
import { CheckCircle2, Info, KeyRound } from "lucide-react";
import type { AzureProviderSetting, UserSettings } from "@/lib/schemas";
interface AzureConfigurationProps {
settings: UserSettings | null | undefined;
envVars: Record<string, string | undefined>;
updateSettings: (settings: Partial<UserSettings>) => Promise<UserSettings>;
}
export function AzureConfiguration({ envVars }: AzureConfigurationProps) {
const azureApiKey = envVars["AZURE_API_KEY"];
const azureResourceName = envVars["AZURE_RESOURCE_NAME"];
const AZURE_API_KEY_VAR = "AZURE_API_KEY";
const AZURE_RESOURCE_NAME_VAR = "AZURE_RESOURCE_NAME";
const isAzureConfigured = !!(azureApiKey && azureResourceName);
export function AzureConfiguration({
settings,
envVars,
updateSettings,
}: AzureConfigurationProps) {
const existing =
(settings?.providerSettings?.azure as AzureProviderSetting | undefined) ??
{};
const existingApiKey = existing.apiKey?.value ?? "";
const existingResourceName = existing.resourceName ?? "";
const [apiKey, setApiKey] = useState(existingApiKey);
const [resourceName, setResourceName] = useState(existingResourceName);
const [saving, setSaving] = useState(false);
const [saved, setSaved] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
setApiKey(existingApiKey);
setResourceName(existingResourceName);
}, [existingApiKey, existingResourceName]);
const envApiKey = envVars[AZURE_API_KEY_VAR];
const envResourceName = envVars[AZURE_RESOURCE_NAME_VAR];
const hasSavedSettings = Boolean(existingApiKey && existingResourceName);
const hasEnvConfiguration = Boolean(envApiKey && envResourceName);
const isConfigured = hasSavedSettings || hasEnvConfiguration;
const usingEnvironmentOnly = hasEnvConfiguration && !hasSavedSettings;
const hasUnsavedChanges = useMemo(() => {
return apiKey !== existingApiKey || resourceName !== existingResourceName;
}, [apiKey, existingApiKey, resourceName, existingResourceName]);
const handleSave = async () => {
setSaving(true);
setSaved(false);
setError(null);
try {
const trimmedApiKey = apiKey.trim();
const trimmedResourceName = resourceName.trim();
const azureSettings: AzureProviderSetting = {
...existing,
};
if (trimmedResourceName) {
azureSettings.resourceName = trimmedResourceName;
} else {
delete azureSettings.resourceName;
}
if (trimmedApiKey) {
azureSettings.apiKey = { value: trimmedApiKey };
} else {
delete azureSettings.apiKey;
}
const providerSettings = {
...settings?.providerSettings,
azure: azureSettings,
};
await updateSettings({
providerSettings,
});
setSaved(true);
} catch (e: any) {
setError(e?.message || "Failed to save Azure settings");
} finally {
setSaving(false);
}
};
const status = useMemo(() => {
if (hasSavedSettings) {
return {
variant: "default" as const,
title: "Azure OpenAI Configured",
description:
"Dyad will use the credentials saved in Settings for Azure OpenAI models.",
icon: KeyRound,
titleClassName: "",
descriptionClassName: "",
alertClassName: "",
};
}
if (usingEnvironmentOnly) {
return {
variant: "default" as const,
title: "Using Environment Variables",
description:
"AZURE_API_KEY and AZURE_RESOURCE_NAME are set. Values saved below will override them.",
icon: Info,
titleClassName: "",
descriptionClassName: "",
alertClassName: "",
};
}
return {
variant: "destructive" as const,
title: "Azure OpenAI Configuration Required",
description:
"Provide your Azure resource name and API key below, or configure the AZURE_API_KEY and AZURE_RESOURCE_NAME environment variables.",
icon: Info,
titleClassName: "text-red-800 dark:text-red-400",
descriptionClassName: "text-red-800 dark:text-red-400",
alertClassName:
"border-red-200 bg-red-100 dark:border-red-800/50 dark:bg-red-800/20",
};
}, [hasSavedSettings, usingEnvironmentOnly]);
const StatusIcon = status.icon;
return (
<div className="space-y-4">
<Alert
variant={isAzureConfigured ? "default" : "destructive"}
className={
isAzureConfigured
? ""
: "border-red-200 bg-red-100 dark:border-red-800/50 dark:bg-red-800/20"
}
>
<Info
className={`h-4 w-4 ${isAzureConfigured ? "" : "text-red-800 dark:text-red-400"}`}
/>
<AlertTitle
className={isAzureConfigured ? "" : "text-red-800 dark:text-red-400"}
>
Azure OpenAI Configuration
<Alert variant={status.variant} className={status.alertClassName}>
<StatusIcon className="h-4 w-4" />
<AlertTitle className={status.titleClassName}>
{status.title}
</AlertTitle>
<AlertDescription
className={isAzureConfigured ? "" : "text-red-800 dark:text-red-400"}
>
Azure OpenAI requires both an API key and resource name to be
configured via environment variables.
<AlertDescription className={status.descriptionClassName}>
{status.description}
</AlertDescription>
</Alert>
<Accordion
type="multiple"
className="w-full space-y-4"
defaultValue={["azure-config"]}
>
<AccordionItem
value="azure-config"
className="border rounded-lg px-4 bg-background"
>
<AccordionTrigger className="text-lg font-medium hover:no-underline cursor-pointer">
Environment Variables Configuration
</AccordionTrigger>
<AccordionContent className="pt-4">
<div className="space-y-4">
<div className="grid grid-cols-1 gap-4">
<div>
<h4 className="font-medium mb-2">
Required Environment Variables:
</h4>
<div className="space-y-3 text-sm">
<div className="flex justify-between items-center p-3 bg-muted rounded border">
<code className="font-mono text-foreground">
AZURE_API_KEY
</code>
<span
className={`px-2 py-1 rounded text-xs font-medium ${azureApiKey ? "bg-green-100 text-green-800 dark:bg-green-800/20 dark:text-green-400" : "bg-red-100 text-red-800 dark:bg-red-800/20 dark:text-red-400"}`}
<label
htmlFor="azure-resource-name"
className="block text-sm font-medium mb-1"
>
{azureApiKey ? "Set" : "Not Set"}
</span>
Resource Name
</label>
<Input
id="azure-resource-name"
value={resourceName}
onChange={(e) => {
setResourceName(e.target.value);
setSaved(false);
setError(null);
}}
placeholder="your-azure-openai-resource"
autoComplete="off"
/>
</div>
<div className="flex justify-between items-center p-3 bg-muted rounded border">
<code className="font-mono text-foreground">
AZURE_RESOURCE_NAME
</code>
<span
className={`px-2 py-1 rounded text-xs font-medium ${azureResourceName ? "bg-green-100 text-green-800 dark:bg-green-800/20 dark:text-green-400" : "bg-red-100 text-red-800 dark:bg-red-800/20 dark:text-red-400"}`}
<div>
<label
htmlFor="azure-api-key"
className="block text-sm font-medium mb-1"
>
{azureResourceName ? "Set" : "Not Set"}
</span>
</div>
API Key
</label>
<Input
id="azure-api-key"
value={apiKey}
onChange={(e) => {
setApiKey(e.target.value);
setSaved(false);
setError(null);
}}
placeholder="Enter your Azure OpenAI API key"
autoComplete="off"
type="password"
/>
</div>
</div>
<div className="mt-4 p-4 bg-blue-50 dark:bg-blue-950/30 rounded border border-blue-200 dark:border-blue-700">
<h5 className="font-medium mb-2 text-blue-900 dark:text-blue-200">
How to configure:
</h5>
<ol className="list-decimal list-inside space-y-1 text-sm text-blue-800 dark:text-blue-300">
<li>Get your API key from the Azure portal</li>
<li>
Find your resource name (the name you gave your Azure OpenAI
resource)
</li>
<li>Set these environment variables before starting Dyad</li>
<li>Restart Dyad after setting the environment variables</li>
</ol>
<div className="flex items-center gap-2">
<Button onClick={handleSave} disabled={saving || !hasUnsavedChanges}>
{saving ? "Saving..." : "Save Settings"}
</Button>
{saved && !error && (
<span className="flex items-center text-green-600 text-sm">
<CheckCircle2 className="h-4 w-4 mr-1" /> Saved
</span>
)}
</div>
{isAzureConfigured && (
<Alert>
<KeyRound className="h-4 w-4" />
<AlertTitle>Azure OpenAI Configured</AlertTitle>
{!isConfigured && !error && (
<Alert variant="default">
<Info className="h-4 w-4" />
<AlertTitle>Configuration Needed</AlertTitle>
<AlertDescription>
Both required environment variables are set. You can now use
Azure OpenAI models.
Azure OpenAI requests require both a resource name and API key.
Enter them above or supply the environment variables instead.
</AlertDescription>
</Alert>
)}
{error && (
<Alert variant="destructive">
<AlertTitle>Save Error</AlertTitle>
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
<Accordion
type="single"
collapsible
defaultValue="azure-env"
className="w-full space-y-4"
>
<AccordionItem
value="azure-env"
className="border rounded-lg px-4 bg-background"
>
<AccordionTrigger className="text-lg font-medium hover:no-underline cursor-pointer">
Environment Variables (optional)
</AccordionTrigger>
<AccordionContent className="pt-4 space-y-4">
<div className="space-y-3 text-sm">
<div className="flex justify-between items-center p-3 bg-muted rounded border">
<code className="font-mono text-foreground">
{AZURE_API_KEY_VAR}
</code>
<span
data-testid="azure-api-key-status"
className={`px-2 py-1 rounded text-xs font-medium ${envApiKey ? "bg-green-100 text-green-800 dark:bg-green-800/20 dark:text-green-400" : "bg-red-100 text-red-800 dark:bg-red-800/20 dark:text-red-400"}`}
>
{envApiKey ? "Set" : "Not Set"}
</span>
</div>
<div className="flex justify-between items-center p-3 bg-muted rounded border">
<code className="font-mono text-foreground">
{AZURE_RESOURCE_NAME_VAR}
</code>
<span
data-testid="azure-resource-name-status"
className={`px-2 py-1 rounded text-xs font-medium ${envResourceName ? "bg-green-100 text-green-800 dark:bg-green-800/20 dark:text-green-400" : "bg-red-100 text-red-800 dark:bg-red-800/20 dark:text-red-400"}`}
>
{envResourceName ? "Set" : "Not Set"}
</span>
</div>
</div>
<div className="text-sm text-muted-foreground space-y-2">
<p>
You can continue to configure Azure via environment variables.
If both variables are present and no settings are saved, Dyad
will use them automatically.
</p>
<p>
Values saved in Settings take precedence over environment
variables. Restart Dyad after changing environment variables.
</p>
</div>
</AccordionContent>
</AccordionItem>

View File

@@ -11,7 +11,11 @@ import {} from "@/components/ui/accordion";
import { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
import { showError } from "@/lib/toast";
import { UserSettings } from "@/lib/schemas";
import {
UserSettings,
AzureProviderSetting,
VertexProviderSetting,
} from "@/lib/schemas";
import { ProviderSettingsHeader } from "./ProviderSettingsHeader";
import { ApiKeyConfiguration } from "./ApiKeyConfiguration";
@@ -69,20 +73,34 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
userApiKey !== "Not Set";
const hasEnvKey = !!(envVarName && envVars[envVarName]);
// Special handling for Azure OpenAI configuration
const isAzureConfigured =
provider === "azure"
? !!(envVars["AZURE_API_KEY"] && envVars["AZURE_RESOURCE_NAME"])
: false;
const azureSettings = settings?.providerSettings?.azure as
| AzureProviderSetting
| undefined;
const azureApiKeyFromSettings = (azureSettings?.apiKey?.value ?? "").trim();
const azureResourceNameFromSettings = (
azureSettings?.resourceName ?? ""
).trim();
const azureHasSavedSettings = Boolean(
azureApiKeyFromSettings && azureResourceNameFromSettings,
);
const azureHasEnvConfiguration = Boolean(
envVars["AZURE_API_KEY"] && envVars["AZURE_RESOURCE_NAME"],
);
// Special handling for Vertex configuration status
const vertexSettings = settings?.providerSettings?.vertex as any;
const vertexSettings = settings?.providerSettings?.vertex as
| VertexProviderSetting
| undefined;
const isVertexConfigured = Boolean(
vertexSettings?.projectId &&
vertexSettings?.location &&
vertexSettings?.serviceAccountKey?.value,
);
const isAzureConfigured =
provider === "azure"
? azureHasSavedSettings || azureHasEnvConfiguration
: false;
const isConfigured =
provider === "azure"
? isAzureConfigured
@@ -280,6 +298,7 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
onSaveKey={handleSaveKey}
onDeleteKey={handleDeleteKey}
isDyad={isDyad}
updateSettings={updateSettings}
/>
)}

View File

@@ -2,7 +2,11 @@ import { useQuery } from "@tanstack/react-query";
import { IpcClient } from "@/ipc/ipc_client";
import type { LanguageModelProvider } from "@/ipc/ipc_types";
import { useSettings } from "./useSettings";
import { cloudProviders, VertexProviderSetting } from "@/lib/schemas";
import {
cloudProviders,
VertexProviderSetting,
AzureProviderSetting,
} from "@/lib/schemas";
export function useLanguageModelProviders() {
const ipcClient = IpcClient.getInstance();
@@ -32,6 +36,20 @@ export function useLanguageModelProviders() {
}
return false;
}
if (provider === "azure") {
const azureSettings = providerSettings as AzureProviderSetting;
const hasSavedSettings = Boolean(
(azureSettings?.apiKey?.value ?? "").trim() &&
(azureSettings?.resourceName ?? "").trim(),
);
if (hasSavedSettings) {
return true;
}
if (envVars["AZURE_API_KEY"] && envVars["AZURE_RESOURCE_NAME"]) {
return true;
}
return false;
}
if (providerSettings?.apiKey?.value) {
return true;
}

View File

@@ -3,7 +3,7 @@ import { createGoogleGenerativeAI as createGoogle } from "@ai-sdk/google";
import { createAnthropic } from "@ai-sdk/anthropic";
import { createXai } from "@ai-sdk/xai";
import { createVertex as createGoogleVertex } from "@ai-sdk/google-vertex";
import { azure } from "@ai-sdk/azure";
import { createAzure } from "@ai-sdk/azure";
import { LanguageModelV2 } from "@ai-sdk/provider";
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
@@ -12,6 +12,7 @@ import type {
LargeLanguageModel,
UserSettings,
VertexProviderSetting,
AzureProviderSetting,
} from "../../lib/schemas";
import { getEnvVar } from "./read_env";
import log from "electron-log";
@@ -335,28 +336,41 @@ function getRegularModelClient(
};
}
// Azure OpenAI requires both API key and resource name as env vars
// We use environment variables for Azure configuration
const resourceName = getEnvVar("AZURE_RESOURCE_NAME");
const azureApiKey = getEnvVar("AZURE_API_KEY");
const azureSettings = settings.providerSettings?.azure as
| AzureProviderSetting
| undefined;
const azureApiKeyFromSettings = (
azureSettings?.apiKey?.value ?? ""
).trim();
const azureResourceNameFromSettings = (
azureSettings?.resourceName ?? ""
).trim();
const envResourceName = (getEnvVar("AZURE_RESOURCE_NAME") ?? "").trim();
const envAzureApiKey = (getEnvVar("AZURE_API_KEY") ?? "").trim();
const resourceName = azureResourceNameFromSettings || envResourceName;
const azureApiKey = azureApiKeyFromSettings || envAzureApiKey;
if (!resourceName) {
throw new Error(
"Azure OpenAI resource name is required. Please set the AZURE_RESOURCE_NAME environment variable.",
"Azure OpenAI resource name is required. Provide it in Settings or set the AZURE_RESOURCE_NAME environment variable.",
);
}
if (!azureApiKey) {
throw new Error(
"Azure OpenAI API key is required. Please set the AZURE_API_KEY environment variable.",
"Azure OpenAI API key is required. Provide it in Settings or set the AZURE_API_KEY environment variable.",
);
}
// Use the default Azure provider with environment variables
// The azure provider automatically picks up AZURE_RESOURCE_NAME and AZURE_API_KEY
const provider = createAzure({
resourceName,
apiKey: azureApiKey,
});
return {
modelClient: {
model: azure(model.name),
model: provider(model.name),
builtinProviderId: providerId,
},
backupModelClients: [],

View File

@@ -98,6 +98,11 @@ export const RegularProviderSettingSchema = z.object({
apiKey: SecretSchema.optional(),
});
export const AzureProviderSettingSchema = z.object({
apiKey: SecretSchema.optional(),
resourceName: z.string().optional(),
});
export const VertexProviderSettingSchema = z.object({
// We make this undefined so that it makes existing callsites easier.
apiKey: z.undefined(),
@@ -109,6 +114,7 @@ export const VertexProviderSettingSchema = z.object({
export const ProviderSettingSchema = z.union([
// Must use more specific type first!
// Zod uses the first type that matches.
AzureProviderSettingSchema,
VertexProviderSettingSchema,
RegularProviderSettingSchema,
]);
@@ -120,6 +126,7 @@ export type ProviderSetting = z.infer<typeof ProviderSettingSchema>;
export type RegularProviderSetting = z.infer<
typeof RegularProviderSettingSchema
>;
export type AzureProviderSetting = z.infer<typeof AzureProviderSettingSchema>;
export type VertexProviderSetting = z.infer<typeof VertexProviderSettingSchema>;
export const RuntimeModeSchema = z.enum(["web-sandbox", "local-node", "unset"]);