dsh-data-domain-knowledge 0.1.0-alpha.1

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.
Files changed (231) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +360 -0
  3. package/cordis.patch.yml +410 -0
  4. package/eval/README.md +76 -0
  5. package/eval/evaluate-retrieval.mjs +121 -0
  6. package/eval/queries.example.jsonl +2 -0
  7. package/lib/client.js +111415 -0
  8. package/lib/types/config-schema.d.ts +35 -0
  9. package/lib/types/config-schema.d.ts.map +1 -0
  10. package/lib/types/config-schema.js +34 -0
  11. package/lib/types/config-schema.js.map +1 -0
  12. package/lib/types/convert/core-loader.d.ts +32 -0
  13. package/lib/types/convert/core-loader.d.ts.map +1 -0
  14. package/lib/types/convert/core-loader.js +60 -0
  15. package/lib/types/convert/core-loader.js.map +1 -0
  16. package/lib/types/convert/host.d.ts +88 -0
  17. package/lib/types/convert/host.d.ts.map +1 -0
  18. package/lib/types/convert/host.js +479 -0
  19. package/lib/types/convert/host.js.map +1 -0
  20. package/lib/types/convert/ingest.d.ts +125 -0
  21. package/lib/types/convert/ingest.d.ts.map +1 -0
  22. package/lib/types/convert/ingest.js +228 -0
  23. package/lib/types/convert/ingest.js.map +1 -0
  24. package/lib/types/convert/settings-schema.d.ts +18 -0
  25. package/lib/types/convert/settings-schema.d.ts.map +1 -0
  26. package/lib/types/convert/settings-schema.js +26 -0
  27. package/lib/types/convert/settings-schema.js.map +1 -0
  28. package/lib/types/convert/types.d.ts +46 -0
  29. package/lib/types/convert/types.d.ts.map +1 -0
  30. package/lib/types/convert/types.js +41 -0
  31. package/lib/types/convert/types.js.map +1 -0
  32. package/lib/types/diagnosis/cards.d.ts +71 -0
  33. package/lib/types/diagnosis/cards.d.ts.map +1 -0
  34. package/lib/types/diagnosis/cards.js +482 -0
  35. package/lib/types/diagnosis/cards.js.map +1 -0
  36. package/lib/types/diagnosis/problem-classes.d.ts +51 -0
  37. package/lib/types/diagnosis/problem-classes.d.ts.map +1 -0
  38. package/lib/types/diagnosis/problem-classes.js +66 -0
  39. package/lib/types/diagnosis/problem-classes.js.map +1 -0
  40. package/lib/types/diagnosis/schema.d.ts +65 -0
  41. package/lib/types/diagnosis/schema.d.ts.map +1 -0
  42. package/lib/types/diagnosis/schema.js +208 -0
  43. package/lib/types/diagnosis/schema.js.map +1 -0
  44. package/lib/types/diagnosis/seed-source.d.ts +20 -0
  45. package/lib/types/diagnosis/seed-source.d.ts.map +1 -0
  46. package/lib/types/diagnosis/seed-source.js +96 -0
  47. package/lib/types/diagnosis/seed-source.js.map +1 -0
  48. package/lib/types/diagnosis/seed.d.ts +50 -0
  49. package/lib/types/diagnosis/seed.d.ts.map +1 -0
  50. package/lib/types/diagnosis/seed.js +242 -0
  51. package/lib/types/diagnosis/seed.js.map +1 -0
  52. package/lib/types/diagnosis/store.d.ts +81 -0
  53. package/lib/types/diagnosis/store.d.ts.map +1 -0
  54. package/lib/types/diagnosis/store.js +409 -0
  55. package/lib/types/diagnosis/store.js.map +1 -0
  56. package/lib/types/diagnosis/types.d.ts +124 -0
  57. package/lib/types/diagnosis/types.d.ts.map +1 -0
  58. package/lib/types/diagnosis/types.js +13 -0
  59. package/lib/types/diagnosis/types.js.map +1 -0
  60. package/lib/types/directory-picker.d.ts +94 -0
  61. package/lib/types/directory-picker.d.ts.map +1 -0
  62. package/lib/types/directory-picker.js +110 -0
  63. package/lib/types/directory-picker.js.map +1 -0
  64. package/lib/types/extract-model.d.ts +76 -0
  65. package/lib/types/extract-model.d.ts.map +1 -0
  66. package/lib/types/extract-model.js +193 -0
  67. package/lib/types/extract-model.js.map +1 -0
  68. package/lib/types/extract.d.ts +121 -0
  69. package/lib/types/extract.d.ts.map +1 -0
  70. package/lib/types/extract.js +549 -0
  71. package/lib/types/extract.js.map +1 -0
  72. package/lib/types/fts.d.ts +96 -0
  73. package/lib/types/fts.d.ts.map +1 -0
  74. package/lib/types/fts.js +184 -0
  75. package/lib/types/fts.js.map +1 -0
  76. package/lib/types/index.d.ts +39 -0
  77. package/lib/types/index.d.ts.map +1 -0
  78. package/lib/types/index.js +1112 -0
  79. package/lib/types/index.js.map +1 -0
  80. package/lib/types/intent/engine.d.ts +108 -0
  81. package/lib/types/intent/engine.d.ts.map +1 -0
  82. package/lib/types/intent/engine.js +501 -0
  83. package/lib/types/intent/engine.js.map +1 -0
  84. package/lib/types/intent/host.d.ts +88 -0
  85. package/lib/types/intent/host.d.ts.map +1 -0
  86. package/lib/types/intent/host.js +389 -0
  87. package/lib/types/intent/host.js.map +1 -0
  88. package/lib/types/intent/preset-tools.d.ts +37 -0
  89. package/lib/types/intent/preset-tools.d.ts.map +1 -0
  90. package/lib/types/intent/preset-tools.js +45 -0
  91. package/lib/types/intent/preset-tools.js.map +1 -0
  92. package/lib/types/intent/settings-schema.d.ts +59 -0
  93. package/lib/types/intent/settings-schema.d.ts.map +1 -0
  94. package/lib/types/intent/settings-schema.js +57 -0
  95. package/lib/types/intent/settings-schema.js.map +1 -0
  96. package/lib/types/intent/store.d.ts +75 -0
  97. package/lib/types/intent/store.d.ts.map +1 -0
  98. package/lib/types/intent/store.js +139 -0
  99. package/lib/types/intent/store.js.map +1 -0
  100. package/lib/types/intent/tools.d.ts +63 -0
  101. package/lib/types/intent/tools.d.ts.map +1 -0
  102. package/lib/types/intent/tools.js +108 -0
  103. package/lib/types/intent/tools.js.map +1 -0
  104. package/lib/types/intent/types.d.ts +189 -0
  105. package/lib/types/intent/types.d.ts.map +1 -0
  106. package/lib/types/intent/types.js +62 -0
  107. package/lib/types/intent/types.js.map +1 -0
  108. package/lib/types/knowledge-bases.d.ts +266 -0
  109. package/lib/types/knowledge-bases.d.ts.map +1 -0
  110. package/lib/types/knowledge-bases.js +1107 -0
  111. package/lib/types/knowledge-bases.js.map +1 -0
  112. package/lib/types/layout.d.ts +262 -0
  113. package/lib/types/layout.d.ts.map +1 -0
  114. package/lib/types/layout.js +312 -0
  115. package/lib/types/layout.js.map +1 -0
  116. package/lib/types/migrate.d.ts +110 -0
  117. package/lib/types/migrate.d.ts.map +1 -0
  118. package/lib/types/migrate.js +280 -0
  119. package/lib/types/migrate.js.map +1 -0
  120. package/lib/types/recall-rank.d.ts +158 -0
  121. package/lib/types/recall-rank.d.ts.map +1 -0
  122. package/lib/types/recall-rank.js +261 -0
  123. package/lib/types/recall-rank.js.map +1 -0
  124. package/lib/types/runtime.d.ts +568 -0
  125. package/lib/types/runtime.d.ts.map +1 -0
  126. package/lib/types/runtime.js +3045 -0
  127. package/lib/types/runtime.js.map +1 -0
  128. package/lib/types/settings-schema.d.ts +117 -0
  129. package/lib/types/settings-schema.d.ts.map +1 -0
  130. package/lib/types/settings-schema.js +92 -0
  131. package/lib/types/settings-schema.js.map +1 -0
  132. package/lib/types/settings-volatile.d.ts +102 -0
  133. package/lib/types/settings-volatile.d.ts.map +1 -0
  134. package/lib/types/settings-volatile.js +114 -0
  135. package/lib/types/settings-volatile.js.map +1 -0
  136. package/lib/types/types.d.ts +799 -0
  137. package/lib/types/types.d.ts.map +1 -0
  138. package/lib/types/types.js +387 -0
  139. package/lib/types/types.js.map +1 -0
  140. package/lib/types/wiki-types.d.ts +68 -0
  141. package/lib/types/wiki-types.d.ts.map +1 -0
  142. package/lib/types/wiki-types.js +322 -0
  143. package/lib/types/wiki-types.js.map +1 -0
  144. package/package.json +101 -0
  145. package/preset/tita-data-agent/agent.cordis.yml +376 -0
  146. package/preset/tita-data-agent/preset.yml +3 -0
  147. package/python/requirements.txt +8 -0
  148. package/scripts/doctor.mjs +70 -0
  149. package/scripts/e2e-pipeline.mjs +355 -0
  150. package/scripts/install-agent-preset.mjs +92 -0
  151. package/scripts/setup-embedding.sh +34 -0
  152. package/scripts/sync-agent-preset.mjs +188 -0
  153. package/src/client/AgentPresetCard.tsx +247 -0
  154. package/src/client/DirectoryPicker.tsx +357 -0
  155. package/src/client/InfoHint.tsx +65 -0
  156. package/src/client/IntentLogicTab.tsx +185 -0
  157. package/src/client/IntentTab.tsx +715 -0
  158. package/src/client/KnowledgeBaseTab.tsx +1454 -0
  159. package/src/client/KnowledgeGraphView.tsx +306 -0
  160. package/src/client/KnowledgePanel.tsx +381 -0
  161. package/src/client/KnowledgeStepRail.tsx +60 -0
  162. package/src/client/NodeSettings.tsx +392 -0
  163. package/src/client/PreviewSections.tsx +116 -0
  164. package/src/client/RecallPanel.tsx +399 -0
  165. package/src/client/TypeDefinitionDrawer.tsx +215 -0
  166. package/src/client/WikiFlowCard.tsx +226 -0
  167. package/src/client/WikiTypeEditor.tsx +332 -0
  168. package/src/client/agent-preset.ts +121 -0
  169. package/src/client/arch/AgentArchitectureGraph.tsx +219 -0
  170. package/src/client/arch/NodeLogicPanel.tsx +133 -0
  171. package/src/client/arch/ProblemDetailPanel.tsx +88 -0
  172. package/src/client/arch/model.ts +691 -0
  173. package/src/client/architecture.css +191 -0
  174. package/src/client/architecture.tsx +530 -0
  175. package/src/client/attribution/AttributionGraph.tsx +104 -0
  176. package/src/client/attribution/AttributionTab.tsx +332 -0
  177. package/src/client/attribution/CaseView.tsx +102 -0
  178. package/src/client/attribution/GraphStage.tsx +87 -0
  179. package/src/client/attribution/RunView.tsx +184 -0
  180. package/src/client/attribution/StageChrome.tsx +403 -0
  181. package/src/client/attribution/ViewSwitch.tsx +107 -0
  182. package/src/client/attribution/graph-options.ts +381 -0
  183. package/src/client/attribution/model.ts +462 -0
  184. package/src/client/config-scope.ts +119 -0
  185. package/src/client/convert/ConvertPanel.tsx +594 -0
  186. package/src/client/convert/styles.css +138 -0
  187. package/src/client/convert/styles.ts +49 -0
  188. package/src/client/format.ts +39 -0
  189. package/src/client/graph/layout.ts +181 -0
  190. package/src/client/graph.css +113 -0
  191. package/src/client/index.ts +98 -0
  192. package/src/client/problem-domains.ts +114 -0
  193. package/src/client/styles.css +940 -0
  194. package/src/client/styles.ts +11 -0
  195. package/src/client/useFitScale.ts +68 -0
  196. package/src/config-schema.ts +45 -0
  197. package/src/convert/core-loader.ts +68 -0
  198. package/src/convert/host.ts +579 -0
  199. package/src/convert/ingest.ts +367 -0
  200. package/src/convert/settings-schema.ts +27 -0
  201. package/src/convert/types.ts +65 -0
  202. package/src/diagnosis/cards.ts +498 -0
  203. package/src/diagnosis/problem-classes.ts +117 -0
  204. package/src/diagnosis/schema.ts +260 -0
  205. package/src/diagnosis/seed-source.ts +107 -0
  206. package/src/diagnosis/seed.ts +291 -0
  207. package/src/diagnosis/store.ts +432 -0
  208. package/src/diagnosis/types.ts +152 -0
  209. package/src/directory-picker.ts +164 -0
  210. package/src/extract-model.ts +227 -0
  211. package/src/extract.ts +632 -0
  212. package/src/fts.ts +203 -0
  213. package/src/index.ts +1091 -0
  214. package/src/intent/README.md +63 -0
  215. package/src/intent/engine.ts +527 -0
  216. package/src/intent/host.ts +462 -0
  217. package/src/intent/preset-tools.ts +48 -0
  218. package/src/intent/settings-schema.ts +63 -0
  219. package/src/intent/store.ts +143 -0
  220. package/src/intent/tools.ts +139 -0
  221. package/src/intent/types.ts +245 -0
  222. package/src/knowledge-bases.ts +1160 -0
  223. package/src/layout.ts +350 -0
  224. package/src/migrate.ts +262 -0
  225. package/src/python/embedding_worker.py +471 -0
  226. package/src/recall-rank.ts +299 -0
  227. package/src/runtime.ts +3069 -0
  228. package/src/settings-schema.ts +94 -0
  229. package/src/settings-volatile.ts +122 -0
  230. package/src/types.ts +1140 -0
  231. package/src/wiki-types.ts +357 -0
@@ -0,0 +1,381 @@
1
+ import { useCallback, useEffect, useState, type ReactNode } from 'react'
2
+ import {
3
+ Activity, AlertCircle, BarChart3, Box, BrainCircuit, Compass, Database, FileText, HardDrive,
4
+ Layers3, Settings2, Stethoscope, Workflow, Wrench, X, Zap,
5
+ } from '@tita-data-agent/ui/icons'
6
+ import type { LucideIcon } from '@tita-data-agent/ui/icons'
7
+ import { Badge, Button, Card, Tabs, TabsContent, TabsList, TabsTrigger, cn } from '@tita-data-agent/ui/components'
8
+ import {
9
+ Drawer, DrawerBody, DrawerContent, DrawerHeader, DrawerTitle,
10
+ } from '@tita-data-agent/ui/overlays'
11
+ import { KNOWLEDGE_SETTINGS_ORDER, NodeSettingsPanel, settingsAnchorId, useScope, type SettingsNode, type SettingsScopeLike } from './NodeSettings.tsx'
12
+ import { LayoutPreview, useAgentLayout } from './DirectoryPicker.tsx'
13
+ import { AgentPresetCard, type AgentPresetRemote } from './AgentPresetCard.tsx'
14
+ import {
15
+ NODE_META, NODE_ORDER, NODE_STATUS_LABEL, noteCountOf, sourceCountOf,
16
+ type KnowledgeStats, type KnowledgeTab, type NodeId, type PanelTab, type Tone,
17
+ } from './architecture.tsx'
18
+ import { AgentArchitectureGraph } from './arch/AgentArchitectureGraph.tsx'
19
+ import { NodeLogicPanel } from './arch/NodeLogicPanel.tsx'
20
+ import { ProblemDetailPanel } from './arch/ProblemDetailPanel.tsx'
21
+ import { formatBuildTime } from './arch/model.ts'
22
+ import { PROBLEM_DOMAINS } from './problem-domains.ts'
23
+ import { AttributionTab } from './attribution/AttributionTab.tsx'
24
+ import { KnowledgeBaseTab } from './KnowledgeBaseTab.tsx'
25
+ import { IntentTab } from './IntentTab.tsx'
26
+ import { RecallPanel } from './RecallPanel.tsx'
27
+ import { styles, toneSuffix } from './styles.ts'
28
+ import '@tita-data-agent/ui/styles.css'
29
+ import './styles.css'
30
+
31
+ interface Props {
32
+ visible: boolean
33
+ onOpenTab?: (kind: 'data-sheet' | 'data-notebook') => void
34
+ settings?: {
35
+ intent?: SettingsScopeLike
36
+ knowledge?: SettingsScopeLike
37
+ convert?: SettingsScopeLike
38
+ }
39
+ /** agent preset 的 Remote 命名空间;「Agent 提示词」卡片读名单、开真源目录。 */
40
+ remote?: AgentPresetRemote
41
+ }
42
+
43
+ interface Envelope<T> { ok?: boolean; data?: T; warnings?: string[]; error?: { message?: string; suggestion?: string } }
44
+
45
+ type ViewId = PanelTab
46
+ type Selection = NodeId | null
47
+
48
+ /**
49
+ * 一级 tab。**只有名字和图标** —— 标题与说明句已经撤掉:tab 自己就写着「知识库」,
50
+ * 下面再挂一个 h1「知识库」加一句介绍,是把同一件事说两遍,还白占一屏顶部空间。
51
+ */
52
+ const VIEWS: Record<ViewId, { label: string; icon: LucideIcon }> = {
53
+ overview: { label: 'Agent 概览', icon: Layers3 },
54
+ intent: { label: '意图', icon: Workflow },
55
+ knowledge: { label: '知识库', icon: Database },
56
+ attribution: { label: '智能归因', icon: Stethoscope },
57
+ memory: { label: '记忆', icon: BrainCircuit },
58
+ eval: { label: '评测', icon: BarChart3 },
59
+ }
60
+
61
+ const VIEW_ORDER: ViewId[] = ['overview', 'intent', 'knowledge', 'attribution', 'memory', 'eval']
62
+
63
+ async function requestJson<T>(url: string, init?: RequestInit): Promise<Envelope<T>> {
64
+ const response = await fetch(url, init)
65
+ const text = await response.text()
66
+ if (!text.trim()) throw new Error(`服务端返回空响应(HTTP ${response.status})`)
67
+ let payload: Envelope<T>
68
+ try {
69
+ payload = JSON.parse(text) as Envelope<T>
70
+ } catch {
71
+ throw new Error(`服务端返回了无效响应(HTTP ${response.status})`)
72
+ }
73
+ if (!response.ok || payload.ok === false) throw new Error(payload.error?.message ?? `请求失败(${response.status})`)
74
+ return payload
75
+ }
76
+
77
+ function MetricCard({ icon: Icon, label, value, hint, tone = 'neutral' }: { icon: LucideIcon; label: string; value: string; hint?: string; tone?: Tone }): ReactNode {
78
+ return <Card className={cn(styles.metricCard, styles[`metricTone${toneSuffix(tone)}`])}>
79
+ <span className={styles.metricIcon}><Icon size={16} strokeWidth={1.8} /></span>
80
+ <span className={styles.metricCopy}><small>{label}</small><strong>{value}</strong>{hint && <em>{hint}</em>}</span>
81
+ </Card>
82
+ }
83
+
84
+ /**
85
+ * 记忆 Tab。
86
+ *
87
+ * 这一页**没有**设置项,也**没有**编造的数字:记忆段的落盘量是真实的,
88
+ * 情节记忆与操作记忆两栏明说「示意」,因为采集与归纳管线还没实现 —— 架构图上
89
+ * 对应的节点标的就是「待实现」。
90
+ *
91
+ * 0032 起记忆住在 `<agentRoot>/memory/<库 id>`,不再在知识树里,所以这里读
92
+ * `stats.memoryFiles` 并把**真实路径**显示出来 —— 「它到底在哪」这一页必须能回答。
93
+ */
94
+ function MemoryTab({ onSelect, stats, version }: { onSelect: (id: NodeId) => void; stats: KnowledgeStats; version: number }): ReactNode {
95
+ const memoryFiles = stats.memoryFiles ?? 0
96
+ const builtAt = formatBuildTime(stats.builtAt)
97
+ return <div className={styles.viewStack}>
98
+ <Card className={styles.workspaceCard}>
99
+ <div className={styles.cardHeader}>
100
+ <div><h3><HardDrive size={14} />真实记忆落盘</h3></div>
101
+ <Badge tone="success">来自 /knowledge/stats</Badge>
102
+ </div>
103
+ <p className={styles.cardDescription}>
104
+ 本地记忆不是白板:文档每次内容变化都会在 <b>memory/</b> 留下版本快照,wiki 卡片也有自己的快照,
105
+ 回滚本身也可回滚;下面这排数字是当前真实落盘量,而不是产品占位。
106
+ </p>
107
+ {/* 路径要说出来:记忆从知识树搬到了根路径下,用户备份时得知道该拷哪。 */}
108
+ {stats.memoryRoot && <p className={styles.kbRoot} title={stats.memoryRoot}>{stats.memoryRoot}</p>}
109
+ <div className={styles.memoryReal}>
110
+ <MetricCard icon={HardDrive} label="memory/ 文件" value={String(memoryFiles)} hint="来源与卡片版本快照" tone="amber" />
111
+ <MetricCard icon={FileText} label="来源文档" value={String(stats.sourceCount ?? 0)} hint="可追溯的 SourceRaw" tone="blue" />
112
+ <MetricCard icon={Box} label="wikiNotes" value={String(noteCountOf(stats))} hint="已编译的召回单元" tone="green" />
113
+ <MetricCard icon={Activity} label="索引版本" value={version ? `v${version}` : '—'} hint={builtAt} tone="violet" />
114
+ </div>
115
+ </Card>
116
+
117
+ <div className={styles.memoryGrid}>
118
+ <Card className={styles.memoryCard}>
119
+ <div className={styles.cardHeader}><div><h3><FileText size={14} />情节记忆</h3></div><Badge tone="warning">待实现</Badge></div>
120
+ <p>记录用户做了什么、采用了什么结果以及事后的反馈;保留时间、会话与来源,之后可以按上下文回忆。采集与归纳管线还没实现之前,这里只描述结构与策略,不展示编造的数字。</p>
121
+ <Button variant="outline" onClick={() => onSelect('episodic-memory')}>查看节点状态<Compass size={14} /></Button>
122
+ </Card>
123
+ <Card className={styles.memoryCard}>
124
+ <div className={styles.cardHeader}><div><h3><Wrench size={14} />操作记忆 → Skill</h3></div><Badge tone="warning">待实现</Badge></div>
125
+ <p>从重复操作里归纳步骤、参数和工具组合,经过质量检查后生成可复用 Skill。提取管线与 Skill Registry 写入都还没有实现。</p>
126
+ <Button variant="outline" onClick={() => onSelect('procedural-memory')}>查看节点状态<Compass size={14} /></Button>
127
+ </Card>
128
+ <Card className={cn(styles.memoryCard, styles.memoryCardWide)}>
129
+ <div className={styles.cardHeader}><div><h3><Wrench size={14} />质量检查与每日更新</h3></div><Badge tone="warning">待实现</Badge></div>
130
+ <div className={styles.timeline}><span>交互采集</span><i>→</i><span>去重归纳</span><i>→</i><span>人工审核</span><i>→</i><span>知识库 / Skill Registry</span></div>
131
+ <p className={styles.cardDescription}>目前真实在跑的质检是<b>卡片 schema 的三层校验</b>(格式 / 引用 / 语义),入口在「知识库 → Wiki」。</p>
132
+ <Button variant="default" onClick={() => onSelect('daily-update')}><Zap size={14} />查看自进化链路</Button>
133
+ </Card>
134
+ </div>
135
+ </div>
136
+ }
137
+
138
+ function EvalTab({ visible, settings, onSelect }: { visible: boolean; settings?: SettingsScopeLike; onSelect: (id: NodeId) => void }): ReactNode {
139
+ const snapshot = useScope(settings)
140
+ const value = snapshot.value ?? {}
141
+ return <div className={styles.viewStack}>
142
+ <div className={styles.evalGrid}>
143
+ <RecallPanel
144
+ visible={visible}
145
+ variant="runner"
146
+ resetKey={String(snapshot.revision ?? '')}
147
+ defaults={{
148
+ ...(typeof value.recallMode === 'string' ? { mode: value.recallMode } : {}),
149
+ ...(typeof value.recallTopK === 'number' ? { topK: value.recallTopK } : {}),
150
+ ...(typeof value.contextTopN === 'number' ? { topN: value.contextTopN } : {}),
151
+ }}
152
+ />
153
+ <Card className={styles.evalPlan}>
154
+ <div className={styles.cardHeader}><div><h3><BarChart3 size={14} />评测计划</h3></div><Badge tone="warning">占位</Badge></div>
155
+ <p className={styles.cardDescription}>评测集接入前,这一栏只声明要评什么;左边的单问题检查已经是真实链路。</p>
156
+ <div className={styles.evalRows}>
157
+ <div><span>编译正确性</span><strong>待运行</strong></div>
158
+ <div><span>三路 Hybrid Recall@10</span><strong>待运行</strong></div>
159
+ <div><span>引用可追溯性</span><strong>待运行</strong></div>
160
+ <div><span>多模态 Markdown</span><strong>待运行</strong></div>
161
+ <div><span>增量编译复用率</span><strong>待运行</strong></div>
162
+ </div>
163
+ <Button variant="outline" onClick={() => onSelect('build')}>查看构建节点</Button>
164
+ </Card>
165
+ </div>
166
+ </div>
167
+ }
168
+
169
+ export function KnowledgePanel({ visible, onOpenTab, settings, remote }: Props): ReactNode {
170
+ const [activeTab, setActiveTab] = useState<ViewId>('overview')
171
+ const [stats, setStats] = useState<KnowledgeStats>({})
172
+ const [statsLoading, setStatsLoading] = useState(false)
173
+ const [statsError, setStatsError] = useState<string | null>(null)
174
+ const [selected, setSelected] = useState<Selection>(null)
175
+ /** 选中的问题域。与节点选中**互斥**:浮层只有一个,两种内容不能同时占着它。 */
176
+ const [selectedDomain, setSelectedDomain] = useState<string | null>(null)
177
+ const [notice, setNotice] = useState<{ tone: 'success' | 'error'; text: string } | null>(null)
178
+ /** 从架构图节点跳到知识库分栏:tab + 一个递增的 nonce(同栏再点也要生效)。 */
179
+ const [knowledgeFocus, setKnowledgeFocus] = useState<{ tab: KnowledgeTab; nonce: number } | undefined>(undefined)
180
+ /**
181
+ * 设置抽屉。面板上只有**一个**设置项(存储位置),而它不属于任何一个视图 ——
182
+ * 所以入口放在 tab 栏最右边,不挂在任何一个 tab 的内容里。
183
+ *
184
+ * 0034:`null` = 关着,否则是**要落到哪一块**。卡片头上的「参数」chip 走的就是这条路
185
+ * —— 它不新开一个家,只是把同一块面板滚到眼前并接管焦点。
186
+ */
187
+ const [settingsTarget, setSettingsTarget] = useState<SettingsNode | null>(null)
188
+ /**
189
+ * 四段布局 + 家当解析结果 + 宿主提供哪种目录选择交互。它是**服务端算出来的**(`layout.ts` 是唯一
190
+ * 真源),前端不自己拼路径。刷新键带上设置 revision:改完根路径,预览要跟着变。
191
+ */
192
+ const knowledgeSnapshot = useScope(settings?.knowledge)
193
+ const { layout, error: layoutError } = useAgentLayout(knowledgeSnapshot.revision ?? 0)
194
+
195
+ /**
196
+ * 「参数」chip 打开的抽屉要落到它指的那一块:滚过去 + 把焦点交过去。
197
+ *
198
+ * 抽屉的内容在 `open` 之后才挂载,所以查 DOM 得等一帧。滚动用默认的瞬时行为
199
+ * (不是 smooth):抽屉自己正在滑入,再叠一段长距离滚动只会让人看不清发生了什么。
200
+ */
201
+ useEffect(() => {
202
+ if (!settingsTarget) return
203
+ const frame = requestAnimationFrame(() => {
204
+ const section = document.getElementById(settingsAnchorId(settingsTarget))
205
+ section?.scrollIntoView({ block: 'start' })
206
+ section?.focus({ preventScroll: true })
207
+ })
208
+ return () => cancelAnimationFrame(frame)
209
+ }, [settingsTarget])
210
+
211
+ /** 打开设置抽屉并落到某一块。传 `null` 就是关。 */
212
+ const openSettings = useCallback((target: SettingsNode | null): void => { setSettingsTarget(target) }, [])
213
+
214
+ const refreshStats = useCallback(async (): Promise<void> => {
215
+ setStatsLoading(true); setStatsError(null)
216
+ try {
217
+ const payload = await requestJson<KnowledgeStats>('/knowledge/stats')
218
+ setStats(payload.data ?? {})
219
+ } catch (error) {
220
+ setStatsError(error instanceof Error ? error.message : String(error))
221
+ } finally {
222
+ setStatsLoading(false)
223
+ }
224
+ }, [])
225
+
226
+ useEffect(() => { if (visible) void refreshStats() }, [visible, refreshStats])
227
+
228
+ const sourceCount = sourceCountOf(stats)
229
+ const version = stats.buildVersion ?? 0
230
+ const selectNode = (id: NodeId | null): void => { setSelected(id); setSelectedDomain(null); setNotice(null) }
231
+ const selectDomain = (id: string | null): void => { setSelectedDomain(id); setSelected(null); setNotice(null) }
232
+
233
+ /** 架构图节点 → 功能 tab。这是节点面板上「去配置」按钮唯一的落点。 */
234
+ const jumpTo = (tab: PanelTab, knowledgeTab?: KnowledgeTab): void => {
235
+ setActiveTab(tab)
236
+ if (tab === 'knowledge' && knowledgeTab) setKnowledgeFocus({ tab: knowledgeTab, nonce: Date.now() })
237
+ }
238
+
239
+ const jumpLabel = selected ? `<${NODE_META[selected].title}>:${NODE_STATUS_LABEL[NODE_META[selected].status]}` : null
240
+
241
+ /**
242
+ * 画布内的详情浮层。**两种内容、一个位置**:选中节点看节点,选中问题域看问题域,
243
+ * 都没选中时浮层根本不出现 —— 那时画布是完整的一张图。
244
+ */
245
+ const inspector = selected
246
+ ? <NodeLogicPanel
247
+ selected={selected} stats={stats} version={version}
248
+ onJump={jumpTo}
249
+ {...(onOpenTab ? { onOpenTab } : {})}
250
+ onClose={() => selectNode(null)}
251
+ />
252
+ : selectedDomain
253
+ ? <ProblemDetailPanel
254
+ domainId={selectedDomain} stats={stats} version={version}
255
+ onSelect={selectNode} onClose={() => selectDomain(null)}
256
+ />
257
+ : null
258
+
259
+ return <section className={styles.panel} aria-label="Tita Data Agent">
260
+ {/* Tab 切换是面板外壳的一部分:它不参与 `.tita-content` 的滚动,永远停在顶部。 */}
261
+ <Tabs className={styles.panelTabs} value={activeTab} onValueChange={value => setActiveTab(value as ViewId)}>
262
+ <div className={styles.tabBar}>
263
+ <TabsList className={styles.tabsList}>
264
+ {VIEW_ORDER.map(id => {
265
+ const TabIcon = VIEWS[id].icon
266
+ return <TabsTrigger key={id} value={id}><TabIcon size={13} />{VIEWS[id].label}</TabsTrigger>
267
+ })}
268
+ </TabsList>
269
+ <Button
270
+ variant="ghost" size="icon" className={styles.tabBarSettings}
271
+ aria-label="设置" title="设置 · 本方案参数"
272
+ onClick={() => openSettings('storage')}
273
+ ><Settings2 size={15} /></Button>
274
+ </div>
275
+
276
+ <div className={cn(styles.content, activeTab === 'attribution' && styles.contentStage)}>
277
+
278
+ {/* 一级 tab 已经说明「现在是哪一页」,这里不再重复一遍标题与说明。
279
+ 只有真的出问题时才占一行:读取失败 / 读取中。 */}
280
+ {(statsError || statsLoading) && <p className={styles.viewStatus} role="status">
281
+ {statsError ? <><AlertCircle size={13} />数据读取失败:{statsError}</> : '正在读取知识库统计…'}
282
+ </p>}
283
+
284
+ {notice && <Card className={cn(styles.notice, notice.tone === 'error' ? styles.noticeError : styles.noticeSuccess)} role="status">
285
+ <AlertCircle size={16} /><span>{notice.text}</span>
286
+ <Button variant="ghost" size="icon" aria-label="关闭提示" title="关闭提示" onClick={() => setNotice(null)}><X size={14} /></Button>
287
+ </Card>}
288
+
289
+ <TabsContent value="overview">
290
+ {/* 概览 = **一个画布**。0027 之前这里是「画布 + 右侧栏」,而问题域还收在栏底的
291
+ 折叠区里 —— 三块内容、两条边框,读起来是三个东西。现在画布是四条带
292
+ (A/B/C 三条链路 + 一条问题域带),点节点或问题域牌子都在**画布内**的
293
+ 浮层里展开详情:不占侧栏、不缩字、可关闭。
294
+ 设置不在这里:面板上唯一的设置入口在顶部 tab 栏最右边的齿轮里。 */}
295
+ <AgentArchitectureGraph
296
+ stats={stats} version={version} selected={selected}
297
+ onSelect={selectNode} visible={visible && activeTab === 'overview'}
298
+ selectedDomain={selectedDomain} onSelectDomain={selectDomain}
299
+ {...(inspector ? { inspector } : {})}
300
+ />
301
+ <nav className={styles.srOnly} aria-label="架构节点列表">
302
+ <ul>{NODE_ORDER.map(id => <li key={id}><button type="button" onClick={() => selectNode(id)}>{NODE_META[id].title}:{NODE_META[id].detail}</button></li>)}</ul>
303
+ </nav>
304
+ <nav className={styles.srOnly} aria-label="问题域列表">
305
+ <ul>{PROBLEM_DOMAINS.map(domain => <li key={domain.id}><button type="button" onClick={() => selectDomain(domain.id)}>{domain.title}:{domain.question}</button></li>)}</ul>
306
+ </nav>
307
+ <span className={styles.srOnly} role="status" aria-live="polite">{jumpLabel ?? ''}</span>
308
+ </TabsContent>
309
+
310
+ <TabsContent value="intent">
311
+ {/* 意图设置(澄清阈值 / TTL / 兜底)也在这一页 —— 设置与它生效的地方同处。 */}
312
+ <IntentTab visible={visible && activeTab === 'intent'} intent={settings?.intent} />
313
+ </TabsContent>
314
+
315
+ <TabsContent value="knowledge">
316
+ <KnowledgeBaseTab
317
+ visible={visible && activeTab === 'knowledge'}
318
+ onChanged={() => void refreshStats()}
319
+ {...(settings?.knowledge ? { knowledge: settings.knowledge } : {})}
320
+ {...(knowledgeFocus ? { focus: knowledgeFocus } : {})}
321
+ onOpenSettings={openSettings}
322
+ />
323
+ </TabsContent>
324
+
325
+ <TabsContent value="attribution" className={styles.stageContent}>
326
+ <AttributionTab visible={visible && activeTab === 'attribution'} />
327
+ </TabsContent>
328
+
329
+ <TabsContent value="memory">
330
+ <MemoryTab onSelect={selectNode} stats={stats} version={version} />
331
+ </TabsContent>
332
+
333
+ <TabsContent value="eval">
334
+ <EvalTab visible={visible && activeTab === 'eval'} {...(settings?.knowledge ? { settings: settings.knowledge } : {})} onSelect={selectNode} />
335
+ </TabsContent>
336
+ </div>
337
+ </Tabs>
338
+
339
+ <Drawer open={settingsTarget !== null} onOpenChange={next => { if (!next) setSettingsTarget(null) }}>
340
+ {settingsTarget !== null && <DrawerContent className={styles.settingsDrawer} resizable resizeKey="tita-knowledge-settings">
341
+ <DrawerHeader>
342
+ {/* 只留标题。原来这里有一段「方案级设置:知识树落在磁盘哪里……」的说明,
343
+ 它把下面每一块的标题又说了一遍,还占了抽屉的第一屏。 */}
344
+ <DrawerTitle>设置</DrawerTitle>
345
+ </DrawerHeader>
346
+ <DrawerBody>
347
+ {/* **方案级参数只有一个入口**:就是这里。
348
+ 0029 之前它们散在知识库的各个分栏里(构建在编译卡、召回在召回卡、重排
349
+ 在精排卡),于是「改一个参数」先要回答「它属于哪一段」——而那个归属是
350
+ 实现细节,不是用户的心智模型。现在抽屉按链路顺序分块:
351
+ 存储 → 收录 → 构建 → 召回 → 重排。
352
+ 0034 补上后半句:分栏里那些「已移到顶部齿轮」的路牌换成卡片头上的「参数」
353
+ chip —— 家还是一个,只是多了一扇离现场更近的门(`settingsTarget` 决定
354
+ 落到哪一块,`highlight` 是那一次落点的一次性强调)。
355
+ 本库的词汇表(来源类型 / Wiki 类型)**不在这里**:它按库持久化,是另一个
356
+ 作用域,住在知识库工具栏的「类型定义」抽屉里。 */}
357
+ <div className={styles.settingsStack}>
358
+ {KNOWLEDGE_SETTINGS_ORDER.map(node => <NodeSettingsPanel
359
+ key={node}
360
+ node={node}
361
+ knowledge={settings?.knowledge}
362
+ highlight={settingsTarget === node}
363
+ pickerKind={layout?.pickerKind ?? null}
364
+ {...(node === 'storage' ? { extra: <LayoutPreview layout={layout} error={layoutError} /> } : {})}
365
+ {...(node === 'build' && layout?.resources ? { resolved: layout.resources } : {})}
366
+ />)}
367
+ {/* Agent 提示词是**关于这个 Agent 本身**的东西,不是知识库的 —— 所以它排在
368
+ 知识链路那四块之后,作为独立一块。正文只读(真源是 preset 的组合文件),
369
+ 主操作是打开那份文件所在的真源目录。默认 preset 不在这里:那是 DSH 设置
370
+ 里的一项,再摆一个只是同一字段的第二个入口。 */}
371
+ <AgentPresetCard
372
+ {...(remote ? { remote } : {})}
373
+ {...(layout?.presetDir ? { presetDir: layout.presetDir } : {})}
374
+ {...(layout?.presetDirExists === undefined ? {} : { presetDirExists: layout.presetDirExists })}
375
+ />
376
+ </div>
377
+ </DrawerBody>
378
+ </DrawerContent>}
379
+ </Drawer>
380
+ </section>
381
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * 知识库的分栏切换器 —— **一条流水线,不是一排并列的 tab**。
3
+ *
4
+ * 0027 之前这里是 Radix 的 `TabsList`:五个等宽按钮并排,视觉上回答的是「五选一」。
5
+ * 但知识库这五栏之间是**有方向的**:SourceRaw 收录 → 编译成 wiki → 构建成图谱 →
6
+ * 召回 → 精排。方向本身就是信息(写入侧在左、读取侧在右),而一排等宽按钮把它抹掉了。
7
+ *
8
+ * 所以这里改成一条**步骤轨**:每一步 = 名词 + 动作 + 这一段的真实计数,步与步之间用
9
+ * 箭头连起来。它不冒充进度条 —— 召回 / 重排是运行时能力,不是「走完就算完成」的阶段,
10
+ * 所以没有「已完成 / 未完成」的勾,只有「现在停在哪一步」。
11
+ *
12
+ * 计数全部来自真实落盘与设置:没有数据的那一步宁可只写动作,也不编一个 0。
13
+ */
14
+
15
+ import { Fragment, type ReactNode } from 'react'
16
+ import { ChevronRight } from '@tita-data-agent/ui/icons'
17
+ import { cn } from '@tita-data-agent/ui/components'
18
+ import { KNOWLEDGE_TABS, type KnowledgeTab } from './architecture.tsx'
19
+ import { styles } from './styles.ts'
20
+
21
+ /** 每一步的两条事实:这一段现在有多少东西,以及它做什么。 */
22
+ export interface StepFact { value: string; note: string }
23
+
24
+ export interface KnowledgeStepRailProps {
25
+ tab: KnowledgeTab
26
+ onChange: (tab: KnowledgeTab) => void
27
+ facts: Record<KnowledgeTab, StepFact>
28
+ }
29
+
30
+ export function KnowledgeStepRail({ tab, onChange, facts }: KnowledgeStepRailProps): ReactNode {
31
+ return <nav className={styles.kbSteps} aria-label="知识链路分栏">
32
+ {KNOWLEDGE_TABS.map((item, index) => {
33
+ const Icon = item.icon
34
+ const fact = facts[item.id]
35
+ const on = tab === item.id
36
+ return <Fragment key={item.id}>
37
+ {index > 0 && <span className={styles.kbStepArrow} aria-hidden="true"><ChevronRight size={13} /></span>}
38
+ <button
39
+ type="button"
40
+ className={cn(styles.kbStep, on && styles.kbStepOn)}
41
+ aria-current={on ? 'step' : undefined}
42
+ onClick={() => onChange(item.id)}
43
+ >
44
+ {/* 两行,不是一行挤四样东西。一行放「图标 + 名字 + 动作 + 计数」在宿主侧栏
45
+ 的宽度下(五步平分约 146px)必然把名字截成「So…」—— 而名字是这一步唯一
46
+ 不可省的东西。所以第一行只放身份,第二行放事实。 */}
47
+ <span className={styles.kbStepTop}>
48
+ <Icon size={13} aria-hidden="true" />
49
+ <b>{item.label}</b>
50
+ <em className={styles.kbStepVerb}>{item.step}</em>
51
+ </span>
52
+ <span className={styles.kbStepFacts}>
53
+ <span className={styles.kbStepValue}>{fact.value}</span>
54
+ <span className={styles.kbStepNote}>{fact.note}</span>
55
+ </span>
56
+ </button>
57
+ </Fragment>
58
+ })}
59
+ </nav>
60
+ }