-
+
{i.name}
+
+ {language.t("settings.providers.tag.custom")}
+
{language.t("dialog.provider.tag.recommended")}
diff --git a/packages/app/src/components/settings-providers.tsx b/packages/app/src/components/settings-providers.tsx
index 86a393b98..abc2bee77 100644
--- a/packages/app/src/components/settings-providers.tsx
+++ b/packages/app/src/components/settings-providers.tsx
@@ -3,13 +3,15 @@ import { useDialog } from "@opencode-ai/ui/context/dialog"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import { Tag } from "@opencode-ai/ui/tag"
import { showToast } from "@opencode-ai/ui/toast"
-import type { IconName } from "@opencode-ai/ui/icons/provider"
+import { iconNames, type IconName } from "@opencode-ai/ui/icons/provider"
import { popularProviders, useProviders } from "@/hooks/use-providers"
import { createMemo, type Component, For, Show } from "solid-js"
import { useLanguage } from "@/context/language"
import { useGlobalSDK } from "@/context/global-sdk"
+import { useGlobalSync } from "@/context/global-sync"
import { DialogConnectProvider } from "./dialog-connect-provider"
import { DialogSelectProvider } from "./dialog-select-provider"
+import { DialogCustomProvider } from "./dialog-custom-provider"
type ProviderSource = "env" | "api" | "config" | "custom"
type ProviderMeta = { source?: ProviderSource }
@@ -18,8 +20,14 @@ export const SettingsProviders: Component = () => {
const dialog = useDialog()
const language = useLanguage()
const globalSDK = useGlobalSDK()
+ const globalSync = useGlobalSync()
const providers = useProviders()
+ const icon = (id: string): IconName => {
+ if (iconNames.includes(id as IconName)) return id as IconName
+ return "synthetic"
+ }
+
const connected = createMemo(() => {
return providers
.connected()
@@ -42,14 +50,53 @@ export const SettingsProviders: Component = () => {
const current = source(item)
if (current === "env") return language.t("settings.providers.tag.environment")
if (current === "api") return language.t("provider.connect.method.apiKey")
- if (current === "config") return language.t("settings.providers.tag.config")
+ if (current === "config") {
+ const id = (item as { id?: string }).id
+ if (id && isConfigCustom(id)) return language.t("settings.providers.tag.custom")
+ return language.t("settings.providers.tag.config")
+ }
if (current === "custom") return language.t("settings.providers.tag.custom")
return language.t("settings.providers.tag.other")
}
const canDisconnect = (item: unknown) => source(item) !== "env"
+ const isConfigCustom = (providerID: string) => {
+ const provider = globalSync.data.config.provider?.[providerID]
+ if (!provider) return false
+ if (provider.npm !== "@ai-sdk/openai-compatible") return false
+ if (!provider.models || Object.keys(provider.models).length === 0) return false
+ return true
+ }
+
+ const disableProvider = async (providerID: string, name: string) => {
+ const before = globalSync.data.config.disabled_providers ?? []
+ const next = before.includes(providerID) ? before : [...before, providerID]
+ globalSync.set("config", "disabled_providers", next)
+
+ await globalSync
+ .updateConfig({ disabled_providers: next })
+ .then(() => {
+ showToast({
+ variant: "success",
+ icon: "circle-check",
+ title: language.t("provider.disconnect.toast.disconnected.title", { provider: name }),
+ description: language.t("provider.disconnect.toast.disconnected.description", { provider: name }),
+ })
+ })
+ .catch((err: unknown) => {
+ globalSync.set("config", "disabled_providers", before)
+ const message = err instanceof Error ? err.message : String(err)
+ showToast({ title: language.t("common.requestFailed"), description: message })
+ })
+ }
+
const disconnect = async (providerID: string, name: string) => {
+ if (isConfigCustom(providerID)) {
+ await globalSDK.client.auth.remove({ providerID }).catch(() => undefined)
+ await disableProvider(providerID, name)
+ return
+ }
await globalSDK.client.auth
.remove({ providerID })
.then(async () => {
@@ -91,7 +138,7 @@ export const SettingsProviders: Component = () => {
{(item) => (
-
+
{item.name}
{type(item)}
@@ -122,7 +169,7 @@ export const SettingsProviders: Component = () => {
-
+
{item.name}
{language.t("dialog.provider.tag.recommended")}
@@ -177,6 +224,27 @@ export const SettingsProviders: Component = () => {
)}
+
+
+
+
+
+
Custom provider
+
{language.t("settings.providers.tag.custom")}
+
+
Add an OpenAI-compatible provider by base URL.
+
+
+