nexus/.planning/phases/28-ollama-integration/28-02-PLAN.md
2026-04-04 03:55:48 +00:00

11 KiB

phase plan type wave depends_on files_modified autonomous requirements must_haves
28-ollama-integration 02 execute 2
28-01
ui/src/api/ollama.ts
ui/src/adapters/hermes-local/config-fields.tsx
ui/src/pages/AgentDetail.tsx
true
OLLA-02
OLLA-03
OLLA-05
HERM-05
truths artifacts key_links
When Ollama is installed, the Hermes agent config shows a model dropdown listing all locally pulled models
When an Ollama model is selected, adapterConfig saves model + provider:custom + base_url:http://localhost:11434/v1
When Ollama is absent, the config shows an install callout with a link to https://ollama.com/download
Recommended models are visually highlighted in the dropdown
Hermes native skills show an 'Hermes skill' badge in the Skills tab
Manual model entry still works as fallback when Ollama is absent
path provides exports
ui/src/api/ollama.ts API client for Ollama status and model listing
ollamaApi
path provides contains
ui/src/adapters/hermes-local/config-fields.tsx Ollama model dropdown, install callout, manual fallback ollamaApi
path provides contains
ui/src/pages/AgentDetail.tsx Hermes skill badge in AgentSkillsTab Hermes skill
from to via pattern
ui/src/adapters/hermes-local/config-fields.tsx ui/src/api/ollama.ts useQuery + ollamaApi.status / ollamaApi.models ollamaApi
from to via pattern
ui/src/adapters/hermes-local/config-fields.tsx server/src/routes/ollama.ts fetch /companies/:companyId/ollama/* ollama
Create the UI surface for Ollama model selection in Hermes agent config and improve Hermes skill visibility.

Purpose: Users can discover, browse, and select local Ollama models when configuring a Hermes agent, with hardware-aware recommendations highlighted. When Ollama is absent, users see install instructions. Hermes native skills are clearly labeled in the Skills tab. Output: Working model selector dropdown, install callout, and Hermes skill badges.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/28-ollama-integration/28-RESEARCH.md @.planning/phases/28-ollama-integration/28-01-SUMMARY.md

@ui/src/adapters/hermes-local/config-fields.tsx @ui/src/adapters/types.ts @ui/src/api/client.ts @ui/src/pages/AgentDetail.tsx (AgentSkillsTab around line 2362, unmanagedSkillRows around 2566)

GET /api/companies/:companyId/ollama/status Response: { installed: boolean; version: string | null; installUrl: string }

GET /api/companies/:companyId/ollama/models Response: { models: OllamaModel[]; ramGb: number } Where OllamaModel = { name: string; // e.g. "qwen2.5-coder:32b" parameterSize: string; // e.g. "32.8B" quantization: string; // e.g. "Q4_K_M" sizeBytes: number; family: string; // e.g. "qwen2" recommended: boolean; recommendationReason: string | null; }

AdapterConfigFieldsProps: { mode, isCreate, adapterType, values, set, config, eff, mark, models, hideInstructionsFile }

Task 1: Create ollamaApi client and enhance HermesLocalConfigFields with model dropdown ui/src/api/ollama.ts, ui/src/adapters/hermes-local/config-fields.tsx - ui/src/api/client.ts (full file — for api.get pattern) - ui/src/api/health.ts (for simple API client pattern) - ui/src/adapters/hermes-local/config-fields.tsx (full file — current state) - ui/src/adapters/types.ts (AdapterConfigFieldsProps interface) - .planning/phases/28-ollama-integration/28-RESEARCH.md (Pattern 3, Pattern 4, Pitfall 1, Pitfall 4) 1. Create `ui/src/api/ollama.ts`: - Import `api` from "./client" (the request helper) - Define types: `OllamaStatus { installed: boolean; version: string | null; installUrl: string }`, `OllamaModel { name: string; parameterSize: string; quantization: string; sizeBytes: number; family: string; recommended: boolean; recommendationReason: string | null }`, `OllamaModelsResponse { models: OllamaModel[]; ramGb: number }` - Export `ollamaApi` object with: - `status(companyId: string): Promise` — GET `/companies/${companyId}/ollama/status` - `models(companyId: string): Promise` — GET `/companies/${companyId}/ollama/models`
2. Rewrite `ui/src/adapters/hermes-local/config-fields.tsx`:
   - Add imports: `useQuery` from `@tanstack/react-query`, `ollamaApi` from `../../api/ollama`
   - Need companyId: extract from URL params using `useParams` from react-router-dom, or accept via a context. Check existing config-fields patterns for how companyId is obtained — look at the component's parent to find how it gets companyId. If not available via props, use `useParams<{ companyId?: string }>()` matching the route pattern `/companies/:companyId/agents/...`.
   - Add two queries (only enabled when companyId is truthy):
     ```
     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 })
     ```
   - Replace the current free-text Model `<DraftInput>` with a hybrid control:
     - **When ollamaStatus?.installed AND ollamaModels?.models.length > 0**: Render a `<select>` dropdown with an empty option ("Select a model..."), then each ollamaModels.models item as an `<option>`. Recommended models get a star prefix in label (e.g., "* qwen2.5-coder:7b (7B, Q4_K_M) - Recommended for your system"). Non-recommended models show plain label (e.g., "qwen2.5-coder:32b (32.8B, Q4_K_M)"). Add a final option "Other (manual entry)..." that switches to a text input.
     - **When ollamaStatus?.installed === false**: Render an install callout div: "Ollama is not detected." with a link to ollamaStatus.installUrl ("Install Ollama"). Below the callout, keep the existing DraftInput for manual model entry (user may have a remote provider).
     - **When ollamaStatus is loading or undefined**: Show the existing DraftInput as fallback.
   - CRITICAL (Pitfall 1 + Pitfall 4): When an Ollama model is selected from the dropdown, set ALL THREE fields atomically:
     - Create mode: `set!({ model: selectedModel, provider: "custom", base_url: "http://localhost:11434/v1" })`
     - Edit mode: `mark("adapterConfig", "model", selectedModel)`, `mark("adapterConfig", "provider", "custom")`, `mark("adapterConfig", "base_url", "http://localhost:11434/v1")`
   - When "Other (manual entry)" is selected or when using the fallback text input, do NOT set provider/base_url — user manages those fields themselves.
   - Style the select with the existing `inputClass` CSS classes for consistency.
   - Style the install callout: use `rounded-md border border-amber-500/30 bg-amber-500/5 p-3 text-sm` with an external link icon or similar.
cd /opt/nexus/ui && npx tsc --noEmit 2>&1 | head -20 - grep -q "ollamaApi" ui/src/api/ollama.ts - grep -q "status" ui/src/api/ollama.ts - grep -q "models" ui/src/api/ollama.ts - grep -q "ollamaApi" ui/src/adapters/hermes-local/config-fields.tsx - grep -q "useQuery" ui/src/adapters/hermes-local/config-fields.tsx - grep -q 'provider.*custom' ui/src/adapters/hermes-local/config-fields.tsx - grep -q 'base_url.*localhost:11434' ui/src/adapters/hermes-local/config-fields.tsx - grep -q "ollama.com/download" ui/src/adapters/hermes-local/config-fields.tsx - grep -q "recommended" ui/src/adapters/hermes-local/config-fields.tsx Hermes config shows Ollama model dropdown when Ollama is detected, install callout when absent, and manual fallback. Selecting an Ollama model sets provider:custom + base_url atomically. TypeScript compiles without errors. Task 2: Add Hermes skill badge rendering in AgentSkillsTab ui/src/pages/AgentDetail.tsx - ui/src/pages/AgentDetail.tsx (lines 2550-2600 for unmanagedSkillRows, lines 2950-2970 for originLabel rendering, lines 3050-3070 for unmanagedSkillRows section header) Update the `AgentSkillsTab` component in `AgentDetail.tsx` to better distinguish Hermes native skills:
1. In the unmanagedSkillRows section header (around line 3063), change the label text to be conditional:
   - If `agent.adapterType === "hermes_local"`: display `(${unmanagedSkillRows.length}) Hermes native skills & user-installed skills`
   - Otherwise: keep existing text `(${unmanagedSkillRows.length}) User-installed skills, not managed by ${VOCAB.appName}`

2. In the `renderSkillRow` function (around line 2960-2961), where `skill.readOnly && skill.originLabel` renders a `<p>` with originLabel text:
   - When `skill.originLabel === "Hermes skill"`, render a small inline badge instead of plain text:
     `<span className="inline-flex items-center rounded-full bg-purple-500/10 px-2 py-0.5 text-xs font-medium text-purple-400">Hermes skill</span>`
   - Keep the existing `<p>` rendering for other originLabel values unchanged.

3. The `unmanagedSkillRows` data already flows from the skill registry API which includes Hermes native skills with `originLabel: "Hermes skill"` and `readOnly: true`. No data changes needed.
cd /opt/nexus/ui && npx tsc --noEmit 2>&1 | head -20 - grep -q "Hermes skill" ui/src/pages/AgentDetail.tsx - grep -q "hermes_local" ui/src/pages/AgentDetail.tsx - grep -q "Hermes native" ui/src/pages/AgentDetail.tsx - grep -q "purple" ui/src/pages/AgentDetail.tsx Hermes native skills show a purple "Hermes skill" badge in the Skills tab. Section header indicates "Hermes native skills" when viewing a Hermes agent. TypeScript compiles without errors. - `cd /opt/nexus/ui && npx tsc --noEmit` — no type errors - Config-fields dropdown renders when Ollama is available (verified by code structure) - Install callout renders when Ollama is absent (verified by code structure) - Hermes skill badge uses distinct purple styling

<success_criteria>

  • ollamaApi client exists with status() and models() methods
  • HermesLocalConfigFields shows model dropdown when Ollama detected, install callout when absent
  • Selecting an Ollama model atomically sets model + provider:custom + base_url
  • Manual model entry works as fallback
  • Hermes native skills show "Hermes skill" badge in Skills tab
  • All TypeScript compiles without errors </success_criteria>
After completion, create `.planning/phases/28-ollama-integration/28-02-SUMMARY.md`