0005. Ollama configuration via instances list only
Date: 2026-04-08
Status
Accepted
Context
Until this change, Ollama could be configured via two coexisting fields on
LLMSettings:
ollama_base_url: str— a single URL field, the original Ollama setting.ollama_instances: list[OllamaInstance]— a list of instances added later to support load balancing across multiple Ollama servers.
Both were live, with code paths threading through every layer of the stack
(load balancer, embedding factory, LLM factory, health checks, CLI, frontend
settings UI). The "single URL" field was treated as a fallback that the load
balancer materialized into a synthetic default instance whenever
ollama_instances was empty. The frontend's "Ollama URL" form field wrote
to ollama_base_url; the multi-instance editor wrote to ollama_instances.
Result: two sources of truth, drift risk between them, and an explicit
"backwards compat" fallback in OllamaLoadBalancer.reload_config.
Why we cared
CLAUDE.md is unambiguous about backwards-compat shims:
NEVER add support for legacy/deprecated formats no longer in use. If you discover legacy support code, propose removing it.
This refactor was discovered during a CLAUDE.md compliance audit. The reviewer flagged the "Backward compatibility: if no instances configured, create one from legacy URL" branch in the load balancer as a textbook violation. Chaos Cypher is pre-production, so no migration window was required — we could rip and replace.
Decision
Remove ollama_base_url from LLMSettings entirely. Make
ollama_instances the sole source of truth for Ollama URLs:
LLMSettings.ollama_instancesnow defaults to a one-element list containing a seeded instance withid="default",name="Default",base_url="http://host.docker.internal:11434".- A new
LLMSettings.primary_ollama_urlcomputed property returns the first enabled instance's URL. Health checks, the CLI URL probe, the embedding factory fallback, and any other "give me an Ollama URL" caller use this property. - The load balancer's "no instances → synthesize from legacy URL" branch is
deleted. The default factory ensures
ollama_instancesis always populated, so the branch was unreachable in practice anyway. - The frontend's "single Ollama URL" form (
OllamaUrlField) now reads and writesollama_instances[0].base_url. The multi-instance editor remains the only way to configure additional GPUs. - The first-run setup wizard writes a single seeded instance instead of a top-level URL field.
- The CLI's
ollama_urlfield (its own user-visible config name) is now materialized into a single instance dict when constructingLLMSettings, rather than being passed asollama_base_url.
Consequences
Positive
- One source of truth for Ollama URLs.
- The load balancer's fallback branch is gone, removing dead-code rot risk.
- The frontend Settings UI is unchanged for single-URL users — the form field still exists, it just reads/writes a different backing field.
- New users who never touch the URL get a working default that matches what the all-in-one Docker container expects.
Negative
extra="forbid"onLLMSettingsmeans any user with a stalesettings.yamlcontainingollama_base_urlwill get a Pydantic validation error on container startup. Acceptable because we are pre-production and have no installed user base to migrate. If we ever need to support this scenario in the future, the migration path is to add amodel_validatoronLLMSettingsthat reads a legacyollama_base_urlkey out of the input dict and seeds the default instance from it before validation runs.
Files touched
packages/core/src/chaoscypher_core/settings.pypackages/core/src/chaoscypher_core/adapters/llm/load_balancer.pypackages/core/src/chaoscypher_core/adapters/llm/factory.pypackages/core/src/chaoscypher_core/adapters/llm/providers/ollama_provider.pypackages/core/src/chaoscypher_core/adapters/embedding/factory.pypackages/cortex/src/chaoscypher_cortex/features/health/service.pypackages/cortex/src/chaoscypher_cortex/features/settings/ollama_models_api.pypackages/cortex/src/chaoscypher_cortex/features/chats/streaming_utils.pypackages/cli/src/chaoscypher_cli/context.pypackages/cli/src/chaoscypher_cli/config.pypackages/interface/src/types/settings.tspackages/interface/src/pages/settings/hooks/useProviderSettings.tspackages/interface/src/pages/settings/components/ProviderSelector.tsxpackages/interface/src/pages/settings/LLMProviderTab.tsxpackages/interface/src/pages/SetupPage.tsxpackages/core/tests/unit/adapters/embedding/test_factory.pypackages/cortex/tests/unit/features/health/test_health_service.pypackages/docs/docs/getting-started/configuration.mdpackages/docs/docs/developer-guide/llm-providers.mdpackages/docs/docs/reference/api/settings.mdpackages/docs/blog/2026-03-12-local-ai-knowledge-graph.md