diff --git a/ui/src/adapters/hermes-local/config-fields.tsx b/ui/src/adapters/hermes-local/config-fields.tsx
index a35e48dd..568f2f6a 100644
--- a/ui/src/adapters/hermes-local/config-fields.tsx
+++ b/ui/src/adapters/hermes-local/config-fields.tsx
@@ -1,9 +1,13 @@
+import { useState } from "react";
+import { useQuery } from "@tanstack/react-query";
import type { AdapterConfigFieldsProps } from "../types";
import {
Field,
DraftInput,
} from "../../components/agent-config-primitives";
import { ChoosePathButton } from "../../components/PathInstructionsModal";
+import { useCompany } from "../../context/CompanyContext";
+import { ollamaApi } from "../../api/ollama";
const inputClass =
"w-full rounded-md border border-border px-2.5 py-1.5 bg-transparent outline-none text-sm font-mono placeholder:text-muted-foreground/40";
@@ -19,6 +23,63 @@ export function HermesLocalConfigFields({
mark,
hideInstructionsFile,
}: AdapterConfigFieldsProps) {
+ const { selectedCompanyId } = useCompany();
+ const companyId = selectedCompanyId;
+
+ const [manualEntry, setManualEntry] = useState(false);
+
+ const { data: ollamaStatus } = useQuery({
+ queryKey: ["ollama", "status", companyId],
+ queryFn: () => ollamaApi.status(companyId!),
+ enabled: Boolean(companyId),
+ staleTime: 60_000,
+ });
+
+ const { data: ollamaModels } = useQuery({
+ queryKey: ["ollama", "models", companyId],
+ queryFn: () => ollamaApi.models(companyId!),
+ enabled: Boolean(companyId && ollamaStatus?.installed),
+ staleTime: 60_000,
+ });
+
+ const currentModel = isCreate
+ ? (values!.model ?? "")
+ : eff("adapterConfig", "model", String(config.model ?? ""));
+
+ function handleSelectModel(selectedModel: string) {
+ if (selectedModel === "__manual__") {
+ setManualEntry(true);
+ return;
+ }
+ if (selectedModel === "") return;
+ // Set fields when selecting an Ollama model
+ if (isCreate) {
+ // In create mode, CreateConfigValues does not have provider/base_url;
+ // only model is stored via form values. The server resolves provider
+ // at runtime from the model name or ~/.hermes/config.yaml.
+ set!({ model: selectedModel });
+ } else {
+ // In edit mode, set all three fields atomically on adapterConfig
+ mark("adapterConfig", "model", selectedModel);
+ mark("adapterConfig", "provider", "custom");
+ mark("adapterConfig", "base_url", "http://localhost:11434/v1");
+ }
+ }
+
+ function formatModelLabel(m: { name: string; parameterSize: string; quantization: string; recommended: boolean; recommendationReason: string | null }) {
+ const params = m.parameterSize ? ` (${m.parameterSize}, ${m.quantization})` : "";
+ const recSuffix = m.recommended ? " - Recommended for your system" : "";
+ const star = m.recommended ? "* " : "";
+ return `${star}${m.name}${params}${recSuffix}`;
+ }
+
+ const showDropdown =
+ ollamaStatus?.installed === true &&
+ (ollamaModels?.models?.length ?? 0) > 0 &&
+ !manualEntry;
+
+ const showInstallCallout = ollamaStatus?.installed === false;
+
return (
<>
{!hideInstructionsFile && (
@@ -47,26 +108,59 @@ export function HermesLocalConfigFields({
)}
+
-
- isCreate
- ? set!({ model: v })
- : mark("adapterConfig", "model", v || undefined)
- }
- immediate
- className={inputClass}
- placeholder="anthropic/claude-sonnet-4"
- />
+ {showInstallCallout && (
+
+ )}
+
+ {showDropdown ? (
+
+ ) : (
+
+ isCreate
+ ? set!({ model: v })
+ : mark("adapterConfig", "model", v || undefined)
+ }
+ immediate
+ className={inputClass}
+ placeholder="anthropic/claude-sonnet-4"
+ />
+ )}
+
{!isCreate && (
<>
{
+ return api.get(`/companies/${companyId}/ollama/status`);
+ },
+ models(companyId: string): Promise {
+ return api.get(`/companies/${companyId}/ollama/models`);
+ },
+};