@goodandready/dsh-model-sync 0.4.3 → 0.4.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/README.md +43 -5
- package/README.ru.md +81 -42
- package/README.zh.md +84 -44
- package/lib/adapter-registry.js +44 -2
- package/lib/inventory.js +13 -1
- package/lib/models.js +150 -0
- package/lib/synchronizer-helpers.js +14 -1
- package/lib/synchronizer.js +33 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
|
|
40
40
|
**`dsh-model-sync`** keeps your **DeepSeek Harness** model catalog up-to-date with upstream AI providers.
|
|
41
41
|
|
|
42
|
-
Instead of manually editing YAML configs whenever a provider launches a new model, adjusts context window limits, or changes pricing, `dsh-model-sync` automatically discovers new releases, updates capability metadata (`vision`, `tools`, `reasoning`, `embeddings`), tracks account balances, and reconciles models on the fly without server restarts.
|
|
42
|
+
Instead of manually editing YAML configs whenever a provider launches a new model, adjusts context window limits, or changes pricing, `dsh-model-sync` automatically discovers new releases, updates capability metadata (`vision`, `tools`, `reasoning`, `embeddings`), tracks account balances, manages model selection visibility, and reconciles models on the fly without server restarts.
|
|
43
43
|
|
|
44
44
|
```mermaid
|
|
45
45
|
graph LR
|
|
@@ -54,6 +54,8 @@ graph LR
|
|
|
54
54
|
Registry -->|x-api-key Auth| P2[Anthropic Claude / Custom Gateways]
|
|
55
55
|
Registry -->|query-key Auth| P3[Google Gemini]
|
|
56
56
|
Registry -->|Local Probe| P4[Local Ollama / vLLM / SGLang]
|
|
57
|
+
Registry -->|CommandCode API| P5[Command Code / visibleModels]
|
|
58
|
+
Registry -->|Plan Discovery| P6[ClineBot Subscription Plan]
|
|
57
59
|
end
|
|
58
60
|
|
|
59
61
|
subgraph Reconcile [Catalog Reconciler & Auditing]
|
|
@@ -61,8 +63,10 @@ graph LR
|
|
|
61
63
|
P2 --> Normalizer
|
|
62
64
|
P3 --> Normalizer
|
|
63
65
|
P4 --> Normalizer
|
|
66
|
+
P5 --> Normalizer
|
|
67
|
+
P6 --> Normalizer
|
|
64
68
|
Normalizer --> Diff[Sync Diff Tracker: Added / Deprecated]
|
|
65
|
-
Diff --> Catalog[Active DSH Models Catalog]
|
|
69
|
+
Diff --> Catalog[Active DSH Models Catalog & visibleModels]
|
|
66
70
|
end
|
|
67
71
|
|
|
68
72
|
style Trigger fill:#1e1e2e,stroke:#89b4fa,stroke-width:2px,color:#cdd6f4
|
|
@@ -75,21 +79,24 @@ graph LR
|
|
|
75
79
|
## ✨ Key Features
|
|
76
80
|
|
|
77
81
|
* 🔄 **Automated Catalog Discovery**: Syncs model lists, aliases, context windows, and capability flags (`vision`, `tools`, `reasoning`, `embeddings`) across 25+ providers.
|
|
78
|
-
* 🛡️ **Minijinja Chat Template & Role Compatibility**: Automatically sets `compat.supportsDeveloperRole: false` for Groq open-source models (Qwen, Llama, Mistral, Gemma, DeepSeek) and DeepSeek API, preventing 400 `Unexpected message role` errors with reasoning models while preserving user overrides.
|
|
79
|
-
* 🌐 **25+ Built-in Provider Adapters**: Pre-configured discovery for OpenAI, Anthropic, Google, DeepSeek, xAI, OpenRouter, Groq, Mistral, Cerebras, Fireworks, HuggingFace, Moonshot, NVIDIA, Qwen, Together, Xiaomi MiMo, SiliconFlow, and local Ollama.
|
|
82
|
+
* 🛡️ **Minijinja Chat Template & Role Compatibility**: Automatically sets `compat.supportsDeveloperRole: false` for Groq open-source models (Qwen, Llama, Mistral, Gemma, DeepSeek) and DeepSeek API, preventing 400 `Unexpected message role` errors with reasoning models while preserving user overrides.
|
|
83
|
+
* 🌐 **25+ Built-in Provider Adapters**: Pre-configured discovery for OpenAI, Anthropic, Google, DeepSeek, xAI, OpenRouter, Groq, Mistral, Cerebras, Fireworks, HuggingFace, Moonshot, NVIDIA, Qwen, Together, Xiaomi MiMo, SiliconFlow, Command Code, ClineBot, and local Ollama.
|
|
84
|
+
* 🎛️ **Command Code Model Selection**: Full discovery of Command Code models with granular checkbox selection in the UI, automatically syncing `visibleModels` to `llm-commandcode` settings.
|
|
85
|
+
* 🎯 **ClineBot Subscription Plan Sync**: Dedicated adapter querying `GET /users/me/plan` to discover only active subscription-included models, eliminating catalog clutter from out-of-plan endpoints.
|
|
80
86
|
* 🔌 **Generic / Custom Adapter Support**: Connect arbitrary OpenAI-compatible `/v1/models` endpoints with configurable auth (`bearer`, `x-api-key`, `query-key`, `none`).
|
|
81
87
|
* 📊 **Balance & Quota Monitoring**: Queries upstream billing endpoints (where supported) to report account credit balances and prevent unexpected exhaustion.
|
|
82
88
|
* 📜 **Sync History & Diff Auditing**: Tracks all newly discovered models, deprecated IDs, and modified capabilities with timestamped diff logs in the Web UI.
|
|
83
89
|
* 🖥️ **Full Web GUI Panel (**Settings → Model Synchronization**)**:
|
|
84
90
|
* Live status cards per provider with active model counters;
|
|
85
91
|
* Instant "Sync Now" button for on-demand catalog refreshes;
|
|
92
|
+
* Granular model checkboxes for selection and visibility management;
|
|
86
93
|
* Provider enable/disable toggles and custom endpoint inputs.
|
|
87
94
|
|
|
88
95
|
---
|
|
89
96
|
|
|
90
97
|
## 🛠️ Supported Discovery Providers
|
|
91
98
|
|
|
92
|
-
| Provider Identifier | Authentication Format | Capabilities Auto-Detection |
|
|
99
|
+
| Provider Identifier | Authentication Format | Capabilities Auto-Detection & Notes |
|
|
93
100
|
|---|---|---|
|
|
94
101
|
| `openai` | Bearer Token | `vision`, `tools`, `reasoning`, `embeddings` |
|
|
95
102
|
| `anthropic` | `x-api-key` header | `vision`, `tools`, `reasoning` |
|
|
@@ -99,6 +106,8 @@ graph LR
|
|
|
99
106
|
| `openrouter` | Bearer Token | Multi-vendor model catalog with pricing & context limits |
|
|
100
107
|
| `groq` | Bearer Token | `tools`, `reasoning`, `vision`, `supportsDeveloperRole: false` |
|
|
101
108
|
| `mistral` | Bearer Token | `tools`, `reasoning`, `vision` |
|
|
109
|
+
| `commandcode` | Bearer Token | `https://api.commandcode.ai/provider/v1/models` catalog with `visibleModels` selection syncing to `llm-commandcode` |
|
|
110
|
+
| `clinebot` | Bearer Token | Active subscription plan discovery (`/users/me/plan`), restricting sync strictly to plan-included models |
|
|
102
111
|
| `fireworks` | Bearer Token | Open-weights inference endpoints |
|
|
103
112
|
| `huggingface` | Bearer Token | Serverless Inference API catalog |
|
|
104
113
|
| `together` | Bearer Token | Open-source model catalog |
|
|
@@ -117,3 +126,32 @@ dsh plugin --profile web add @goodandready/dsh-model-sync
|
|
|
117
126
|
> Restart DSH Web UI after installation (`systemctl --user restart dsh-web`) to activate background catalog synchronization.
|
|
118
127
|
|
|
119
128
|
---
|
|
129
|
+
|
|
130
|
+
## ⚙️ Configuration (`settings.yaml`)
|
|
131
|
+
|
|
132
|
+
```yaml
|
|
133
|
+
dsh-model-sync:
|
|
134
|
+
enabled: true
|
|
135
|
+
syncIntervalMinutes: 60
|
|
136
|
+
autoReconcile: true
|
|
137
|
+
enableBalanceChecks: true
|
|
138
|
+
providers:
|
|
139
|
+
commandcode:
|
|
140
|
+
enabled: true
|
|
141
|
+
clinebot:
|
|
142
|
+
enabled: true
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
| Parameter | Type | Default | Description |
|
|
146
|
+
|---|---|---|---|
|
|
147
|
+
| `enabled` | `boolean` | `true` | Master switch for background synchronization |
|
|
148
|
+
| `syncIntervalMinutes` | `number` | `60` | Polling frequency for catalog refreshes |
|
|
149
|
+
| `autoReconcile` | `boolean` | `true` | Automatically apply discovered models to active catalog |
|
|
150
|
+
| `enableBalanceChecks` | `boolean` | `true` | Query billing endpoints where supported |
|
|
151
|
+
| `providers.<id>.enabled`| `boolean` | `true` | Enable or disable discovery for specific provider |
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 📄 License
|
|
156
|
+
|
|
157
|
+
MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)
|
package/README.ru.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
|
-
<h3>Динамическая синхронизация каталогов
|
|
5
|
+
<h3>Динамическая синхронизация каталогов LLM-моделей и автоматический мониторинг баланса для DeepSeek Harness</h3>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<a href="https://www.npmjs.com/package/@goodandready/dsh-model-sync"><img src="https://img.shields.io/npm/v/@goodandready/dsh-model-sync.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
<table align="center">
|
|
25
25
|
<tr>
|
|
26
26
|
<td align="center">
|
|
27
|
-
⭐ <strong>Если вам нравится этот плагин, поставьте ему
|
|
27
|
+
⭐ <strong>Если вам нравится этот плагин, поставьте ему Star на GitHub</strong> — это покажет мне, что плагин полезен, и добавит мотивации продолжать его развитие.
|
|
28
28
|
<br><br>
|
|
29
|
-
🐛 <strong>Если вы нашли баг или хотите предложить
|
|
29
|
+
🐛 <strong>Если вы нашли баг или хотите предложить новую функцию</strong>, создайте Issue на GitHub на любом языке — я рассмотрю предложение и реализую полезные улучшения в одной из следующих версий плагина.
|
|
30
30
|
</td>
|
|
31
31
|
</tr>
|
|
32
32
|
</table>
|
|
@@ -37,32 +37,36 @@
|
|
|
37
37
|
|
|
38
38
|
## ⚡ Обзор
|
|
39
39
|
|
|
40
|
-
**`dsh-model-sync`**
|
|
40
|
+
**`dsh-model-sync`** поддерживает каталог моделей **DeepSeek Harness** в актуальном состоянии с апстрим AI-провайдерами.
|
|
41
41
|
|
|
42
|
-
Вместо ручного редактирования YAML-файлов при
|
|
42
|
+
Вместо ручного редактирования YAML-файлов при выпуске провайдером новой модели, изменении размера контекста или цен, `dsh-model-sync` автоматически обнаруживает новые релизы, обновляет метаданные возможностей (`vision`, `tools`, `reasoning`, `embeddings`), отслеживает баланс аккаунтов, управляет видимостью моделей в интерфейсе и синхронизирует модели на лету без перезапуска сервера.
|
|
43
43
|
|
|
44
44
|
```mermaid
|
|
45
45
|
graph LR
|
|
46
|
-
subgraph Trigger [
|
|
46
|
+
subgraph Trigger [Расписание и ручной запуск]
|
|
47
47
|
Cron[⏰ Фоновый планировщик опроса] --> Engine[Ядро dsh-model-sync]
|
|
48
|
-
WebUI[🖥️ Кнопка
|
|
48
|
+
WebUI[🖥️ Настройки: Кнопка Sync Now] --> Engine
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
subgraph Providers [25+
|
|
51
|
+
subgraph Providers [25+ Апстрим-провайдеров]
|
|
52
52
|
Engine --> Registry{Реестр адаптеров}
|
|
53
|
-
Registry
|
|
54
|
-
Registry
|
|
55
|
-
Registry
|
|
56
|
-
Registry -->|Локальный опрос| P4[
|
|
53
|
+
Registry -->|Bearer Auth| P1[OpenAI / DeepSeek / OpenRouter / Groq]
|
|
54
|
+
Registry -->|x-api-key Auth| P2[Anthropic Claude / Пользовательские шлюзы]
|
|
55
|
+
Registry -->|query-key Auth| P3[Google Gemini]
|
|
56
|
+
Registry -->|Локальный опрос| P4[Локальные Ollama / vLLM / SGLang]
|
|
57
|
+
Registry -->|CommandCode API| P5[Command Code / visibleModels]
|
|
58
|
+
Registry -->|Опрос подписки| P6[План подписки ClineBot]
|
|
57
59
|
end
|
|
58
60
|
|
|
59
61
|
subgraph Reconcile [Сверка каталога и аудит]
|
|
60
|
-
P1 --> Normalizer[
|
|
62
|
+
P1 --> Normalizer[Нормализатор моделей и тегирование возможностей]
|
|
61
63
|
P2 --> Normalizer
|
|
62
64
|
P3 --> Normalizer
|
|
63
65
|
P4 --> Normalizer
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
P5 --> Normalizer
|
|
67
|
+
P6 --> Normalizer
|
|
68
|
+
Normalizer --> Diff[Трекер изменений: Добавлено / Устарело]
|
|
69
|
+
Diff --> Catalog[Активный каталог моделей DSH и visibleModels]
|
|
66
70
|
end
|
|
67
71
|
|
|
68
72
|
style Trigger fill:#1e1e2e,stroke:#89b4fa,stroke-width:2px,color:#cdd6f4
|
|
@@ -74,15 +78,41 @@ graph LR
|
|
|
74
78
|
|
|
75
79
|
## ✨ Ключевые возможности
|
|
76
80
|
|
|
77
|
-
* 🔄 **Автоматическое обнаружение
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
* 🔄 **Автоматическое обнаружение каталога**: синхронизация списков моделей, алиасов, контекстных окон и флагов возможностей (`vision`, `tools`, `reasoning`, `embeddings`) для 25+ провайдеров.
|
|
82
|
+
* 🛡️ **Совместимость шаблонов чата и ролей Minijinja**: автоматическая установка `compat.supportsDeveloperRole: false` для открытых моделей Groq (Qwen, Llama, Mistral, Gemma, DeepSeek) и DeepSeek API, предотвращая ошибки 400 `Unexpected message role` для reasoning-моделей с сохранением пользовательских настроек.
|
|
83
|
+
* 🌐 **25+ встроенных адаптеров провайдеров**: готовая поддержка OpenAI, Anthropic, Google, DeepSeek, xAI, OpenRouter, Groq, Mistral, Cerebras, Fireworks, HuggingFace, Moonshot, NVIDIA, Qwen, Together, Xiaomi MiMo, SiliconFlow, Command Code, ClineBot и локальной Ollama.
|
|
84
|
+
* 🎛️ **Управление моделями Command Code**: полное обнаружение моделей Command Code с возможностью выбора чекбоксами в UI и автоматической синхронизацией `visibleModels` в настройках `llm-commandcode`.
|
|
85
|
+
* 🎯 **Синхронизация тарифного плана ClineBot**: выделенный адаптер, опрашивающий `GET /users/me/plan`, который получает исключительно модели, входящие в активную подписку, исключая появление сотен недоступных моделей.
|
|
86
|
+
* 🔌 **Поддержка универсальных и кастомных адаптеров**: подключение произвольных OpenAI-совместимых эндпоинтов `/v1/models` с настраиваемой авторизацией (`bearer`, `x-api-key`, `query-key`, `none`).
|
|
87
|
+
* 📊 **Мониторинг баланса и квот**: опрос биллинговых эндпоинтов провайдеров (где поддерживается) для предотвращения неожиданного исчерпания средств.
|
|
88
|
+
* 📜 **История синхронизации и аудит диффов**: отслеживание всех добавленных моделей, устаревших ID и изменённых возможностей с журналом изменений во времени.
|
|
89
|
+
* 🖥️ **Полноценная панель Web UI (**Настройки → Синхронизация моделей**)**:
|
|
90
|
+
* Карточки статуса по каждому провайдеру со счётчиками активных моделей;
|
|
91
|
+
* Кнопка мгновенной синхронизации «Sync Now»;
|
|
92
|
+
* Выбор и скрытие моделей чекбоксами;
|
|
93
|
+
* Переключатели включения/отключения провайдеров и ввод кастомных эндпоинтов.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 🛠️ Поддерживаемые провайдеры
|
|
98
|
+
|
|
99
|
+
| Идентификатор провайдера | Формат авторизации | Автоопределение возможностей и примечания |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| `openai` | Bearer Token | `vision`, `tools`, `reasoning`, `embeddings` |
|
|
102
|
+
| `anthropic` | Заголовок `x-api-key` | `vision`, `tools`, `reasoning` |
|
|
103
|
+
| `google` | Query-параметр / Ключ | `vision`, `tools`, `reasoning`, `embeddings` |
|
|
104
|
+
| `deepseek` | Bearer Token | `tools`, `reasoning` |
|
|
105
|
+
| `xai` | Bearer Token | `vision`, `tools`, `reasoning` |
|
|
106
|
+
| `openrouter` | Bearer Token | Мультипровайдерный каталог с ценами и контекстными лимитами |
|
|
107
|
+
| `groq` | Bearer Token | `tools`, `reasoning`, `vision`, `supportsDeveloperRole: false` |
|
|
108
|
+
| `mistral` | Bearer Token | `tools`, `reasoning`, `vision` |
|
|
109
|
+
| `commandcode` | Bearer Token | Каталог `https://api.commandcode.ai/provider/v1/models` с сохранением выбора в `visibleModels` для `llm-commandcode` |
|
|
110
|
+
| `clinebot` | Bearer Token | Опрос активного тарифного плана (`/users/me/plan`), синхронизация строго включённых в подписку моделей |
|
|
111
|
+
| `fireworks` | Bearer Token | Эндпоинты открытых весов |
|
|
112
|
+
| `huggingface` | Bearer Token | Каталог Serverless Inference API |
|
|
113
|
+
| `together` | Bearer Token | Каталог моделей Open-Source |
|
|
114
|
+
| `ollama` | Локальный HTTP (`none`) | Локальное обнаружение офлайн-моделей (`/api/tags`) |
|
|
115
|
+
| `custom` | Настраиваемый | Любой кастомный шлюз, совместимый с OpenAI или Google |
|
|
86
116
|
|
|
87
117
|
---
|
|
88
118
|
|
|
@@ -92,27 +122,36 @@ graph LR
|
|
|
92
122
|
dsh plugin --profile web add @goodandready/dsh-model-sync
|
|
93
123
|
```
|
|
94
124
|
|
|
125
|
+
> [!IMPORTANT]
|
|
126
|
+
> Перезапустите Web UI DSH после установки (`systemctl --user restart dsh-web`), чтобы активировать фоновую синхронизацию каталогов.
|
|
127
|
+
|
|
95
128
|
---
|
|
96
129
|
|
|
97
|
-
##
|
|
130
|
+
## ⚙️ Конфигурация (`settings.yaml`)
|
|
131
|
+
|
|
132
|
+
```yaml
|
|
133
|
+
dsh-model-sync:
|
|
134
|
+
enabled: true
|
|
135
|
+
syncIntervalMinutes: 60
|
|
136
|
+
autoReconcile: true
|
|
137
|
+
enableBalanceChecks: true
|
|
138
|
+
providers:
|
|
139
|
+
commandcode:
|
|
140
|
+
enabled: true
|
|
141
|
+
clinebot:
|
|
142
|
+
enabled: true
|
|
143
|
+
```
|
|
98
144
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
145
|
+
| Параметр | Тип | По умолчанию | Описание |
|
|
146
|
+
|---|---|---|---|
|
|
147
|
+
| `enabled` | `boolean` | `true` | Главный переключатель фоновой синхронизации |
|
|
148
|
+
| `syncIntervalMinutes` | `number` | `60` | Периодичность опроса провайдеров (в минутах) |
|
|
149
|
+
| `autoReconcile` | `boolean` | `true` | Автоматически применять обнаруженные модели к активному каталогу |
|
|
150
|
+
| `enableBalanceChecks` | `boolean` | `true` | Опрашивать биллинговые эндпоинты при наличии поддержки |
|
|
151
|
+
| `providers.<id>.enabled`| `boolean` | `true` | Включение или отключение обнаружения для конкретного провайдера |
|
|
104
152
|
|
|
105
|
-
|
|
153
|
+
---
|
|
106
154
|
|
|
107
|
-
|
|
108
|
-
* Реализована генерация слабого ETag для маршрута `GET /dsh-model-sync/status`. При 15-секундном опросе UI возвращается пустой ответ `304 Not Modified`, исключающий холостую сериализацию JSON и сетевой оверхед.
|
|
109
|
-
* 🌐 **Условные запросы к API провайдеров (Upstream Conditional Requests)**:
|
|
110
|
-
* Передача заголовков `If-None-Match` и `If-Modified-Since` в generic и declarative адаптерах. При ответе `304` от апстрим-провайдера мгновенно возвращается кэш без повторного парсинга моделей.
|
|
111
|
-
* 🎯 **Debounce поиска и useMemo в веб-интерфейсе**:
|
|
112
|
-
* Добавлен debounce (150 мс) на ввод в строку поиска и мемоизация `React.useMemo` для фильтрации и сортировки моделей в пикере, обеспечивая плавный отклик без микрофризов.
|
|
113
|
-
* 🧠 **Расширенное распознавание возможностей моделей**:
|
|
114
|
-
* Распознавание современных reasoning/thinking моделей (`DeepSeek-R1`, `o1`, `o3-mini`, CoT) и моделей для кода (`code`), поддержка тега `code` в политиках фильтрации.
|
|
115
|
-
* 🛡️ **Экспоненциальный джиттер при повторах**:
|
|
116
|
-
* Защита от thundering herd с помощью случайного коэффициента джиттера (`0.5 - 1.0`) при повторных запросах к API провайдеров.
|
|
155
|
+
## 📄 Лицензия
|
|
117
156
|
|
|
118
|
-
|
|
157
|
+
MIT © [GooDAnDReaDY](https://github.com/GooDAnDReaDY)
|
package/README.zh.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
|
-
<h3
|
|
5
|
+
<h3>面向 DeepSeek Harness 的动态大模型目录同步与自动化余额监控插件</h3>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<a href="https://www.npmjs.com/package/@goodandready/dsh-model-sync"><img src="https://img.shields.io/npm/v/@goodandready/dsh-model-sync.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
14
|
<p align="center">
|
|
15
|
-
<a href="https://goodandready.app/"><img src="https://img.shields.io/badge
|
|
15
|
+
<a href="https://goodandready.app/"><img src="https://img.shields.io/badge/作者所有项目-goodandready.app-ff4500.svg?style=for-the-badge&logo=rocket&logoColor=white&labelColor=1a1a2e" alt="作者所有项目"></a>
|
|
16
16
|
</p>
|
|
17
17
|
|
|
18
18
|
<p align="center">
|
|
@@ -35,49 +35,38 @@
|
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
|
-
## 🚀 v0.3.13 性能与质量优化
|
|
39
|
-
|
|
40
|
-
* ⚡ **ETag / 304 Not Modified 状态缓存**:
|
|
41
|
-
* 为 `GET /dsh-model-sync/status` 提供弱 ETag 支持。UI 定时 15 秒轮询时直接返回 `304 Not Modified`,避免多余 JSON 序列化与流量损耗。
|
|
42
|
-
* 🌐 **上游条件请求 (Upstream Conditional Requests)**:
|
|
43
|
-
* 向提供商 API 发送 `If-None-Match` / `If-Modified-Since`。当提供商返回 304 时直接命中缓存,无需重复解析。
|
|
44
|
-
* 🎯 **模型搜索防抖与 useMemo 优化**:
|
|
45
|
-
* 模型选择器搜索框加入 150ms debounce 与 `React.useMemo` 缓存,大幅提升大模型列表下的筛选与排序流畅度。
|
|
46
|
-
* 🧠 **扩展模型能力检测**:
|
|
47
|
-
* 支持现代推理/思考模型(DeepSeek-R1、o1/o3-mini)及代码专用模型(`code`)的能力标记与策略过滤。
|
|
48
|
-
* 🛡️ **带抖动的指数退避重试 (Exponential Jitter)**:
|
|
49
|
-
* 在 API 失败重试时加入随机抖动系数,有效防止高并发下的惊群效应。
|
|
50
|
-
|
|
51
|
-
---
|
|
52
|
-
|
|
53
38
|
## ⚡ 插件概览
|
|
54
39
|
|
|
55
|
-
**`dsh-model-sync`**
|
|
40
|
+
**`dsh-model-sync`** 保持您的 **DeepSeek Harness** 模型目录与上游各大 AI 服务商最新发布完全同步。
|
|
56
41
|
|
|
57
|
-
|
|
42
|
+
每当服务商推出新模型、调整上下文窗口上限或变更费率时,不再需要手动逐项编辑 YAML 配置文件。`dsh-model-sync` 能够自动发现新版本、更新模型能力元数据(`vision`、`tools`、`reasoning`、`embeddings`)、监控账户余额并在无需重启服务的情况下即时对齐模型目录。
|
|
58
43
|
|
|
59
44
|
```mermaid
|
|
60
45
|
graph LR
|
|
61
46
|
subgraph Trigger [调度与手动触发]
|
|
62
|
-
Cron[⏰
|
|
47
|
+
Cron[⏰ 后台轮询调度器] --> Engine[dsh-model-sync 核心引擎]
|
|
63
48
|
WebUI[🖥️ 设置面板: 立即同步按钮] --> Engine
|
|
64
49
|
end
|
|
65
50
|
|
|
66
51
|
subgraph Providers [25+ 上游服务商]
|
|
67
|
-
Engine --> Registry{
|
|
68
|
-
Registry -->|Bearer
|
|
69
|
-
Registry -->|x-api-key
|
|
70
|
-
Registry -->|query-key
|
|
71
|
-
Registry
|
|
52
|
+
Engine --> Registry{适配器注册中心}
|
|
53
|
+
Registry -->|Bearer 认证| P1[OpenAI / DeepSeek / OpenRouter / Groq]
|
|
54
|
+
Registry -->|x-api-key 认证| P2[Anthropic Claude / 自定义网关]
|
|
55
|
+
Registry -->|query-key 认证| P3[Google Gemini]
|
|
56
|
+
Registry -->|本地探测| P4[本地 Ollama / vLLM / SGLang]
|
|
57
|
+
Registry -->|CommandCode 接口| P5[Command Code / visibleModels]
|
|
58
|
+
Registry -->|套餐检测| P6[ClineBot 订阅套餐]
|
|
72
59
|
end
|
|
73
60
|
|
|
74
|
-
subgraph Reconcile [
|
|
75
|
-
P1 --> Normalizer[
|
|
61
|
+
subgraph Reconcile [目录对齐与审计追踪]
|
|
62
|
+
P1 --> Normalizer[模型标准化器与能力打标]
|
|
76
63
|
P2 --> Normalizer
|
|
77
64
|
P3 --> Normalizer
|
|
78
65
|
P4 --> Normalizer
|
|
79
|
-
|
|
80
|
-
|
|
66
|
+
P5 --> Normalizer
|
|
67
|
+
P6 --> Normalizer
|
|
68
|
+
Normalizer --> Diff[差异跟踪: 新增 / 已废弃]
|
|
69
|
+
Diff --> Catalog[当前可用 DSH 模型目录与 visibleModels]
|
|
81
70
|
end
|
|
82
71
|
|
|
83
72
|
style Trigger fill:#1e1e2e,stroke:#89b4fa,stroke-width:2px,color:#cdd6f4
|
|
@@ -87,30 +76,81 @@ graph LR
|
|
|
87
76
|
|
|
88
77
|
---
|
|
89
78
|
|
|
90
|
-
##
|
|
79
|
+
## ✨ 核心特性
|
|
80
|
+
|
|
81
|
+
* 🔄 **自动化目录同步**:自动对齐 25+ 服务商的模型清单、别名、上下文窗口及能力标签(`vision`、`tools`、`reasoning`、`embeddings`)。
|
|
82
|
+
* 🛡️ **Minijinja 聊天模板与角色兼容性保障**:自动为 Groq 开源模型(Qwen、Llama、Mistral、Gemma、DeepSeek)及 DeepSeek API 注入 `compat.supportsDeveloperRole: false`,避免推理模型报 400 `Unexpected message role` 错误,同时保留用户自定义配置。
|
|
83
|
+
* 🌐 **内置 25+ 服务商适配器**:预置支持 OpenAI、Anthropic、Google、DeepSeek、xAI、OpenRouter、Groq、Mistral、Cerebras、Fireworks、HuggingFace、Moonshot、NVIDIA、Qwen、Together、Xiaomi MiMo、SiliconFlow、Command Code、ClineBot 以及本地 Ollama。
|
|
84
|
+
* 🎛️ **Command Code 模型精细管理**:完整发现 Command Code 模型目录,在 Web 界面提供勾选过滤,自动同步 `visibleModels` 至 `llm-commandcode` 设置中。
|
|
85
|
+
* 🎯 **ClineBot 订阅套餐精准匹配**:专属适配器直接调用 `GET /users/me/plan`,只拉取当前有效订阅套餐包含的模型,彻底解决全量拉取 400+ 外部模型导致的混淆。
|
|
86
|
+
* 🔌 **通用与自定义适配器扩展**:支持接入任意兼容 OpenAI `/v1/models` 规范的网关,支持多种认证方式(`bearer`、`x-api-key`、`query-key`、`none`)。
|
|
87
|
+
* 📊 **余额与用量监控**:实时查询上游计费端点,精准反馈账户额度并防止因欠费导致任务中断。
|
|
88
|
+
* 📜 **同步历史与变更审计**:完整记录新模型上线、旧模型废弃及能力变更的时间线与详细差异。
|
|
89
|
+
* 🖥️ **全功能 Web 管理面板 (**设置 → 模型同步**)**:
|
|
90
|
+
* 每个服务商的卡片化状态展示与模型计数;
|
|
91
|
+
* 一键即时同步(Sync Now);
|
|
92
|
+
* 模型勾选过滤与显示控制;
|
|
93
|
+
* 服务商启用开关及自定义 Endpoint 地址输入。
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 🛠️ 支持的服务商列表
|
|
98
|
+
|
|
99
|
+
| 服务商标识符 | 认证机制 | 能力自检与特性说明 |
|
|
100
|
+
|---|---|---|
|
|
101
|
+
| `openai` | Bearer Token | `vision`, `tools`, `reasoning`, `embeddings` |
|
|
102
|
+
| `anthropic` | `x-api-key` 请求头 | `vision`, `tools`, `reasoning` |
|
|
103
|
+
| `google` | Query 参数 / 密钥 | `vision`, `tools`, `reasoning`, `embeddings` |
|
|
104
|
+
| `deepseek` | Bearer Token | `tools`, `reasoning` |
|
|
105
|
+
| `xai` | Bearer Token | `vision`, `tools`, `reasoning` |
|
|
106
|
+
| `openrouter` | Bearer Token | 多厂商汇聚模型目录,包含计费与上下文规格 |
|
|
107
|
+
| `groq` | Bearer Token | `tools`, `reasoning`, `vision`, `supportsDeveloperRole: false` |
|
|
108
|
+
| `mistral` | Bearer Token | `tools`, `reasoning`, `vision` |
|
|
109
|
+
| `commandcode` | Bearer Token | 对接 `https://api.commandcode.ai/provider/v1/models`,同步勾选至 `llm-commandcode` 的 `visibleModels` |
|
|
110
|
+
| `clinebot` | Bearer Token | 仅同步有效订阅套餐 (`/users/me/plan`) 包含的模型 |
|
|
111
|
+
| `fireworks` | Bearer Token | 开源权重推理节点 |
|
|
112
|
+
| `huggingface` | Bearer Token | Serverless Inference API 目录 |
|
|
113
|
+
| `together` | Bearer Token | 开源大模型服务网络 |
|
|
114
|
+
| `ollama` | 本地 HTTP (`none`) | 本地离线模型实时发现 (`/api/tags`) |
|
|
115
|
+
| `custom` | 自定义配置 | 任意兼容 OpenAI 或 Google 规范的 API 网关 |
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 📦 快速安装
|
|
91
120
|
|
|
92
121
|
```bash
|
|
93
122
|
dsh plugin --profile web add @goodandready/dsh-model-sync
|
|
94
123
|
```
|
|
95
124
|
|
|
96
|
-
|
|
125
|
+
> [!IMPORTANT]
|
|
126
|
+
> 安装后请重启 DSH Web 界面 (`systemctl --user restart dsh-web`),以激活后台目录自动同步。
|
|
97
127
|
|
|
98
|
-
|
|
128
|
+
---
|
|
99
129
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
-
|
|
104
|
-
|
|
130
|
+
## ⚙️ 配置说明 (`settings.yaml`)
|
|
131
|
+
|
|
132
|
+
```yaml
|
|
133
|
+
dsh-model-sync:
|
|
134
|
+
enabled: true
|
|
135
|
+
syncIntervalMinutes: 60
|
|
136
|
+
autoReconcile: true
|
|
137
|
+
enableBalanceChecks: true
|
|
138
|
+
providers:
|
|
139
|
+
commandcode:
|
|
140
|
+
enabled: true
|
|
141
|
+
clinebot:
|
|
142
|
+
enabled: true
|
|
143
|
+
```
|
|
105
144
|
|
|
106
|
-
|
|
145
|
+
| 参数名称 | 类型 | 默认值 | 功能说明 |
|
|
146
|
+
|---|---|---|---|
|
|
147
|
+
| `enabled` | `boolean` | `true` | 后台自动同步主开关 |
|
|
148
|
+
| `syncIntervalMinutes` | `number` | `60` | 目录更新轮询间隔(分钟) |
|
|
149
|
+
| `autoReconcile` | `boolean` | `true` | 是否自动将新发现的模型生效至可用列表 |
|
|
150
|
+
| `enableBalanceChecks` | `boolean` | `true` | 在受支持的服务商处查询余额 |
|
|
151
|
+
| `providers.<id>.enabled`| `boolean` | `true` | 单独开启或关闭指定服务商的同步 |
|
|
107
152
|
|
|
108
|
-
|
|
109
|
-
- **发布隔离**: 通过 `.npmignore` 严格过滤测试、规划和文档文件,保证 npm 发布包体积小于 256 KiB。
|
|
110
|
-
- **模型批量可用性测试**: 支持通过 `POST /dsh-model-sync/batch-try` 并发批量测试所选模型,展示延迟徽标并一键取消选中不可用模型。
|
|
111
|
-
- **价格与上下文窗口过滤**: 支持按每百万 Token 最高价格及最小上下文长度过滤候选模型。
|
|
112
|
-
- **配置导入与导出**: 支持通过 `GET /export` 与 `POST /import` 导出及恢复全部策略、模型选择、别名和定时计划。
|
|
113
|
-
- **自定义模型别名映射**: 支持通过 `GET/POST /aliases` 为特定服务商与模型组合设置便于调用的简短别名。
|
|
153
|
+
---
|
|
114
154
|
|
|
115
155
|
## 📄 开源协议
|
|
116
156
|
|
package/lib/adapter-registry.js
CHANGED
|
@@ -1,14 +1,51 @@
|
|
|
1
1
|
import { catalogRequestError } from './reliability.js'
|
|
2
2
|
import { canUseGenericAdapter, createGenericAdapter } from './generic-adapter.js'
|
|
3
|
-
import { normalizeModels } from './models.js'
|
|
3
|
+
import { normalizeModels, parseClinePassModels } from './models.js'
|
|
4
4
|
|
|
5
5
|
const MAX_RESPONSE_BYTES = 4 * 1024 * 1024
|
|
6
6
|
const SAFE_AUTH = new Set(['bearer', 'x-api-key', 'query-key', 'none'])
|
|
7
|
-
const SAFE_PARSERS = new Set(['openai', 'google'])
|
|
7
|
+
const SAFE_PARSERS = new Set(['openai', 'google', 'clinebot-plan'])
|
|
8
8
|
const CAPABILITIES = new Set(['vision', 'tools', 'reasoning', 'embeddings', 'code'])
|
|
9
9
|
const BLOCKED_HEADERS = /authorization|api[_-]?key|token|secret|cookie/i
|
|
10
10
|
|
|
11
11
|
const PROVIDER_ADAPTERS = Object.freeze({
|
|
12
|
+
clinebot: {
|
|
13
|
+
endpoint: 'https://api.cline.bot/api/v1/users/me/plan',
|
|
14
|
+
auth: 'bearer',
|
|
15
|
+
parser: 'clinebot-plan',
|
|
16
|
+
defaultModels: [
|
|
17
|
+
'cline-pass/deepseek-v4-flash',
|
|
18
|
+
'cline-pass/deepseek-v4-pro',
|
|
19
|
+
'cline-pass/glm-5.2',
|
|
20
|
+
'cline-pass/kimi-k3',
|
|
21
|
+
'cline-pass/kimi-k2.7-code',
|
|
22
|
+
'cline-pass/kimi-k2.6',
|
|
23
|
+
'cline-pass/qwen3.7-max',
|
|
24
|
+
'cline-pass/qwen3.7-plus',
|
|
25
|
+
'cline-pass/minimax-m3',
|
|
26
|
+
'cline-pass/mimo-v2.5',
|
|
27
|
+
'cline-pass/mimo-v2.5-pro',
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
commandcode: {
|
|
31
|
+
endpoint: 'https://api.commandcode.ai/provider/v1/models',
|
|
32
|
+
auth: 'bearer',
|
|
33
|
+
fields: {
|
|
34
|
+
id: 'id',
|
|
35
|
+
name: 'name',
|
|
36
|
+
contextWindow: 'context_length',
|
|
37
|
+
},
|
|
38
|
+
defaultModels: [
|
|
39
|
+
'deepseek/deepseek-v4.1-flash',
|
|
40
|
+
'deepseek/deepseek-v4-flash',
|
|
41
|
+
'qwen/qwen3.8-flash',
|
|
42
|
+
'google/gemini-3.8-flash',
|
|
43
|
+
'meta/muse-spark-1.3-contributor',
|
|
44
|
+
'zai-org/glm-5.1',
|
|
45
|
+
'xiaomi/mimo-v2.5-pro',
|
|
46
|
+
'minimaxai/minimax-m3',
|
|
47
|
+
],
|
|
48
|
+
},
|
|
12
49
|
anthropic: { endpoint: 'https://api.anthropic.com/v1/models', auth: 'x-api-key', headers: { 'anthropic-version': '2023-06-01' } },
|
|
13
50
|
google: { endpoint: 'https://generativelanguage.googleapis.com/v1beta/models', auth: 'query-key', parse: 'google' },
|
|
14
51
|
openai: { endpoint: 'https://api.openai.com/v1/models', auth: 'bearer' },
|
|
@@ -62,6 +99,11 @@ function mappedRows(provider, rows, descriptor) {
|
|
|
62
99
|
}
|
|
63
100
|
|
|
64
101
|
function parseRows(provider, payload, descriptor) {
|
|
102
|
+
if (descriptor.parse === 'clinebot-plan') {
|
|
103
|
+
const plan = payload?.data?.plan || payload?.data || payload?.plan || payload
|
|
104
|
+
const included = plan?.features?.included || plan?.includedModels || []
|
|
105
|
+
return parseClinePassModels(included)
|
|
106
|
+
}
|
|
65
107
|
if (descriptor.parse === 'google') {
|
|
66
108
|
const rows = Array.isArray(payload?.models) ? payload.models : []
|
|
67
109
|
return normalizeModels(provider, rows.map((row) => ({
|
package/lib/inventory.js
CHANGED
|
@@ -5,6 +5,7 @@ export const API_KEY_PROVIDER_IDS = Object.freeze([
|
|
|
5
5
|
'ant-ling',
|
|
6
6
|
'azure-openai-responses',
|
|
7
7
|
'cerebras',
|
|
8
|
+
'commandcode',
|
|
8
9
|
'cloudflare-ai-gateway',
|
|
9
10
|
'cloudflare-workers-ai',
|
|
10
11
|
'deepseek',
|
|
@@ -63,7 +64,18 @@ export function listApiKeyProviders(llm, settings) {
|
|
|
63
64
|
const out = []
|
|
64
65
|
const seen = new Set()
|
|
65
66
|
for (const entry of directory) {
|
|
66
|
-
|
|
67
|
+
let profile = profiles[entry.provider]
|
|
68
|
+
if (!profile && (entry.provider === 'commandcode' || entry.settingsNs === 'llm-commandcode')) {
|
|
69
|
+
const cc = settings?.get?.('llm-commandcode')
|
|
70
|
+
profile = {
|
|
71
|
+
apiKeyEnv: cc?.apiKeyEnv ?? 'COMMANDCODE_API_KEY',
|
|
72
|
+
credentialRef: cc?.apiKeyEnv ?? 'COMMANDCODE_API_KEY',
|
|
73
|
+
baseURL: cc?.apiBase ?? 'https://api.commandcode.ai',
|
|
74
|
+
models: cc?.visibleModels ?? [],
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
profile = profile ?? {}
|
|
78
|
+
}
|
|
67
79
|
if (!isApiKeyProvider(entry, profile) || seen.has(entry.provider)) continue
|
|
68
80
|
seen.add(entry.provider)
|
|
69
81
|
out.push({
|
package/lib/models.js
CHANGED
|
@@ -380,3 +380,153 @@ export function diffModels(previous, next) {
|
|
|
380
380
|
hasChanges: added.length > 0 || removed.length > 0 || renamed.length > 0 || changed.length > 0,
|
|
381
381
|
}
|
|
382
382
|
}
|
|
383
|
+
|
|
384
|
+
export const CLINEPASS_MODELS = Object.freeze([
|
|
385
|
+
{
|
|
386
|
+
id: 'cline-pass/deepseek-v4-flash',
|
|
387
|
+
name: 'DeepSeek V4 Flash',
|
|
388
|
+
contextWindow: 200000,
|
|
389
|
+
maxTokens: 8192,
|
|
390
|
+
capabilities: { vision: true, code: true, reasoning: true },
|
|
391
|
+
input: ['text', 'image'],
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
id: 'cline-pass/deepseek-v4-pro',
|
|
395
|
+
name: 'DeepSeek V4 Pro',
|
|
396
|
+
contextWindow: 200000,
|
|
397
|
+
maxTokens: 8192,
|
|
398
|
+
capabilities: { vision: true, code: true, reasoning: true },
|
|
399
|
+
input: ['text', 'image'],
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
id: 'cline-pass/glm-5.2',
|
|
403
|
+
name: 'GLM 5.2',
|
|
404
|
+
contextWindow: 200000,
|
|
405
|
+
maxTokens: 8192,
|
|
406
|
+
capabilities: { code: true },
|
|
407
|
+
input: ['text'],
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
id: 'cline-pass/kimi-k3',
|
|
411
|
+
name: 'Kimi K3',
|
|
412
|
+
contextWindow: 200000,
|
|
413
|
+
maxTokens: 8192,
|
|
414
|
+
capabilities: { reasoning: true },
|
|
415
|
+
input: ['text'],
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
id: 'cline-pass/kimi-k2.7-code',
|
|
419
|
+
name: 'Kimi K2.7 Code',
|
|
420
|
+
contextWindow: 200000,
|
|
421
|
+
maxTokens: 8192,
|
|
422
|
+
capabilities: { code: true, tools: true },
|
|
423
|
+
input: ['text'],
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
id: 'cline-pass/kimi-k2.6',
|
|
427
|
+
name: 'Kimi K2.6',
|
|
428
|
+
contextWindow: 200000,
|
|
429
|
+
maxTokens: 8192,
|
|
430
|
+
capabilities: { code: true },
|
|
431
|
+
input: ['text'],
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
id: 'cline-pass/qwen3.7-max',
|
|
435
|
+
name: 'Qwen 3.7 Max',
|
|
436
|
+
contextWindow: 200000,
|
|
437
|
+
maxTokens: 8192,
|
|
438
|
+
capabilities: { vision: true },
|
|
439
|
+
input: ['text', 'image'],
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
id: 'cline-pass/qwen3.7-plus',
|
|
443
|
+
name: 'Qwen 3.7 Plus',
|
|
444
|
+
contextWindow: 200000,
|
|
445
|
+
maxTokens: 8192,
|
|
446
|
+
capabilities: { vision: true },
|
|
447
|
+
input: ['text', 'image'],
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
id: 'cline-pass/minimax-m3',
|
|
451
|
+
name: 'MiniMax M3',
|
|
452
|
+
contextWindow: 200000,
|
|
453
|
+
maxTokens: 8192,
|
|
454
|
+
capabilities: { reasoning: true },
|
|
455
|
+
input: ['text'],
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
id: 'cline-pass/mimo-v2.5',
|
|
459
|
+
name: 'MiMo V2.5',
|
|
460
|
+
contextWindow: 200000,
|
|
461
|
+
maxTokens: 8192,
|
|
462
|
+
capabilities: { code: true },
|
|
463
|
+
input: ['text'],
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
id: 'cline-pass/mimo-v2.5-pro',
|
|
467
|
+
name: 'MiMo V2.5 Pro',
|
|
468
|
+
contextWindow: 200000,
|
|
469
|
+
maxTokens: 8192,
|
|
470
|
+
capabilities: { code: true, reasoning: true },
|
|
471
|
+
input: ['text'],
|
|
472
|
+
},
|
|
473
|
+
])
|
|
474
|
+
|
|
475
|
+
export function parseClinePassModels(includedInput) {
|
|
476
|
+
if (!includedInput) return [...CLINEPASS_MODELS]
|
|
477
|
+
let includedText = ''
|
|
478
|
+
if (Array.isArray(includedInput)) {
|
|
479
|
+
const foundStr = includedInput.find((item) => typeof item === 'string' && /includes\s+/i.test(item))
|
|
480
|
+
if (foundStr) {
|
|
481
|
+
includedText = foundStr
|
|
482
|
+
} else {
|
|
483
|
+
includedText = includedInput.filter((x) => typeof x === 'string').join(', ')
|
|
484
|
+
}
|
|
485
|
+
} else if (typeof includedInput === 'string') {
|
|
486
|
+
includedText = includedInput
|
|
487
|
+
} else {
|
|
488
|
+
return [...CLINEPASS_MODELS]
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const clean = includedText
|
|
492
|
+
.replace(/^includes\s+/i, '')
|
|
493
|
+
.replace(/\band\b/gi, ',')
|
|
494
|
+
.replace(/\./g, '')
|
|
495
|
+
.trim()
|
|
496
|
+
|
|
497
|
+
const parts = clean
|
|
498
|
+
.split(',')
|
|
499
|
+
.map((p) => p.trim())
|
|
500
|
+
.filter(Boolean)
|
|
501
|
+
|
|
502
|
+
if (!parts.length) return [...CLINEPASS_MODELS]
|
|
503
|
+
|
|
504
|
+
const normalize = (s) =>
|
|
505
|
+
String(s || '')
|
|
506
|
+
.toLowerCase()
|
|
507
|
+
.replace(/[\s._-]+/g, '')
|
|
508
|
+
|
|
509
|
+
const matched = []
|
|
510
|
+
for (const name of parts) {
|
|
511
|
+
const targetNorm = normalize(name)
|
|
512
|
+
const found = CLINEPASS_MODELS.find(
|
|
513
|
+
(m) =>
|
|
514
|
+
normalize(m.name) === targetNorm ||
|
|
515
|
+
normalize(m.id.replace(/^cline-pass\//, '')) === targetNorm
|
|
516
|
+
)
|
|
517
|
+
if (found) {
|
|
518
|
+
matched.push(found)
|
|
519
|
+
} else {
|
|
520
|
+
const idPart = name.toLowerCase().replace(/[\s_]+/g, '-')
|
|
521
|
+
matched.push({
|
|
522
|
+
id: `cline-pass/${idPart}`,
|
|
523
|
+
name,
|
|
524
|
+
contextWindow: 200000,
|
|
525
|
+
maxTokens: 8192,
|
|
526
|
+
capabilities: {},
|
|
527
|
+
input: ['text'],
|
|
528
|
+
})
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return normalizeModels('clinebot', matched.length ? matched : [...CLINEPASS_MODELS])
|
|
532
|
+
}
|
|
@@ -53,7 +53,20 @@ export function descriptorOf(settings, ns) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export function profileOf(settings, provider) {
|
|
56
|
-
try {
|
|
56
|
+
try {
|
|
57
|
+
const piai = settings?.get(PIAI_NS)?.providers?.[provider]
|
|
58
|
+
if (piai) return piai
|
|
59
|
+
if (provider === 'commandcode') {
|
|
60
|
+
const cc = settings?.get?.('llm-commandcode')
|
|
61
|
+
return {
|
|
62
|
+
apiKeyEnv: cc?.apiKeyEnv ?? 'COMMANDCODE_API_KEY',
|
|
63
|
+
credentialRef: cc?.apiKeyEnv ?? 'COMMANDCODE_API_KEY',
|
|
64
|
+
baseURL: cc?.apiBase ?? 'https://api.commandcode.ai',
|
|
65
|
+
models: cc?.visibleModels ?? [],
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {}
|
|
69
|
+
}
|
|
57
70
|
catch { return {} }
|
|
58
71
|
}
|
|
59
72
|
|
package/lib/synchronizer.js
CHANGED
|
@@ -369,13 +369,24 @@ export function createModelSynchronizer(ctx, { getConfig = () => ({}), saveConfi
|
|
|
369
369
|
const resultsToApply = results.filter((row) => row.status === 'ok' && row.changed && (!dryRun || providerAutoApply(row.provider)))
|
|
370
370
|
if (resultsToApply.length > 0) {
|
|
371
371
|
const settings = settingsOf(ctx)
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
372
|
+
const piaiRows = resultsToApply.filter((r) => r.provider !== 'commandcode')
|
|
373
|
+
const ccRows = resultsToApply.filter((r) => r.provider === 'commandcode')
|
|
374
|
+
if (piaiRows.length > 0) {
|
|
375
|
+
await replaceWithRetry(settings, PIAI_NS, (section) => {
|
|
376
|
+
let next = section
|
|
377
|
+
for (const row of piaiRows) {
|
|
378
|
+
next = catalogPatch(next, row.provider, row.next)
|
|
379
|
+
}
|
|
380
|
+
return next
|
|
381
|
+
})
|
|
382
|
+
}
|
|
383
|
+
for (const row of ccRows) {
|
|
384
|
+
if (typeof settings?.mutate === 'function') {
|
|
385
|
+
await settings.mutate('llm-commandcode', [
|
|
386
|
+
{ op: 'set', path: ['visibleModels'], value: row.selectedModels?.length ? row.selectedModels : row.next.map(m => m.id) }
|
|
387
|
+
])
|
|
376
388
|
}
|
|
377
|
-
|
|
378
|
-
})
|
|
389
|
+
}
|
|
379
390
|
result.applied = true
|
|
380
391
|
}
|
|
381
392
|
if (!dryRun || resultsToApply.length > 0) {
|
|
@@ -500,9 +511,6 @@ export function createModelSynchronizer(ctx, { getConfig = () => ({}), saveConfi
|
|
|
500
511
|
const settings = settingsOf(ctx)
|
|
501
512
|
const section = settings?.get?.(PIAI_NS)
|
|
502
513
|
const descriptor = descriptorOf(settings, PIAI_NS)
|
|
503
|
-
if (!section || !descriptor || typeof settings.replace !== 'function') {
|
|
504
|
-
throw Object.assign(new Error('llm-pi-ai settings are not writable'), { code: 'SETTINGS_UNAVAILABLE' })
|
|
505
|
-
}
|
|
506
514
|
const current = normalizeModels(provider, profileOf(settings, provider).models ?? row.models)
|
|
507
515
|
const nextModels = selectedModels.length
|
|
508
516
|
? selectedModels.map((id) => availableById.get(id))
|
|
@@ -510,11 +518,24 @@ export function createModelSynchronizer(ctx, { getConfig = () => ({}), saveConfi
|
|
|
510
518
|
const previousModelSelections = structuredClone(config.modelSelections ?? {})
|
|
511
519
|
const modelSelections = { ...previousModelSelections, [provider]: selectedModels }
|
|
512
520
|
|
|
513
|
-
// Persist the allowlist first.
|
|
514
|
-
// allowlist back so the two namespaces cannot be left disagreeing.
|
|
521
|
+
// Persist the allowlist first.
|
|
515
522
|
await saveConfig({ modelSelections })
|
|
516
523
|
try {
|
|
517
|
-
|
|
524
|
+
if (provider === 'commandcode') {
|
|
525
|
+
if (typeof settings?.mutate === 'function') {
|
|
526
|
+
await settings.mutate('llm-commandcode', [
|
|
527
|
+
{ op: 'set', path: ['visibleModels'], value: selectedModels }
|
|
528
|
+
])
|
|
529
|
+
}
|
|
530
|
+
if (section?.providers?.commandcode && typeof settings?.replace === 'function') {
|
|
531
|
+
await replaceWithRetry(settings, PIAI_NS, (sec) => catalogPatch(sec, provider, nextModels))
|
|
532
|
+
}
|
|
533
|
+
} else {
|
|
534
|
+
if (!section || !descriptor || typeof settings.replace !== 'function') {
|
|
535
|
+
throw Object.assign(new Error('llm-pi-ai settings are not writable'), { code: 'SETTINGS_UNAVAILABLE' })
|
|
536
|
+
}
|
|
537
|
+
await replaceWithRetry(settings, PIAI_NS, (sec) => catalogPatch(sec, provider, nextModels))
|
|
538
|
+
}
|
|
518
539
|
} catch (error) {
|
|
519
540
|
try {
|
|
520
541
|
await saveConfig({ modelSelections: previousModelSelections })
|