From 48e480536c833300a12e1ad820bbe365cadb0ec4 Mon Sep 17 00:00:00 2001 From: Louis Date: Tue, 23 Sep 2025 14:32:48 +0700 Subject: [PATCH 1/2] feat: add azure as first class provider --- .../public/images/model-provider/azure.svg | 23 ++++++++++++ web-app/src/consts/providers.ts | 35 +++++++++++++++++++ web-app/src/containers/dialogs/AddModel.tsx | 8 ++--- web-app/src/lib/utils.ts | 4 ++- web-app/src/services/providers/tauri.ts | 8 ++--- web-app/src/services/providers/web.ts | 25 ++++++++----- 6 files changed, 86 insertions(+), 17 deletions(-) create mode 100644 web-app/public/images/model-provider/azure.svg diff --git a/web-app/public/images/model-provider/azure.svg b/web-app/public/images/model-provider/azure.svg new file mode 100644 index 0000000000..4c86f9e252 --- /dev/null +++ b/web-app/public/images/model-provider/azure.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web-app/src/consts/providers.ts b/web-app/src/consts/providers.ts index 9f40177bbd..30d7452f68 100644 --- a/web-app/src/consts/providers.ts +++ b/web-app/src/consts/providers.ts @@ -59,6 +59,41 @@ export const predefinedProviders = [ ], models: [], }, + { + active: true, + api_key: '', + base_url: 'https://YOUR-RESOURCE-NAME.openai.azure.com/openai/v1', + explore_models_url: + 'https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models', + provider: 'azure', + settings: [ + { + key: 'api-key', + title: 'API Key', + description: + 'The Azure OpenAI API uses API keys for authentication. Visit your [Azure OpenAI Studio](https://oai.azure.com/) to retrieve the API key from your resource.', + controller_type: 'input', + controller_props: { + placeholder: 'Insert API Key', + value: '', + type: 'password', + input_actions: ['unobscure', 'copy'], + }, + }, + { + key: 'base-url', + title: 'Base URL', + description: + 'Your Azure OpenAI resource endpoint. See the [Azure OpenAI documentation](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/latest) for more information.', + controller_type: 'input', + controller_props: { + placeholder: 'https://YOUR-RESOURCE-NAME.openai.azure.com/openai/v1', + value: 'https://YOUR-RESOURCE-NAME.openai.azure.com/openai/v1', + }, + }, + ], + models: [], + }, { active: true, api_key: '', diff --git a/web-app/src/containers/dialogs/AddModel.tsx b/web-app/src/containers/dialogs/AddModel.tsx index a8f51f36d5..2b87fb2227 100644 --- a/web-app/src/containers/dialogs/AddModel.tsx +++ b/web-app/src/containers/dialogs/AddModel.tsx @@ -51,15 +51,15 @@ export const DialogAddModel = ({ provider, trigger }: DialogAddModelProps) => { ( providerModels[ provider.provider as unknown as keyof typeof providerModels - ].supportsToolCalls as unknown as string[] - ).includes(modelId) + ]?.supportsToolCalls as unknown as string[] + )?.includes(modelId) ? ModelCapabilities.TOOLS : undefined, ( providerModels[ provider.provider as unknown as keyof typeof providerModels - ].supportsImages as unknown as string[] - ).includes(modelId) + ]?.supportsImages as unknown as string[] + )?.includes(modelId) ? ModelCapabilities.VISION : undefined, ].filter(Boolean) as string[], diff --git a/web-app/src/lib/utils.ts b/web-app/src/lib/utils.ts index 0d3fa8f61c..60a0557202 100644 --- a/web-app/src/lib/utils.ts +++ b/web-app/src/lib/utils.ts @@ -28,6 +28,8 @@ export function getProviderLogo(provider: string) { return '/images/model-provider/gemini.svg' case 'openai': return '/images/model-provider/openai.svg' + case 'azure': + return '/images/model-provider/azure.svg' default: return undefined } @@ -161,5 +163,5 @@ export function formatDuration(startTime: number, endTime?: number): string { } export function sanitizeModelId(modelId: string): string { - return modelId.replace(/[^a-zA-Z0-9/_\-.]/g, '').replace(/\./g, "_") + return modelId.replace(/[^a-zA-Z0-9/_\-.]/g, '').replace(/\./g, '_') } diff --git a/web-app/src/services/providers/tauri.ts b/web-app/src/services/providers/tauri.ts index 8e546987ff..50f1217da3 100644 --- a/web-app/src/services/providers/tauri.ts +++ b/web-app/src/services/providers/tauri.ts @@ -35,15 +35,15 @@ export class TauriProvidersService extends DefaultProvidersService { ( providerModels[ provider.provider as unknown as keyof typeof providerModels - ].supportsToolCalls as unknown as string[] - ).includes(model) + ]?.supportsToolCalls as unknown as string[] + )?.includes(model) ? ModelCapabilities.TOOLS : undefined, ( providerModels[ provider.provider as unknown as keyof typeof providerModels - ].supportsImages as unknown as string[] - ).includes(model) + ]?.supportsImages as unknown as string[] + )?.includes(model) ? ModelCapabilities.VISION : undefined, ].filter(Boolean) as string[] diff --git a/web-app/src/services/providers/web.ts b/web-app/src/services/providers/web.ts index 5ad426a11a..6a7865be82 100644 --- a/web-app/src/services/providers/web.ts +++ b/web-app/src/services/providers/web.ts @@ -93,8 +93,8 @@ export class WebProvidersService implements ProvidersService { ( providerModels[ provider.provider as unknown as keyof typeof providerModels - ].supportsToolCalls as unknown as string[] - ).includes(model) + ]?.supportsToolCalls as unknown as string[] + )?.includes(model) ? ModelCapabilities.TOOLS : undefined, ].filter(Boolean) as string[] @@ -163,7 +163,9 @@ export class WebProvidersService implements ProvidersService { // Handle different response formats that providers might use if (data.data && Array.isArray(data.data)) { // OpenAI format: { data: [{ id: "model-id" }, ...] } - return data.data.map((model: { id: string }) => model.id).filter(Boolean) + return data.data + .map((model: { id: string }) => model.id) + .filter(Boolean) } else if (Array.isArray(data)) { // Direct array format: ["model-id1", "model-id2", ...] return data @@ -189,11 +191,15 @@ export class WebProvidersService implements ProvidersService { 'Authentication failed', 'Access forbidden', 'Models endpoint not found', - 'Failed to fetch models from' + 'Failed to fetch models from', ] - if (error instanceof Error && - structuredErrorPrefixes.some(prefix => (error as Error).message.startsWith(prefix))) { + if ( + error instanceof Error && + structuredErrorPrefixes.some((prefix) => + (error as Error).message.startsWith(prefix) + ) + ) { throw new Error(error.message) } @@ -211,7 +217,10 @@ export class WebProvidersService implements ProvidersService { } } - async updateSettings(providerName: string, settings: ProviderSetting[]): Promise { + async updateSettings( + providerName: string, + settings: ProviderSetting[] + ): Promise { await ExtensionManager.getInstance() .getEngine(providerName) ?.updateSettings( @@ -233,4 +242,4 @@ export class WebProvidersService implements ProvidersService { // Web implementation uses regular fetch return fetch } -} \ No newline at end of file +} From 71f684e82c95eee42417975e9bdb326d9b9e665d Mon Sep 17 00:00:00 2001 From: Louis Date: Tue, 23 Sep 2025 14:35:38 +0700 Subject: [PATCH 2/2] fix: deployment url --- web-app/src/consts/providers.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web-app/src/consts/providers.ts b/web-app/src/consts/providers.ts index 30d7452f68..328ba3a9ad 100644 --- a/web-app/src/consts/providers.ts +++ b/web-app/src/consts/providers.ts @@ -63,8 +63,7 @@ export const predefinedProviders = [ active: true, api_key: '', base_url: 'https://YOUR-RESOURCE-NAME.openai.azure.com/openai/v1', - explore_models_url: - 'https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models', + explore_models_url: 'https://oai.azure.com/deployments', provider: 'azure', settings: [ {