Desktop Material

Ollama model manager

Desktop Material manages an Ollama provider without exposing the native API as a free-form request editor. There are two ways in.

Settings → Ollama is the standalone tab and the primary route. It renders the lifecycle workspace directly and never depends on Copilot access or a Copilot licence, so it is reachable while signed out. The command palette entries Ollama model manager and Ollama chat open this tab, as do the settings-search results for Ollama.

Settings → Copilot → Providers remains available for users who manage Ollama alongside their other Copilot model providers. Add the Ollama (local) provider preset, save it, and choose Manage models on that provider to open the same lifecycle workspace.

Setup state

When no Ollama provider is configured yet, the standalone tab shows a setup state rather than any Copilot content. It offers an endpoint field prefilled with http://127.0.0.1:11434, a Connect action, and short guidance on installing and starting Ollama.

Connecting validates the endpoint with the same loopback rules the client enforces, canonicalizes it to its origin, and probes /api/version before anything is saved. Only after that probe succeeds is a managed provider persisted (type: openai, authKind: none, integration: ollama, base URL <origin>/v1) — identical to a provider created through the Copilot provider dialog, so both routes manage the same record.

The Connect button stays enabled for an invalid endpoint on purpose: activating it explains the problem in an alert that is associated with the endpoint field through aria-describedby, which a disabled control could not announce. A non-loopback host is rejected before any network request is made. A failed probe or a failed save reports the specific reason and leaves the tab unconfigured rather than appearing connected.

When more than one Ollama provider exists, a labelled picker selects which one the tab manages; it is hidden for the common single-endpoint case.

The provider's configured URL serves two related purposes. Copilot requests use Ollama's OpenAI-compatible endpoint, while the manager derives its fixed native routes from the same trusted origin. The saved provider URL must be an exact loopback /v1 base; remote hosts, arbitrary path prefixes, and a saved /api base are rejected. The manager derives the loopback origin and appends only its fixed native /api/* routes. The default preset is http://127.0.0.1:11434/v1; no API key is required.

Inventory and inspection

The manager reports endpoint health and version, then loads installed and currently running models independently. A partial response keeps usable data visible and identifies the unavailable scope instead of clearing the whole workspace.

Ollama may omit metadata or report it differently across model families. The manager labels missing values rather than manufacturing them, bounds displayed text, and treats installed and running inventories as separate sources of truth.

Model lifecycle

After a successful inventory-changing operation, the installed Ollama inventory is synchronized back to that provider's selectable Copilot model list. The refreshed inventory is authoritative: stale configured entries are removed, new installed names are added, and existing per-model reasoning settings are retained where identifiers still match. If Ollama succeeds but provider persistence fails, the manager reports that split outcome instead of claiming a complete operation.

Versioned multi-chat workspace

Expanding Chat opens a provider-scoped workspace instead of one volatile transcript. A chat can be created, selected, renamed, or deleted independently, and changing that chat's model keeps its existing transcript intact. Deletion names the conversation, requires inline confirmation, and removes only that conversation and its local history.

Each chat owns a separate local Git repository below the app's user-data directory. The provider identity and endpoint are hashed before they become a directory name; every conversation then uses a generated UUID. User messages are committed before a network request starts, completed assistant messages are committed after the stream finishes, and stopping a response commits any partial text already received. Appearance, font, model, and title changes are versioned in the same conversation repository. The shared history viewer can undo, redo, or restore a selected point by adding an audit commit; it never resets or rewrites an earlier successful commit.

The workspace also provides:

Attached images use Ollama's native chat images field. The workspace validates the payload boundary, but the selected model still needs vision support; Ollama can reject an otherwise valid image request when that model does not accept images. A failed or cancelled request does not discard the already committed user prompt. A corrupt or oversized session is ignored during discovery and cannot escape its provider-owned directory; Git or filesystem failures surface as a bounded workspace error instead of silently falling back to unversioned memory.

Endpoint, privacy, and recovery boundaries

Only HTTP or HTTPS loopback URLs such as localhost, 127.0.0.1, and [::1] are accepted. The saved path must be exactly /v1; every remote host, arbitrary prefix, embedded credential, query string, fragment, or saved /api base is rejected. The manager derives only that loopback origin and appends fixed native /api/* routes, and operation errors are bounded before display. Provider tokens are not required for Ollama and are never added to management URLs, process arguments, logs, or repository files.

Chat transcripts, image bytes, appearance, and font settings stay in the local provider-scoped repositories. They leave the machine only when the user sends a chat request to the configured loopback Ollama endpoint. The workspace stores no provider URL, credential, arbitrary file path, or rendered data URL inside a conversation document.

Loading, empty, unavailable, partial, cancellation, validation, and operation failure states remain distinct. Refreshing or changing providers aborts stale requests, pull cancellation owns only the active pull, and one model operation cannot silently target another model.

All visible labels, status announcements, validation messages, confirmations, and accessible names follow the app's English, playful Hong Kong Cantonese, or English / 香港粵語 language mode. The workspace is keyboard reachable, announces changing progress and results, and reflows at compact window sizes.

Ollama API references

The manager uses Ollama's documented native API contracts for installed models, running models, model details, pull, copy, delete, and version. Pull progress uses Ollama's streaming NDJSON response format. Loading and unloading use the documented generate keep-alive behavior; model creation and registry publishing are outside this manager's scope.

Verification

Acceptance covers endpoint validation and normalization, bounded response parsing, health/version and partial inventory behavior, streamed pull progress and cancellation, copy/rename partial results, load/unload, confirmed deletion, provider-model synchronization, stale-request suppression, language modes, keyboard/accessibility semantics, responsive styling, and a deterministic loopback Ollama fixture.

Focused chat acceptance additionally covers independent repository discovery, message/image/appearance/font commits, non-rewriting undo/redo/restore, malformed-image rejection, bounded native chat serialization, streamed UI updates, cancellation, model changes without transcript loss, localized history controls, and compact workspace styling.

Standalone-tab acceptance covers the unconfigured setup state rendering without any Copilot sign-in content, a non-Ollama BYOK provider still counting as unconfigured, loopback rejection before any network request, endpoint canonicalization from a /v1 base, the exact persisted provider record, alert association with the endpoint field, probe and save failures leaving the tab unconfigured, the manager rendering directly for a configured provider, and the multi-provider picker. See app/test/unit/ui/ollama-preferences-test.tsx; the tab's own copy is pinned in both catalogs by app/test/unit/ollama-manager-i18n-test.ts.

Exact source 27ffc1af7dd1223809c69ea0f72ddab369869f31 completed the required low-level-MCP production build in 213.16 seconds. The deterministic lifecycle exercise covered health, inventory, search, running state, pull cancellation with rollback, completed pull, copy, rename, load, unload, confirmed delete, and provider synchronization. Its accepted synthetic-only capture is 1452×1001, 128,903 bytes, and SHA-256 f1735c664248cd1b10a64e672dbbab24c95dabab99a62deeaf93557145a36509. The manager, Preferences shell, and controls remain contained above the footer with zero overlaps and no horizontal overflow; privacy inspection passed and owned runtime cleanup completed. Final exact-main Windows CI, Pages, and wiki publication checks are intentionally recorded later in HANDOFF.md.