dsh-data-domain-knowledge 0.1.1-alpha.2 → 0.1.1-alpha.3
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 +23 -16
- package/lib/client.js +596 -841
- package/lib/contract-refs.d.ts +0 -1
- package/lib/doc-page.js +7 -7
- package/lib/pages.js +928 -1145
- package/lib/types/agent-behavior-rules.d.ts.map +1 -1
- package/lib/types/agent-behavior-rules.js +1 -2
- package/lib/types/agent-behavior-rules.js.map +1 -1
- package/lib/types/axis-contract.js +1 -1
- package/lib/types/axis-contract.js.map +1 -1
- package/lib/types/card-organize.d.ts +2 -1
- package/lib/types/card-organize.d.ts.map +1 -1
- package/lib/types/card-organize.js +5 -3
- package/lib/types/card-organize.js.map +1 -1
- package/lib/types/config-schema.d.ts +1 -1
- package/lib/types/config-schema.js +1 -1
- package/lib/types/convert/host.d.ts +2 -3
- package/lib/types/convert/host.d.ts.map +1 -1
- package/lib/types/convert/host.js +5 -2
- package/lib/types/convert/host.js.map +1 -1
- package/lib/types/convert/ingest.d.ts +2 -0
- package/lib/types/convert/ingest.d.ts.map +1 -1
- package/lib/types/convert/ingest.js +1 -0
- package/lib/types/convert/ingest.js.map +1 -1
- package/lib/types/diagnosis/types.d.ts +1 -1
- package/lib/types/diagnosis/types.js +1 -1
- package/lib/types/images.js +1 -1
- package/lib/types/index.d.ts +0 -4
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +30 -41
- package/lib/types/index.js.map +1 -1
- package/lib/types/knowledge-contract.d.ts +6 -0
- package/lib/types/knowledge-contract.d.ts.map +1 -1
- package/lib/types/knowledge-contract.js +6 -2
- package/lib/types/knowledge-contract.js.map +1 -1
- package/lib/types/layout.d.ts +9 -26
- package/lib/types/layout.d.ts.map +1 -1
- package/lib/types/layout.js +10 -28
- package/lib/types/layout.js.map +1 -1
- package/lib/types/materialize-snapshot.d.ts +5 -0
- package/lib/types/materialize-snapshot.d.ts.map +1 -1
- package/lib/types/migrate.d.ts +3 -1
- package/lib/types/migrate.d.ts.map +1 -1
- package/lib/types/migrate.js +3 -1
- package/lib/types/migrate.js.map +1 -1
- package/lib/types/organize-contract.d.ts.map +1 -1
- package/lib/types/organize-contract.js +2 -1
- package/lib/types/organize-contract.js.map +1 -1
- package/lib/types/pipeline/engine.d.ts +1 -0
- package/lib/types/pipeline/engine.d.ts.map +1 -1
- package/lib/types/pipeline/engine.js +9 -3
- package/lib/types/pipeline/engine.js.map +1 -1
- package/lib/types/pipeline/types.d.ts +6 -0
- package/lib/types/pipeline/types.d.ts.map +1 -1
- package/lib/types/pipeline/types.js.map +1 -1
- package/lib/types/present.d.ts +1 -1
- package/lib/types/present.js +1 -1
- package/lib/types/preset-ids.d.ts +0 -1
- package/lib/types/preset-ids.d.ts.map +1 -1
- package/lib/types/preset-ids.js +0 -3
- package/lib/types/preset-ids.js.map +1 -1
- package/lib/types/query-analysis.d.ts +0 -5
- package/lib/types/query-analysis.d.ts.map +1 -1
- package/lib/types/query-analysis.js +0 -16
- package/lib/types/query-analysis.js.map +1 -1
- package/lib/types/recall-delivery.d.ts +36 -0
- package/lib/types/recall-delivery.d.ts.map +1 -0
- package/lib/types/recall-delivery.js +16 -0
- package/lib/types/recall-delivery.js.map +1 -0
- package/lib/types/runtime.d.ts +26 -12
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/runtime.js +231 -106
- package/lib/types/runtime.js.map +1 -1
- package/lib/types/settings-volatile.d.ts +0 -7
- package/lib/types/settings-volatile.d.ts.map +1 -1
- package/lib/types/settings-volatile.js +0 -7
- package/lib/types/settings-volatile.js.map +1 -1
- package/lib/types/source-identity.d.ts +3 -0
- package/lib/types/source-identity.d.ts.map +1 -0
- package/lib/types/source-identity.js +5 -0
- package/lib/types/source-identity.js.map +1 -0
- package/lib/types/store/axis-references.d.ts.map +1 -1
- package/lib/types/store/axis-references.js +5 -3
- package/lib/types/store/axis-references.js.map +1 -1
- package/lib/types/store/knowledge-db.d.ts +1 -1
- package/lib/types/store/knowledge-db.d.ts.map +1 -1
- package/lib/types/store/knowledge-db.js +16 -2
- package/lib/types/store/knowledge-db.js.map +1 -1
- package/lib/types/types.d.ts +5 -4
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/types.js +6 -3
- package/lib/types/types.js.map +1 -1
- package/lib/types/wiki-availability.d.ts +11 -0
- package/lib/types/wiki-availability.d.ts.map +1 -0
- package/lib/types/wiki-availability.js +39 -0
- package/lib/types/wiki-availability.js.map +1 -0
- package/lib/types/wiki-finalize.d.ts +4 -2
- package/lib/types/wiki-finalize.d.ts.map +1 -1
- package/lib/types/wiki-finalize.js +9 -27
- package/lib/types/wiki-finalize.js.map +1 -1
- package/lib/types/wiki-links.d.ts +35 -0
- package/lib/types/wiki-links.d.ts.map +1 -0
- package/lib/types/wiki-links.js +129 -0
- package/lib/types/wiki-links.js.map +1 -0
- package/package.json +16 -16
- package/scripts/doctor.mjs +0 -7
- package/scripts/setup-embedding.sh +0 -0
- package/src/agent-behavior-rules.ts +1 -2
- package/src/axis-contract.ts +1 -1
- package/src/card-organize.ts +5 -3
- package/src/client/AttributionEventGuide.tsx +3 -3
- package/src/client/AxisMatrix.tsx +86 -19
- package/src/client/DirectoryPicker.tsx +0 -2
- package/src/client/GuideArchitectureCanvas.tsx +5 -12
- package/src/client/ImportFilesDialog.tsx +1 -0
- package/src/client/InfoHint.tsx +1 -1
- package/src/client/KnowledgeBaseTab.tsx +113 -39
- package/src/client/KnowledgePanel.tsx +16 -9
- package/src/client/OperationLogTab.tsx +1 -1
- package/src/client/RecallPanel.tsx +5 -16
- package/src/client/TabGuide.tsx +3 -7
- package/src/client/WikiFlowPanel.tsx +10 -11
- package/src/client/arch/NodeLogicPanel.tsx +3 -3
- package/src/client/arch/model.ts +5 -113
- package/src/client/architecture.css +1 -1
- package/src/client/architecture.tsx +13 -27
- package/src/client/attribution/AttributionTab.tsx +141 -10
- package/src/client/pages.css +2 -2
- package/src/client/pages.tsx +4 -11
- package/src/client/styles.css +15 -96
- package/src/config-schema.ts +1 -1
- package/src/convert/host.ts +7 -5
- package/src/convert/ingest.ts +3 -0
- package/src/diagnosis/types.ts +1 -1
- package/src/images.ts +1 -1
- package/src/index.ts +27 -49
- package/src/knowledge-contract.ts +7 -2
- package/src/layout.ts +10 -32
- package/src/materialize-snapshot.ts +1 -0
- package/src/migrate.ts +3 -1
- package/src/organize-contract.ts +2 -1
- package/src/pipeline/engine.ts +9 -3
- package/src/pipeline/types.ts +2 -0
- package/src/present.ts +1 -1
- package/src/preset-ids.ts +0 -4
- package/src/query-analysis.ts +0 -20
- package/src/recall-delivery.ts +19 -0
- package/src/runtime.ts +220 -102
- package/src/settings-volatile.ts +0 -7
- package/src/source-identity.ts +4 -0
- package/src/store/axis-references.ts +5 -3
- package/src/store/knowledge-db.ts +14 -2
- package/src/types.ts +6 -4
- package/src/wiki-availability.ts +37 -0
- package/src/wiki-finalize.ts +9 -25
- package/src/wiki-links.ts +120 -0
- package/lib/types/intent/engine.d.ts +0 -122
- package/lib/types/intent/engine.d.ts.map +0 -1
- package/lib/types/intent/engine.js +0 -488
- package/lib/types/intent/engine.js.map +0 -1
- package/lib/types/intent/host.d.ts +0 -68
- package/lib/types/intent/host.d.ts.map +0 -1
- package/lib/types/intent/host.js +0 -249
- package/lib/types/intent/host.js.map +0 -1
- package/lib/types/intent/scope.d.ts +0 -25
- package/lib/types/intent/scope.d.ts.map +0 -1
- package/lib/types/intent/scope.js +0 -116
- package/lib/types/intent/scope.js.map +0 -1
- package/lib/types/intent/store.d.ts +0 -33
- package/lib/types/intent/store.d.ts.map +0 -1
- package/lib/types/intent/store.js +0 -239
- package/lib/types/intent/store.js.map +0 -1
- package/lib/types/intent/tools.d.ts +0 -62
- package/lib/types/intent/tools.d.ts.map +0 -1
- package/lib/types/intent/tools.js +0 -130
- package/lib/types/intent/tools.js.map +0 -1
- package/lib/types/intent/types.d.ts +0 -170
- package/lib/types/intent/types.d.ts.map +0 -1
- package/lib/types/intent/types.js +0 -56
- package/lib/types/intent/types.js.map +0 -1
- package/src/client/IntentLogic.tsx +0 -192
- package/src/client/IntentTab.tsx +0 -343
- package/src/intent/README.md +0 -42
- package/src/intent/engine.ts +0 -515
- package/src/intent/host.ts +0 -315
- package/src/intent/scope.ts +0 -125
- package/src/intent/store.ts +0 -218
- package/src/intent/tools.ts +0 -154
- package/src/intent/types.ts +0 -229
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
-
CircleDot, Database, GitBranch, History, Sparkles, Stethoscope, X,
|
|
2
|
+
CircleDot, Database, GitBranch, History, Library, Sparkles, Stethoscope, X,
|
|
3
3
|
} from '@tita-agent/ui/icons'
|
|
4
4
|
import { useCallback, useEffect, useMemo, useState, type ReactNode } from 'react'
|
|
5
|
-
import { Button } from '@tita-agent/ui/components'
|
|
5
|
+
import { Badge, Button } from '@tita-agent/ui/components'
|
|
6
6
|
import { Dialog, DialogContent, DialogTitle } from '@tita-agent/ui/overlays'
|
|
7
|
+
import { requestJson } from '../request.ts'
|
|
7
8
|
import { AttributionGraph } from './AttributionGraph.tsx'
|
|
8
9
|
import { CaseView } from './CaseView.tsx'
|
|
9
10
|
import { EventView } from './EventView.tsx'
|
|
@@ -35,6 +36,16 @@ interface DiagnosisPayload {
|
|
|
35
36
|
canLoadSample?: boolean
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
/** 客户端只消费搜索返回的这几个字段,不直接依赖服务端 KnowledgeCard 模块。 */
|
|
40
|
+
interface EvidenceWikiCard {
|
|
41
|
+
entityId: string
|
|
42
|
+
entityType: string
|
|
43
|
+
title: string
|
|
44
|
+
summary?: string
|
|
45
|
+
content: string
|
|
46
|
+
metadata?: Record<string, unknown>
|
|
47
|
+
}
|
|
48
|
+
|
|
38
49
|
/** 服务端的三种来源 → 徽标上那句人话。 */
|
|
39
50
|
function sourceLabelOf(source: string | undefined): string {
|
|
40
51
|
if (source === 'cards') return '可复用诊断卡片'
|
|
@@ -43,7 +54,7 @@ function sourceLabelOf(source: string | undefined): string {
|
|
|
43
54
|
return '还没有排查知识'
|
|
44
55
|
}
|
|
45
56
|
|
|
46
|
-
type SubView = 'graph' | 'case' | 'event' | 'run'
|
|
57
|
+
type SubView = 'graph' | 'case' | 'event' | 'run' | 'knowledge'
|
|
47
58
|
|
|
48
59
|
function normalizeQuestion(value: string): string {
|
|
49
60
|
return value.toLocaleLowerCase().replace(/[^\p{L}\p{N}]+/gu, '')
|
|
@@ -80,15 +91,19 @@ function symptomMatchOf(question: string | undefined, symptoms: readonly Attribu
|
|
|
80
91
|
* - 画布左上 = 操作(四组按动线顺序);右上 = 读法(四个开关同排);
|
|
81
92
|
* 左下 = 读数(统计 + 来源);底中 = 搜索;右侧 = 图例正文 / 节点详情。
|
|
82
93
|
*/
|
|
83
|
-
export function AttributionTab({ visible, activeQuestion, onOpenKnowledge }: {
|
|
94
|
+
export function AttributionTab({ visible, activeQuestion, kbId, onOpenKnowledge, onOpenWiki }: {
|
|
84
95
|
visible: boolean
|
|
85
96
|
/** 当前会话最近一条用户消息;仅精确匹配到唯一现象时自动高亮。 */
|
|
86
97
|
activeQuestion?: string
|
|
98
|
+
/** 与知识库分栏当前选中的库保持一致。 */
|
|
99
|
+
kbId?: string
|
|
87
100
|
/**
|
|
88
101
|
* 切到「知识库」那一栏。空态的主操作要用它 —— 排查知识是从来源与卡片编译来的,
|
|
89
102
|
* 空的时候该把人送去**录知识的那一栏**,而不是让人在这一栏里等。
|
|
90
103
|
*/
|
|
91
104
|
onOpenKnowledge?: () => void
|
|
105
|
+
/** 打开召回卡片的原 Wiki。 */
|
|
106
|
+
onOpenWiki?: (card: Pick<EvidenceWikiCard, 'entityId' | 'entityType'>) => void
|
|
92
107
|
}): ReactNode {
|
|
93
108
|
const [subView, setSubView] = useState<SubView>('graph')
|
|
94
109
|
/** 默认关系图:空问题态先看完整排查关系。 */
|
|
@@ -211,7 +226,8 @@ export function AttributionTab({ visible, activeQuestion, onOpenKnowledge }: {
|
|
|
211
226
|
setPhase('loading')
|
|
212
227
|
void (async () => {
|
|
213
228
|
try {
|
|
214
|
-
const
|
|
229
|
+
const query = kbId ? `?kb=${encodeURIComponent(kbId)}` : ''
|
|
230
|
+
const response = await fetch(`/diagnosis/graph${query}`)
|
|
215
231
|
if (!response.ok) throw new Error(`HTTP ${response.status}`)
|
|
216
232
|
const body = await response.json() as { data?: DiagnosisPayload }
|
|
217
233
|
if (cancelled) return
|
|
@@ -222,7 +238,7 @@ export function AttributionTab({ visible, activeQuestion, onOpenKnowledge }: {
|
|
|
222
238
|
}
|
|
223
239
|
})()
|
|
224
240
|
return () => { cancelled = true }
|
|
225
|
-
}, [visible, reloadToken, applyPayload])
|
|
241
|
+
}, [visible, reloadToken, applyPayload, kbId])
|
|
226
242
|
|
|
227
243
|
/**
|
|
228
244
|
* 载入内置示例:**显式的一次写**,将来源记录与可复用诊断卡写入当前知识库。
|
|
@@ -232,7 +248,8 @@ export function AttributionTab({ visible, activeQuestion, onOpenKnowledge }: {
|
|
|
232
248
|
const loadSample = async (): Promise<void> => {
|
|
233
249
|
setSampleBusy(true); setSampleError(null)
|
|
234
250
|
try {
|
|
235
|
-
const
|
|
251
|
+
const query = kbId ? `?kb=${encodeURIComponent(kbId)}` : ''
|
|
252
|
+
const response = await fetch(`/diagnosis/sample${query}`, { method: 'POST' })
|
|
236
253
|
const body = await response.json() as { ok?: boolean; data?: DiagnosisPayload; error?: { message?: string } }
|
|
237
254
|
if (!response.ok || body.ok === false) throw new Error(body.error?.message ?? `HTTP ${response.status}`)
|
|
238
255
|
applyPayload(body.data ?? {})
|
|
@@ -275,6 +292,8 @@ export function AttributionTab({ visible, activeQuestion, onOpenKnowledge }: {
|
|
|
275
292
|
const symptomCounts = useMemo(() => symptomCountByProblemClass(data.nodes), [data])
|
|
276
293
|
const symptoms = useMemo(() => data.nodes.filter(node => node.kind === 'symptom'), [data])
|
|
277
294
|
const matchedSymptom = useMemo(() => symptomMatchOf(activeQuestion, symptoms), [activeQuestion, symptoms])
|
|
295
|
+
const selectedSymptom = selectedId ? symptoms.find(node => node.id === selectedId) : undefined
|
|
296
|
+
const evidenceQuery = activeQuestion?.trim() || selectedSymptom?.title || matchedSymptom?.title || ''
|
|
278
297
|
useEffect(() => {
|
|
279
298
|
// 新会话问题或新载入的图谱:只在唯一、明确匹配时预选;其余情况保持全图。
|
|
280
299
|
setSelectedId(matchedSymptom?.id)
|
|
@@ -434,6 +453,10 @@ export function AttributionTab({ visible, activeQuestion, onOpenKnowledge }: {
|
|
|
434
453
|
hint: '剪枝:按优先分排出下一步该查什么,每个检查点都有主操作。',
|
|
435
454
|
meta: String(candidateCount),
|
|
436
455
|
},
|
|
456
|
+
{
|
|
457
|
+
id: 'knowledge', label: '知识', icon: Library,
|
|
458
|
+
hint: '依据:按当前问题召回 Wiki Fact / Wiki Way;可打开原 Wiki 核对。',
|
|
459
|
+
},
|
|
437
460
|
]
|
|
438
461
|
|
|
439
462
|
const layoutHint = layout === 'line'
|
|
@@ -517,6 +540,7 @@ export function AttributionTab({ visible, activeQuestion, onOpenKnowledge }: {
|
|
|
517
540
|
/>}
|
|
518
541
|
{...(subView === 'case' ? { layer: <CaseView cases={cases} activeId={activeCase} onActiveChange={setActiveCase} /> } : {})}
|
|
519
542
|
{...(subView === 'event' ? { layer: <EventView events={events} /> } : {})}
|
|
543
|
+
{...(subView === 'knowledge' ? { layer: <KnowledgeEvidenceView query={evidenceQuery} kbId={kbId} onOpenWiki={onOpenWiki} /> } : {})}
|
|
520
544
|
{...(subView === 'run' ? {
|
|
521
545
|
layer: <RunView
|
|
522
546
|
data={data}
|
|
@@ -621,8 +645,10 @@ export function AttributionTab({ visible, activeQuestion, onOpenKnowledge }: {
|
|
|
621
645
|
if (phase !== 'ready' || (data.nodes.length === 0 && events.length === 0)) {
|
|
622
646
|
return <div className="flex min-h-0 flex-1 flex-col gap-1.5">
|
|
623
647
|
{helpError && <AttributionHelpNotice message={helpError} onDismiss={() => setHelpError(null)} />}
|
|
624
|
-
<div className="flex min-w-0 items-center justify-
|
|
625
|
-
|
|
648
|
+
<div className="flex min-w-0 flex-wrap items-center justify-between gap-1.5">{switcher}{helpHint}</div>
|
|
649
|
+
{subView === 'knowledge'
|
|
650
|
+
? <div className="min-h-0 flex-1 overflow-y-auto px-2"><KnowledgeEvidenceView query={evidenceQuery} kbId={kbId} onOpenWiki={onOpenWiki} /></div>
|
|
651
|
+
: <AttributionEmpty
|
|
626
652
|
phase={phase}
|
|
627
653
|
canLoadSample={canLoadSample}
|
|
628
654
|
busy={sampleBusy}
|
|
@@ -630,7 +656,7 @@ export function AttributionTab({ visible, activeQuestion, onOpenKnowledge }: {
|
|
|
630
656
|
onLoadSample={() => void loadSample()}
|
|
631
657
|
onRetry={() => setReloadToken(token => token + 1)}
|
|
632
658
|
{...(onOpenKnowledge ? { onOpenKnowledge } : {})}
|
|
633
|
-
/>
|
|
659
|
+
/>}
|
|
634
660
|
</div>
|
|
635
661
|
}
|
|
636
662
|
|
|
@@ -731,6 +757,111 @@ function AttributionHelpNotice({ message, onDismiss }: { message: string; onDism
|
|
|
731
757
|
</div>
|
|
732
758
|
}
|
|
733
759
|
|
|
760
|
+
/**
|
|
761
|
+
* 只读查看当前问题从知识库召回的 Wiki Fact / Wiki Way。
|
|
762
|
+
*
|
|
763
|
+
* 这里消费正式的 `/knowledge/search` 结果,不再保存第二份问题定位知识。召回顺序保留,
|
|
764
|
+
* 每条结果都能跳回原 Wiki;正文默认收起,避免把完整 Wiki 挤进窄栏。
|
|
765
|
+
*/
|
|
766
|
+
function KnowledgeEvidenceView({ query, kbId, onOpenWiki }: {
|
|
767
|
+
query: string
|
|
768
|
+
kbId?: string
|
|
769
|
+
onOpenWiki?: (card: Pick<EvidenceWikiCard, 'entityId' | 'entityType'>) => void
|
|
770
|
+
}): ReactNode {
|
|
771
|
+
const [items, setItems] = useState<EvidenceWikiCard[]>([])
|
|
772
|
+
const [phase, setPhase] = useState<'idle' | 'loading' | 'ready' | 'error'>('idle')
|
|
773
|
+
const [error, setError] = useState('')
|
|
774
|
+
const [retry, setRetry] = useState(0)
|
|
775
|
+
const [expanded, setExpanded] = useState<ReadonlySet<string>>(new Set())
|
|
776
|
+
|
|
777
|
+
useEffect(() => {
|
|
778
|
+
const trimmed = query.trim()
|
|
779
|
+
if (!trimmed) {
|
|
780
|
+
setItems([])
|
|
781
|
+
setPhase('idle')
|
|
782
|
+
setError('')
|
|
783
|
+
return undefined
|
|
784
|
+
}
|
|
785
|
+
let cancelled = false
|
|
786
|
+
setPhase('loading')
|
|
787
|
+
setItems([])
|
|
788
|
+
setExpanded(new Set())
|
|
789
|
+
setError('')
|
|
790
|
+
const params = new URLSearchParams({ query: trimmed, top_k: '30', top_n: '20' })
|
|
791
|
+
if (kbId) params.set('kb', kbId)
|
|
792
|
+
void requestJson<{ wiki?: EvidenceWikiCard[] }>(`/knowledge/search?${params.toString()}`)
|
|
793
|
+
.then(payload => {
|
|
794
|
+
if (cancelled) return
|
|
795
|
+
setItems((payload.data?.wiki ?? []).filter(card => card.metadata?.wiki_kind === 'fact' || card.metadata?.wiki_kind === 'way'))
|
|
796
|
+
setPhase('ready')
|
|
797
|
+
})
|
|
798
|
+
.catch(cause => {
|
|
799
|
+
if (cancelled) return
|
|
800
|
+
setItems([])
|
|
801
|
+
setError(cause instanceof Error ? cause.message : String(cause))
|
|
802
|
+
setPhase('error')
|
|
803
|
+
})
|
|
804
|
+
return () => { cancelled = true }
|
|
805
|
+
}, [query, kbId, retry])
|
|
806
|
+
|
|
807
|
+
const toggleExpanded = (id: string): void => setExpanded(current => {
|
|
808
|
+
const next = new Set(current)
|
|
809
|
+
if (next.has(id)) next.delete(id)
|
|
810
|
+
else next.add(id)
|
|
811
|
+
return next
|
|
812
|
+
})
|
|
813
|
+
|
|
814
|
+
return <section className="mx-auto flex w-full max-w-3xl flex-col gap-2 py-1" aria-label="知识库召回依据">
|
|
815
|
+
<header className="border-b border-tita-line pb-2">
|
|
816
|
+
<h3 className="text-[13px] font-semibold text-tita-ink">问题定位相关 Wiki</h3>
|
|
817
|
+
<p className="mt-1 text-[10.5px] leading-relaxed text-tita-ink-muted">
|
|
818
|
+
按当前问题召回已有 Wiki Fact / Wiki Way。这里只读展示,不会创建或修改知识。
|
|
819
|
+
</p>
|
|
820
|
+
{query.trim() && <p className="mt-1 line-clamp-2 break-words text-[10.5px] leading-relaxed text-tita-ink" title={query}>{query}</p>}
|
|
821
|
+
</header>
|
|
822
|
+
|
|
823
|
+
{phase === 'idle' && <p className="py-5 text-center text-[11px] text-tita-ink-muted">
|
|
824
|
+
当前没有可用于召回的问题。请在会话中提出问题,或先在图谱里选择一个问题现象。
|
|
825
|
+
</p>}
|
|
826
|
+
{phase === 'loading' && <p role="status" className="py-5 text-center text-[11px] text-tita-ink-muted">
|
|
827
|
+
正在召回 Wiki Fact / Wiki Way…
|
|
828
|
+
</p>}
|
|
829
|
+
{phase === 'error' && <div className="flex flex-col items-start gap-2 py-3">
|
|
830
|
+
<p role="alert" className="text-[10.5px] leading-relaxed text-tita-danger">知识召回失败:{error}</p>
|
|
831
|
+
<Button size="sm" variant="outline" className="h-[26px] min-h-[26px] px-2 text-[10.5px]" onClick={() => setRetry(value => value + 1)}>重试召回</Button>
|
|
832
|
+
</div>}
|
|
833
|
+
{phase === 'ready' && items.length === 0 && <p className="py-5 text-center text-[11px] leading-relaxed text-tita-ink-muted">
|
|
834
|
+
当前问题没有召回到 Wiki Fact 或 Wiki Way。可以补充问题现象,或到知识库检查相关 Wiki 是否已编译。
|
|
835
|
+
</p>}
|
|
836
|
+
{items.length > 0 && <ol className="divide-y divide-tita-line">
|
|
837
|
+
{items.map(card => {
|
|
838
|
+
const wikiKind = card.metadata?.wiki_kind === 'way' ? 'way' : 'fact'
|
|
839
|
+
const open = expanded.has(card.entityId)
|
|
840
|
+
return <li key={`${card.entityType}:${card.entityId}`} className="py-2.5 first:pt-1">
|
|
841
|
+
<article className="flex min-w-0 flex-col gap-1.5">
|
|
842
|
+
<header className="flex min-w-0 items-start gap-1.5">
|
|
843
|
+
<Badge tone={wikiKind === 'way' ? 'success' : 'neutral'} className="mt-0.5 shrink-0">
|
|
844
|
+
{wikiKind === 'way' ? 'Wiki Way' : 'Wiki Fact'}
|
|
845
|
+
</Badge>
|
|
846
|
+
<h4 className="min-w-0 break-words text-[11.5px] font-semibold leading-snug text-tita-ink">{card.title}</h4>
|
|
847
|
+
</header>
|
|
848
|
+
{card.summary && <p className="break-words text-[10.5px] leading-relaxed text-tita-ink-muted">{card.summary}</p>}
|
|
849
|
+
<div className="flex flex-wrap items-center gap-1">
|
|
850
|
+
<Button size="sm" variant="ghost" className="h-[25px] min-h-[25px] px-1.5 text-[10px]" onClick={() => toggleExpanded(card.entityId)}>
|
|
851
|
+
{open ? '收起 Wiki 正文' : '展开 Wiki 正文'}
|
|
852
|
+
</Button>
|
|
853
|
+
{onOpenWiki && <Button size="sm" variant="outline" className="h-[25px] min-h-[25px] px-1.5 text-[10px]" onClick={() => onOpenWiki(card)}>
|
|
854
|
+
打开原 Wiki
|
|
855
|
+
</Button>}
|
|
856
|
+
</div>
|
|
857
|
+
{open && <pre className="max-h-[36vh] overflow-auto overscroll-contain whitespace-pre-wrap break-words rounded-md bg-tita-surface-muted p-2 text-[10px] leading-relaxed text-tita-ink">{card.content}</pre>}
|
|
858
|
+
</article>
|
|
859
|
+
</li>
|
|
860
|
+
})}
|
|
861
|
+
</ol>}
|
|
862
|
+
</section>
|
|
863
|
+
}
|
|
864
|
+
|
|
734
865
|
/** 大类 id → 它下面的子类 id。取消大类时要把这些子类一起撤掉。 */
|
|
735
866
|
const PROBLEM_CLASS_IDS_TO_SUBCLASSES: Record<string, string[]> = Object.fromEntries(
|
|
736
867
|
PROBLEM_CLASSES.map(item => [item.id, item.subclasses.map(sub => sub.id)]),
|
package/src/client/pages.css
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* 通用整页(抽取试跑 /
|
|
2
|
+
* 通用整页(抽取试跑 / 编译链路)的页面样式。
|
|
3
3
|
*
|
|
4
4
|
* 它和 `doc-page.css` 是同一种东西:一份**单独的浏览器文档**,没有 DSH 外壳、没有侧栏
|
|
5
5
|
* 那 380px 的宽度限制。所以这里的决定围绕两件事:
|
|
6
6
|
*
|
|
7
7
|
* 1. **一条常驻的窄条**回答「我在哪一页、看的是哪个库」—— 整页可以滚很久,标题滚走了
|
|
8
8
|
* 就只剩内容,而这三页都需要知道自己在哪。
|
|
9
|
-
* 2.
|
|
9
|
+
* 2. **两条内容宽度**:说明类(编译链路)是读排版,约 860px 就够;试跑报告
|
|
10
10
|
* 里有一张矩阵,给它 1180px。宽度是**内容**的属性,所以按页给,不按壳给。
|
|
11
11
|
*
|
|
12
12
|
* 颜色全部走共享令牌(`@tita-agent/ui/styles.css` 带进来的那一套)。插件自己的
|
package/src/client/pages.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 通用整页:**抽取试跑 / 抽取轨迹 / 编译和召回链路 /
|
|
2
|
+
* 通用整页:**抽取试跑 / 抽取轨迹 / 编译和召回链路 / Agent 方案说明**。
|
|
3
3
|
*
|
|
4
4
|
* 这些页面共用一个 bundle、一条宿主路由(`/knowledge/pages/<...>`),因为它们是同一种东西:
|
|
5
5
|
* 「在面板里放不下、也不该挤进 380px 侧栏」的内容。**它们开在真的浏览器窗口里** ——
|
|
@@ -15,9 +15,8 @@
|
|
|
15
15
|
* - **抽取轨迹**:同一份理由的另一面 —— 报告几十 KB,2026-10-06 从抽屉里就地展开
|
|
16
16
|
* 改成开整页(用户点名「点击要在浏览器打开」)。
|
|
17
17
|
* - **编译和召回链路**:按架构说明 SourceRaw 如何变成 Wiki,以及一次 Query 如何变成 Agent Context。
|
|
18
|
-
* - **意图的逻辑**:同上,讲的是词表为什么这么设计。
|
|
19
18
|
*
|
|
20
|
-
*
|
|
19
|
+
* 说明类内容原来住在表单里(编译流程对话框的说明页)。抽成
|
|
21
20
|
* 整页之后,**每个容器只装一种东西**:浮层装要动手的,整页装要读的。
|
|
22
21
|
*
|
|
23
22
|
* ## 地址形状
|
|
@@ -26,7 +25,6 @@
|
|
|
26
25
|
* /knowledge/pages/trial?kb=<知识库 id> 抽取试跑
|
|
27
26
|
* /knowledge/pages/run?kb=<库 id>&run=<轨迹 id> 一次抽取的完整报告
|
|
28
27
|
* /knowledge/pages/help/compile 编译和召回链路
|
|
29
|
-
* /knowledge/pages/help/intent 意图的逻辑
|
|
30
28
|
* /knowledge/pages/help/attribution 智能归因事件
|
|
31
29
|
* /knowledge/pages/help/overview?node=... TitaAgent 完整架构和逻辑说明
|
|
32
30
|
* ```
|
|
@@ -40,7 +38,6 @@ import '@tita-agent/ui/styles.css'
|
|
|
40
38
|
import './styles.css'
|
|
41
39
|
import './pages.css'
|
|
42
40
|
import { AttributionEventGuide } from './AttributionEventGuide.tsx'
|
|
43
|
-
import { IntentLogic } from './IntentLogic.tsx'
|
|
44
41
|
import { TabGuidePage } from './TabGuide.tsx'
|
|
45
42
|
import { WikiFlowPanel } from './WikiFlowPanel.tsx'
|
|
46
43
|
import { WikiTrialRun, type TrialDocumentOption } from './WikiTrialRun.tsx'
|
|
@@ -59,7 +56,7 @@ type PageRoute =
|
|
|
59
56
|
| { kind: 'trial'; kb: string }
|
|
60
57
|
| { kind: 'run'; kb: string; run: string }
|
|
61
58
|
| { kind: 'tasks'; kb: string }
|
|
62
|
-
| { kind: 'help'; topic: '
|
|
59
|
+
| { kind: 'help'; topic: 'compile' | 'attribution' | 'overview'; node?: string }
|
|
63
60
|
| { kind: 'unknown' }
|
|
64
61
|
|
|
65
62
|
/**
|
|
@@ -78,7 +75,7 @@ function routeOf(location: Location): PageRoute {
|
|
|
78
75
|
if (parts[0] === 'help' && parts[1] === 'overview') {
|
|
79
76
|
return { kind: 'help', topic: 'overview', node: params.get('node') ?? undefined }
|
|
80
77
|
}
|
|
81
|
-
if (parts[0] === 'help' && (parts[1] === '
|
|
78
|
+
if (parts[0] === 'help' && (parts[1] === 'compile' || parts[1] === 'attribution')) {
|
|
82
79
|
return { kind: 'help', topic: parts[1] }
|
|
83
80
|
}
|
|
84
81
|
return { kind: 'unknown' }
|
|
@@ -256,9 +253,6 @@ function App(): ReactNode {
|
|
|
256
253
|
if (route.topic === 'overview') {
|
|
257
254
|
return <PageShell title="TitaAgent 完整架构" meta="从理解请求到持续改进的三层闭环" column="guide"><TabGuidePage initialNode={route.node} /></PageShell>
|
|
258
255
|
}
|
|
259
|
-
if (route.topic === 'intent') {
|
|
260
|
-
return <PageShell title="意图的逻辑" meta="这套词表为什么这样设计" column="help"><IntentLogic /></PageShell>
|
|
261
|
-
}
|
|
262
256
|
if (route.topic === 'compile') {
|
|
263
257
|
return <PageShell title="编译和召回链路" meta="信息编译为知识,然后才值得被召回使用" column="help"><WikiFlowPanel /></PageShell>
|
|
264
258
|
}
|
|
@@ -272,7 +266,6 @@ function App(): ReactNode {
|
|
|
272
266
|
<code>/knowledge/pages/run?kb=<知识库 id>&run=<轨迹 id></code> · 一次抽取的报告<br />
|
|
273
267
|
<code>/knowledge/pages/tasks?kb=<知识库 id></code> · 知识处理与运行记录<br />
|
|
274
268
|
<code>/knowledge/pages/help/compile</code> · 编译和召回链路<br />
|
|
275
|
-
<code>/knowledge/pages/help/intent</code> · 意图的逻辑<br />
|
|
276
269
|
<code>/knowledge/pages/help/attribution</code> · 智能归因事件<br />
|
|
277
270
|
<code>/knowledge/pages/help/overview?node=knowledge</code> · 五个 Tab 的方案说明</p>
|
|
278
271
|
<p>它们都由面板上的入口打开,不需要手写。</p>
|
package/src/client/styles.css
CHANGED
|
@@ -672,7 +672,7 @@ html[data-platform='darwin'] [data-window-drag] [data-conversation-tabs] > [role
|
|
|
672
672
|
同一个图标有两种含义时,光标是零成本的那条线索。 */
|
|
673
673
|
.tita-infoHintAction { cursor: pointer; }
|
|
674
674
|
.tita-infoHintAction:hover { color: var(--tita-ink); }
|
|
675
|
-
/* 说明靠右(2026-10-05
|
|
675
|
+
/* 说明靠右(2026-10-05):知识库工具栏的排版规矩 —— 动作一组靠左,
|
|
676
676
|
`?` 自己站在最右端。`margin-left: auto` 在换行时也成立:它落到第二行,仍然右对齐。 */
|
|
677
677
|
.tita-kbToolbarHint { flex: 0 0 auto; margin-left: auto; }
|
|
678
678
|
/* 标题里的说明入口(设置小节同理):与标题文字留一格气,
|
|
@@ -852,7 +852,7 @@ html[data-platform='darwin'] [data-window-drag] [data-conversation-tabs] > [role
|
|
|
852
852
|
/*
|
|
853
853
|
* 行右侧的次要事实(类型 · 时间 / N 个来源 / N 张)。
|
|
854
854
|
*
|
|
855
|
-
* 0042
|
|
855
|
+
* 0042 补的定义:这个类名被五处用着(卡片行、原文抽屉的编译产物、
|
|
856
856
|
* 类型定义、卡片类型编辑器)却**一直没有样式** —— 于是它只传递了一个名字,
|
|
857
857
|
* 右对齐与「比主文字轻」全靠父级的 flex 碰运气。一次定义到位。
|
|
858
858
|
*/
|
|
@@ -1014,91 +1014,11 @@ html[data-platform='darwin'] [data-window-drag] [data-conversation-tabs] > [role
|
|
|
1014
1014
|
.tita-kbInlineImage { margin: 10px 0; text-align: center; }
|
|
1015
1015
|
.tita-kbInlineImage img { display: block; width: auto; max-width: 100%; max-height: 360px; margin: 0 auto; object-fit: contain; }
|
|
1016
1016
|
.tita-kbPre { max-height: 320px; margin: 0; border: 1px solid var(--tita-line); border-radius: 9px; background: var(--tita-surface); color: var(--tita-ink); padding: 10px; overflow: auto; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10.5px; line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
1017
|
-
/* 文本视图的 `<pre>` 自带 max-height
|
|
1017
|
+
/* 文本视图的 `<pre>` 自带 max-height(类型定义视图也会用),进了正文块之后
|
|
1018
1018
|
由外层那一层负责滚 —— 同一件事不画两条滚动条。 */
|
|
1019
1019
|
.tita-drawerBody .tita-kbPre { max-height: none; border: 0; border-radius: 0; background: transparent; padding: 0; }
|
|
1020
1020
|
|
|
1021
|
-
|
|
1022
|
-
.tita-intentTop { display: flex; min-width: 0; align-items: flex-start; justify-content: space-between; gap: 12px; border-bottom: 1px solid var(--tita-line); padding-bottom: 12px; }
|
|
1023
|
-
.tita-intentTop h2 { margin: 0; color: var(--tita-ink); font-size: 17px; font-weight: 780; letter-spacing: -.025em; line-height: 1.3; }
|
|
1024
|
-
.tita-intentTop p { max-width: 320px; margin: 5px 0 0; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.6; }
|
|
1025
|
-
.tita-intentControls { display: grid; gap: 8px; border-bottom: 1px solid var(--tita-line); padding-bottom: 12px; }
|
|
1026
|
-
.tita-intentPrimaryFilter { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; min-width: 0; align-items: center; gap: 7px; }
|
|
1027
|
-
.tita-intentFilterHeading { display: grid; min-width: 42px; gap: 1px; }
|
|
1028
|
-
.tita-intentFilterHeading small { color: var(--tita-primary); font-size: 9px; font-weight: 700; white-space: nowrap; }
|
|
1029
|
-
.tita-intentFilterCount { color: var(--tita-ink-faint); font-size: 10px; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
1030
|
-
.tita-intentSecondaryFilters { display: grid; grid-template-columns: auto minmax(0, .7fr) auto minmax(0, 1.3fr) auto; min-width: 0; align-items: center; gap: 7px; }
|
|
1031
|
-
.tita-intentControlLabel { color: var(--tita-ink-muted); font-size: 11px; white-space: nowrap; }
|
|
1032
|
-
.tita-intentFilterSummary { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; gap: 5px 7px; border-radius: 8px; background: var(--tita-surface-muted); padding: 6px 8px; color: var(--tita-ink-faint); font-size: 10px; line-height: 1.4; }
|
|
1033
|
-
.tita-intentFilterSummary > span { color: var(--tita-ink-muted); font-weight: 650; }
|
|
1034
|
-
.tita-intentSectionHead { display: flex; min-width: 0; align-items: flex-start; justify-content: space-between; gap: 12px; }
|
|
1035
|
-
.tita-intentSectionHead h3 { margin: 0; color: var(--tita-ink); font-size: 13.5px; font-weight: 760; }
|
|
1036
|
-
.tita-intentSectionSubhead { margin: 3px 0 0; color: var(--tita-ink-faint); font-size: 10px; line-height: 1.45; }
|
|
1037
|
-
.tita-intentSectionHead > time, .tita-intentSectionCount { flex: 0 0 auto; color: var(--tita-ink-faint); font-size: 10px; font-variant-numeric: tabular-nums; }
|
|
1038
|
-
.tita-intentEyebrow { display: inline-flex; align-items: center; gap: 5px; color: var(--tita-ink-faint); font-size: 9.5px; font-weight: 750; letter-spacing: .08em; text-transform: uppercase; }
|
|
1039
|
-
.tita-intentInterpretation { display: grid; gap: 11px; border-color: var(--tita-line-strong); padding: 14px 15px; }
|
|
1040
|
-
.tita-intentUserBlock { min-width: 0; border: 1px solid var(--tita-line); border-radius: 9px; background: var(--tita-surface-muted); padding: 10px 11px; }
|
|
1041
|
-
.tita-intentStepLabel { display: block; color: var(--tita-ink-faint); font-size: 10px; font-weight: 700; }
|
|
1042
|
-
.tita-intentUserInput { margin: 5px 0 0; color: var(--tita-ink); font-size: 12px; line-height: 1.65; overflow-wrap: anywhere; }
|
|
1043
|
-
.tita-intentVerdict { min-width: 0; border: 1px solid var(--tita-primary); border-radius: 9px; background: var(--tita-primary-soft); padding: 11px; }
|
|
1044
|
-
.tita-intentVerdictHead { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
1045
|
-
.tita-intentResultTitle { margin: 5px 0 0; color: var(--tita-ink); font-size: 15px; font-weight: 780; letter-spacing: -.02em; line-height: 1.35; overflow-wrap: anywhere; }
|
|
1046
|
-
.tita-intentResultText { margin: 4px 0 0; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.6; }
|
|
1047
|
-
.tita-intentResultTags { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; gap: 5px; margin-top: 8px; }
|
|
1048
|
-
.tita-intentLowConfidence { color: var(--tita-tone-amber); font-size: 10px; font-weight: 650; }
|
|
1049
|
-
.tita-intentMetaGrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
|
|
1050
|
-
.tita-intentMetaCell { display: grid; min-width: 0; gap: 3px; border-top: 1px solid var(--tita-line); padding-top: 8px; }
|
|
1051
|
-
.tita-intentMetaCell span { color: var(--tita-ink-faint); font-size: 9.5px; }
|
|
1052
|
-
.tita-intentMetaCell strong { color: var(--tita-ink-muted); font-size: 10.5px; font-weight: 650; overflow-wrap: anywhere; }
|
|
1053
|
-
.tita-intentDiagnostics { border-top: 1px solid var(--tita-line); padding-top: 9px; }
|
|
1054
|
-
.tita-intentDiagnostics summary { color: var(--tita-ink-faint); font-size: 10px; cursor: pointer; list-style-position: inside; }
|
|
1055
|
-
.tita-intentDiagnostics summary:hover { color: var(--tita-ink-muted); }
|
|
1056
|
-
.tita-intentDiagnosticsBody { display: grid; gap: 5px; margin: 8px 0 0 15px; color: var(--tita-ink-faint); font-size: 10px; line-height: 1.5; }
|
|
1057
|
-
.tita-intentDiagnosticsBody code { border-radius: 4px; background: var(--tita-surface-muted); padding: 1px 4px; color: var(--tita-tone-violet); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 9.5px; }
|
|
1058
|
-
.tita-intentEmptyCard { display: grid; justify-items: center; gap: 6px; padding: 25px 15px; text-align: center; }
|
|
1059
|
-
.tita-intentEmptyCard > svg { color: var(--tita-ink-faint); }
|
|
1060
|
-
.tita-intentEmptyCard strong { color: var(--tita-ink); font-size: 12px; }
|
|
1061
|
-
.tita-intentEmptyCard p, .tita-intentEmpty { margin: 0; color: var(--tita-ink-faint); font-size: 11px; line-height: 1.6; }
|
|
1062
|
-
.tita-intentHistory { border-top: 1px solid var(--tita-line); padding-top: 13px; }
|
|
1063
|
-
.tita-intentHistoryList { display: grid; margin: 10px 0 0; padding: 0; list-style: none; }
|
|
1064
|
-
.tita-intentHistoryItem { display: grid; min-width: 0; gap: 4px; border-bottom: 1px solid var(--tita-line); padding: 10px 0 11px; }
|
|
1065
|
-
.tita-intentHistoryItem:first-child { padding-top: 0; }
|
|
1066
|
-
.tita-intentHistoryItem:last-child { border-bottom: 0; }
|
|
1067
|
-
.tita-intentHistoryPrimary { display: flex; min-width: 0; align-items: baseline; justify-content: space-between; gap: 9px; }
|
|
1068
|
-
.tita-intentHistoryPrimary strong { min-width: 0; color: var(--tita-ink); font-size: 12px; font-weight: 720; overflow-wrap: anywhere; }
|
|
1069
|
-
.tita-intentHistoryPrimary time { display: inline-flex; flex: 0 0 auto; align-items: center; gap: 4px; color: var(--tita-ink-muted); font-size: 10px; font-variant-numeric: tabular-nums; }
|
|
1070
|
-
.tita-intentHistoryMeta { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; gap: 4px 9px; color: var(--tita-ink-faint); font-size: 10px; }
|
|
1071
|
-
.tita-intentHistoryMeta span + span::before { content: '·'; margin-right: 9px; color: var(--tita-line-strong); }
|
|
1072
|
-
.tita-intentHistoryWarning { color: var(--tita-tone-amber); }
|
|
1073
|
-
.tita-intentHistoryItem p { margin: 0; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.55; overflow-wrap: anywhere; }
|
|
1074
|
-
.tita-intentFooter { display: flex; flex-wrap: wrap; gap: 5px 12px; border-top: 1px solid var(--tita-line); padding-top: 10px; color: var(--tita-ink-faint); font-size: 10px; }
|
|
1075
|
-
.tita-intentFooter span + span::before { content: '·'; margin-right: 12px; color: var(--tita-line-strong); }
|
|
1076
|
-
|
|
1077
|
-
/* 「意图的逻辑」:说明性内容,读的是排版而不是控件。它现在是一份整页
|
|
1078
|
-
(`/knowledge/pages/help/intent`),所以这几条规则同时服务面板与那一页。 */
|
|
1079
|
-
.tita-intentDocList { display: grid; margin: 3px 0 0; padding-left: 18px; gap: 5px; color: var(--tita-ink); font-size: 11.5px; line-height: 1.75; }
|
|
1080
|
-
.tita-intentDocList b { color: var(--tita-ink); }
|
|
1081
|
-
.tita-intentDocList code, .tita-kbHint code { border-radius: 4px; background: var(--tita-surface-muted); padding: 0 4px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10.5px; }
|
|
1082
|
-
.tita-intentDocGrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 8px; margin-top: 10px; }
|
|
1083
|
-
.tita-intentDocGrid > div { display: grid; gap: 3px; border: 1px solid var(--tita-line); border-radius: 9px; background: var(--tita-surface); padding: 9px 11px; }
|
|
1084
|
-
.tita-intentDocGrid strong { color: var(--tita-ink); font-size: 11.5px; }
|
|
1085
|
-
.tita-intentDocGrid span { color: var(--tita-ink-faint); font-size: 10.5px; line-height: 1.6; }
|
|
1086
|
-
.tita-intentDocTableWrap { overflow-x: auto; margin-top: 8px; }
|
|
1087
|
-
.tita-intentDocTable { width: 100%; border-collapse: collapse; font-size: 11px; }
|
|
1088
|
-
.tita-intentDocTable th { border-bottom: 1px solid var(--tita-line); color: var(--tita-ink-muted); padding: 6px 8px; font-weight: 700; text-align: left; white-space: nowrap; }
|
|
1089
|
-
.tita-intentDocTable td { border-bottom: 1px solid var(--tita-surface-muted); color: var(--tita-ink); padding: 6px 8px; line-height: 1.65; vertical-align: top; }
|
|
1090
|
-
.tita-intentDocTable td:first-child code { border-radius: 4px; background: var(--tita-surface-muted); padding: 1px 4px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10.5px; }
|
|
1091
|
-
.tita-intentDocTable td:nth-child(2) { color: var(--tita-tone-green); text-align: center; }
|
|
1092
|
-
.tita-intentPaperList { display: grid; margin: 8px 0 0; padding: 0; list-style: none; gap: 10px; }
|
|
1093
|
-
.tita-intentPaperList > li { border: 1px solid var(--tita-line); border-radius: 8px; background: var(--tita-surface); padding: 9px 11px; }
|
|
1094
|
-
.tita-intentPaperList a { color: var(--tita-primary); font-size: 12px; font-weight: 640; text-decoration: none; }
|
|
1095
|
-
.tita-intentPaperList a:hover { text-decoration: underline; }
|
|
1096
|
-
.tita-intentPaperMeta { display: block; margin-top: 2px; color: var(--tita-ink-faint); font-size: 10px; }
|
|
1097
|
-
.tita-intentPaperList p { margin: 6px 0 0; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.75; }
|
|
1098
|
-
.tita-intentPaperNote { color: var(--tita-ink-faint) !important; font-size: 10.5px !important; }
|
|
1099
|
-
.tita-intentOasLegend { display: flex; flex-wrap: wrap; align-items: center; gap: 5px 12px; margin-top: 9px; color: var(--tita-ink-faint); font-size: 10px; }
|
|
1100
|
-
.tita-intentOasLegend span { display: inline-flex; align-items: center; gap: 4px; }
|
|
1101
|
-
.tita-intentOasLegend svg { color: var(--tita-primary); }
|
|
1021
|
+
.tita-kbHint code { border-radius: 4px; background: var(--tita-surface-muted); padding: 0 4px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10.5px; }
|
|
1102
1022
|
|
|
1103
1023
|
/* Wiki 编译与召回流程:总览图 + 当前选中节点说明。 */
|
|
1104
1024
|
.tita-wikiFlowSection + .tita-wikiFlowSection { margin-top: 30px; border-top: 1px solid var(--tita-line); padding-top: 24px; }
|
|
@@ -1414,6 +1334,7 @@ html[data-platform='darwin'] [data-window-drag] [data-conversation-tabs] > [role
|
|
|
1414
1334
|
.tita-trialMatrixGrid tbody th > .tita-trialMatrixAxisPick { margin-bottom: 3px; }
|
|
1415
1335
|
.tita-trialMatrixObjectHead { text-align: center; }
|
|
1416
1336
|
.tita-trialMatrixLoose { display: block; width: 100%; max-width: 100%; margin: 0; border: 1px solid transparent; border-radius: 5px; color: var(--tita-ink-faint); padding: 0 3px; font: inherit; font-size: 9px; font-weight: 500; line-height: 1.5; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1337
|
+
.tita-trialMatrixLooseStack { display: grid; min-width: 0; justify-items: center; gap: 1px; }
|
|
1417
1338
|
.tita-trialMatrixLooseEmpty { display: block; height: 14px; }
|
|
1418
1339
|
.tita-trialMatrixLoosePick { border-color: var(--tita-line); background: none; cursor: pointer; }
|
|
1419
1340
|
.tita-trialMatrixLoosePick:hover { border-color: var(--tita-line-strong); color: var(--tita-ink-muted); }
|
|
@@ -1450,6 +1371,16 @@ html[data-platform='darwin'] [data-window-drag] [data-conversation-tabs] > [role
|
|
|
1450
1371
|
.tita-trialMatrixCellPick:focus-visible { outline: 2px solid var(--tita-focus-ring); outline-offset: 0; }
|
|
1451
1372
|
.tita-trialMatrixCellPickOn { border-color: var(--tita-primary); background: var(--tita-primary-soft); color: var(--tita-primary); }
|
|
1452
1373
|
.tita-trialMatrixCellValue { font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums; }
|
|
1374
|
+
.tita-trialMatrixCellStack { display: grid; min-width: 0; justify-items: center; gap: 1px; }
|
|
1375
|
+
.tita-trialMatrixCellStack .tita-trialMatrixCellPick,
|
|
1376
|
+
.tita-trialMatrixCellStack .tita-trialMatrixCellStatic { min-height: 20px; }
|
|
1377
|
+
.tita-trialMatrixBreakdown { display: flex; min-width: 0; flex-wrap: wrap; justify-content: center; gap: 2px; }
|
|
1378
|
+
.tita-trialMatrixBreakdownItem { display: inline-flex; align-items: center; justify-content: center; border: 1px solid transparent; border-radius: 4px; color: var(--tita-ink-muted); padding: 0 2px; font-size: 8px; font-weight: 650; line-height: 1.35; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
1379
|
+
.tita-trialMatrixBreakdownPick { background: transparent; cursor: pointer; }
|
|
1380
|
+
.tita-trialMatrixBreakdownPick:hover { border-color: var(--tita-line-strong); background: var(--tita-surface-muted); color: var(--tita-ink); }
|
|
1381
|
+
.tita-trialMatrixBreakdownPick:focus-visible { outline: 2px solid var(--tita-focus-ring); outline-offset: 1px; }
|
|
1382
|
+
.tita-trialMatrixBreakdownOn { border-color: var(--tita-primary); background: var(--tita-primary-soft); color: var(--tita-primary); }
|
|
1383
|
+
.tita-trialMatrixBreakdownOn:hover { background: var(--tita-primary-soft); color: var(--tita-primary); }
|
|
1453
1384
|
.tita-trialMatrixGroups { display: grid; gap: 0; }
|
|
1454
1385
|
.tita-trialMatrixGroupLine { display: block; min-width: 0; }
|
|
1455
1386
|
.tita-trialMatrixGroupLine + .tita-trialMatrixGroupLine { border-top: 1px dashed var(--tita-line); }
|
|
@@ -2049,8 +1980,6 @@ button.tita-trialMatrixGroup:focus-visible { outline: 2px solid var(--tita-focus
|
|
|
2049
1980
|
.tita-recallAnalysisTitle > svg { flex: 0 0 auto; color: var(--tita-tone-violet); }
|
|
2050
1981
|
.tita-recallAnalysisTitle h4 { margin: 0; color: var(--tita-ink); font-size: 11.5px; font-weight: 720; }
|
|
2051
1982
|
.tita-recallAnalysisTitle span { display: block; margin-top: 1px; color: var(--tita-ink-faint); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 9.5px; }
|
|
2052
|
-
.tita-recallAnalysisIntent { display: inline-flex; flex: 0 0 auto; align-items: baseline; gap: 5px; border-radius: 999px; background: var(--tita-tone-violet-tint); color: var(--tita-tone-violet); padding: 3px 8px; font-size: 10.5px; font-weight: 700; }
|
|
2053
|
-
.tita-recallAnalysisIntent b { color: var(--tita-ink-faint); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 9px; font-weight: 600; }
|
|
2054
1983
|
.tita-recallAnalysisAxes { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 7px; }
|
|
2055
1984
|
.tita-recallAnalysisAxis { display: grid; min-width: 0; gap: 5px; border: 1px solid var(--tita-line); border-radius: 8px; background: var(--tita-surface); padding: 7px 8px; }
|
|
2056
1985
|
.tita-recallAnalysisAxisHead { display: flex; align-items: center; gap: 5px; color: var(--tita-ink-muted); font-size: 10px; }
|
|
@@ -2161,16 +2090,6 @@ button.tita-trialMatrixGroup:focus-visible { outline: 2px solid var(--tita-focus
|
|
|
2161
2090
|
.tita-hitSnippet { margin: 0; color: var(--tita-ink-muted); font-size: 11px; line-height: 1.6; overflow-wrap: anywhere; }
|
|
2162
2091
|
.tita-hitFoot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; color: var(--tita-ink-faint); font-size: 10px; }
|
|
2163
2092
|
|
|
2164
|
-
/* 意图:从最近记录里归纳出的分布(act / 分类 / 低置信度)。 */
|
|
2165
|
-
.tita-distList { display: grid; gap: 7px; margin: 10px 0 0; }
|
|
2166
|
-
.tita-distRow { display: grid; grid-template-columns: minmax(56px, 96px) minmax(0, 1fr) 34px; align-items: center; gap: 8px; }
|
|
2167
|
-
.tita-distRow > span { overflow: hidden; color: var(--tita-ink-muted); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
|
|
2168
|
-
.tita-distBar { height: 7px; overflow: hidden; border-radius: 999px; background: var(--tita-surface-muted); }
|
|
2169
|
-
.tita-distBar > i { display: block; height: 100%; border-radius: 999px; background: var(--tita-tone-violet); }
|
|
2170
|
-
.tita-distBarFts > i { background: var(--tita-primary); }
|
|
2171
|
-
.tita-distBarGraph > i { background: var(--tita-tone-green); }
|
|
2172
|
-
.tita-distCount { color: var(--tita-ink-faint); font-size: 10.5px; text-align: right; }
|
|
2173
|
-
|
|
2174
2093
|
/* 记忆:真实落盘计数(04_memory)与「示意」卡片分开摆,避免把占位数字读成真实数据。 */
|
|
2175
2094
|
.tita-memoryReal { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 14px; }
|
|
2176
2095
|
|
package/src/config-schema.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* (`{op:'set', path:['knowledge','recallTopK']}`)。
|
|
10
10
|
*
|
|
11
11
|
* `knowledge` / `convert` 沿用原来的配置边界;`behaviorRules` 保存可关闭的系统提示词
|
|
12
|
-
*
|
|
12
|
+
* 扩展开关。
|
|
13
13
|
*
|
|
14
14
|
* 「单独一个模块」的理由和 `settings-schema.ts` 一样,
|
|
15
15
|
* 是**可测**:`tests/settings-schema.test.mjs` 要能 import 这个 schema 并把它解析
|
package/src/convert/host.ts
CHANGED
|
@@ -91,14 +91,13 @@ export interface ConvertHostDeps {
|
|
|
91
91
|
* 0.1.7 起设置不再是命名空间,而是随 `apply(ctx, config)` 进来的 entry config,
|
|
92
92
|
* 所以这里由调用方把那一组传下来。省略时用 `DEFAULT_CONVERT_CONFIG`。
|
|
93
93
|
*
|
|
94
|
-
*
|
|
95
|
-
* 字段是 volatile,解包是一次快照,而 volatile 改动不会重载插件。
|
|
94
|
+
* 给函数可响应设置热更新:字段是 volatile,解包是一次快照,而 volatile 改动不会重载插件。
|
|
96
95
|
*/
|
|
97
96
|
config?: Partial<ConvertPluginConfig> | (() => Partial<ConvertPluginConfig>)
|
|
98
97
|
}
|
|
99
98
|
|
|
100
99
|
/**
|
|
101
|
-
* `applyConvert`
|
|
100
|
+
* `applyConvert` 交给组合根的那一个把手。
|
|
102
101
|
*
|
|
103
102
|
* 转换这一组也有 volatile 字段(`maxConcurrent` / `maxInputBytes` / `retentionDays` /
|
|
104
103
|
* `toolWaitMs` / `ingestEnabled`),其中一个解包漏了就是沉默的错误:`maxInputBytes`
|
|
@@ -131,7 +130,7 @@ function lineageOf(args: Args, exec?: ToolRunContext): Record<string, string> |
|
|
|
131
130
|
const sessionId = typeof args.session_key === 'string' && args.session_key.trim() ? args.session_key.trim() : exec?.agent?.session.header.id
|
|
132
131
|
if (sessionId) lineage.sessionId = sessionId
|
|
133
132
|
const fields: Array<[string, string]> = [
|
|
134
|
-
['turnId', 'turn_id'], ['
|
|
133
|
+
['turnId', 'turn_id'], ['taskId', 'task_id'],
|
|
135
134
|
['subtaskId', 'subtask_id'], ['parentSubtaskId', 'parent_subtask_id'], ['runId', 'run_id'], ['traceId', 'trace_id'], ['domain', 'domain'],
|
|
136
135
|
]
|
|
137
136
|
for (const [target, source] of fields) if (typeof args[source] === 'string' && args[source].trim()) lineage[target] = args[source].trim().slice(0, 160)
|
|
@@ -506,7 +505,8 @@ export function applyConvert(ctx: Context, deps: ConvertHostDeps): ConvertHost {
|
|
|
506
505
|
convert_task_id: { type: 'string', required: true },
|
|
507
506
|
kb: { type: 'string', description: '目标知识库 id;省略则写入默认知识库' },
|
|
508
507
|
title: { type: 'string' },
|
|
509
|
-
source_id: { type: 'string', description: '
|
|
508
|
+
source_id: { type: 'string', description: '导入前分配的稳定 Source Raw ID;重试沿用,更新已有来源时明确传入其 ID。' },
|
|
509
|
+
allow_duplicate_title: { type: 'boolean', description: '显式允许同名独立来源;来源身份由 source_id 指定。' },
|
|
510
510
|
plan_only: { type: 'boolean' },
|
|
511
511
|
session_key: { type: 'string' },
|
|
512
512
|
task_id: { type: 'string' },
|
|
@@ -533,6 +533,7 @@ export function applyConvert(ctx: Context, deps: ConvertHostDeps): ConvertHost {
|
|
|
533
533
|
knowledgeRoot: target.knowledgeRoot,
|
|
534
534
|
...(typeof args.title === 'string' && args.title.trim() ? { title: args.title.trim() } : {}),
|
|
535
535
|
...(typeof args.source_id === 'string' && args.source_id.trim() ? { sourceId: args.source_id.trim() } : {}),
|
|
536
|
+
...(args.allow_duplicate_title === true ? { allowDuplicateTitle: true } : {}),
|
|
536
537
|
planOnly: args.plan_only === true,
|
|
537
538
|
...(lineageOf(args, exec) ? { lineage: lineageOf(args, exec)! } : {}),
|
|
538
539
|
})
|
|
@@ -588,6 +589,7 @@ export function applyConvert(ctx: Context, deps: ConvertHostDeps): ConvertHost {
|
|
|
588
589
|
knowledgeRoot: target.knowledgeRoot,
|
|
589
590
|
...(typeof body.title === 'string' && body.title.trim() ? { title: body.title.trim() } : {}),
|
|
590
591
|
...(typeof body.source_id === 'string' && body.source_id.trim() ? { sourceId: body.source_id.trim() } : {}),
|
|
592
|
+
...(body.allow_duplicate_title === true ? { allowDuplicateTitle: true } : {}),
|
|
591
593
|
planOnly: body.plan_only === true,
|
|
592
594
|
...(body.lineage && typeof body.lineage === 'object' ? { lineage: body.lineage as Record<string, string> } : {}),
|
|
593
595
|
})
|
package/src/convert/ingest.ts
CHANGED
|
@@ -46,6 +46,7 @@ export interface IngestTarget {
|
|
|
46
46
|
externalId?: string
|
|
47
47
|
metadata?: Record<string, unknown>
|
|
48
48
|
sourceId?: string
|
|
49
|
+
allowDuplicateTitle?: boolean
|
|
49
50
|
lineage?: LineageContext
|
|
50
51
|
/** OKF v0.2 溯源信息(0048):写成 front matter 的顶层键,不塞进 metadata。 */
|
|
51
52
|
provenance?: SourceProvenance
|
|
@@ -77,6 +78,7 @@ export interface IngestOptions {
|
|
|
77
78
|
* 历史保留、卡片与知识的出处不变、正文与图片引用一起更新。
|
|
78
79
|
*/
|
|
79
80
|
sourceId?: string
|
|
81
|
+
allowDuplicateTitle?: boolean
|
|
80
82
|
metadata?: Record<string, unknown>
|
|
81
83
|
lineage?: Record<string, unknown>
|
|
82
84
|
/** Dry run: build the plan without copying assets or writing the source. */
|
|
@@ -455,6 +457,7 @@ export async function ingestDelivery(options: IngestOptions): Promise<Envelope<I
|
|
|
455
457
|
metadata,
|
|
456
458
|
provenance,
|
|
457
459
|
...(options.sourceId?.trim() ? { sourceId: options.sourceId.trim() } : {}),
|
|
460
|
+
...(options.allowDuplicateTitle === true ? { allowDuplicateTitle: true } : {}),
|
|
458
461
|
...(options.lineage ? { lineage: options.lineage as LineageContext } : {}),
|
|
459
462
|
})
|
|
460
463
|
|
package/src/diagnosis/types.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* `/diagnosis/graph`)。视图是消费者,不是所有者 —— 模型放在领域层,客户端和宿主
|
|
6
6
|
* 各自 import,谁都不能私自改形状。
|
|
7
7
|
*
|
|
8
|
-
* 目录约定:`src/diagnosis/` 与 `src/
|
|
8
|
+
* 目录约定:`src/diagnosis/` 与 `src/convert/` 平级,都是领域子模块;
|
|
9
9
|
* `src/client/` 只放渲染。判据与 0020 第 3 条一致:**目录镜像领域流水线,不镜像技术层次**。
|
|
10
10
|
*/
|
|
11
11
|
|
package/src/images.ts
CHANGED
|
@@ -185,7 +185,7 @@ export async function publishKnowledgeImage(
|
|
|
185
185
|
throw new ImagePublishError('ASSET_NAME_COLLISION', '目标文件名已被不同内容占用,未覆盖现有图片。')
|
|
186
186
|
}
|
|
187
187
|
if (!existing?.equals(bytes)) await writeBufferAtomic(join(assetDir, storedFilename), bytes)
|
|
188
|
-
// Standalone hosted images have no SourceRaw or card by design. Keep that
|
|
188
|
+
// Standalone hosted images have no SourceRaw or card by design. Keep that reason beside the
|
|
189
189
|
// asset so the orphan sweep does not mistake it for a disposable extraction artifact.
|
|
190
190
|
await writeTextAtomic(join(assetDir, PUBLISHED_IMAGE_MARKER), 'image_publish\n')
|
|
191
191
|
const relativePath = `${ASSETS_DIR}/${bucket}/${storedFilename}`
|