Repository navigation
Description
Part of #1437 (assessment: laptop data plane on Praxis) · prep work, useful to Cortex on its own
Status: there is no shipped Cortex–Praxis integration today (the paused
cmd/cortex-praxisconverter is not one), and the interim path is not decided (LLM traffic to praxis-ai as an inference server, or all traffic through Praxis via #1491). This is a refactor with no behaviour change: it is useful to Cortex on its own, and would make a possible move to Praxis easier whichever path is taken.
Problem
The inference-router is one of the larger plugins (1,942 non-blank lines; 2,407 with routerconfig/). Only cpex (5,135) and inference-parser (2,427) are bigger. Its orchestration is mixed with plugin glue: 116 pctx. references across its files.
| File | Lines | Lines with pctx. |
|---|---|---|
plugin.go |
739 | 35 |
models.go |
439 | 35 |
fit.go |
288 | 14 |
strip.go |
224 | 11 |
limits.go |
155 | 4 |
pins.go |
97 | 2 |
Much of the heavy logic is already pure. About 25 functions take no pctx, including the fitting and limit algorithms: fitBody, trim, capOutput, conversationKey, limitRefusal, plausible, learn, stripReasoning, fingerprint, refusesModel, capturePath, kindOf and turnOf. wentTo, serverOf and substitute don't take pctx either. Tests already call several of them directly (fitBody 17 times, learn 9).
What's tangled is the routing and model-choice orchestration: serverFor, unpinned, chooseModel, applyLimits and keepOwnReasoning take pctx, and tests never call them directly. Side effects are mixed into those decisions:
- durable store reads and writes: pins (with a 24 h renewal throttle), substitutes, refusals, learned limits, reasoning records, catalogs; each with a
pctx.Sharedfallback; - the on-disk archive, read through
wentTo(p.history.Earlier, up to 2,000 events); - request mutation:
Redirect,SetRedirectPath,SetRequestModel,SetBody, credential headers (setKey,dropProviderCredentials), andMarkResend; - records and logs: 15
pctx.Recordcalls; - background state: the catalog-fetch goroutine.
So routing and model choice can only be tested through a full plugin request.
Proposal
Split into a decision core and a plugin shell:
- Core: pure functions over plain inputs —
ChooseServer(agent, pin, sessionFact, request) → (server, reason),ChooseModel(server, request, catalog, refusals) → model,Fit(server, model, request, limits) → edits— plus the steps the first sketch missed:- the pin's second phase. A pin is decided, then settled or abandoned after the redirect outcome and the observe check (
plugin.go:334-402); - learning from a refusal (the Resend path,
models.go:187-269); - reasoning stripping (
strip.go:151).
- the pin's second phase. A pin is decided, then settled or abandoned after the redirect outcome and the observe check (
- Shell: reads
pctx, calls the core, applies the redirect, path, model and header changes, and owns the store, credentials, resend and the archive read.
The router's session-history read (wentTo scans view.Events, then the archive) is replaced by a session fact in #1499, which owns that change; it must survive a restart. The core's input is InferenceExtension, so it stays on core/pipeline until #1502 lands.
This is the riskiest of the module splits (stateful, two-phase, with resend), so it goes last.
Done when
- The decision core has table tests with no
pipeline.Context. - The plugin's existing tests pass, with only those that call moved helpers changing their package qualifier.
Evidence read at main @ e455a04 (2026-10-25); not built.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsNew/ToDo
Activity