DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

lordraiden /

lordraiden/dsh-mistral-pro

Verified

DeepSeek Harness (DSH) plugin: Mistral AI subscription (Vibe Pro/Team) model provider with multi-account failover, prompt-cache tuning and a composer usage panel. · DeepSeek Harness(DSH)插件:接入 Mistral AI 订阅(Vibe Pro/Team)的模型提供方,支持多账号故障切换、提示词缓存调优和输入框额度面板。

★ 1 Stars0 Forks1 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@1e7b5b22

dsh-mistral-pro

Connects Mistral subscriptions (Vibe Pro / Team) to DeepSeek Harness. Provides the mistral-pro model route, multi-account failover, prompt-cache tuning, and a usage popover in the composer toolbar.

version DSH license EN · 中 · 日

English · 中文 · 日本語


Mistral Pro usage panel

This is a fork. lordraiden/dsh-mistral-pro merges the two independent fix lineages that existed on top of upstream 0.1.2: santera-pixel/dsh-mistral-pro (0.2.0-0.2.4) and the upstream issue #1 patch series (0.1.3-0.1.8). See CHANGELOG.md for the full reconciliation.

What 0.4.0 changes

0.4.0 closes the audit issues opened against 0.3.0. Provider behaviour is unchanged by default; the release makes the existing behaviour tested, observable and documented.

  • CI and a real test entry point. npm test discovers every test/*.mjs suite and npm run check syntax-checks every shipped file; GitHub Actions runs both on Node 22 and 24 for every push and pull request to main.
  • New coverage for the native transport (byte-exact request body), account failover (before output only), truncated streams (TRANSPORT), the retry-policy shape, and both cache-key modes.
  • shared no longer degenerates to a model-only key when the session has no system message: it falls back to the per-conversation auto derivation.
  • The cache key rotates once per context compaction, using the checkpoint DSH puts on the replacement user message. Sessions that never compact keep exactly the same key.
  • response_error in the finish chunk is inspected. A validation failure is logged, and an unusable tool call raises MALFORMED_RESPONSE instead of being dispatched; the retry policy re-runs the request.
  • New /cache diagnostic route aggregates the wire log into requests, prompt/cached tokens and a hit rate.
  • The wire log is created 0600, and an existing wider log is tightened on the next write.
  • Settings UI: cache.affinity, cache.reportUsage and advanced.transport are editable in Settings → Mistral Pro → Advanced.
  • Docs: the Chinese and Japanese READMEs are back at parity, and CONTRIBUTING.md documents the upstream sync strategy and the independent version policy.
  • x-affinity is documented as what it is: an undocumented, unverified cache-routing hint (see Prompt caching).

What 0.3.0 changes

  • Tool calls no longer arrive as unknown tool "". Mistral repeats function.name: "" on every continuation chunk of a streamed tool call; the accumulator now only accepts a non-empty name and keeps the first one it saw.
  • One block-start per block, so the live thinking panel no longer collapses while streaming.
  • id is always a string on tool-call-delta, which the harness stream contract requires.
  • Retry policy is the resolved, flattened shape, so the first transient failure no longer dies with session event "llm/retry" carries non-JSON-serializable data.
  • A truncated stream raises TRANSPORT instead of ending the turn "successfully" with partial output.
  • No injected max_tokens cap (advanced.maxTokensCap defaults to 0). Reasoning shares the completion budget, so a cap that looks generous for text can be consumed by thinking alone.
  • Attempt state is committed on tool calls. A mid-stream failure after a tool call can no longer trigger account failover and splice two accounts' output into one turn.
  • Prompt-cache tuning: advanced.promptCacheKey now supports auto, shared, off, or a custom string, and /wire records the cached_tokens each request actually returned.

Installation

Requires DSH >= 0.2.1-rc.5 and Node ^22.19.0 || >=24.0.0.

Install the fork pinned to a release tag:

dsh plugin --profile web add github:lordraiden/dsh-mistral-pro#v0.4.0

Or add it to the profile's package.json and install:

{
  "dependencies": {
    "dsh-mistral-pro": "github:lordraiden/dsh-mistral-pro#v0.4.0"
  }
}
pnpm install

From a local checkout, pack a tarball (recommended) or link the directory:

npm pack
dsh plugin --profile web add /absolute/path/to/dsh-mistral-pro-0.4.0.tgz

Linking a directory creates a symlink. Because pnpm does not resolve peerDependencies for symlinked targets, the repo includes a node_modules/@deepseek-ai/* symlink directory pointing to the active profile's shared dependencies (excluded when packing the tarball).

Once installed, enable dsh-mistral-pro on the Plugins page and reload. A host restart is required after changing the installed version.

Adding Accounts

Open Settings → Mistral Pro → Accounts:

  1. Click Add account and enter a display name (used only for display and ordering).
  2. Set Credential reference to an environment variable name, such as MISTRAL_API_KEY.
  3. Paste your API key into the password field and click Set key. Keys are stored in the DSH credential store and are never sent back to the browser.
  4. Click Test to run a check against /v1/models.

You can generate an API key under Code › Vibe CLI. The same key works across Free, subscription plans, and pay-as-you-go; requests consume your monthly plan allowance first before falling back to pay-as-you-go billing.

Accounts are tried in list order (use ↑ / ↓ to adjust). The first enabled account acts as the primary. When a failover succeeds, the working account is promoted in memory for the rest of the process lifetime without modifying your saved config.

Models

Settings → Mistral Pro → Models combines the built-in catalog with models returned by /v1/models. Uncheck Visible to hide a model from the picker. Context window, max output tokens, and Default effort (reasoning_effort) can be overridden per model.

Built-in catalog (2026-11, 19 models total, 11 visible by default). With a key, the live /v1/models listing wins and aliases collapse into one entry:

Model Context Reasoning
mistral-large-4 1M low / medium / high
mistral-small-2603 256K none / low / medium / high
mistral-medium-3-5 128K none / low / medium / high
zai-glm-5-3 1M low / high / max
zai-glm-5-3-flash 1M low / high / max
deepseek-v4-1-flash-1017 1M none / low / high / max
ministral-3-{14b,8b,3b}-25-12 256K —
codestral-25-08 / codestral-latest 256K —

mistral-large-2512, devstral-2512, and mistral-medium-2508 are hidden by default and can be enabled in the model table.

Usage Popover

The plugin adds a ● Mistral button to the composer toolbar. Clicking it opens a usage panel showing:

  • usage percentage, used / total quota, and reset countdown for each window;
  • prepaid credit balance and current-month API spend;
  • manual refresh and last-updated timestamp.

The toolbar button displays the first recognized quota window, prioritizing labels matching Vibe, monthly, or plan. Background tabs pause polling and refresh immediately when brought back into focus.

Usage comes from api.mistral.ai/v1/billing/budget, read with the API key you saved. No extra setup is needed. The endpoint returns the Vibe and API monthly budgets: percentage used, budget amount, and reset time.

If the key lacks the usage permission (the billing endpoint answers 403), you can use an admin-site cookie instead:

  1. Sign in to Mistral Admin.
  2. Open DevTools → Network, select any request to admin.mistral.ai, and copy the full Cookie request header. It must include ory_session_* (if csrftoken is present, the plugin sends X-CSRFTOKEN automatically).
  3. Paste the header under Settings → Mistral Pro → Advanced and click Set cookie.

The cookie path queries GET /subscription, GET /api/billing/v2/usage?month=&year=, and GET /api/billing/credits independently, so a failure on one endpoint won't block the others. If the session expires and redirects to the login page, the panel prompts you to update the cookie.

Request Flow & Failover

The llm service passes session messages to the adapter, which formats them into OpenAI-compatible payloads, POSTs to https://api.mistral.ai/v1/chat/completions, and parses the SSE response into DSH StreamChunks. Assistant reasoning history is replayed back as thinking chunks as required by Mistral's API.

Account failover only triggers before the stream emits content:

  • On 401 / 403, 429, quota exhaustion, or a missing/invalid key, the adapter switches to the next account and places the failed one on a 300-second in-memory cooldown.
  • Once text, reasoning, or a tool call has been emitted, errors are surfaced directly without switching accounts so partial responses from different accounts are never mixed.

Prompt caching

Mistral's prompt caching reuses previously computed prompt tokens when requests share the same prefix. Cached tokens are billed at 10% of the standard input price and are reported in usage.prompt_tokens_details.cached_tokens.

Two facts from the documentation drive every decision here:

  • prompt_cache_key is a routing hint. It "increases the chance of a cache hit, but doesn't guarantee one" — a hit happens when the API finds a compatible cached prefix. The key does not create the cache; prefix stability does.
  • Cache blocks are 64 tokens. cached_tokens is always a multiple of 64, and prompts under 64 tokens never hit. Longer shared prefixes reuse more.

What the plugin does to keep the prefix stable:

  • It never injects volatile content (timestamps, session ids, counters) into the request.
  • It preserves message order and replays assistant reasoning as thinking blocks, so the prefix stays byte-identical between turns.
  • It drops non-wire keys per role (sanitizeMessage), deterministically, so a foreign field can never change the prefix on one turn and not the next.
  • It sends stream_options.include_usage, without which cached_tokens would never be reported at all.

advanced.promptCacheKey selects the key:

Value Key derivation Best for
auto (default) model + system prompt + first user message One long conversation: every appended turn keeps the same key.
shared model + system prompt only; falls back to auto when the session has no system/developer message Many short conversations that reuse the same (large) system prompt: the first turn of a new conversation can still hit the cached system-prompt prefix.
off field omitted Providers or proxies that reject the field.
any other string sent as-is Managing keys externally (one per workflow, team, or subscription).

auto and shared are a trade-off, not a ranking. auto isolates conversations, which is what Mistral recommends ("use a stable application-level identifier, such as a conversation ID"). shared gives up that isolation so separate conversations can reuse the system prompt. The provider still verifies prefix compatibility, so a shared key can only miss — it can never return another conversation's content.

With no leading system or developer message there is no shared prefix to reuse, so shared falls back to the per-conversation auto key instead of emitting a model-only key that every conversation on the model would share.

A compaction rewrites the conversation prefix, so the derived key (auto and shared) rotates once per compaction: the newest compactionId from DSH's compaction checkpoint is mixed into the hash, giving the rewritten prefix a clean cache entry instead of filing it under a key that pointed at the old prefix. Sessions that never compact are unaffected — their key derivation is byte-identical.

The plugin also sends an x-affinity header with the same value. The header is undocumented and unverified: Mistral's prompt-caching documentation does not mention it, and an A/B run against the live API measured no difference in cache hits (7/8 with the header vs 8/8 without; the only miss was the first, cold call). It is kept because third-party evidence suggests it can influence cache routing, servers that do not know it ignore it, and removing it cannot be shown neutral — treat it as a hint that may help, not a guarantee that requests land on the same server. cache.affinity and cache.reportUsage can be turned off independently; turning off reportUsage also costs you the cache-hit numbers.

To see what actually happened, GET /plugins/dsh-mistral-pro/wire?limit=20 now includes a usage entry per request with promptTokens and cachedTokens. A hit rate near 0% with a stable prefix usually means the prompt is under 64 tokens, the cache expired between turns, or something upstream is rewriting the first messages.

GET /plugins/dsh-mistral-pro/cache sums the whole log into requests, promptTokens, cachedTokens and hitRate, plus the 20 newest usage entries.

Settings

  • Accounts: add, remove, reorder, enable/disable, store a key (save & enable), test connectivity.
  • Models: toggle visibility, override context window, max output, and default reasoning effort.
  • Advanced: automatic failover, cooldown, timeouts, catalog refresh, output cap, prompt-cache key, cache affinity, stream usage reporting, transport, proxy, button display mode, refresh interval, and an optional cookie fallback.
    • Send x-affinity (cache.affinity, on by default): sends the cache key as x-affinity; an undocumented, unverified cache-routing hint (see Prompt caching).
    • Report stream usage (cache.reportUsage, on by default): sends stream_options.include_usage; turning it off also hides the cachedTokens counts.
    • Transport (advanced.transport, default https): https posts the serialized body over node:https; fetch routes through globalThis.fetch so in-process wrappers can see and rewrite the request.

Mistral Pro settings page

All settings take effect immediately without restarting the plugin.

Diagnostic Routes

Same-origin read-only routes registered for troubleshooting:

Route Description
/plugins/dsh-mistral-pro/health account status, registered providers, model count
/plugins/dsh-mistral-pro/meta active usage / advanced / accounts configuration
/plugins/dsh-mistral-pro/models model catalog, visibility, limits, reasoning options
/plugins/dsh-mistral-pro/usage?account=&force=1 aggregated usage snapshot
/plugins/dsh-mistral-pro/test?account= run a /v1/models check against a specific account
/plugins/dsh-mistral-pro/refresh force a live model catalog refresh
/plugins/dsh-mistral-pro/settings settings namespace and revision
/plugins/dsh-mistral-pro/wire?limit=20 newest wire fingerprints, per-message key layout, and cache usage
/plugins/dsh-mistral-pro/cache aggregate prompt-cache hit rate and the 20 newest usage entries

Transport and the wire log

The chat request is posted over node:https with the body the adapter already serialized, not through globalThis.fetch.

Proxy plugins replace globalThis.fetch and re-serialize request bodies on the way out. billion-context's ACP loop, for one, adds reasoning_content: "" to every assistant tool-call message whenever the model id matches /deepseek/i — and deepseek-v4-1-flash-1017 does. Mistral forbids that field on assistant messages, so the rewrite turns every turn after the first tool call into 422 extra_forbidden. node:https cannot be patched by another plugin in the same process, so the bytes the adapter builds are the bytes that leave the machine.

  • DSH_MISTRAL_TRANSPORT=fetch restores the old path; use it only when no wrapper rewrites bodies.
  • A configured advanced.proxy always uses fetch + undici ProxyAgent, because that path needs the dispatcher.
  • Trade-off: traffic sent this way skips a proxy plugin's compression and rewrite. Correctness comes first.

Every request appends one line to %TEMP%\dsh-mistral-wire.log (override with DSH_MISTRAL_WIRE_LOG): model id, message count, each message's role and keys, how many messages carry reasoning_content, and a 16-hex SHA-256 of the serialized body. A second usage line records promptTokens and cachedTokens. The log is trimmed to 400 lines and a logging failure never interrupts a call.

The log is created with mode 0600: %TEMP% is usually /tmp (1777), and the entries are prompt metadata. If an older version left the file world-readable, the next write tightens it to 0600, and rotation keeps that mode.

Data & Privacy

  • Chat requests are sent only to the configured Mistral API endpoint.
  • Model catalog refreshes send only the API key and User-Agent.
  • Usage queries are sent only to admin.mistral.ai (configurable) using the cookie you provide.
  • API keys and cookies are stored in the host credential store; the browser only sees whether a credential is set.

Known Limitations

  • The /v1/billing/budget and admin usage endpoints are undocumented. The plugin reads them by field name, so upstream schema changes may hide individual windows.
  • prompt_cache_key is a hint, not a guarantee: a stable prefix can still miss after the provider's cache expires, and prompts under 64 tokens never hit.
  • Failover account promotion is kept in memory only and resets to the configured order when the process restarts.
  • Image inputs require DSH's attachments service and fall back to placeholder text if unavailable.
  • Proxy support requires undici to be resolvable in the host environment; otherwise proxied requests fail with an explicit error.

Development

npm test                  # every test/*.mjs suite, no network, no keys
npm run check             # node --check over every shipped JS file
node test/tool-name.mjs   # or run a single suite directly

Tests run against a local mock HTTP server simulating Mistral SSE streams, with no external network calls and no API keys.

Upstream sync

This fork tracks upstream 01NGVI-QINGYI/dsh-mistral-pro by cherry-picking concrete fixes — never by merging upstream main, which would revert the 0.2.x/0.3.0 work. Add the remote once and inspect what is new:

git remote add upstream https://github.com/01NGVI-QINGYI/dsh-mistral-pro
git fetch upstream --depth=50
git log --oneline upstream/main

See CONTRIBUTING.md for the merge policy, the version policy and how to record a sync in CHANGELOG.md.

License

MIT · originally by 01NGVI-QINGYI, fork maintained by lordraiden.

—/ 5

No ratings yet

Verified DSH bundle

Commit 1e7b5b22299b

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout