DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

miku05231 /

miku05231/dsh-provider-quote

Verified

DSH 插件:对正在使用的模型提供商询价(余额 + 单价,参考 cc-switch 的「服务商询价」)

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@ae3e2ada

provider-quote · 对正在使用的模型提供商询价

参考 cc-switch 的「服务商询价」,把同一件事做进 DSH:当前在用哪个提供商,就查它的余额与单价。

问的问题 数据来源
我还有多少钱? 按 baseURL 自动分派厂商余额适配器(见下表),也可直接粘贴 cc-switch 的 usage_script
这个模型多少钱? OpenRouter 公开价格目录 https://openrouter.ai/api/v1/models(输入 / 输出 / 缓存读写,USD per 1M tokens),可本地覆盖
我能用哪些模型? 提供商自己的 GET /models(OpenAI 兼容)

装什么

一个 bundle,两半:

  • 宿主
    • index.js —— 包根转发(export * from './lib/host.js')
    • lib/host.js —— 宿主实现;lib/pricing.js —— 与 DSH 无关的纯询价引擎(适配器、价格目录、报价与文本渲染)
    • 工具 provider_quote —— agent 直接问价:provider_quote() / provider_quote({ all: true }) / provider_quote({ provider: 'openrouter' }) / provider_quote({ models: true })
    • 路由
      • GET /api/provider-quote/summary(?refresh=1 跳过 30s 报价缓存)
      • GET /api/provider-quote/refresh(同上,路径别名)
      • GET /api/provider-quote/ping —— 自检:版本、当前加载的模块 URL、目录缓存里的模型数
    • 可选的定时预热(autoRefreshMinutes)
  • 客户端(client.js) —— 两个挂载点:
    • 对话输入框下方的常驻胶囊(slot conversation.composer.dock,与自带的 stats 同一行):● 余额 12.4 USD —— 只显示当前会话选中的提供商的余额;在输入框的模型选择器里换提供商,胶囊立刻跟着换(报价本身含所有提供商的余额,不需要重新询价);点一下立即重新询价(走 ?refresh=1,跳过宿主的 30s 缓存),查询中是「余额查询中…」,失败是「余额没查到」,选中的提供商没被收录时是「余额未知」并自动重查一次;悬停可见提供商、余额来源与失败原因;每 5 分钟静默刷新一次。要看各提供商余额、模型单价与目录候选,用下面的设置页面板。
    • 「设置 → 模型」页底部的询价面板(slot settings.models.footer):当前提供商的余额、当前模型的单价、其余提供商的概览,带「刷新询价」按钮。
    • 两个组件共用同一份取数(模块内 store),不会重复请求宿主。

安装

# 在会话里用 plugin_manager 指定工作区目录(不要在 profile 目录里手写 package.json / 跑 pnpm)
plugin_manager action=install_bundle target=<本插件的绝对路径,如 ~/dev/dsh-provider-quote>

装完看返回的 application 与 warnings:applied 即生效;宿主代码改动后需要重启 DSH 才会加载新代码(见文末「注意」)。

余额适配器

adapters: auto(默认)时按 baseURL 的 host 优先、其余适配器兜底依次尝试;每次查询会记录试过的 URL 与 HTTP 状态,失败也给出可解释的原因。

id 接口 适用
openrouter GET {origin}/api/v1/credits(+ /api/v1/key) OpenRouter 官方:余额 = total_credits − total_usage
deepseek-official GET {origin}/user/balance DeepSeek 官方:balance_infos[](CNY/USD),is_available
relay-user-balance GET {base}/user/balance、{origin}/user/balance 多数中转站的通用布局:balance / remaining / is_active / unit
one-api GET {base}/api/user/self one-api / New API 布局:quota(÷500000 估算 USD,仅参考)
openai-billing GET {base}/dashboard/billing/subscription + /usage 旧版 OpenAI 计费接口
script:<host> 你配置的 scripts 任意厂商:直接粘贴 cc-switch 的 usage_script

自定义脚本与 cc-switch 完全兼容,模板变量 {{baseUrl}} / {{apiKey}} 会被替换:

scripts:
  relay.example.com: >-
    ({ request: { url: "{{baseUrl}}/user/balance", method: "GET",
                  headers: { "Authorization": "Bearer {{apiKey}}", "User-Agent": "cc-switch/1.0" } },
       extractor: function (response) { return { isValid: response.is_active || true, remaining: response.balance, unit: "USD" }; } })

以上这条就是从 cc-switch 的 cc-switch.db → providers.meta.usage_script 里原样抄来的:把你在 cc-switch 里给某个供应商配好的脚本粘到 scripts.<host>,行为与 cc-switch 一致(插件不直接读 cc-switch 的数据库,只认这个格式)。

中转站与其他网关

「中转站」指第三方把各家官方 API 转卖或聚合出来的一个 OpenAI 兼容端点(常见 one-api、New API 面板,也有自研网关)。在 DSH 里它就是一个 baseURL 不同的普通提供商,但有几个特点值得知道:

  • 余额接口各家不同,没有统一标准:GET {base}/user/balance(多数中转站,字段 balance / remaining / is_active / unit)、one-api 系的 GET {base}/api/user/self(quota,÷500000 估算 USD)、旧版 OpenAI 的 /dashboard/billing/*;也有部分网关(只转发、不做结算的那种)根本没有公开余额接口。插件按 baseURL 的 host 优先匹配、其余适配器兜底逐个试,查不到也会列出试过的 URL 与 HTTP 状态,而不是留白。
  • 有倍率:中转站通常按自己的倍率计费(cc-switch 里叫 cost_multiplier)。插件显示的单价来自 OpenRouter 公开目录,是参考价;对不上就把实付价写进 priceOverrides(可直接给 input / output / cacheRead / cacheWrite)。
  • 模型会重名:同一个 id(例如 deepseek-flash)在不同站可能是不同后端,所以插件不猜价 —— 匹配不到就写「未匹配到价格」并给出目录候选,由你确认后再 priceOverrides 锚定。
  • key 走凭据名:配置里写 apiKeyEnv: MY_RELAY_API_KEY 这类名字,插件用 ctx.credentials 按需解析(改 key 立即生效),从不落盘、不打印明文。
  • 同一站可以配多个条目:比如同一个中转站分成「按月套餐」和「按量付费」两个 key,写两个 provider id 即可分别看余额与单价。

单价匹配

模型 id 依次按 priceOverrides.<model>.openrouterId → 提供商返回的 openrouter.slug → 完整 id → 裸模型名 → 归一化名(去厂商前缀与符号)匹配目录;匹配不到就如实写「未匹配到价格」,不猜价,并在括注里给出目录中的候选(suggestions,最多 3 条):

deepseek-flash 未定价(候选:deepseek/deepseek-v4-flash、deepseek/deepseek-v4.1-flash、deepseek/deepseek-v4-flash-0731)

看到候选就说明「大概是哪个型号」,确认后写进 priceOverrides 即可(两形态都支持):

priceOverrides:
  deepseek-flash: { openrouterId: deepseek/deepseek-chat }   # 指向目录里的对应条目
  my-local-model: { input: 0.3, output: 0.9, cacheRead: 0.03, cacheWrite: 0.4 }

价格目录缓存

目录一次抓 458 个模型,默认缓存 6 小时(catalogTtlMs):

  • 缓存里存的是整个目录(不是摘要),重放缓存时仍能正常定价;
  • 抓取失败(超时 / 非 JSON / 返回 0 个模型)时保留上一次成功的目录并标记 stale,面板显示「· 沿用缓存」,文本输出写「沿用上次成功获取(重取失败: …)」;
  • 完全没有可用目录时才显示「价格目录不可用」。

提供商从哪来

  1. 自动发现:读取 profile 里各插件行的生效配置(configEditor),凡出现 providers: { id: { baseURL, apiKeyEnv, models } } 形状即登记 —— 例如官方 llm-pi-ai 适配器里配的中转站条目。解析不出 baseURL 的条目(如凭据型 provider 的占位行)会被跳过,只有你在 config.providers 里显式写了才保留。
  2. API key:apiKeyEnv 指向的凭据名经 ctx.credentials 解析(每次查询重新解析,改 key 立即生效),插件从不读写、也不打印明文。
  3. 兜底:插件自己的 config.providers 可覆盖或补全(baseURL / apiKeyEnv / adapters / skipModels)。
  4. 内置路由:像 deepseek-official(DeepSeek 官方账号,凭据型 provider,配置里不写 baseURL)这类官方提供商由插件内置映射到官方端点 —— deepseek-official → https://api.deepseek.com + 凭据 DEEPSEEK_API_KEY,因此切到它也能查到余额(GET /user/balance,CNY)。

配置(cordis.patch.yml 中该行的 config)

键 默认 含义
enabled true 关掉整行
adapters auto 'auto' 或适配器 id 数组(写死集合)
timeoutMs 12000 单个 HTTP 请求超时
catalogTtlMs 21600000 价格目录缓存(6h)
autoRefreshMinutes 0 后台预热间隔,0 = 关闭
scripts {} host/glob → cc-switch usage_script
priceOverrides {} 模型 → 目录 id 或直给单价
providers {} provider id → { baseURL, apiKeyEnv, adapters, skipModels }

演示(模拟数据)

下面这段是 node tests/demo-openrouter.mjs 的输出:以 OpenRouter 为例,用 fetch 桩喂进 credits / key / 价格目录 / 模型列表,跑真实的报价管线(适配器分派、目录解析、单价匹配、文本渲染都是真的),余额与单价数字是编的:

$ node tests/demo-openrouter.mjs
# 提供商询价  2026-10-10T03:41:55.634Z
当前使用:openrouter · anthropic/claude-sonnet-4.5
价格目录:https://openrouter.ai/api/v1/models(458 个模型)

## OpenRouter ★ 使用中
- baseURL: https://openrouter.ai/api
- 余额: 12.4 USD · 总额 25 USD,已用 12.6 USD · 账户有效 · 来源 GET /api/v1/credits
- 单价(USD / 1M tokens,2/2 个模型已定价):
  - ★ anthropic/claude-sonnet-4.5: 入 $3 / 出 $15 / 缓存读 $0.3(USD / 1M tokens)  ←目录 anthropic/claude-sonnet-4.5
  - openai/gpt-5-mini: 入 $0.25 / 出 $2 / 缓存读 $0.025(USD / 1M tokens)  ←目录 openai/gpt-5-mini

在 DSH 里等价的取数方式(换成你自己的提供商会返回真实数字):

curl -s "http://127.0.0.1:<DSH 端口>/api/provider-quote/summary"    # 加 ?refresh=1 跳过 30s 缓存
curl -s "http://127.0.0.1:<DSH 端口>/api/provider-quote/ping"       # 自检:版本、模块 URL、目录缓存模型数
$ curl -s "http://127.0.0.1:<DSH 端口>/api/provider-quote/ping"
{"ok":true,"version":"0.3.0","entry":"lib/host.js","catalogModels":458,
 "adapters":"auto","providers":["openrouter","deepseek-official"]}

agent 侧则直接用工具:provider_quote() / provider_quote({ models: true }) / provider_quote({ provider: 'openrouter' })。

自测

node tests/demo-openrouter.mjs  # 演示:fetch 桩喂 OpenRouter 数据跑真实报价管线(不联网、不需要 key)
node tests/smoke.mjs --offline  # 16 项:只跑纯函数与缓存逻辑(含 fetch 桩,不联网)—— 任何机器都能直接跑
node tests/client-smoke.mjs     # 11 项:客户端 bundle 的模块工厂 + 两个 slot 注册 + 面板渲染 + 余额胶囊(显示/点击刷新/失败态/跟随提供商选择)

联网的那一段(真实余额 / 模型列表 / 价格目录)仓库里不含任何真实端点,要自己用环境变量把目标传进去 —— 目标清单是你自己的,不会进版本库:

DSH_QUOTE_LIVE='[{"id":"openrouter","name":"OpenRouter","baseURL":"https://openrouter.ai/api",
                  "apiKeyEnv":"OPENROUTER_API_KEY","models":["deepseek/deepseek-chat"],
                  "expectBalance":true,"expectModels":true}]' \
DSH_CREDENTIALS=~/.dsh/.credentials.yaml node tests/smoke.mjs

每条目标支持 usageScript(cc-switch 脚本,按该 host 生效)、priceOverrides、expectBalance / expectModels / expectFailure;凭据只从 DSH_CREDENTIALS(默认 ~/.dsh/.credentials.yaml)读取,从不打印明文,不设 DSH_QUOTE_LIVE 就跳过该段。

lib/pricing.js 不 import 任何 DSH 模块,所以适配器可以在 DSH 之外独立验证 —— 这也是上面脚本能直接跑的原因。

注意与已知边界

  • 改宿主代码后必须重启 DSH:loader 按「解析后的模块 URL」缓存模块,换文件名(新 URL)或 plugin_manager set_bundle 关/开可以在运行中切到新代码代,同一 URL 的旧模块实例换不掉。客户端半(client.js)不受影响:保存后 set_bundle 关/开即会在页面上重新挂载(无需重启)。
  • 价格是 OpenRouter 目录参考价;中转站常有自己的倍率,差额需用 priceOverrides 校正。
  • 部分网关(只转发不结算的那种)没有公开余额接口,面板会明确显示「额度没查到」并列出试过的接口,与 cc-switch 的表现一致。
  • 面板不会写配置:scripts / priceOverrides / providers 目前手写在 cordis.patch.yml 的 config 里(改完重启 DSH 生效)。
  • 插件只读不写:查询余额会带上你的 API key 去对应厂商自己的接口,插件不发往任何第三方;client.js 也只在本地渲染,不上报任何数据。

开源协议

MIT © 2026 miku05231

你可以自由使用、修改、分发(保留版权与许可声明即可)。插件不自带任何依赖、不收集数据;查询余额时会把凭据发往你自己配置的提供商端点,这是该功能本身必需的。

—/ 5

No ratings yet

Verified DSH bundle

Commit ae3e2ada538a

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