@goodandready/dsh-clinebot 0.5.19 → 0.5.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/lib/client.js +103 -23
- package/lib/cline-client.js +21 -2
- package/lib/models.js +83 -136
- package/lib/provider-sync.js +21 -0
- package/lib/proxy-token.js +2 -2
- package/lib/routes/models.js +35 -6
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,22 @@ All notable changes to `@goodandready/dsh-clinebot` will be documented in this f
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.21] - 2026-10-12
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Configurable Model Max Output Tokens (maxTokens) & Form Controls (Issue #216, GH #13)**: Exposed editable `maxTokens` (Max Output Tokens) in the Models settings UI alongside context window, complete with preset Default/Original buttons, input controls, and badge status displays. Added context length and max output token configuration fields to the Custom Models form.
|
|
12
|
+
- **Wildcard Override Defaults and Apply-to-All Action (Issue #216, GH #13)**: Implemented wildcard model context override (`modelId: '*'`) in `getAllModels` and an "All" action in the settings UI calling `/dsh-clinebot/models/context` (`mode: 'apply-all'`) to batch-apply preferred context window and max output tokens across all models.
|
|
13
|
+
- **Authentic Vendor Model Limits (Issue #216, GH #13)**: Added accurate `originalMaxTokens` specifications across models (Qwen 131072, GLM 128000, MiniMax 128000, Kimi 65536, DeepSeek-V4.1 65536, MiMo 16384) and updated original context windows for DeepSeek-V4.1 and GLM-5.3.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- **Preservation of Manual Provider Token Limits Across Sync (Issue #216, GH #13)**: `buildPiAiProvider` and `upsertPiAiProvider` now query existing provider models from `llm-pi-ai.providers.clinebot.models` and preserve user edits to `contextWindow` and `maxTokens` during dynamic catalog synchronization when not explicitly overridden by plugin configuration.
|
|
17
|
+
- **Context Override Detection for Output Limits (Issue #216, GH #13)**: Updated `isContextOverridden` calculation to consider custom `maxTokens` deviations (`activeMaxTokens !== defaultMaxTokens`), properly reflecting override badges in the UI.
|
|
18
|
+
|
|
19
|
+
## [0.5.20] - 2026-10-11
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- **Local ESLint Binary Execution and devDependencies Enforcement (Issue #214)**: Added `eslint` (`^9.39.5`) to `devDependencies` in `package.json` and committed `package-lock.json`. Updated `scripts/build-client.js` to strictly enforce local binary execution (`node_modules/.bin/eslint`) and immediately abort with exit code 1 if `eslint` is missing, preventing silent bypass of `no-undef` validation on fresh clones and CI environments. Added automated decomposition test in `test/decomposition.test.js` validating local `eslint` presence and zero `no-undef` violations.
|
|
23
|
+
|
|
8
24
|
## [0.5.19] - 2026-10-07
|
|
9
25
|
|
|
10
26
|
### Added
|
package/lib/client.js
CHANGED
|
@@ -147,12 +147,18 @@ const en = {
|
|
|
147
147
|
'models.apply_original_all': '⚡ Original Specs',
|
|
148
148
|
'models.reset_contexts_all': '↺ Reset Contexts',
|
|
149
149
|
'models.context_updated_msg': 'Model context limits updated and synchronized to DSH.',
|
|
150
|
-
'models.ctx_default': 'Default ({ctx})',
|
|
151
|
-
'models.ctx_original': 'Original ({ctx} · {provider})',
|
|
152
|
-
'models.ctx_edit': 'Edit
|
|
150
|
+
'models.ctx_default': 'Default ({ctx} / {max})',
|
|
151
|
+
'models.ctx_original': 'Original ({ctx} / {max} · {provider})',
|
|
152
|
+
'models.ctx_edit': 'Edit Limits',
|
|
153
|
+
'models.ctx_window_label': 'Context (e.g. 200000)',
|
|
154
|
+
'models.ctx_max_tokens_label': 'Max Out (e.g. 16384)',
|
|
155
|
+
'models.ctx_apply_all': 'All',
|
|
156
|
+
'models.ctx_apply_all_title': 'Apply these limits across all models',
|
|
153
157
|
'models.ctx_custom_placeholder': 'Tokens (e.g. 2000000)',
|
|
154
158
|
'models.ctx_save': 'Save',
|
|
155
159
|
'models.ctx_cancel': 'Cancel',
|
|
160
|
+
'models.custom_ctx_label': 'Context',
|
|
161
|
+
'models.custom_max_label': 'Max Out',
|
|
156
162
|
'models.ctx_tag_original': 'Original',
|
|
157
163
|
'models.ctx_tag_custom': 'Custom',
|
|
158
164
|
'stats.title': '📈 Current DSH Session Telemetry',
|
|
@@ -304,12 +310,18 @@ const en = {
|
|
|
304
310
|
'models.apply_original_all': '⚡ 原厂规格',
|
|
305
311
|
'models.reset_contexts_all': '↺ 重置上下文',
|
|
306
312
|
'models.context_updated_msg': '模型上下文限制已更新并同步生效至 DSH。',
|
|
307
|
-
'models.ctx_default': '默认 ({ctx})',
|
|
308
|
-
'models.ctx_original': '原厂 ({ctx} · {provider})',
|
|
309
|
-
'models.ctx_edit': '
|
|
313
|
+
'models.ctx_default': '默认 ({ctx} / {max})',
|
|
314
|
+
'models.ctx_original': '原厂 ({ctx} / {max} · {provider})',
|
|
315
|
+
'models.ctx_edit': '编辑限制',
|
|
316
|
+
'models.ctx_window_label': '上下文 (如 200000)',
|
|
317
|
+
'models.ctx_max_tokens_label': '最大输出 (如 16384)',
|
|
318
|
+
'models.ctx_apply_all': '全部',
|
|
319
|
+
'models.ctx_apply_all_title': '将此限制批量应用到所有模型',
|
|
310
320
|
'models.ctx_custom_placeholder': 'Token 数量 (如 2000000)',
|
|
311
321
|
'models.ctx_save': '保存',
|
|
312
322
|
'models.ctx_cancel': '取消',
|
|
323
|
+
'models.custom_ctx_label': '上下文',
|
|
324
|
+
'models.custom_max_label': '最大输出',
|
|
313
325
|
'models.ctx_tag_original': '原厂',
|
|
314
326
|
'models.ctx_tag_custom': '自定义',
|
|
315
327
|
'stats.title': '📈 当前 DSH 会话请求遥测',
|
|
@@ -1219,17 +1231,21 @@ function QuotaSection({ keyPresent, status, usage, busy, handleRefreshQuota, t }
|
|
|
1219
1231
|
function CustomModelsSub({ customModels = [], onConfigUpdate, busy, t }) {
|
|
1220
1232
|
const [newId, setNewId] = React.useState('')
|
|
1221
1233
|
const [newName, setNewName] = React.useState('')
|
|
1234
|
+
const [newCtx, setNewCtx] = React.useState('')
|
|
1235
|
+
const [newMax, setNewMax] = React.useState('')
|
|
1222
1236
|
const [isExpanded, setIsExpanded] = React.useState(false)
|
|
1223
1237
|
|
|
1224
1238
|
function handleAdd() {
|
|
1225
1239
|
const trimmedId = newId.trim()
|
|
1226
1240
|
const trimmedName = newName.trim() || trimmedId
|
|
1227
1241
|
if (!trimmedId) return
|
|
1242
|
+
const parsedCtx = parseInt(newCtx, 10)
|
|
1243
|
+
const parsedMax = parseInt(newMax, 10)
|
|
1228
1244
|
const nextList = [...customModels.filter((m) => m.id !== trimmedId), {
|
|
1229
1245
|
id: trimmedId,
|
|
1230
1246
|
name: trimmedName,
|
|
1231
|
-
contextLength: 128000,
|
|
1232
|
-
maxTokens: 8192,
|
|
1247
|
+
contextLength: parsedCtx > 0 ? parsedCtx : 128000,
|
|
1248
|
+
maxTokens: parsedMax > 0 ? parsedMax : 8192,
|
|
1233
1249
|
category: 'general',
|
|
1234
1250
|
isCustom: true,
|
|
1235
1251
|
}]
|
|
@@ -1238,6 +1254,8 @@ function CustomModelsSub({ customModels = [], onConfigUpdate, busy, t }) {
|
|
|
1238
1254
|
}
|
|
1239
1255
|
setNewId('')
|
|
1240
1256
|
setNewName('')
|
|
1257
|
+
setNewCtx('')
|
|
1258
|
+
setNewMax('')
|
|
1241
1259
|
}
|
|
1242
1260
|
|
|
1243
1261
|
function handleDelete(id) {
|
|
@@ -1288,7 +1306,7 @@ function CustomModelsSub({ customModels = [], onConfigUpdate, busy, t }) {
|
|
|
1288
1306
|
React.createElement('input', {
|
|
1289
1307
|
type: 'text',
|
|
1290
1308
|
className: 'cb-input',
|
|
1291
|
-
style: { flex: '
|
|
1309
|
+
style: { flex: '2 1 140px' },
|
|
1292
1310
|
placeholder: t('models.custom_id'),
|
|
1293
1311
|
value: newId,
|
|
1294
1312
|
disabled: !!busy,
|
|
@@ -1297,12 +1315,32 @@ function CustomModelsSub({ customModels = [], onConfigUpdate, busy, t }) {
|
|
|
1297
1315
|
React.createElement('input', {
|
|
1298
1316
|
type: 'text',
|
|
1299
1317
|
className: 'cb-input',
|
|
1300
|
-
style: { flex: '
|
|
1318
|
+
style: { flex: '2 1 140px' },
|
|
1301
1319
|
placeholder: t('models.custom_name'),
|
|
1302
1320
|
value: newName,
|
|
1303
1321
|
disabled: !!busy,
|
|
1304
1322
|
onChange: (e) => setNewName(e.target.value),
|
|
1305
1323
|
}),
|
|
1324
|
+
React.createElement('input', {
|
|
1325
|
+
type: 'number',
|
|
1326
|
+
className: 'cb-input',
|
|
1327
|
+
style: { flex: '1 1 90px' },
|
|
1328
|
+
placeholder: t('models.custom_ctx_label'),
|
|
1329
|
+
title: t('models.custom_ctx_label'),
|
|
1330
|
+
value: newCtx,
|
|
1331
|
+
disabled: !!busy,
|
|
1332
|
+
onChange: (e) => setNewCtx(e.target.value),
|
|
1333
|
+
}),
|
|
1334
|
+
React.createElement('input', {
|
|
1335
|
+
type: 'number',
|
|
1336
|
+
className: 'cb-input',
|
|
1337
|
+
style: { flex: '1 1 80px' },
|
|
1338
|
+
placeholder: t('models.custom_max_label'),
|
|
1339
|
+
title: t('models.custom_max_label'),
|
|
1340
|
+
value: newMax,
|
|
1341
|
+
disabled: !!busy,
|
|
1342
|
+
onChange: (e) => setNewMax(e.target.value),
|
|
1343
|
+
}),
|
|
1306
1344
|
React.createElement(
|
|
1307
1345
|
'button',
|
|
1308
1346
|
{
|
|
@@ -1338,7 +1376,8 @@ function CustomModelsSub({ customModels = [], onConfigUpdate, busy, t }) {
|
|
|
1338
1376
|
'div',
|
|
1339
1377
|
null,
|
|
1340
1378
|
React.createElement('span', { style: { fontWeight: 500 } }, cm.name || cm.id),
|
|
1341
|
-
React.createElement('span', { style: { color: 'var(--dsw-alias-label-secondary)', marginLeft: '8px' } }, `(${cm.id})`)
|
|
1379
|
+
React.createElement('span', { style: { color: 'var(--dsw-alias-label-secondary)', marginLeft: '8px' } }, `(${cm.id})`),
|
|
1380
|
+
React.createElement('span', { style: { color: 'var(--dsw-alias-label-secondary)', marginLeft: '8px', fontSize: '11px' } }, `[${cm.contextLength || 128000} / ${cm.maxTokens || 8192}]`)
|
|
1342
1381
|
),
|
|
1343
1382
|
React.createElement(
|
|
1344
1383
|
'button',
|
|
@@ -1385,6 +1424,7 @@ function ModelsSection({
|
|
|
1385
1424
|
const [viewFilter, setViewFilter] = React.useState('all')
|
|
1386
1425
|
const [editingModelId, setEditingModelId] = React.useState(null)
|
|
1387
1426
|
const [customCtxVal, setCustomCtxVal] = React.useState('')
|
|
1427
|
+
const [customMaxVal, setCustomMaxVal] = React.useState('')
|
|
1388
1428
|
|
|
1389
1429
|
function formatCtx(num) {
|
|
1390
1430
|
num = Number(num) || 200000
|
|
@@ -1580,9 +1620,11 @@ function ModelsSection({
|
|
|
1580
1620
|
const isEnabled = !disabledSet.has(m.id)
|
|
1581
1621
|
const defaultCtx = m.defaultContextLength || m.clineContextLength || 200000
|
|
1582
1622
|
const origCtx = m.originalContextLength || 200000
|
|
1623
|
+
const defaultMax = m.defaultMaxTokens || 8192
|
|
1624
|
+
const origMax = m.originalMaxTokens || defaultMax
|
|
1583
1625
|
const origProv = m.originalProvider || 'Original'
|
|
1584
1626
|
const isOverridden = Boolean(m.isContextOverridden)
|
|
1585
|
-
const isOriginal = isOverridden && (m.contextLength === origCtx)
|
|
1627
|
+
const isOriginal = isOverridden && (m.contextLength === origCtx && (m.maxTokens || defaultMax) === origMax)
|
|
1586
1628
|
|
|
1587
1629
|
return React.createElement(
|
|
1588
1630
|
'tr',
|
|
@@ -1641,7 +1683,7 @@ function ModelsSection({
|
|
|
1641
1683
|
setEditingModelId(null)
|
|
1642
1684
|
},
|
|
1643
1685
|
},
|
|
1644
|
-
t('models.ctx_default', { ctx: formatCtx(defaultCtx) })
|
|
1686
|
+
t('models.ctx_default', { ctx: formatCtx(defaultCtx), max: formatCtx(defaultMax) })
|
|
1645
1687
|
),
|
|
1646
1688
|
React.createElement(
|
|
1647
1689
|
'button',
|
|
@@ -1651,41 +1693,76 @@ function ModelsSection({
|
|
|
1651
1693
|
style: { padding: '2px 6px', fontSize: '11px' },
|
|
1652
1694
|
onClick: () => {
|
|
1653
1695
|
if (typeof handleUpdateModelContext === 'function') {
|
|
1654
|
-
handleUpdateModelContext({ modelId: m.id, contextLength: origCtx })
|
|
1696
|
+
handleUpdateModelContext({ modelId: m.id, contextLength: origCtx, maxTokens: origMax })
|
|
1655
1697
|
}
|
|
1656
1698
|
setEditingModelId(null)
|
|
1657
1699
|
},
|
|
1658
1700
|
},
|
|
1659
|
-
t('models.ctx_original', { ctx: formatCtx(origCtx), provider: origProv })
|
|
1701
|
+
t('models.ctx_original', { ctx: formatCtx(origCtx), max: formatCtx(origMax), provider: origProv })
|
|
1660
1702
|
)
|
|
1661
1703
|
),
|
|
1662
1704
|
React.createElement(
|
|
1663
1705
|
'div',
|
|
1664
|
-
{ style: { display: 'flex', gap: '4px', alignItems: 'center' } },
|
|
1706
|
+
{ style: { display: 'flex', gap: '4px', alignItems: 'center', flexWrap: 'wrap' } },
|
|
1665
1707
|
React.createElement('input', {
|
|
1666
1708
|
type: 'number',
|
|
1667
1709
|
className: 'cb-input',
|
|
1668
|
-
style: { height: '24px', fontSize: '11px', padding: '0 6px', width: '
|
|
1669
|
-
placeholder: t('models.
|
|
1710
|
+
style: { height: '24px', fontSize: '11px', padding: '0 6px', width: '90px' },
|
|
1711
|
+
placeholder: t('models.ctx_window_label'),
|
|
1712
|
+
title: t('models.ctx_window_label'),
|
|
1670
1713
|
value: customCtxVal,
|
|
1671
1714
|
onChange: (e) => setCustomCtxVal(e.target.value),
|
|
1672
1715
|
}),
|
|
1716
|
+
React.createElement('input', {
|
|
1717
|
+
type: 'number',
|
|
1718
|
+
className: 'cb-input',
|
|
1719
|
+
style: { height: '24px', fontSize: '11px', padding: '0 6px', width: '75px' },
|
|
1720
|
+
placeholder: t('models.ctx_max_tokens_label'),
|
|
1721
|
+
title: t('models.ctx_max_tokens_label'),
|
|
1722
|
+
value: customMaxVal,
|
|
1723
|
+
onChange: (e) => setCustomMaxVal(e.target.value),
|
|
1724
|
+
}),
|
|
1673
1725
|
React.createElement(
|
|
1674
1726
|
'button',
|
|
1675
1727
|
{
|
|
1676
1728
|
type: 'button',
|
|
1677
1729
|
className: 'cb-btn cb-btn-primary',
|
|
1678
|
-
style: { padding: '2px
|
|
1730
|
+
style: { padding: '2px 6px', fontSize: '11px' },
|
|
1679
1731
|
onClick: () => {
|
|
1680
|
-
const
|
|
1681
|
-
|
|
1682
|
-
|
|
1732
|
+
const cVal = parseInt(customCtxVal, 10)
|
|
1733
|
+
const mVal = parseInt(customMaxVal, 10)
|
|
1734
|
+
if (typeof handleUpdateModelContext === 'function') {
|
|
1735
|
+
const payload = { modelId: m.id }
|
|
1736
|
+
if (cVal > 0) payload.contextLength = cVal
|
|
1737
|
+
if (mVal > 0) payload.maxTokens = mVal
|
|
1738
|
+
handleUpdateModelContext(payload)
|
|
1683
1739
|
}
|
|
1684
1740
|
setEditingModelId(null)
|
|
1685
1741
|
},
|
|
1686
1742
|
},
|
|
1687
1743
|
t('models.ctx_save')
|
|
1688
1744
|
),
|
|
1745
|
+
React.createElement(
|
|
1746
|
+
'button',
|
|
1747
|
+
{
|
|
1748
|
+
type: 'button',
|
|
1749
|
+
className: 'cb-btn',
|
|
1750
|
+
title: t('models.ctx_apply_all_title'),
|
|
1751
|
+
style: { padding: '2px 5px', fontSize: '11px' },
|
|
1752
|
+
onClick: () => {
|
|
1753
|
+
const cVal = parseInt(customCtxVal, 10)
|
|
1754
|
+
const mVal = parseInt(customMaxVal, 10)
|
|
1755
|
+
if (typeof handleUpdateModelContext === 'function') {
|
|
1756
|
+
const payload = { mode: 'apply-all' }
|
|
1757
|
+
if (cVal > 0) payload.contextLength = cVal
|
|
1758
|
+
if (mVal > 0) payload.maxTokens = mVal
|
|
1759
|
+
handleUpdateModelContext(payload)
|
|
1760
|
+
}
|
|
1761
|
+
setEditingModelId(null)
|
|
1762
|
+
},
|
|
1763
|
+
},
|
|
1764
|
+
t('models.ctx_apply_all')
|
|
1765
|
+
),
|
|
1689
1766
|
React.createElement(
|
|
1690
1767
|
'button',
|
|
1691
1768
|
{
|
|
@@ -1707,10 +1784,11 @@ function ModelsSection({
|
|
|
1707
1784
|
style: { fontWeight: '600', cursor: 'pointer' },
|
|
1708
1785
|
onClick: () => {
|
|
1709
1786
|
setCustomCtxVal(String(m.contextLength || defaultCtx))
|
|
1787
|
+
setCustomMaxVal(String(m.maxTokens || defaultMax))
|
|
1710
1788
|
setEditingModelId(m.id)
|
|
1711
1789
|
},
|
|
1712
1790
|
},
|
|
1713
|
-
formatCtx(m.contextLength || defaultCtx)
|
|
1791
|
+
`${formatCtx(m.contextLength || defaultCtx)} / ${formatCtx(m.maxTokens || defaultMax)}`
|
|
1714
1792
|
),
|
|
1715
1793
|
isOverridden
|
|
1716
1794
|
? React.createElement(
|
|
@@ -1720,6 +1798,7 @@ function ModelsSection({
|
|
|
1720
1798
|
style: { fontSize: '10px', padding: '1px 6px', cursor: 'pointer' },
|
|
1721
1799
|
onClick: () => {
|
|
1722
1800
|
setCustomCtxVal(String(m.contextLength || defaultCtx))
|
|
1801
|
+
setCustomMaxVal(String(m.maxTokens || defaultMax))
|
|
1723
1802
|
setEditingModelId(m.id)
|
|
1724
1803
|
},
|
|
1725
1804
|
},
|
|
@@ -1735,6 +1814,7 @@ function ModelsSection({
|
|
|
1735
1814
|
title: t('models.ctx_edit'),
|
|
1736
1815
|
onClick: () => {
|
|
1737
1816
|
setCustomCtxVal(String(m.contextLength || defaultCtx))
|
|
1817
|
+
setCustomMaxVal(String(m.maxTokens || defaultMax))
|
|
1738
1818
|
setEditingModelId(m.id)
|
|
1739
1819
|
},
|
|
1740
1820
|
},
|
package/lib/cline-client.js
CHANGED
|
@@ -490,10 +490,15 @@ export function buildPiAiProvider({
|
|
|
490
490
|
modelContextOverrides = [],
|
|
491
491
|
models = [],
|
|
492
492
|
customModels = [],
|
|
493
|
+
existingModels = [],
|
|
493
494
|
displayName = PROVIDER_DISPLAY_NAME,
|
|
494
495
|
} = {}) {
|
|
495
496
|
const allAvailable = getAllModels(dynamicModels, modelContextOverrides, customModels)
|
|
496
497
|
const customModelsMap = new Map((Array.isArray(customModels) ? customModels : []).filter(Boolean).map(cm => [cm.id, cm]))
|
|
498
|
+
const existingMap = new Map((Array.isArray(existingModels) ? existingModels : []).filter(Boolean).map(m => [m.id, m]))
|
|
499
|
+
const explicitOverridesSet = new Set((Array.isArray(modelContextOverrides) ? modelContextOverrides : []).map(o => o?.modelId))
|
|
500
|
+
const hasWildcardOverride = explicitOverridesSet.has('*')
|
|
501
|
+
|
|
497
502
|
const modelList = (Array.isArray(models) && models.length ? models : allAvailable).map((m) => {
|
|
498
503
|
let item
|
|
499
504
|
if (typeof m === 'string') {
|
|
@@ -504,12 +509,26 @@ export function buildPiAiProvider({
|
|
|
504
509
|
item = m
|
|
505
510
|
}
|
|
506
511
|
const hasImage = item.input?.includes('image') || item.input?.includes('vision')
|
|
512
|
+
|
|
513
|
+
// Preserve manual provider edits if user configured contextWindow or maxTokens in llm-pi-ai without explicit override in clinebot
|
|
514
|
+
const existingEntry = existingMap.get(item.id)
|
|
515
|
+
let ctxWin = Number(item.contextLength || item.contextWindow) || 200000
|
|
516
|
+
let maxTok = Number(item.maxTokens) || 8192
|
|
517
|
+
if (existingEntry && !explicitOverridesSet.has(item.id) && !hasWildcardOverride) {
|
|
518
|
+
if (typeof existingEntry.contextWindow === 'number' && existingEntry.contextWindow > 0) {
|
|
519
|
+
ctxWin = existingEntry.contextWindow
|
|
520
|
+
}
|
|
521
|
+
if (typeof existingEntry.maxTokens === 'number' && existingEntry.maxTokens > 0) {
|
|
522
|
+
maxTok = existingEntry.maxTokens
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
507
526
|
const res = {
|
|
508
527
|
id: item.id,
|
|
509
528
|
name: item.name || item.id,
|
|
510
529
|
description: formatModelDescription(item),
|
|
511
|
-
contextWindow:
|
|
512
|
-
maxTokens:
|
|
530
|
+
contextWindow: ctxWin,
|
|
531
|
+
maxTokens: maxTok,
|
|
513
532
|
input: hasImage ? ['text', 'image'] : ['text'],
|
|
514
533
|
provider: PROVIDER_ID,
|
|
515
534
|
}
|
package/lib/models.js
CHANGED
|
@@ -52,12 +52,26 @@ export function getOriginalModelContext(nameOrId) {
|
|
|
52
52
|
if (norm.includes('qwen')) return 1000000
|
|
53
53
|
if (norm.includes('minimax')) return 1000000
|
|
54
54
|
if (norm.includes('mimo')) return 1000000
|
|
55
|
+
if (norm.includes('glm-5.3') || norm.includes('glm-53')) return 1000000
|
|
56
|
+
if (norm.includes('deepseek-v4.1') || norm.includes('deepseek-v41')) return 1000000
|
|
55
57
|
if (norm.includes('deepseek')) return 128000
|
|
56
58
|
if (norm.includes('glm')) return 128000
|
|
57
59
|
return 200000
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
function
|
|
62
|
+
export function getOriginalModelMaxTokens(nameOrId) {
|
|
63
|
+
const norm = String(nameOrId || '').toLowerCase()
|
|
64
|
+
if (norm.includes('qwen')) return 131072
|
|
65
|
+
if (norm.includes('glm')) return 128000
|
|
66
|
+
if (norm.includes('minimax')) return 128000
|
|
67
|
+
if (norm.includes('kimi')) return 65536
|
|
68
|
+
if (norm.includes('deepseek-v4.1') || norm.includes('deepseek-v41')) return 65536
|
|
69
|
+
if (norm.includes('deepseek')) return 16384
|
|
70
|
+
if (norm.includes('mimo')) return 16384
|
|
71
|
+
return 16384
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function getOriginalModelProvider(nameOrId) {
|
|
61
75
|
const norm = String(nameOrId || '').toLowerCase()
|
|
62
76
|
if (norm.includes('kimi')) return 'Moonshot AI'
|
|
63
77
|
if (norm.includes('qwen')) return 'Alibaba Cloud'
|
|
@@ -68,181 +82,102 @@ function getOriginalModelProvider(nameOrId) {
|
|
|
68
82
|
return 'Original Vendor'
|
|
69
83
|
}
|
|
70
84
|
|
|
85
|
+
function createCuratedModel({
|
|
86
|
+
id, name, description,
|
|
87
|
+
ctx = 128000, maxTokens = 8192,
|
|
88
|
+
origCtx, origMax, origProv,
|
|
89
|
+
input = ['text'], category = 'general',
|
|
90
|
+
recommended = false, reasoningEfforts,
|
|
91
|
+
}) {
|
|
92
|
+
const c = origCtx || getOriginalModelContext(id)
|
|
93
|
+
const p = origProv || getOriginalModelProvider(id)
|
|
94
|
+
const om = origMax || getOriginalModelMaxTokens(id)
|
|
95
|
+
return {
|
|
96
|
+
id, name, description,
|
|
97
|
+
clineContextLength: ctx, defaultContextLength: ctx, contextLength: ctx,
|
|
98
|
+
originalContextLength: c, originalProvider: p, originalMaxTokens: om,
|
|
99
|
+
maxTokens, defaultMaxTokens: maxTokens,
|
|
100
|
+
input, category, recommended, isCustom: false,
|
|
101
|
+
...(reasoningEfforts ? { reasoningEfforts } : {}),
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
71
105
|
export const CLINE_MODELS = Object.freeze([
|
|
72
|
-
{
|
|
106
|
+
createCuratedModel({
|
|
73
107
|
id: 'cline-pass/deepseek-v4-flash',
|
|
74
108
|
name: 'DeepSeek V4 Flash',
|
|
75
109
|
description: 'High-speed reasoning & code completion model optimized for agentic loops.',
|
|
76
|
-
|
|
77
|
-
defaultContextLength: 128000,
|
|
78
|
-
originalContextLength: 128000,
|
|
79
|
-
originalProvider: 'DeepSeek',
|
|
80
|
-
contextLength: 128000,
|
|
81
|
-
maxTokens: 8192,
|
|
82
|
-
input: ['text', 'image'],
|
|
83
|
-
category: 'coding',
|
|
84
|
-
recommended: true,
|
|
85
|
-
isCustom: false,
|
|
110
|
+
ctx: 128000, maxTokens: 8192, input: ['text', 'image'], category: 'coding', recommended: true,
|
|
86
111
|
reasoningEfforts: ['low', 'medium', 'high'],
|
|
87
|
-
},
|
|
88
|
-
{
|
|
112
|
+
}),
|
|
113
|
+
createCuratedModel({
|
|
89
114
|
id: 'cline-pass/deepseek-v4-pro',
|
|
90
115
|
name: 'DeepSeek V4 Pro',
|
|
91
116
|
description: 'Flagship reasoning and multi-turn architectural coding model.',
|
|
92
|
-
|
|
93
|
-
defaultContextLength: 128000,
|
|
94
|
-
originalContextLength: 128000,
|
|
95
|
-
originalProvider: 'DeepSeek',
|
|
96
|
-
contextLength: 128000,
|
|
97
|
-
maxTokens: 8192,
|
|
98
|
-
input: ['text', 'image'],
|
|
99
|
-
category: 'coding',
|
|
100
|
-
recommended: true,
|
|
101
|
-
isCustom: false,
|
|
117
|
+
ctx: 128000, maxTokens: 8192, input: ['text', 'image'], category: 'coding', recommended: true,
|
|
102
118
|
reasoningEfforts: ['low', 'medium', 'high', 'max'],
|
|
103
|
-
},
|
|
104
|
-
{
|
|
119
|
+
}),
|
|
120
|
+
createCuratedModel({
|
|
105
121
|
id: 'cline-pass/glm-5.2',
|
|
106
122
|
name: 'GLM 5.2',
|
|
107
123
|
description: 'Bilingual general & coding model with strong instruction following.',
|
|
108
|
-
|
|
109
|
-
defaultContextLength: 128000,
|
|
110
|
-
originalContextLength: 128000,
|
|
111
|
-
originalProvider: 'Zhipu AI',
|
|
112
|
-
contextLength: 128000,
|
|
113
|
-
maxTokens: 8192,
|
|
114
|
-
input: ['text'],
|
|
115
|
-
category: 'general',
|
|
116
|
-
recommended: false,
|
|
117
|
-
isCustom: false,
|
|
124
|
+
ctx: 128000, maxTokens: 8192, input: ['text'], category: 'general', recommended: false,
|
|
118
125
|
reasoningEfforts: ['low', 'medium', 'high'],
|
|
119
|
-
},
|
|
120
|
-
{
|
|
126
|
+
}),
|
|
127
|
+
createCuratedModel({
|
|
121
128
|
id: 'cline-pass/kimi-k3',
|
|
122
129
|
name: 'Kimi K3',
|
|
123
130
|
description: 'Long-context reasoning & document synthesis model by Moonshot AI.',
|
|
124
|
-
|
|
125
|
-
defaultContextLength: 200000,
|
|
126
|
-
originalContextLength: 2000000,
|
|
127
|
-
originalProvider: 'Moonshot AI',
|
|
128
|
-
contextLength: 200000,
|
|
129
|
-
maxTokens: 8192,
|
|
130
|
-
input: ['text'],
|
|
131
|
-
category: 'reasoning',
|
|
132
|
-
recommended: true,
|
|
133
|
-
isCustom: false,
|
|
131
|
+
ctx: 200000, maxTokens: 8192, input: ['text'], category: 'reasoning', recommended: true,
|
|
134
132
|
reasoningEfforts: ['low', 'medium', 'high', 'max'],
|
|
135
|
-
},
|
|
136
|
-
{
|
|
133
|
+
}),
|
|
134
|
+
createCuratedModel({
|
|
137
135
|
id: 'cline-pass/kimi-k2.7-code',
|
|
138
136
|
name: 'Kimi K2.7 Code',
|
|
139
137
|
description: 'Specialized coding model with agentic tool calling support.',
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
originalProvider: 'Moonshot AI',
|
|
144
|
-
contextLength: 200000,
|
|
145
|
-
maxTokens: 8192,
|
|
146
|
-
input: ['text'],
|
|
147
|
-
category: 'coding',
|
|
148
|
-
recommended: false,
|
|
149
|
-
isCustom: false,
|
|
150
|
-
},
|
|
151
|
-
{
|
|
138
|
+
ctx: 200000, maxTokens: 8192, input: ['text'], category: 'coding', recommended: false,
|
|
139
|
+
}),
|
|
140
|
+
createCuratedModel({
|
|
152
141
|
id: 'cline-pass/kimi-k2.6',
|
|
153
142
|
name: 'Kimi K2.6',
|
|
154
143
|
description: 'Balanced long-context conversational model.',
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
originalProvider: 'Moonshot AI',
|
|
159
|
-
contextLength: 200000,
|
|
160
|
-
maxTokens: 8192,
|
|
161
|
-
input: ['text'],
|
|
162
|
-
category: 'general',
|
|
163
|
-
recommended: false,
|
|
164
|
-
isCustom: false,
|
|
165
|
-
},
|
|
166
|
-
{
|
|
144
|
+
ctx: 200000, maxTokens: 8192, input: ['text'], category: 'general', recommended: false,
|
|
145
|
+
}),
|
|
146
|
+
createCuratedModel({
|
|
167
147
|
id: 'cline-pass/qwen3.7-max',
|
|
168
148
|
name: 'Qwen 3.7 Max',
|
|
169
149
|
description: 'Large-scale multimodal foundation model from Alibaba Cloud with reasoning support.',
|
|
170
|
-
|
|
171
|
-
defaultContextLength: 128000,
|
|
172
|
-
originalContextLength: 1000000,
|
|
173
|
-
originalProvider: 'Alibaba Cloud',
|
|
174
|
-
contextLength: 128000,
|
|
175
|
-
maxTokens: 8192,
|
|
176
|
-
input: ['text', 'image'],
|
|
177
|
-
category: 'multimodal',
|
|
178
|
-
recommended: true,
|
|
179
|
-
isCustom: false,
|
|
150
|
+
ctx: 128000, maxTokens: 8192, input: ['text', 'image'], category: 'multimodal', recommended: true,
|
|
180
151
|
reasoningEfforts: ['low', 'medium', 'high', 'max'],
|
|
181
|
-
},
|
|
182
|
-
{
|
|
152
|
+
}),
|
|
153
|
+
createCuratedModel({
|
|
183
154
|
id: 'cline-pass/qwen3.7-plus',
|
|
184
155
|
name: 'Qwen 3.7 Plus',
|
|
185
156
|
description: 'Fast, capable multimodal model with strong multilingual skills and reasoning.',
|
|
186
|
-
|
|
187
|
-
defaultContextLength: 128000,
|
|
188
|
-
originalContextLength: 1000000,
|
|
189
|
-
originalProvider: 'Alibaba Cloud',
|
|
190
|
-
contextLength: 128000,
|
|
191
|
-
maxTokens: 8192,
|
|
192
|
-
input: ['text', 'image'],
|
|
193
|
-
category: 'multimodal',
|
|
194
|
-
recommended: false,
|
|
195
|
-
isCustom: false,
|
|
157
|
+
ctx: 128000, maxTokens: 8192, input: ['text', 'image'], category: 'multimodal', recommended: false,
|
|
196
158
|
reasoningEfforts: ['low', 'medium', 'high', 'max'],
|
|
197
|
-
},
|
|
198
|
-
{
|
|
159
|
+
}),
|
|
160
|
+
createCuratedModel({
|
|
199
161
|
id: 'cline-pass/minimax-m3',
|
|
200
162
|
name: 'MiniMax M3',
|
|
201
163
|
description: 'High throughput reasoning and synthesis model.',
|
|
202
|
-
|
|
203
|
-
defaultContextLength: 200000,
|
|
204
|
-
originalContextLength: 1000000,
|
|
205
|
-
originalProvider: 'MiniMax',
|
|
206
|
-
contextLength: 200000,
|
|
207
|
-
maxTokens: 8192,
|
|
208
|
-
input: ['text'],
|
|
209
|
-
category: 'reasoning',
|
|
210
|
-
recommended: false,
|
|
211
|
-
isCustom: false,
|
|
164
|
+
ctx: 200000, maxTokens: 8192, input: ['text'], category: 'reasoning', recommended: false,
|
|
212
165
|
reasoningEfforts: ['low', 'medium', 'high'],
|
|
213
|
-
},
|
|
214
|
-
{
|
|
166
|
+
}),
|
|
167
|
+
createCuratedModel({
|
|
215
168
|
id: 'cline-pass/mimo-v2.5',
|
|
216
169
|
name: 'MiMo V2.5',
|
|
217
170
|
description: 'Xiaomi MiMo efficient instruction model with reasoning and multimodal support.',
|
|
218
|
-
|
|
219
|
-
defaultContextLength: 128000,
|
|
220
|
-
originalContextLength: 1000000,
|
|
221
|
-
originalProvider: 'Xiaomi',
|
|
222
|
-
contextLength: 128000,
|
|
223
|
-
maxTokens: 8192,
|
|
224
|
-
input: ['text', 'image'],
|
|
225
|
-
category: 'general',
|
|
226
|
-
recommended: false,
|
|
227
|
-
isCustom: false,
|
|
171
|
+
ctx: 128000, maxTokens: 8192, input: ['text', 'image'], category: 'general', recommended: false,
|
|
228
172
|
reasoningEfforts: ['low', 'medium', 'high', 'max'],
|
|
229
|
-
},
|
|
230
|
-
{
|
|
173
|
+
}),
|
|
174
|
+
createCuratedModel({
|
|
231
175
|
id: 'cline-pass/mimo-v2.5-pro',
|
|
232
176
|
name: 'MiMo V2.5 Pro',
|
|
233
177
|
description: 'Xiaomi MiMo advanced agentic reasoning model with multimodal support.',
|
|
234
|
-
|
|
235
|
-
defaultContextLength: 128000,
|
|
236
|
-
originalContextLength: 1000000,
|
|
237
|
-
originalProvider: 'Xiaomi',
|
|
238
|
-
contextLength: 128000,
|
|
239
|
-
maxTokens: 8192,
|
|
240
|
-
input: ['text', 'image'],
|
|
241
|
-
category: 'coding',
|
|
242
|
-
recommended: false,
|
|
243
|
-
isCustom: false,
|
|
178
|
+
ctx: 128000, maxTokens: 8192, input: ['text', 'image'], category: 'coding', recommended: false,
|
|
244
179
|
reasoningEfforts: ['low', 'medium', 'high', 'max'],
|
|
245
|
-
},
|
|
180
|
+
}),
|
|
246
181
|
])
|
|
247
182
|
|
|
248
183
|
/**
|
|
@@ -303,6 +238,7 @@ export function parsePlanIncludedModels(includedInput) {
|
|
|
303
238
|
const isVision = /(vision|vl|multimodal|omni|image)/i.test(name)
|
|
304
239
|
const hasReasoning = /(reason|think|r1|pro|flash|max|plus|k3|m3|glm|qwen|mimo)/i.test(name)
|
|
305
240
|
const origCtx = getOriginalModelContext(name)
|
|
241
|
+
const origMax = getOriginalModelMaxTokens(name)
|
|
306
242
|
const origProv = getOriginalModelProvider(name)
|
|
307
243
|
matched.push({
|
|
308
244
|
id: `cline-pass/${idPart}`,
|
|
@@ -311,9 +247,11 @@ export function parsePlanIncludedModels(includedInput) {
|
|
|
311
247
|
clineContextLength: 200000,
|
|
312
248
|
defaultContextLength: 200000,
|
|
313
249
|
originalContextLength: origCtx,
|
|
250
|
+
originalMaxTokens: origMax,
|
|
314
251
|
originalProvider: origProv,
|
|
315
252
|
contextLength: 200000,
|
|
316
253
|
maxTokens: 8192,
|
|
254
|
+
defaultMaxTokens: 8192,
|
|
317
255
|
input: isVision ? ['text', 'image'] : ['text'],
|
|
318
256
|
category: hasReasoning ? 'reasoning' : (isVision ? 'multimodal' : 'general'),
|
|
319
257
|
recommended: false,
|
|
@@ -351,26 +289,31 @@ export function getAllModels(dynamicModels = [], modelContextOverrides = [], cus
|
|
|
351
289
|
}
|
|
352
290
|
}
|
|
353
291
|
|
|
292
|
+
const wildcardOv = overridesMap.get('*')
|
|
354
293
|
function applyOverride(m) {
|
|
355
294
|
const defaultCtx = m.clineContextLength || m.defaultContextLength || m.contextLength || 200000
|
|
295
|
+
const defaultMax = m.defaultMaxTokens || m.maxTokens || 8192
|
|
356
296
|
const origCtx = m.originalContextLength || getOriginalModelContext(m.id)
|
|
297
|
+
const origMax = m.originalMaxTokens || getOriginalModelMaxTokens(m.id)
|
|
357
298
|
const origProv = m.originalProvider || getOriginalModelProvider(m.id)
|
|
358
|
-
const ov = overridesMap.get(m.id)
|
|
299
|
+
const ov = overridesMap.get(m.id) || wildcardOv
|
|
359
300
|
const activeCtx = ov && typeof ov.contextLength === 'number' && ov.contextLength > 0
|
|
360
301
|
? ov.contextLength
|
|
361
302
|
: defaultCtx
|
|
362
303
|
const activeMaxTokens = ov && typeof ov.maxTokens === 'number' && ov.maxTokens > 0
|
|
363
304
|
? ov.maxTokens
|
|
364
|
-
:
|
|
305
|
+
: defaultMax
|
|
365
306
|
return {
|
|
366
307
|
...m,
|
|
367
308
|
contextLength: activeCtx,
|
|
368
309
|
maxTokens: activeMaxTokens,
|
|
369
310
|
defaultContextLength: defaultCtx,
|
|
370
311
|
clineContextLength: defaultCtx,
|
|
312
|
+
defaultMaxTokens: defaultMax,
|
|
371
313
|
originalContextLength: origCtx,
|
|
314
|
+
originalMaxTokens: origMax,
|
|
372
315
|
originalProvider: origProv,
|
|
373
|
-
isContextOverridden: activeCtx !== defaultCtx,
|
|
316
|
+
isContextOverridden: activeCtx !== defaultCtx || activeMaxTokens !== defaultMax,
|
|
374
317
|
}
|
|
375
318
|
}
|
|
376
319
|
|
|
@@ -392,7 +335,9 @@ export function getAllModels(dynamicModels = [], modelContextOverrides = [], cus
|
|
|
392
335
|
clineContextLength: curated.clineContextLength || curated.contextLength,
|
|
393
336
|
defaultContextLength: curated.defaultContextLength || curated.contextLength,
|
|
394
337
|
originalContextLength: curated.originalContextLength || getOriginalModelContext(curated.id),
|
|
338
|
+
originalMaxTokens: curated.originalMaxTokens || getOriginalModelMaxTokens(curated.id),
|
|
395
339
|
originalProvider: curated.originalProvider || getOriginalModelProvider(curated.id),
|
|
340
|
+
defaultMaxTokens: curated.defaultMaxTokens || curated.maxTokens || 8192,
|
|
396
341
|
input: dm.input && dm.input.length ? dm.input : curated.input,
|
|
397
342
|
category: dm.category || curated.category,
|
|
398
343
|
reasoningEfforts: dm.reasoningEfforts !== undefined ? dm.reasoningEfforts : curated.reasoningEfforts,
|
|
@@ -404,7 +349,9 @@ export function getAllModels(dynamicModels = [], modelContextOverrides = [], cus
|
|
|
404
349
|
clineContextLength: dm.contextLength || 200000,
|
|
405
350
|
defaultContextLength: dm.contextLength || 200000,
|
|
406
351
|
originalContextLength: dm.originalContextLength || getOriginalModelContext(dm.id),
|
|
352
|
+
originalMaxTokens: dm.originalMaxTokens || getOriginalModelMaxTokens(dm.id),
|
|
407
353
|
originalProvider: dm.originalProvider || getOriginalModelProvider(dm.id),
|
|
354
|
+
defaultMaxTokens: dm.maxTokens || 8192,
|
|
408
355
|
unverified: false,
|
|
409
356
|
})
|
|
410
357
|
})
|
package/lib/provider-sync.js
CHANGED
|
@@ -239,6 +239,22 @@ export async function upsertPiAiProvider(ctx, cfg, activeModelIds) {
|
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
+
let existingModels = []
|
|
243
|
+
try {
|
|
244
|
+
if (typeof settings.describe === 'function') {
|
|
245
|
+
const descriptors = settings.describe()
|
|
246
|
+
const piAiDesc = Array.isArray(descriptors) ? descriptors.find((d) => d?.ns === LLM_PI_AI_NS) : null
|
|
247
|
+
const existingProv = piAiDesc?.value?.providers?.[PROVIDER_ID]
|
|
248
|
+
if (Array.isArray(existingProv?.models)) existingModels = existingProv.models
|
|
249
|
+
} else if (typeof settings.get === 'function') {
|
|
250
|
+
const piAiVal = settings.get(LLM_PI_AI_NS)
|
|
251
|
+
const existingProv = piAiVal?.providers?.[PROVIDER_ID]
|
|
252
|
+
if (Array.isArray(existingProv?.models)) existingModels = existingProv.models
|
|
253
|
+
}
|
|
254
|
+
} catch {
|
|
255
|
+
/* ignore read error */
|
|
256
|
+
}
|
|
257
|
+
|
|
242
258
|
const providerObj = buildPiAiProvider({
|
|
243
259
|
baseUrl: effectiveBaseUrl,
|
|
244
260
|
apiKeyEnv: isProxy ? LOCAL_PROXY_KEY_ENV : (activeAcc?.apiKeyEnv || pub.apiKeyEnv),
|
|
@@ -246,6 +262,7 @@ export async function upsertPiAiProvider(ctx, cfg, activeModelIds) {
|
|
|
246
262
|
dynamicModels: pub.dynamicModels,
|
|
247
263
|
modelContextOverrides: pub.modelContextOverrides,
|
|
248
264
|
customModels: pub.customModels,
|
|
265
|
+
existingModels,
|
|
249
266
|
displayName: PROVIDER_DISPLAY_NAME,
|
|
250
267
|
})
|
|
251
268
|
|
|
@@ -257,6 +274,10 @@ export async function upsertPiAiProvider(ctx, cfg, activeModelIds) {
|
|
|
257
274
|
},
|
|
258
275
|
])
|
|
259
276
|
|
|
277
|
+
ctx?.logger?.info?.(
|
|
278
|
+
`[dsh-clinebot] Synchronized llm-pi-ai provider "${PROVIDER_ID}" with ${providerObj.models.length} models.`
|
|
279
|
+
)
|
|
280
|
+
|
|
260
281
|
return providerObj
|
|
261
282
|
}
|
|
262
283
|
|
package/lib/proxy-token.js
CHANGED
|
@@ -14,9 +14,9 @@ export const LOCAL_PROXY_KEY_ENV = 'CLINEBOT_LOCAL_PROXY_TOKEN'
|
|
|
14
14
|
let cachedToken = null
|
|
15
15
|
|
|
16
16
|
const require = createRequire(import.meta.url)
|
|
17
|
-
let packageVersion = '0.5.
|
|
17
|
+
let packageVersion = '0.5.21'
|
|
18
18
|
try {
|
|
19
|
-
packageVersion = require('../package.json').version || '0.5.
|
|
19
|
+
packageVersion = require('../package.json').version || '0.5.21'
|
|
20
20
|
} catch (err) {
|
|
21
21
|
/* fallback to default version if manifest is unreadable */
|
|
22
22
|
}
|
package/lib/routes/models.js
CHANGED
|
@@ -2,7 +2,7 @@ import { writeJson, readBody } from '../http.js'
|
|
|
2
2
|
import { assertTrustedSettingsRequest } from '../access.js'
|
|
3
3
|
import { publicConfig, plainConfig, Config } from '../config.js'
|
|
4
4
|
import { resolveActiveAccountKey, resolvePathWithHome } from '../provider-sync.js'
|
|
5
|
-
import { getAllModels, saveModelsDiskCache, getOriginalModelContext } from '../models.js'
|
|
5
|
+
import { getAllModels, saveModelsDiskCache, getOriginalModelContext, getOriginalModelMaxTokens } from '../models.js'
|
|
6
6
|
import { fetchUsageLimits } from '../cline-client.js'
|
|
7
7
|
import { publicUsage } from '../http.js'
|
|
8
8
|
|
|
@@ -129,15 +129,36 @@ export function registerModelsRoutes(ctx, { live, getSettingsApi, syncProviderSt
|
|
|
129
129
|
let currentOverrides = Array.isArray(pub.modelContextOverrides) ? [...pub.modelContextOverrides] : []
|
|
130
130
|
|
|
131
131
|
if (body.mode === 'all-original') {
|
|
132
|
-
// Set all models to their authentic original creator context limits
|
|
132
|
+
// Set all models to their authentic original creator context & token limits
|
|
133
133
|
currentOverrides = allModels.map((m) => ({
|
|
134
134
|
modelId: m.id,
|
|
135
135
|
contextLength: m.originalContextLength || getOriginalModelContext(m.id),
|
|
136
|
-
maxTokens: m.
|
|
136
|
+
maxTokens: m.originalMaxTokens || getOriginalModelMaxTokens(m.id),
|
|
137
137
|
}))
|
|
138
138
|
} else if (body.mode === 'all-default' || body.mode === 'reset-all') {
|
|
139
139
|
// Reset all overrides back to provider defaults
|
|
140
140
|
currentOverrides = []
|
|
141
|
+
} else if (body.mode === 'apply-all') {
|
|
142
|
+
// Bulk apply limits across all models and set wildcard default
|
|
143
|
+
const rawCtx = Number(body.contextLength)
|
|
144
|
+
const contextLength = Number.isFinite(rawCtx) && rawCtx > 0 ? Math.max(1000, rawCtx) : undefined
|
|
145
|
+
const rawMax = Number(body.maxTokens)
|
|
146
|
+
const maxTokens = Number.isFinite(rawMax) && rawMax > 0 ? Math.max(256, rawMax) : undefined
|
|
147
|
+
|
|
148
|
+
currentOverrides = allModels.map((m) => {
|
|
149
|
+
const existing = currentOverrides.find((o) => o.modelId === m.id)
|
|
150
|
+
return {
|
|
151
|
+
modelId: m.id,
|
|
152
|
+
contextLength: contextLength !== undefined ? contextLength : (existing?.contextLength || m.contextLength),
|
|
153
|
+
maxTokens: maxTokens !== undefined ? maxTokens : (existing?.maxTokens || m.maxTokens),
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
currentOverrides = currentOverrides.filter((o) => o.modelId !== '*')
|
|
157
|
+
currentOverrides.push({
|
|
158
|
+
modelId: '*',
|
|
159
|
+
contextLength: contextLength || 200000,
|
|
160
|
+
maxTokens: maxTokens || 16384,
|
|
161
|
+
})
|
|
141
162
|
} else if (Array.isArray(body.overrides)) {
|
|
142
163
|
// Bulk array override
|
|
143
164
|
currentOverrides = body.overrides
|
|
@@ -153,11 +174,19 @@ export function registerModelsRoutes(ctx, { live, getSettingsApi, syncProviderSt
|
|
|
153
174
|
currentOverrides = currentOverrides.filter((o) => o.modelId !== modelId)
|
|
154
175
|
} else {
|
|
155
176
|
const rawCtx = Number(body.contextLength)
|
|
156
|
-
const contextLength = Number.isFinite(rawCtx) && rawCtx > 0 ? Math.max(1000, rawCtx) : 200000
|
|
157
177
|
const rawMax = Number(body.maxTokens)
|
|
158
|
-
const maxTokens = Number.isFinite(rawMax) && rawMax > 0 ? Math.max(256, rawMax) : 8192
|
|
159
|
-
|
|
160
178
|
const existingIdx = currentOverrides.findIndex((o) => o.modelId === modelId)
|
|
179
|
+
const existingEntry = existingIdx >= 0 ? currentOverrides[existingIdx] : null
|
|
180
|
+
const targetModel = allModels.find((m) => m.id === modelId)
|
|
181
|
+
|
|
182
|
+
const contextLength = Number.isFinite(rawCtx) && rawCtx > 0
|
|
183
|
+
? Math.max(1000, rawCtx)
|
|
184
|
+
: (existingEntry?.contextLength || targetModel?.contextLength || 200000)
|
|
185
|
+
|
|
186
|
+
const maxTokens = Number.isFinite(rawMax) && rawMax > 0
|
|
187
|
+
? Math.max(256, rawMax)
|
|
188
|
+
: (existingEntry?.maxTokens || targetModel?.maxTokens || 8192)
|
|
189
|
+
|
|
161
190
|
const newEntry = { modelId, contextLength, maxTokens }
|
|
162
191
|
if (existingIdx >= 0) {
|
|
163
192
|
currentOverrides[existingIdx] = newEntry
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-clinebot",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.21",
|
|
4
4
|
"description": "DeepSeek Harness companion for ClineBot / ClinePass: dynamic subscription models sync, quota exhaustion warnings, session metrics, dedicated settings page, live usage limits, and /cline slash-command.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -64,7 +64,8 @@
|
|
|
64
64
|
"@deepseek-ai/schemastery": "^3.18.1"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@deepseek-ai/schemastery": "^3.18.1"
|
|
67
|
+
"@deepseek-ai/schemastery": "^3.18.1",
|
|
68
|
+
"eslint": "^9.39.5"
|
|
68
69
|
},
|
|
69
70
|
"homepage": "https://github.com/GooDAnDReaDY/dsh-clinebot#readme",
|
|
70
71
|
"bugs": {
|