cordis-plugin-aux-reasoning
Verified@argszero/cordis-plugin-aux-reasoning Β· v0.1.0 Β· MIT
Auxiliary-call reasoning policy for dsh: the harness marks its own background calls (compaction checkpoints, session titles) with a `purpose` and names no reasoning level for them, so `dsh-llm` fills the gap from the route's declared default and they thin
Install
dsh plugin add @argszero/cordis-plugin-aux-reasoning Confirm the layer applied with dsh --profile default --dump-config β see the install guide.
Source
Tags
Readme
@argszero/cordis-plugin-aux-reasoning
Stop the harness's own background model calls from thinking.
A deepseek-harness plugin.
Source discussion: #7109 β Honor purpose when an auxiliary call names no
reasoning level.
npm install @argszero/cordis-plugin-aux-reasoning
The gap
The harness marks the model calls it makes on its own behalf β not on the
user's β with a purpose:
dsh-compaction-basicstreams a context checkpoint withpurpose: 'compaction';dsh-session-title-llmstreams a session title withpurpose: 'session-title'.
Neither names a reasoningEffort. dsh-llm fills that gap from the route:
const requested = defaulted.reasoningEffort
const effective = requested ?? reasoning.defaultEffort
So on any route whose profile default is a thinking level, compaction and
session titles think β at full length β inside a maxTokens cap the caller
chose. That is the wrong default for a call whose entire job is to produce a
bounded amount of visible text:
- thinking spends the cap first, so a summariser whose thinking eats the cap truncates the checkpoint;
- on a local model the cost is wall-clock rather than quota β a few hundred thinking tokens is minutes per compaction at single-digit tok/s.
The harness has already decided this for its own adapter: dsh-llm-deepseek
returns { thinking: 'disabled' } for purpose === 'session-title', and the
compaction call carries x-deepseek-harness-compact: 1 for the server to act
on. A pi-ai-backed route has no equivalent β so the same harness behaves two
ways depending on which adapter serves the model. This plugin supplies the
missing half from outside the adapter.
What it does
It joins the public llm/stream waterfall and, for a request that
- carries a configured auxiliary
purpose, - names no
reasoningEffortof its own, - targets a route that genuinely offers the configured level, and
- would otherwise inherit a different level from the route's declared default,
re-dispatches the request as a new object with that level named.
Every other request takes the identity path: next() is called with the
original object. Nothing is copied, and nothing is re-dispatched.
aux-reasoning: purpose=session-title pi-ai/local-qwen: high -> off
Why the level is looked up, not assumed
Naming reasoningEffort is not a preference, it is a claim that the route
supports that level. dsh-llm rejects the call with
UNSUPPORTED_REASONING_EFFORT when the route declares no reasoning metadata at
all, and again when the named level is missing from the route's efforts. A
plugin that named off unconditionally would therefore convert a working
(merely expensive) configuration into a hard failure on every non-reasoning
route: compaction and session titles would stop working entirely.
So the plugin asks ctx.llm.resolveModelInfo() β the very query the dispatch
itself resolves against β and substitutes only when the level is genuinely
offered. Every skip is a pass-through.
| situation | what happens |
|---|---|
| route offers the level, default differs | level named, request re-dispatched |
| route offers the level, default already the target | left alone |
| route does not offer the level | left alone, reported |
| route declares no reasoning levels | left alone, reported |
| route declares no default | left alone, reported |
| call names its own level | left alone, never overridden |
| purpose not in the configured set | left alone |
| route cannot describe itself | left alone, reported |
Configuration
With no configuration the plugin treats compaction and session-title as
auxiliary and names off.
- insert:
- id: aux-reasoning
name: '@argszero/cordis-plugin-aux-reasoning'
config:
purposes: [compaction, session-title]
effort: off # the level named for those calls
reportLimit: 5 # bounded diagnostics; 0 silences them
observeOnly: false # true = report what would change, change nothing
observeOnly: true is the safe way to see what the plugin would do on your
routes before letting it act.
Install and mount
The package ships a bundle patch, so adding it to a profile's plugin list is
enough; dsh.bundle.patch points at cordis.patch.yml, which inserts the
plugin under the id aux-reasoning. To mount it by hand, add the same entry to
your own cordis.yml:
- name: '@argszero/cordis-plugin-aux-reasoning'
config:
effort: off
Scope and honesty
- Ordinary turns are never touched. Only requests carrying a configured
purposeare considered, and only when they name no level themselves. Your interactive thinking configuration is not this plugin's business. - The plugin changes the request, not the route. It cannot make a provider behave differently when the adapter sends no reasoning option for either the named level or the default β on such a route the two requests are byte-for- byte identical, and the plugin is a no-op there by construction.
- A substitution is a re-dispatch. The requests other
llm/streamlisteners see are exactly the requests that are sent (the plugin registers withprepend: true), and each proposed call reaches the adapter exactly once β never a discarded call plus a real one. - Complementary, not overlapping, with
@argszero/cordis-plugin-thinking-level-fallback, which re-sends a call after a provider rejects the level it was sent. This plugin decides before the first dispatch; that one reacts after a failure.
Compatibility
Peer-tested against the dsh-llm prerelease lines 0.1.2-rc.1,
0.1.3-alpha.2, 0.1.5-* and 0.1.6-* (the peerDependencies range lists
each line explicitly, because semver caret ranges silently exclude
prereleases of other versions).
Tests
npm test
25 tests: the decision rules in isolation, plus integration tests against a
real @deepseek-ai/cordis context and the published @deepseek-ai/dsh-llm
runtime, asserting what the adapter receives β including the negative
control that naming off on a route with no reasoning metadata fails with
UNSUPPORTED_REASONING_EFFORT, which is exactly what the unconditional form of
this plugin would have done.
The suite passes against each dsh-llm prerelease line this manifest claims
(0.1.2-rc.1, 0.1.3-alpha.2, 0.1.5-rc.2, 0.1.6-alpha.2), installed one
at a time.
What the wire shows
Mounting the plugin next to @deepseek-ai/dsh-llm-deepseek (installed from
npm, pointed at a loopback server, route configured reasoningEffort: high)
makes the change visible in the HTTP body:
| request | without the plugin | with the plugin |
|---|---|---|
purpose: 'compaction' |
"thinking":{"type":"enabled"}, "reasoning_effort":"high" |
"thinking":{"type":"disabled"}, no effort |
purpose: 'session-title' |
"thinking":{"type":"disabled"} |
unchanged β the adapter already decides this |
| no purpose | "thinking":{"type":"enabled"}, "reasoning_effort":"high" |
unchanged β ordinary turns are not this plugin's business |
The first row is worth reading twice: even on DeepSeek's own route the
compaction call is sent with thinking enabled. The adapter disables it for
session-title and otherwise relies on x-deepseek-harness-compact: 1 for the
server to act on β the header is still sent, but it is advice, not a local
decision. So the missing decision is not specific to pi-ai-backed routes.
License
MIT