Skip to content

feat(cli): widen list_models to expose model family (#128, Slice B-i)#172

Merged
jasonssdev merged 1 commit into
mainfrom
feat/list-models-family
Jul 25, 2026
Merged

feat(cli): widen list_models to expose model family (#128, Slice B-i)#172
jasonssdev merged 1 commit into
mainfrom
feat/list-models-family

Conversation

@jasonssdev

Copy link
Copy Markdown
Owner

Summary

Slice B-i of #128 — the foundation for the interactive model picker. Widens OllamaClient.list_models() so a follow-up slice can filter embedding models out of the chat-model list.

  • list_models() now returns list[InstalledModel] (tag + family), parsed from the /api/tags details.family field, instead of a bare list[str].
  • New is_embedding_model() classifies the families {bert, nomic-bert} (case-insensitive) as embeddings. A missing or unknown family is treated as non-embedding — ambiguity never hides a legitimate chat model.
  • Defensive parsing: a missing / null / non-dict details, or a missing / non-str family, yields family=None without dropping the entry or raising; malformed top-level shapes still surface as OllamaError.
  • Every list_models() consumer adapted behavior-preservingly (doctor checks 3/4/5, the init post-write preflight, the model spike). model_tag_matches keeps its list[str] signature; call sites pass [m.tag for m in installed].

Scope

src/openkos/llm/ollama.py (contract), src/openkos/cli/main.py (call-site adaptation), evals/model_spike/run_spike.py (one caller), plus test updates. ~215 lines.

Verification

  • uv run pytest: 2099 passed, exit 0
  • uv run ruff check . && uv run ruff format --check .: clean
  • uv run mypy .: clean
  • SDD verify (B-i scope): PASS — 10/10 scenarios
  • Adversarial reliability review: 0 findings (parsing robust against hostile /api/tags shapes; all consumers adapted; behavior-preserving)

Strict TDD throughout (RED confirmed before each GREEN).

Issue

Advances #128 (Slice B-i). The picker itself lands in Slice B-ii (a follow-up PR), which will close #128.

Foundation for the interactive model picker: OllamaClient.list_models()
now returns list[InstalledModel] (tag + family) parsed from the /api/tags
`details.family` field, instead of a bare list of tags. This lets a
follow-up slice filter embedding models out of the chat-model picker.

- Add the InstalledModel frozen dataclass and is_embedding_model(), which
  classifies the families {bert, nomic-bert} (case-insensitive) as
  embeddings; a missing or unknown family is treated as non-embedding so
  ambiguity never hides a legitimate chat model.
- Parse family defensively: a missing, null, or non-dict `details`, or a
  missing/non-str `family`, yields family=None without dropping the entry
  or raising; malformed top-level shapes still surface as OllamaError.
- Adapt every list_models() consumer behavior-preservingly (doctor checks
  3/4/5, the init post-write preflight, the model spike): model_tag_matches
  keeps its list[str] signature; call sites pass [m.tag for m in installed].

Advances #128 (Slice B-i); the picker itself lands in Slice B-ii.
@jasonssdev
jasonssdev merged commit 0652a64 into main Jul 25, 2026
6 checks passed
@jasonssdev
jasonssdev deleted the feat/list-models-family branch July 25, 2026 04:34
jasonssdev added a commit that referenced this pull request Jul 25, 2026
Merge the llm-client, workspace-init, and doctor-command delta specs into
the main specs tree and move the change folder to the archive. Closes the
SDD cycle for the interactive model picker (both sub-slices: list_models
widening #172 and the picker #173), which closed #128.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: interactive model picker in openkos init (list installed Ollama models)

1 participant