diff --git a/apps/desktop/src/components/settings/ai/llm/configure.tsx b/apps/desktop/src/components/settings/ai/llm/configure.tsx index 6f85a860d..6ed456ac2 100644 --- a/apps/desktop/src/components/settings/ai/llm/configure.tsx +++ b/apps/desktop/src/components/settings/ai/llm/configure.tsx @@ -16,10 +16,7 @@ export function ConfigureProviders() { {PROVIDERS.map((provider) => ( ))} @@ -27,25 +24,15 @@ export function ConfigureProviders() { ); } -function ProviderCard({ - providerId, - providerName, - icon, - providerConfig, -}: { - providerId: ProviderId; - providerName: string; - icon: React.ReactNode; - providerConfig: typeof PROVIDERS[number]; -}) { - const [provider, setProvider] = useProvider(providerId); +function ProviderCard({ config }: { config: typeof PROVIDERS[number] }) { + const [provider, setProvider] = useProvider(config.id); const form = useForm({ onSubmit: ({ value }) => setProvider(value), defaultValues: provider ?? ({ type: "llm", - base_url: "", + base_url: config.baseUrl ?? "", api_key: "", } satisfies internal.AIProvider), listeners: { @@ -65,22 +52,22 @@ function ProviderCard({ return (
- {icon} - {providerName} - {providerConfig.badge && ( + {config.icon} + {config.displayName} + {config.badge && ( - {providerConfig.badge} + {config.badge} )}
- +
{ @@ -88,28 +75,48 @@ function ProviderCard({ e.stopPropagation(); }} > - - {(field) => ( - - - {(field) => ( - + {!config.baseUrl && ( + + {(field) => ( + + )} + + )} + {config?.apiKey && ( + + {(field) => ( + + )} + + )} + {config.baseUrl && ( +
+ + Advanced + +
+ + {(field) => ( + + )} + +
+
+ )}
@@ -119,6 +126,8 @@ function ProviderCard({ function ProviderContext({ providerId }: { providerId: ProviderId }) { const content = providerId === "hyprnote" ? "Hyprnote is great" + : providerId === "custom" + ? "We only support **OpenAI compatible** endpoints for now." : ""; if (!content) { diff --git a/apps/desktop/src/components/settings/ai/llm/shared.tsx b/apps/desktop/src/components/settings/ai/llm/shared.tsx index 0d80d60e3..1b090e13d 100644 --- a/apps/desktop/src/components/settings/ai/llm/shared.tsx +++ b/apps/desktop/src/components/settings/ai/llm/shared.tsx @@ -1,3 +1,4 @@ +import { Icon } from "@iconify-icon/react"; import { Anthropic, LmStudio, Ollama, OpenAI, OpenRouter } from "@lobehub/icons"; export type ProviderId = typeof PROVIDERS[number]["id"]; @@ -9,7 +10,7 @@ export const PROVIDERS = [ badge: "Recommended", icon: Hyprnote, apiKey: false, - baseUrl: { value: "https://api.hyprnote.com/v1", immutable: true }, + baseUrl: "https://api.hyprnote.com/v1", }, { id: "openai", @@ -17,7 +18,7 @@ export const PROVIDERS = [ badge: null, icon: , apiKey: true, - baseUrl: { value: "https://api.openai.com/v1", immutable: true }, + baseUrl: "https://api.openai.com/v1", }, { id: "anthropic", @@ -25,7 +26,7 @@ export const PROVIDERS = [ badge: null, icon: , apiKey: true, - baseUrl: { value: "https://api.anthropic.com/v1", immutable: true }, + baseUrl: "https://api.anthropic.com/v1", }, { id: "openrouter", @@ -33,7 +34,7 @@ export const PROVIDERS = [ badge: null, icon: , apiKey: true, - baseUrl: { value: "https://openrouter.ai/api/v1", immutable: true }, + baseUrl: "https://openrouter.ai/api/v1", }, { id: "ollama", @@ -41,7 +42,7 @@ export const PROVIDERS = [ badge: null, icon: , apiKey: false, - baseUrl: { value: "http://localhost:11434", immutable: false }, + baseUrl: "http://localhost:11434", }, { id: "lmstudio", @@ -49,6 +50,14 @@ export const PROVIDERS = [ badge: null, icon: , apiKey: false, - baseUrl: { value: "http://localhost:8000", immutable: false }, + baseUrl: "http://localhost:8000", + }, + { + id: "custom", + displayName: "Custom", + badge: null, + icon: , + apiKey: true, + baseUrl: undefined, }, ] as const; diff --git a/apps/desktop/src/components/settings/ai/shared/index.tsx b/apps/desktop/src/components/settings/ai/shared/index.tsx index eb0edec35..90d32880f 100644 --- a/apps/desktop/src/components/settings/ai/shared/index.tsx +++ b/apps/desktop/src/components/settings/ai/shared/index.tsx @@ -26,23 +26,25 @@ export function FormField({ icon, placeholder, type, - hidden, }: { field: AnyFieldApi; label: string; icon: string; placeholder?: string; type?: string; - hidden?: boolean; }) { - const { meta: { errors, isTouched, isDirty } } = field.state; - const hasError = isDirty && isTouched && errors && errors.length > 0; + const { meta: { errors, isTouched } } = field.state; + const hasError = isTouched && errors && errors.length > 0; const errorMessage = hasError - ? (typeof errors[0] === "string" ? errors[0] : (errors[0] as any)?.message || "Invalid value") + ? (typeof errors[0] === "string" + ? errors[0] + : "message" in errors[0] + ? errors[0].message + : JSON.stringify(errors[0])) : null; return ( -