Move os-me OllamaEmbedder onto LLM.embed #1470

Closed
opened 2026-10-28 20:12:58 +00:00 by jared · 4 comments
Owner

Problem

plugins/os-me/lib/os_me/ollama_embedder.rb posts to Ollama through Decision::Model::Http::Connection. That is decision-model transport. The cc-os spec (docs/llm-stack/spec.md, build-order step 4) moves this call onto LLM.embed from llm-stack.

Reproduce

n/a: refactor. No defect to reproduce.

Approach

Keep OsMe::OllamaEmbedder#embed(texts). It must still take an Array of Strings and return an Array of vectors. CandidateRefresh calls it through OllamaEmbedder.standard.

Replace the HTTP connection with a call to LLM.embed. Remove the MODEL, BASE_URL, and TIMEOUT constants. Move the model name and the endpoint into an llm-stack profile. Wire LLM.configure at the os-me entry point, as the other os-me llm-stack callers do.

Update plugins/os-me/test/ollama_embedder_test.rb. The fake must stand in for the LLM client, not for post_json. Bump the llm-stack revision in plugins/os-me/Gemfile.lock to the release that ships LLM.embed.

Required work:

  1. Remove the decision-model transport from the embedder — check: absent Decision::Model::Http in plugins/os-me/lib/os_me/ollama_embedder.rb
  2. Call LLM.embed in the embedder — check: present LLM.embed in plugins/os-me/lib/os_me/ollama_embedder.rb
  3. Rewrite plugins/os-me/test/ollama_embedder_test.rb for the new call.
  4. Bump the llm-stack pin in plugins/os-me/Gemfile.lock.

Orchestrator work: run bin/refresh-plugins after the change.

Acceptance criteria

  1. Given two texts, when OsMe::OllamaEmbedder#embed runs, then it returns two vectors in input order.
  2. Given OsMe::OllamaEmbedder, when its source is read, then it holds no reference to Decision::Model::Http.
  3. Given OsMe::CandidateRefresh, when it runs with the standard embedder, then it embeds through LLM.embed.
  4. Given plugins/os-me/Gemfile.lock, when it is read, then its llm-stack revision is one that ships LLM.embed.

Owner sketch

module OsMe
  class OllamaEmbedder
    def embed(texts)
    end
  end
end

Non-goals

  • Moving other os-me code off decision-model transport.
  • Changing PatternClusterer or CandidateRefresh.

Blocked by: jared/llm-stack#5

Origin

  • Trigger: cc-os docs/llm-stack/spec.md, build-order step 4. Jared asked for this ticket and for the matching llm-stack ticket in session 4924697c-7a72-4455-a9f0-ba97fc3f1c6d.
  • Improvised this session: none
  • Chain: os-me embedder uses decision-model transport ← llm-stack had no embed ← DESIGN (docs/llm-stack/spec.md, section 6, step 4)
  • Root candidate: none found; searched llm-stack, LLM.embed, embed, Ollama, nomic, ollama_embedder, transport

VERDICT: CREATE

  • Where: plugins/os-me/lib/os_me/ollama_embedder.rb
  • Session: 4924697c-7a72-4455-a9f0-ba97fc3f1c6d
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/4924697c-7a72-4455-a9f0-ba97fc3f1c6d.jsonl
## Problem `plugins/os-me/lib/os_me/ollama_embedder.rb` posts to Ollama through `Decision::Model::Http::Connection`. That is decision-model transport. The cc-os spec (`docs/llm-stack/spec.md`, build-order step 4) moves this call onto `LLM.embed` from `llm-stack`. ## Reproduce n/a: refactor. No defect to reproduce. ## Approach Keep `OsMe::OllamaEmbedder#embed(texts)`. It must still take an Array of Strings and return an Array of vectors. `CandidateRefresh` calls it through `OllamaEmbedder.standard`. Replace the HTTP connection with a call to `LLM.embed`. Remove the `MODEL`, `BASE_URL`, and `TIMEOUT` constants. Move the model name and the endpoint into an `llm-stack` profile. Wire `LLM.configure` at the os-me entry point, as the other os-me `llm-stack` callers do. Update `plugins/os-me/test/ollama_embedder_test.rb`. The fake must stand in for the `LLM` client, not for `post_json`. Bump the `llm-stack` revision in `plugins/os-me/Gemfile.lock` to the release that ships `LLM.embed`. Required work: 1. Remove the decision-model transport from the embedder — check: absent `Decision::Model::Http` in `plugins/os-me/lib/os_me/ollama_embedder.rb` 2. Call `LLM.embed` in the embedder — check: present `LLM.embed` in `plugins/os-me/lib/os_me/ollama_embedder.rb` 3. Rewrite `plugins/os-me/test/ollama_embedder_test.rb` for the new call. 4. Bump the `llm-stack` pin in `plugins/os-me/Gemfile.lock`. Orchestrator work: run `bin/refresh-plugins` after the change. ## Acceptance criteria 1. Given two texts, when `OsMe::OllamaEmbedder#embed` runs, then it returns two vectors in input order. 2. Given `OsMe::OllamaEmbedder`, when its source is read, then it holds no reference to `Decision::Model::Http`. 3. Given `OsMe::CandidateRefresh`, when it runs with the standard embedder, then it embeds through `LLM.embed`. 4. Given `plugins/os-me/Gemfile.lock`, when it is read, then its `llm-stack` revision is one that ships `LLM.embed`. ## Owner sketch ```ruby module OsMe class OllamaEmbedder def embed(texts) end end end ``` ## Non-goals - Moving other os-me code off decision-model transport. - Changing `PatternClusterer` or `CandidateRefresh`. Blocked by: jared/llm-stack#5 ## Origin - Trigger: cc-os `docs/llm-stack/spec.md`, build-order step 4. Jared asked for this ticket and for the matching `llm-stack` ticket in session 4924697c-7a72-4455-a9f0-ba97fc3f1c6d. - Improvised this session: none - Chain: os-me embedder uses decision-model transport ← `llm-stack` had no `embed` ← DESIGN (`docs/llm-stack/spec.md`, section 6, step 4) - Root candidate: none found; searched llm-stack, LLM.embed, embed, Ollama, nomic, ollama_embedder, transport VERDICT: CREATE - Where: `plugins/os-me/lib/os_me/ollama_embedder.rb` - Session: 4924697c-7a72-4455-a9f0-ba97fc3f1c6d - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/4924697c-7a72-4455-a9f0-ba97fc3f1c6d.jsonl
Author
Owner

llm-stack 0.2.0 has LLM.embed. Pin commit: 88b5f17144f06b449271cb970974a6c031f0d40a (tag v0.2.0, jared/llm-stack#5 closed).

Facts for the swap of plugins/os-me/lib/os_me/ollama_embedder.rb:

  • Call: LLM.embed(profile: :nomic, texts: [...]). texts: is a keyword, not positional as in the spec sketch.
  • Profile: { provider: "ollama", model: "nomic-embed-text" }. No credential. endpoint: is optional; default http://localhost:11434. The gem appends /v1, so give the host only.
  • Returns LLM::Embedding with vectors (Array of Float arrays, one per text, input order), usage, cost (0), meta. The adapter calls .vectors.
  • Route: OpenAI-compatible POST /v1/embeddings, not native /api/embed. Spike: each text embedded alone gave the same vector as in the batch.
  • Errors: LLM::ProviderError. Timeout: 60 s.
  • Pins are lockfile revisions. Re-bundle each plugin lock that must see the new commit.
  • docs/llm-stack/spec.md: the "Not built" header is stale (steps 1 to 4 are now built), and the sketch shows positional texts.
llm-stack 0.2.0 has `LLM.embed`. Pin commit: `88b5f17144f06b449271cb970974a6c031f0d40a` (tag `v0.2.0`, jared/llm-stack#5 closed). Facts for the swap of `plugins/os-me/lib/os_me/ollama_embedder.rb`: - Call: `LLM.embed(profile: :nomic, texts: [...])`. `texts:` is a keyword, not positional as in the spec sketch. - Profile: `{ provider: "ollama", model: "nomic-embed-text" }`. No credential. `endpoint:` is optional; default `http://localhost:11434`. The gem appends `/v1`, so give the host only. - Returns `LLM::Embedding` with `vectors` (Array of Float arrays, one per text, input order), `usage`, `cost` (0), `meta`. The adapter calls `.vectors`. - Route: OpenAI-compatible `POST /v1/embeddings`, not native `/api/embed`. Spike: each text embedded alone gave the same vector as in the batch. - Errors: `LLM::ProviderError`. Timeout: 60 s. - Pins are lockfile revisions. Re-bundle each plugin lock that must see the new commit. - `docs/llm-stack/spec.md`: the "Not built" header is stale (steps 1 to 4 are now built), and the sketch shows positional `texts`.
Author
Owner

Work started on branch ticket-1470 via /os-sdlc:implement (poodr-ticket-implementation). Gem side shipped: llm-stack 0.2.0 at 88b5f17 (jared/llm-stack#5).

Work started on branch ticket-1470 via /os-sdlc:implement (poodr-ticket-implementation). Gem side shipped: llm-stack 0.2.0 at 88b5f17 (jared/llm-stack#5).
Author
Owner

Follow-up dropped by ticket-skeptic (need-to-exist): OllamaEmbedder.standard sets the global client with LLM.configure, while HeadlessRunner injects an LLM::Client instance. No collision exists today (no other os-me caller uses LLM.configure). If a second caller collides, file it against the llm-stack API design. Note: this ticket's text wrongly said other os-me callers use LLM.configure.

Follow-up dropped by ticket-skeptic (need-to-exist): OllamaEmbedder.standard sets the global client with LLM.configure, while HeadlessRunner injects an LLM::Client instance. No collision exists today (no other os-me caller uses LLM.configure). If a second caller collides, file it against the llm-stack API design. Note: this ticket's text wrongly said other os-me callers use LLM.configure.
Author
Owner

Resolution

Done: OsMe::OllamaEmbedder now calls LLM.embed(profile: :nomic, texts:).vectors on llm-stack 0.2.0; Decision::Model::Http transport and MODEL/BASE_URL/TIMEOUT removed; os-me Gemfile.lock pins llm-stack 88b5f17 (0.2.0).

Evidence: Implementation 215 complete (14 dispatches, 1 reroute onto poodr-guard-implementation for a test-fake defect); suite + required-work gates green; mutation check passed twice; commit 46a86534 fast-forwarded to main.

Follow-ups: Inject the client instead of LLM.configure: dropped by ticket-skeptic (need-to-exist), reason posted on this ticket. HeadlessClaude replacement already filed as #1471 (blocked by jared/decision-model#11).

## Resolution **Done:** OsMe::OllamaEmbedder now calls LLM.embed(profile: :nomic, texts:).vectors on llm-stack 0.2.0; Decision::Model::Http transport and MODEL/BASE_URL/TIMEOUT removed; os-me Gemfile.lock pins llm-stack 88b5f17 (0.2.0). **Evidence:** Implementation 215 complete (14 dispatches, 1 reroute onto poodr-guard-implementation for a test-fake defect); suite + required-work gates green; mutation check passed twice; commit 46a86534 fast-forwarded to main. **Follow-ups:** Inject the client instead of LLM.configure: dropped by ticket-skeptic (need-to-exist), reason posted on this ticket. HeadlessClaude replacement already filed as #1471 (blocked by jared/decision-model#11).
jared closed this issue 2026-10-28 21:35:58 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jared/cc-os#1470
No description provided.