@gehennawu/dsh-service 2.0.7 → 2.0.8
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.en.md +58 -10
- package/README.md +58 -10
- package/client.js +1 -1
- package/index.js +127 -7
- package/model-config-routes.js +633 -0
- package/model-config-schema.js +132 -0
- package/model-smart-fill.js +2206 -0
- package/model-workbench-routes.js +1740 -0
- package/model-workbench-settings.js +389 -0
- package/model-workbench.js +1618 -0
- package/package.json +10 -2
- package/provider-availability.js +84 -0
- package/subagent-routes.js +7 -0
package/index.js
CHANGED
|
@@ -20,6 +20,46 @@ import { createSessionsRoutes } from './session-routes.js'
|
|
|
20
20
|
import { createQuotaRoutes } from './quota-routes.js'
|
|
21
21
|
import { createSubagentRoutes } from './subagent-routes.js'
|
|
22
22
|
import { createBackupRoutes } from './backup-routes.js'
|
|
23
|
+
import { createModelConfigRoutes } from './model-config-routes.js'
|
|
24
|
+
import { createProviderAvailability, installProviderAvailability } from './provider-availability.js'
|
|
25
|
+
import { createWorkbenchStore, loadPiAiCatalog } from './model-workbench.js'
|
|
26
|
+
import {
|
|
27
|
+
WORKBENCH_SIDECAR_VERSION,
|
|
28
|
+
applySuggestions,
|
|
29
|
+
applyUndo,
|
|
30
|
+
arbitrateModelSuggestions,
|
|
31
|
+
buildPiAiIndex,
|
|
32
|
+
abilityPatch,
|
|
33
|
+
consensusFields,
|
|
34
|
+
createFreshnessManager,
|
|
35
|
+
deriveKeyRefFor,
|
|
36
|
+
diffModelLists,
|
|
37
|
+
emptyWorkbenchSidecar,
|
|
38
|
+
fetchModelsProbe,
|
|
39
|
+
hostnameOf,
|
|
40
|
+
indexModelParamsDev,
|
|
41
|
+
isPrivateIpAddress,
|
|
42
|
+
loadModelParamsSnapshot,
|
|
43
|
+
loadWorkbenchSidecar,
|
|
44
|
+
modelParamsCandidates,
|
|
45
|
+
modelParamsDevEfforts,
|
|
46
|
+
modelPatchFromPiAi,
|
|
47
|
+
openRouterModelFacts,
|
|
48
|
+
openRouterVerifySource,
|
|
49
|
+
pickOpenRouterMatch,
|
|
50
|
+
modelsProbeLookup,
|
|
51
|
+
normalizeBaseURLForMatch,
|
|
52
|
+
parseModelListing,
|
|
53
|
+
probeBaseURLCheck,
|
|
54
|
+
profileFingerprint,
|
|
55
|
+
readLlmProfiles,
|
|
56
|
+
saveModelParamsSnapshot,
|
|
57
|
+
saveWorkbenchSidecar,
|
|
58
|
+
selfCheckModelsPatch,
|
|
59
|
+
suggestRouteId,
|
|
60
|
+
suggestionId,
|
|
61
|
+
undoPreviewModels,
|
|
62
|
+
} from './model-workbench.js'
|
|
23
63
|
import { collectPluginHealth, pluginCheckItem, restartPluginEntry } from './plugin-health.js'
|
|
24
64
|
import {
|
|
25
65
|
buildCliproxyAccountPlan as buildCliproxyAccountPlanAdapter,
|
|
@@ -88,6 +128,8 @@ const DEFAULT_FEATURE_SETTINGS = Object.freeze({
|
|
|
88
128
|
// v1.8 新增:composer 模型按钮显示厂家/渠道图标(宽态加在模型名前,
|
|
89
129
|
// 窄态替换官方默认图标;未适配渠道保留官方默认图标)。
|
|
90
130
|
modelProviderIcons: true,
|
|
131
|
+
// v1.10 新增:模型配置工作台(供应商与模型 / 子代理路由),写路径经两段式确认。
|
|
132
|
+
modelCatalog: true,
|
|
91
133
|
})
|
|
92
134
|
/** 标记为「可热更新」的 Config 字段(0.1.7-alpha.1 的表单与原地热更新以此为准)。 */
|
|
93
135
|
const volatileField = (schema) => (typeof schema?.extra === 'function' ? schema.extra('volatile', true) : schema)
|
|
@@ -4236,7 +4278,7 @@ function subagentRouteCandidates(request, config, options = {}) {
|
|
|
4236
4278
|
|
|
4237
4279
|
function resolveSubagentInjection(request, config, options = {}) {
|
|
4238
4280
|
for (const candidate of subagentRouteCandidates(request, config, options)) {
|
|
4239
|
-
if (options.isRoutable !== undefined && !options.isRoutable(candidate.provider)) {
|
|
4281
|
+
if (options.isRoutable !== undefined && !options.isRoutable(candidate.provider, candidate.model)) {
|
|
4240
4282
|
options.note?.(`${candidate.provider}/${candidate.model} skipped (not routable)`)
|
|
4241
4283
|
continue
|
|
4242
4284
|
}
|
|
@@ -5348,6 +5390,18 @@ function apply(ctx, featureConfig) {
|
|
|
5348
5390
|
// 版本一变即自然失效,不必手工清理。
|
|
5349
5391
|
const releaseNotesCache = new Map()
|
|
5350
5392
|
const quotaThrottle = createQuotaThrottle()
|
|
5393
|
+
// 模型配置工作台(v1.10):侧车仓库持有写前快照/绑定/验证摘要/人工恢复态。
|
|
5394
|
+
// 写串行化在仓库内部;目录随 DSH 升级才变,进程内缓存一次。
|
|
5395
|
+
const modelProfileDir = ctx.get('profileContext')?.dir ?? process.env.DSH_PROFILE_DIR
|
|
5396
|
+
const workbenchStore = createWorkbenchStore(typeof modelProfileDir === 'string' && modelProfileDir !== '' ? modelProfileDir : join(dshHome, 'dsh-service-model-config', 'legacy'))
|
|
5397
|
+
const providerAvailability = createProviderAvailability(workbenchStore)
|
|
5398
|
+
const notifyProviderAvailability = () => ctx.emit('llm/adapters-updated')
|
|
5399
|
+
ctx.inject(['llm'], (scope) => {
|
|
5400
|
+
const llm = scope.get('llm') ?? ctx.get('llm')
|
|
5401
|
+
if (!llm) return
|
|
5402
|
+
const dispose = installProviderAvailability(llm, providerAvailability, notifyProviderAvailability)
|
|
5403
|
+
scope.effect(() => dispose)
|
|
5404
|
+
})
|
|
5351
5405
|
// 技能管理(v0.22):侧车索引缓存 + 批量补全状态。批量随 Fiber 销毁中止。
|
|
5352
5406
|
// 可重赋值的 let 一律以 { current } 箱体跨模块传递:端点工厂解构是值快照,
|
|
5353
5407
|
// let 重赋值不会穿过解构边界;箱体靠对象身份保持活绑定。
|
|
@@ -5432,7 +5486,8 @@ function apply(ctx, featureConfig) {
|
|
|
5432
5486
|
const hasActivation = typeof subagents?.startActivation === 'function'
|
|
5433
5487
|
const hasLegacyStarts = typeof subagents?.start === 'function' && typeof subagents?.startContinuable === 'function'
|
|
5434
5488
|
if (!hasActivation && !hasLegacyStarts) return
|
|
5435
|
-
const isRoutable = (provider) => {
|
|
5489
|
+
const isRoutable = (provider, model) => {
|
|
5490
|
+
if (model !== undefined && !providerAvailability.modelEnabled(provider, model)) return false
|
|
5436
5491
|
const llm = ctx.get('llm')
|
|
5437
5492
|
if (llm === undefined || typeof llm.listProviders !== 'function') return false
|
|
5438
5493
|
try {
|
|
@@ -5483,6 +5538,8 @@ function apply(ctx, featureConfig) {
|
|
|
5483
5538
|
return quotaProviderUnusable(view) === false
|
|
5484
5539
|
}
|
|
5485
5540
|
const applyInjection = (request) => {
|
|
5541
|
+
const explicit = request?.agentOptions?.provider
|
|
5542
|
+
if (typeof explicit === 'string' && explicit) providerAvailability.assertEnabled(explicit, request?.agentOptions?.model)
|
|
5486
5543
|
// 功能关闭:零记录(完全静默,与原生行为一致)——记录是 subagentRoute 功能的一部分。
|
|
5487
5544
|
if (!featureEnabled('subagentRoute')) return { request, dispatch: undefined }
|
|
5488
5545
|
const routeOptions = {
|
|
@@ -5493,7 +5550,7 @@ function apply(ctx, featureConfig) {
|
|
|
5493
5550
|
}
|
|
5494
5551
|
const injected = resolveSubagentInjection(request, subagentRouteRef.current, routeOptions)
|
|
5495
5552
|
const routeCandidates = injected === undefined ? [] : subagentRouteCandidates(request, subagentRouteRef.current, routeOptions)
|
|
5496
|
-
.filter((candidate) => isRoutable(candidate.provider) && isQuotaHealthy(candidate.provider) !== false)
|
|
5553
|
+
.filter((candidate) => isRoutable(candidate.provider, candidate.model) && isQuotaHealthy(candidate.provider) !== false)
|
|
5497
5554
|
.filter((candidate, index, candidates) => candidates.findIndex((item) => item.provider === candidate.provider && item.model === candidate.model) === index)
|
|
5498
5555
|
const injectedIndex = routeCandidates.findIndex((candidate) => candidate.provider === injected?.provider && candidate.model === injected?.model)
|
|
5499
5556
|
const eligibleRoutes = injectedIndex < 0 ? [] : routeCandidates.slice(injectedIndex)
|
|
@@ -5525,17 +5582,20 @@ function apply(ctx, featureConfig) {
|
|
|
5525
5582
|
const originalStart = subagents.start
|
|
5526
5583
|
const originalStartContinuable = subagents.startContinuable
|
|
5527
5584
|
if (hasActivation) {
|
|
5528
|
-
subagents.startActivation = (spec) => {
|
|
5585
|
+
subagents.startActivation = async (spec) => {
|
|
5586
|
+
await providerAvailability.loading
|
|
5529
5587
|
const { request: decorated, dispatch } = applyInjection(spec.request)
|
|
5530
5588
|
if (dispatch === undefined) return originalStartActivation.call(subagents, spec)
|
|
5531
5589
|
return runWithDispatch(dispatch, () => originalStartActivation.call(subagents, { ...spec, request: decorated }))
|
|
5532
5590
|
}
|
|
5533
5591
|
} else {
|
|
5534
|
-
subagents.start = (name, request) => {
|
|
5592
|
+
subagents.start = async (name, request) => {
|
|
5593
|
+
await providerAvailability.loading
|
|
5535
5594
|
const { request: decorated, dispatch } = applyInjection(request)
|
|
5536
5595
|
return runWithDispatch(dispatch, () => originalStart.call(subagents, name, decorated))
|
|
5537
5596
|
}
|
|
5538
|
-
subagents.startContinuable = (spec) => {
|
|
5597
|
+
subagents.startContinuable = async (spec) => {
|
|
5598
|
+
await providerAvailability.loading
|
|
5539
5599
|
const { request: decorated, dispatch } = applyInjection(spec.request)
|
|
5540
5600
|
return runWithDispatch(dispatch, () => originalStartContinuable.call(subagents, { ...spec, request: decorated }))
|
|
5541
5601
|
}
|
|
@@ -6201,8 +6261,29 @@ function apply(ctx, featureConfig) {
|
|
|
6201
6261
|
createSkillsRoutes({ ctx, describeJobs, dshHome, makeDescribeJobLogger, registerSkillCall, serializeSkillsIndexWrite, skillFileCache, skillsActiveControllers, skillsBatchRef, skillsIndexRef, SKILL_DESCRIPTION_MAX_CHARS, SKILL_USAGE_MAX_CHARS, bodyHashOf, describeSkillDraft, evaluateSkillFile, fixLegacySkillInvocationKeys, listSkillModels, locateSkillFrontmatter, mutateSkillEntryById, name, normalizeSkillDescribeLang, publicSkillEntry, rpcTechnicalFailure, sanitizeSkillDraftText, scanSkillEntries, selectSkillBatchCandidates, setSkillInvocationKey }),
|
|
6202
6262
|
createSessionsRoutes({ ctx, dshHome, sessionBytesCache, sessionDeletePlans, sessionTitleCache, sessionTitlesReady, sessionViewCache, SESSIONS_BYTES_MAX_IDS, SESSIONS_DELETE_PLAN_TTL_MS, SESSIONS_VIEW_PAGE_SIZE, listSessionsForManage, loadDeletedSessions, name, resolveSessionBytesForIds, resolveSessionForDelete, rpcFailure, rpcTechnicalFailure, saveDeletedSessions, searchSessionsContent, sessionExists, sessionIsLive, viewSessionPage }),
|
|
6203
6263
|
createQuotaRoutes({ ctx, kickQuotaRefresh, quotaThrottle, refreshQuotaConfigCache, serializeQuotaConfigWrite, MAX_QUOTA_PROVIDER_NAME, MAX_QUOTA_RESET_CARDS, MAX_QUOTA_RESET_CARDS_PER_PROVIDER, MAX_QUOTA_RESET_CARD_ACCOUNT, canonicalResetCardExpiresAt, QUOTA_ADAPTER_BY_KIND, name, quotaCredentialConfigured, quotaCredentialEndpoint, quotaCredentialHintNames, readQuotaProfiles, resolveQuotaKind, rpcTechnicalFailure }),
|
|
6204
|
-
createSubagentRoutes({ ctx, dispatchRing, serializeSubagentRouteWrite, subagentRouteRef, subagentRouteLoadPromise, subagentSeamRef, MAX_SUBAGENT_ROUTE_FIELD, SUBAGENT_ROUTE_FALLBACK_MAX, SUBAGENT_ROUTE_MODES, listSubagentDispatches, listSubagentModels, rpcTechnicalFailure }),
|
|
6264
|
+
createSubagentRoutes({ ctx, providerAvailability, dispatchRing, serializeSubagentRouteWrite, subagentRouteRef, subagentRouteLoadPromise, subagentSeamRef, MAX_SUBAGENT_ROUTE_FIELD, SUBAGENT_ROUTE_FALLBACK_MAX, SUBAGENT_ROUTE_MODES, listSubagentDispatches, listSubagentModels, rpcTechnicalFailure }),
|
|
6205
6265
|
createBackupRoutes({ ctx, backupIntegrity, backupProgress, clearBackupProgress, downloadTokens, dshHome, dshVersion, setBackupProgress, withBackupLock, backupNameVersionSuffix, createBackup, deleteBackup, exportBackup, formatBackupTimestamp, importBackup, listBackups, listLocalProfiles, name, rpcFailure }),
|
|
6266
|
+
createModelConfigRoutes({
|
|
6267
|
+
ctx,
|
|
6268
|
+
loadPiCatalog: loadPiAiCatalog,
|
|
6269
|
+
workbenchStore,
|
|
6270
|
+
providerAvailability,
|
|
6271
|
+
notifyProviderAvailability,
|
|
6272
|
+
// 移除守卫的只读引用面:子代理路由配置 + 官方默认模型选择(都是宿主侧事实,浏览器不参与)。
|
|
6273
|
+
loadSubagentRoute: async () => {
|
|
6274
|
+
await subagentRouteLoadPromise
|
|
6275
|
+
return subagentRouteRef.current
|
|
6276
|
+
},
|
|
6277
|
+
loadAgentDefaultModelSelection: () => {
|
|
6278
|
+
const service = ctx.get('agentDefaultModel')
|
|
6279
|
+
if (service === undefined || typeof service.currentSelection !== 'function') return undefined
|
|
6280
|
+
return service.currentSelection()
|
|
6281
|
+
},
|
|
6282
|
+
writeSubagentRoute: serializeSubagentRouteWrite,
|
|
6283
|
+
listSubagentModels,
|
|
6284
|
+
dshHome,
|
|
6285
|
+
pluginVersion,
|
|
6286
|
+
}),
|
|
6206
6287
|
])
|
|
6207
6288
|
|
|
6208
6289
|
const dispatchEndpoint = createRpcDispatcher({ endpoints: rpcEndpoints, featureEnabled, logger: ctx.logger })
|
|
@@ -6312,6 +6393,45 @@ export {
|
|
|
6312
6393
|
saveUnifiedConfig,
|
|
6313
6394
|
updateUnifiedConfigSection,
|
|
6314
6395
|
sanitizeSettingsNavConfig,
|
|
6396
|
+
// 模型配置工作台(v1.10):领域纯函数经 index.js 再导出,测试从 '../index.js' 导入的
|
|
6397
|
+
// 既有约定保持不变(旧分支 test/workbench.test.js 即此形状)。
|
|
6398
|
+
WORKBENCH_SIDECAR_VERSION,
|
|
6399
|
+
applySuggestions,
|
|
6400
|
+
applyUndo,
|
|
6401
|
+
arbitrateModelSuggestions,
|
|
6402
|
+
buildPiAiIndex,
|
|
6403
|
+
createWorkbenchStore,
|
|
6404
|
+
deriveKeyRefFor,
|
|
6405
|
+
diffModelLists,
|
|
6406
|
+
emptyWorkbenchSidecar,
|
|
6407
|
+
fetchModelsProbe,
|
|
6408
|
+
hostnameOf,
|
|
6409
|
+
isPrivateIpAddress,
|
|
6410
|
+
loadWorkbenchSidecar,
|
|
6411
|
+
modelPatchFromPiAi,
|
|
6412
|
+
modelsProbeLookup,
|
|
6413
|
+
normalizeBaseURLForMatch,
|
|
6414
|
+
parseModelListing,
|
|
6415
|
+
probeBaseURLCheck,
|
|
6416
|
+
profileFingerprint,
|
|
6417
|
+
readLlmProfiles,
|
|
6418
|
+
saveWorkbenchSidecar,
|
|
6419
|
+
selfCheckModelsPatch,
|
|
6420
|
+
suggestRouteId,
|
|
6421
|
+
suggestionId,
|
|
6422
|
+
undoPreviewModels,
|
|
6423
|
+
// P3.5 补充源(modelparams.dev)与多源共识:同样经 index.js 再导出供测试直达。
|
|
6424
|
+
abilityPatch,
|
|
6425
|
+
consensusFields,
|
|
6426
|
+
createFreshnessManager,
|
|
6427
|
+
indexModelParamsDev,
|
|
6428
|
+
loadModelParamsSnapshot,
|
|
6429
|
+
modelParamsCandidates,
|
|
6430
|
+
modelParamsDevEfforts,
|
|
6431
|
+
openRouterModelFacts,
|
|
6432
|
+
openRouterVerifySource,
|
|
6433
|
+
pickOpenRouterMatch,
|
|
6434
|
+
saveModelParamsSnapshot,
|
|
6315
6435
|
}
|
|
6316
6436
|
// 插件 Config:0.1.7-alpha.1 起宿主配置面由 Profile 的插件 Config 托管;声明它即可让
|
|
6317
6437
|
// 功能开关出现在插件管理页,并让宿主经 `loader/volatile-update` 热更新(无需重启)。
|