New Ollama Models command: list what the configured host can reach, filter with the search bar, pick the model Lam uses, unload one that is holding memory, and on a server you run, pull and delete models.
Also fixes two bugs and a prompt gap found while testing this against a local server.
Unload
GET /api/ps reports what is loaded right now, and those rows get a green loaded tag, so "why is my GPU busy" is answered in the same place as "what do I have". Loaded rows offer Unload Model, which frees the memory without deleting anything.
No confirmation, unlike deleting: nothing leaves the disk, and the worst case is that the next question pays to load the model again. The toast reports what was freed.
It is POST /api/generate with keep_alive: 0 and no prompt, which is how Ollama says "release this now" and what its own ollama stop does. Verified against 0.35.1 with the model genuinely loaded:
1. before: []
2. after loading: [ "ornith-1.5:9b (11.0 GB in memory)" ]
3. unloaded ornith-1.5:9b, freed 11.0 GB
4. after unloading: []
The reply is {"done":true,"done_reason":"unload"}. The DELETE form of the route answers 405, so there is no delete-shaped alternative.
A host that does not serve /api/ps (older builds answer 404) shows no loaded tags and nothing else changes. That check resolving to nothing is not an error, and it must not take the model list down with it.
Fix: web tools were 404ing on a local host
The extension posted to the cloud path on every host. The two kinds of host serve the same operations at different paths, probed directly:
/api/web_search
/api/experimental/web_search
ollama.com
401 (exists, needs a key)
404
localhost:11434
404
200, with real results
So there is no path that works for both. The path is now chosen by host, and the other is tried if the first answers 404 (a 404 means the route is absent, so nothing is ever sent twice). Verified through the real tool layer against a local server: web_search and web_fetch both return real content.
The error hint was also wrong ("The web tools require an Ollama Cloud host"), so a failing model was given bad advice on top of a bad request.
Fix: the model had no idea what it was
describeRuntime appends this to the system prompt, after the user's own prompt if set, because none of it is a style choice:
What you are, for questions about your own setup. Do not speculate beyond this:
- You are Lam, running inside the Vicinae launcher on the user's desktop, and answering as
`ornith-1.5:9b` served by the Ollama server at http://localhost:11434, which is reachable
on this machine.
- You see only what the user types or attaches. You cannot read their screen, files,
clipboard, calendar or anything else on the machine, and you cannot act on it.
- A model name ending in `:cloud` is proxied by that server to ollama.com, so the conversation
leaves this machine when it is used. Anything else is running locally.
- You have `web_search` and `web_fetch`. Use them rather than guessing about anything recent,
and say so if they fail.
- You can read images. A screenshot attached to a question is yours to describe, so describe it.
Before this, a local model described Vicinae as "a desktop launcher for Ollama", reported its own web tools as not configured, and answered a question about an attached screenshot by reasoning out loud about whether it was able to see, instead of describing it.
The :cloud line states Ollama's naming convention rather than looking it up. The authoritative field is remote_host on /api/tags, which this command reads and shows as a cloud tag, but pulling it into the chat would cost a second request per model change for one sentence.
Why a pick is not written to the preference
An extension cannot write its own preferences: @vicinae/api has getPreferenceValues and no setter. The only way to change the preference would be to write Vicinae's config file, and that does not work. Measured, not assumed:
Pointed lam-agent's ollamaHost at a local listener that logged every request, in ~/.config/vicinae/settings.json
Launched the setup command with vicinae-cli cmd launch
The listener received nothing; the requests went to the old host
Restored the file, verified byte-identical
Preferences are read once at startup and held in memory, so a write applies only after a Vicinae restart. It would also be a lost update (Vicinae owns and rewrites that file) and undocumented, so it would break on an update.
So the pick goes to LocalStorage and loadPreferences layers it over the preference. The cost is surfaced rather than hidden: while a pick is active, editing the Model preference has no effect. The setup screen shows both values and offers Reset to the Preference.
Other API details verified against Ollama 0.35.1
DELETE /api/delete, not POST: the POST form answers 405 method not allowed.
POST /api/pull reports a bad name as HTTP 200 with an error in the body, so the stream is checked and not just the status. Same trap the chat stream already had.
A local daemon's list mixes real weights with :cloud entries proxied to ollama.com (a few hundred bytes, remote_model/remote_host), so the two are told apart rather than both being called installed. Deleting a cloud entry drops a reference, not gigabytes.
/api/tags includes capabilities and size; both are optional, so a server that omits them shows nothing rather than implying "no vision".
formatBytes uses decimal units and matches ollama list exactly (6550813657 bytes prints as 6.6 GB in both).
Also
loadPreferences is now the single preferences reader. getLamPreferences is no longer exported, because a second synchronous reader would look right and silently ignore the pick. Views use hooks/usePreferences.
ChatView is split into an outer loader and ChatSession, so useChat is only created once the model is known.
"unknown" and "no" are kept distinct wherever vision is reported: canSeeImages stays fail-closed for the snip action, but the prompt gets the tri-state, so a failed check is never reported to the model as an inability.
Verification
487 tests pass.
tsc --noEmit, eslint ., vici lint, vici build all clean.
Real end-to-end against a local daemon using only deliberate, reversible calls: the full load/ps/unload cycle above, listModels parsing, deleteModel on a missing name (which proves the DELETE route without deleting anything), pullModel on a bad name (which proves the inline-error path having downloaded nothing), and both web tools returning real content.
Command launched via vicinae-cli cmd launch: /api/ps and /api/tags both requested, 200, no worker errors.
New **Ollama Models** command: list what the configured host can reach, filter with the search bar, pick the model Lam uses, unload one that is holding memory, and on a server you run, pull and delete models.
Also fixes two bugs and a prompt gap found while testing this against a local server.
## Unload
`GET /api/ps` reports what is loaded right now, and those rows get a green `loaded` tag, so "why is my GPU busy" is answered in the same place as "what do I have". Loaded rows offer **Unload Model**, which frees the memory without deleting anything.
No confirmation, unlike deleting: nothing leaves the disk, and the worst case is that the next question pays to load the model again. The toast reports what was freed.
It is `POST /api/generate` with `keep_alive: 0` and no prompt, which is how Ollama says "release this now" and what its own `ollama stop` does. Verified against 0.35.1 with the model genuinely loaded:
```
1. before: []
2. after loading: [ "ornith-1.5:9b (11.0 GB in memory)" ]
3. unloaded ornith-1.5:9b, freed 11.0 GB
4. after unloading: []
```
The reply is `{"done":true,"done_reason":"unload"}`. The `DELETE` form of the route answers 405, so there is no delete-shaped alternative.
A host that does not serve `/api/ps` (older builds answer 404) shows no loaded tags and nothing else changes. That check resolving to nothing is not an error, and it must not take the model list down with it.
## Fix: web tools were 404ing on a local host
The extension posted to the cloud path on every host. The two kinds of host serve the same operations at **different paths**, probed directly:
| | `/api/web_search` | `/api/experimental/web_search` |
| --- | --- | --- |
| `ollama.com` | **401** (exists, needs a key) | 404 |
| `localhost:11434` | 404 | **200**, with real results |
So there is no path that works for both. The path is now chosen by host, and the other is tried if the first answers 404 (a 404 means the route is absent, so nothing is ever sent twice). Verified through the real tool layer against a local server: `web_search` and `web_fetch` both return real content.
The error hint was also wrong ("The web tools require an Ollama Cloud host"), so a failing model was given bad advice on top of a bad request.
## Fix: the model had no idea what it was
`describeRuntime` appends this to the system prompt, after the user's own prompt if set, because none of it is a style choice:
```
What you are, for questions about your own setup. Do not speculate beyond this:
- You are Lam, running inside the Vicinae launcher on the user's desktop, and answering as
`ornith-1.5:9b` served by the Ollama server at http://localhost:11434, which is reachable
on this machine.
- You see only what the user types or attaches. You cannot read their screen, files,
clipboard, calendar or anything else on the machine, and you cannot act on it.
- A model name ending in `:cloud` is proxied by that server to ollama.com, so the conversation
leaves this machine when it is used. Anything else is running locally.
- You have `web_search` and `web_fetch`. Use them rather than guessing about anything recent,
and say so if they fail.
- You can read images. A screenshot attached to a question is yours to describe, so describe it.
```
Before this, a local model described Vicinae as "a desktop launcher for Ollama", reported its own web tools as not configured, and answered a question about an attached screenshot by reasoning out loud about whether it was able to see, instead of describing it.
The `:cloud` line states Ollama's naming convention rather than looking it up. The authoritative field is `remote_host` on `/api/tags`, which this command reads and shows as a `cloud` tag, but pulling it into the chat would cost a second request per model change for one sentence.
## Why a pick is not written to the preference
An extension cannot write its own preferences: `@vicinae/api` has `getPreferenceValues` and no setter. The only way to change the preference would be to write Vicinae's config file, and **that does not work**. Measured, not assumed:
1. Pointed lam-agent's `ollamaHost` at a local listener that logged every request, in `~/.config/vicinae/settings.json`
2. Launched the setup command with `vicinae-cli cmd launch`
3. The listener received nothing; the requests went to the old host
4. Restored the file, verified byte-identical
Preferences are read once at startup and held in memory, so a write applies only after a Vicinae restart. It would also be a lost update (Vicinae owns and rewrites that file) and undocumented, so it would break on an update.
So the pick goes to LocalStorage and `loadPreferences` layers it over the preference. The cost is surfaced rather than hidden: while a pick is active, editing the `Model` preference has no effect. The setup screen shows both values and offers **Reset to the Preference**.
## Other API details verified against Ollama 0.35.1
- `DELETE /api/delete`, not `POST`: the POST form answers `405 method not allowed`.
- `POST /api/pull` reports a bad name as **HTTP 200 with an `error` in the body**, so the stream is checked and not just the status. Same trap the chat stream already had.
- A local daemon's list mixes real weights with `:cloud` entries proxied to ollama.com (a few hundred bytes, `remote_model`/`remote_host`), so the two are told apart rather than both being called installed. Deleting a cloud entry drops a reference, not gigabytes.
- `/api/tags` includes `capabilities` and `size`; both are optional, so a server that omits them shows nothing rather than implying "no vision".
- `formatBytes` uses decimal units and matches `ollama list` exactly (6550813657 bytes prints as 6.6 GB in both).
## Also
- `loadPreferences` is now the single preferences reader. `getLamPreferences` is no longer exported, because a second synchronous reader would look right and silently ignore the pick. Views use `hooks/usePreferences`.
- `ChatView` is split into an outer loader and `ChatSession`, so `useChat` is only created once the model is known.
- `"unknown"` and `"no"` are kept distinct wherever vision is reported: `canSeeImages` stays fail-closed for the snip action, but the prompt gets the tri-state, so a failed check is never reported to the model as an inability.
## Verification
- 487 tests pass.
- `tsc --noEmit`, `eslint .`, `vici lint`, `vici build` all clean.
- Real end-to-end against a local daemon using only deliberate, reversible calls: the full load/ps/unload cycle above, `listModels` parsing, `deleteModel` on a missing name (which proves the DELETE route without deleting anything), `pullModel` on a bad name (which proves the inline-error path having downloaded nothing), and both web tools returning real content.
- Command launched via `vicinae-cli cmd launch`: `/api/ps` and `/api/tags` both requested, 200, no worker errors.
adrianbonpin
changed title from feat: list, pick and manage Ollama models from a command to feat: Ollama models command, with load/pull/delete and unload2026-10-05 09:41:55 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
New Ollama Models command: list what the configured host can reach, filter with the search bar, pick the model Lam uses, unload one that is holding memory, and on a server you run, pull and delete models.
Also fixes two bugs and a prompt gap found while testing this against a local server.
Unload
GET /api/psreports what is loaded right now, and those rows get a greenloadedtag, so "why is my GPU busy" is answered in the same place as "what do I have". Loaded rows offer Unload Model, which frees the memory without deleting anything.No confirmation, unlike deleting: nothing leaves the disk, and the worst case is that the next question pays to load the model again. The toast reports what was freed.
It is
POST /api/generatewithkeep_alive: 0and no prompt, which is how Ollama says "release this now" and what its ownollama stopdoes. Verified against 0.35.1 with the model genuinely loaded:The reply is
{"done":true,"done_reason":"unload"}. TheDELETEform of the route answers 405, so there is no delete-shaped alternative.A host that does not serve
/api/ps(older builds answer 404) shows no loaded tags and nothing else changes. That check resolving to nothing is not an error, and it must not take the model list down with it.Fix: web tools were 404ing on a local host
The extension posted to the cloud path on every host. The two kinds of host serve the same operations at different paths, probed directly:
/api/web_search/api/experimental/web_searchollama.comlocalhost:11434So there is no path that works for both. The path is now chosen by host, and the other is tried if the first answers 404 (a 404 means the route is absent, so nothing is ever sent twice). Verified through the real tool layer against a local server:
web_searchandweb_fetchboth return real content.The error hint was also wrong ("The web tools require an Ollama Cloud host"), so a failing model was given bad advice on top of a bad request.
Fix: the model had no idea what it was
describeRuntimeappends this to the system prompt, after the user's own prompt if set, because none of it is a style choice:Before this, a local model described Vicinae as "a desktop launcher for Ollama", reported its own web tools as not configured, and answered a question about an attached screenshot by reasoning out loud about whether it was able to see, instead of describing it.
The
:cloudline states Ollama's naming convention rather than looking it up. The authoritative field isremote_hoston/api/tags, which this command reads and shows as acloudtag, but pulling it into the chat would cost a second request per model change for one sentence.Why a pick is not written to the preference
An extension cannot write its own preferences:
@vicinae/apihasgetPreferenceValuesand no setter. The only way to change the preference would be to write Vicinae's config file, and that does not work. Measured, not assumed:ollamaHostat a local listener that logged every request, in~/.config/vicinae/settings.jsonvicinae-cli cmd launchPreferences are read once at startup and held in memory, so a write applies only after a Vicinae restart. It would also be a lost update (Vicinae owns and rewrites that file) and undocumented, so it would break on an update.
So the pick goes to LocalStorage and
loadPreferenceslayers it over the preference. The cost is surfaced rather than hidden: while a pick is active, editing theModelpreference has no effect. The setup screen shows both values and offers Reset to the Preference.Other API details verified against Ollama 0.35.1
DELETE /api/delete, notPOST: the POST form answers405 method not allowed.POST /api/pullreports a bad name as HTTP 200 with anerrorin the body, so the stream is checked and not just the status. Same trap the chat stream already had.:cloudentries proxied to ollama.com (a few hundred bytes,remote_model/remote_host), so the two are told apart rather than both being called installed. Deleting a cloud entry drops a reference, not gigabytes./api/tagsincludescapabilitiesandsize; both are optional, so a server that omits them shows nothing rather than implying "no vision".formatBytesuses decimal units and matchesollama listexactly (6550813657 bytes prints as 6.6 GB in both).Also
loadPreferencesis now the single preferences reader.getLamPreferencesis no longer exported, because a second synchronous reader would look right and silently ignore the pick. Views usehooks/usePreferences.ChatViewis split into an outer loader andChatSession, souseChatis only created once the model is known."unknown"and"no"are kept distinct wherever vision is reported:canSeeImagesstays fail-closed for the snip action, but the prompt gets the tri-state, so a failed check is never reported to the model as an inability.Verification
tsc --noEmit,eslint .,vici lint,vici buildall clean.listModelsparsing,deleteModelon a missing name (which proves the DELETE route without deleting anything),pullModelon a bad name (which proves the inline-error path having downloaded nothing), and both web tools returning real content.vicinae-cli cmd launch:/api/psand/api/tagsboth requested, 200, no worker errors.feat: list, pick and manage Ollama models from a commandto feat: Ollama models command, with load/pull/delete and unload