@lemoncat7/dsh-knowledge 1.0.13 → 1.1.0

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/web/app.js CHANGED
@@ -349,16 +349,17 @@ async function ensureKnowledgeBases(force = false) {
349
349
  }
350
350
 
351
351
  async function loadKnowledgeBasesPage() {
352
- const requests = [api('knowledge-bases'), api('mounts'), api('settings')]
352
+ const requests = [api('knowledge-bases'), api('mounts'), api('settings'), api('service')]
353
353
  if (state.mountContext.sessionId) {
354
354
  const params = new URLSearchParams({ sessionId: state.mountContext.sessionId })
355
355
  if (state.mountContext.projectId) params.set('projectId', state.mountContext.projectId)
356
356
  requests.push(api(`mounts/resolve?${params}`))
357
357
  }
358
- const [bases, mounts, settings, resolved = []] = await Promise.all(requests)
358
+ const [bases, mounts, settings, service, resolved = []] = await Promise.all(requests)
359
359
  state.knowledgeBases = bases
360
360
  state.mounts = mounts
361
361
  state.settings = settings
362
+ state.service = service
362
363
  state.resolvedMounts = resolved
363
364
  await refreshStats()
364
365
  }
@@ -752,7 +753,7 @@ function renderKnowledgeBases() {
752
753
  const archivedBases = state.knowledgeBases.filter(base => base.status === 'archived')
753
754
  const contextAvailable = Boolean(state.mountContext.projectId || state.mountContext.sessionId)
754
755
  const query = state.knowledgeBaseQuery.trim().toLocaleLowerCase()
755
- const matchesQuery = base => !query || [base.name, base.description, base.defaultTags.join(' '), base.writebackProvider, base.writebackModel]
756
+ const matchesQuery = base => !query || [base.name, base.description, base.defaultTags.join(' ')]
756
757
  .some(value => String(value || '').toLocaleLowerCase().includes(query))
757
758
  const visibleActiveBases = activeBases.filter(matchesQuery)
758
759
  const visibleArchivedBases = archivedBases.filter(matchesQuery)
@@ -765,11 +766,11 @@ function renderKnowledgeBases() {
765
766
  type: 'button', role: 'tab', class: 'tab', 'aria-selected': String(state.knowledgeBaseView === id),
766
767
  onClick: () => { state.knowledgeBaseView = id; renderShell() },
767
768
  }, element('span', {}, label), element('span', { class: 'tab-count' }, count)))),
768
- actionButton('默认回写模型', () => openGlobalWritebackModelEditor(), 'ghost small'),
769
+ actionButton('本机回写模型', () => openGlobalWritebackModelEditor(), 'ghost small'),
769
770
  ),
770
- element('p', {}, state.settings.writebackProvider && state.settings.writebackModel
771
- ? `未单独指定模型的知识库使用 ${state.settings.writebackProvider} / ${state.settings.writebackModel}。`
772
- : '未单独指定模型的知识库跟随当前会话模型。'),
771
+ element('p', {}, state.service.writebackProvider && state.service.writebackModel
772
+ ? `仅当前客户端使用 ${state.service.writebackProvider} / ${state.service.writebackModel} 回写。`
773
+ : '当前客户端跟随每轮会话模型回写。'),
773
774
  )
774
775
  return element('div', { class: 'bases-page' },
775
776
  switcher,
@@ -781,7 +782,7 @@ function renderKnowledgeBases() {
781
782
  element('div', { class: 'knowledge-base-toolbar' },
782
783
  element('div', { class: 'search-box base-search' }, interfaceIcon('search', 'search-symbol'), element('input', {
783
784
  class: 'input', type: 'search', value: state.knowledgeBaseQuery,
784
- placeholder: '搜索名称、描述、标签或模型', 'aria-label': '搜索知识库',
785
+ placeholder: '搜索名称、描述或标签', 'aria-label': '搜索知识库',
785
786
  onInput: event => {
786
787
  state.knowledgeBaseQuery = event.target.value
787
788
  renderShell()
@@ -795,7 +796,7 @@ function renderKnowledgeBases() {
795
796
  visibleActiveBases.length
796
797
  ? element('div', { class: 'base-grid' }, visibleActiveBases.map(renderKnowledgeBaseCard))
797
798
  : query && visibleArchivedBases.length === 0
798
- ? emptyState('没有匹配的知识库', '尝试搜索名称、描述、标签或回写模型。')
799
+ ? emptyState('没有匹配的知识库', '尝试搜索名称、描述或标签。')
799
800
  : !query ? emptyState('还没有可用知识库', '使用右上角按钮创建第一个知识库。') : null,
800
801
  visibleArchivedBases.length ? element('details', { class: 'archived-bases', open: Boolean(query) },
801
802
  element('summary', {}, element('span', {}, '已归档知识库'), element('span', { class: 'summary-count' }, visibleArchivedBases.length)),
@@ -821,20 +822,20 @@ function renderKnowledgeBases() {
821
822
 
822
823
  async function openGlobalWritebackModelEditor() {
823
824
  await loadModelCatalog()
824
- const custom = element('input', { type: 'checkbox', checked: Boolean(state.settings.writebackProvider && state.settings.writebackModel) })
825
- const route = modelRouteFields(state.settings.writebackProvider || '', state.settings.writebackModel || '')
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 || '')
826
827
  const form = element('form', { class: 'form-grid' },
827
- element('label', { class: 'check-option span-2' }, custom, element('span', {}, element('strong', {}, '使用全局默认模型'), element('small', {}, '关闭后,未单独配置的知识库跟随当前会话模型。'))),
828
+ element('label', { class: 'check-option span-2' }, custom, element('span', {}, element('strong', {}, '当前客户端使用专用模型'), element('small', {}, '关闭后,跟随当前客户端每轮会话实际使用的模型。'))),
828
829
  route.provider.wrapper, route.model.wrapper,
829
830
  )
830
831
  const sync = () => { route.provider.input.disabled = route.model.input.disabled = !custom.checked; route.provider.input.required = route.model.input.required = custom.checked }
831
832
  custom.addEventListener('change', sync); sync()
832
- openSheet({ title: '默认回写模型', description: '知识库自己的模型优先于这里;这里留空则跟随当前会话。', body: form, primaryLabel: '保存', onPrimary: async () => {
833
+ openSheet({ title: '本机回写模型', description: '此设置只保存在当前 DSH 客户端,不会写入中央知识库或影响其他设备。', body: form, primaryLabel: '保存', onPrimary: async () => {
833
834
  if (!form.reportValidity()) return false
834
- state.settings = await api('settings', { method: 'PUT', body: { patch: custom.checked
835
+ state.service = await api('service', { method: 'PUT', body: custom.checked
835
836
  ? { writebackProvider: route.provider.input.value, writebackModel: route.model.input.value }
836
- : { writebackProvider: null, writebackModel: null } } })
837
- showToast('默认回写模型已更新。'); renderShell(); return true
837
+ : { writebackProvider: null, writebackModel: null } })
838
+ showToast('当前客户端的回写模型已更新。'); renderShell(); return true
838
839
  } })
839
840
  }
840
841
 
@@ -861,7 +862,7 @@ function renderKnowledgeBaseCard(base) {
861
862
  hiddenTagCount ? element('span', { class: 'tag' }, `+${hiddenTagCount}`) : null),
862
863
  element('div', { class: 'base-card-meta' },
863
864
  element('span', {}, element('strong', {}, '回写策略'), base.writebackPolicy === 'proactive' ? '主动' : '严谨'),
864
- element('span', {}, element('strong', {}, '回写模型'), base.writebackProvider && base.writebackModel ? `${base.writebackProvider} / ${base.writebackModel}` : state.settings.writebackProvider && state.settings.writebackModel ? `默认 · ${state.settings.writebackProvider} / ${state.settings.writebackModel}` : '跟随当前会话'),
865
+ element('span', {}, element('strong', {}, '回写模型'), state.service.writebackProvider && state.service.writebackModel ? `本机 · ${state.service.writebackProvider} / ${state.service.writebackModel}` : '跟随当前会话'),
865
866
  base.extractionInstructions ? element('span', {}, element('strong', {}, '提取规则'), '已设置') : null,
866
867
  ),
867
868
  element('div', { class: 'base-card-actions' },
@@ -921,15 +922,14 @@ function renderMountManager(activeBases) {
921
922
  const query = manager.query.trim().toLowerCase()
922
923
  const rows = activeBases.map(base => ({ base, view: mountView(base, manager.targetKind, targetId) }))
923
924
  const visibleRows = rows.filter(({ base, view }) => {
924
- const model = base.writebackProvider && base.writebackModel ? `${base.writebackProvider} ${base.writebackModel}` : '跟随会话'
925
- const searchable = `${base.name} ${base.description} ${base.defaultTags.join(' ')} ${model}`.toLowerCase()
925
+ const searchable = `${base.name} ${base.description} ${base.defaultTags.join(' ')}`.toLowerCase()
926
926
  return (!query || searchable.includes(query)) && (manager.filter === 'all' || manager.filter === view.statusKey)
927
927
  })
928
928
  const visibleIds = visibleRows.map(({ base }) => base.id)
929
929
  const selectedCount = manager.selectedIds.size
930
930
  const searchInput = element('input', {
931
931
  class: 'input', type: 'search', value: manager.query,
932
- placeholder: '搜索名称、描述、标签或模型', 'aria-label': '搜索可挂载知识库',
932
+ placeholder: '搜索名称、描述或标签', 'aria-label': '搜索可挂载知识库',
933
933
  onInput: (event) => { manager.query = event.target.value; renderShell() },
934
934
  })
935
935
  const filter = selectControl('筛选挂载状态', [
@@ -983,8 +983,8 @@ function renderMountListRow(base, view, targetKind, targetId) {
983
983
  renderShell()
984
984
  },
985
985
  })
986
- const modelLabel = base.writebackProvider && base.writebackModel
987
- ? `${base.writebackProvider} / ${base.writebackModel}`
986
+ const modelLabel = state.service.writebackProvider && state.service.writebackModel
987
+ ? `本机 · ${state.service.writebackProvider} / ${state.service.writebackModel}`
988
988
  : '跟随当前会话模型'
989
989
  return element('article', { class: `mount-list-row${selected ? ' is-selected' : ''}`, role: 'listitem' },
990
990
  element('label', { class: 'mount-select' }, checkbox, element('span', { class: 'visually-hidden' }, `选择 ${base.name}`)),
@@ -1400,8 +1400,7 @@ function parseTags(value) {
1400
1400
  return [...new Set(value.split(/[,,]/).map(tag => tag.trim().toLowerCase()).filter(Boolean))]
1401
1401
  }
1402
1402
 
1403
- async function openKnowledgeBaseEditor(base) {
1404
- await loadModelCatalog()
1403
+ function openKnowledgeBaseEditor(base) {
1405
1404
  const source = base || { name: '', description: '', defaultTags: [], extractionInstructions: '', writebackPolicy: 'conservative' }
1406
1405
  const form = element('form', { class: 'form-grid' })
1407
1406
  const name = formField('名称', 'input', source.name, { required: true, maxlength: 100, placeholder: '例如:项目规范' })
@@ -1412,25 +1411,9 @@ async function openKnowledgeBaseEditor(base) {
1412
1411
  { value: 'conservative', label: '严谨(高置信度直写)' },
1413
1412
  { value: 'proactive', label: '主动(更积极沉淀)' },
1414
1413
  ], source.writebackPolicy || 'conservative')
1415
- const customModel = element('input', { type: 'checkbox', checked: Boolean(source.writebackProvider && source.writebackModel) })
1416
- const route = modelRouteFields(source.writebackProvider || '', source.writebackModel || '')
1417
- const modelHint = element('div', { class: 'field-hint span-2' }, '默认跟随当前会话模型。指定后,仅这个知识库使用专用模型回写。')
1418
- const updateModelFields = () => {
1419
- route.provider.input.disabled = !customModel.checked
1420
- route.model.input.disabled = !customModel.checked
1421
- route.provider.input.required = customModel.checked
1422
- route.model.input.required = customModel.checked
1423
- }
1424
- customModel.addEventListener('change', updateModelFields)
1425
- updateModelFields()
1426
1414
  for (const field of [name, description, tags, instructions]) field.wrapper.classList.add('span-2')
1427
1415
  form.append(
1428
1416
  name.wrapper, description.wrapper, tags.wrapper, instructions.wrapper, policy.wrapper,
1429
- element('label', { class: 'check-option span-2' }, customModel, element('span', {},
1430
- element('strong', {}, '为这个知识库指定回写模型'),
1431
- element('small', {}, '关闭时自动使用当前会话正在使用的模型。'),
1432
- )),
1433
- route.provider.wrapper, route.model.wrapper, modelHint,
1434
1417
  )
1435
1418
  openSheet({
1436
1419
  title: base ? '编辑知识库' : '创建知识库',
@@ -1445,8 +1428,6 @@ async function openKnowledgeBaseEditor(base) {
1445
1428
  defaultTags: parseTags(tags.input.value),
1446
1429
  extractionInstructions: instructions.input.value.trim(),
1447
1430
  writebackPolicy: policy.input.value,
1448
- writebackProvider: customModel.checked ? route.provider.input.value : '',
1449
- writebackModel: customModel.checked ? route.model.input.value : '',
1450
1431
  }
1451
1432
  if (base) await api(`knowledge-bases/${encodeURIComponent(base.id)}`, { method: 'PUT', body: { draft } })
1452
1433
  else await api('knowledge-bases', { method: 'POST', body: { draft } })