@mofeng2223/dsh-claude-provider 0.1.0 → 0.2.1
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/README.md +23 -11
- package/README.zh-CN.md +56 -0
- package/cordis.patch.yml +0 -4
- package/lib/client.js +55 -46
- package/package.json +14 -1
- package/src/index.js +65 -24
package/README.md
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
# DSH Claude Provider
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
English | [简体中文](./README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
`@mofeng2223/dsh-claude-provider` adds a visible, reusable Claude provider type
|
|
6
|
+
to DeepSeek Harness and fixes its routes when an Anthropic Messages adapter still
|
|
5
7
|
serialize reasoning as `thinking.type: enabled` with `budget_tokens`. For the
|
|
6
|
-
|
|
8
|
+
explicitly registered provider/model pairs, it converts the outgoing request to
|
|
7
9
|
`thinking.type: adaptive` plus `output_config.effort`.
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
Use **Add Claude provider** to create any number of Provider IDs under this one
|
|
12
|
+
type. Membership is stored explicitly in the plugin's own settings namespace.
|
|
13
|
+
The plugin never infers membership from the `anthropic-messages` protocol, a
|
|
14
|
+
`claude-*` model ID, or a default reasoning value. Generic custom providers and
|
|
15
|
+
built-in providers are therefore untouched even when they use the same wire
|
|
16
|
+
protocol.
|
|
17
|
+
|
|
18
|
+
Only explicitly typed providers receive the Claude badge, native model
|
|
19
|
+
discovery, recorded capacity defaults, per-model thinking modes, and adaptive
|
|
20
|
+
request conversion. At request time the plugin reads the selected model's
|
|
21
|
+
configured effort list: four- and five-level profiles use adaptive thinking,
|
|
22
|
+
while an On/Off profile keeps the legacy budget-based path. Newly entered
|
|
23
|
+
models default to five levels, so future model IDs can be configured without a
|
|
24
|
+
plugin update. Claude providers select High by default; toggle-only models are
|
|
25
|
+
shown as On/Off and default to On, without an extra provider-default choice.
|
|
14
26
|
|
|
15
27
|
The same package supplies both the Host request adapter and the Web settings
|
|
16
28
|
client. The client marks its model-directory request with a private discovery
|
|
@@ -57,7 +69,7 @@ npx @deepseek-ai/dsh plugin --profile headless remove @mofeng2223/dsh-claude-pro
|
|
|
57
69
|
```
|
|
58
70
|
|
|
59
71
|
Uninstall intentionally leaves `~/.dsh/settings.yaml` and stored credentials
|
|
60
|
-
alone. Existing
|
|
72
|
+
alone. Existing Claude-type providers therefore remain visible as ordinary custom
|
|
61
73
|
`anthropic-messages` routes, but lose this plugin's Claude badge, per-model
|
|
62
74
|
thinking-mode controls, native Anthropic model discovery, and adaptive request
|
|
63
75
|
rewrite.
|
|
@@ -65,8 +77,8 @@ rewrite.
|
|
|
65
77
|
The plugin never stores or logs API keys. Model discovery uses the one-shot key
|
|
66
78
|
typed into the form or resolves the existing route's credential through DSH;
|
|
67
79
|
the value is sent only to that route's Anthropic Models API. Its request
|
|
68
|
-
transformer runs only inside
|
|
69
|
-
|
|
80
|
+
transformer runs only inside `llm/stream` calls for Provider IDs explicitly
|
|
81
|
+
registered under this plugin's type and leaves every other provider untouched.
|
|
70
82
|
|
|
71
83
|
The generated `.tgz` package is portable. Copy it to Windows and install it by
|
|
72
84
|
its local path; no source build or platform-specific dependency is required.
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# DSH Claude Provider
|
|
2
|
+
|
|
3
|
+
[English](./README.md) | 简体中文
|
|
4
|
+
|
|
5
|
+
`@mofeng2223/dsh-claude-provider` 为 DeepSeek Harness 增加一个可见且可复用的“Claude 提供方”类型,并修正其 Anthropic Messages 适配器仍将思考参数序列化为 `thinking.type: enabled` 与 `budget_tokens` 的问题。对于明确登记在此类型下的提供方和模型组合,插件会将请求转换为 `thinking.type: adaptive` 与 `output_config.effort`。
|
|
6
|
+
|
|
7
|
+
通过“添加 Claude 提供方”可以在同一个类型下创建任意多个 Provider ID。类型归属会明确保存在插件自己的设置命名空间中。插件不会根据 `anthropic-messages` 协议、`claude-*` 模型 ID 或默认思考强度推断类型。因此,即使通用自定义提供方或内置提供方使用相同协议,插件也不会修改它们。
|
|
8
|
+
|
|
9
|
+
只有明确登记为此类型的提供方才会获得 Claude 标识、原生模型发现、已记录容量预填、按模型设置思考模式以及 adaptive 请求转换。发送请求时,插件会读取所选模型配置的思考档位:四档和五档配置使用 adaptive thinking;开启/关闭配置继续使用旧版基于预算的思考方式。手动新增的模型默认使用五档,因此未来新增的模型 ID 也可以直接配置。Claude 提供方默认选择 High;仅支持开关的模型显示为 On/Off,并默认选择 On,不再额外显示 Default。
|
|
10
|
+
|
|
11
|
+
同一个 npm 包同时提供 Host 请求适配器和 Web 设置客户端。客户端会使用内部发现协议标记“获取模型目录”请求,再由 Host 部分处理。只有这个请求会调用 Anthropic 原生的 `GET /v1/models` 接口,并使用 `x-api-key` 和 `anthropic-version` 请求头。
|
|
12
|
+
|
|
13
|
+
插件支持 Anthropic 游标分页,并会在提供方返回相关字段时读取 `display_name`、`max_input_tokens` 和 `max_tokens`。插件不会尝试使用 OpenAI 兼容的模型列表接口,也不会回退到 Bearer 鉴权。
|
|
14
|
+
|
|
15
|
+
## 安装
|
|
16
|
+
|
|
17
|
+
直接安装 npm 上发布的插件:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
npx @deepseek-ai/dsh plugin --profile web add @mofeng2223/dsh-claude-provider
|
|
21
|
+
npx @deepseek-ai/dsh plugin --profile headless add @mofeng2223/dsh-claude-provider
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
安装完成后,重启对应的 Harness 进程。
|
|
25
|
+
|
|
26
|
+
修改插件源码后,可以重新构建 Web 客户端并生成单一分发包:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
npm run build
|
|
30
|
+
npm pack --pack-destination dist
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
日常安装不建议直接使用源码目录。直接安装源码目录会创建 pnpm `link:` 依赖;在目前的 DSH/pnpm 版本中,移除依赖和 bundle 注册后,顶层符号链接仍有可能残留。通过 npm 或 `.tgz` 归档安装时,文件由包管理器管理,卸载后不会留下这类链接。
|
|
34
|
+
|
|
35
|
+
## 调试
|
|
36
|
+
|
|
37
|
+
临时设置 `DSH_CLAUDE_PROVIDER_DEBUG=1`,可以为每个被改写的请求输出一行经过脱敏的诊断信息。诊断内容只包含提供方、模型和所选思考强度,不包含 API 密钥。
|
|
38
|
+
|
|
39
|
+
## 卸载
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
npx @deepseek-ai/dsh plugin --profile web remove @mofeng2223/dsh-claude-provider
|
|
43
|
+
npx @deepseek-ai/dsh plugin --profile headless remove @mofeng2223/dsh-claude-provider
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
卸载插件时会保留 `~/.dsh/settings.yaml` 和已经保存的凭据。现有的 Claude 类型提供方仍会作为普通的 `anthropic-messages` 自定义路由显示,但会失去本插件提供的 Claude 标识、按模型配置思考模式、Anthropic 原生模型发现以及 adaptive 请求改写功能。
|
|
47
|
+
|
|
48
|
+
## 安全与隐私
|
|
49
|
+
|
|
50
|
+
插件不会存储或记录 API 密钥。获取模型列表时,插件会使用表单中临时输入的密钥,或者通过 DSH 解析现有路由保存的凭据。密钥只会发送到该路由对应的 Anthropic Models API。
|
|
51
|
+
|
|
52
|
+
请求转换器只会在明确登记到本插件类型的 Provider ID 所对应的 `llm/stream` 调用中运行,不会修改任何其他提供方。
|
|
53
|
+
|
|
54
|
+
## Windows 支持
|
|
55
|
+
|
|
56
|
+
发布到 npm 的插件和生成的 `.tgz` 包均可跨平台使用。Windows 可以直接通过 npm 包名或本地 `.tgz` 路径安装,无需在目标电脑上重新构建,也不依赖平台专用的原生模块。
|
package/cordis.patch.yml
CHANGED
package/lib/client.js
CHANGED
|
@@ -542,7 +542,7 @@ window.__ModuleLoader__.load({
|
|
|
542
542
|
const [providersResponse, settingsResponse] = await Promise.all([this.api.llm.providers({}), this.api.settings.describe({})]);
|
|
543
543
|
if (!providersResponse.result.ok) throw new Error(providersResponse.result.error.message);
|
|
544
544
|
if (!settingsResponse.result.ok) throw new Error(settingsResponse.result.error.message);
|
|
545
|
-
providers = providersResponse.result.value.providers;
|
|
545
|
+
providers = providersResponse.result.value.providers.filter((entry) => entry.provider !== CLAUDE_PROVIDER_DIRECTORY_SENTINEL);
|
|
546
546
|
writable = settingsResponse.result.value.writable;
|
|
547
547
|
views = settingsResponse.result.value.namespaces;
|
|
548
548
|
} catch (error) {
|
|
@@ -1088,9 +1088,9 @@ window.__ModuleLoader__.load({
|
|
|
1088
1088
|
const [route, setRoute] = (0, react.useState)("");
|
|
1089
1089
|
const [displayName, setDisplayName] = (0, react.useState)("");
|
|
1090
1090
|
const [baseURL, setBaseURL] = (0, react.useState)("");
|
|
1091
|
-
const
|
|
1092
|
-
const isAdaptiveClaude = providerType ===
|
|
1093
|
-
const [protocol, setProtocol] = (0, react.useState)(protocols[0] ?? "");
|
|
1091
|
+
const providerType = props.providerType ?? "generic";
|
|
1092
|
+
const isAdaptiveClaude = providerType === CLAUDE_PROVIDER_TYPE;
|
|
1093
|
+
const [protocol, setProtocol] = (0, react.useState)(isAdaptiveClaude ? "anthropic-messages" : protocols[0] ?? "");
|
|
1094
1094
|
const [keyDraft, setKeyDraft] = (0, react.useState)("");
|
|
1095
1095
|
const [models, setModels] = (0, react.useState)([]);
|
|
1096
1096
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
@@ -1136,6 +1136,13 @@ window.__ModuleLoader__.load({
|
|
|
1136
1136
|
if (!response.result.ok) return response.result.error.message;
|
|
1137
1137
|
setCommitted(true);
|
|
1138
1138
|
}
|
|
1139
|
+
if (isAdaptiveClaude) {
|
|
1140
|
+
const typed = await api.settings.mutate({
|
|
1141
|
+
ns: CLAUDE_PROVIDER_SETTINGS_NS,
|
|
1142
|
+
ops: [{ op: "set", path: ["providerTypes", route], value: CLAUDE_PROVIDER_TYPE }]
|
|
1143
|
+
});
|
|
1144
|
+
if (!typed.result.ok) return typed.result.error.message;
|
|
1145
|
+
}
|
|
1139
1146
|
if (storesKey) {
|
|
1140
1147
|
const stored = await api.credentials.set({
|
|
1141
1148
|
ref: keyRef,
|
|
@@ -1167,30 +1174,9 @@ window.__ModuleLoader__.load({
|
|
|
1167
1174
|
className: ModelsSection_module_css_default["editorHeader"],
|
|
1168
1175
|
children: (0, react_jsx_runtime.jsx)("span", {
|
|
1169
1176
|
className: ModelsSection_module_css_default["editorTitle"],
|
|
1170
|
-
children: t("customTitle")
|
|
1177
|
+
children: t(isAdaptiveClaude ? "claudeCustomTitle" : "customTitle")
|
|
1171
1178
|
})
|
|
1172
1179
|
}),
|
|
1173
|
-
(0, react_jsx_runtime.jsxs)("div", {
|
|
1174
|
-
className: ModelsSection_module_css_default["field"],
|
|
1175
|
-
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
1176
|
-
className: ModelsSection_module_css_default["fieldLabel"],
|
|
1177
|
-
children: t("customProviderType")
|
|
1178
|
-
}), (0, react_jsx_runtime.jsxs)("select", {
|
|
1179
|
-
className: `${ModelsSection_module_css_default["input"]} ${ModelsSection_module_css_default["selectInput"]}`,
|
|
1180
|
-
value: providerType,
|
|
1181
|
-
"aria-label": t("customProviderType"),
|
|
1182
|
-
disabled: profileDisabled,
|
|
1183
|
-
onChange: (event) => {
|
|
1184
|
-
const nextType = event.target.value;
|
|
1185
|
-
setProviderType(nextType);
|
|
1186
|
-
if (nextType === "claude-adaptive") {
|
|
1187
|
-
setProtocol("anthropic-messages");
|
|
1188
|
-
setModels((current) => current.map(withDefaultClaudeReasoning));
|
|
1189
|
-
}
|
|
1190
|
-
},
|
|
1191
|
-
children: [(0, react_jsx_runtime.jsx)("option", { value: "generic", children: t("customProviderTypeGeneric") }), (0, react_jsx_runtime.jsx)("option", { value: "claude-adaptive", children: t("customProviderTypeClaude") })]
|
|
1192
|
-
})]
|
|
1193
|
-
}),
|
|
1194
1180
|
isAdaptiveClaude ? (0, react_jsx_runtime.jsxs)("p", {
|
|
1195
1181
|
className: ModelsSection_module_css_default["savedNotice"],
|
|
1196
1182
|
role: "status",
|
|
@@ -1662,7 +1648,7 @@ window.__ModuleLoader__.load({
|
|
|
1662
1648
|
...catalogProps,
|
|
1663
1649
|
probe,
|
|
1664
1650
|
probeBlocked: keyFailure,
|
|
1665
|
-
thinkingPresets:
|
|
1651
|
+
thinkingPresets: props.thinkingPresets === true,
|
|
1666
1652
|
api
|
|
1667
1653
|
})
|
|
1668
1654
|
]
|
|
@@ -1761,6 +1747,11 @@ window.__ModuleLoader__.load({
|
|
|
1761
1747
|
}]
|
|
1762
1748
|
});
|
|
1763
1749
|
if (!response.result.ok) return response.result.error.message;
|
|
1750
|
+
const typeResponse = await api.settings.mutate({
|
|
1751
|
+
ns: CLAUDE_PROVIDER_SETTINGS_NS,
|
|
1752
|
+
ops: [{ op: "unset", path: ["providerTypes", target.provider] }]
|
|
1753
|
+
});
|
|
1754
|
+
if (!typeResponse.result.ok) return typeResponse.result.error.message;
|
|
1764
1755
|
} catch (error) {
|
|
1765
1756
|
return messageOf$1(error);
|
|
1766
1757
|
}
|
|
@@ -1793,6 +1784,9 @@ window.__ModuleLoader__.load({
|
|
|
1793
1784
|
};
|
|
1794
1785
|
}
|
|
1795
1786
|
const CLAUDE_DISCOVERY_API = "mofeng-anthropic-models";
|
|
1787
|
+
const CLAUDE_PROVIDER_SETTINGS_NS = "dsh-claude-provider";
|
|
1788
|
+
const CLAUDE_PROVIDER_TYPE = "claude-adaptive";
|
|
1789
|
+
const CLAUDE_PROVIDER_DIRECTORY_SENTINEL = "dsh-claude-provider-type";
|
|
1796
1790
|
const CLAUDE_FIVE_EFFORTS = Object.freeze({ low: "low", medium: "medium", high: "high", xhigh: "xhigh", max: "max" });
|
|
1797
1791
|
const CLAUDE_FOUR_EFFORTS = Object.freeze({ low: "low", medium: "medium", high: "high", max: "max" });
|
|
1798
1792
|
const CLAUDE_TOGGLE_EFFORTS = Object.freeze({ off: null, high: "high" });
|
|
@@ -1881,11 +1875,8 @@ window.__ModuleLoader__.load({
|
|
|
1881
1875
|
});
|
|
1882
1876
|
}
|
|
1883
1877
|
function isClaudeProvider(row, state) {
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
if (namespace === void 0) return false;
|
|
1887
|
-
const profile = (0, _deepseek_ai_dsh_client_schema_form.getPath)(namespace.value, row.entry.settingsPath);
|
|
1888
|
-
return profile !== null && typeof profile === "object" && profile.api === "anthropic-messages" && Array.isArray(profile.models) && profile.models.length > 0;
|
|
1878
|
+
const namespace = state.namespaces.get(CLAUDE_PROVIDER_SETTINGS_NS);
|
|
1879
|
+
return namespace?.value?.providerTypes?.[row.entry.provider] === CLAUDE_PROVIDER_TYPE;
|
|
1889
1880
|
}
|
|
1890
1881
|
/** Stable visible and accessible identity for one provider target. */
|
|
1891
1882
|
function providerTargetLabel(target) {
|
|
@@ -1919,7 +1910,7 @@ window.__ModuleLoader__.load({
|
|
|
1919
1910
|
const [deleting, setDeleting] = (0, react.useState)(false);
|
|
1920
1911
|
const [deleteFailure, setDeleteFailure] = (0, react.useState)(void 0);
|
|
1921
1912
|
const [savedTarget, setSavedTarget] = (0, react.useState)(void 0);
|
|
1922
|
-
const [
|
|
1913
|
+
const [declaringType, setDeclaringType] = (0, react.useState)(void 0);
|
|
1923
1914
|
const [dismissedSetup, setDismissedSetup] = (0, react.useState)(() => /* @__PURE__ */ new Set());
|
|
1924
1915
|
const announceSaved = (target) => {
|
|
1925
1916
|
controller.load().then(() => {
|
|
@@ -1929,7 +1920,7 @@ window.__ModuleLoader__.load({
|
|
|
1929
1920
|
const closeEditor = (changed, target) => {
|
|
1930
1921
|
setEditing(void 0);
|
|
1931
1922
|
setAdding(false);
|
|
1932
|
-
|
|
1923
|
+
setDeclaringType(void 0);
|
|
1933
1924
|
if (changed) announceSaved(target);
|
|
1934
1925
|
};
|
|
1935
1926
|
/**
|
|
@@ -2026,6 +2017,7 @@ window.__ModuleLoader__.load({
|
|
|
2026
2017
|
children: renderProviderEditor({
|
|
2027
2018
|
target,
|
|
2028
2019
|
namespace,
|
|
2020
|
+
thinkingPresets: isClaudeProvider(row, state),
|
|
2029
2021
|
api,
|
|
2030
2022
|
t,
|
|
2031
2023
|
readOnly: !state.writable,
|
|
@@ -2055,7 +2047,7 @@ window.__ModuleLoader__.load({
|
|
|
2055
2047
|
}) : null,
|
|
2056
2048
|
adaptiveClaude ? (0, react_jsx_runtime.jsx)("span", {
|
|
2057
2049
|
className: ModelsSection_module_css_default["rowTag"],
|
|
2058
|
-
children: t("
|
|
2050
|
+
children: t("claudeTypeTag")
|
|
2059
2051
|
}) : null,
|
|
2060
2052
|
credentialConfigured ? (0, react_jsx_runtime.jsx)("span", {
|
|
2061
2053
|
className: `${ModelsSection_module_css_default["credentialDot"]} ${ModelsSection_module_css_default["credentialDotConfigured"]}`,
|
|
@@ -2077,7 +2069,7 @@ window.__ModuleLoader__.load({
|
|
|
2077
2069
|
"aria-label": providerCopy(t("editProvider"), target),
|
|
2078
2070
|
onClick: () => {
|
|
2079
2071
|
setSavedTarget(void 0);
|
|
2080
|
-
|
|
2072
|
+
setDeclaringType(void 0);
|
|
2081
2073
|
setAdding(false);
|
|
2082
2074
|
setEditing(open ? void 0 : target);
|
|
2083
2075
|
},
|
|
@@ -2098,6 +2090,7 @@ window.__ModuleLoader__.load({
|
|
|
2098
2090
|
}), open ? renderProviderEditor({
|
|
2099
2091
|
target,
|
|
2100
2092
|
namespace,
|
|
2093
|
+
thinkingPresets: isClaudeProvider(row, state),
|
|
2101
2094
|
api,
|
|
2102
2095
|
t,
|
|
2103
2096
|
readOnly: !state.writable,
|
|
@@ -2145,18 +2138,19 @@ window.__ModuleLoader__.load({
|
|
|
2145
2138
|
closeEditor(changed, addTarget);
|
|
2146
2139
|
}
|
|
2147
2140
|
}, addTarget.provider)]
|
|
2148
|
-
}) :
|
|
2141
|
+
}) : declaringType !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
|
|
2149
2142
|
className: ModelsSection_module_css_default["addCard"],
|
|
2150
2143
|
children: (0, react_jsx_runtime.jsx)(CustomProviderCard, {
|
|
2151
2144
|
taken: state.rows.map((row) => row.entry.provider),
|
|
2152
2145
|
protocols,
|
|
2146
|
+
providerType: declaringType,
|
|
2153
2147
|
/* v8 ignore next -- the card only opens from a button disabled without this namespace */
|
|
2154
2148
|
revision: state.namespaces.get("llm-pi-ai")?.revision ?? 0,
|
|
2155
2149
|
api,
|
|
2156
2150
|
t,
|
|
2157
2151
|
readOnly: !state.writable,
|
|
2158
2152
|
onClose: (changed) => {
|
|
2159
|
-
|
|
2153
|
+
setDeclaringType(void 0);
|
|
2160
2154
|
if (changed) controller.load();
|
|
2161
2155
|
}
|
|
2162
2156
|
})
|
|
@@ -2171,7 +2165,7 @@ window.__ModuleLoader__.load({
|
|
|
2171
2165
|
/* v8 ignore next -- the button is disabled while nothing is addable */
|
|
2172
2166
|
if (first === void 0) return;
|
|
2173
2167
|
setSavedTarget(void 0);
|
|
2174
|
-
|
|
2168
|
+
setDeclaringType(void 0);
|
|
2175
2169
|
setAdding(true);
|
|
2176
2170
|
setEditing(targetOf(first));
|
|
2177
2171
|
},
|
|
@@ -2184,9 +2178,20 @@ window.__ModuleLoader__.load({
|
|
|
2184
2178
|
setSavedTarget(void 0);
|
|
2185
2179
|
setAdding(false);
|
|
2186
2180
|
setEditing(void 0);
|
|
2187
|
-
|
|
2181
|
+
setDeclaringType("generic");
|
|
2188
2182
|
},
|
|
2189
2183
|
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), t("customAdd")]
|
|
2184
|
+
}), (0, react_jsx_runtime.jsxs)("button", {
|
|
2185
|
+
type: "button",
|
|
2186
|
+
className: ModelsSection_module_css_default["addButton"],
|
|
2187
|
+
disabled: !state.namespaces.has(CLAUDE_PROVIDER_SETTINGS_NS) || !state.writable,
|
|
2188
|
+
onClick: () => {
|
|
2189
|
+
setSavedTarget(void 0);
|
|
2190
|
+
setAdding(false);
|
|
2191
|
+
setEditing(void 0);
|
|
2192
|
+
setDeclaringType(CLAUDE_PROVIDER_TYPE);
|
|
2193
|
+
},
|
|
2194
|
+
children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), t("claudeCustomAdd")]
|
|
2190
2195
|
})]
|
|
2191
2196
|
})
|
|
2192
2197
|
}),
|
|
@@ -2662,9 +2667,11 @@ window.__ModuleLoader__.load({
|
|
|
2662
2667
|
customAdd: "Add a custom provider",
|
|
2663
2668
|
customTitle: "Custom provider",
|
|
2664
2669
|
customTag: "Custom",
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2670
|
+
claudeCustomAdd: "Add Claude provider",
|
|
2671
|
+
claudeCustomTitle: "Claude provider",
|
|
2672
|
+
claudeTypeUnavailable: "Claude provider type storage is unavailable.",
|
|
2673
|
+
claudeTypeTag: "Claude provider",
|
|
2674
|
+
claudeTypeIntro: "Only this provider type enables Claude model discovery, recorded capacities, thinking modes, and adaptive request conversion.",
|
|
2668
2675
|
adaptiveClaudeEnabled: "Choose a thinking mode for each model. New models default to five levels.",
|
|
2669
2676
|
adaptiveClaudeHelpLabel: "Thinking mode help",
|
|
2670
2677
|
adaptiveClaudeHelp: "Five (low / medium / high / xhigh / max): Fable 5, Opus 5, Opus 4.8, Opus 4.7, Sonnet 5\nFour (low / medium / high / max): Opus 4.6, Sonnet 4.6\nOn / Off: Haiku 4.5",
|
|
@@ -2769,9 +2776,11 @@ window.__ModuleLoader__.load({
|
|
|
2769
2776
|
customAdd: "添加自定义提供方",
|
|
2770
2777
|
customTitle: "自定义提供方",
|
|
2771
2778
|
customTag: "自定义",
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2779
|
+
claudeCustomAdd: "添加 Claude 提供方",
|
|
2780
|
+
claudeCustomTitle: "Claude 提供方",
|
|
2781
|
+
claudeTypeUnavailable: "Claude 提供方类型存储不可用。",
|
|
2782
|
+
claudeTypeTag: "Claude 提供方",
|
|
2783
|
+
claudeTypeIntro: "只有此提供方类型会启用 Claude 模型发现、已记录容量、思考模式和 adaptive 请求转换。",
|
|
2775
2784
|
adaptiveClaudeEnabled: "每个模型可单独选择思考模式;新增模型默认使用五档。",
|
|
2776
2785
|
adaptiveClaudeHelpLabel: "思考模式说明",
|
|
2777
2786
|
adaptiveClaudeHelp: "五档(低 / 中 / 高 / 超高 / 最大):Fable 5、Opus 5、Opus 4.8、Opus 4.7、Sonnet 5\n四档(低 / 中 / 高 / 最大):Opus 4.6、Sonnet 4.6\n开启 / 关闭:Haiku 4.5",
|
package/package.json
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mofeng2223/dsh-claude-provider",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Custom Claude provider support for DeepSeek Harness",
|
|
5
5
|
"author": "mofeng2223",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/MoFeng2223/dsh-claude-provider.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/MoFeng2223/dsh-claude-provider#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/MoFeng2223/dsh-claude-provider/issues"
|
|
14
|
+
},
|
|
7
15
|
"keywords": [
|
|
8
16
|
"deepseek-harness",
|
|
9
17
|
"dsh-plugin",
|
|
@@ -15,6 +23,10 @@
|
|
|
15
23
|
"registry": "https://registry.npmjs.org/"
|
|
16
24
|
},
|
|
17
25
|
"type": "module",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@deepseek-ai/dsh-settings": "0.1.0-rc.6",
|
|
28
|
+
"@deepseek-ai/schemastery": "3.18.1"
|
|
29
|
+
},
|
|
18
30
|
"exports": {
|
|
19
31
|
".": "./src/index.js",
|
|
20
32
|
"./client": "./lib/client.js",
|
|
@@ -25,6 +37,7 @@
|
|
|
25
37
|
"lib/client.js",
|
|
26
38
|
"cordis.patch.yml",
|
|
27
39
|
"README.md",
|
|
40
|
+
"README.zh-CN.md",
|
|
28
41
|
"LICENSE"
|
|
29
42
|
],
|
|
30
43
|
"scripts": {
|
package/src/index.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
2
|
+
import z from '@deepseek-ai/schemastery'
|
|
3
|
+
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
|
|
2
4
|
|
|
3
5
|
export const name = 'claude-provider'
|
|
4
|
-
export const inject = ['llm']
|
|
6
|
+
export const inject = ['llm', 'settings']
|
|
7
|
+
|
|
8
|
+
export const CLAUDE_PROVIDER_TYPE = 'claude-adaptive'
|
|
9
|
+
export const CLAUDE_PROVIDER_SETTINGS_NS = settingsNamespace('dsh-claude-provider')
|
|
10
|
+
export const CLAUDE_PROVIDER_DIRECTORY_SENTINEL = 'dsh-claude-provider-type'
|
|
11
|
+
|
|
12
|
+
const ProviderTypeSettings = z.object({
|
|
13
|
+
providerTypes: z.dict(z.const(CLAUDE_PROVIDER_TYPE)).default({}),
|
|
14
|
+
})
|
|
5
15
|
|
|
6
16
|
const DEFAULT_EFFORT_MAP = Object.freeze({
|
|
7
17
|
minimal: 'low',
|
|
@@ -235,36 +245,55 @@ function installClaudeModelDiscovery(ctx) {
|
|
|
235
245
|
}
|
|
236
246
|
|
|
237
247
|
function resolveConfig(config = {}) {
|
|
238
|
-
if (!Array.isArray(config.routes) || config.routes.length === 0) {
|
|
239
|
-
throw new Error('claude-provider: routes must contain at least one provider')
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const targets = new Map()
|
|
243
|
-
for (const [routeIndex, route] of config.routes.entries()) {
|
|
244
|
-
const provider = requireNonEmptyString(route?.provider, `routes[${routeIndex}].provider`)
|
|
245
|
-
if (!Array.isArray(route.models) || route.models.length === 0) {
|
|
246
|
-
throw new Error(`claude-provider: route "${provider}" must contain at least one model`)
|
|
247
|
-
}
|
|
248
|
-
if (targets.has(provider)) {
|
|
249
|
-
throw new Error(`claude-provider: duplicate provider route "${provider}"`)
|
|
250
|
-
}
|
|
251
|
-
targets.set(provider, new Set(route.models.map((model, modelIndex) =>
|
|
252
|
-
requireNonEmptyString(model, `routes[${routeIndex}].models[${modelIndex}]`))))
|
|
253
|
-
}
|
|
254
|
-
|
|
255
248
|
const effortMap = { ...DEFAULT_EFFORT_MAP, ...config.effortMap }
|
|
256
249
|
for (const [level, effort] of Object.entries(effortMap)) {
|
|
257
250
|
requireNonEmptyString(level, 'effortMap key')
|
|
258
251
|
requireNonEmptyString(effort, `effortMap.${level}`)
|
|
259
252
|
}
|
|
260
253
|
|
|
261
|
-
return {
|
|
254
|
+
return { effortMap, debug: config.debug === true }
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** Whether an explicitly registered provider belongs to this plugin's Claude type. */
|
|
258
|
+
export function isClaudeProviderType(settings, provider) {
|
|
259
|
+
if (typeof provider !== 'string' || provider.length === 0) return false
|
|
260
|
+
return settings?.providerTypes?.[provider] === CLAUDE_PROVIDER_TYPE
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** Give typed Claude routes a concrete default and readable labels for toggle-only models. */
|
|
264
|
+
export function withClaudeProviderReasoningDefaults(modelInfo) {
|
|
265
|
+
const reasoning = modelInfo?.reasoning
|
|
266
|
+
if (reasoning === undefined || !Array.isArray(reasoning.efforts)) return modelInfo
|
|
267
|
+
const ids = new Set(reasoning.efforts.map(effort => String(effort.id)))
|
|
268
|
+
const toggleOnly = ids.size === 2 && ids.has('off') && ids.has('high')
|
|
269
|
+
return {
|
|
270
|
+
...modelInfo,
|
|
271
|
+
reasoning: {
|
|
272
|
+
...reasoning,
|
|
273
|
+
defaultEffort: 'high',
|
|
274
|
+
efforts: toggleOnly
|
|
275
|
+
? reasoning.efforts.map(effort => ({
|
|
276
|
+
...effort,
|
|
277
|
+
name: effort.id === 'off' ? 'Off' : effort.id === 'high' ? 'On' : effort.name,
|
|
278
|
+
}))
|
|
279
|
+
: reasoning.efforts,
|
|
280
|
+
},
|
|
281
|
+
}
|
|
262
282
|
}
|
|
263
283
|
|
|
264
|
-
function
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
284
|
+
function installClaudeModelInfoDefaults(ctx, providerTypes) {
|
|
285
|
+
const llm = ctx.llm
|
|
286
|
+
const upstream = llm.resolveModelInfo
|
|
287
|
+
const wrapped = async function (provider, model, signal) {
|
|
288
|
+
const info = await upstream.call(this, provider, model, signal)
|
|
289
|
+
return isClaudeProviderType(providerTypes.get(), provider)
|
|
290
|
+
? withClaudeProviderReasoningDefaults(info)
|
|
291
|
+
: info
|
|
292
|
+
}
|
|
293
|
+
llm.resolveModelInfo = wrapped
|
|
294
|
+
ctx.effect(() => () => {
|
|
295
|
+
if (llm.resolveModelInfo === wrapped) llm.resolveModelInfo = upstream
|
|
296
|
+
})
|
|
268
297
|
}
|
|
269
298
|
|
|
270
299
|
/** Four- and five-level model profiles are adaptive; the two-level toggle is legacy budget thinking. */
|
|
@@ -418,6 +447,18 @@ async function* modelAwareStream(ctx, storage, resolved, options, next) {
|
|
|
418
447
|
|
|
419
448
|
export function apply(ctx, config) {
|
|
420
449
|
const resolved = resolveConfig(config)
|
|
450
|
+
const providerTypes = ctx.settings.register(
|
|
451
|
+
CLAUDE_PROVIDER_SETTINGS_NS,
|
|
452
|
+
ProviderTypeSettings,
|
|
453
|
+
{ base: { providerTypes: {} } },
|
|
454
|
+
)
|
|
455
|
+
ctx.llm.registerConfigurableProviders([{
|
|
456
|
+
provider: CLAUDE_PROVIDER_DIRECTORY_SENTINEL,
|
|
457
|
+
displayName: 'Claude Provider Type',
|
|
458
|
+
settingsNs: CLAUDE_PROVIDER_SETTINGS_NS,
|
|
459
|
+
settingsPath: ['providerTypes'],
|
|
460
|
+
}])
|
|
461
|
+
installClaudeModelInfoDefaults(ctx, providerTypes)
|
|
421
462
|
installClaudeModelDiscovery(ctx)
|
|
422
463
|
const storage = new AsyncLocalStorage()
|
|
423
464
|
const upstreamFetch = globalThis.fetch
|
|
@@ -447,7 +488,7 @@ export function apply(ctx, config) {
|
|
|
447
488
|
})
|
|
448
489
|
|
|
449
490
|
ctx.on('llm/stream', (options, next) => {
|
|
450
|
-
if (!
|
|
491
|
+
if (!isClaudeProviderType(providerTypes.get(), options.provider)) return next()
|
|
451
492
|
return modelAwareStream(ctx, storage, resolved, options, next)
|
|
452
493
|
})
|
|
453
494
|
}
|