dsh-openrouter-provider-advisor 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -3
- package/README.md +23 -10
- package/README_EN.md +20 -8
- package/dsh.plugin.json +3 -3
- package/lib/client.js +19 -31
- package/lib/index.js +26 -20
- package/lib/types/client/index.d.ts +31 -2
- package/lib/types/client/provider-panel.d.ts +14 -6
- package/lib/types/host-adapter.d.ts +30 -40
- package/lib/types/index.d.ts +14 -1
- package/package.json +5 -23
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,48 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.1.4] - 2026-09-04
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Compatible with DSH web `>= 0.1.2-rc.1` again after the breaking client/Host
|
|
10
|
+
API changes in that release. The previous build failed to load on current
|
|
11
|
+
DSH because it imported `settingsNamespace` / `installSettingsSection` from
|
|
12
|
+
`@deepseek-ai/dsh-settings` (both removed in 0.1.2) and injected the removed
|
|
13
|
+
`apiProxy` service.
|
|
14
|
+
- Host: the OpenRouter settings namespace is now registered through
|
|
15
|
+
`ctx.settings.register(...)` and read through its live owner scope
|
|
16
|
+
(`dsh >= 0.1.2` settings API), replacing the removed helper functions.
|
|
17
|
+
- Host: session model catalog reads and session model selections now go
|
|
18
|
+
through `ctx.sessionController.modelCatalog()` / `selectModel(...)`
|
|
19
|
+
(the Session business API that replaced the `apiProxy` RPC service).
|
|
20
|
+
- Client: dropped every import from the removed `@deepseek-ai/dsh-client-*`
|
|
21
|
+
type packages (including `dsh-client-runtime`); the bundle now types the
|
|
22
|
+
runtime structurally, like current first-party/external plugins.
|
|
23
|
+
- Client: `sidebar.footer.action` / `shell.overlay` seats no longer inject a
|
|
24
|
+
`useSessions` render prop, so the trigger and overlay read the current
|
|
25
|
+
session from the root `sessions` service's list store instead.
|
|
26
|
+
- Manifest: `dsh.plugin.json` engine floor raised to `>= 0.1.2-rc.1`, and the
|
|
27
|
+
client module injection list narrowed to the modules current DSH serves.
|
|
28
|
+
|
|
29
|
+
### Validation
|
|
30
|
+
|
|
31
|
+
- 80 automated tests passing (adapter specs updated to the
|
|
32
|
+
`sessionController` seam).
|
|
33
|
+
- TypeScript typecheck and production build passing.
|
|
34
|
+
- `npm audit --omit=dev` reports zero vulnerabilities.
|
|
35
|
+
|
|
36
|
+
## [0.1.3] - 2026-08-25
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- Updated npm `repository`, `homepage`, and `bugs` metadata after the GitHub account was renamed from `xuanfengtechx` to `xuan666-lab`, allowing DSH Store to associate the published package with the current repository URL.
|
|
41
|
+
|
|
42
|
+
### Validation
|
|
43
|
+
|
|
44
|
+
- Automated tests, TypeScript typecheck, and production build passing.
|
|
45
|
+
- `npm audit --omit=dev` reports zero vulnerabilities.
|
|
46
|
+
|
|
5
47
|
## [0.1.2] - 2026-08-24
|
|
6
48
|
|
|
7
49
|
### Added
|
|
@@ -51,6 +93,7 @@ All notable changes to this project are documented here.
|
|
|
51
93
|
|
|
52
94
|
- Initial public release.
|
|
53
95
|
|
|
54
|
-
[0.1.
|
|
55
|
-
[0.1.
|
|
56
|
-
[0.1.
|
|
96
|
+
[0.1.3]: https://github.com/xuan666-lab/dsh-openrouter-provider-advisor/compare/v0.1.2...v0.1.3
|
|
97
|
+
[0.1.2]: https://github.com/xuan666-lab/dsh-openrouter-provider-advisor/compare/v0.1.1...v0.1.2
|
|
98
|
+
[0.1.1]: https://github.com/xuan666-lab/dsh-openrouter-provider-advisor/compare/v0.1.0...v0.1.1
|
|
99
|
+
[0.1.0]: https://github.com/xuan666-lab/dsh-openrouter-provider-advisor/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -6,6 +6,17 @@
|
|
|
6
6
|
|
|
7
7
|
> **核心价值:在不更换模型的前提下,减少不必要的调用成本,并在价格、速度和可靠性之间找到更适合当前工作负载的线路。**
|
|
8
8
|
|
|
9
|
+
插件推荐有机会找到比模型官网 API 明显更便宜的同模型线路。以 2026-08-25 面板中的 StreamLake `deepseek-v4-flash-0731` 报价为例,按 `1 USD ≈ 6.7225 CNY` 换算,与当时 DeepSeek 官网高峰期 Flash 报价对比如下:
|
|
10
|
+
|
|
11
|
+
| 每百万 tokens | StreamLake | DeepSeek 官网高峰期 | 预计降低 |
|
|
12
|
+
| --- | ---: | ---: | ---: |
|
|
13
|
+
| 输入(缓存命中) | $0.0028 ≈ ¥0.0188 | ¥0.10 | **81.2%** |
|
|
14
|
+
| 输入(缓存未命中) | $0.088 ≈ ¥0.5916 | ¥3.00 | **80.3%** |
|
|
15
|
+
| 输出 | $0.064 ≈ ¥0.4302 | ¥9.00 | **95.2%** |
|
|
16
|
+
| Code Agent 加权估算(2% / 8% / 90%) | ≈ ¥0.0632 | ≈ ¥0.8700 | **92.7%** |
|
|
17
|
+
|
|
18
|
+
> 以上仅是特定时间点的价格示例,不构成持续低价承诺;OpenRouter 供应商报价、DeepSeek 官网定价和汇率都会变化,请以插件实时结果与 [DeepSeek 官方价格页](https://api-docs.deepseek.com/zh-cn/quick_start/pricing/) 为准。汇率参考:[USD/CNY 2026-08-25](https://www.investing.com/currencies/usd-cny-historical-data)。
|
|
19
|
+
|
|
9
20
|
🌏 **中文** · [English](./README_EN.md)
|
|
10
21
|
|
|
11
22
|

|
|
@@ -36,7 +47,7 @@
|
|
|
36
47
|
|
|
37
48
|
## 安装
|
|
38
49
|
|
|
39
|
-
前置条件:DSH `0.1.
|
|
50
|
+
前置条件:DSH `0.1.2-rc.1` 或更新版本(插件适配了 0.1.2 移除 `settingsNamespace`/`installSettingsSection` 与 `apiProxy` 服务后的 Host/Client API)、Node.js `^22.19.0` 或 `>=24`,并已在 **DSH 设置 → 模型** 中配置 OpenRouter provider/API Key。
|
|
40
51
|
|
|
41
52
|
```bash
|
|
42
53
|
dsh plugin --profile web add dsh-openrouter-provider-advisor@latest
|
|
@@ -85,20 +96,21 @@ session.selectModel 切换当前会话
|
|
|
85
96
|
价格成本 = 输入价 × 2% + 输出价 × 8% + 缓存读取价 × 90%
|
|
86
97
|
```
|
|
87
98
|
|
|
88
|
-
###
|
|
99
|
+
### 可用率降权
|
|
89
100
|
|
|
90
|
-
`status != 0` 的 endpoint 直接排除。其余 endpoint
|
|
101
|
+
`status != 0` 的 endpoint 直接排除。其余 endpoint 使用连续、温和的通用降权,避免短时小幅波动造成排名断崖:
|
|
91
102
|
|
|
92
103
|
| OpenRouter 近 30 分钟 uptime | 保留分数 |
|
|
93
104
|
| --- | ---: |
|
|
94
105
|
| ≥99.5% | 100% |
|
|
95
|
-
| 99–99.5% |
|
|
96
|
-
|
|
|
97
|
-
|
|
|
98
|
-
|
|
|
99
|
-
|
|
|
106
|
+
| 99–99.5% | 97%–100%(连续插值) |
|
|
107
|
+
| 98–99% | 90%–97%(连续插值) |
|
|
108
|
+
| 95–98% | 75%–90%(连续插值) |
|
|
109
|
+
| 90–95% | 50%–75%(连续插值) |
|
|
110
|
+
| <90% | 10%–50%(连续插值) |
|
|
111
|
+
| 无数据 | 85% |
|
|
100
112
|
|
|
101
|
-
|
|
113
|
+
用户设置中的阈值和系数会在阈值以下 5 个百分点内渐进生效,并与通用降权取更严格者。
|
|
102
114
|
|
|
103
115
|
## Agent 工具
|
|
104
116
|
|
|
@@ -136,13 +148,14 @@ POST /api/v1/presets/{slug}/chat/completions
|
|
|
136
148
|
| 插件已安装但界面没变化 | 硬刷新浏览器;Host 有改动时重启 DSH。 |
|
|
137
149
|
| 未检测到 OpenRouter 凭据 | 在 DSH 设置 → 模型中配置 OpenRouter provider/API Key。 |
|
|
138
150
|
| 某供应商分数高但请求 429 | uptime 有统计延迟,临时切换到其他供应商。 |
|
|
151
|
+
| 面板默认选中的模型不是当前会话选的那一个 | DSH `>= 0.1.2` 的会话级模型选择由前端模型目录维护,Host 目录 API 只提供部署默认;在面板下拉里选择要分析的模型即可,推荐与切换都会按所选模型执行。 |
|
|
139
152
|
| `minimum release age` | 等待 profile 安全策略允许该版本后重试。 |
|
|
140
153
|
| DSH 拒绝 `.env` 中的 `DEEPSEEK_BASE_URL` | 从项目 `.env` 移除,并在启动 DSH 的终端中 `export`。 |
|
|
141
154
|
|
|
142
155
|
## 本地源码开发
|
|
143
156
|
|
|
144
157
|
```bash
|
|
145
|
-
git clone https://github.com/
|
|
158
|
+
git clone https://github.com/xuan666-lab/dsh-openrouter-provider-advisor.git
|
|
146
159
|
cd dsh-openrouter-provider-advisor
|
|
147
160
|
npm install
|
|
148
161
|
npm run verify
|
package/README_EN.md
CHANGED
|
@@ -6,6 +6,17 @@ It combines quantization, speed, cache-heavy pricing, context, and uptime into a
|
|
|
6
6
|
|
|
7
7
|
> **Core value: reduce avoidable inference spend without changing models, while finding a practical balance between cost, speed, and reliability.**
|
|
8
8
|
|
|
9
|
+
The recommendations can surface same-model routes priced well below the model vendor's own API. For example, the StreamLake `deepseek-v4-flash-0731` quote shown on 2026-08-25 compares with DeepSeek's peak-hour Flash pricing as follows, using `1 USD ≈ 6.7225 CNY`:
|
|
10
|
+
|
|
11
|
+
| Per 1M tokens | StreamLake | DeepSeek peak hours | Estimated reduction |
|
|
12
|
+
| --- | ---: | ---: | ---: |
|
|
13
|
+
| Input (cache hit) | $0.0028 ≈ ¥0.0188 | ¥0.10 | **81.2%** |
|
|
14
|
+
| Input (cache miss) | $0.088 ≈ ¥0.5916 | ¥3.00 | **80.3%** |
|
|
15
|
+
| Output | $0.064 ≈ ¥0.4302 | ¥9.00 | **95.2%** |
|
|
16
|
+
| Code Agent weighted estimate (2% / 8% / 90%) | ≈ ¥0.0632 | ≈ ¥0.8700 | **92.7%** |
|
|
17
|
+
|
|
18
|
+
> This is a point-in-time example, not a guarantee of persistent savings. OpenRouter provider quotes, DeepSeek pricing, and FX rates can all change; check the live recommendations and [DeepSeek's official pricing page](https://api-docs.deepseek.com/quick_start/pricing/). FX reference: [USD/CNY on 2026-08-25](https://www.investing.com/currencies/usd-cny-historical-data).
|
|
19
|
+
|
|
9
20
|
[中文](./README.md) · **English**
|
|
10
21
|
|
|
11
22
|

|
|
@@ -17,7 +28,7 @@ It combines quantization, speed, cache-heavy pricing, context, and uptime into a
|
|
|
17
28
|
- Model-aware ranking without a hard-coded model catalog.
|
|
18
29
|
- Best overall, price-first, speed-first, and context-first strategies.
|
|
19
30
|
- Code Agent price blend: 2% input, 8% output, 90% cache-read by default.
|
|
20
|
-
-
|
|
31
|
+
- Continuous, gentle 30-minute uptime penalties and hard filtering for unavailable endpoints.
|
|
21
32
|
- One-click switching for the active session and deployment default.
|
|
22
33
|
- Linked percentage sliders with locks; each group always totals 100%.
|
|
23
34
|
- Reuses DSH credential references; API keys never reach the browser.
|
|
@@ -78,13 +89,14 @@ cost = input price × 2% + output price × 8% + cache-read price × 90%
|
|
|
78
89
|
| OpenRouter trailing 30-minute uptime | Score retained |
|
|
79
90
|
| --- | ---: |
|
|
80
91
|
| ≥99.5% | 100% |
|
|
81
|
-
| 99–99.5% |
|
|
82
|
-
|
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
|
|
|
92
|
+
| 99–99.5% | 97%–100% (continuous) |
|
|
93
|
+
| 98–99% | 90%–97% (continuous) |
|
|
94
|
+
| 95–98% | 75%–90% (continuous) |
|
|
95
|
+
| 90–95% | 50%–75% (continuous) |
|
|
96
|
+
| <90% | 10%–50% (continuous) |
|
|
97
|
+
| Missing | 85% |
|
|
86
98
|
|
|
87
|
-
Endpoints with nonzero status are excluded.
|
|
99
|
+
Endpoints with nonzero status are excluded. Configured penalties ramp in across five percentage points below the threshold and combine with the stricter general curve.
|
|
88
100
|
|
|
89
101
|
## Credentials and security
|
|
90
102
|
|
|
@@ -102,7 +114,7 @@ Only official OpenRouter APIs are used: `/models`, `/models/{id}/endpoints`, and
|
|
|
102
114
|
## Development
|
|
103
115
|
|
|
104
116
|
```bash
|
|
105
|
-
git clone https://github.com/
|
|
117
|
+
git clone https://github.com/xuan666-lab/dsh-openrouter-provider-advisor.git
|
|
106
118
|
cd dsh-openrouter-provider-advisor
|
|
107
119
|
npm install
|
|
108
120
|
npm run verify
|
package/dsh.plugin.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-openrouter-provider-advisor",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"main": "./lib/index.js",
|
|
5
|
-
"description": "OpenRouter
|
|
5
|
+
"description": "OpenRouter 供应商推荐与安全切换:按质量、速度、缓存价格、上下文和可用率排序(兼容 DSH >= 0.1.2-rc.1)",
|
|
6
6
|
"engines": {
|
|
7
|
-
"dsh": ">=0.1.
|
|
7
|
+
"dsh": ">=0.1.2-rc.1"
|
|
8
8
|
},
|
|
9
9
|
"contributes": {
|
|
10
10
|
"tools": ["recommend_openrouter_providers", "switch_openrouter_provider"],
|
package/lib/client.js
CHANGED
|
@@ -670,13 +670,9 @@ function consume(operation) {
|
|
|
670
670
|
}
|
|
671
671
|
function ProviderSidebarTrigger(props) {
|
|
672
672
|
const i18n = useUiI18n();
|
|
673
|
-
const
|
|
674
|
-
const currentAddress = props.useSessions((state) => state.currentAddress);
|
|
673
|
+
const sessionState = (0, react.useSyncExternalStore)(props.sessions.subscribe, props.sessions.getSnapshot, props.sessions.getSnapshot);
|
|
675
674
|
const panel = (0, react.useSyncExternalStore)(props.store.subscribe, props.store.getSnapshot, props.store.getSnapshot);
|
|
676
|
-
const target = providerTriggerState(
|
|
677
|
-
current,
|
|
678
|
-
currentAddress
|
|
679
|
-
});
|
|
675
|
+
const target = providerTriggerState(sessionState);
|
|
680
676
|
return (0, react.createElement)(react.Fragment, null, (0, react.createElement)("style", null, "div:has(> div[data-slot=\"sidebar.footer.action\"] > button[data-openrouter-provider-trigger]){flex-wrap:wrap;row-gap:4px}"), (0, react.createElement)("button", {
|
|
681
677
|
type: "button",
|
|
682
678
|
disabled: target.disabled,
|
|
@@ -724,13 +720,9 @@ function freshnessLabel(updatedAt, now, i18n = createI18n("zh-CN")) {
|
|
|
724
720
|
const providerGridColumns = "minmax(110px,1fr) minmax(0,2fr) minmax(54px,auto)";
|
|
725
721
|
function ProviderOverlay(props) {
|
|
726
722
|
const i18n = useUiI18n();
|
|
727
|
-
const
|
|
728
|
-
const currentAddress = props.useSessions((state$1) => state$1.currentAddress);
|
|
723
|
+
const sessionState = (0, react.useSyncExternalStore)(props.sessions.subscribe, props.sessions.getSnapshot, props.sessions.getSnapshot);
|
|
729
724
|
const state = (0, react.useSyncExternalStore)(props.store.subscribe, props.store.getSnapshot, props.store.getSnapshot);
|
|
730
|
-
const target = providerTriggerState(
|
|
731
|
-
current,
|
|
732
|
-
currentAddress
|
|
733
|
-
});
|
|
725
|
+
const target = providerTriggerState(sessionState);
|
|
734
726
|
const dialogRef = (0, react.useRef)(null);
|
|
735
727
|
const [now, setNow] = (0, react.useState)(() => Date.now());
|
|
736
728
|
(0, react.useEffect)(() => {
|
|
@@ -1708,15 +1700,14 @@ function OpenRouterProvidersSettingsCard({ scope }) {
|
|
|
1708
1700
|
const inject = [
|
|
1709
1701
|
"sessions",
|
|
1710
1702
|
"slots",
|
|
1711
|
-
"
|
|
1712
|
-
"
|
|
1703
|
+
"locale",
|
|
1704
|
+
"settingsScope"
|
|
1713
1705
|
];
|
|
1714
1706
|
function apply(ctx) {
|
|
1715
|
-
|
|
1716
|
-
attachLocale(client.locale);
|
|
1707
|
+
attachLocale(ctx.locale);
|
|
1717
1708
|
ctx.effect(() => {
|
|
1718
|
-
const offZh =
|
|
1719
|
-
const offEn =
|
|
1709
|
+
const offZh = ctx.locale.register(LOCALE_NS, "zh", zh);
|
|
1710
|
+
const offEn = ctx.locale.register(LOCALE_NS, "en", en);
|
|
1720
1711
|
return () => {
|
|
1721
1712
|
offZh();
|
|
1722
1713
|
offEn();
|
|
@@ -1724,30 +1715,27 @@ function apply(ctx) {
|
|
|
1724
1715
|
};
|
|
1725
1716
|
}, "openrouter-providers: locale dictionaries");
|
|
1726
1717
|
const panelStore = createProviderPanelStore();
|
|
1718
|
+
const sessions = ctx.sessions.list;
|
|
1727
1719
|
const BoundTrigger = (props) => (0, react.createElement)(ProviderSidebarTrigger, {
|
|
1728
1720
|
...props,
|
|
1729
|
-
store: panelStore
|
|
1721
|
+
store: panelStore,
|
|
1722
|
+
sessions
|
|
1730
1723
|
});
|
|
1731
|
-
const BoundOverlay = (
|
|
1732
|
-
|
|
1733
|
-
|
|
1724
|
+
const BoundOverlay = () => (0, react.createElement)(ProviderOverlay, {
|
|
1725
|
+
store: panelStore,
|
|
1726
|
+
sessions
|
|
1734
1727
|
});
|
|
1735
|
-
|
|
1728
|
+
ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
|
|
1736
1729
|
name: "sidebar.footer.action",
|
|
1737
1730
|
id: "openrouter-providers-trigger",
|
|
1738
1731
|
order: 20
|
|
1739
1732
|
}, BoundTrigger));
|
|
1740
|
-
|
|
1741
|
-
name: "shell.overlay",
|
|
1742
|
-
id: "openrouter-providers-overlay",
|
|
1743
|
-
order: 20
|
|
1744
|
-
}, BoundOverlay));
|
|
1733
|
+
ctx.slots.inject("shell.overlay", () => ctx.slots.register({ name: "shell.overlay" }, BoundOverlay));
|
|
1745
1734
|
ctx.effect(() => () => panelStore.dispose(), "openrouter-providers: provider panel store");
|
|
1746
|
-
const scope =
|
|
1747
|
-
|
|
1735
|
+
const scope = ctx.settingsScope.bind({ namespace: "openrouter-providers" });
|
|
1736
|
+
ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
|
|
1748
1737
|
name: "settings.plugin.item",
|
|
1749
1738
|
key: "openrouter-providers",
|
|
1750
|
-
locale: "openrouter-providers",
|
|
1751
1739
|
inject: () => ({ scope })
|
|
1752
1740
|
}, OpenRouterProvidersSettingsCard));
|
|
1753
1741
|
}
|
package/lib/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
2
|
-
import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
3
2
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
4
3
|
import z from "@deepseek-ai/schemastery";
|
|
5
4
|
|
|
@@ -622,10 +621,6 @@ var OpenRouterClient = class {
|
|
|
622
621
|
|
|
623
622
|
//#endregion
|
|
624
623
|
//#region src/host-adapter.ts
|
|
625
|
-
function unwrap(operation, response) {
|
|
626
|
-
if (!response.result.ok) throw new Error(`${operation} failed: ${response.result.error.code}: ${response.result.error.message}`);
|
|
627
|
-
return response.result.value;
|
|
628
|
-
}
|
|
629
624
|
function createHostController(ctx, config, credentialRef$1) {
|
|
630
625
|
const clients = /* @__PURE__ */ new Map();
|
|
631
626
|
const client = (credential) => {
|
|
@@ -644,6 +639,7 @@ function createHostController(ctx, config, credentialRef$1) {
|
|
|
644
639
|
};
|
|
645
640
|
const profiles = () => ctx.settings.get("llm-pi-ai")?.providers ?? {};
|
|
646
641
|
const fallbackRef = "OPENROUTER_API_KEY";
|
|
642
|
+
const catalog = async () => ctx.sessionController.modelCatalog();
|
|
647
643
|
return new OpenRouterProviderController({
|
|
648
644
|
config,
|
|
649
645
|
credential: async (provider) => {
|
|
@@ -670,11 +666,11 @@ function createHostController(ctx, config, credentialRef$1) {
|
|
|
670
666
|
source: providerRef === void 0 ? "fallback" : "provider"
|
|
671
667
|
};
|
|
672
668
|
},
|
|
673
|
-
sessionSelection: async (
|
|
674
|
-
sessionDirectory: async (
|
|
675
|
-
const value =
|
|
669
|
+
sessionSelection: async () => (await catalog()).default,
|
|
670
|
+
sessionDirectory: async () => {
|
|
671
|
+
const value = await catalog();
|
|
676
672
|
return {
|
|
677
|
-
current: value.
|
|
673
|
+
current: value.default,
|
|
678
674
|
groups: value.groups ?? []
|
|
679
675
|
};
|
|
680
676
|
},
|
|
@@ -693,7 +689,7 @@ function createHostController(ctx, config, credentialRef$1) {
|
|
|
693
689
|
value: models
|
|
694
690
|
}]),
|
|
695
691
|
selectModel: async (selection) => {
|
|
696
|
-
|
|
692
|
+
await ctx.sessionController.selectModel(selection);
|
|
697
693
|
},
|
|
698
694
|
saveDefault: (selection) => ctx.agentDefaultModel.saveSelection(selection)
|
|
699
695
|
});
|
|
@@ -904,15 +900,28 @@ function createProviderTools(defineTool$1, controller) {
|
|
|
904
900
|
//#endregion
|
|
905
901
|
//#region src/index.ts
|
|
906
902
|
const name = "openrouter-providers";
|
|
903
|
+
/**
|
|
904
|
+
* Required host services.
|
|
905
|
+
*
|
|
906
|
+
* `sessionController` (Session Remote owner, provided by dsh-api-session-controller)
|
|
907
|
+
* replaced the pre-0.1.2 `apiProxy` RPC service that was removed from DSH: the
|
|
908
|
+
* panel now reads the shared model catalog and installs session model
|
|
909
|
+
* selections directly through the Host Session business API.
|
|
910
|
+
*/
|
|
907
911
|
const inject = [
|
|
908
912
|
"tools",
|
|
909
913
|
"settings",
|
|
910
914
|
"credentials",
|
|
911
915
|
"agentDefaultModel",
|
|
912
|
-
"
|
|
916
|
+
"sessionController",
|
|
913
917
|
"webServer"
|
|
914
918
|
];
|
|
915
|
-
|
|
919
|
+
/**
|
|
920
|
+
* Settings namespace this plugin owns. Registered through the current
|
|
921
|
+
* `ctx.settings.register` API; the older `settingsNamespace()` helper was
|
|
922
|
+
* removed in DSH 0.1.2.
|
|
923
|
+
*/
|
|
924
|
+
const NS = "openrouter-providers";
|
|
916
925
|
const weights = z.object({
|
|
917
926
|
quantization: z.number().min(0).max(1).default(.3),
|
|
918
927
|
speed: z.number().min(0).max(1).default(.3),
|
|
@@ -933,16 +942,13 @@ const Config = z.object({
|
|
|
933
942
|
uptimePenaltyFactor: z.number().min(0).max(1).default(.85)
|
|
934
943
|
});
|
|
935
944
|
function apply(ctx, initial = DEFAULT_CONFIG) {
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
validate: validateConfig
|
|
939
|
-
setSource: (current) => {
|
|
940
|
-
source = current;
|
|
941
|
-
},
|
|
942
|
-
onChange: () => {}
|
|
945
|
+
const scope = ctx.settings.register(NS, Config, {
|
|
946
|
+
base: initial,
|
|
947
|
+
validate: validateConfig
|
|
943
948
|
});
|
|
949
|
+
const readConfig = () => scope.get();
|
|
944
950
|
const host = ctx;
|
|
945
|
-
const controller = createHostController(host,
|
|
951
|
+
const controller = createHostController(host, readConfig, credentialRef);
|
|
946
952
|
ctx.effect(() => ctx.on("tools/pre-execute", providerToolApproval), "openrouter-providers: approve provider switch");
|
|
947
953
|
ctx.effect(() => host.webServer.register({
|
|
948
954
|
kind: "prefix",
|
|
@@ -1,4 +1,33 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type SessionsListStoreLike } from './provider-panel.js';
|
|
2
|
+
import { type SettingsScopeLike } from './settings-card.js';
|
|
3
|
+
import { type LocaleServiceLike } from './i18n.js';
|
|
4
|
+
/**
|
|
5
|
+
* Structural client-context subset — mirrors how current external plugins
|
|
6
|
+
* (dsh-market, dsh-web-updater) type the runtime: the removed
|
|
7
|
+
* `@deepseek-ai/dsh-client-runtime` type package no longer exists in
|
|
8
|
+
* DSH >= 0.1.2, so the plugin imports no client type package and types only
|
|
9
|
+
* the services it touches.
|
|
10
|
+
*/
|
|
11
|
+
export interface OrpClientContext {
|
|
12
|
+
effect(dispose: () => unknown, label?: string): void;
|
|
13
|
+
locale: LocaleServiceLike & {
|
|
14
|
+
register(namespace: string, locale: string, messages: Record<string, string>): () => void;
|
|
15
|
+
};
|
|
16
|
+
/** Root sessions service (list store carrying the current session selection). */
|
|
17
|
+
sessions: {
|
|
18
|
+
list: SessionsListStoreLike;
|
|
19
|
+
};
|
|
20
|
+
slots: {
|
|
21
|
+
inject(name: string, register: () => unknown): void;
|
|
22
|
+
register(options: Record<string, unknown>, component: unknown): unknown;
|
|
23
|
+
};
|
|
24
|
+
/** Settings-namespace binder provided by the settings section (dsh web >= 0.1.0-rc.7). */
|
|
25
|
+
settingsScope: {
|
|
26
|
+
bind(spec: {
|
|
27
|
+
namespace: string;
|
|
28
|
+
}): SettingsScopeLike;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
2
31
|
export declare const inject: string[];
|
|
3
|
-
export declare function apply(ctx:
|
|
32
|
+
export declare function apply(ctx: OrpClientContext): void;
|
|
4
33
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -3,12 +3,19 @@ import type { RecommendationResponse } from '../directory.js';
|
|
|
3
3
|
import type { RankedProvider } from '../types.js';
|
|
4
4
|
import type { RankingStrategy } from '../config.js';
|
|
5
5
|
import type { ProviderPanelStore } from './provider-panel-store.js';
|
|
6
|
-
interface SessionListLike {
|
|
6
|
+
export interface SessionListLike {
|
|
7
7
|
current?: string | undefined;
|
|
8
8
|
currentAddress?: unknown | undefined;
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Observable snapshot of the root sessions list store. DSH >= 0.1.2 no longer
|
|
12
|
+
* injects a `useSessions` render prop into `sidebar.footer.action` /
|
|
13
|
+
* `shell.overlay` occupants, so the components subscribe to the root sessions
|
|
14
|
+
* service directly (the same list store the useSessions hook reads).
|
|
15
|
+
*/
|
|
16
|
+
export interface SessionsListStoreLike {
|
|
17
|
+
getSnapshot(): SessionListLike;
|
|
18
|
+
subscribe(listener: () => void): () => void;
|
|
12
19
|
}
|
|
13
20
|
export interface ProviderRowView {
|
|
14
21
|
group: string;
|
|
@@ -52,17 +59,18 @@ export declare function providerTriggerState(state: SessionListLike): {
|
|
|
52
59
|
disabled: boolean;
|
|
53
60
|
};
|
|
54
61
|
export declare function providerTriggerLayout(wide: boolean): CSSProperties;
|
|
55
|
-
export declare function ProviderSidebarTrigger(props:
|
|
62
|
+
export declare function ProviderSidebarTrigger(props: {
|
|
56
63
|
wide: boolean;
|
|
57
64
|
store: ProviderPanelStore;
|
|
65
|
+
sessions: SessionsListStoreLike;
|
|
58
66
|
}): ReactNode;
|
|
59
67
|
export declare function freshnessLabel(updatedAt: number, now: number, i18n?: {
|
|
60
68
|
locale: import("./i18n.js").SupportedLocale;
|
|
61
69
|
t(key: import("./i18n.js").TranslationKey, params?: Record<string, string | number>): string;
|
|
62
70
|
}): string;
|
|
63
71
|
export declare const providerGridColumns = "minmax(110px,1fr) minmax(0,2fr) minmax(54px,auto)";
|
|
64
|
-
export declare function ProviderOverlay(props:
|
|
72
|
+
export declare function ProviderOverlay(props: {
|
|
65
73
|
store: ProviderPanelStore;
|
|
74
|
+
sessions: SessionsListStoreLike;
|
|
66
75
|
}): ReactNode;
|
|
67
|
-
export {};
|
|
68
76
|
//# sourceMappingURL=provider-panel.d.ts.map
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import type { Config } from './config.js';
|
|
2
2
|
import { OpenRouterProviderController, type SessionSelection } from './controller.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Session model catalog surfaced by `ctx.sessionController.modelCatalog()`
|
|
5
|
+
* (structural subset — the full wire type carries more fields).
|
|
6
|
+
*/
|
|
7
|
+
interface SessionModelCatalog {
|
|
8
|
+
default: SessionSelection;
|
|
9
|
+
groups?: Array<{
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
models: Array<{
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
contextWindow?: number;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}>;
|
|
18
|
+
}>;
|
|
14
19
|
}
|
|
15
20
|
export interface HostContextLike {
|
|
16
21
|
credentials: {
|
|
@@ -26,37 +31,22 @@ export interface HostContextLike {
|
|
|
26
31
|
value: unknown;
|
|
27
32
|
}>): Promise<void>;
|
|
28
33
|
};
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
name: string;
|
|
43
|
-
}>;
|
|
44
|
-
}>;
|
|
45
|
-
}>>;
|
|
46
|
-
selectModel(input: {
|
|
47
|
-
payload: {
|
|
48
|
-
sessionId: string;
|
|
49
|
-
provider: string;
|
|
50
|
-
model: string;
|
|
51
|
-
reasoningEffort?: string;
|
|
52
|
-
};
|
|
53
|
-
}): Promise<RpcResult<{
|
|
54
|
-
selected: SessionSelection;
|
|
55
|
-
}>>;
|
|
56
|
-
};
|
|
34
|
+
/**
|
|
35
|
+
* Host Session business API (dsh >= 0.1.2, provided by dsh-api-session-controller).
|
|
36
|
+
* Replaces the removed `apiProxy` RPC service: catalog reads and session model
|
|
37
|
+
* selections now go through this in-process service instead of an HTTP remote.
|
|
38
|
+
*/
|
|
39
|
+
sessionController: {
|
|
40
|
+
modelCatalog(): Promise<SessionModelCatalog>;
|
|
41
|
+
selectModel(request: {
|
|
42
|
+
sessionId: string;
|
|
43
|
+
provider: string;
|
|
44
|
+
model: string;
|
|
45
|
+
reasoningEffort?: string;
|
|
46
|
+
}): Promise<unknown>;
|
|
57
47
|
};
|
|
58
48
|
agentDefaultModel: {
|
|
59
|
-
saveSelection(selection:
|
|
49
|
+
saveSelection(selection: SessionSelection): Promise<void>;
|
|
60
50
|
};
|
|
61
51
|
}
|
|
62
52
|
export declare function createHostController(ctx: HostContextLike, config: () => Config, credentialRef: (name: string) => unknown): OpenRouterProviderController;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -2,8 +2,21 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
2
2
|
import z from '@deepseek-ai/schemastery';
|
|
3
3
|
import { type Config as ProviderConfig } from './config.js';
|
|
4
4
|
export declare const name = "openrouter-providers";
|
|
5
|
+
/**
|
|
6
|
+
* Required host services.
|
|
7
|
+
*
|
|
8
|
+
* `sessionController` (Session Remote owner, provided by dsh-api-session-controller)
|
|
9
|
+
* replaced the pre-0.1.2 `apiProxy` RPC service that was removed from DSH: the
|
|
10
|
+
* panel now reads the shared model catalog and installs session model
|
|
11
|
+
* selections directly through the Host Session business API.
|
|
12
|
+
*/
|
|
5
13
|
export declare const inject: string[];
|
|
6
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Settings namespace this plugin owns. Registered through the current
|
|
16
|
+
* `ctx.settings.register` API; the older `settingsNamespace()` helper was
|
|
17
|
+
* removed in DSH 0.1.2.
|
|
18
|
+
*/
|
|
19
|
+
export declare const NS: "openrouter-providers";
|
|
7
20
|
export declare const Config: z<ProviderConfig>;
|
|
8
21
|
export declare function apply(ctx: Context, initial?: ProviderConfig): void;
|
|
9
22
|
export { OpenRouterProviderDirectory } from './directory.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-openrouter-provider-advisor",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "DSH web plugin that ranks OpenRouter upstream providers by quality, speed, cache-heavy price, context, and uptime, then safely switches the active route.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
],
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
|
-
"url": "git+https://github.com/
|
|
15
|
+
"url": "git+https://github.com/xuan666-lab/dsh-openrouter-provider-advisor.git"
|
|
16
16
|
},
|
|
17
|
-
"homepage": "https://github.com/
|
|
17
|
+
"homepage": "https://github.com/xuan666-lab/dsh-openrouter-provider-advisor#readme",
|
|
18
18
|
"bugs": {
|
|
19
|
-
"url": "https://github.com/
|
|
19
|
+
"url": "https://github.com/xuan666-lab/dsh-openrouter-provider-advisor/issues"
|
|
20
20
|
},
|
|
21
21
|
"publishConfig": {
|
|
22
22
|
"access": "public"
|
|
@@ -44,11 +44,7 @@
|
|
|
44
44
|
"platform": "web",
|
|
45
45
|
"inject": [
|
|
46
46
|
"@deepseek-ai/dsh-client-runtime",
|
|
47
|
-
"@deepseek-ai/dsh-client-
|
|
48
|
-
"@deepseek-ai/dsh-client-ui-layout",
|
|
49
|
-
"@deepseek-ai/dsh-client-ui-sidebar",
|
|
50
|
-
"@deepseek-ai/dsh-client-ui-settings",
|
|
51
|
-
"@deepseek-ai/dsh-client-ui-settings-plugins"
|
|
47
|
+
"@deepseek-ai/dsh-client-ui-slots"
|
|
52
48
|
]
|
|
53
49
|
}
|
|
54
50
|
},
|
|
@@ -62,28 +58,14 @@
|
|
|
62
58
|
},
|
|
63
59
|
"peerDependencies": {
|
|
64
60
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
65
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
|
|
66
|
-
"@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
|
|
67
|
-
"@deepseek-ai/dsh-client-ui-layout": "^0.1.0-rc.8",
|
|
68
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.8",
|
|
69
|
-
"@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.0-rc.8",
|
|
70
|
-
"@deepseek-ai/dsh-client-ui-sidebar": "^0.1.0-rc.8",
|
|
71
61
|
"@deepseek-ai/dsh-credentials": "^0.1.0-rc.8",
|
|
72
|
-
"@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
|
|
73
62
|
"@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
|
|
74
63
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
75
64
|
"react": ">=18.2.0 <20"
|
|
76
65
|
},
|
|
77
66
|
"devDependencies": {
|
|
78
67
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
79
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
|
|
80
|
-
"@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
|
|
81
|
-
"@deepseek-ai/dsh-client-ui-layout": "^0.1.0-rc.8",
|
|
82
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.8",
|
|
83
|
-
"@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.0-rc.8",
|
|
84
|
-
"@deepseek-ai/dsh-client-ui-sidebar": "^0.1.0-rc.8",
|
|
85
68
|
"@deepseek-ai/dsh-credentials": "^0.1.0-rc.8",
|
|
86
|
-
"@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
|
|
87
69
|
"@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
|
|
88
70
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
89
71
|
"@types/node": "^22.20.0",
|