Stacked on #6 (facts). Retarget to master once that merges.
What
IAnswerModel port gets its first adapter: Wiki.Adapters.Anthropic (Messages API, SSE streaming, 429/529 retry, health via GET /v1/models). Registered per Wiki:AnswerModels:Models:<name>, key from Wiki__AnswerModels__Models__<name>__ApiKey.
QuestionUseCase (Application): ask (rank + judge) → pick paragraphs carrying probability mass → below the coverage gate emit a Gap and call no model; otherwise stream the answer model over the evidence.
SaveAnswerUseCase: writes output/<date>-<slug>.md in the wiki-synth shape (question, sources, confidence, answered_by, covered); numbered suffix on collision.
POST /api/v1/questions{question, k, model, judge, answerModel, save}. Anonymous read; save: true needs a signed-in user or an API key (the API-key scheme is authenticated explicitly on that path, since the default scheme is the proxy header).
Answers page (MudBlazor): streams the same use case, shows evidence cards, saves on demand, lists saved answers.
Tests
60 contract tests for the Anthropic adapter (mocked SSE), 3 use-case tests (evidence→text→done, gate blocks the model, saved document shape), host endpoint test incl. 400/401/save.
dotnet test Wiki.slnx: 124 green.
Follow-up (homelab)
ExternalSecret for the Anthropic key (apps/ai/wiki-anthropic) and an image bump.
Stacked on #6 (facts). Retarget to master once that merges.
## What
- `IAnswerModel` port gets its first adapter: `Wiki.Adapters.Anthropic` (Messages API, SSE streaming, 429/529 retry, health via `GET /v1/models`). Registered per `Wiki:AnswerModels:Models:<name>`, key from `Wiki__AnswerModels__Models__<name>__ApiKey`.
- `QuestionUseCase` (Application): ask (rank + judge) → pick paragraphs carrying probability mass → below the coverage gate emit a **Gap** and call no model; otherwise stream the answer model over the evidence.
- `SaveAnswerUseCase`: writes `output/<date>-<slug>.md` in the wiki-synth shape (question, sources, confidence, answered_by, covered); numbered suffix on collision.
- `POST /api/v1/questions` `{question, k, model, judge, answerModel, save}`. Anonymous read; `save: true` needs a signed-in user or an API key (the API-key scheme is authenticated explicitly on that path, since the default scheme is the proxy header).
- **Answers** page (MudBlazor): streams the same use case, shows evidence cards, saves on demand, lists saved answers.
## Tests
- 60 contract tests for the Anthropic adapter (mocked SSE), 3 use-case tests (evidence→text→done, gate blocks the model, saved document shape), host endpoint test incl. 400/401/save.
- `dotnet test Wiki.slnx`: 124 green.
## Follow-up (homelab)
ExternalSecret for the Anthropic key (`apps/ai/wiki-anthropic`) and an image bump.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Implements IAnswerModel over Anthropic's streaming Messages API: SSE
parsing (text_delta/usage/error), 429/529 retry with Retry-After or
exponential backoff, and a pure AnswerPrompt.Build for the evidence
and judgements prompt shape. Health check hits GET /v1/models.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
POST /api/v1/questions runs ask (rank + judge), selects the paragraphs that
carry probability mass, and streams one answer-model call over them. Below
the coverage gate no model is called and the response names the gap. With
save: true (signed-in user or API key) the answer lands in output/ as a
synth-shaped document with sources, confidence and the answering model.
The Answers page streams the same use case and lists saved answers.
Anthropic is the first IAnswerModel adapter; the answer model is chosen per
request or from Wiki:AnswerModels:Active.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Reopened against main after #6 merged (Gitea closed the stacked PRs). #8 has this branch merged in, so merging #7 first is the clean order; merging #8 alone also carries this work.
Reopened against main after #6 merged (Gitea closed the stacked PRs). #8 has this branch merged in, so merging #7 first is the clean order; merging #8 alone also carries this work.
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.
Stacked on #6 (facts). Retarget to master once that merges.
What
IAnswerModelport gets its first adapter:Wiki.Adapters.Anthropic(Messages API, SSE streaming, 429/529 retry, health viaGET /v1/models). Registered perWiki:AnswerModels:Models:<name>, key fromWiki__AnswerModels__Models__<name>__ApiKey.QuestionUseCase(Application): ask (rank + judge) → pick paragraphs carrying probability mass → below the coverage gate emit a Gap and call no model; otherwise stream the answer model over the evidence.SaveAnswerUseCase: writesoutput/<date>-<slug>.mdin the wiki-synth shape (question, sources, confidence, answered_by, covered); numbered suffix on collision.POST /api/v1/questions{question, k, model, judge, answerModel, save}. Anonymous read;save: trueneeds a signed-in user or an API key (the API-key scheme is authenticated explicitly on that path, since the default scheme is the proxy header).Tests
dotnet test Wiki.slnx: 124 green.Follow-up (homelab)
ExternalSecret for the Anthropic key (
apps/ai/wiki-anthropic) and an image bump.🤖 Generated with Claude Code
Reopened against main after #6 merged (Gitea closed the stacked PRs). #8 has this branch merged in, so merging #7 first is the clean order; merging #8 alone also carries this work.