dsh-catalog-refresh
界面增强 活跃维护

dsh-catalog-refresh

joshryandavis/dsh-catalog-refresh

提供自动重建模型目录的插件,支持OpenRouter、OpenCode、Fireworks等平台的模型目录更新,无需人工干预即可完成目录重建。

0
Stars 标星
0
Forks 分支
0
Watchers 关注
0
Open Issues
JavaScript
主要语言
MIT
开源协议
35 KB
仓库大小
27 天前
最后推送
一键安装扩展 / 插件指令
dsh plugin --profile web add github:joshryandavis/dsh-catalog-refresh
git clone https://github.com/joshryandavis/dsh-catalog-refresh.git
git clone git@github.com:joshryandavis/dsh-catalog-refresh.git
README.md main

English | 中文

dsh-catalog-refresh

Rebuild the model catalogues DSH serves at runtime, so OpenRouter, OpenCode,
Fireworks, and friends show the live model lists instead of the stale
snapshot baked into the @earendil-works/pi-ai version DSH pins.

Why this exists

DSH gets every LLM provider model list from the @earendil-works/pi-ai
package it bundles: dsh-llm-pi-ai reads pi-ai's static MODELS registry
(dist/providers/data/*.json, generated when pi-ai publishes). The model
picker, the Models page discovery probe, and request routing all serve that
baked-in list — so when OpenRouter adds a model after a pi-ai release, DSH
users must hand-add custom models until DSH bumps pi-ai.

This plugin fetches the providers' own live model endpoints at boot (and
then on a cadence), converts them into pi-ai Model entries, and patches
the harness's pi-ai MODELS registry in place — the same module instance
dsh-llm-pi-ai reads. Patching MODELS alone is not enough for the model
picker: dsh-llm-pi-ai materializes each route's model list when its
settings section changes, so the plugin also writes a per-refresh stamp
into the declared providers' headers in the llm-pi-ai settings section
(a deep merge; nothing configured is touched). That makes dsh-llm-pi-ai
re-resolve its routes against the patched registry, so the picker updates
live — no restart required.

Sources

provider endpoint auth detail
openrouter https://openrouter.ai/api/v1/models public full (pricing, context, max tokens, reasoning, modalities)
opencode https://opencode.ai/zen/v1/models public id list; installed metadata kept, new ids routed by prefix
fireworks https://api.fireworks.ai/inference/v1/models API key id + context, vision, chat flags
groq https://api.groq.com/openai/v1/models API key id + context, max tokens, modalities
together https://api.together.ai/v1/models API key id list; installed metadata kept
deepseek https://api.deepseek.com/models API key id list; installed metadata kept

Key-gated sources use the conventional environment variable
(FIREWORKS_API_KEY, GROQ_API_KEY, TOGETHER_API_KEY, DEEPSEEK_API_KEY)
or the credential record DSH's Models page stores (llm-pi-ai/<provider>).
Without a key the source is skipped and the installed catalogue stays in
place.

How it patches

  1. Locates the pi-ai package the running harness imports, anchored on the
    dsh CLI entry that started the process (process.argv[1] →
    @deepseek-ai/dsh-llm-pi-ai → @earendil-works/pi-ai).
  2. Imports that package's dist/models.generated.js by file URL — Node's
    module cache keyed by URL makes it the same instance dsh-llm-pi-ai
    reads via @earendil-works/pi-ai/providers/all.
  3. Replaces MODELS[provider] with the rebuilt map. Discovery calls read
    the registry live, so the Models-page probe is immediately fresh.
  4. Nudges the picker. dsh-llm-pi-ai only re-materializes a route's
    model list when its settings section changes, and the picker
    (llm.models / session.models) serves those materialized lists. So
    after every patch the plugin writes a fresh stamp into each declared
    provider's headers in the llm-pi-ai settings section (settings.yaml
    gains an x-catalog-refresh: <timestamp> entry — inert, and the deep
    merge leaves every other field intact). The section change makes
    dsh-llm-pi-ai re-resolve its routes against the patched registry, so
    the picker shows the rebuilt catalogue immediately.

Boot runs the cache round first (no network, so the picker resolves fresh
data as early as possible), then the live round; both nudge. The stamp is
fingerprint-gated: a provider is only nudged when its model set actually
changed (or once per boot), so settings.yaml is not rewritten on every
restart.

Merging rules: for OpenRouter the live entry wins (pricing, context, max
tokens, reasoning, modalities) while curated fields (compat,
thinkingLevelMap) carry over from the installed entry when present. For
id-list sources, installed entries stay authoritative for curated fields
(compat, thinkingLevelMap, reasoning, cost, wire protocol) while the
live listing refreshes the structural facts the endpoint reports
(context window, max tokens, input modalities, name) — Fireworks and Groq
both publish these — and unknown ids are added with structural defaults.
Entries the endpoint reports as non-chat (speech/audio output such as Groq's
whisper/orpheus) are dropped: they cannot serve chat traffic.

Fetched lists are cached under $DSH_HOME/catalog-refresh/; a restart
without network still applies the last good refresh.

Reasoning-effort support

dsh-llm-pi-ai only shows the composer's reasoning-effort selector for
models that declare effort metadata. The pi-ai catalogue ships none, and the
dsh-thinking-effort plugin only covers hand-declared settings models — so
catalogue-served models lost the selector after the rebuild. This plugin
bakes a default thinkingLevelMap into every rebuilt reasoning model,
mirroring dsh-thinking-effort's official preset:

  • Off / High / Max for reasoning models (OpenRouter's
    openai-completions wire receives reasoning: { effort: "high" | "max" }),
  • no effort control for non-reasoning models (same as before).

The offered levels come from CATALOG_REFRESH_EFFORTS (comma-separated level
ids; off is always supported). For example
CATALOG_REFRESH_EFFORTS=off,low,medium,high offers Off/Low/Medium/High
instead. Custom gateway spellings (e.g. sending ultra for High) still need
per-model reasoningEfforts in the llm-pi-ai settings document — this
plugin only provides the catalogue default.

Id-list sources (opencode, fireworks, groq, together, deepseek) return model
ids only — no capability metadata. Known ids keep the installed catalogue's
flags; unknown ids assume reasoning-capable (the overwhelming case for
chat models), so new models like Fireworks glm-5p3 get the effort selector
immediately. Definitively non-reasoning families (Fireworks
*-embedding-* / *-reranker-*) are excluded and never advertise effort.

Fireworks wire note. Fireworks validates Anthropic-style
thinking.budget_tokens strictly and rejects requests where
max_tokens is not strictly greater than the budget (a 400 this plugin's
effort support surfaced). The plugin therefore routes all Fireworks
models through Fireworks' native OpenAI-compatible endpoint with
reasoning_effort (low/medium/high/max all accepted), which avoids
budget validation entirely. Off on a Fireworks reasoning model maps to the
provider default — Fireworks rejects disabling thinking on thinking models,
so that is the closest equivalent.

Configuration (environment)

variable meaning default
CATALOG_REFRESH_DISABLE 1 disables the plugin entirely off
CATALOG_REFRESH_INTERVAL_HOURS re-fetch cadence 12
CATALOG_REFRESH_HOME cache directory override $DSH_HOME or ~/.dsh
CATALOG_REFRESH_EFFORTS offered reasoning levels for rebuilt models off,high,max

Install

dsh plugin --profile <profile> add dsh-catalog-refresh

Boot output shows one line per source:

[dsh-catalog-refresh] openrouter: patched 396 models (live)
[dsh-catalog-refresh] opencode: patched 67 models (live)
[dsh-catalog-refresh] fireworks: skipped (… answered 401)

Operational notes

  • pnpm copies the package into the profile at install time. After editing
    the plugin source, run pnpm install again in the profile (the profile's
    node_modules/dsh-catalog-refresh is a snapshot, not a symlink),
    then restart dsh web.
  • Key-gated sources (fireworks, groq, together, deepseek, …) use the
    conventional environment variable or the credential record DSH's Models page
    stores (llm-pi-ai/<provider>).
  • The patch is live only in the running process; restarting the app re-runs
    the refresh (cached lists apply first if the network is unavailable).
  • settings.yaml gains an x-catalog-refresh header per declared
    provider
    — this is the re-resolution nudge, not a configuration change.
    It is safe to delete; the next refresh rewrites it.

Development

npm run check   # syntax-check the modules
npm test        # unit tests (conversion, merge, cache-first patch, nudge) — no network, no machine-specific paths