dsh-data-domain-knowledge 0.1.0-alpha.9 → 0.1.1-alpha.2

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 (438) hide show
  1. package/README.md +118 -567
  2. package/cordis.patch.yml +5 -418
  3. package/eval/README.md +19 -8
  4. package/eval/benchmark-extraction-format.mjs +20 -0
  5. package/eval/evaluate-retrieval.mjs +17 -8
  6. package/eval/queries.example.jsonl +1 -0
  7. package/lib/client.js +4181 -3239
  8. package/lib/contract-envelope.d.ts +91 -0
  9. package/lib/contract-envelope.js +106 -0
  10. package/lib/contract-layout.d.ts +130 -0
  11. package/lib/contract-layout.js +138 -0
  12. package/lib/contract-refs.d.ts +39 -0
  13. package/lib/contract-refs.js +9 -0
  14. package/lib/contract-version.d.ts +311 -0
  15. package/lib/contract-version.js +270 -0
  16. package/lib/contract-wire.d.ts +147 -0
  17. package/lib/contract-wire.js +99 -0
  18. package/lib/contracts.d.ts +12 -0
  19. package/lib/contracts.js +12 -0
  20. package/lib/doc-page.js +2 -2
  21. package/lib/pages.js +3329 -1296
  22. package/lib/types/agent-behavior-rules.d.ts +10 -0
  23. package/lib/types/agent-behavior-rules.d.ts.map +1 -0
  24. package/lib/types/agent-behavior-rules.js +28 -0
  25. package/lib/types/agent-behavior-rules.js.map +1 -0
  26. package/lib/types/axis-registry.d.ts +37 -5
  27. package/lib/types/axis-registry.d.ts.map +1 -1
  28. package/lib/types/axis-registry.js +234 -26
  29. package/lib/types/axis-registry.js.map +1 -1
  30. package/lib/types/card-organize.d.ts +27 -12
  31. package/lib/types/card-organize.d.ts.map +1 -1
  32. package/lib/types/card-organize.js +58 -73
  33. package/lib/types/card-organize.js.map +1 -1
  34. package/lib/types/compiler/embedding-identity.d.ts +3 -0
  35. package/lib/types/compiler/embedding-identity.d.ts.map +1 -0
  36. package/lib/types/compiler/embedding-identity.js +24 -0
  37. package/lib/types/compiler/embedding-identity.js.map +1 -0
  38. package/lib/types/compiler/snapshot.d.ts +16 -0
  39. package/lib/types/compiler/snapshot.d.ts.map +1 -0
  40. package/lib/types/compiler/snapshot.js +2 -0
  41. package/lib/types/compiler/snapshot.js.map +1 -0
  42. package/lib/types/config-schema.d.ts +6 -3
  43. package/lib/types/config-schema.d.ts.map +1 -1
  44. package/lib/types/config-schema.js +6 -2
  45. package/lib/types/config-schema.js.map +1 -1
  46. package/lib/types/convert/host.d.ts +6 -5
  47. package/lib/types/convert/host.d.ts.map +1 -1
  48. package/lib/types/convert/host.js +58 -18
  49. package/lib/types/convert/host.js.map +1 -1
  50. package/lib/types/convert/ingest.d.ts +10 -1
  51. package/lib/types/convert/ingest.d.ts.map +1 -1
  52. package/lib/types/convert/ingest.js +68 -9
  53. package/lib/types/convert/ingest.js.map +1 -1
  54. package/lib/types/derive-contract.d.ts +3 -0
  55. package/lib/types/derive-contract.d.ts.map +1 -0
  56. package/lib/types/derive-contract.js +15 -0
  57. package/lib/types/derive-contract.js.map +1 -0
  58. package/lib/types/diagnosis/cards.d.ts +7 -3
  59. package/lib/types/diagnosis/cards.d.ts.map +1 -1
  60. package/lib/types/diagnosis/cards.js +13 -8
  61. package/lib/types/diagnosis/cards.js.map +1 -1
  62. package/lib/types/diagnosis/run.d.ts +4 -13
  63. package/lib/types/diagnosis/run.d.ts.map +1 -1
  64. package/lib/types/diagnosis/run.js +26 -16
  65. package/lib/types/diagnosis/run.js.map +1 -1
  66. package/lib/types/diagnosis/schema.js +2 -2
  67. package/lib/types/diagnosis/schema.js.map +1 -1
  68. package/lib/types/diagnosis/seed-source.d.ts +1 -1
  69. package/lib/types/diagnosis/seed-source.d.ts.map +1 -1
  70. package/lib/types/diagnosis/seed-source.js +5 -4
  71. package/lib/types/diagnosis/seed-source.js.map +1 -1
  72. package/lib/types/diagnosis/seed.d.ts +4 -4
  73. package/lib/types/diagnosis/seed.d.ts.map +1 -1
  74. package/lib/types/diagnosis/seed.js +6 -8
  75. package/lib/types/diagnosis/seed.js.map +1 -1
  76. package/lib/types/diagnosis/store.d.ts +24 -18
  77. package/lib/types/diagnosis/store.d.ts.map +1 -1
  78. package/lib/types/diagnosis/store.js +130 -65
  79. package/lib/types/diagnosis/store.js.map +1 -1
  80. package/lib/types/diagnosis/types.d.ts +47 -15
  81. package/lib/types/diagnosis/types.d.ts.map +1 -1
  82. package/lib/types/diagnosis/types.js.map +1 -1
  83. package/lib/types/environment/dsh-python.d.ts +77 -0
  84. package/lib/types/environment/dsh-python.d.ts.map +1 -0
  85. package/lib/types/environment/dsh-python.js +139 -0
  86. package/lib/types/environment/dsh-python.js.map +1 -0
  87. package/lib/types/environment/install.d.ts +15 -0
  88. package/lib/types/environment/install.d.ts.map +1 -0
  89. package/lib/types/environment/install.js +297 -0
  90. package/lib/types/environment/install.js.map +1 -0
  91. package/lib/types/environment/manifest.d.ts +208 -0
  92. package/lib/types/environment/manifest.d.ts.map +1 -0
  93. package/lib/types/environment/manifest.js +26 -0
  94. package/lib/types/environment/manifest.js.map +1 -0
  95. package/lib/types/environment/model-catalog.d.ts +223 -0
  96. package/lib/types/environment/model-catalog.d.ts.map +1 -0
  97. package/lib/types/environment/model-catalog.js +129 -0
  98. package/lib/types/environment/model-catalog.js.map +1 -0
  99. package/lib/types/environment/process.d.ts +10 -0
  100. package/lib/types/environment/process.d.ts.map +1 -0
  101. package/lib/types/environment/process.js +54 -0
  102. package/lib/types/environment/process.js.map +1 -0
  103. package/lib/types/environment/selection.d.ts +9 -0
  104. package/lib/types/environment/selection.d.ts.map +1 -0
  105. package/lib/types/environment/selection.js +29 -0
  106. package/lib/types/environment/selection.js.map +1 -0
  107. package/lib/types/environment/service.d.ts +64 -0
  108. package/lib/types/environment/service.d.ts.map +1 -0
  109. package/lib/types/environment/service.js +424 -0
  110. package/lib/types/environment/service.js.map +1 -0
  111. package/lib/types/environment/types.d.ts +56 -0
  112. package/lib/types/environment/types.d.ts.map +1 -0
  113. package/lib/types/environment/types.js +2 -0
  114. package/lib/types/environment/types.js.map +1 -0
  115. package/lib/types/events.d.ts +76 -0
  116. package/lib/types/events.d.ts.map +1 -0
  117. package/lib/types/events.js +52 -0
  118. package/lib/types/events.js.map +1 -0
  119. package/lib/types/extract-contract.d.ts +3 -0
  120. package/lib/types/extract-contract.d.ts.map +1 -0
  121. package/lib/types/extract-contract.js +41 -0
  122. package/lib/types/extract-contract.js.map +1 -0
  123. package/lib/types/extract-model.d.ts +5 -0
  124. package/lib/types/extract-model.d.ts.map +1 -1
  125. package/lib/types/extract-model.js.map +1 -1
  126. package/lib/types/extract-table-contract.d.ts +3 -0
  127. package/lib/types/extract-table-contract.d.ts.map +1 -0
  128. package/lib/types/extract-table-contract.js +27 -0
  129. package/lib/types/extract-table-contract.js.map +1 -0
  130. package/lib/types/extract-table.d.ts +8 -0
  131. package/lib/types/extract-table.d.ts.map +1 -0
  132. package/lib/types/extract-table.js +158 -0
  133. package/lib/types/extract-table.js.map +1 -0
  134. package/lib/types/extraction-runs.d.ts +30 -6
  135. package/lib/types/extraction-runs.d.ts.map +1 -1
  136. package/lib/types/extraction-runs.js +69 -8
  137. package/lib/types/extraction-runs.js.map +1 -1
  138. package/lib/types/fs-atomic.d.ts +0 -7
  139. package/lib/types/fs-atomic.d.ts.map +1 -1
  140. package/lib/types/fs-atomic.js +17 -2
  141. package/lib/types/fs-atomic.js.map +1 -1
  142. package/lib/types/images.d.ts +26 -0
  143. package/lib/types/images.d.ts.map +1 -0
  144. package/lib/types/images.js +196 -0
  145. package/lib/types/images.js.map +1 -0
  146. package/lib/types/index.d.ts +2 -2
  147. package/lib/types/index.d.ts.map +1 -1
  148. package/lib/types/index.js +598 -209
  149. package/lib/types/index.js.map +1 -1
  150. package/lib/types/intent/engine.d.ts +12 -4
  151. package/lib/types/intent/engine.d.ts.map +1 -1
  152. package/lib/types/intent/engine.js +43 -5
  153. package/lib/types/intent/engine.js.map +1 -1
  154. package/lib/types/intent/host.d.ts +5 -8
  155. package/lib/types/intent/host.d.ts.map +1 -1
  156. package/lib/types/intent/host.js +56 -11
  157. package/lib/types/intent/host.js.map +1 -1
  158. package/lib/types/intent/scope.d.ts +25 -0
  159. package/lib/types/intent/scope.d.ts.map +1 -0
  160. package/lib/types/intent/scope.js +116 -0
  161. package/lib/types/intent/scope.js.map +1 -0
  162. package/lib/types/intent/tools.d.ts +24 -20
  163. package/lib/types/intent/tools.d.ts.map +1 -1
  164. package/lib/types/intent/tools.js +65 -23
  165. package/lib/types/intent/tools.js.map +1 -1
  166. package/lib/types/intent/types.d.ts +11 -4
  167. package/lib/types/intent/types.d.ts.map +1 -1
  168. package/lib/types/knowledge-bases.d.ts +11 -5
  169. package/lib/types/knowledge-bases.d.ts.map +1 -1
  170. package/lib/types/knowledge-bases.js +52 -20
  171. package/lib/types/knowledge-bases.js.map +1 -1
  172. package/lib/types/knowledge-contract.d.ts +32 -0
  173. package/lib/types/knowledge-contract.d.ts.map +1 -0
  174. package/lib/types/knowledge-contract.js +116 -0
  175. package/lib/types/knowledge-contract.js.map +1 -0
  176. package/lib/types/knowledge.d.ts +31 -9
  177. package/lib/types/knowledge.d.ts.map +1 -1
  178. package/lib/types/knowledge.js +137 -60
  179. package/lib/types/knowledge.js.map +1 -1
  180. package/lib/types/layout.d.ts +10 -29
  181. package/lib/types/layout.d.ts.map +1 -1
  182. package/lib/types/layout.js +17 -36
  183. package/lib/types/layout.js.map +1 -1
  184. package/lib/types/materialize-snapshot.d.ts +49 -0
  185. package/lib/types/materialize-snapshot.d.ts.map +1 -0
  186. package/lib/types/materialize-snapshot.js +2 -0
  187. package/lib/types/materialize-snapshot.js.map +1 -0
  188. package/lib/types/migrate.d.ts +11 -0
  189. package/lib/types/migrate.d.ts.map +1 -1
  190. package/lib/types/migrate.js +83 -2
  191. package/lib/types/migrate.js.map +1 -1
  192. package/lib/types/model-text.d.ts +58 -1
  193. package/lib/types/model-text.d.ts.map +1 -1
  194. package/lib/types/model-text.js +85 -30
  195. package/lib/types/model-text.js.map +1 -1
  196. package/lib/types/oplog.d.ts.map +1 -1
  197. package/lib/types/oplog.js +7 -0
  198. package/lib/types/oplog.js.map +1 -1
  199. package/lib/types/organize-contract.d.ts +18 -0
  200. package/lib/types/organize-contract.d.ts.map +1 -0
  201. package/lib/types/organize-contract.js +82 -0
  202. package/lib/types/organize-contract.js.map +1 -0
  203. package/lib/types/pipeline/engine.d.ts +66 -0
  204. package/lib/types/pipeline/engine.d.ts.map +1 -0
  205. package/lib/types/pipeline/engine.js +352 -0
  206. package/lib/types/pipeline/engine.js.map +1 -0
  207. package/lib/types/pipeline/store.d.ts +73 -0
  208. package/lib/types/pipeline/store.d.ts.map +1 -0
  209. package/lib/types/pipeline/store.js +143 -0
  210. package/lib/types/pipeline/store.js.map +1 -0
  211. package/lib/types/pipeline/types.d.ts +102 -0
  212. package/lib/types/pipeline/types.d.ts.map +1 -0
  213. package/lib/types/pipeline/types.js +5 -0
  214. package/lib/types/pipeline/types.js.map +1 -0
  215. package/lib/types/present.d.ts +2 -0
  216. package/lib/types/present.d.ts.map +1 -1
  217. package/lib/types/present.js +9 -4
  218. package/lib/types/present.js.map +1 -1
  219. package/lib/types/preset-ids.d.ts +4 -0
  220. package/lib/types/preset-ids.d.ts.map +1 -0
  221. package/lib/types/preset-ids.js +6 -0
  222. package/lib/types/preset-ids.js.map +1 -0
  223. package/lib/types/query-analysis.d.ts +13 -1
  224. package/lib/types/query-analysis.d.ts.map +1 -1
  225. package/lib/types/query-analysis.js +62 -6
  226. package/lib/types/query-analysis.js.map +1 -1
  227. package/lib/types/recall-rank.d.ts +3 -3
  228. package/lib/types/recall-rank.d.ts.map +1 -1
  229. package/lib/types/recall-rank.js +4 -4
  230. package/lib/types/recall-rank.js.map +1 -1
  231. package/lib/types/retention.d.ts +39 -0
  232. package/lib/types/retention.d.ts.map +1 -0
  233. package/lib/types/retention.js +42 -0
  234. package/lib/types/retention.js.map +1 -0
  235. package/lib/types/retrieval/vector-index.d.ts +32 -0
  236. package/lib/types/retrieval/vector-index.d.ts.map +1 -0
  237. package/lib/types/retrieval/vector-index.js +15 -0
  238. package/lib/types/retrieval/vector-index.js.map +1 -0
  239. package/lib/types/retrieval/visibility.d.ts +6 -0
  240. package/lib/types/retrieval/visibility.d.ts.map +1 -0
  241. package/lib/types/retrieval/visibility.js +11 -0
  242. package/lib/types/retrieval/visibility.js.map +1 -0
  243. package/lib/types/runtime.d.ts +272 -27
  244. package/lib/types/runtime.d.ts.map +1 -1
  245. package/lib/types/runtime.js +2751 -537
  246. package/lib/types/runtime.js.map +1 -1
  247. package/lib/types/settings-schema.d.ts +17 -15
  248. package/lib/types/settings-schema.d.ts.map +1 -1
  249. package/lib/types/settings-schema.js +50 -47
  250. package/lib/types/settings-schema.js.map +1 -1
  251. package/lib/types/settings-volatile.d.ts +4 -4
  252. package/lib/types/settings-volatile.js +4 -4
  253. package/lib/types/source-evidence.d.ts +14 -0
  254. package/lib/types/source-evidence.d.ts.map +1 -0
  255. package/lib/types/source-evidence.js +36 -0
  256. package/lib/types/source-evidence.js.map +1 -0
  257. package/lib/types/store/axis-references.d.ts +5 -0
  258. package/lib/types/store/axis-references.d.ts.map +1 -0
  259. package/lib/types/store/axis-references.js +106 -0
  260. package/lib/types/store/axis-references.js.map +1 -0
  261. package/lib/types/store/derived.d.ts +12 -1
  262. package/lib/types/store/derived.d.ts.map +1 -1
  263. package/lib/types/store/derived.js +71 -12
  264. package/lib/types/store/derived.js.map +1 -1
  265. package/lib/types/store/knowledge-db.d.ts +31 -1
  266. package/lib/types/store/knowledge-db.d.ts.map +1 -1
  267. package/lib/types/store/knowledge-db.js +252 -18
  268. package/lib/types/store/knowledge-db.js.map +1 -1
  269. package/lib/types/store/migrate.d.ts.map +1 -1
  270. package/lib/types/store/migrate.js +17 -6
  271. package/lib/types/store/migrate.js.map +1 -1
  272. package/lib/types/trajectory-types.d.ts +172 -0
  273. package/lib/types/trajectory-types.d.ts.map +1 -0
  274. package/lib/types/trajectory-types.js +36 -0
  275. package/lib/types/trajectory-types.js.map +1 -0
  276. package/lib/types/types.d.ts +59 -27
  277. package/lib/types/types.d.ts.map +1 -1
  278. package/lib/types/types.js +6 -15
  279. package/lib/types/types.js.map +1 -1
  280. package/lib/types/wiki-finalize.d.ts +16 -0
  281. package/lib/types/wiki-finalize.d.ts.map +1 -1
  282. package/lib/types/wiki-finalize.js +26 -0
  283. package/lib/types/wiki-finalize.js.map +1 -1
  284. package/lib/types/wiki-materialize.d.ts +4 -0
  285. package/lib/types/wiki-materialize.d.ts.map +1 -1
  286. package/lib/types/wiki-materialize.js +21 -7
  287. package/lib/types/wiki-materialize.js.map +1 -1
  288. package/lib/types/wiki-trial-types.d.ts +87 -0
  289. package/lib/types/wiki-trial-types.d.ts.map +1 -1
  290. package/lib/types/wiki-trial-types.js +0 -16
  291. package/lib/types/wiki-trial-types.js.map +1 -1
  292. package/lib/types/wiki-trial.d.ts +40 -16
  293. package/lib/types/wiki-trial.d.ts.map +1 -1
  294. package/lib/types/wiki-trial.js +337 -224
  295. package/lib/types/wiki-trial.js.map +1 -1
  296. package/package.json +29 -20
  297. package/python/prepare-models.py +19 -0
  298. package/python/requirements.txt +8 -6
  299. package/scripts/doctor.mjs +13 -19
  300. package/scripts/environment.mjs +29 -0
  301. package/scripts/setup-embedding.sh +15 -5
  302. package/src/agent-behavior-rules.ts +37 -0
  303. package/src/axis-registry.ts +234 -27
  304. package/src/card-organize.ts +69 -75
  305. package/src/client/AgentBehaviorRulesCard.tsx +65 -0
  306. package/src/client/AttributionEventGuide.tsx +41 -39
  307. package/src/client/AxisRegistrySection.tsx +204 -164
  308. package/src/client/CompileDialogs.tsx +44 -29
  309. package/src/client/DeleteDocumentDialog.tsx +36 -5
  310. package/src/client/DirectoryPicker.tsx +0 -10
  311. package/src/client/EnvironmentSetup.tsx +243 -0
  312. package/src/client/ExpansionGraph.tsx +13 -7
  313. package/src/client/ExtractionTasksPage.tsx +578 -0
  314. package/src/client/GuideArchitectureCanvas.tsx +470 -0
  315. package/src/client/ImportFilesDialog.tsx +2 -13
  316. package/src/client/IntentLogic.tsx +2 -2
  317. package/src/client/IntentTab.tsx +118 -47
  318. package/src/client/KnowledgeBaseTab.tsx +277 -197
  319. package/src/client/KnowledgeLayerSection.tsx +24 -6
  320. package/src/client/KnowledgePanel.tsx +58 -93
  321. package/src/client/KnowledgeSettingsDrawer.tsx +90 -0
  322. package/src/client/ListPager.tsx +15 -5
  323. package/src/client/MaterializeTrajectoryView.tsx +124 -0
  324. package/src/client/NodeSettings.tsx +210 -89
  325. package/src/client/NormalizeTrajectoryView.tsx +136 -0
  326. package/src/client/PipelineNotice.tsx +26 -0
  327. package/src/client/PreviewSections.tsx +118 -51
  328. package/src/client/RecallPanel.tsx +89 -85
  329. package/src/client/TabGuide.tsx +159 -184
  330. package/src/client/TrialReportView.tsx +391 -214
  331. package/src/client/TypeDefinitionDrawer.tsx +150 -23
  332. package/src/client/WikiCardPreview.tsx +62 -16
  333. package/src/client/WikiFlowPanel.tsx +4 -4
  334. package/src/client/arch/NodeLogicPanel.tsx +3 -4
  335. package/src/client/arch/model.ts +6 -9
  336. package/src/client/architecture.tsx +23 -33
  337. package/src/client/attribution/AttributionGraph.tsx +10 -6
  338. package/src/client/attribution/AttributionTab.tsx +136 -84
  339. package/src/client/attribution/CaseView.tsx +3 -1
  340. package/src/client/attribution/EventView.tsx +108 -0
  341. package/src/client/attribution/RunView.tsx +2 -2
  342. package/src/client/attribution/StageChrome.tsx +34 -3
  343. package/src/client/attribution/ViewSwitch.tsx +20 -5
  344. package/src/client/attribution/graph-options.ts +47 -39
  345. package/src/client/attribution/model.ts +17 -10
  346. package/src/client/config-scope.ts +3 -3
  347. package/src/client/graph/palette.ts +165 -0
  348. package/src/client/index.ts +49 -42
  349. package/src/client/pages.css +459 -135
  350. package/src/client/pages.tsx +37 -9
  351. package/src/client/problem-domains.ts +1 -1
  352. package/src/client/session-remote.ts +9 -0
  353. package/src/client/styles.css +205 -62
  354. package/src/client/wiki-trial.ts +44 -3
  355. package/src/compiler/embedding-identity.ts +21 -0
  356. package/src/compiler/snapshot.ts +17 -0
  357. package/src/config-schema.ts +8 -3
  358. package/src/convert/host.ts +56 -21
  359. package/src/convert/ingest.ts +74 -10
  360. package/src/derive-contract.ts +14 -0
  361. package/src/diagnosis/cards.ts +13 -8
  362. package/src/diagnosis/run.ts +27 -17
  363. package/src/diagnosis/schema.ts +2 -2
  364. package/src/diagnosis/seed-source.ts +5 -4
  365. package/src/diagnosis/seed.ts +6 -8
  366. package/src/diagnosis/store.ts +138 -69
  367. package/src/diagnosis/types.ts +34 -15
  368. package/src/environment/dsh-python.ts +156 -0
  369. package/src/environment/install.ts +244 -0
  370. package/src/environment/manifest.ts +28 -0
  371. package/src/environment/model-catalog.ts +147 -0
  372. package/src/environment/process.ts +54 -0
  373. package/src/environment/selection.ts +29 -0
  374. package/src/environment/service.ts +348 -0
  375. package/src/environment/types.ts +45 -0
  376. package/src/events.ts +108 -0
  377. package/src/extract-contract.ts +42 -0
  378. package/src/extract-model.ts +10 -1
  379. package/src/extract-table-contract.ts +26 -0
  380. package/src/extract-table.ts +132 -0
  381. package/src/extraction-runs.ts +70 -11
  382. package/src/fs-atomic.ts +19 -2
  383. package/src/images.ts +203 -0
  384. package/src/index.ts +553 -205
  385. package/src/intent/README.md +8 -6
  386. package/src/intent/engine.ts +44 -6
  387. package/src/intent/host.ts +59 -18
  388. package/src/intent/scope.ts +125 -0
  389. package/src/intent/tools.ts +70 -28
  390. package/src/intent/types.ts +11 -4
  391. package/src/knowledge-bases.ts +56 -20
  392. package/src/knowledge-contract.ts +114 -0
  393. package/src/knowledge.ts +138 -69
  394. package/src/layout.ts +17 -37
  395. package/src/materialize-snapshot.ts +46 -0
  396. package/src/migrate.ts +75 -2
  397. package/src/model-text.ts +109 -4
  398. package/src/oplog.ts +7 -0
  399. package/src/organize-contract.ts +83 -0
  400. package/src/pipeline/engine.ts +323 -0
  401. package/src/pipeline/store.ts +147 -0
  402. package/src/pipeline/types.ts +93 -0
  403. package/src/present.ts +10 -4
  404. package/src/preset-ids.ts +6 -0
  405. package/src/python/embedding_worker.py +66 -2
  406. package/src/query-analysis.ts +68 -8
  407. package/src/recall-rank.ts +5 -5
  408. package/src/retention.ts +76 -0
  409. package/src/retrieval/vector-index.ts +45 -0
  410. package/src/retrieval/visibility.ts +14 -0
  411. package/src/runtime.ts +2708 -567
  412. package/src/settings-schema.ts +11 -8
  413. package/src/settings-volatile.ts +4 -4
  414. package/src/source-evidence.ts +37 -0
  415. package/src/store/axis-references.ts +96 -0
  416. package/src/store/derived.ts +70 -12
  417. package/src/store/knowledge-db.ts +248 -17
  418. package/src/store/migrate.ts +16 -6
  419. package/src/trajectory-types.ts +142 -0
  420. package/src/types.ts +61 -35
  421. package/src/wiki-finalize.ts +28 -0
  422. package/src/wiki-materialize.ts +25 -6
  423. package/src/wiki-trial-types.ts +83 -1
  424. package/src/wiki-trial.ts +357 -185
  425. package/lib/types/intent/preset-tools.d.ts +0 -37
  426. package/lib/types/intent/preset-tools.d.ts.map +0 -1
  427. package/lib/types/intent/preset-tools.js +0 -45
  428. package/lib/types/intent/preset-tools.js.map +0 -1
  429. package/preset/tita-data-agent/agent.cordis.yml +0 -394
  430. package/preset/tita-data-agent/preset.yml +0 -3
  431. package/scripts/install-agent-preset.mjs +0 -92
  432. package/scripts/sync-agent-preset.mjs +0 -188
  433. package/src/client/AgentPresetCard.tsx +0 -247
  434. package/src/client/agent-preset.ts +0 -121
  435. package/src/client/convert/ConvertPanel.tsx +0 -583
  436. package/src/client/convert/styles.css +0 -138
  437. package/src/client/convert/styles.ts +0 -49
  438. package/src/intent/preset-tools.ts +0 -48
@@ -42,8 +42,8 @@ import { styles } from './styles.ts'
42
42
  * Markdown 原子的界面文案。必须是**模块级常量**:平台明确要求这个对象引用稳定
43
43
  * (换一个身份会让流式渲染的缓存整体作废),在渲染里现造一个对象正好踩中那条。
44
44
  *
45
- * 这三个词与 `plugins/dsh-data-notebook` 的同名常量逐字相同。没有抽回 `packages/ui`
46
- * 是刻意的:`MarkdownLabels` 的类型来自**宿主提供的**平台包,而 data-notebook 的
45
+ * 这三个词是预览区的显示文案。没有抽回 `packages/ui`
46
+ * 是刻意的:`MarkdownLabels` 的类型来自**宿主提供的**平台包,而各预览消费者的
47
47
  * 独立整页入口必须能把平台包整块排除在 bundle 之外 —— 共享层一旦引它,那条「独立
48
48
  * 页面不拉平台依赖」的约束就断了。三行文案不值得拿那条约束换。
49
49
  */
@@ -524,78 +524,128 @@ export interface VersionContentDiffData {
524
524
  }
525
525
 
526
526
  type ContentDiffLine = { kind: 'add' | 'remove' | 'same'; text: string }
527
- type ContentDiffDisplayLine = ContentDiffLine | { kind: 'skip'; count: number }
528
-
529
- /** Small, bounded LCS diff for Markdown/plain-text version bodies. */
530
- function contentDiffOf(before: string, after: string): ContentDiffLine[] {
531
- const oldLines = before.replace(/\r\n/g, '\n').split('\n').slice(0, 600)
532
- const newLines = after.replace(/\r\n/g, '\n').split('\n').slice(0, 600)
533
- const matrix = Array.from({ length: oldLines.length + 1 }, () => new Uint16Array(newLines.length + 1))
534
- for (let oldIndex = oldLines.length - 1; oldIndex >= 0; oldIndex -= 1) {
535
- for (let newIndex = newLines.length - 1; newIndex >= 0; newIndex -= 1) {
536
- matrix[oldIndex]![newIndex] = oldLines[oldIndex] === newLines[newIndex]
537
- ? (matrix[oldIndex + 1]![newIndex + 1] ?? 0) + 1
538
- : Math.max(matrix[oldIndex + 1]![newIndex] ?? 0, matrix[oldIndex]![newIndex + 1] ?? 0)
527
+ type ContentDiffDisplayLine =
528
+ | { kind: 'pair'; before?: string; after?: string }
529
+ | { kind: 'skip'; count: number }
530
+
531
+ /** Line diff for Markdown/plain-text version bodies. Full mode keeps every line. */
532
+ function contentDiffOf(before: string, after: string, fullText = false): ContentDiffLine[] {
533
+ const oldLines = before.replace(/\r\n/g, '\n').split('\n')
534
+ const newLines = after.replace(/\r\n/g, '\n').split('\n')
535
+ if (!fullText) {
536
+ oldLines.length = Math.min(oldLines.length, 600)
537
+ newLines.length = Math.min(newLines.length, 600)
538
+ }
539
+
540
+ // Trim shared edges before LCS. Wiki cards often have a small edit in a long body.
541
+ let prefix = 0
542
+ let oldEnd = oldLines.length
543
+ let newEnd = newLines.length
544
+ if (fullText) {
545
+ while (prefix < oldEnd && prefix < newEnd && oldLines[prefix] === newLines[prefix]) prefix += 1
546
+ while (oldEnd > prefix && newEnd > prefix && oldLines[oldEnd - 1] === newLines[newEnd - 1]) {
547
+ oldEnd -= 1
548
+ newEnd -= 1
539
549
  }
550
+ } else {
551
+ prefix = 0
552
+ oldEnd = oldLines.length
553
+ newEnd = newLines.length
540
554
  }
555
+
556
+ const oldMiddle = oldLines.slice(prefix, oldEnd)
557
+ const newMiddle = newLines.slice(prefix, newEnd)
541
558
  const lines: ContentDiffLine[] = []
542
- let oldIndex = 0
543
- let newIndex = 0
544
- while (oldIndex < oldLines.length || newIndex < newLines.length) {
545
- if (oldIndex < oldLines.length && newIndex < newLines.length && oldLines[oldIndex] === newLines[newIndex]) {
546
- lines.push({ kind: 'same', text: oldLines[oldIndex]! })
547
- oldIndex += 1
548
- newIndex += 1
549
- } else if (oldIndex < oldLines.length && (newIndex >= newLines.length || (matrix[oldIndex + 1]![newIndex] ?? 0) >= (matrix[oldIndex]![newIndex + 1] ?? 0))) {
550
- lines.push({ kind: 'remove', text: oldLines[oldIndex]! })
551
- oldIndex += 1
552
- } else {
553
- lines.push({ kind: 'add', text: newLines[newIndex]! })
554
- newIndex += 1
559
+ for (let index = 0; index < prefix; index += 1) lines.push({ kind: 'same', text: oldLines[index]! })
560
+
561
+ // Bound the quadratic LCS allocation for unusually large, completely rewritten hunks.
562
+ // The full text remains visible; only that hunk is marked as remove/add when it exceeds the budget.
563
+ if (fullText && (oldMiddle.length + newMiddle.length > 20_000 || oldMiddle.length * newMiddle.length > 1_000_000)) {
564
+ for (const text of oldMiddle) lines.push({ kind: 'remove', text })
565
+ for (const text of newMiddle) lines.push({ kind: 'add', text })
566
+ } else {
567
+ const matrix = Array.from({ length: oldMiddle.length + 1 }, () => new Uint16Array(newMiddle.length + 1))
568
+ for (let oldIndex = oldMiddle.length - 1; oldIndex >= 0; oldIndex -= 1) {
569
+ for (let newIndex = newMiddle.length - 1; newIndex >= 0; newIndex -= 1) {
570
+ matrix[oldIndex]![newIndex] = oldMiddle[oldIndex] === newMiddle[newIndex]
571
+ ? (matrix[oldIndex + 1]![newIndex + 1] ?? 0) + 1
572
+ : Math.max(matrix[oldIndex + 1]![newIndex] ?? 0, matrix[oldIndex]![newIndex + 1] ?? 0)
573
+ }
574
+ }
575
+ let oldIndex = 0
576
+ let newIndex = 0
577
+ while (oldIndex < oldMiddle.length || newIndex < newMiddle.length) {
578
+ if (oldIndex < oldMiddle.length && newIndex < newMiddle.length && oldMiddle[oldIndex] === newMiddle[newIndex]) {
579
+ lines.push({ kind: 'same', text: oldMiddle[oldIndex]! })
580
+ oldIndex += 1
581
+ newIndex += 1
582
+ } else if (oldIndex < oldMiddle.length && (newIndex >= newMiddle.length || (matrix[oldIndex + 1]![newIndex] ?? 0) >= (matrix[oldIndex]![newIndex + 1] ?? 0))) {
583
+ lines.push({ kind: 'remove', text: oldMiddle[oldIndex]! })
584
+ oldIndex += 1
585
+ } else {
586
+ lines.push({ kind: 'add', text: newMiddle[newIndex]! })
587
+ newIndex += 1
588
+ }
555
589
  }
556
590
  }
591
+
592
+ for (let index = 0; index < oldLines.length - oldEnd; index += 1) {
593
+ lines.push({ kind: 'same', text: oldLines[oldEnd + index]! })
594
+ }
557
595
  return lines
558
596
  }
559
597
 
560
- function compactContentDiff(lines: ContentDiffLine[]): ContentDiffDisplayLine[] {
598
+ function compactContentDiff(lines: ContentDiffLine[], fullText = false): ContentDiffDisplayLine[] {
561
599
  const out: ContentDiffDisplayLine[] = []
562
600
  let index = 0
563
601
  while (index < lines.length) {
564
- if (lines[index]?.kind !== 'same') {
565
- out.push(lines[index]!)
566
- index += 1
602
+ if (lines[index]?.kind === 'same') {
603
+ const start = index
604
+ while (index < lines.length && lines[index]?.kind === 'same') index += 1
605
+ const count = index - start
606
+ const visible = fullText || count <= 6
607
+ ? lines.slice(start, index)
608
+ : [...lines.slice(start, start + 2), { kind: 'skip' as const, count: count - 4 }, ...lines.slice(index - 2, index)]
609
+ for (const line of visible) {
610
+ if (line.kind === 'skip') out.push(line)
611
+ else out.push({ kind: 'pair', before: line.text, after: line.text })
612
+ }
567
613
  continue
568
614
  }
569
- const start = index
570
- while (index < lines.length && lines[index]?.kind === 'same') index += 1
571
- const count = index - start
572
- if (count <= 6) out.push(...lines.slice(start, index))
573
- else {
574
- out.push(...lines.slice(start, start + 2))
575
- out.push({ kind: 'skip', count: count - 4 })
576
- out.push(...lines.slice(index - 2, index))
615
+ const removed: string[] = []
616
+ const added: string[] = []
617
+ while (index < lines.length && lines[index]?.kind !== 'same') {
618
+ const line = lines[index]!
619
+ if (line.kind === 'remove') removed.push(line.text)
620
+ if (line.kind === 'add') added.push(line.text)
621
+ index += 1
622
+ }
623
+ const count = Math.max(removed.length, added.length)
624
+ for (let pair = 0; pair < count; pair += 1) {
625
+ out.push({ before: removed[pair], after: added[pair], kind: 'pair' })
577
626
  }
578
627
  }
579
628
  return out
580
629
  }
581
630
 
582
631
  /** Read-only Markdown/plain-text comparison for SourceRaw and WikiCard. */
583
- export function VersionContentDiff({ data, loading = false, error }: {
632
+ export function VersionContentDiff({ data, loading = false, error, fullText = false }: {
584
633
  data?: VersionContentDiffData
585
634
  loading?: boolean
586
635
  error?: string
636
+ fullText?: boolean
587
637
  }): ReactNode {
588
638
  if (loading) return <div className="tita-versionDiff"><div className="tita-versionStepHint">正在读取历史正文…</div></div>
589
639
  if (error !== undefined) return <div className="tita-versionDiff"><div className="tita-kbHint">读取版本差异失败:{error}</div></div>
590
640
  if (data === undefined) return null
591
- const lines = contentDiffOf(data.before, data.after)
592
- const displayed = compactContentDiff(lines)
641
+ const lines = contentDiffOf(data.before, data.after, fullText)
642
+ const displayed = compactContentDiff(lines, fullText)
593
643
  const added = lines.filter(line => line.kind === 'add').length
594
644
  const removed = lines.filter(line => line.kind === 'remove').length
595
645
  const changed = Math.min(added, removed)
596
646
  const truncated = data.beforeTruncated === true || data.afterTruncated === true
597
647
  return (
598
- <div className="tita-versionDiff" aria-label="版本差异">
648
+ <div className={fullText ? 'tita-versionDiff tita-versionDiff-fullText' : 'tita-versionDiff'} aria-label="版本差异">
599
649
  <div className="tita-versionDiffBar">
600
650
  <p className="tita-versionDiffHead">
601
651
  {added === 0 && removed === 0 ? '正文没有差异' : '正文差异'}
@@ -605,14 +655,31 @@ export function VersionContentDiff({ data, loading = false, error }: {
605
655
  </p>
606
656
  </div>
607
657
  {truncated && <p className="tita-kbHint">只比较当前读取范围;正文过长,末尾内容未参与比较。</p>}
608
- {lines.length > 0 && (added > 0 || removed > 0) && (
609
- <div className="tita-versionDiffHunk">
610
- <div className="tita-versionDiffHunkHead">{data.beforeLabel} → {data.afterLabel}</div>
611
- <pre className="tita-versionDiffLines">
612
- {displayed.map((line, index) => line.kind === 'skip'
613
- ? <span className="tita-versionDiffSkip" key={`skip-${index}`}>⋯ {line.count} 行未变化</span>
614
- : <span className={`tita-versionDiffLine tita-versionDiffLine-${line.kind}`} key={`${line.kind}-${index}`}>{`${line.kind === 'add' ? '+' : line.kind === 'remove' ? '-' : ' '} ${line.text}`}</span>)}
615
- </pre>
658
+ {lines.length > 0 && (fullText || added > 0 || removed > 0) && (
659
+ <div className="tita-versionDiffTextSplit">
660
+ <div className="tita-versionDiffTextSplitHead">
661
+ <span>历史版本 · {data.beforeLabel}</span>
662
+ <span>当前版本 · {data.afterLabel}</span>
663
+ </div>
664
+ {displayed.map((line, index) => line.kind === 'skip'
665
+ ? <div className="tita-versionDiffTextSkip" key={`skip-${index}`}>⋯ {line.count} 行未变化</div>
666
+ : (() => {
667
+ const same = line.before !== undefined && line.after !== undefined && line.before === line.after
668
+ const beforeClass = line.before === undefined
669
+ ? 'tita-versionDiffTextCell tita-versionDiffTextCell-empty'
670
+ : same
671
+ ? 'tita-versionDiffTextCell tita-versionDiffTextCell-same'
672
+ : 'tita-versionDiffTextCell tita-versionDiffTextCell-before'
673
+ const afterClass = line.after === undefined
674
+ ? 'tita-versionDiffTextCell tita-versionDiffTextCell-empty'
675
+ : same
676
+ ? 'tita-versionDiffTextCell tita-versionDiffTextCell-same'
677
+ : 'tita-versionDiffTextCell tita-versionDiffTextCell-after'
678
+ return <div className="tita-versionDiffTextSplitRow" key={`pair-${index}`}>
679
+ <span className={beforeClass}>{line.before === undefined ? '' : `${same ? ' ' : '-'} ${line.before}`}</span>
680
+ <span className={afterClass}>{line.after === undefined ? '' : `${same ? ' ' : '+'} ${line.after}`}</span>
681
+ </div>
682
+ })())}
616
683
  </div>
617
684
  )}
618
685
  </div>
@@ -9,10 +9,11 @@
9
9
  * `chunk_index` / `score` 一起给出 —— 只报「命中 N 条」看不出召回质量。
10
10
  */
11
11
 
12
- import { Fragment, useEffect, useState, type ReactNode } from 'react'
12
+ import { Fragment, useEffect, useRef, useState, type ReactNode } from 'react'
13
13
  import { ArrowRight, ArrowUpDown, BrainCircuit, CircleHelp, GitBranch, Layers3, Play, Search, Shapes } from '@tita-agent/ui/icons'
14
- import { Button, Card, Input, SelectMenu, cn } from '@tita-agent/ui/components'
14
+ import { Button, Card, Input, cn } from '@tita-agent/ui/components'
15
15
  import { ExpansionGraph } from './ExpansionGraph.tsx'
16
+ import { requestJson } from './request.ts'
16
17
  import { styles } from './styles.ts'
17
18
 
18
19
  /** 一条命中的 wikiNote。`match` 是**真正贡献了这次命中**的路线。 */
@@ -96,7 +97,7 @@ export interface QueryAnalysisView {
96
97
  unresolved: QueryAxisKind[]
97
98
  }
98
99
 
99
- /** 召回链路的三个步骤:三路候选 → Section 重排 → 完整 WikiCard 交付。 */
100
+ /** 召回链路的三个步骤:四路候选 → Section 重排 → 完整 WikiCard 交付。 */
100
101
  export type RecallStep = 'recall' | 'rerank' | 'deliver'
101
102
 
102
103
  export interface RecallResultView {
@@ -108,7 +109,7 @@ export interface RecallResultView {
108
109
  /** 融合与重排作用的候选池深度(0031)。它与 `recallTopK` 是两个数。 */
109
110
  candidatePool?: number
110
111
  /**
111
- * 三路候选**去重后**的候选条数。
112
+ * 四路候选**去重后**的候选条数。
112
113
  *
113
114
  * 面板上这三个数必须各归各位:`candidatePool` 是每一路的超采深度(50),
114
115
  * `fusedCount` 是融合后的池子(实测「投资」那次是 17),`hits.length` 是重排之后
@@ -140,6 +141,7 @@ export interface RecallResultView {
140
141
  /** 面板账本(0050):每一路的全部候选 + 重排的全部候选(含被丢弃的)。 */
141
142
  ledger?: {
142
143
  coordinate: LedgerRowView[]
144
+ relation?: LedgerRowView[]
143
145
  fts: LedgerRowView[]
144
146
  vector: LedgerRowView[]
145
147
  rerank: Array<LedgerRowView & { fused: number; logit?: number; rerankScore?: number; dropped: boolean }>
@@ -155,7 +157,7 @@ export interface RecallResultView {
155
157
  * 只给「这次测试很慢」的感受,人没法判断该不该管它。
156
158
  */
157
159
  timings?: {
158
- ftsMs: number; coordinateMs: number; vectorMs: number; embedMs?: number; scanMs?: number
160
+ ftsMs: number; coordinateMs: number; relationMs?: number; vectorMs: number; embedMs?: number; scanMs?: number
159
161
  graphMs: number; fuseMs: number; rerankMs?: number; assembleMs: number; totalMs: number
160
162
  }
161
163
  /** 命中几张卡 + 1 跳扩展几张(0050)。 */
@@ -167,22 +169,6 @@ export interface RecallResultView {
167
169
  indexVersion?: number
168
170
  }
169
171
 
170
- interface Envelope<T> { ok?: boolean; data?: T; error?: { code?: string; message?: string; suggestion?: string } | null }
171
-
172
- async function requestJson<T>(url: string): Promise<Envelope<T>> {
173
- const response = await fetch(url)
174
- const text = await response.text()
175
- if (!text.trim()) throw new Error(`服务端返回空响应(HTTP ${response.status})`)
176
- let payload: Envelope<T>
177
- try { payload = JSON.parse(text) as Envelope<T> } catch { throw new Error(`服务端返回了无效响应(HTTP ${response.status})`) }
178
- const error = payload.error
179
- if (!response.ok || payload.ok === false) {
180
- const detail = [error?.code, error?.message].filter(Boolean).join(' · ') || `请求失败(${response.status})`
181
- throw new Error(error?.suggestion ? `${detail} — ${error.suggestion}` : detail)
182
- }
183
- return payload
184
- }
185
-
186
172
  /**
187
173
  * 账本列表(0050):**每一路的全部候选**,没过阈值的也在(虚线 + 淡色 + 一句原因)。
188
174
  *
@@ -316,43 +302,22 @@ export function DeliveryView({ wiki, onOpenWiki }: { wiki: WikiCardView[]; onOpe
316
302
  }
317
303
 
318
304
  /**
319
- * 召回是**三路**(Coordinate + 关键词 BM25 + 向量)加一个后置图谱 boost(0031)。
305
+ * 召回是四路(Coordinate + 对象关系 + 关键词 BM25 + 向量)加一个后置图谱 boost。
320
306
  *
321
307
  * 图谱不再是独立一路:它不能制造候选,只给已经在候选池里的条目加权。所以这一行显示的是
322
308
  * 「加权了几条」,而不是「跑没跑」—— 后者对它已经没有意义。
323
309
  */
324
310
  export const RECALL_ROUTES: Array<{ id: string; label: string; hint: string }> = [
325
311
  { id: 'coordinate', label: 'Coordinate', hint: 'Object × Action × Scene 坐标命中后展开为 Section' },
312
+ { id: 'relation', label: '关系', hint: '从命中的对象沿一跳 related_to 关系召回相关对象卡片' },
326
313
  { id: 'fts', label: '关键词', hint: 'FTS5 + BM25(中文按双字词切分)' },
327
314
  { id: 'vector', label: '向量', hint: 'WeMM 向量近邻(binary 预筛 + int8 精算余弦)' },
328
315
  ]
329
316
 
330
- /**
331
- * 路线选项保留 FTS / Vector 两个调试开关;默认 Hybrid 会额外启用 Coordinate 路。
332
- *
333
- * 图谱 boost 不是一条路(它不能制造候选,只给已在池里的条目加权),所以它不该出现在
334
- * 「选哪一路」的下拉里 —— 上一版那两条「融合 + 图谱 boost」「同融合」正是这个错:
335
- * 把加权当成了路线。融合是候选路的**组合方式**,写在选项文字里,不是一个独立选项。
336
- */
337
- export const RECALL_MODES: Array<{ id: string; label: string }> = [
338
- { id: 'hybrid', label: 'Coordinate + 关键词 + 向量(融合,默认)' },
339
- { id: 'fts', label: '仅关键词' },
340
- { id: 'vector', label: '仅向量' },
341
- ]
342
-
343
- /**
344
- * 参数摘要里用的短名字。
345
- *
346
- * `RECALL_MODES` 的 label 是给下拉用的**整句**(「Coordinate + 关键词 + 向量(融合,默认)」),
347
- * 直接塞进那行摘要会把行撑爆 —— 摘要是拿来扫的,不是拿来读的。两处各留一份是对的:
348
- * 下拉里要能说清「哪个是默认」,摘要里要短。
349
- */
350
- const MODE_SHORT: Record<string, string> = { hybrid: 'Coordinate + 关键词 + 向量', fts: '仅关键词', vector: '仅向量' }
351
-
352
317
  const HIT_ROUTES: Record<string, string> = {
353
- coordinate: styles.hitRouteHybrid, fts: styles.hitRouteFts, vector: styles.hitRouteVector, graph: styles.hitRouteGraph, hybrid: styles.hitRouteHybrid,
318
+ coordinate: styles.hitRouteHybrid, relation: styles.hitRouteGraph, fts: styles.hitRouteFts, vector: styles.hitRouteVector, graph: styles.hitRouteGraph, hybrid: styles.hitRouteHybrid,
354
319
  }
355
- const HIT_ROUTE_LABEL: Record<string, string> = { coordinate: 'Coordinate', fts: '关键词', vector: '向量', hybrid: '多路', graph: '图谱加权' }
320
+ const HIT_ROUTE_LABEL: Record<string, string> = { coordinate: 'Coordinate', relation: '关系扩展', fts: '关键词', vector: '向量', hybrid: '多路', graph: '图谱加权' }
356
321
 
357
322
  /** 去掉 markdown 脚手架,让命中读起来是一段正文而不是源码。 */
358
323
  export function snippetOf(content: string, limit = 200): string {
@@ -436,17 +401,16 @@ export interface RecallPanelProps {
436
401
  /** `recall` 看命中切片,`rerank` 看进入上下文的 topN,`runner` 是评测里的紧凑版。 */
437
402
  /**
438
403
  * `recall` = 知识库 → **召回重排**那一栏(0033 起召回与重排合成一栏,一次运行
439
- * 从上到下给出三路命中 → 融合排序 → 精排丢了多少 → 进上下文的 topN)。
404
+ * 从上到下给出四路命中 → 融合排序 → 精排丢了多少 → 进上下文的 topN)。
440
405
  * `runner` = 评测里的紧凑版单问题检查。
441
406
  */
442
407
  variant?: 'recall' | 'runner'
443
- defaults?: { mode?: string; topK?: number; topN?: number }
408
+ defaults?: { topK?: number; topN?: number }
444
409
  /** 设置写完或知识库切换时,用它把面板重置回初始值。 */
445
410
  resetKey?: string
446
411
  /**
447
- * 卡头上的「参数」入口(0033)。面板**不认识设置**:它只在自己的卡头上腾一个位置,
448
- * 具体挂哪一块由调用方决定(召回栏挂召回设置、重排栏挂重排设置、评测栏不挂)。
449
- * 参数住在设置抽屉里,这里只是那扇离现场更近的门。
412
+ * 卡头上的设置入口。面板**不认识设置**,只留一个位置让调用方挂最近的入口;
413
+ * 召回、重排与上下文共用同一组设置,评测栏不挂入口。
450
414
  */
451
415
  settingsChip?: ReactNode
452
416
  /** 点某张卡片时打开它的预览抽屉。不给就是纯文本(面板不自己造一个抽屉出来)。 */
@@ -462,18 +426,32 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
462
426
  * 提示语改成直说这一栏要什么:**要测试的内容**。
463
427
  */
464
428
  const [query, setQuery] = useState('')
465
- const [mode, setMode] = useState(defaults?.mode ?? 'hybrid')
466
429
  const [topK, setTopK] = useState(defaults?.topK ?? 10)
467
430
  const [topN, setTopN] = useState(defaults?.topN ?? 5)
431
+ /**
432
+ * 两个参数框里**正在被敲的那串字**。
433
+ *
434
+ * 输入框原来直接吃数字、每次按键都夹一次(`Math.max(1, …)`),于是「全选删掉重打」
435
+ * 这一步永远做不到:清空的一瞬间就被改写回 1,人只能先选中再输入。**空串是一种
436
+ * 合法的中间态**,所以草稿留成字符串,只在失焦时夹一次。
437
+ */
438
+ const [topKDraft, setTopKDraft] = useState(String(defaults?.topK ?? 10))
439
+ const [topNDraft, setTopNDraft] = useState(String(defaults?.topN ?? 5))
468
440
  const [busy, setBusy] = useState(false)
469
441
  const [error, setError] = useState<string | null>(null)
470
442
  const [result, setResult] = useState<RecallResultView | null>(null)
443
+ /**
444
+ * 本次运行的票号。连按两次「测试」或连敲两次回车会发两个请求,先发的可能后回 ——
445
+ * 那一次的结果会把新一次盖掉,而屏幕上的参数是新一次的。每个 `setState` 前比票号,
446
+ * 过期的整段丢掉(与卡片列表 `eventsRequestRef` 同一条纪律)。
447
+ */
448
+ const runRef = useRef(0)
471
449
  /** 三个 tab:召回 / 重排 / 交付 —— 一条链路的三段,各自回答一个问题。 */
472
450
  const [tab, setTab] = useState<RecallStep>('recall')
473
451
  /** 召回 tab 里正在看哪一路(`all` = 看融合后的命中)。 */
474
- const [routeView, setRouteView] = useState<'all' | 'coordinate' | 'fts' | 'vector'>('all')
452
+ const [routeView, setRouteView] = useState<'all' | 'coordinate' | 'relation' | 'fts' | 'vector'>('all')
475
453
  /**
476
- * 三个参数旋钮默认**收起来**。
454
+ * 两个片段数量参数默认**收起来**。
477
455
  *
478
456
  * 它们此前常驻一整条横带,而那条横带压在步骤轨与命中列表上面 —— 列表之前一共五条
479
457
  * 横带,一屏里真正要读的东西被推下去。参数和命中列表不是同一件事,不该有同等高度:
@@ -491,20 +469,36 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
491
469
 
492
470
  // 知识库换了或设置存了:面板上的默认值跟着走,否则显示的还是上一个库的参数。
493
471
  // 用 effect 而不是渲染期 setState:这条同步是「外部输入变了」而不是渲染推导。
494
- const modeDefault = defaults?.mode
495
472
  const topKDefault = defaults?.topK
496
473
  const topNDefault = defaults?.topN
497
474
  useEffect(() => {
498
475
  if (!visible) return
499
- if (modeDefault) setMode(modeDefault)
500
- if (topKDefault) setTopK(topKDefault)
501
- if (topNDefault) setTopN(topNDefault)
476
+ if (topKDefault) { setTopK(topKDefault); setTopKDraft(String(topKDefault)) }
477
+ if (topNDefault) { setTopN(topNDefault); setTopNDraft(String(topNDefault)) }
502
478
  setResult(null)
503
- }, [visible, resetKey, modeDefault, topKDefault, topNDefault])
479
+ }, [visible, resetKey, topKDefault, topNDefault])
480
+
481
+ /** 夹一次并写回草稿。数字为空或非数就退回当前值 —— 不合法的一律不落地。 */
482
+ const commitTopK = (): void => {
483
+ const next = Math.max(1, Math.min(50, Number(topKDraft) || topK))
484
+ setTopK(next); setTopKDraft(String(next))
485
+ // topN 的上限跟着 topK:改小 topK 之后原来那个 topN 可能已经越界。
486
+ setTopN(current => {
487
+ const capped = Math.min(current, next)
488
+ setTopNDraft(String(capped))
489
+ return capped
490
+ })
491
+ }
492
+ const commitTopN = (): void => {
493
+ const next = Math.max(1, Math.min(topK, Number(topNDraft) || topN))
494
+ setTopN(next); setTopNDraft(String(next))
495
+ }
504
496
 
505
497
  const run = async (): Promise<void> => {
506
498
  const trimmed = query.trim()
507
499
  if (!trimmed) { setError('请输入要测试的召回问题。'); return }
500
+ const ticket = runRef.current + 1
501
+ runRef.current = ticket
508
502
  setBusy(true); setError(null); setDroppedOpen(null)
509
503
  try {
510
504
  const boundedTopN = Math.min(topN, topK)
@@ -516,7 +510,7 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
516
510
  * 点下去却是「这一路没有候选」—— 计数来自 `diagnostics`,明细来自 `ledger`,
517
511
  * 两者来源不同,缺一半就自相矛盾。
518
512
  */
519
- const params = new URLSearchParams({ query: trimmed, top_k: String(topK), top_n: String(boundedTopN), mode, detail: 'true' })
513
+ const params = new URLSearchParams({ query: trimmed, top_k: String(topK), top_n: String(boundedTopN), detail: 'true' })
520
514
  if (kbId) params.set('kb', kbId)
521
515
  const payload = await requestJson<{
522
516
  hits?: Array<Partial<RecallHitView> & { content?: string }>
@@ -551,6 +545,7 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
551
545
  indexVersion?: number
552
546
  ledger?: {
553
547
  coordinate?: LedgerRowView[]
548
+ relation?: LedgerRowView[]
554
549
  fts?: LedgerRowView[]
555
550
  vector?: LedgerRowView[]
556
551
  rerank?: Array<LedgerRowView & { fused?: number; logit?: number; rerankScore?: number; dropped?: boolean }>
@@ -611,6 +606,8 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
611
606
  ...(expansion && item.via ? { via: `${item.via.title}(${item.via.relation})` } : {}),
612
607
  }
613
608
  })
609
+ // 票号过期就整段丢掉:这份响应属于上一次点击,落地只会把新一次的结果盖掉。
610
+ if (runRef.current !== ticket) return
614
611
  setResult({
615
612
  count: hits.length,
616
613
  backend: data.backend ?? 'fts',
@@ -644,6 +641,7 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
644
641
  // 而后者只可能出现在老服务端上。
645
642
  ledger: {
646
643
  coordinate: data.ledger?.coordinate ?? [],
644
+ relation: data.ledger?.relation ?? [],
647
645
  fts: data.ledger?.fts ?? [],
648
646
  vector: data.ledger?.vector ?? [],
649
647
  rerank: (data.ledger?.rerank ?? []).map(row => ({ ...row, fused: Number(row.fused ?? row.score ?? 0), dropped: row.dropped === true })),
@@ -673,15 +671,18 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
673
671
  ...(data.indexVersion === undefined ? {} : { indexVersion: data.indexVersion }),
674
672
  })
675
673
  } catch (cause) {
674
+ if (runRef.current !== ticket) return
676
675
  setError(cause instanceof Error ? cause.message : String(cause))
677
676
  } finally {
678
- setBusy(false)
677
+ // 只有还是当前这一次才关掉「测试中」:过期的那次把 busy 关掉,会让正在跑的那次
678
+ // 看起来已经结束(按钮重新可点,于是又多开一次请求)。
679
+ if (runRef.current === ticket) setBusy(false)
679
680
  }
680
681
  }
681
682
 
682
683
  const title = variant === 'runner' ? '单问题召回检查' : '召回 · 重排'
683
684
  // 这一栏没有「一句话说明」:那条步骤轨就是说明。每一步带自己的计数,
684
- // 「三路各自命中了什么 → Section 重排 → 返回哪些完整 WikiCard」
685
+ // 「四路各自命中了什么 → Section 重排 → 返回哪些完整 WikiCard」
685
686
  // 这句话由轨上的真实数字说,比一句固定文案准,也不会随实现变化而过期。
686
687
 
687
688
  /**
@@ -704,9 +705,9 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
704
705
  const steps: Array<{ id: RecallStep; icon: typeof Search; label: string; verb: string; value: string; note: string; cost: string; costTitle: string }> = [
705
706
  {
706
707
  id: 'recall', icon: Search, label: '召回', verb: '找',
707
- value: result ? `坐标 ${keptOf('coordinate') ?? 0} · 关键词 ${keptOf('fts') ?? 0} · 向量 ${keptOf('vector') ?? 0}` : '未运行',
708
- note: '三路各自进入候选池的条数',
709
- cost: timings ? ms(timings.coordinateMs + timings.ftsMs + timings.vectorMs) : '',
708
+ value: result ? `坐标 ${keptOf('coordinate') ?? 0} · 关系 ${keptOf('relation') ?? 0} · 关键词 ${keptOf('fts') ?? 0} · 向量 ${keptOf('vector') ?? 0}` : '未运行',
709
+ note: '四路各自进入候选池的条数',
710
+ cost: timings ? ms(timings.coordinateMs + (timings.relationMs ?? 0) + timings.ftsMs + timings.vectorMs) : '',
710
711
  costTitle: timings
711
712
  ? `关键词 ${ms(timings.ftsMs)}${timings.embedMs === undefined ? '' : ` · 查询编码 ${ms(timings.embedMs)}${timings.scanMs === undefined ? '' : ` · 索引扫描 ${ms(timings.scanMs)}`}`}`
712
713
  : '',
@@ -753,10 +754,9 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
753
754
  * 但界面上不写出来,人就只能靠记忆判断哪个数真的生效了。所以不一致时明说,
754
755
  * 并在 `title` 里给出设置里的那一组值。
755
756
  */
756
- const overridden = (defaults?.mode !== undefined && mode !== defaults.mode)
757
- || (defaults?.topK !== undefined && topK !== defaults.topK)
757
+ const overridden = (defaults?.topK !== undefined && topK !== defaults.topK)
758
758
  || (defaults?.topN !== undefined && topN !== defaults.topN)
759
- const defaultHint = `设置里的默认:${MODE_SHORT[defaults?.mode ?? ''] ?? defaults?.mode ?? '未读到'} · 召回 topK ${defaults?.topK ?? '—'} · 上下文 topN ${defaults?.topN ?? '—'}`
759
+ const defaultHint = `设置里的默认:召回 topK ${defaults?.topK ?? '—'} · 上下文 topN ${defaults?.topN ?? '—'}`
760
760
 
761
761
  /*
762
762
  * 这一栏的横带节奏交给**容器**统一给(`.tita-recallPanel` 是一个 gap 14px 的网格),
@@ -781,20 +781,23 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
781
781
  <div className={styles.inputRow}>
782
782
  <Input value={query} placeholder="请输入要测试的内容"
783
783
  onChange={event => setQuery(event.target.value)}
784
- onKeyDown={event => { if (event.key === 'Enter') void run() }} />
784
+ // 回车与按钮走**同一条判断**:按钮上写着 `disabled={busy}`,而回车原来没有这一条,
785
+ // 于是「测试中连敲回车」会同时开出好几个请求。空串也不打 —— 那时它在报一个
786
+ // 人还没写完的输入。
787
+ onKeyDown={event => { if (event.key === 'Enter' && !busy && query.trim() !== '') void run() }} />
785
788
  <Button variant="default" onClick={() => void run()} disabled={busy}>
786
789
  {variant === 'runner' && <Play size={13} />}{busy ? '测试中…' : variant === 'runner' ? '运行检查' : '测试'}
787
790
  </Button>
788
791
  </div>
789
792
  {result?.queryAnalysis && <QueryAnalysisSummary analysis={result.queryAnalysis} />}
790
793
  {/* 参数不再常驻一条横带(0050 第三版):平时只留**一行只读摘要**,值与设置里的默认
791
- 都在这一行上说清;要改才点开三个旋钮。
792
- 为什么不是整块搬进设置抽屉:这一栏里的三个值是**这一次运行**的参数,设置抽屉里那
794
+ 都在这一行上说清;要改才展开片段数量。
795
+ 为什么不是整块搬进设置抽屉:这一栏里的两个值是**这一次运行**的参数,设置抽屉里那
793
796
  三个是**默认值** —— 搬过去就等于把「临时改一次」变成「改掉全方案的默认」。
794
797
  两者不一致时这一行会写明,这正是 0050 那版注释里记着的坑
795
798
  (「设置里 topK=20、召回面板还按 10 跑」)。 */}
796
799
  <p className={styles.recallParamLine}>
797
- <b>{MODE_SHORT[mode] ?? mode}</b>
800
+ <b>多路径召回</b>
798
801
  <span>召回 topK <b>{topK}</b></span>
799
802
  <span>上下文 topN <b>{Math.min(topN, topK)}</b></span>
800
803
  {overridden && <span className={styles.recallParamOver} title={defaultHint}>非默认</span>}
@@ -802,18 +805,17 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
802
805
  onClick={() => setParamOpen(open => !open)}>{paramOpen ? '收起参数' : '改参数'}</button>
803
806
  </p>
804
807
  {paramOpen && <div className={styles.recallControls}>
805
- <div className={styles.recallControl}>
806
- <span>路线</span>
807
- <SelectMenu className="w-auto" value={mode} disabled={busy} onValueChange={setMode} aria-label="召回路线"
808
- options={RECALL_MODES.map(item => ({ value: item.id, label: item.label }))} />
809
- </div>
810
808
  <label>召回 topK
811
- <Input type="number" min={1} max={50} value={topK} disabled={busy}
812
- onChange={event => setTopK(Math.max(1, Math.min(50, Number(event.target.value) || 1)))} />
809
+ <Input type="number" min={1} max={50} value={topKDraft} disabled={busy}
810
+ onChange={event => setTopKDraft(event.target.value)}
811
+ onBlur={commitTopK}
812
+ onKeyDown={event => { if (event.key === 'Enter') commitTopK() }} />
813
813
  </label>
814
814
  <label>上下文 topN
815
- <Input type="number" min={1} max={topK} value={Math.min(topN, topK)} disabled={busy}
816
- onChange={event => setTopN(Math.max(1, Math.min(topK, Number(event.target.value) || 1)))} />
815
+ <Input type="number" min={1} max={topK} value={topNDraft} disabled={busy}
816
+ onChange={event => setTopNDraft(event.target.value)}
817
+ onBlur={commitTopN}
818
+ onKeyDown={event => { if (event.key === 'Enter') commitTopN() }} />
817
819
  </label>
818
820
  </div>}
819
821
  {error && <p className={cn(styles.kbNotice, styles.kbNoticeError)} role="alert">{error}</p>}
@@ -866,7 +868,7 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
866
868
  但这枚 chip 不能删 —— 它是从单路视角回到合并视角的唯一入口。
867
869
  */}
868
870
  <button type="button" className={cn(styles.filterChip, routeView === 'all' && styles.filterChipOn)}
869
- title={`全部:三路去重融合后的命中列表(这次 ${fusedCount} 条候选,重排留下 ${result?.hits.length ?? 0} 条)`}
871
+ title={`全部:四路去重融合后的命中列表(这次 ${fusedCount} 条候选,重排留下 ${result?.hits.length ?? 0} 条)`}
870
872
  onClick={() => setRouteView('all')}>
871
873
  全部
872
874
  </button>
@@ -874,7 +876,8 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
874
876
  const detail = routesDetail.find(item => item.route === route.id)
875
877
  const rows = route.id === 'coordinate'
876
878
  ? result?.ledger?.coordinate ?? []
877
- : route.id === 'fts' ? result?.ledger?.fts ?? [] : result?.ledger?.vector ?? []
879
+ : route.id === 'relation' ? result?.ledger?.relation ?? []
880
+ : route.id === 'fts' ? result?.ledger?.fts ?? [] : result?.ledger?.vector ?? []
878
881
  const kept = detail?.kept ?? rows.filter(row => row.kept).length
879
882
  const total = detail?.candidates ?? rows.length
880
883
  // 降级与「这一路没有候选」是两件事:前者要能一眼看出来(0031 的诚实边界)。
@@ -882,7 +885,7 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
882
885
  return <button key={route.id} type="button"
883
886
  title={degraded && detail?.reason ? `${route.hint} —— ${detail.reason}` : route.hint}
884
887
  className={cn(styles.filterChip, routeView === route.id && styles.filterChipOn, degraded && styles.filterChipWarn)}
885
- onClick={() => setRouteView(route.id as 'coordinate' | 'fts' | 'vector')}>
888
+ onClick={() => setRouteView(route.id as 'coordinate' | 'relation' | 'fts' | 'vector')}>
886
889
  {route.label} {degraded ? '降级' : `${kept}/${total}`}
887
890
  </button>
888
891
  })}
@@ -921,7 +924,8 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
921
924
  : <LedgerList
922
925
  rows={routeView === 'coordinate'
923
926
  ? result.ledger?.coordinate ?? []
924
- : routeView === 'fts' ? result.ledger?.fts ?? [] : result.ledger?.vector ?? []}
927
+ : routeView === 'relation' ? result.ledger?.relation ?? []
928
+ : routeView === 'fts' ? result.ledger?.fts ?? [] : result.ledger?.vector ?? []}
925
929
  scoreLabel={row => row.score.toFixed(3)}
926
930
  droppedLabel="低于阈值"
927
931
  />}