feat(chat): ChatProvider abstraction over REM synthesizer — #178 chat-side foundation#192
Merged
Merged
Conversation
…-side foundation Mirrors the EmbeddingProvider pattern from PR #187 on the chat half of issue #178 (node-llama-cpp bridge for the native-app track #176). The REM synthesizer in scripts/synthesize-cluster.mjs talked Ollama HTTP directly: /api/chat for the call, /api/generate for unload, plus inline extractJSON to strip Qwen3 <think> blocks. Hard-codes the provider, no seam for the in-process llama.cpp path. This change extracts the seam: * mcp-server/src/services/chat.ts — ChatProvider interface, OllamaChatProvider with the existing fetch+think:true behaviour, extractJSON pure helper, and createChatProvider() env factory honouring MYCELIUM_LLM_PROVIDER (matches createEmbeddingProvider's switch). llama-cpp branch throws a clear stub error pointing at the follow-up PR. * mcp-server/src/__tests__/chat.test.ts — 19 unit tests covering extractJSON edge cases (think blocks, code fences, prose-wrapped JSON, empty input), OllamaChatProvider shape (model/messages/keep_alive/ options sent correctly, HTTP error path, per-request keepAlive override, unload best-effort), and factory env routing. * scripts/synthesize-cluster.mjs — uses the abstraction instead of inline fetch + extractJSON. unloadQwen() kept as a back-compat alias of the new unloadModel(). No package.json changes, no new dependencies — keeps the merge surface clean against PR #187 (which adds node-llama-cpp). The LlamaCppChatProvider itself is the next slice and lands once #187 is on main. Tests: 958 pass (was 939; +19 new), 0 fail. Pillar 1 (decentralised AI) — strengthened indirectly by setting up the seam that lets the native-app build drop the second daemon. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dewinator
added a commit
that referenced
this pull request
May 3, 2026
…n W4.1
W4.1 of docs/wave-4-anti-echo.md says "the first PR of this wave creates
the directory" — this is that scaffold. Lands two files only:
- mcp-server/src/__tests__/fixtures/anti-echo/README.md
Developer-facing spec for the corpus shape, mirrors the governance
rules from the anchor doc but written for the file-format reader.
- mcp-server/src/__tests__/fixtures/anti-echo/corpus-types.ts
`AntiEchoCorpusFixture` + `AntiEchoCohortFixture` discriminated union
over the v1.1 Lesson envelope (services/wire-types.ts). Types only,
no loader, no harness — those land alongside the first concrete
fixture per category in subsequent PRs.
Why scaffold-first instead of one big "land all 8 fixtures" PR: the eight
attack categories from wave-4-anti-echo.md §"Corpus categories" each have
their own subtleties (cohort vs single-envelope, signing-key handling,
which §10 mechanism asserts). Decomposing into one fixture per follow-up
PR keeps each diff reviewable and lets the harness shape evolve from the
first concrete fixture rather than from speculation.
Why this can land while the 9-PR native-app queue is open: the new
directory lives entirely under `__tests__/fixtures/`, so it has zero file
overlap with the native-app stack (#185 / #187 / #188 / #189 / #190 /
#191 / #192 / #193 / #194). 939/939 node --test tests still green;
`tsc --noEmit` clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dewinator
added a commit
that referenced
this pull request
May 3, 2026
…rain) Reed merged 10 PRs today: all 3 W4.1 anti-echo (#197/#198/#201), both W2 federation (#199/#200), 5 native-app (#190/#191/#192/#193/#194). Only the linear 4-PR #178-stack remains open (#185 independent + #187 → #188 → #189 strictly stacked). Three-cohort split collapsed to one cohort — old order- independence proofs (143rd/148th tick) now obsolete. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ChatProviderseam overscripts/synthesize-cluster.mjs's inline Ollama HTTP /<think>parsing so the next slice can drop aLlamaCppChatProviderin behind the sameMYCELIUM_LLM_PROVIDERenv switch.package.jsonchange. Keeps the merge surface clean against PR feat(embeddings): LlamaCppEmbeddingProvider behind MYCELIUM_LLM_PROVIDER — #178 part 1 #187 (which addsnode-llama-cppasoptionalDependencies); the actualLlamaCppChatProviderlands once that's onmain.What changes
mcp-server/src/services/chat.ts(new):ChatProviderinterface,OllamaChatProviderextracted verbatim fromsynthesize-cluster.mjs'scallQwen+unloadQwen,extractJSON()pure helper (Qwen3<think>blocks,```jsonfences, prose-wrapped JSON),createChatProvider()env factory honouringMYCELIUM_LLM_PROVIDER. Thellama-cppbranch throws a clear stub error pointing at the follow-up PR — no silent fallthrough.mcp-server/src/__tests__/chat.test.ts(new): 19 unit testsextractJSON: think blocks, multiple think blocks, fenced JSON (with/withoutjson), prose-wrapped, empty input, no-JSON-foundOllamaChatProvider: shape sent to/api/chat(model/messages/keep_alive/think/options), missing-fields fallback to empty strings, HTTP error path with status + body, per-requestkeepAliveoverride,unload()postskeep_alive=0on/api/generate,unload()swallows network errors (best-effort)createChatProvider: default → ollama, case-insensitive,llama-cppstub throws,llamacppalias, unknown provider with helpful listscripts/synthesize-cluster.mjs(refactor): usescreateChatProvider()+extractJSONfrom the compiled module instead of inline fetch.unloadModel()is the new name;unloadQwenkept as a back-compat re-export.Why this slice now
Spike-2 doc §"Suggested follow-up issues" #2: "add
LlamaCppChatProviderand route REM digest through it whenMYCELIUM_LLM_PROVIDER=llama-cpp". The pre-step is to givesynthesize-cluster.mjsa swap point — that's this PR. Without it the LlamaCpp impl would have to either fork the script or re-introduce the same hard-coded HTTP shape.Out of scope
LlamaCppChatProvideritself (depends on PR feat(embeddings): LlamaCppEmbeddingProvider behind MYCELIUM_LLM_PROVIDER — #178 part 1 #187 landing for thenode-llama-cpppackage)qwen3:8b), same promptsTest plan
npm run build— cleannpm test— 958 pass (was 939; +19 new), 0 failnode --check scripts/synthesize-cluster.mjs— OKnode scripts/synthesize-cluster.mjs --dryagainst a live Ollama (env paths preserved:OLLAMA_URL,REM_MODEL,REM_KEEP_ALIVE,REM_NUM_CTX)Pillar check
Pillar 1 (decentralised AI) — strengthened indirectly: sets up the seam that lets the native-app build drop the Ollama daemon. No pillar weakened.
🤖 Generated with Claude Code