@p-dsh-market/conversation-knowledge-map 0.1.7 → 0.1.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/lib/client.js CHANGED
@@ -268,9 +268,17 @@ window.__ModuleLoader__.load({
268
268
 
269
269
  function GenerationStrip(props) {
270
270
  var generation = props.store.generation
271
+ var progress = generation.progress || { percent: 0, current: 0, total: 0, label: '' }
272
+ var percent = Math.max(0, Math.min(100, Number(progress.percent) || 0))
271
273
  return React.createElement('div', { className: 'ckm-generation-strip', 'data-status': generation.status }, [
272
- React.createElement('strong', { key: 'status' }, generation.message || generation.status),
273
- generation.error ? React.createElement('span', { key: 'error' }, generation.error) : null,
274
+ React.createElement('div', { key: 'body', className: 'ckm-generation-body' }, [
275
+ React.createElement('div', { key: 'status-row', className: 'ckm-generation-status-row' }, [
276
+ React.createElement('strong', { key: 'status' }, generation.message || generation.status),
277
+ React.createElement('span', { key: 'detail' }, progress.total ? (progress.current + '/' + progress.total + ' 个对话 · ' + progress.label) : progress.label)
278
+ ]),
279
+ React.createElement('div', { key: 'progress', className: 'ckm-progress-track', role: 'progressbar', 'aria-valuemin': 0, 'aria-valuemax': 100, 'aria-valuenow': percent, 'aria-label': '知识视图生成进度' }, React.createElement('span', { className: 'ckm-progress-value', style: { width: percent + '%' } }))
280
+ ]),
281
+ generation.error ? React.createElement('span', { key: 'error', className: 'ckm-generation-error' }, generation.error) : null,
274
282
  ['failed', 'cancelled'].includes(generation.status) ? null : Button({ key: 'cancel', className: 'ckm-danger', onClick: function () { request('/generations/' + encodeURIComponent(generation.id) + '/cancel', { method: 'POST' }).then(function (body) { applyGeneration(props.store, body.generation) }).catch(function (error) { props.store.error = error.message; notifyStore(props.store) }) } }, '取消')
275
283
  ])
276
284
  }
@@ -563,7 +571,7 @@ window.__ModuleLoader__.load({
563
571
  '.ckm-page-header h2{margin:0;font-size:16px}.ckm-page-header p{margin:3px 0 0;color:var(--dsw-alias-label-secondary);font-size:11px;word-break:break-all}' +
564
572
  '.ckm-page-actions,.ckm-modal-actions{display:flex;align-items:center;gap:8px}.ckm-tab,.ckm-secondary,.ckm-primary,.ckm-danger,.ckm-header-action{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:7px 11px;background:transparent;color:var(--dsw-alias-label-primary);cursor:pointer;font:inherit;font-size:12px}.ckm-tab[data-active=true],.ckm-primary{border-color:var(--dsw-alias-button-info-fill);background:var(--dsw-alias-button-info-fill);color:var(--dsw-alias-label-primary-foreground)}.ckm-tab:hover,.ckm-secondary:hover,.ckm-header-action:hover{background:var(--dsw-alias-interactive-bg-hover)}.ckm-primary:hover{background:var(--dsw-alias-button-info-hover)}.ckm-danger{border-color:#d36b6b;color:#ffb7b7}.ckm-danger:hover{background:#d94a4a22}.ckm-page button:disabled,.ckm-modal button:disabled{opacity:.5;cursor:not-allowed}' +
565
573
  '.ckm-empty{display:flex;flex:1;min-height:260px;flex-direction:column;align-items:center;justify-content:center;padding:28px;text-align:center}.ckm-empty-icon{display:grid;place-items:center;width:44px;height:44px;margin-bottom:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:14px;color:var(--dsw-alias-state-business-primary);font-size:24px}.ckm-empty h2{margin:0 0 8px;font-size:17px}.ckm-empty p{max-width:520px;margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.7}' +
566
- '.ckm-generation-strip{display:flex;align-items:center;gap:12px;padding:9px 20px;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-state-business-tertiary);font-size:12px}.ckm-generation-strip span{color:var(--dsw-alias-label-secondary)}.ckm-generation-strip .ckm-danger{margin-left:auto}' +
574
+ '.ckm-generation-strip{display:flex;align-items:center;gap:12px;padding:10px 20px;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-state-business-tertiary);font-size:12px}.ckm-generation-body{display:grid;gap:7px;flex:1;min-width:0}.ckm-generation-status-row{display:flex;justify-content:space-between;gap:12px}.ckm-generation-status-row span{color:var(--dsw-alias-label-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ckm-progress-track{height:6px;overflow:hidden;border-radius:999px;background:var(--dsw-alias-bg-layer-1)}.ckm-progress-value{display:block;height:100%;border-radius:inherit;background:var(--dsw-alias-state-business-primary);transition:width .25s ease}.ckm-generation-error{color:#ff9898;max-width:38%;overflow-wrap:anywhere}.ckm-generation-strip .ckm-danger{margin-left:auto}' +
567
575
  '.ckm-workspace{display:grid;grid-template-columns:minmax(0,1fr) 330px;min-height:0;flex:1}.ckm-mind-canvas,.ckm-graph-canvas{min-width:0;min-height:0;overflow:auto;padding:20px;background:var(--dsw-alias-bg-base)}.ckm-detail{min-width:0;min-height:0;overflow:auto;padding:20px;border-left:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1)}.ckm-panel-hint{margin:0 0 14px;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:1.6}.ckm-tree{display:flex;flex-direction:column;gap:8px;max-width:900px;margin:0 auto}.ckm-tree-node{display:flex;flex-direction:column;align-items:flex-start;gap:5px;width:calc(100% - 0px);padding:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);text-align:left;cursor:pointer}.ckm-tree-node[data-active=true]{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-tertiary)}.ckm-tree-node:hover{background:var(--dsw-alias-interactive-bg-hover)}.ckm-tree-node strong{font-size:13px}.ckm-tree-node span:last-child{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.65}.ckm-node-type{display:inline-block;color:var(--dsw-alias-state-business-primary);font-size:10px;letter-spacing:.04em;text-transform:uppercase}.ckm-detail-head{display:flex;flex-direction:column;gap:5px}.ckm-detail h3{margin:0;font-size:16px;line-height:1.45}.ckm-narrative{font-size:13px;line-height:1.8}.ckm-source-box{margin:16px 0;padding:12px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-alias-bg-base);font-size:11px;line-height:1.6}.ckm-source-box strong{display:block;margin-bottom:5px}.ckm-source-box ul{margin:0;padding-left:18px;color:var(--dsw-alias-label-secondary)}.ckm-detail-empty{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.7}' +
568
576
  '.ckm-graph-layout{grid-template-columns:minmax(0,1fr) 330px}.ckm-graph-toolbar{display:flex;gap:8px}.ckm-model-selectors{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.4fr);gap:8px}.ckm-graph-toolbar input,.ckm-graph-toolbar select,.ckm-field textarea,.ckm-field select,.ckm-field input,.ckm-modal textarea,.ckm-modal select{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px 9px;background:var(--dsw-specific-input-major);color:var(--dsw-alias-label-primary);font:inherit;font-size:12px}.ckm-graph-toolbar input{flex:1}.ckm-graph-svg{display:block;width:100%;min-height:420px;margin-top:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1)}.ckm-edge{stroke:var(--dsw-alias-border-l2);stroke-width:1.5}.ckm-graph-node{cursor:pointer}.ckm-graph-node circle{fill:var(--dsw-alias-state-business-tertiary);stroke:var(--dsw-alias-state-business-primary);stroke-width:1.5}.ckm-graph-node[data-active=true] circle{fill:var(--dsw-alias-button-info-fill);stroke:var(--dsw-alias-label-primary)}.ckm-graph-node text{fill:var(--dsw-alias-label-primary);font-size:11px}' +
569
577
  '.ckm-modal-backdrop{position:fixed;inset:0;z-index:120;display:flex;align-items:center;justify-content:center;padding:20px;background:rgba(0,0,0,.46)}.ckm-modal{width:min(680px,calc(100vw - 40px));max-height:min(760px,calc(100vh - 40px));overflow:auto;padding:22px;border:1px solid var(--dsw-alias-border-l2);border-radius:14px;box-shadow:0 18px 56px #0008}.ckm-modal h3{margin:0;font-size:17px}.ckm-modal p{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.65}.ckm-modal-head{display:flex;align-items:center;justify-content:space-between}.ckm-icon-close{border:0;background:transparent;color:var(--dsw-alias-label-secondary);font-size:20px;cursor:pointer}.ckm-workspace-label{padding:9px;border-radius:8px;background:var(--dsw-alias-bg-layer-1);word-break:break-all}.ckm-field{display:flex;flex-direction:column;gap:7px;margin:14px 0;color:var(--dsw-alias-label-secondary);font-size:12px}.ckm-field textarea{resize:vertical}.ckm-session-list{display:flex;max-height:220px;flex-direction:column;gap:5px;overflow:auto}.ckm-session-option{display:flex;align-items:flex-start;gap:8px;padding:8px;border:1px solid transparent;border-radius:8px;background:var(--dsw-alias-bg-layer-1);cursor:pointer}.ckm-session-option:hover{border-color:var(--dsw-alias-border-l2)}.ckm-session-option input,.ckm-inline-field input{margin-top:3px}.ckm-session-option span{display:flex;flex-direction:column;gap:3px}.ckm-session-option small{color:var(--dsw-alias-label-secondary);font-size:10px}.ckm-inline-field{display:flex;align-items:flex-start;gap:7px;margin:10px 0;color:var(--dsw-alias-label-secondary);font-size:11px;line-height:1.5}.ckm-confirm-summary{display:grid;grid-template-columns:100px 1fr;gap:7px 12px;margin:18px 0;font-size:12px}.ckm-confirm-summary dt{color:var(--dsw-alias-label-secondary)}.ckm-confirm-summary dd{margin:0;word-break:break-all}.ckm-warning{padding:10px;border-radius:8px;background:#d29c2518;color:var(--dsw-alias-label-secondary)}.ckm-error{color:#ff9898!important}.ckm-modal-actions{justify-content:flex-end;margin-top:18px}.ckm-header-pending{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-tertiary)}' +
@@ -178,6 +178,7 @@ function outputPrompt(kind, summaries, prompt, strict) {
178
178
  if (kind === 'mind-map') {
179
179
  return [
180
180
  '请根据以下多个对话摘要生成阶段性思维导图。节点不是关键词,narrative 必须是至少一段完整说明,包含背景、当前认识和下一步/未决点。',
181
+ '最多生成 12 个节点;每个 narrative 控制在 300 字以内。不要输出 schema 之外的字段。确保最终内容是可被 JSON.parse 直接解析的完整 JSON。',
181
182
  'JSON 形状:{"rootId":"...","nodes":[{"id":"...","parentId":null,"type":"theme|stage|question|decision|solution|risk|conclusion","title":"...","narrative":"...","primarySourceSessionId":"...","sourceRefs":[{"sessionId":"...","eventSeqs":[1]}],"openQuestions":[]}]}。',
182
183
  ...rules,
183
184
  `额外要求:${shortText(prompt, 2000) || '没有额外要求。'}`,
@@ -397,6 +398,7 @@ export class KnowledgeGenerationOrchestrator {
397
398
  createdAt: task.createdAt,
398
399
  updatedAt: task.updatedAt,
399
400
  revision: task.revision || 0,
401
+ progress: task.progress ? clone(task.progress) : null,
400
402
  result: task.result ? clone(task.result) : null,
401
403
  events: task.events.slice(-40)
402
404
  }
@@ -437,6 +439,7 @@ export class KnowledgeGenerationOrchestrator {
437
439
  message: phaseMessage('created'),
438
440
  error: '',
439
441
  result: null,
442
+ progress: { percent: 0, current: 0, total: Array.isArray(request.selectedSessionIds) ? request.selectedSessionIds.length : 0, label: '准备生成' },
440
443
  revision: 0,
441
444
  createdAt: this.now(),
442
445
  updatedAt: this.now(),
@@ -470,7 +473,7 @@ export class KnowledgeGenerationOrchestrator {
470
473
 
471
474
  async run(task, request) {
472
475
  try {
473
- this.update(task, 'reading-sources')
476
+ this.update(task, 'reading-sources', { progress: { percent: 5, current: 0, total: request.selectedSessionIds.length, label: '读取对话记录' } })
474
477
  const sources = await this.sourceReader({ sessionQuery: this.sessionQuery, sessions: this.sessions }, {
475
478
  cwd: request.cwd,
476
479
  sessionIds: request.selectedSessionIds,
@@ -480,9 +483,14 @@ export class KnowledgeGenerationOrchestrator {
480
483
  const sourceSessionIds = [...new Set(sources.map((source) => source.sessionId))]
481
484
  this.assertNotCancelled(task)
482
485
  logMessage(this.logger, 'info', 'generation sources loaded id=%s sources=%d events=%d', logId(task.id), sources.length, sources.reduce((total, source) => total + (Array.isArray(source.events) ? source.events.length : 0), 0))
483
- this.update(task, 'summarizing', { sourceCount: sources.length })
486
+ this.update(task, 'summarizing', { sourceCount: sources.length, progress: { percent: 10, current: 0, total: sources.length, label: '准备整理对话' } })
484
487
  const summaries = []
485
- for (const source of sources) {
488
+ for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex += 1) {
489
+ const source = sources[sourceIndex]
490
+ this.update(task, 'summarizing', {
491
+ sourceCount: sources.length,
492
+ progress: { percent: 10 + Math.floor(60 * sourceIndex / sources.length), current: sourceIndex, total: sources.length, label: source.title }
493
+ })
486
494
  for (const chunk of chunkSourceText(source)) {
487
495
  this.assertNotCancelled(task)
488
496
  const value = await this.runModel({
@@ -496,25 +504,38 @@ export class KnowledgeGenerationOrchestrator {
496
504
  })
497
505
  summaries.push(normalizeSummary(value, source, chunk))
498
506
  }
507
+ this.update(task, 'summarizing', {
508
+ sourceCount: sources.length,
509
+ progress: { percent: 10 + Math.floor(60 * (sourceIndex + 1) / sources.length), current: sourceIndex + 1, total: sources.length, label: source.title }
510
+ })
499
511
  }
500
512
  this.assertNotCancelled(task)
501
513
  let mindMap = null
502
514
  let knowledgeGraph = null
503
515
  if (request.outputMode === 'mind-map' || request.outputMode === 'both') {
504
- this.update(task, 'building-mind-map')
505
- const value = await this.runModel({
506
- kind: 'mind-map',
507
- prompt: outputPrompt('mind-map', summaries, request.prompt, request.strict === true),
508
- cwd: request.cwd,
509
- strict: request.strict === true,
510
- selectedSessionIds: sourceSessionIds,
511
- model: request.model,
512
- signal: task.controller.signal
516
+ this.update(task, 'building-mind-map', { progress: { percent: 78, current: sources.length, total: sources.length, label: '生成思维导图' } })
517
+ const mindMapPrompt = outputPrompt('mind-map', summaries, request.prompt, request.strict === true)
518
+ const generateMindMap = async (prompt) => this.runModel({
519
+ kind: 'mind-map', prompt, cwd: request.cwd, strict: request.strict === true,
520
+ selectedSessionIds: sourceSessionIds, model: request.model, signal: task.controller.signal
513
521
  })
514
- mindMap = validateMindMap(value, { selectedSessionIds: sourceSessionIds, strict: request.strict === true })
522
+ try {
523
+ const value = await generateMindMap(mindMapPrompt)
524
+ mindMap = validateMindMap(value, { selectedSessionIds: sourceSessionIds, strict: request.strict === true })
525
+ } catch (error) {
526
+ this.assertNotCancelled(task)
527
+ logMessage(this.logger, 'warn', 'mind map first attempt invalid id=%s error=%s', logId(task.id), errorMessage(error))
528
+ this.update(task, 'building-mind-map', {
529
+ message: '首次结构无效,正在自动修复思维导图…',
530
+ progress: { percent: 82, current: sources.length, total: sources.length, label: '自动修复结构' }
531
+ })
532
+ const retryPrompt = `${mindMapPrompt}\n\n上一次输出未通过校验:${shortText(errorMessage(error), 500)}。请从头重新生成,不要复用上一次文本;只输出完整、严格合法的 JSON。`
533
+ const retryValue = await generateMindMap(retryPrompt)
534
+ mindMap = validateMindMap(retryValue, { selectedSessionIds: sourceSessionIds, strict: request.strict === true })
535
+ }
515
536
  }
516
537
  if (request.outputMode === 'knowledge-graph' || request.outputMode === 'both') {
517
- this.update(task, 'building-knowledge-graph')
538
+ this.update(task, 'building-knowledge-graph', { progress: { percent: 86, current: sources.length, total: sources.length, label: '生成知识图谱' } })
518
539
  const value = await this.runModel({
519
540
  kind: 'knowledge-graph',
520
541
  prompt: outputPrompt('knowledge-graph', summaries, request.prompt, request.strict === true),
@@ -528,8 +549,8 @@ export class KnowledgeGenerationOrchestrator {
528
549
  }
529
550
  assertOutputSourceRefs({ mindMap, knowledgeGraph }, sources)
530
551
  this.assertNotCancelled(task)
531
- this.update(task, 'validating')
532
- this.update(task, 'saving')
552
+ this.update(task, 'validating', { progress: { percent: 92, current: sources.length, total: sources.length, label: '校验结构和来源' } })
553
+ this.update(task, 'saving', { progress: { percent: 97, current: sources.length, total: sources.length, label: '保存知识视图' } })
533
554
  const saved = await this.storage.saveBundle({
534
555
  cwd: request.cwd,
535
556
  expectedRevision: request.expectedRevision,
@@ -545,7 +566,7 @@ export class KnowledgeGenerationOrchestrator {
545
566
  task.revision = saved.revision
546
567
  task.result = { revision: saved.revision, manifest: saved.manifest, mindMap, knowledgeGraph }
547
568
  logMessage(this.logger, 'info', 'generation completed id=%s revision=%d elapsedMs=%d', logId(task.id), saved.revision, this.now() - task.createdAt)
548
- this.update(task, 'completed', { revision: saved.revision, result: task.result })
569
+ this.update(task, 'completed', { revision: saved.revision, result: task.result, progress: { percent: 100, current: sources.length, total: sources.length, label: '生成完成' } })
549
570
  } catch (error) {
550
571
  if (task.controller.signal.aborted || /取消|cancel/i.test(errorMessage(error))) {
551
572
  this.update(task, 'cancelled', { error: '' })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@p-dsh-market/conversation-knowledge-map",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "DSH 多对话思维导图与静态知识图谱",
5
5
  "keywords": [
6
6
  "dsh",