dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DSH plugin: searchable/filterable model manager for OmniRoute (or any OpenAI-compatible gateway) — provider directory, modality discovery, multi-select enable.
OmniRoute model manager: a DSH host+client plugin that pulls every OmniRoute model and its modalities, and lets you control which OmniRoute models DSH actually serves through a searchable / filterable / multi-select settings page.
Pain point: OmniRoute (an OpenAI-compatible gateway) exposes a lot of models (1000+ on this machine). DSH's built-in "Models" page has no search or filter, and its discovery contract does not return modalities (
DiscoveredModelViewonly has id/name/contextWindow/maxTokens). This plugin maps OmniRoute/v1/modelsrealinput_modalities/context_length/max_output_tokensintollm-pi-ai.providers.<route>.models; a single check applies immediately, no restart needed.
/, e.g.
opencode-go/deepseek-… → opencode-go) is parsed out; the UI has a "Vendor" dropdown and a
vendor column to filter/view by vendor (opencode, oc, auto, github, no namespace…).llm-pi-ai.providers (route, displayName, api, baseURL, model count, OpenAI-compatible or not);
the top "Route" dropdown switches which endpoint's models you manage./v1/models (default http://localhost:20128/v1), giving each
model's input (text/image, derived from input_modalities/capabilities.vision),
contextWindow, maxTokens.llm-pi-ai.providers.<route>.models in one go (DSH serves exactly the
checked ones), written through the official settings seam into settings.yaml and effective on the
next request; sibling fields like apiKeyEnv/api/baseURL are preserved.settings.yaml).text/image (what DSH understands); video/audio etc. are clipped so the
plugin never writes modalities DSH cannot handle (and thus fails validation).web_search to one of two sources, selected in the Web search card — the OmniRoute aggregate search (POST /v1/search, Tavily/Brave/Exa/Serper/Ollama/…) or the DeepSeek official native search. When the OmniRoute source is disabled, or the source is set to DeepSeek official, web_search automatically falls back to DeepSeek's official search instead of failing. For the OmniRoute source, the key is optional (OmniRoute uses its own per-provider stored keys) but if your endpoint requires one it resolves in this order: literal searchApiKey → DSH credentials ref named by searchApiKeyEnv → process env (same pattern as the official dsh-web-search-deepseek).Every OmniRoute call now needs a valid key — since OmniRoute 3.8.50 even
GET /v1/models (the model catalog) rejects anonymous requests with 401
(REQUIRE_API_KEY does not change this).
apiKeyEnv through the DSH credential store first (the exact seam llm-pi-ai
uses for chat), then the process env, then the ?apiKey= query parameter.
Store the OmniRoute gateway token once as a DSH credential (or set the env
var) and both the catalog and chat work.llm-pi-ai adapter (via @earendil-works/pi-ai)
requires a non-empty key and throws No API key for provider: <route>
otherwise. Fill the provider's API key field (a DSH credential ref) in the
"Models" settings. pi-ai also accepts a provider-level
headers.authorization, but that is the non-standard path, not recommended.For the web search provider the key is genuinely optional (OmniRoute serves
POST /v1/search anonymously and uses its own stored upstream keys) — see the
Web search bullet above.
src/index.ts): registers two same-origin JSON routes (ctx.webServer.register):
GET /omniroute-models/api/catalog?provider=<route> — enumerates the llm-pi-ai provider
directory (providers) → reads the route's current models → requests GET {baseURL}/models →
returns { configured, compatible, provider, displayName, api, baseURL, providers:[...], models:[{id,name,input,contextWindow,maxTokens,enabled}], enabledCount, totalCount }; returns compatible:false only when the route protocol cannot be listed through an OpenAI-shaped /v1/models; anthropic-messages/openai-responses are pulled just like openai-completions (the OmniRoute gateway's model catalog is protocol-agnostic and always OpenAI format).POST /omniroute-models/api/apply — { provider, models:[{id,name,input,contextWindow,maxTokens}] },
validated then written via ctx.settings.mutate('llm-pi-ai', [{op:'set', path:['providers',provider,'models'], value}], expectedRevision);
conflicts return SETTINGS_CONFLICT, schema rejection returns settings-rejected.llm-pi-ai namespace (that is the adapter
@deepseek-ai/dsh-llm-pi-ai's); it only reads/writes an existing namespace.src/client/index.tsx): subscribes the settings.section slot (id omniroute-models, order 15),
renders a React table; all data goes through same-origin fetch, no ctx.api/LLM remote.src/index.ts): owns the omniroute-models settings namespace for search config and, when enabled, registers a ctx.web WebSearchProvider (id omniroute) so DSH's web_search uses OmniRoute; config/test routes GET|POST /omniroute-models/api/search-config and POST /omniroute-models/api/search-test. The section is registered via installSettingsSection (which waits for the settings service), so the plugin's activation order never races it; available() only requires enabled + an http(s) base URL, and the key is resolved lazily per request./v1/models can mix in embedding/video/image-generation
entries; the host's fetchCatalog filters by type (embedding|video|image|audio|music|rerank) so they are
never mis-listed/mis-saved as text chat models. OmniRoute ≥3.8.50 additionally lists its search
engines as pseudo models (brave-search/web, tavily-search/news, …) with no type; those are
filtered by their -search vendor suffix.@deepseek-ai/dsh-client-locale), namespace omniroute-models.src/client/locales.ts: zh is the key-set source of truth; en: Record<OmniKey,string>
is compile-checked complete.inject = ['slots','locale']; apply calls ctx.effect(() => ctx.locale.register(NS, { zh, en })),
then ctx.locale.bind(NS) for t; the settings.section registers with inject: () => ({ t }) and
label: () => t('nav'); the section reads copy from props.t and refreshes when the language switches
(DSH settings → Language).code (e.g. catalog.notConfigured,
apply.duplicateId) plus params, keeping the Chinese message for curl/development; the client's hostMessage
only translates known code (host.* keys), so SETTINGS_CONFLICT/settings-rejected exception codes are not mis-translated.locales.ts, add it to dictionaries, and add the locale to the
registration loop in apply.# Dev prerequisites: install build tooling, then point @deepseek-ai runtime types at the DSH harness
pnpm install && pnpm link:dev
npm run build # host → lib/index.js
npm run build:client # client → lib/client.js
dev_inject_plugin /path/to/dsh-omniroute-models, host+client applied immediately.dsh plugin --profile web add github:publieople/dsh-omniroute-models#v0.2.5, then restart the target profile.text + image badge).llm-pi-ai.providers.omniroute.models in settings.yaml.curl "http://127.0.0.1:3080/omniroute-models/api/catalog" should return the model list + modalities + enabled.curl -X POST .../api/apply -d '{"provider":"omniroute","models":[{...}]}'.~/.dsh/settings.yaml's llm-pi-ai.providers.omniroute.models (with input/contextWindow/maxTokens, siblings preserved).