@lemoncat7/dsh-knowledge 1.0.11 → 1.0.14
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/lib/api.d.ts +9 -1
- package/lib/api.d.ts.map +1 -1
- package/lib/api.js +12 -24
- package/lib/api.js.map +1 -1
- package/lib/client.js +28 -5
- package/lib/client.js.map +2 -2
- package/lib/extraction.d.ts +5 -1
- package/lib/extraction.d.ts.map +1 -1
- package/lib/extraction.js +8 -11
- package/lib/extraction.js.map +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +125 -32
- package/lib/index.js.map +1 -1
- package/lib/runtime.d.ts +15 -0
- package/lib/runtime.d.ts.map +1 -1
- package/lib/runtime.js.map +1 -1
- package/lib/service-settings.d.ts +2 -0
- package/lib/service-settings.d.ts.map +1 -1
- package/lib/service-settings.js +7 -2
- package/lib/service-settings.js.map +1 -1
- package/lib/tools.js +0 -26
- package/lib/tools.js.map +1 -1
- package/package.json +1 -1
- package/web/app.js +63 -46
package/web/app.js
CHANGED
|
@@ -64,6 +64,7 @@ const state = {
|
|
|
64
64
|
candidateTargets: new Map(),
|
|
65
65
|
candidateStatus: 'pending',
|
|
66
66
|
settings: { writebackPolicy: 'conservative', updatedAt: '' },
|
|
67
|
+
modelCatalog: null,
|
|
67
68
|
settingsSaving: false,
|
|
68
69
|
tokens: [],
|
|
69
70
|
service: { publicApiEnabled: false, publicApiPrefix: '/knowledge-api/v1', remote: false },
|
|
@@ -348,16 +349,17 @@ async function ensureKnowledgeBases(force = false) {
|
|
|
348
349
|
}
|
|
349
350
|
|
|
350
351
|
async function loadKnowledgeBasesPage() {
|
|
351
|
-
const requests = [api('knowledge-bases'), api('mounts'), api('settings')]
|
|
352
|
+
const requests = [api('knowledge-bases'), api('mounts'), api('settings'), api('service')]
|
|
352
353
|
if (state.mountContext.sessionId) {
|
|
353
354
|
const params = new URLSearchParams({ sessionId: state.mountContext.sessionId })
|
|
354
355
|
if (state.mountContext.projectId) params.set('projectId', state.mountContext.projectId)
|
|
355
356
|
requests.push(api(`mounts/resolve?${params}`))
|
|
356
357
|
}
|
|
357
|
-
const [bases, mounts, settings, resolved = []] = await Promise.all(requests)
|
|
358
|
+
const [bases, mounts, settings, service, resolved = []] = await Promise.all(requests)
|
|
358
359
|
state.knowledgeBases = bases
|
|
359
360
|
state.mounts = mounts
|
|
360
361
|
state.settings = settings
|
|
362
|
+
state.service = service
|
|
361
363
|
state.resolvedMounts = resolved
|
|
362
364
|
await refreshStats()
|
|
363
365
|
}
|
|
@@ -751,7 +753,7 @@ function renderKnowledgeBases() {
|
|
|
751
753
|
const archivedBases = state.knowledgeBases.filter(base => base.status === 'archived')
|
|
752
754
|
const contextAvailable = Boolean(state.mountContext.projectId || state.mountContext.sessionId)
|
|
753
755
|
const query = state.knowledgeBaseQuery.trim().toLocaleLowerCase()
|
|
754
|
-
const matchesQuery = base => !query || [base.name, base.description, base.defaultTags.join(' ')
|
|
756
|
+
const matchesQuery = base => !query || [base.name, base.description, base.defaultTags.join(' ')]
|
|
755
757
|
.some(value => String(value || '').toLocaleLowerCase().includes(query))
|
|
756
758
|
const visibleActiveBases = activeBases.filter(matchesQuery)
|
|
757
759
|
const visibleArchivedBases = archivedBases.filter(matchesQuery)
|
|
@@ -764,11 +766,11 @@ function renderKnowledgeBases() {
|
|
|
764
766
|
type: 'button', role: 'tab', class: 'tab', 'aria-selected': String(state.knowledgeBaseView === id),
|
|
765
767
|
onClick: () => { state.knowledgeBaseView = id; renderShell() },
|
|
766
768
|
}, element('span', {}, label), element('span', { class: 'tab-count' }, count)))),
|
|
767
|
-
actionButton('
|
|
769
|
+
actionButton('本机回写模型', () => openGlobalWritebackModelEditor(), 'ghost small'),
|
|
768
770
|
),
|
|
769
|
-
element('p', {}, state.
|
|
770
|
-
?
|
|
771
|
-
: '
|
|
771
|
+
element('p', {}, state.service.writebackProvider && state.service.writebackModel
|
|
772
|
+
? `仅当前客户端使用 ${state.service.writebackProvider} / ${state.service.writebackModel} 回写。`
|
|
773
|
+
: '当前客户端跟随每轮会话模型回写。'),
|
|
772
774
|
)
|
|
773
775
|
return element('div', { class: 'bases-page' },
|
|
774
776
|
switcher,
|
|
@@ -780,7 +782,7 @@ function renderKnowledgeBases() {
|
|
|
780
782
|
element('div', { class: 'knowledge-base-toolbar' },
|
|
781
783
|
element('div', { class: 'search-box base-search' }, interfaceIcon('search', 'search-symbol'), element('input', {
|
|
782
784
|
class: 'input', type: 'search', value: state.knowledgeBaseQuery,
|
|
783
|
-
placeholder: '
|
|
785
|
+
placeholder: '搜索名称、描述或标签', 'aria-label': '搜索知识库',
|
|
784
786
|
onInput: event => {
|
|
785
787
|
state.knowledgeBaseQuery = event.target.value
|
|
786
788
|
renderShell()
|
|
@@ -794,7 +796,7 @@ function renderKnowledgeBases() {
|
|
|
794
796
|
visibleActiveBases.length
|
|
795
797
|
? element('div', { class: 'base-grid' }, visibleActiveBases.map(renderKnowledgeBaseCard))
|
|
796
798
|
: query && visibleArchivedBases.length === 0
|
|
797
|
-
? emptyState('没有匹配的知识库', '
|
|
799
|
+
? emptyState('没有匹配的知识库', '尝试搜索名称、描述或标签。')
|
|
798
800
|
: !query ? emptyState('还没有可用知识库', '使用右上角按钮创建第一个知识库。') : null,
|
|
799
801
|
visibleArchivedBases.length ? element('details', { class: 'archived-bases', open: Boolean(query) },
|
|
800
802
|
element('summary', {}, element('span', {}, '已归档知识库'), element('span', { class: 'summary-count' }, visibleArchivedBases.length)),
|
|
@@ -818,22 +820,22 @@ function renderKnowledgeBases() {
|
|
|
818
820
|
)
|
|
819
821
|
}
|
|
820
822
|
|
|
821
|
-
function openGlobalWritebackModelEditor() {
|
|
822
|
-
|
|
823
|
-
const
|
|
824
|
-
const
|
|
823
|
+
async function openGlobalWritebackModelEditor() {
|
|
824
|
+
await loadModelCatalog()
|
|
825
|
+
const custom = element('input', { type: 'checkbox', checked: Boolean(state.service.writebackProvider && state.service.writebackModel) })
|
|
826
|
+
const route = modelRouteFields(state.service.writebackProvider || '', state.service.writebackModel || '')
|
|
825
827
|
const form = element('form', { class: 'form-grid' },
|
|
826
|
-
element('label', { class: 'check-option span-2' }, custom, element('span', {}, element('strong', {}, '
|
|
827
|
-
provider.wrapper, model.wrapper,
|
|
828
|
+
element('label', { class: 'check-option span-2' }, custom, element('span', {}, element('strong', {}, '当前客户端使用专用模型'), element('small', {}, '关闭后,跟随当前客户端每轮会话实际使用的模型。'))),
|
|
829
|
+
route.provider.wrapper, route.model.wrapper,
|
|
828
830
|
)
|
|
829
|
-
const sync = () => { provider.input.disabled = model.input.disabled = !custom.checked; provider.input.required = model.input.required = custom.checked }
|
|
831
|
+
const sync = () => { route.provider.input.disabled = route.model.input.disabled = !custom.checked; route.provider.input.required = route.model.input.required = custom.checked }
|
|
830
832
|
custom.addEventListener('change', sync); sync()
|
|
831
|
-
openSheet({ title: '
|
|
833
|
+
openSheet({ title: '本机回写模型', description: '此设置只保存在当前 DSH 客户端,不会写入中央知识库或影响其他设备。', body: form, primaryLabel: '保存', onPrimary: async () => {
|
|
832
834
|
if (!form.reportValidity()) return false
|
|
833
|
-
state.
|
|
834
|
-
? { writebackProvider: provider.input.value
|
|
835
|
-
: { writebackProvider: null, writebackModel: null } }
|
|
836
|
-
showToast('
|
|
835
|
+
state.service = await api('service', { method: 'PUT', body: custom.checked
|
|
836
|
+
? { writebackProvider: route.provider.input.value, writebackModel: route.model.input.value }
|
|
837
|
+
: { writebackProvider: null, writebackModel: null } })
|
|
838
|
+
showToast('当前客户端的回写模型已更新。'); renderShell(); return true
|
|
837
839
|
} })
|
|
838
840
|
}
|
|
839
841
|
|
|
@@ -860,7 +862,7 @@ function renderKnowledgeBaseCard(base) {
|
|
|
860
862
|
hiddenTagCount ? element('span', { class: 'tag' }, `+${hiddenTagCount}`) : null),
|
|
861
863
|
element('div', { class: 'base-card-meta' },
|
|
862
864
|
element('span', {}, element('strong', {}, '回写策略'), base.writebackPolicy === 'proactive' ? '主动' : '严谨'),
|
|
863
|
-
element('span', {}, element('strong', {}, '回写模型'),
|
|
865
|
+
element('span', {}, element('strong', {}, '回写模型'), state.service.writebackProvider && state.service.writebackModel ? `本机 · ${state.service.writebackProvider} / ${state.service.writebackModel}` : '跟随当前会话'),
|
|
864
866
|
base.extractionInstructions ? element('span', {}, element('strong', {}, '提取规则'), '已设置') : null,
|
|
865
867
|
),
|
|
866
868
|
element('div', { class: 'base-card-actions' },
|
|
@@ -920,15 +922,14 @@ function renderMountManager(activeBases) {
|
|
|
920
922
|
const query = manager.query.trim().toLowerCase()
|
|
921
923
|
const rows = activeBases.map(base => ({ base, view: mountView(base, manager.targetKind, targetId) }))
|
|
922
924
|
const visibleRows = rows.filter(({ base, view }) => {
|
|
923
|
-
const
|
|
924
|
-
const searchable = `${base.name} ${base.description} ${base.defaultTags.join(' ')} ${model}`.toLowerCase()
|
|
925
|
+
const searchable = `${base.name} ${base.description} ${base.defaultTags.join(' ')}`.toLowerCase()
|
|
925
926
|
return (!query || searchable.includes(query)) && (manager.filter === 'all' || manager.filter === view.statusKey)
|
|
926
927
|
})
|
|
927
928
|
const visibleIds = visibleRows.map(({ base }) => base.id)
|
|
928
929
|
const selectedCount = manager.selectedIds.size
|
|
929
930
|
const searchInput = element('input', {
|
|
930
931
|
class: 'input', type: 'search', value: manager.query,
|
|
931
|
-
placeholder: '
|
|
932
|
+
placeholder: '搜索名称、描述或标签', 'aria-label': '搜索可挂载知识库',
|
|
932
933
|
onInput: (event) => { manager.query = event.target.value; renderShell() },
|
|
933
934
|
})
|
|
934
935
|
const filter = selectControl('筛选挂载状态', [
|
|
@@ -982,8 +983,8 @@ function renderMountListRow(base, view, targetKind, targetId) {
|
|
|
982
983
|
renderShell()
|
|
983
984
|
},
|
|
984
985
|
})
|
|
985
|
-
const modelLabel =
|
|
986
|
-
?
|
|
986
|
+
const modelLabel = state.service.writebackProvider && state.service.writebackModel
|
|
987
|
+
? `本机 · ${state.service.writebackProvider} / ${state.service.writebackModel}`
|
|
987
988
|
: '跟随当前会话模型'
|
|
988
989
|
return element('article', { class: `mount-list-row${selected ? ' is-selected' : ''}`, role: 'listitem' },
|
|
989
990
|
element('label', { class: 'mount-select' }, checkbox, element('span', { class: 'visually-hidden' }, `选择 ${base.name}`)),
|
|
@@ -1410,26 +1411,9 @@ function openKnowledgeBaseEditor(base) {
|
|
|
1410
1411
|
{ value: 'conservative', label: '严谨(高置信度直写)' },
|
|
1411
1412
|
{ value: 'proactive', label: '主动(更积极沉淀)' },
|
|
1412
1413
|
], source.writebackPolicy || 'conservative')
|
|
1413
|
-
const customModel = element('input', { type: 'checkbox', checked: Boolean(source.writebackProvider && source.writebackModel) })
|
|
1414
|
-
const provider = formField('模型提供方(Provider)', 'input', source.writebackProvider || '', { maxlength: 100, placeholder: '例如:cli' })
|
|
1415
|
-
const model = formField('模型名称(Model)', 'input', source.writebackModel || '', { maxlength: 200, placeholder: '例如:kimi-k2.7-code' })
|
|
1416
|
-
const modelHint = element('div', { class: 'field-hint span-2' }, '默认跟随当前会话模型。指定后,仅这个知识库使用专用模型回写。')
|
|
1417
|
-
const updateModelFields = () => {
|
|
1418
|
-
provider.input.disabled = !customModel.checked
|
|
1419
|
-
model.input.disabled = !customModel.checked
|
|
1420
|
-
provider.input.required = customModel.checked
|
|
1421
|
-
model.input.required = customModel.checked
|
|
1422
|
-
}
|
|
1423
|
-
customModel.addEventListener('change', updateModelFields)
|
|
1424
|
-
updateModelFields()
|
|
1425
1414
|
for (const field of [name, description, tags, instructions]) field.wrapper.classList.add('span-2')
|
|
1426
1415
|
form.append(
|
|
1427
1416
|
name.wrapper, description.wrapper, tags.wrapper, instructions.wrapper, policy.wrapper,
|
|
1428
|
-
element('label', { class: 'check-option span-2' }, customModel, element('span', {},
|
|
1429
|
-
element('strong', {}, '为这个知识库指定回写模型'),
|
|
1430
|
-
element('small', {}, '关闭时自动使用当前会话正在使用的模型。'),
|
|
1431
|
-
)),
|
|
1432
|
-
provider.wrapper, model.wrapper, modelHint,
|
|
1433
1417
|
)
|
|
1434
1418
|
openSheet({
|
|
1435
1419
|
title: base ? '编辑知识库' : '创建知识库',
|
|
@@ -1444,8 +1428,6 @@ function openKnowledgeBaseEditor(base) {
|
|
|
1444
1428
|
defaultTags: parseTags(tags.input.value),
|
|
1445
1429
|
extractionInstructions: instructions.input.value.trim(),
|
|
1446
1430
|
writebackPolicy: policy.input.value,
|
|
1447
|
-
writebackProvider: customModel.checked ? provider.input.value.trim() : '',
|
|
1448
|
-
writebackModel: customModel.checked ? model.input.value.trim() : '',
|
|
1449
1431
|
}
|
|
1450
1432
|
if (base) await api(`knowledge-bases/${encodeURIComponent(base.id)}`, { method: 'PUT', body: { draft } })
|
|
1451
1433
|
else await api('knowledge-bases', { method: 'POST', body: { draft } })
|
|
@@ -1741,6 +1723,41 @@ function selectField(label, options, value) {
|
|
|
1741
1723
|
return { input, wrapper: element('div', { class: 'field' }, element('label', {}, label), input) }
|
|
1742
1724
|
}
|
|
1743
1725
|
|
|
1726
|
+
async function loadModelCatalog() {
|
|
1727
|
+
if (state.modelCatalog !== null) return state.modelCatalog
|
|
1728
|
+
try {
|
|
1729
|
+
const response = await fetch('/knowledge-control/v1/models', { headers: { accept: 'application/json' } })
|
|
1730
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`)
|
|
1731
|
+
const payload = await response.json()
|
|
1732
|
+
state.modelCatalog = Array.isArray(payload.providers) ? payload.providers : []
|
|
1733
|
+
} catch (error) {
|
|
1734
|
+
state.modelCatalog = []
|
|
1735
|
+
showToast(`无法读取当前 DSH 的模型目录:${error instanceof Error ? error.message : String(error)}`, 'error')
|
|
1736
|
+
}
|
|
1737
|
+
return state.modelCatalog
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
function modelRouteFields(currentProvider, currentModel) {
|
|
1741
|
+
const providers = [...(state.modelCatalog || [])]
|
|
1742
|
+
if (currentProvider && !providers.some(item => item.id === currentProvider)) {
|
|
1743
|
+
providers.push({ id: currentProvider, name: `${currentProvider}(当前配置)`, models: [] })
|
|
1744
|
+
}
|
|
1745
|
+
const provider = selectField('模型提供方', providers.map(item => ({ value: item.id, label: item.name || item.id })), currentProvider || providers[0]?.id || '')
|
|
1746
|
+
const model = selectField('回写模型', [], '')
|
|
1747
|
+
const refreshModels = preferred => {
|
|
1748
|
+
const selected = providers.find(item => item.id === provider.input.value)
|
|
1749
|
+
const models = [...(selected?.models || [])]
|
|
1750
|
+
if (preferred && !models.some(item => item.id === preferred)) models.push({ id: preferred, name: `${preferred}(当前配置)` })
|
|
1751
|
+
model.input.replaceChildren(...models.map(item => element('option', {
|
|
1752
|
+
value: item.id, selected: item.id === preferred,
|
|
1753
|
+
}, item.name && item.name !== item.id ? `${item.name} · ${item.id}` : item.id)))
|
|
1754
|
+
if (preferred && models.some(item => item.id === preferred)) model.input.value = preferred
|
|
1755
|
+
}
|
|
1756
|
+
provider.input.addEventListener('change', () => refreshModels(''))
|
|
1757
|
+
refreshModels(currentModel)
|
|
1758
|
+
return { provider, model }
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1744
1761
|
async function reviewCandidate(candidate, decision, resolution) {
|
|
1745
1762
|
const approve = decision === 'approve'
|
|
1746
1763
|
openConfirm({
|