dsh-data-domain-knowledge 0.1.0-alpha.13 → 0.1.0-alpha.15
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/README.md +89 -56
- package/cordis.patch.yml +36 -129
- package/eval/benchmark-extraction-format.mjs +20 -0
- package/lib/client.js +3262 -4142
- package/lib/doc-page.js +1 -1
- package/lib/pages.js +1147 -364
- package/lib/types/axis-registry.d.ts +8 -1
- package/lib/types/axis-registry.d.ts.map +1 -1
- package/lib/types/axis-registry.js +62 -7
- package/lib/types/axis-registry.js.map +1 -1
- package/lib/types/convert/host.d.ts.map +1 -1
- package/lib/types/convert/host.js +3 -0
- package/lib/types/convert/host.js.map +1 -1
- package/lib/types/convert/ingest.d.ts +9 -0
- package/lib/types/convert/ingest.d.ts.map +1 -1
- package/lib/types/convert/ingest.js +68 -9
- package/lib/types/convert/ingest.js.map +1 -1
- package/lib/types/diagnosis/cards.d.ts +7 -3
- package/lib/types/diagnosis/cards.d.ts.map +1 -1
- package/lib/types/diagnosis/cards.js +13 -8
- package/lib/types/diagnosis/cards.js.map +1 -1
- package/lib/types/diagnosis/run.d.ts +4 -13
- package/lib/types/diagnosis/run.d.ts.map +1 -1
- package/lib/types/diagnosis/run.js +26 -16
- package/lib/types/diagnosis/run.js.map +1 -1
- package/lib/types/diagnosis/schema.js +2 -2
- package/lib/types/diagnosis/schema.js.map +1 -1
- package/lib/types/diagnosis/seed-source.d.ts +1 -1
- package/lib/types/diagnosis/seed-source.d.ts.map +1 -1
- package/lib/types/diagnosis/seed-source.js +5 -4
- package/lib/types/diagnosis/seed-source.js.map +1 -1
- package/lib/types/diagnosis/seed.d.ts +4 -4
- package/lib/types/diagnosis/seed.d.ts.map +1 -1
- package/lib/types/diagnosis/seed.js +6 -8
- package/lib/types/diagnosis/seed.js.map +1 -1
- package/lib/types/diagnosis/store.d.ts +18 -17
- package/lib/types/diagnosis/store.d.ts.map +1 -1
- package/lib/types/diagnosis/store.js +94 -66
- package/lib/types/diagnosis/store.js.map +1 -1
- package/lib/types/diagnosis/types.d.ts +10 -16
- package/lib/types/diagnosis/types.d.ts.map +1 -1
- package/lib/types/diagnosis/types.js.map +1 -1
- package/lib/types/environment/dsh-python.d.ts +77 -0
- package/lib/types/environment/dsh-python.d.ts.map +1 -0
- package/lib/types/environment/dsh-python.js +139 -0
- package/lib/types/environment/dsh-python.js.map +1 -0
- package/lib/types/environment/install.d.ts +9 -1
- package/lib/types/environment/install.d.ts.map +1 -1
- package/lib/types/environment/install.js +149 -24
- package/lib/types/environment/install.js.map +1 -1
- package/lib/types/environment/model-catalog.d.ts +34 -2
- package/lib/types/environment/model-catalog.d.ts.map +1 -1
- package/lib/types/environment/model-catalog.js +40 -2
- package/lib/types/environment/model-catalog.js.map +1 -1
- package/lib/types/environment/service.d.ts +13 -3
- package/lib/types/environment/service.d.ts.map +1 -1
- package/lib/types/environment/service.js +127 -23
- package/lib/types/environment/service.js.map +1 -1
- package/lib/types/environment/types.d.ts +9 -0
- package/lib/types/environment/types.d.ts.map +1 -1
- package/lib/types/events.d.ts +2 -0
- package/lib/types/events.d.ts.map +1 -1
- package/lib/types/events.js +13 -0
- package/lib/types/events.js.map +1 -1
- package/lib/types/extract-contract.d.ts +1 -1
- package/lib/types/extract-contract.d.ts.map +1 -1
- package/lib/types/extract-contract.js +34 -31
- package/lib/types/extract-contract.js.map +1 -1
- package/lib/types/extract-model.d.ts +2 -0
- package/lib/types/extract-model.d.ts.map +1 -1
- package/lib/types/extract-model.js.map +1 -1
- package/lib/types/extract-table-contract.d.ts +3 -0
- package/lib/types/extract-table-contract.d.ts.map +1 -0
- package/lib/types/extract-table-contract.js +30 -0
- package/lib/types/extract-table-contract.js.map +1 -0
- package/lib/types/extract-table.d.ts +8 -0
- package/lib/types/extract-table.d.ts.map +1 -0
- package/lib/types/extract-table.js +151 -0
- package/lib/types/extract-table.js.map +1 -0
- package/lib/types/extraction-runs.d.ts +30 -6
- package/lib/types/extraction-runs.d.ts.map +1 -1
- package/lib/types/extraction-runs.js +69 -8
- package/lib/types/extraction-runs.js.map +1 -1
- package/lib/types/fs-atomic.d.ts +0 -7
- package/lib/types/fs-atomic.d.ts.map +1 -1
- package/lib/types/fs-atomic.js +17 -2
- package/lib/types/fs-atomic.js.map +1 -1
- package/lib/types/images.d.ts +1 -1
- package/lib/types/images.d.ts.map +1 -1
- package/lib/types/images.js +2 -2
- package/lib/types/images.js.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +232 -50
- package/lib/types/index.js.map +1 -1
- package/lib/types/intent/scope.d.ts.map +1 -1
- package/lib/types/intent/scope.js +1 -0
- package/lib/types/intent/scope.js.map +1 -1
- package/lib/types/intent/tools.d.ts +19 -0
- package/lib/types/intent/tools.d.ts.map +1 -1
- package/lib/types/intent/tools.js +37 -1
- package/lib/types/intent/tools.js.map +1 -1
- package/lib/types/knowledge-bases.d.ts.map +1 -1
- package/lib/types/knowledge-bases.js +5 -16
- package/lib/types/knowledge-bases.js.map +1 -1
- package/lib/types/knowledge.d.ts +3 -0
- package/lib/types/knowledge.d.ts.map +1 -1
- package/lib/types/knowledge.js +21 -5
- package/lib/types/knowledge.js.map +1 -1
- package/lib/types/materialize-snapshot.d.ts +49 -0
- package/lib/types/materialize-snapshot.d.ts.map +1 -0
- package/lib/types/materialize-snapshot.js +2 -0
- package/lib/types/materialize-snapshot.js.map +1 -0
- package/lib/types/model-text.d.ts +1 -1
- package/lib/types/model-text.d.ts.map +1 -1
- package/lib/types/model-text.js +5 -4
- package/lib/types/model-text.js.map +1 -1
- package/lib/types/oplog.d.ts.map +1 -1
- package/lib/types/oplog.js +7 -0
- package/lib/types/oplog.js.map +1 -1
- package/lib/types/pipeline/engine.d.ts +60 -0
- package/lib/types/pipeline/engine.d.ts.map +1 -0
- package/lib/types/pipeline/engine.js +338 -0
- package/lib/types/pipeline/engine.js.map +1 -0
- package/lib/types/pipeline/store.d.ts +73 -0
- package/lib/types/pipeline/store.d.ts.map +1 -0
- package/lib/types/pipeline/store.js +143 -0
- package/lib/types/pipeline/store.js.map +1 -0
- package/lib/types/pipeline/types.d.ts +101 -0
- package/lib/types/pipeline/types.d.ts.map +1 -0
- package/lib/types/pipeline/types.js +5 -0
- package/lib/types/pipeline/types.js.map +1 -0
- package/lib/types/present.d.ts +2 -0
- package/lib/types/present.d.ts.map +1 -1
- package/lib/types/present.js +9 -4
- package/lib/types/present.js.map +1 -1
- package/lib/types/retention.d.ts +39 -0
- package/lib/types/retention.d.ts.map +1 -0
- package/lib/types/retention.js +42 -0
- package/lib/types/retention.js.map +1 -0
- package/lib/types/runtime.d.ts +115 -5
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/runtime.js +862 -132
- package/lib/types/runtime.js.map +1 -1
- package/lib/types/source-evidence.d.ts +9 -1
- package/lib/types/source-evidence.d.ts.map +1 -1
- package/lib/types/source-evidence.js +13 -5
- package/lib/types/source-evidence.js.map +1 -1
- package/lib/types/store/axis-references.js +3 -3
- package/lib/types/store/axis-references.js.map +1 -1
- package/lib/types/store/derived.d.ts +1 -1
- package/lib/types/store/derived.d.ts.map +1 -1
- package/lib/types/store/derived.js +41 -7
- package/lib/types/store/derived.js.map +1 -1
- package/lib/types/store/knowledge-db.d.ts +18 -0
- package/lib/types/store/knowledge-db.d.ts.map +1 -1
- package/lib/types/store/knowledge-db.js +111 -6
- package/lib/types/store/knowledge-db.js.map +1 -1
- package/lib/types/store/migrate.js +1 -1
- package/lib/types/store/migrate.js.map +1 -1
- package/lib/types/trajectory-types.d.ts +172 -0
- package/lib/types/trajectory-types.d.ts.map +1 -0
- package/lib/types/trajectory-types.js +36 -0
- package/lib/types/trajectory-types.js.map +1 -0
- package/lib/types/types.d.ts +3 -4
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/types.js.map +1 -1
- package/lib/types/wiki-finalize.d.ts +16 -0
- package/lib/types/wiki-finalize.d.ts.map +1 -1
- package/lib/types/wiki-finalize.js +26 -0
- package/lib/types/wiki-finalize.js.map +1 -1
- package/lib/types/wiki-materialize.d.ts.map +1 -1
- package/lib/types/wiki-materialize.js +3 -15
- package/lib/types/wiki-materialize.js.map +1 -1
- package/lib/types/wiki-trial-types.d.ts +3 -0
- package/lib/types/wiki-trial-types.d.ts.map +1 -1
- package/lib/types/wiki-trial.d.ts +2 -0
- package/lib/types/wiki-trial.d.ts.map +1 -1
- package/lib/types/wiki-trial.js +31 -7
- package/lib/types/wiki-trial.js.map +1 -1
- package/package.json +6 -3
- package/preset/tita-data-agent/agent.cordis.yml +36 -129
- package/src/axis-registry.ts +57 -9
- package/src/client/AttributionEventGuide.tsx +10 -9
- package/src/client/CompileDialogs.tsx +10 -2
- package/src/client/EnvironmentSetup.tsx +76 -16
- package/src/client/ExpansionGraph.tsx +13 -7
- package/src/client/ExtractionTasksPage.tsx +543 -156
- package/src/client/ImportFilesDialog.tsx +2 -13
- package/src/client/IntentTab.tsx +3 -1
- package/src/client/KnowledgeBaseTab.tsx +139 -122
- package/src/client/KnowledgeLayerSection.tsx +19 -2
- package/src/client/KnowledgePanel.tsx +17 -3
- package/src/client/ListPager.tsx +15 -5
- package/src/client/MaterializeTrajectoryView.tsx +124 -0
- package/src/client/NodeSettings.tsx +90 -25
- package/src/client/NormalizeTrajectoryView.tsx +136 -0
- package/src/client/PipelineNotice.tsx +26 -0
- package/src/client/RecallPanel.tsx +55 -25
- package/src/client/TabGuide.tsx +4 -4
- package/src/client/TrialReportView.tsx +61 -16
- package/src/client/TypeDefinitionDrawer.tsx +33 -9
- package/src/client/WikiCardPreview.tsx +43 -10
- package/src/client/WikiFlowPanel.tsx +4 -4
- package/src/client/architecture.tsx +1 -1
- package/src/client/attribution/AttributionTab.tsx +37 -36
- package/src/client/attribution/CaseView.tsx +3 -1
- package/src/client/attribution/EventView.tsx +14 -4
- package/src/client/attribution/RunView.tsx +2 -2
- package/src/client/attribution/ViewSwitch.tsx +20 -5
- package/src/client/attribution/graph-options.ts +38 -31
- package/src/client/attribution/model.ts +16 -9
- package/src/client/graph/palette.ts +165 -0
- package/src/client/pages.css +224 -28
- package/src/client/pages.tsx +33 -11
- package/src/client/styles.css +61 -6
- package/src/client/wiki-trial.ts +30 -1
- package/src/convert/host.ts +3 -0
- package/src/convert/ingest.ts +73 -9
- package/src/diagnosis/cards.ts +13 -8
- package/src/diagnosis/run.ts +27 -17
- package/src/diagnosis/schema.ts +2 -2
- package/src/diagnosis/seed-source.ts +5 -4
- package/src/diagnosis/seed.ts +6 -8
- package/src/diagnosis/store.ts +99 -69
- package/src/diagnosis/types.ts +10 -16
- package/src/environment/dsh-python.ts +156 -0
- package/src/environment/install.ts +134 -19
- package/src/environment/model-catalog.ts +45 -3
- package/src/environment/service.ts +111 -23
- package/src/environment/types.ts +6 -0
- package/src/events.ts +12 -0
- package/src/extract-contract.ts +6 -3
- package/src/extract-model.ts +2 -0
- package/src/extract-table-contract.ts +29 -0
- package/src/extract-table.ts +126 -0
- package/src/extraction-runs.ts +70 -11
- package/src/fs-atomic.ts +19 -2
- package/src/images.ts +2 -2
- package/src/index.ts +207 -72
- package/src/intent/scope.ts +1 -0
- package/src/intent/tools.ts +38 -1
- package/src/knowledge-bases.ts +4 -12
- package/src/knowledge.ts +24 -4
- package/src/materialize-snapshot.ts +46 -0
- package/src/model-text.ts +5 -3
- package/src/oplog.ts +7 -0
- package/src/pipeline/engine.ts +312 -0
- package/src/pipeline/store.ts +147 -0
- package/src/pipeline/types.ts +92 -0
- package/src/present.ts +10 -4
- package/src/retention.ts +76 -0
- package/src/runtime.ts +853 -131
- package/src/source-evidence.ts +12 -5
- package/src/store/axis-references.ts +3 -3
- package/src/store/derived.ts +40 -7
- package/src/store/knowledge-db.ts +111 -5
- package/src/store/migrate.ts +1 -1
- package/src/trajectory-types.ts +142 -0
- package/src/types.ts +3 -4
- package/src/wiki-finalize.ts +28 -0
- package/src/wiki-materialize.ts +3 -14
- package/src/wiki-trial-types.ts +3 -0
- package/src/wiki-trial.ts +26 -7
- package/src/client/convert/ConvertPanel.tsx +0 -583
- package/src/client/convert/styles.css +0 -138
- package/src/client/convert/styles.ts +0 -49
|
@@ -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
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` 是**真正贡献了这次命中**的路线。 */
|
|
@@ -168,22 +169,6 @@ export interface RecallResultView {
|
|
|
168
169
|
indexVersion?: number
|
|
169
170
|
}
|
|
170
171
|
|
|
171
|
-
interface Envelope<T> { ok?: boolean; data?: T; error?: { code?: string; message?: string; suggestion?: string } | null }
|
|
172
|
-
|
|
173
|
-
async function requestJson<T>(url: string): Promise<Envelope<T>> {
|
|
174
|
-
const response = await fetch(url)
|
|
175
|
-
const text = await response.text()
|
|
176
|
-
if (!text.trim()) throw new Error(`服务端返回空响应(HTTP ${response.status})`)
|
|
177
|
-
let payload: Envelope<T>
|
|
178
|
-
try { payload = JSON.parse(text) as Envelope<T> } catch { throw new Error(`服务端返回了无效响应(HTTP ${response.status})`) }
|
|
179
|
-
const error = payload.error
|
|
180
|
-
if (!response.ok || payload.ok === false) {
|
|
181
|
-
const detail = [error?.code, error?.message].filter(Boolean).join(' · ') || `请求失败(${response.status})`
|
|
182
|
-
throw new Error(error?.suggestion ? `${detail} — ${error.suggestion}` : detail)
|
|
183
|
-
}
|
|
184
|
-
return payload
|
|
185
|
-
}
|
|
186
|
-
|
|
187
172
|
/**
|
|
188
173
|
* 账本列表(0050):**每一路的全部候选**,没过阈值的也在(虚线 + 淡色 + 一句原因)。
|
|
189
174
|
*
|
|
@@ -443,9 +428,24 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
|
|
|
443
428
|
const [query, setQuery] = useState('')
|
|
444
429
|
const [topK, setTopK] = useState(defaults?.topK ?? 10)
|
|
445
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))
|
|
446
440
|
const [busy, setBusy] = useState(false)
|
|
447
441
|
const [error, setError] = useState<string | null>(null)
|
|
448
442
|
const [result, setResult] = useState<RecallResultView | null>(null)
|
|
443
|
+
/**
|
|
444
|
+
* 本次运行的票号。连按两次「测试」或连敲两次回车会发两个请求,先发的可能后回 ——
|
|
445
|
+
* 那一次的结果会把新一次盖掉,而屏幕上的参数是新一次的。每个 `setState` 前比票号,
|
|
446
|
+
* 过期的整段丢掉(与卡片列表 `eventsRequestRef` 同一条纪律)。
|
|
447
|
+
*/
|
|
448
|
+
const runRef = useRef(0)
|
|
449
449
|
/** 三个 tab:召回 / 重排 / 交付 —— 一条链路的三段,各自回答一个问题。 */
|
|
450
450
|
const [tab, setTab] = useState<RecallStep>('recall')
|
|
451
451
|
/** 召回 tab 里正在看哪一路(`all` = 看融合后的命中)。 */
|
|
@@ -473,14 +473,32 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
|
|
|
473
473
|
const topNDefault = defaults?.topN
|
|
474
474
|
useEffect(() => {
|
|
475
475
|
if (!visible) return
|
|
476
|
-
if (topKDefault) setTopK(topKDefault)
|
|
477
|
-
if (topNDefault) setTopN(topNDefault)
|
|
476
|
+
if (topKDefault) { setTopK(topKDefault); setTopKDraft(String(topKDefault)) }
|
|
477
|
+
if (topNDefault) { setTopN(topNDefault); setTopNDraft(String(topNDefault)) }
|
|
478
478
|
setResult(null)
|
|
479
479
|
}, [visible, resetKey, topKDefault, topNDefault])
|
|
480
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
|
+
}
|
|
496
|
+
|
|
481
497
|
const run = async (): Promise<void> => {
|
|
482
498
|
const trimmed = query.trim()
|
|
483
499
|
if (!trimmed) { setError('请输入要测试的召回问题。'); return }
|
|
500
|
+
const ticket = runRef.current + 1
|
|
501
|
+
runRef.current = ticket
|
|
484
502
|
setBusy(true); setError(null); setDroppedOpen(null)
|
|
485
503
|
try {
|
|
486
504
|
const boundedTopN = Math.min(topN, topK)
|
|
@@ -588,6 +606,8 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
|
|
|
588
606
|
...(expansion && item.via ? { via: `${item.via.title}(${item.via.relation})` } : {}),
|
|
589
607
|
}
|
|
590
608
|
})
|
|
609
|
+
// 票号过期就整段丢掉:这份响应属于上一次点击,落地只会把新一次的结果盖掉。
|
|
610
|
+
if (runRef.current !== ticket) return
|
|
591
611
|
setResult({
|
|
592
612
|
count: hits.length,
|
|
593
613
|
backend: data.backend ?? 'fts',
|
|
@@ -651,9 +671,12 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
|
|
|
651
671
|
...(data.indexVersion === undefined ? {} : { indexVersion: data.indexVersion }),
|
|
652
672
|
})
|
|
653
673
|
} catch (cause) {
|
|
674
|
+
if (runRef.current !== ticket) return
|
|
654
675
|
setError(cause instanceof Error ? cause.message : String(cause))
|
|
655
676
|
} finally {
|
|
656
|
-
|
|
677
|
+
// 只有还是当前这一次才关掉「测试中」:过期的那次把 busy 关掉,会让正在跑的那次
|
|
678
|
+
// 看起来已经结束(按钮重新可点,于是又多开一次请求)。
|
|
679
|
+
if (runRef.current === ticket) setBusy(false)
|
|
657
680
|
}
|
|
658
681
|
}
|
|
659
682
|
|
|
@@ -758,7 +781,10 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
|
|
|
758
781
|
<div className={styles.inputRow}>
|
|
759
782
|
<Input value={query} placeholder="请输入要测试的内容"
|
|
760
783
|
onChange={event => setQuery(event.target.value)}
|
|
761
|
-
|
|
784
|
+
// 回车与按钮走**同一条判断**:按钮上写着 `disabled={busy}`,而回车原来没有这一条,
|
|
785
|
+
// 于是「测试中连敲回车」会同时开出好几个请求。空串也不打 —— 那时它在报一个
|
|
786
|
+
// 人还没写完的输入。
|
|
787
|
+
onKeyDown={event => { if (event.key === 'Enter' && !busy && query.trim() !== '') void run() }} />
|
|
762
788
|
<Button variant="default" onClick={() => void run()} disabled={busy}>
|
|
763
789
|
{variant === 'runner' && <Play size={13} />}{busy ? '测试中…' : variant === 'runner' ? '运行检查' : '测试'}
|
|
764
790
|
</Button>
|
|
@@ -780,12 +806,16 @@ export function RecallPanel({ kbId, kbName, visible, variant = 'recall', default
|
|
|
780
806
|
</p>
|
|
781
807
|
{paramOpen && <div className={styles.recallControls}>
|
|
782
808
|
<label>召回 topK
|
|
783
|
-
<Input type="number" min={1} max={50} value={
|
|
784
|
-
onChange={event =>
|
|
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() }} />
|
|
785
813
|
</label>
|
|
786
814
|
<label>上下文 topN
|
|
787
|
-
<Input type="number" min={1} max={topK} value={
|
|
788
|
-
onChange={event =>
|
|
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() }} />
|
|
789
819
|
</label>
|
|
790
820
|
</div>}
|
|
791
821
|
{error && <p className={cn(styles.kbNotice, styles.kbNoticeError)} role="alert">{error}</p>}
|
package/src/client/TabGuide.tsx
CHANGED
|
@@ -162,9 +162,9 @@ const COMPILE_PROMPT_SECTIONS: GuideSection[] = [
|
|
|
162
162
|
{
|
|
163
163
|
title: '这一步发给模型的提示词',
|
|
164
164
|
points: [
|
|
165
|
-
'
|
|
166
|
-
'**归一坐标**(Resolve
|
|
167
|
-
'**建索引**(Index
|
|
165
|
+
'抽取 Knowledge / Event 使用 LLM;归一遇到歧义时调用模型补判;WikiCard 可选择模型组织。三个阶段独立运行。',
|
|
166
|
+
'**归一坐标**(Resolve)优先查 canonical、alias 与作用域;需要语义比较时由模型在已有候选中补判,不确定就保留待处理。',
|
|
167
|
+
'**建索引**(Index)从已保存卡片切召回单元,构建关键词、向量和图谱;向量编码使用所选向量模型,不重复文档抽取。',
|
|
168
168
|
'所以下面两段就是这条链路全部的模型输入。它们都是 **system 段**;user 段每次都不同(文档正文、这一坐标下的知识清单),列不出固定文本。',
|
|
169
169
|
],
|
|
170
170
|
},
|
|
@@ -176,7 +176,7 @@ const COMPILE_PROMPT_SECTIONS: GuideSection[] = [
|
|
|
176
176
|
'这是**稳定前缀**:同一份契约每份文档都发一遍,所以它进 system 而不是 user。',
|
|
177
177
|
'运行时在这段之后还会注入两样随库与文档变化的东西,所以不在上面那段里:本库**已有的坐标清单**(对象 / 动作 / 场景,含定义与别名),以及**本库骨架允许的小节名**。',
|
|
178
178
|
'已有坐标刻意放在 user 段 —— system 保持稳定,上游缓存才命中得了(`sessionId` 被钉成 `knowledge:<库 id>` 就是为了这件事)。',
|
|
179
|
-
'
|
|
179
|
+
'模型默认回 TOON 固定列表格,历史 JSON 仍兼容:`knowledge` 是抽出来的知识(每条只挂一个坐标),`objects` / `actions` / `scenes` 是它用到的原始说法与定义,`dropped` 是它丢掉且说得出理由的片段。',
|
|
180
180
|
],
|
|
181
181
|
wide: true,
|
|
182
182
|
},
|
|
@@ -80,6 +80,29 @@ const EVENT_STEP_LABEL: Record<TrialEventStepKind, string> = {
|
|
|
80
80
|
observation: '观察', check: '核对', action: '操作', result: '结果', conclusion: '结论',
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* 区块导航。
|
|
85
|
+
*
|
|
86
|
+
* 这份报告可以有七步、几十条知识、几百 KB —— 打开它的人第一件事是「跳到我要看的那一段」,
|
|
87
|
+
* 而不是从上往下读。所以顶上放一条**锚点条**:它是这一页的目录,不是第二个流程说明。
|
|
88
|
+
*
|
|
89
|
+
* 用 `<a href>` 而不是按钮:这一页就是一份文档,锚点带来的是「复制链接给同事」这种
|
|
90
|
+
* 浏览器本来就有的能力(`#steps` 直接落到处理步骤)。
|
|
91
|
+
*/
|
|
92
|
+
const SECTIONS: Array<{ id: string; label: string }> = [
|
|
93
|
+
{ id: 'trial-overview', label: '概览' },
|
|
94
|
+
{ id: 'trial-output', label: '抽取内容' },
|
|
95
|
+
{ id: 'trial-steps', label: '处理步骤' },
|
|
96
|
+
{ id: 'trial-requests', label: '模型请求' },
|
|
97
|
+
{ id: 'trial-review', label: '核验结果' },
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
function SectionNav({ items }: { items: typeof SECTIONS }): ReactNode {
|
|
101
|
+
return <nav className={styles.trialNav} aria-label="报告区块">
|
|
102
|
+
{items.map(section => <a key={section.id} href={`#${section.id}`}>{section.label}</a>)}
|
|
103
|
+
</nav>
|
|
104
|
+
}
|
|
105
|
+
|
|
83
106
|
function InlineDisclosure({ summary, children }: { summary: string; children: ReactNode }): ReactNode {
|
|
84
107
|
const [expanded, setExpanded] = useState(false)
|
|
85
108
|
return <>
|
|
@@ -421,20 +444,37 @@ export function TrialReport({ report }: { report: TrialReportView }): ReactNode
|
|
|
421
444
|
}
|
|
422
445
|
|
|
423
446
|
const defaultTab = report.knowledge.length > 0 ? 'knowledge' : events.length > 0 ? 'events' : 'cards'
|
|
447
|
+
/**
|
|
448
|
+
* 「这一次什么都没抽出来」。
|
|
449
|
+
*
|
|
450
|
+
* 一次模型没跑通的运行会有五行 0、一个三个空页签的内容区、一条空列表 —— 一屏全是
|
|
451
|
+
* 「没有」。而这时真正有信息的只有两样:**为什么失败**,以及**发出去的提示词**。
|
|
452
|
+
* 所以这种记录把概览压成一行、整段跳过内容区,直接把位置让给模型请求与核验。
|
|
453
|
+
*/
|
|
454
|
+
const empty = !report.model.ok && report.knowledge.length === 0 && events.length === 0 && report.wikiCards.length === 0
|
|
455
|
+
/** 流水线的抽取阶段落下的记录:报告里的 ②③④ 段只是试跑顺带算的预览。 */
|
|
456
|
+
const extractOnly = report.scope === 'extract-only'
|
|
424
457
|
|
|
425
458
|
return <div className={styles.trialReport}>
|
|
426
|
-
|
|
459
|
+
{/* 只列真的会渲染出来的区块:空运行的「抽取内容」整段被跳过,留着就是一个点了没反应的锚点。 */}
|
|
460
|
+
<SectionNav items={empty ? SECTIONS.filter(section => section.id !== 'trial-output') : SECTIONS} />
|
|
461
|
+
<section id="trial-overview" className={styles.trialOverview} aria-labelledby="trial-overview-title">
|
|
427
462
|
<div className={styles.trialSectionHead}>
|
|
428
463
|
<h2 id="trial-overview-title">结果概览</h2>
|
|
429
464
|
<span>{report.model.ok ? '已完成' : '需检查'}</span>
|
|
430
465
|
</div>
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
<
|
|
436
|
-
<
|
|
437
|
-
|
|
466
|
+
{/* 空运行不排五行 0:那五行没有一个数字是结论,而失败原因在上面那一条里。 */}
|
|
467
|
+
{empty
|
|
468
|
+
// 一行平话,不是虚线空态框:框会在红色报错上方再压一层「这里什么都没有」,
|
|
469
|
+
// 而真正该被读到的是下面那句失败原因。
|
|
470
|
+
? <p className={styles.trialOverviewEmpty}>这一次没有形成任何 Knowledge、Event 或 WikiCard。</p>
|
|
471
|
+
: <dl className={styles.trialOverviewStats}>
|
|
472
|
+
<div><dt>Knowledge</dt><dd>{report.knowledge.length}</dd></div>
|
|
473
|
+
<div><dt>Event</dt><dd>{events.length}</dd></div>
|
|
474
|
+
<div><dt>坐标</dt><dd>{report.objects.length} / {report.actions.length} / {report.scenes.length}<small>对象 / 动作 / 场景</small></dd></div>
|
|
475
|
+
{!extractOnly && <div><dt>WikiCard</dt><dd>{report.wikiCards.length}</dd></div>}
|
|
476
|
+
<div><dt>已报告 Token</dt><dd>{usageLabel}<small>{usageNote}</small></dd></div>
|
|
477
|
+
</dl>}
|
|
438
478
|
<p className={styles.trialOverviewTitle}>
|
|
439
479
|
<strong>{report.title}</strong><span>{report.lines} 行 · {report.chars.toLocaleString('zh-CN')} 字</span>
|
|
440
480
|
</p>
|
|
@@ -445,7 +485,7 @@ export function TrialReport({ report }: { report: TrialReportView }): ReactNode
|
|
|
445
485
|
<TriangleAlert size={12} />{report.model.why ?? '模型没有参与'} · 本次没有形成有效 Knowledge 或 Event。
|
|
446
486
|
</p>}
|
|
447
487
|
|
|
448
|
-
<section className={styles.trialSection} aria-labelledby="trial-output-title">
|
|
488
|
+
{!empty && <section id="trial-output" className={styles.trialSection} aria-labelledby="trial-output-title">
|
|
449
489
|
<div className={styles.trialSectionHead}>
|
|
450
490
|
<h2 id="trial-output-title">抽取内容</h2>
|
|
451
491
|
<span>按类型查看抽取结果</span>
|
|
@@ -475,18 +515,23 @@ export function TrialReport({ report }: { report: TrialReportView }): ReactNode
|
|
|
475
515
|
: <p className={styles.trialEmpty}>没有可物化的 WikiCard。</p>}
|
|
476
516
|
</TabsContent>
|
|
477
517
|
</Tabs>
|
|
478
|
-
</section>
|
|
518
|
+
</section>}
|
|
479
519
|
|
|
480
|
-
<section className={styles.trialSection} aria-labelledby="trial-steps-title">
|
|
520
|
+
<section id="trial-steps" className={styles.trialSection} aria-labelledby="trial-steps-title">
|
|
481
521
|
<div className={styles.trialSectionHead}>
|
|
482
522
|
<h2 id="trial-steps-title">处理步骤</h2>
|
|
483
|
-
<span>4 个阶段 · {report.steps.length}
|
|
523
|
+
<span>{extractOnly ? '1 个阶段 · 抽取' : `4 个阶段 · ${report.steps.length} 步`}</span>
|
|
484
524
|
</div>
|
|
525
|
+
{/* 只在流水线的抽取记录上说明:旧的全链路编译里这四段是真跑过的,说了反而错。 */}
|
|
526
|
+
{extractOnly && <p className={styles.trialNote}>
|
|
527
|
+
这里只列<b>抽取</b>这一步。② 归一、③ 物化、④ 建索引在这个库里是独立阶段,
|
|
528
|
+
各有自己的轨迹与任务;下面是抽取时按机械规则顺带算的预览,不代表库里的现状。
|
|
529
|
+
</p>}
|
|
485
530
|
<ol className={styles.trialSteps}>
|
|
486
|
-
{report.steps.map((step, index) => {
|
|
531
|
+
{(extractOnly ? report.steps.filter(step => step.id === 'extract') : report.steps).map((step, index, list) => {
|
|
487
532
|
const expanded = openStep === step.id
|
|
488
533
|
const items = step.items ?? []
|
|
489
|
-
const phaseStart = index === 0 ||
|
|
534
|
+
const phaseStart = index === 0 || list[index - 1]!.phase !== step.phase
|
|
490
535
|
return <li key={step.id} className={styles.trialStepWrap}>
|
|
491
536
|
{phaseStart && <p className={styles.trialPhaseHead}>{step.phase}</p>}
|
|
492
537
|
<div className={cn(styles.trialStep, step.needsHuman && styles.trialStepWarn)}>
|
|
@@ -524,7 +569,7 @@ export function TrialReport({ report }: { report: TrialReportView }): ReactNode
|
|
|
524
569
|
</ol>
|
|
525
570
|
</section>
|
|
526
571
|
|
|
527
|
-
<section className={styles.trialSection} aria-labelledby="trial-requests-title">
|
|
572
|
+
<section id="trial-requests" className={styles.trialSection} aria-labelledby="trial-requests-title">
|
|
528
573
|
<div className={styles.trialSectionHead}>
|
|
529
574
|
<h2 id="trial-requests-title">模型请求</h2>
|
|
530
575
|
<span>逐次查看输入、输出和 Token 用量</span>
|
|
@@ -553,7 +598,7 @@ export function TrialReport({ report }: { report: TrialReportView }): ReactNode
|
|
|
553
598
|
/>}
|
|
554
599
|
</section>
|
|
555
600
|
|
|
556
|
-
<section className={styles.trialSection} aria-labelledby="trial-review-title">
|
|
601
|
+
<section id="trial-review" className={styles.trialSection} aria-labelledby="trial-review-title">
|
|
557
602
|
<div className={styles.trialSectionHead}>
|
|
558
603
|
<h2 id="trial-review-title">核验结果</h2>
|
|
559
604
|
<span>{report.exceptions.length > 0 ? `${report.exceptions.length} 项需检查` : '本地核验已完成'}</span>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useMemo, useState, type ChangeEvent, type ReactNode } from 'react'
|
|
1
|
+
import { useEffect, useMemo, useRef, useState, type ChangeEvent, type ReactNode } from 'react'
|
|
2
2
|
import { Check } from '@tita-agent/ui/icons'
|
|
3
3
|
import { Button, Field, SelectMenu, Textarea, type SelectMenuOption } from '@tita-agent/ui/components'
|
|
4
4
|
import {
|
|
@@ -12,6 +12,7 @@ export interface CompileModelSelectionView {
|
|
|
12
12
|
mode: 'default' | 'fixed'
|
|
13
13
|
provider?: string
|
|
14
14
|
model?: string
|
|
15
|
+
reasoningEffort?: string
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
interface CompileModelCatalogItem {
|
|
@@ -21,6 +22,7 @@ interface CompileModelCatalogItem {
|
|
|
21
22
|
description?: string
|
|
22
23
|
available: boolean
|
|
23
24
|
current?: boolean
|
|
25
|
+
reasoning?: { efforts: Array<{ id: string; name: string; description?: string }>; defaultEffort?: string }
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
interface CompileModelCatalog {
|
|
@@ -50,8 +52,19 @@ export function TypeDefinitionDrawer({
|
|
|
50
52
|
}: TypeDefinitionDrawerProps): ReactNode {
|
|
51
53
|
const [noteDraft, setNoteDraft] = useState(extractionNote)
|
|
52
54
|
const [modelDraft, setModelDraft] = useState(DEFAULT_MODEL_VALUE)
|
|
55
|
+
const [effortDraft, setEffortDraft] = useState(compileModel.reasoningEffort ?? '__default__')
|
|
56
|
+
useEffect(() => { setEffortDraft(compileModel.reasoningEffort ?? '__default__') }, [compileModel.reasoningEffort])
|
|
53
57
|
const [catalog, setCatalog] = useState<CompileModelCatalog | null>(null)
|
|
54
58
|
const [catalogError, setCatalogError] = useState('')
|
|
59
|
+
/**
|
|
60
|
+
* 上一次读回来的默认模型。
|
|
61
|
+
*
|
|
62
|
+
* 放进 ref 而不是当成 effect 的依赖:这份 effect **自己会写 `catalog`**,把
|
|
63
|
+
* `catalog?.default.provider` 列进依赖就是让输出当输入 —— 每读一次目录都会再触发
|
|
64
|
+
* 一次自己,打开抽屉要打两三次 `/knowledge/models`。ref 让它读到最新值,但不触发。
|
|
65
|
+
*/
|
|
66
|
+
const catalogRef = useRef<CompileModelCatalog | null>(null)
|
|
67
|
+
catalogRef.current = catalog
|
|
55
68
|
useEffect(() => { setNoteDraft(extractionNote) }, [extractionNote])
|
|
56
69
|
useEffect(() => {
|
|
57
70
|
if (compileModel.mode === 'fixed' && compileModel.provider && compileModel.model) {
|
|
@@ -64,14 +77,16 @@ export function TypeDefinitionDrawer({
|
|
|
64
77
|
if (!open || onSaveCompileModel === undefined) return undefined
|
|
65
78
|
let cancelled = false
|
|
66
79
|
setCatalogError('')
|
|
67
|
-
|
|
68
|
-
|
|
80
|
+
const route = modelDraft.startsWith('fixed:') ? modelDraft.split(':').slice(1).map(decodeURIComponent) : [catalogRef.current?.default.provider, catalogRef.current?.default.model]
|
|
81
|
+
const params = new URLSearchParams({ kb: kbId })
|
|
82
|
+
if (route[0] && route[1]) { params.set('provider', route[0]); params.set('model', route[1]) }
|
|
83
|
+
void requestJson<CompileModelCatalog>(`/knowledge/models?${params}`).then(result => {
|
|
69
84
|
if (!cancelled) setCatalog(result.data ?? null)
|
|
70
85
|
}).catch(error => {
|
|
71
86
|
if (!cancelled) setCatalogError(error instanceof Error ? error.message : String(error))
|
|
72
87
|
})
|
|
73
88
|
return () => { cancelled = true }
|
|
74
|
-
}, [kbId, onSaveCompileModel, open])
|
|
89
|
+
}, [kbId, onSaveCompileModel, open, modelDraft])
|
|
75
90
|
|
|
76
91
|
const modelOptions = useMemo(() => {
|
|
77
92
|
const options: SelectMenuOption[] = [{
|
|
@@ -100,20 +115,26 @@ export function TypeDefinitionDrawer({
|
|
|
100
115
|
return options
|
|
101
116
|
}, [catalog, compileModel.mode, compileModel.model, compileModel.provider])
|
|
102
117
|
|
|
103
|
-
const modelChanged = modelDraft !== (compileModel.mode === 'fixed' && compileModel.provider && compileModel.model
|
|
118
|
+
const modelChanged = effortDraft !== (compileModel.reasoningEffort ?? '__default__') || modelDraft !== (compileModel.mode === 'fixed' && compileModel.provider && compileModel.model
|
|
104
119
|
? fixedModelValue(compileModel.provider, compileModel.model)
|
|
105
120
|
: DEFAULT_MODEL_VALUE)
|
|
106
121
|
const saveCompileModel = async (): Promise<void> => {
|
|
107
122
|
if (onSaveCompileModel === undefined) return
|
|
108
123
|
if (modelDraft === DEFAULT_MODEL_VALUE) {
|
|
109
|
-
await onSaveCompileModel({ mode: 'default' })
|
|
124
|
+
await onSaveCompileModel({ mode: 'default', ...(effortDraft === '__default__' ? {} : { reasoningEffort: effortDraft }) })
|
|
110
125
|
return
|
|
111
126
|
}
|
|
112
127
|
const selected = catalog?.models.find(item => fixedModelValue(item.provider, item.id) === modelDraft && item.available)
|
|
113
128
|
if (!selected) return
|
|
114
|
-
await onSaveCompileModel({ mode: 'fixed', provider: selected.provider, model: selected.id })
|
|
129
|
+
await onSaveCompileModel({ mode: 'fixed', provider: selected.provider, model: selected.id, ...(effortDraft === '__default__' ? {} : { reasoningEffort: effortDraft }) })
|
|
115
130
|
}
|
|
116
131
|
|
|
132
|
+
const selectedModel = catalog?.models.find(item => modelDraft === DEFAULT_MODEL_VALUE
|
|
133
|
+
? item.provider === catalog.default.provider && item.id === catalog.default.model
|
|
134
|
+
: fixedModelValue(item.provider, item.id) === modelDraft)
|
|
135
|
+
const effortOptions: SelectMenuOption[] = [{ value: '__default__', label: '跟随模型默认强度' }, ...(selectedModel?.reasoning?.efforts ?? []).map(item => ({ value: item.id, label: item.name }))]
|
|
136
|
+
if (effortDraft !== '__default__' && !effortOptions.some(option => option.value === effortDraft)) effortOptions.push({ value: effortDraft, label: `当前不可用:${effortDraft}`, disabled: true })
|
|
137
|
+
|
|
117
138
|
return <Drawer open={open} onOpenChange={onOpenChange}>
|
|
118
139
|
{open && <DrawerContent
|
|
119
140
|
className={styles.settingsDrawer}
|
|
@@ -129,10 +150,13 @@ export function TypeDefinitionDrawer({
|
|
|
129
150
|
<div className={styles.settingsSectionHeader}>
|
|
130
151
|
<div><h3>编译模型</h3></div>
|
|
131
152
|
</div>
|
|
132
|
-
<Field as="div" label="模型" hint=
|
|
133
|
-
<SelectMenu value={modelDraft} disabled={busy !== null || (catalog === null && catalogError === '')} onValueChange={setModelDraft}
|
|
153
|
+
<Field as="div" label="模型" hint="用于抽取、归一补判与卡片组织;任务启动时保存实际选择。">
|
|
154
|
+
<SelectMenu value={modelDraft} disabled={busy !== null || (catalog === null && catalogError === '')} onValueChange={value => { setModelDraft(value); setEffortDraft('__default__') }}
|
|
134
155
|
options={modelOptions} placeholder="加载 DSH 模型目录…" aria-label="编译模型" />
|
|
135
156
|
</Field>
|
|
157
|
+
<Field as="div" label="思考强度" hint={selectedModel?.reasoning?.efforts.length ? '档位来自当前模型的 DSH 目录。' : '当前模型未提供可选档位,使用模型默认值。'}>
|
|
158
|
+
<SelectMenu value={effortDraft} options={effortOptions} onValueChange={setEffortDraft} disabled={busy !== null || !selectedModel?.reasoning?.efforts.length} aria-label="编译模型思考强度" />
|
|
159
|
+
</Field>
|
|
136
160
|
{catalog?.default && !catalog.default.available && <p className={styles.kbHint} role="status">
|
|
137
161
|
DSH 默认模型不可用;可在 DSH 设置中修复,或选择固定模型。
|
|
138
162
|
</p>}
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { useEffect, useMemo, useState, type ReactNode } from 'react'
|
|
23
|
-
import { AlertCircle, Check, GitCompare, RefreshCw } from '@tita-agent/ui/icons'
|
|
24
|
-
import { Button, cn } from '@tita-agent/ui/components'
|
|
23
|
+
import { AlertCircle, Check, CircleDot, GitCompare, RefreshCw } from '@tita-agent/ui/icons'
|
|
24
|
+
import { Badge, Button, cn } from '@tita-agent/ui/components'
|
|
25
25
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@tita-agent/ui/overlays'
|
|
26
26
|
import type { AssetRef } from './asset-images.ts'
|
|
27
27
|
import type { VersionManagerEntry } from '@tita-agent/ui/versions'
|
|
@@ -34,13 +34,10 @@ import { STATUS_VIEW } from './WikiCardRow.tsx'
|
|
|
34
34
|
import { styles } from './styles.ts'
|
|
35
35
|
import { formatDateTime } from './format.ts'
|
|
36
36
|
import type { CardRelatedView, KnowledgeCardView, WikiCardVersionView } from './KnowledgeBaseTab.tsx'
|
|
37
|
+
import type { AttributionEvent } from './attribution/model.ts'
|
|
37
38
|
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
* 客户端不能 import 服务端模块(那边拉 node: 内置),所以这里重列一次,并由一条测试
|
|
41
|
-
* 钉住两边相等(同 `SOURCE_FORMAT_LABEL` 那类「契约重列」的处置)。
|
|
42
|
-
*/
|
|
43
|
-
const RELATED_SECTION = ['相关']
|
|
39
|
+
/** 正文里的生成式导航小节名;它们由结构化引用展示,不重复留在正文。 */
|
|
40
|
+
const RELATED_SECTION = ['相关', '关联 Event', '关联 Events', '关联事件']
|
|
44
41
|
|
|
45
42
|
/** 一张卡片的完整预览数据(正文 + 版本 + 图片 + 相关 + 坐标名字)。 */
|
|
46
43
|
export interface CardPreviewDetail {
|
|
@@ -90,6 +87,9 @@ export interface WikiCardPreviewProps {
|
|
|
90
87
|
onCompare?: (entry: VersionManagerEntry) => void
|
|
91
88
|
versionDiff?: { data?: VersionContentDiffData; loading: boolean; error?: string }
|
|
92
89
|
onOpenRelated: (link: { entityType: string; entityId: string; title: string }) => void
|
|
90
|
+
/** 打开 Event 栏并定位到这张卡片引用的事件。 */
|
|
91
|
+
onOpenEvent: (eventId: string) => void
|
|
92
|
+
events?: AttributionEvent[]
|
|
93
93
|
/**
|
|
94
94
|
* SourceRaw 的 docId → 文档标题。
|
|
95
95
|
*
|
|
@@ -113,7 +113,7 @@ function sourceRefsText(refs: string[], titles?: Map<string, string>): string {
|
|
|
113
113
|
|
|
114
114
|
export function WikiCardPreview({
|
|
115
115
|
kbId, detail, versionEntries, currentVersionId, loading,
|
|
116
|
-
onChanged, onOpenRelated, sourceTitles,
|
|
116
|
+
onChanged, onOpenRelated, onOpenEvent, events = [], sourceTitles,
|
|
117
117
|
onCompare, versionDiff = { loading: false },
|
|
118
118
|
}: WikiCardPreviewProps): ReactNode {
|
|
119
119
|
const [recompiling, setRecompiling] = useState(false)
|
|
@@ -122,9 +122,27 @@ export function WikiCardPreview({
|
|
|
122
122
|
const status = STATUS_VIEW[detail.card.status]?.label ?? detail.card.status
|
|
123
123
|
const refs = sourceRefsText(detail.card.sourceRefs, sourceTitles)
|
|
124
124
|
const knowledge = useCardKnowledge(kbId, detail.card.entityId, sourceTitles)
|
|
125
|
+
const relatedEvents = useMemo(() => {
|
|
126
|
+
const raw = detail.card.metadata?.event_refs
|
|
127
|
+
let ids: string[] = []
|
|
128
|
+
if (Array.isArray(raw)) ids = raw.filter((item): item is string => typeof item === 'string')
|
|
129
|
+
else if (typeof raw === 'string') {
|
|
130
|
+
try {
|
|
131
|
+
const parsed: unknown = JSON.parse(raw)
|
|
132
|
+
if (Array.isArray(parsed)) ids = parsed.filter((item): item is string => typeof item === 'string')
|
|
133
|
+
} catch { /* 无效派生引用不显示 */ }
|
|
134
|
+
}
|
|
135
|
+
const byId = new Map(events.map(event => [event.id, event]))
|
|
136
|
+
return [...new Set(ids)].map(id => byId.get(id) ?? { id }).map(event => ({
|
|
137
|
+
id: event.id,
|
|
138
|
+
title: 'title' in event ? event.title : event.id,
|
|
139
|
+
summary: 'summary' in event ? event.summary : '',
|
|
140
|
+
occurredAt: 'occurredAt' in event ? event.occurredAt : undefined,
|
|
141
|
+
}))
|
|
142
|
+
}, [detail.card.metadata, events])
|
|
125
143
|
useEffect(() => { setCompareOpen(false) }, [detail.card.entityId])
|
|
126
144
|
/**
|
|
127
|
-
*
|
|
145
|
+
* 移除旧版本正文里的生成式导航区块,再显示结构化卡片与 Event 引用。
|
|
128
146
|
*
|
|
129
147
|
* 投影顺序固定为:物化正文 → 去掉旧导航区 → 补引用清单。
|
|
130
148
|
*/
|
|
@@ -206,6 +224,21 @@ export function WikiCardPreview({
|
|
|
206
224
|
{recompiling ? '编译中…' : '按坐标重新编译'}</Button>}
|
|
207
225
|
/>
|
|
208
226
|
|
|
227
|
+
{relatedEvents.length > 0 && <section className="flex min-w-0 flex-col gap-1.5" aria-label="关联 Event">
|
|
228
|
+
<h3 className="m-0 flex items-center gap-1.5 text-[11px] font-semibold text-tita-ink">
|
|
229
|
+
<CircleDot size={12} aria-hidden="true" />关联 Event <Badge tone="neutral">{relatedEvents.length}</Badge>
|
|
230
|
+
</h3>
|
|
231
|
+
<ul className="m-0 grid list-none gap-1 p-0">
|
|
232
|
+
{relatedEvents.map(event => <li key={event.id} className="min-w-0 rounded-md border border-tita-line px-2 py-1.5">
|
|
233
|
+
<Button variant="ghost" size="sm" className="h-auto max-w-full justify-start px-0 py-0 text-left text-[10.5px]" onClick={() => onOpenEvent(event.id)}>
|
|
234
|
+
<span className="min-w-0 truncate">{event.title}</span>
|
|
235
|
+
{event.occurredAt && <span className="shrink-0 text-[9px] text-tita-ink-faint">· {event.occurredAt}</span>}
|
|
236
|
+
</Button>
|
|
237
|
+
{event.summary && <p className="m-0 mt-0.5 line-clamp-2 text-[10px] leading-relaxed text-tita-ink-muted">{event.summary}</p>}
|
|
238
|
+
</li>)}
|
|
239
|
+
</ul>
|
|
240
|
+
</section>}
|
|
241
|
+
|
|
209
242
|
{recompileNotice && <div className={cn(styles.cardRecompileNotice, recompileNotice.tone === 'error' ? styles.cardRecompileNoticeError : styles.cardRecompileNoticeSuccess)} role="status">
|
|
210
243
|
{recompileNotice.tone === 'error' ? <AlertCircle size={12} aria-hidden="true" /> : <Check size={12} aria-hidden="true" />}
|
|
211
244
|
<span>{recompileNotice.text}</span>
|
|
@@ -165,7 +165,7 @@ const STAGES: Stage[] = [
|
|
|
165
165
|
body: [
|
|
166
166
|
'**三个轴分别归一**:先找 canonical,再找 alias,最后才新建候选。匹配不唯一时,**不猜**。',
|
|
167
167
|
'每条知识绑定一个**坐标**:三个轴实体 id 的**复合键**。**Coordinate 不是实体**,没有自己的表。',
|
|
168
|
-
'
|
|
168
|
+
'归一化文本一致时合并并追加来源;语义相似只记候选,不自动合并。相互矛盾的说法需显式标记 disputed。',
|
|
169
169
|
],
|
|
170
170
|
rule: {
|
|
171
171
|
order: '2',
|
|
@@ -176,7 +176,7 @@ const STAGES: Stage[] = [
|
|
|
176
176
|
produces: {
|
|
177
177
|
layer: 'Canonical Knowledge',
|
|
178
178
|
ask: '在哪里',
|
|
179
|
-
note: '
|
|
179
|
+
note: '轴绑定为 id;同文事实合并,语义候选待确认',
|
|
180
180
|
},
|
|
181
181
|
},
|
|
182
182
|
{
|
|
@@ -198,7 +198,7 @@ const STAGES: Stage[] = [
|
|
|
198
198
|
title: '坐标决定卡片身份,卡名只是显示',
|
|
199
199
|
body: '一个坐标最多一张卡。坐标是数据库主键;标题由对象、场景和动作生成,只是**显示名**,不参与判重。',
|
|
200
200
|
},
|
|
201
|
-
model: '
|
|
201
|
+
model: '系统分组,可选模型组织',
|
|
202
202
|
produces: {
|
|
203
203
|
layer: 'Wiki Card',
|
|
204
204
|
ask: '怎么组织这些知道的东西',
|
|
@@ -217,7 +217,7 @@ const STAGES: Stage[] = [
|
|
|
217
217
|
'**Knowledge 是证据层,不是第二个检索入口**:命中 Section 后,再带出对应知识、状态和出处。',
|
|
218
218
|
'向量、关键词和图谱 boost 都属于**可重建的派生层**(`derived.db`)。**轴注册表不是索引**,而是坐标解析时使用的命名真源。',
|
|
219
219
|
],
|
|
220
|
-
model: '
|
|
220
|
+
model: '系统切分 + 向量编码',
|
|
221
221
|
produces: {
|
|
222
222
|
layer: '可检索 Wiki',
|
|
223
223
|
/* 派生层,所以没有 ask —— 它不是实体,不回答上面那三个问题。 */
|
|
@@ -286,7 +286,7 @@ icon: Database, lane: 'knowledge', tab: 'knowledge', knowledgeTab: 'source-raw',
|
|
|
286
286
|
{ label: '物化 WikiCard', detail: '按 (对象, 动作, 场景) 分组,卡名由公式推导(对象名 + 场景 + 动作名);小节与字段作为组织提示,卡片直接入库,review 与备注在预览中处理', state: 'done' },
|
|
287
287
|
{ label: '建索引', detail: '切召回单元、写索引页与相关卡片、检查断链,不调模型', state: 'done' },
|
|
288
288
|
],
|
|
289
|
-
status: 'live', statusNote: '
|
|
289
|
+
status: 'live', statusNote: '导入默认只保存来源;抽取 Knowledge / Event、归一、物化分别运行,也可全链路运行。未变文档复用检查点;构建成功后统一发布索引。模型不可用时任务明确失败,可查看输出与历史后重试。',
|
|
290
290
|
icon: RefreshCw, lane: 'knowledge', tab: 'knowledge', knowledgeTab: 'wiki',
|
|
291
291
|
},
|
|
292
292
|
build: {
|