@mofeng2223/dsh-claude-provider 0.1.0 → 0.2.3
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 +79 -46
- package/README.zh-CN.md +105 -0
- package/cordis.patch.yml +0 -4
- package/lib/client.js +109 -100
- package/package.json +15 -2
- package/src/index.js +121 -24
package/README.md
CHANGED
|
@@ -1,72 +1,105 @@
|
|
|
1
1
|
# DSH Claude Provider
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
3
|
+
English | [简体中文](./README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
DeepSeek Harness's generic reasoning controls do not fully match the request parameters expected by newer Claude models. A selected level can therefore fail with HTTP 400, or be silently mapped to another effective level, such as Max behaving as High. `@mofeng2223/dsh-claude-provider` adds an explicit Claude provider type and sends the reasoning parameters that each configured Claude model expects.
|
|
6
|
+
|
|
7
|
+
## What this plugin does
|
|
8
|
+
|
|
9
|
+
1. **Adds a dedicated Claude provider type**
|
|
10
|
+
|
|
11
|
+
The Models settings page gains a separate **Claude Provider** form. You can create multiple Provider IDs under this type without mixing them with generic custom providers.
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<img src="./docs/images/provider-entry.en.jpg" alt="Add Claude Provider entry in DeepSeek Harness" width="580">
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
Selecting **Add Claude Provider** opens the dedicated Anthropic Messages form:
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="./docs/images/claude-provider-form.en.jpg" alt="Claude Provider form in DeepSeek Harness" width="580">
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
2. **Adds model-specific reasoning modes**
|
|
24
|
+
|
|
25
|
+
Each model can use one of three configurable mode sets:
|
|
26
|
+
|
|
27
|
+
- Five levels: Low, Medium, High, XHigh, and Max
|
|
28
|
+
- Four levels: Low, Medium, High, and Max
|
|
29
|
+
- Toggle: On or Off
|
|
30
|
+
|
|
31
|
+
New models default to five levels. Claude providers default to High, while toggle-only models default to On. For adaptive-thinking models, the plugin converts the selection to Claude's `thinking.type: adaptive` and `output_config.effort` request format instead of allowing the adapter to collapse or reject the selected level.
|
|
32
|
+
|
|
33
|
+
<p align="center">
|
|
34
|
+
<img src="./docs/images/model-defaults.en.jpg" alt="Claude model capacity defaults and reasoning modes" width="580">
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
3. **Adds native Anthropic model discovery**
|
|
38
|
+
|
|
39
|
+
DeepSeek Harness's generic custom provider cannot list models for the `anthropic-messages` protocol. This plugin adds model discovery for Claude providers through the provider's native Anthropic-compatible `GET /v1/models` endpoint, including cursor pagination.
|
|
40
|
+
|
|
41
|
+
4. **Fills known Claude model defaults after discovery**
|
|
42
|
+
|
|
43
|
+
When a discovered model matches a recorded Claude model ID, the plugin automatically fills its context window, maximum output length, and reasoning-mode set. Models entered manually remain fully editable and are not overwritten by this lookup.
|
|
44
|
+
|
|
45
|
+
5. **Leaves every other provider unchanged**
|
|
46
|
+
|
|
47
|
+
All discovery, defaults, reasoning controls, and request rewriting are limited to Provider IDs explicitly created as **Claude Providers**. DeepSeek Harness's built-in providers and ordinary custom providers keep their original behavior, even when they use `anthropic-messages` or expose a `claude-*` model ID.
|
|
22
48
|
|
|
23
49
|
## Install
|
|
24
50
|
|
|
25
|
-
|
|
26
|
-
|
|
51
|
+
### Published package
|
|
52
|
+
|
|
53
|
+
Install the Web profile for the browser interface:
|
|
27
54
|
|
|
28
55
|
```sh
|
|
56
|
+
npx @deepseek-ai/dsh plugin --profile web add @mofeng2223/dsh-claude-provider
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Install the Headless profile for command-line runs without the Web interface:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
npx @deepseek-ai/dsh plugin --profile headless add @mofeng2223/dsh-claude-provider
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Restart the corresponding DeepSeek Harness process after installation.
|
|
66
|
+
|
|
67
|
+
### From source
|
|
68
|
+
|
|
69
|
+
Build and package the repository first:
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
git clone https://github.com/MoFeng2223/dsh-claude-provider.git
|
|
73
|
+
cd dsh-claude-provider
|
|
74
|
+
npm install
|
|
29
75
|
npm run build
|
|
76
|
+
mkdir -p dist
|
|
30
77
|
npm pack --pack-destination dist
|
|
31
78
|
```
|
|
32
79
|
|
|
33
|
-
Install the
|
|
80
|
+
Install the generated package into the Web profile:
|
|
34
81
|
|
|
35
82
|
```sh
|
|
36
|
-
npx @deepseek-ai/dsh plugin --profile web add
|
|
37
|
-
npx @deepseek-ai/dsh plugin --profile headless add @mofeng2223/dsh-claude-provider
|
|
83
|
+
npx @deepseek-ai/dsh plugin --profile web add ./dist/mofeng2223-dsh-claude-provider-*.tgz
|
|
38
84
|
```
|
|
39
85
|
|
|
40
|
-
|
|
86
|
+
Or install it into the Headless profile:
|
|
41
87
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Archive installs are package-manager-owned and uninstall without those stale
|
|
46
|
-
links.
|
|
88
|
+
```sh
|
|
89
|
+
npx @deepseek-ai/dsh plugin --profile headless add ./dist/mofeng2223-dsh-claude-provider-*.tgz
|
|
90
|
+
```
|
|
47
91
|
|
|
48
|
-
|
|
49
|
-
each rewritten request. The diagnostic contains only provider, model, and
|
|
50
|
-
selected effort.
|
|
92
|
+
## Uninstall
|
|
51
93
|
|
|
52
|
-
|
|
94
|
+
Run the command for each profile where the plugin was installed:
|
|
53
95
|
|
|
54
96
|
```sh
|
|
55
97
|
npx @deepseek-ai/dsh plugin --profile web remove @mofeng2223/dsh-claude-provider
|
|
56
98
|
npx @deepseek-ai/dsh plugin --profile headless remove @mofeng2223/dsh-claude-provider
|
|
57
99
|
```
|
|
58
100
|
|
|
59
|
-
|
|
60
|
-
alone. Existing custom providers therefore remain visible as ordinary custom
|
|
61
|
-
`anthropic-messages` routes, but lose this plugin's Claude badge, per-model
|
|
62
|
-
thinking-mode controls, native Anthropic model discovery, and adaptive request
|
|
63
|
-
rewrite.
|
|
101
|
+
Uninstalling the plugin does not delete `~/.dsh/settings.yaml` or stored credentials. Existing Claude providers remain configured as ordinary custom `anthropic-messages` routes, but lose the Claude-specific interface, model discovery, defaults, reasoning controls, and adaptive request conversion supplied by this plugin.
|
|
64
102
|
|
|
65
|
-
|
|
66
|
-
typed into the form or resolves the existing route's credential through DSH;
|
|
67
|
-
the value is sent only to that route's Anthropic Models API. Its request
|
|
68
|
-
transformer runs only inside matching `llm/stream` calls and leaves nonmatching
|
|
69
|
-
providers and models untouched.
|
|
103
|
+
## License
|
|
70
104
|
|
|
71
|
-
|
|
72
|
-
its local path; no source build or platform-specific dependency is required.
|
|
105
|
+
[MIT](./LICENSE)
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# DSH Claude Provider
|
|
2
|
+
|
|
3
|
+
[English](./README.md) | 简体中文
|
|
4
|
+
|
|
5
|
+
DeepSeek Harness 的通用推理强度控制与新版 Claude 模型要求的请求参数并不完全匹配,因此选择某个档位后,可能直接出现 HTTP 400,也可能被适配器静默映射成另一个实际档位,例如界面选择 Max,实际请求却只相当于 High。`@mofeng2223/dsh-claude-provider` 增加了一个明确的 Claude 提供方类型,并按照每个 Claude 模型的配置发送正确的推理参数。
|
|
6
|
+
|
|
7
|
+
## 插件功能
|
|
8
|
+
|
|
9
|
+
1. **增加独立的 Claude 提供方类型**
|
|
10
|
+
|
|
11
|
+
模型设置页面会增加单独的“Claude 提供方”表单。你可以在这个类型下创建多个 Provider ID,不会与普通自定义提供方混在一起。
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<img src="./docs/images/provider-entry.jpg" alt="DeepSeek Harness 中新增的 Claude 提供方入口" width="580">
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
点击“添加 Claude 提供方”后,会打开独立的 Anthropic Messages 配置表单:
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="./docs/images/claude-provider-form.jpg" alt="DeepSeek Harness 中的 Claude 提供方表单" width="580">
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
2. **增加按模型设置的推理模式**
|
|
24
|
+
|
|
25
|
+
每个模型可以单独选择以下三种档位配置之一:
|
|
26
|
+
|
|
27
|
+
- 五档:Low、Medium、High、XHigh、Max
|
|
28
|
+
- 四档:Low、Medium、High、Max
|
|
29
|
+
- 开关:On、Off
|
|
30
|
+
|
|
31
|
+
新增模型默认使用五档。Claude 提供方默认选择 High,仅支持开关的模型默认选择 On。对于 adaptive thinking 模型,插件会把所选档位转换成 Claude 使用的 `thinking.type: adaptive` 与 `output_config.effort` 请求格式,避免适配器拒绝请求或把档位降级。
|
|
32
|
+
|
|
33
|
+
<p align="center">
|
|
34
|
+
<img src="./docs/images/model-defaults.jpg" alt="Claude 模型容量预填与思考模式设置" width="580">
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
3. **补充 Anthropic 原生模型探测**
|
|
38
|
+
|
|
39
|
+
DeepSeek Harness 的普通自定义提供方在使用 `anthropic-messages` 协议时无法获取模型列表。本插件为 Claude 提供方增加了基于 Anthropic 兼容 `GET /v1/models` 接口的模型探测,并支持游标分页。
|
|
40
|
+
|
|
41
|
+
4. **探测后自动填写常用 Claude 模型参数**
|
|
42
|
+
|
|
43
|
+
探测到的模型 ID 如果与插件已经记录的 Claude 模型匹配,插件会自动填写上下文窗口、最大输出长度和推理模式。用户手动添加的模型仍然可以自由编辑,不会被这项自动匹配覆盖。
|
|
44
|
+
|
|
45
|
+
5. **不影响其他提供方**
|
|
46
|
+
|
|
47
|
+
模型探测、参数预填、推理档位和请求转换只对明确创建为“Claude 提供方”的 Provider ID 生效。DeepSeek Harness 的内置提供方和普通自定义提供方会保持原有行为,即使它们同样使用 `anthropic-messages` 协议,或者包含 `claude-*` 模型 ID,也不会被插件修改。
|
|
48
|
+
|
|
49
|
+
## 安装
|
|
50
|
+
|
|
51
|
+
### 安装已发布的版本
|
|
52
|
+
|
|
53
|
+
浏览器界面使用 Web Profile:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
npx @deepseek-ai/dsh plugin --profile web add @mofeng2223/dsh-claude-provider
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
不使用 Web 界面的命令行运行使用 Headless Profile:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
npx @deepseek-ai/dsh plugin --profile headless add @mofeng2223/dsh-claude-provider
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
安装后需要重启对应的 DeepSeek Harness 进程。
|
|
66
|
+
|
|
67
|
+
### 从源码安装
|
|
68
|
+
|
|
69
|
+
先克隆、构建并打包项目:
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
git clone https://github.com/MoFeng2223/dsh-claude-provider.git
|
|
73
|
+
cd dsh-claude-provider
|
|
74
|
+
npm install
|
|
75
|
+
npm run build
|
|
76
|
+
mkdir -p dist
|
|
77
|
+
npm pack --pack-destination dist
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
将生成的包安装到 Web Profile:
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
npx @deepseek-ai/dsh plugin --profile web add ./dist/mofeng2223-dsh-claude-provider-*.tgz
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
或者安装到 Headless Profile:
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
npx @deepseek-ai/dsh plugin --profile headless add ./dist/mofeng2223-dsh-claude-provider-*.tgz
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## 卸载
|
|
93
|
+
|
|
94
|
+
插件安装在哪个 Profile,就执行对应的卸载命令:
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
npx @deepseek-ai/dsh plugin --profile web remove @mofeng2223/dsh-claude-provider
|
|
98
|
+
npx @deepseek-ai/dsh plugin --profile headless remove @mofeng2223/dsh-claude-provider
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
卸载插件不会删除 `~/.dsh/settings.yaml` 或已经保存的凭据。已有的 Claude 提供方会继续作为普通的 `anthropic-messages` 自定义路由保留,但会失去本插件提供的 Claude 专用界面、模型探测、参数预填、推理档位和 adaptive 请求转换。
|
|
102
|
+
|
|
103
|
+
## 许可证
|
|
104
|
+
|
|
105
|
+
[MIT](./LICENSE)
|
package/cordis.patch.yml
CHANGED
package/lib/client.js
CHANGED
|
@@ -68,67 +68,67 @@ window.__ModuleLoader__.load({
|
|
|
68
68
|
document.head.appendChild(tag);
|
|
69
69
|
}
|
|
70
70
|
var ModelsSection_module_css_default = {
|
|
71
|
-
"
|
|
72
|
-
"editorActions": "zGbnIq_editorActions",
|
|
73
|
-
"error": "zGbnIq_error",
|
|
74
|
-
"modelRow": "zGbnIq_modelRow",
|
|
71
|
+
"editorRoute": "zGbnIq_editorRoute",
|
|
75
72
|
"section": "zGbnIq_section",
|
|
76
|
-
"linkButton": "zGbnIq_linkButton",
|
|
77
|
-
"hiddenLabel": "zGbnIq_hiddenLabel",
|
|
78
|
-
"rows": "zGbnIq_rows",
|
|
79
|
-
"candidateLabel": "zGbnIq_candidateLabel",
|
|
80
|
-
"input": "zGbnIq_input",
|
|
81
|
-
"notice": "zGbnIq_notice",
|
|
82
|
-
"fetchDialog": "zGbnIq_fetchDialog",
|
|
83
|
-
"candidate": "zGbnIq_candidate",
|
|
84
|
-
"customizedBody": "zGbnIq_customizedBody",
|
|
85
|
-
"modelListHead": "zGbnIq_modelListHead",
|
|
86
73
|
"deleteDialog": "zGbnIq_deleteDialog",
|
|
87
|
-
"
|
|
74
|
+
"editorHeader": "zGbnIq_editorHeader",
|
|
88
75
|
"addModelButton": "zGbnIq_addModelButton",
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"rowName": "zGbnIq_rowName",
|
|
93
|
-
"modelAdvanced": "zGbnIq_modelAdvanced",
|
|
76
|
+
"fieldLabel": "zGbnIq_fieldLabel",
|
|
77
|
+
"deleteConfirm": "zGbnIq_deleteConfirm",
|
|
78
|
+
"addButton": "zGbnIq_addButton",
|
|
94
79
|
"primaryButton": "zGbnIq_primaryButton",
|
|
95
|
-
"
|
|
96
|
-
"rowIdentity": "zGbnIq_rowIdentity",
|
|
80
|
+
"advancedHint": "zGbnIq_advancedHint",
|
|
97
81
|
"modelCatalog": "zGbnIq_modelCatalog",
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"field": "zGbnIq_field",
|
|
82
|
+
"modelRow": "zGbnIq_modelRow",
|
|
83
|
+
"fetchDialog": "zGbnIq_fetchDialog",
|
|
84
|
+
"modelAdvanced": "zGbnIq_modelAdvanced",
|
|
85
|
+
"editorActions": "zGbnIq_editorActions",
|
|
103
86
|
"credentialDotConfigured": "zGbnIq_credentialDotConfigured",
|
|
104
87
|
"addBlock": "zGbnIq_addBlock",
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"addButton": "zGbnIq_addButton",
|
|
109
|
-
"candidateList": "zGbnIq_candidateList",
|
|
110
|
-
"secondaryButton": "zGbnIq_secondaryButton",
|
|
111
|
-
"modelCatalogMeta": "zGbnIq_modelCatalogMeta",
|
|
112
|
-
"modelField": "zGbnIq_modelField",
|
|
113
|
-
"customized": "zGbnIq_customized",
|
|
88
|
+
"dangerButton": "zGbnIq_dangerButton",
|
|
89
|
+
"rowCard": "zGbnIq_rowCard",
|
|
90
|
+
"credentialDot": "zGbnIq_credentialDot",
|
|
114
91
|
"selectInput": "zGbnIq_selectInput",
|
|
92
|
+
"error": "zGbnIq_error",
|
|
93
|
+
"secondaryButton": "zGbnIq_secondaryButton",
|
|
94
|
+
"modelCatalogHeading": "zGbnIq_modelCatalogHeading",
|
|
95
|
+
"modelList": "zGbnIq_modelList",
|
|
96
|
+
"modelEntry": "zGbnIq_modelEntry",
|
|
97
|
+
"candidate": "zGbnIq_candidate",
|
|
98
|
+
"modelFieldLabel": "zGbnIq_modelFieldLabel",
|
|
99
|
+
"editor": "zGbnIq_editor",
|
|
100
|
+
"intro": "zGbnIq_intro",
|
|
101
|
+
"modelCatalogTitle": "zGbnIq_modelCatalogTitle",
|
|
102
|
+
"input": "zGbnIq_input",
|
|
103
|
+
"rowTag": "zGbnIq_rowTag",
|
|
104
|
+
"notice": "zGbnIq_notice",
|
|
105
|
+
"modelEmpty": "zGbnIq_modelEmpty",
|
|
106
|
+
"addCard": "zGbnIq_addCard",
|
|
115
107
|
"iconButton": "zGbnIq_iconButton",
|
|
108
|
+
"linkButton": "zGbnIq_linkButton",
|
|
109
|
+
"hiddenLabel": "zGbnIq_hiddenLabel",
|
|
110
|
+
"candidateId": "zGbnIq_candidateId",
|
|
116
111
|
"setupCard": "zGbnIq_setupCard",
|
|
117
|
-
"
|
|
112
|
+
"iconButtonDanger": "zGbnIq_iconButtonDanger",
|
|
113
|
+
"title": "zGbnIq_title",
|
|
114
|
+
"rowIdentity": "zGbnIq_rowIdentity",
|
|
115
|
+
"savedNotice": "zGbnIq_savedNotice",
|
|
116
|
+
"candidateLabel": "zGbnIq_candidateLabel",
|
|
118
117
|
"rowActions": "zGbnIq_rowActions",
|
|
119
|
-
"addCard": "zGbnIq_addCard",
|
|
120
|
-
"candidateId": "zGbnIq_candidateId",
|
|
121
|
-
"modelCatalogHeading": "zGbnIq_modelCatalogHeading",
|
|
122
|
-
"credentialDotMissing": "zGbnIq_credentialDotMissing",
|
|
123
|
-
"rowTag": "zGbnIq_rowTag",
|
|
124
|
-
"customizedSummary": "zGbnIq_customizedSummary",
|
|
125
118
|
"addActions": "zGbnIq_addActions",
|
|
119
|
+
"candidateList": "zGbnIq_candidateList",
|
|
120
|
+
"rows": "zGbnIq_rows",
|
|
121
|
+
"rowName": "zGbnIq_rowName",
|
|
122
|
+
"modelField": "zGbnIq_modelField",
|
|
123
|
+
"customized": "zGbnIq_customized",
|
|
124
|
+
"modelListHead": "zGbnIq_modelListHead",
|
|
126
125
|
"editorTitle": "zGbnIq_editorTitle",
|
|
127
|
-
"
|
|
128
|
-
"
|
|
129
|
-
"
|
|
130
|
-
"
|
|
131
|
-
"
|
|
126
|
+
"rowHead": "zGbnIq_rowHead",
|
|
127
|
+
"field": "zGbnIq_field",
|
|
128
|
+
"modelCatalogMeta": "zGbnIq_modelCatalogMeta",
|
|
129
|
+
"credentialDotMissing": "zGbnIq_credentialDotMissing",
|
|
130
|
+
"customizedSummary": "zGbnIq_customizedSummary",
|
|
131
|
+
"customizedBody": "zGbnIq_customizedBody"
|
|
132
132
|
};
|
|
133
133
|
//#endregion
|
|
134
134
|
//#region lib/types/client/EditorFooter.js
|
|
@@ -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
|
}),
|
|
@@ -2230,10 +2235,10 @@ window.__ModuleLoader__.load({
|
|
|
2230
2235
|
document.head.appendChild(tag);
|
|
2231
2236
|
}
|
|
2232
2237
|
var OnboardingModal_module_css_default = {
|
|
2233
|
-
"
|
|
2234
|
-
"title": "jLrgrW_title",
|
|
2238
|
+
"content": "jLrgrW_content",
|
|
2235
2239
|
"dialog": "jLrgrW_dialog",
|
|
2236
|
-
"
|
|
2240
|
+
"title": "jLrgrW_title",
|
|
2241
|
+
"body": "jLrgrW_body"
|
|
2237
2242
|
};
|
|
2238
2243
|
//#endregion
|
|
2239
2244
|
//#region lib/types/client/OnboardingModal.js
|
|
@@ -2383,10 +2388,10 @@ window.__ModuleLoader__.load({
|
|
|
2383
2388
|
document.head.appendChild(tag);
|
|
2384
2389
|
}
|
|
2385
2390
|
var WelcomeNotice_module_css_default = {
|
|
2386
|
-
"error": "t1T8VW_error",
|
|
2387
|
-
"primary": "t1T8VW_primary",
|
|
2388
2391
|
"copy": "t1T8VW_copy",
|
|
2389
|
-
"actions": "t1T8VW_actions"
|
|
2392
|
+
"actions": "t1T8VW_actions",
|
|
2393
|
+
"primary": "t1T8VW_primary",
|
|
2394
|
+
"error": "t1T8VW_error"
|
|
2390
2395
|
};
|
|
2391
2396
|
//#endregion
|
|
2392
2397
|
//#region lib/types/client/WelcomeNotice.js
|
|
@@ -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.
|
|
3
|
+
"version": "0.2.3",
|
|
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.7",
|
|
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": {
|
|
@@ -35,7 +48,7 @@
|
|
|
35
48
|
"node": ">=22.19"
|
|
36
49
|
},
|
|
37
50
|
"devDependencies": {
|
|
38
|
-
"@deepseek-ai/dsh-client-ui-settings-models": "0.1.0-rc.
|
|
51
|
+
"@deepseek-ai/dsh-client-ui-settings-models": "0.1.0-rc.7"
|
|
39
52
|
},
|
|
40
53
|
"dsh": {
|
|
41
54
|
"bundle": {
|
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 }
|
|
262
255
|
}
|
|
263
256
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return
|
|
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
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
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. */
|
|
@@ -275,6 +304,61 @@ export function shouldUseAdaptiveThinking(modelInfo) {
|
|
|
275
304
|
return ids.has('low') && ids.has('medium') && ids.has('high') && ids.has('max')
|
|
276
305
|
}
|
|
277
306
|
|
|
307
|
+
function hasAdaptiveEffortMap(map) {
|
|
308
|
+
if (map === null || typeof map !== 'object' || Array.isArray(map)) return false
|
|
309
|
+
return ['low', 'medium', 'high', 'max'].every(level => typeof map[level] === 'string' && map[level].length > 0)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/** Whether a pi-ai catalog model should be serialized with adaptive thinking. */
|
|
313
|
+
export function piModelHasAdaptiveEfforts(model) {
|
|
314
|
+
return hasAdaptiveEffortMap(model?.thinkingLevelMap)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Stamp `compat.forceAdaptiveThinking` onto a pi-ai model so the Anthropic
|
|
319
|
+
* adapter builds `thinking.type: adaptive` before `@anthropic-ai/sdk` sees the
|
|
320
|
+
* payload. That is what removes the SDK deprecation warning for Opus 4.6.
|
|
321
|
+
*/
|
|
322
|
+
export function withForcedAdaptiveThinking(model, provider, settings) {
|
|
323
|
+
if (!isClaudeProviderType(settings, provider)) return model
|
|
324
|
+
if (model === null || typeof model !== 'object' || Array.isArray(model)) return model
|
|
325
|
+
if (model.compat?.forceAdaptiveThinking === true) return model
|
|
326
|
+
if (!piModelHasAdaptiveEfforts(model)) return model
|
|
327
|
+
return {
|
|
328
|
+
...model,
|
|
329
|
+
compat: { ...model.compat, forceAdaptiveThinking: true },
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function installForceAdaptiveThinking(ctx, providerTypes) {
|
|
334
|
+
const llm = ctx.llm
|
|
335
|
+
if (typeof llm.listProviders !== 'function' || typeof llm.registration !== 'function') return
|
|
336
|
+
|
|
337
|
+
const patched = new WeakSet()
|
|
338
|
+
const wrap = () => {
|
|
339
|
+
for (const provider of llm.listProviders()) {
|
|
340
|
+
const id = provider?.id
|
|
341
|
+
if (typeof id !== 'string' || id.length === 0) continue
|
|
342
|
+
let adapter
|
|
343
|
+
try {
|
|
344
|
+
adapter = llm.registration(id).adapter
|
|
345
|
+
} catch {
|
|
346
|
+
continue
|
|
347
|
+
}
|
|
348
|
+
if (adapter === null || typeof adapter !== 'object' || typeof adapter.modelOf !== 'function') continue
|
|
349
|
+
if (patched.has(adapter)) continue
|
|
350
|
+
const original = adapter.modelOf.bind(adapter)
|
|
351
|
+
adapter.modelOf = (snapshot, providerId, modelId) => (
|
|
352
|
+
withForcedAdaptiveThinking(original(snapshot, providerId, modelId), providerId, providerTypes.get())
|
|
353
|
+
)
|
|
354
|
+
patched.add(adapter)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
wrap()
|
|
359
|
+
ctx.effect(() => ctx.on('llm/adapters-updated', wrap), 'claude-provider: force adaptive thinking')
|
|
360
|
+
}
|
|
361
|
+
|
|
278
362
|
function effortFromBudget(budget) {
|
|
279
363
|
if (typeof budget !== 'number' || !Number.isFinite(budget)) return 'high'
|
|
280
364
|
if (budget <= 2048) return 'low'
|
|
@@ -418,6 +502,19 @@ async function* modelAwareStream(ctx, storage, resolved, options, next) {
|
|
|
418
502
|
|
|
419
503
|
export function apply(ctx, config) {
|
|
420
504
|
const resolved = resolveConfig(config)
|
|
505
|
+
const providerTypes = ctx.settings.register(
|
|
506
|
+
CLAUDE_PROVIDER_SETTINGS_NS,
|
|
507
|
+
ProviderTypeSettings,
|
|
508
|
+
{ base: { providerTypes: {} } },
|
|
509
|
+
)
|
|
510
|
+
ctx.llm.registerConfigurableProviders([{
|
|
511
|
+
provider: CLAUDE_PROVIDER_DIRECTORY_SENTINEL,
|
|
512
|
+
displayName: 'Claude Provider Type',
|
|
513
|
+
settingsNs: CLAUDE_PROVIDER_SETTINGS_NS,
|
|
514
|
+
settingsPath: ['providerTypes'],
|
|
515
|
+
}])
|
|
516
|
+
installClaudeModelInfoDefaults(ctx, providerTypes)
|
|
517
|
+
installForceAdaptiveThinking(ctx, providerTypes)
|
|
421
518
|
installClaudeModelDiscovery(ctx)
|
|
422
519
|
const storage = new AsyncLocalStorage()
|
|
423
520
|
const upstreamFetch = globalThis.fetch
|
|
@@ -447,7 +544,7 @@ export function apply(ctx, config) {
|
|
|
447
544
|
})
|
|
448
545
|
|
|
449
546
|
ctx.on('llm/stream', (options, next) => {
|
|
450
|
-
if (!
|
|
547
|
+
if (!isClaudeProviderType(providerTypes.get(), options.provider)) return next()
|
|
451
548
|
return modelAwareStream(ctx, storage, resolved, options, next)
|
|
452
549
|
})
|
|
453
550
|
}
|