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
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { SqliteDatabase } from '../fts.js'
|
|
2
|
+
import { applyRetention } from '../retention.js'
|
|
3
|
+
import type { TrialReport } from '../wiki-trial-types.js'
|
|
4
|
+
import type { PipelineEvent, PipelineTask } from './types.js'
|
|
5
|
+
|
|
6
|
+
export interface ExtractionCheckpoint {
|
|
7
|
+
id: string
|
|
8
|
+
sourceId: string
|
|
9
|
+
sourceVersion: number
|
|
10
|
+
sourceHash: string
|
|
11
|
+
fingerprint: string
|
|
12
|
+
report: TrialReport
|
|
13
|
+
createdAt: string
|
|
14
|
+
normalized?: { report: TrialReport; knowledgeIds: Record<string, string>; eventIds: Record<string, string>; fingerprint: string; unresolvedKnowledgeIds?: string[] }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Business checkpoints live with truth; execution history lives with extraction audits. */
|
|
18
|
+
export class PipelineStore {
|
|
19
|
+
constructor(readonly truth: SqliteDatabase, private readonly audit: SqliteDatabase) {
|
|
20
|
+
truth.exec(`
|
|
21
|
+
CREATE TABLE IF NOT EXISTS pipeline_extractions (
|
|
22
|
+
id TEXT PRIMARY KEY, source_id TEXT NOT NULL, source_version INTEGER NOT NULL,
|
|
23
|
+
source_hash TEXT NOT NULL, fingerprint TEXT NOT NULL, report TEXT NOT NULL,
|
|
24
|
+
normalized TEXT, created_at TEXT NOT NULL
|
|
25
|
+
);
|
|
26
|
+
CREATE INDEX IF NOT EXISTS pipeline_extract_cache ON pipeline_extractions(source_id, source_version, fingerprint);
|
|
27
|
+
CREATE TABLE IF NOT EXISTS pipeline_sources (source_id TEXT PRIMARY KEY, extracted_id TEXT, normalized_id TEXT);
|
|
28
|
+
CREATE TABLE IF NOT EXISTS pipeline_materializations (group_key TEXT PRIMARY KEY, fingerprint TEXT NOT NULL, card_id TEXT NOT NULL);
|
|
29
|
+
`)
|
|
30
|
+
audit.exec(`
|
|
31
|
+
CREATE TABLE IF NOT EXISTS pipeline_tasks (id TEXT PRIMARY KEY, queued_at TEXT NOT NULL, state TEXT NOT NULL, payload TEXT NOT NULL);
|
|
32
|
+
CREATE INDEX IF NOT EXISTS pipeline_tasks_time ON pipeline_tasks(queued_at DESC);
|
|
33
|
+
CREATE TABLE IF NOT EXISTS pipeline_events (
|
|
34
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT, task_id TEXT NOT NULL, kind TEXT NOT NULL,
|
|
35
|
+
text TEXT NOT NULL, source_id TEXT, at TEXT NOT NULL, stage TEXT
|
|
36
|
+
);
|
|
37
|
+
CREATE INDEX IF NOT EXISTS pipeline_events_task ON pipeline_events(task_id, seq);
|
|
38
|
+
`)
|
|
39
|
+
const eventColumns = audit.prepare('PRAGMA table_info(pipeline_events)').all() as Array<{ name: string }>
|
|
40
|
+
if (!eventColumns.some(column => column.name === 'stage')) audit.exec('ALTER TABLE pipeline_events ADD COLUMN stage TEXT')
|
|
41
|
+
// A new runtime never claims that an abandoned request is still running.
|
|
42
|
+
for (const task of (audit.prepare("SELECT payload FROM pipeline_tasks WHERE state IN ('queued','running','pausing','paused','cancelling')").all() as Array<{ payload: string }>).map(row => JSON.parse(row.payload) as PipelineTask)) {
|
|
43
|
+
if (['queued', 'running', 'pausing', 'paused', 'cancelling'].includes(task.state)) {
|
|
44
|
+
this.putTask({ ...task, state: 'interrupted', finishedAt: new Date().toISOString(), summary: '服务已重新启动;已完成的阶段保留,可重新运行此任务。' })
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
putTask(task: PipelineTask): void {
|
|
50
|
+
this.audit.prepare('INSERT INTO pipeline_tasks(id, queued_at, state, payload) VALUES (?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET state=excluded.state, payload=excluded.payload')
|
|
51
|
+
.run(task.id, task.queuedAt, task.state, JSON.stringify(task))
|
|
52
|
+
this.pruneAudit()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 审计库的保留上限(0070)。
|
|
57
|
+
*
|
|
58
|
+
* 实时预览每 250ms 或每 1KB 落一行,一次五分钟的抽取就能加一千行,而这里原来一条
|
|
59
|
+
* `DELETE` 都没有。上限是「水位以上保留」:升级那一刻已有的任务与事件一行不删
|
|
60
|
+
* (见 `retention.ts`)。任务的保留上限同时也是事件的:先按任务裁,再把已经不存在的
|
|
61
|
+
* 那些任务的事件(同样只在水位以上)删掉,最后兜一层总量。
|
|
62
|
+
*/
|
|
63
|
+
private pruneAudit(): void {
|
|
64
|
+
applyRetention(this.audit, 'pipeline_tasks', { table: 'pipeline_tasks', key: 'rowid', keep: 200 })
|
|
65
|
+
applyRetention(this.audit, 'pipeline_events', { table: 'pipeline_events', key: 'seq', keep: 50_000, where: 'task_id NOT IN (SELECT id FROM pipeline_tasks)' })
|
|
66
|
+
applyRetention(this.audit, 'pipeline_events', { table: 'pipeline_events', key: 'seq', keep: 50_000 })
|
|
67
|
+
}
|
|
68
|
+
task(id: string): PipelineTask | undefined {
|
|
69
|
+
const row = this.audit.prepare('SELECT payload FROM pipeline_tasks WHERE id=?').get(id) as { payload: string } | undefined
|
|
70
|
+
return row ? JSON.parse(row.payload) as PipelineTask : undefined
|
|
71
|
+
}
|
|
72
|
+
tasks(limit = 20, offset = 0): { tasks: PipelineTask[]; total: number } {
|
|
73
|
+
const rows = this.audit.prepare("SELECT payload FROM pipeline_tasks ORDER BY CASE WHEN state IN ('queued','running','pausing','paused','cancelling') THEN 0 ELSE 1 END, queued_at DESC LIMIT ? OFFSET ?")
|
|
74
|
+
.all(Math.max(1, Math.min(10000, limit)), Math.max(0, offset)) as Array<{ payload: string }>
|
|
75
|
+
const total = Number((this.audit.prepare('SELECT COUNT(*) AS n FROM pipeline_tasks').get() as { n: number }).n)
|
|
76
|
+
return { tasks: rows.map(row => JSON.parse(row.payload) as PipelineTask), total }
|
|
77
|
+
}
|
|
78
|
+
event(taskId: string, kind: PipelineEvent['kind'], text: string, sourceId?: string, stage?: PipelineEvent['stage']): void {
|
|
79
|
+
this.audit.prepare('INSERT INTO pipeline_events(task_id,kind,text,source_id,at,stage) VALUES (?,?,?,?,?,?)')
|
|
80
|
+
.run(taskId, kind, text, sourceId ?? null, new Date().toISOString(), stage ?? null)
|
|
81
|
+
}
|
|
82
|
+
events(taskId: string, after = 0, limit = 100): { events: PipelineEvent[]; next: number; hasMore: boolean } {
|
|
83
|
+
const take = Number.isFinite(limit) ? Math.max(1, Math.min(500, Math.trunc(limit))) : 100
|
|
84
|
+
after = Number.isFinite(after) ? Math.max(0, Math.trunc(after)) : 0
|
|
85
|
+
const rows = this.audit.prepare('SELECT * FROM pipeline_events WHERE task_id=? AND seq>? ORDER BY seq LIMIT ?')
|
|
86
|
+
.all(taskId, after, take + 1) as Array<Record<string, unknown>>
|
|
87
|
+
const hasMore = rows.length > take
|
|
88
|
+
const events = rows.slice(0, take).map(row => ({ seq: Number(row.seq), taskId: String(row.task_id), kind: String(row.kind) as PipelineEvent['kind'], text: String(row.text), ...(row.source_id ? { sourceId: String(row.source_id) } : {}), ...(row.stage ? { stage: String(row.stage) as PipelineEvent['stage'] } : {}), at: String(row.at) }))
|
|
89
|
+
return { events, next: events.at(-1)?.seq ?? after, hasMore }
|
|
90
|
+
}
|
|
91
|
+
checkpoint(id: string): ExtractionCheckpoint | undefined {
|
|
92
|
+
const row = this.truth.prepare('SELECT * FROM pipeline_extractions WHERE id=?').get(id) as Record<string, unknown> | undefined
|
|
93
|
+
return row ? { id: String(row.id), sourceId: String(row.source_id), sourceVersion: Number(row.source_version), sourceHash: String(row.source_hash), fingerprint: String(row.fingerprint), report: JSON.parse(String(row.report)), createdAt: String(row.created_at), ...(row.normalized ? { normalized: JSON.parse(String(row.normalized)) } : {}) } : undefined
|
|
94
|
+
}
|
|
95
|
+
cached(sourceId: string, version: number, fingerprint: string): ExtractionCheckpoint | undefined {
|
|
96
|
+
const row = this.truth.prepare('SELECT id FROM pipeline_extractions WHERE source_id=? AND source_version=? AND fingerprint=? ORDER BY created_at DESC, rowid DESC LIMIT 1')
|
|
97
|
+
.get(sourceId, version, fingerprint) as { id: string } | undefined
|
|
98
|
+
return row ? this.checkpoint(row.id) : undefined
|
|
99
|
+
}
|
|
100
|
+
head(sourceId: string): { extracted?: ExtractionCheckpoint; normalized?: ExtractionCheckpoint } {
|
|
101
|
+
const row = this.truth.prepare('SELECT extracted_id,normalized_id FROM pipeline_sources WHERE source_id=?').get(sourceId) as { extracted_id?: string; normalized_id?: string } | undefined
|
|
102
|
+
return { extracted: row?.extracted_id ? this.checkpoint(row.extracted_id) : undefined, normalized: row?.normalized_id ? this.checkpoint(row.normalized_id) : undefined }
|
|
103
|
+
}
|
|
104
|
+
saveExtraction(value: ExtractionCheckpoint): void {
|
|
105
|
+
this.truth.exec('SAVEPOINT pipeline_extract')
|
|
106
|
+
try {
|
|
107
|
+
this.truth.prepare('INSERT INTO pipeline_extractions(id,source_id,source_version,source_hash,fingerprint,report,created_at) VALUES (?,?,?,?,?,?,?)')
|
|
108
|
+
.run(value.id, value.sourceId, value.sourceVersion, value.sourceHash, value.fingerprint, JSON.stringify(value.report), value.createdAt)
|
|
109
|
+
this.selectExtraction(value.sourceId, value.id)
|
|
110
|
+
this.truth.exec('RELEASE pipeline_extract')
|
|
111
|
+
} catch (cause) { this.truth.exec('ROLLBACK TO pipeline_extract; RELEASE pipeline_extract'); throw cause }
|
|
112
|
+
}
|
|
113
|
+
selectExtraction(sourceId: string, id: string): void {
|
|
114
|
+
this.truth.prepare('INSERT INTO pipeline_sources(source_id,extracted_id) VALUES (?,?) ON CONFLICT(source_id) DO UPDATE SET extracted_id=excluded.extracted_id').run(sourceId, id)
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 刷新一份抽取检查点的指纹(0070)。
|
|
118
|
+
*
|
|
119
|
+
* 指纹里含「本文提到的实体清单」,而归一会往注册表里学新实体 —— 它们本来就在这份
|
|
120
|
+
* 文档里,所以学完之后指纹必然变。不刷新的话,同一份未改动的文档下次抽取一定复用不上
|
|
121
|
+
* (每次都要再调一次模型)。归一成功后把这一行改成学完之后的值,复用才成立。
|
|
122
|
+
*/
|
|
123
|
+
refreshFingerprint(id: string, fingerprint: string): void {
|
|
124
|
+
this.truth.prepare('UPDATE pipeline_extractions SET fingerprint=? WHERE id=?').run(fingerprint, id)
|
|
125
|
+
}
|
|
126
|
+
saveNormalization(value: ExtractionCheckpoint, normalized: NonNullable<ExtractionCheckpoint['normalized']>): void {
|
|
127
|
+
this.truth.prepare('UPDATE pipeline_extractions SET normalized=? WHERE id=?').run(JSON.stringify(normalized), value.id)
|
|
128
|
+
this.truth.prepare('INSERT INTO pipeline_sources(source_id,normalized_id) VALUES (?,?) ON CONFLICT(source_id) DO UPDATE SET normalized_id=excluded.normalized_id').run(value.sourceId, value.id)
|
|
129
|
+
}
|
|
130
|
+
unresolvedKnowledge(): { ids: Set<string>; sources: Set<string> } {
|
|
131
|
+
const rows = this.truth.prepare('SELECT s.source_id, e.normalized FROM pipeline_sources s JOIN pipeline_extractions e ON e.id=s.normalized_id WHERE e.normalized IS NOT NULL').all() as Array<{ source_id: string; normalized: string }>
|
|
132
|
+
const ids = new Set<string>(); const sources = new Set<string>()
|
|
133
|
+
for (const row of rows) {
|
|
134
|
+
const pending = (JSON.parse(row.normalized) as NonNullable<ExtractionCheckpoint['normalized']>).unresolvedKnowledgeIds ?? []
|
|
135
|
+
if (pending.length) sources.add(row.source_id)
|
|
136
|
+
for (const id of pending) ids.add(id)
|
|
137
|
+
}
|
|
138
|
+
return { ids, sources }
|
|
139
|
+
}
|
|
140
|
+
materialization(key: string): { fingerprint: string; cardId: string } | undefined {
|
|
141
|
+
const row = this.truth.prepare('SELECT fingerprint,card_id FROM pipeline_materializations WHERE group_key=?').get(key) as { fingerprint: string; card_id: string } | undefined
|
|
142
|
+
return row ? { fingerprint: row.fingerprint, cardId: row.card_id } : undefined
|
|
143
|
+
}
|
|
144
|
+
materialized(key: string, fingerprint: string, cardId: string): void {
|
|
145
|
+
this.truth.prepare('INSERT INTO pipeline_materializations(group_key,fingerprint,card_id) VALUES (?,?,?) ON CONFLICT(group_key) DO UPDATE SET fingerprint=excluded.fingerprint,card_id=excluded.card_id').run(key, fingerprint, cardId)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/** Shared, browser-safe contracts for independently runnable knowledge stages. */
|
|
2
|
+
export type PipelineStage = 'extract' | 'normalize' | 'materialize' | 'build' | 'all'
|
|
3
|
+
export type PipelineState = 'queued' | 'running' | 'pausing' | 'paused' | 'cancelling' | 'succeeded' | 'partial' | 'failed' | 'cancelled' | 'interrupted'
|
|
4
|
+
export type PipelineAction = 'pause' | 'resume' | 'cancel'
|
|
5
|
+
|
|
6
|
+
export interface PipelineSelection {
|
|
7
|
+
sourceIds?: string[]
|
|
8
|
+
groupKeys?: string[]
|
|
9
|
+
force?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface PipelineModel {
|
|
13
|
+
provider?: string
|
|
14
|
+
model?: string
|
|
15
|
+
reasoningEffort?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PipelineTask {
|
|
19
|
+
id: string
|
|
20
|
+
stage: PipelineStage
|
|
21
|
+
state: PipelineState
|
|
22
|
+
selection: PipelineSelection
|
|
23
|
+
model: PipelineModel
|
|
24
|
+
queuedAt: string
|
|
25
|
+
startedAt?: string
|
|
26
|
+
finishedAt?: string
|
|
27
|
+
currentStage?: Exclude<PipelineStage, 'all'>
|
|
28
|
+
done: number
|
|
29
|
+
total: number
|
|
30
|
+
reused: number
|
|
31
|
+
failed: number
|
|
32
|
+
summary: string
|
|
33
|
+
warnings: string[]
|
|
34
|
+
results?: Partial<Record<Exclude<PipelineStage, 'all'>, { done: number; total: number; reused: number; failed: number; summary?: string; skipped?: boolean }>>
|
|
35
|
+
legacy?: boolean
|
|
36
|
+
reportId?: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface PipelineEvent {
|
|
40
|
+
seq: number
|
|
41
|
+
taskId: string
|
|
42
|
+
kind: 'output' | 'progress' | 'warning' | 'report'
|
|
43
|
+
text: string
|
|
44
|
+
sourceId?: string
|
|
45
|
+
stage?: Exclude<PipelineStage, 'all'>
|
|
46
|
+
at: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface MaterializationGroup {
|
|
50
|
+
key: string
|
|
51
|
+
title: string
|
|
52
|
+
coordinate: { objectId: string; actionId: string; sceneId: string }
|
|
53
|
+
knowledgeCount: number
|
|
54
|
+
eventCount: number
|
|
55
|
+
sourceIds: string[]
|
|
56
|
+
cardId?: string
|
|
57
|
+
state: 'new' | 'changed' | 'current' | 'withdrawn' | 'blocked'
|
|
58
|
+
reason?: string
|
|
59
|
+
fingerprint: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 物化阶段的批(宿主产生、宿主消费)。
|
|
64
|
+
*
|
|
65
|
+
* `snapshot` 对引擎是**不透明的**:它的字段类型来自服务端模块(Knowledge / EventEntity /
|
|
66
|
+
* 图片 / 相关推荐),而那些模块都依赖 `node:*`。本模块被客户端 import 取值,所以这里
|
|
67
|
+
* 不能描述它 —— 真正的形状在 `materialize-snapshot.ts`,只有服务端看得见。
|
|
68
|
+
* 引擎只负责把 `snapshot` 原样传回 `materialize()`。
|
|
69
|
+
*/
|
|
70
|
+
export interface MaterializationBatch {
|
|
71
|
+
groups: MaterializationGroup[]
|
|
72
|
+
/** 坐标仍有歧义 + 等待归一,两部分的合计。 */
|
|
73
|
+
blocked: number
|
|
74
|
+
blockedAmbiguous: number
|
|
75
|
+
blockedAwaitingNormalize: number
|
|
76
|
+
snapshot: unknown
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface PipelineStatus {
|
|
80
|
+
sources: Array<{ id: string; title: string; version: number; extractionId?: string; normalizedId?: string; state: 'pending' | 'extracted' | 'normalized' | 'outdated' }>
|
|
81
|
+
groups: MaterializationGroup[]
|
|
82
|
+
blocked: number
|
|
83
|
+
pendingNew: number
|
|
84
|
+
pendingUpdated: number
|
|
85
|
+
pendingWithdrawn: number
|
|
86
|
+
indexPending: boolean
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const PIPELINE_STAGE_LABEL: Record<PipelineStage, string> = {
|
|
90
|
+
extract: '抽取 Knowledge / Event', normalize: '归一合并', materialize: '物化 WikiCard', build: '构建检索索引', all: '全链路运行',
|
|
91
|
+
}
|
|
92
|
+
export const PIPELINE_ACTIVE_STATES: readonly PipelineState[] = ['queued', 'running', 'pausing', 'paused', 'cancelling']
|
package/src/present.ts
CHANGED
|
@@ -54,6 +54,7 @@ function short(text: string, max = 48): string {
|
|
|
54
54
|
export interface ImportMetaSource { sourceId: string; title?: string; bytes: number }
|
|
55
55
|
export interface ImportMetaFailure { index: number; title?: string; code: string; message: string }
|
|
56
56
|
export interface ImportMetaCompile {
|
|
57
|
+
taskId?: string; state?: string
|
|
57
58
|
cardsCreated: number; cardsUpdated: number; cardsReused?: number
|
|
58
59
|
notesReused?: number; notesRecompiled?: number
|
|
59
60
|
sourcesExtracted?: number; sourcesSkipped?: number; cardsRemoved?: number
|
|
@@ -101,7 +102,7 @@ export function importCallView(args: { sources?: unknown[]; compile?: boolean })
|
|
|
101
102
|
return {
|
|
102
103
|
card: 'generic',
|
|
103
104
|
kind: 'edit',
|
|
104
|
-
title: `导入 ${count} 份来源${args?.compile ===
|
|
105
|
+
title: `导入 ${count} 份来源${args?.compile === true ? '并处理知识' : ''}`,
|
|
105
106
|
rawInput: titles.length > 0 ? titles.map(title => short(title)) : { 份数: count },
|
|
106
107
|
}
|
|
107
108
|
}
|
|
@@ -112,10 +113,15 @@ export function importResultMarkdown(meta: ImportMeta): string {
|
|
|
112
113
|
for (const item of meta.written) lines.push(`- ${item.title ? `**${short(item.title)}**` : item.sourceId} · ${item.bytes} 字符`)
|
|
113
114
|
for (const item of meta.failed) lines.push(`- 第 ${item.index + 1} 份${item.title ? `「${short(item.title)}」` : ''}失败:${item.code} ${item.message}`)
|
|
114
115
|
if (meta.compile) {
|
|
115
|
-
const { cardsCreated, cardsUpdated, cardsReused, notesReused, notesRecompiled, embedding, version } = meta.compile
|
|
116
|
+
const { cardsCreated, cardsUpdated, cardsReused, notesReused, notesRecompiled, embedding, version, state, taskId } = meta.compile
|
|
117
|
+
if (state) {
|
|
118
|
+
const label = state === 'succeeded' ? '已完成' : state === 'partial' ? '部分完成' : state === 'cancelled' ? '已停止' : '未完成'
|
|
119
|
+
lines.push('', `知识处理${label}:新建 ${cardsCreated} 张、更新 ${cardsUpdated} 张。${taskId ? `任务:${taskId}。` : ''}详情请打开「知识处理」页的运行记录。`)
|
|
120
|
+
return lines.join('\n')
|
|
121
|
+
}
|
|
116
122
|
lines.push('', `编译:新建 ${cardsCreated} 张、并入 ${cardsUpdated} 张、复用 ${cardsReused ?? 0} 张;召回片段复用 ${notesReused ?? 0} 条、重编 ${notesRecompiled ?? 0} 条;索引 v${version ?? '?'}${embedding ? `(向量 ${embedding})` : ''}。`)
|
|
117
123
|
} else {
|
|
118
|
-
lines.push('', '
|
|
124
|
+
lines.push('', '这次只保存来源(默认 `compile: false`)。下一步使用 `run_knowledge_pipeline` 分别抽取、归一、物化,最后构建检索索引。')
|
|
119
125
|
}
|
|
120
126
|
return lines.join('\n')
|
|
121
127
|
}
|
|
@@ -314,7 +320,7 @@ export function addSourceCallView(args: { title?: string; content?: string }): {
|
|
|
314
320
|
}
|
|
315
321
|
|
|
316
322
|
export function addSourceResultMarkdown(meta: AddSourceMeta): string {
|
|
317
|
-
return `**来源已写入** —— 「${meta.title ? short(meta.title) : meta.sourceId}」(${meta.bytes} 字符)。\n\n下一步:\`
|
|
323
|
+
return `**来源已写入** —— 「${meta.title ? short(meta.title) : meta.sourceId}」(${meta.bytes} 字符)。\n\n下一步:\`run_knowledge_pipeline\` 分别抽取、归一和物化,最后构建检索索引。`
|
|
318
324
|
}
|
|
319
325
|
|
|
320
326
|
export interface WriteCardsMeta {
|
package/src/retention.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 保留上限(0070)。
|
|
3
|
+
*
|
|
4
|
+
* 文档里写着保留策略(「命名永久 + 最近 100 + 每日末版;机器版本只活到硬上限」),
|
|
5
|
+
* 而代码里对这几张表一条 `DELETE` 都没有:`revision` / `blob` / `tree` / `operations` /
|
|
6
|
+
* `pipeline_extractions` / `pipeline_events` 只增不减。这个模块把**新数据**的上限补上。
|
|
7
|
+
*
|
|
8
|
+
* ## 为什么是「水位 + 上限」,而不是直接按条数删
|
|
9
|
+
*
|
|
10
|
+
* 上限一旦生效,第一次裁剪就会把库里**已有的**历史删掉 —— 那是不可逆的,而用户的决定是
|
|
11
|
+
* 「只对新数据加上限,不动存量」。所以每张表第一次裁剪时先把当前最大键记成**水位**:
|
|
12
|
+
* 水位以下的既有行永不删除;水位以上的新行按上限保留。代价是总量变成
|
|
13
|
+
* 「升级那一刻的存量 + 上限」,而不是「上限」—— 这是「不动存量」能给出的最强的界。
|
|
14
|
+
*
|
|
15
|
+
* ## 键列的要求
|
|
16
|
+
*
|
|
17
|
+
* `key` 必须是单调递增的整数列(`rowid` 或自增 `seq`)。用时间戳当键不行:同一毫秒里
|
|
18
|
+
* 两行的顺序不确定,裁剪会随机丢一行。
|
|
19
|
+
*/
|
|
20
|
+
import type { SqliteDatabase } from './fts.js'
|
|
21
|
+
|
|
22
|
+
export interface RetentionSpec {
|
|
23
|
+
/** 表名。内插进 SQL,所以调用方只能传常量。 */
|
|
24
|
+
table: string
|
|
25
|
+
/** 单调递增的整数键列,例如 `rowid` 或 `seq`。 */
|
|
26
|
+
key: string
|
|
27
|
+
/** 水位以上保留多少行;`0` 表示水位以上的匹配行全部删掉(用于回收无引用的 blob)。 */
|
|
28
|
+
keep: number
|
|
29
|
+
/** 额外的行条件(可含子查询)。同样只允许常量 SQL。 */
|
|
30
|
+
where?: string
|
|
31
|
+
/** 分组表达式:给了就是「每组保留 keep 行」,而不是全表保留。 */
|
|
32
|
+
groupBy?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** 水位表的表名。它跟着所在的那个库走(审计库与 knowledge.db 各有一份)。 */
|
|
36
|
+
const FLOOR_TABLE = 'retention_floor'
|
|
37
|
+
|
|
38
|
+
function ensureFloors(db: SqliteDatabase): void {
|
|
39
|
+
db.exec(`CREATE TABLE IF NOT EXISTS ${FLOOR_TABLE} (name TEXT PRIMARY KEY, floor INTEGER NOT NULL, at TEXT NOT NULL)`)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function floorOf(db: SqliteDatabase, name: string, table: string, key: string): number {
|
|
43
|
+
const row = db.prepare(`SELECT floor FROM ${FLOOR_TABLE} WHERE name = ?`).get(name) as { floor?: unknown } | undefined
|
|
44
|
+
if (row !== undefined) return Number(row.floor ?? 0)
|
|
45
|
+
const max = Number((db.prepare(`SELECT COALESCE(MAX(${key}), 0) AS m FROM ${table}`).get() as { m?: unknown } | undefined)?.m ?? 0)
|
|
46
|
+
db.prepare(`INSERT OR IGNORE INTO ${FLOOR_TABLE}(name, floor, at) VALUES (?, ?, ?)`).run(name, max, new Date().toISOString())
|
|
47
|
+
return max
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 裁掉水位以上的超限行,返回删掉的行数。
|
|
52
|
+
*
|
|
53
|
+
* 幂等且可反复调用:没有超限时最多花一次索引区间计数。
|
|
54
|
+
*/
|
|
55
|
+
export function applyRetention(db: SqliteDatabase, name: string, spec: RetentionSpec): number {
|
|
56
|
+
ensureFloors(db)
|
|
57
|
+
const floor = floorOf(db, name, spec.table, spec.key)
|
|
58
|
+
const filter = spec.where === undefined ? '' : ` AND (${spec.where})`
|
|
59
|
+
const above = Number((db.prepare(`SELECT COUNT(*) AS n FROM ${spec.table} WHERE ${spec.key} > ?${filter}`).get(floor) as { n?: unknown } | undefined)?.n ?? 0)
|
|
60
|
+
if (above <= spec.keep) return 0
|
|
61
|
+
if (spec.groupBy === undefined) {
|
|
62
|
+
const changes = spec.keep <= 0
|
|
63
|
+
? db.prepare(`DELETE FROM ${spec.table} WHERE ${spec.key} > ?${filter}`).run(floor)
|
|
64
|
+
: db.prepare(`DELETE FROM ${spec.table} WHERE ${spec.key} > ? AND ${spec.key} < (
|
|
65
|
+
SELECT ${spec.key} FROM ${spec.table} WHERE ${spec.key} > ?${filter} ORDER BY ${spec.key} DESC LIMIT 1 OFFSET ?
|
|
66
|
+
)${filter}`).run(floor, floor, spec.keep - 1)
|
|
67
|
+
return Number((changes as { changes?: unknown })?.changes ?? 0)
|
|
68
|
+
}
|
|
69
|
+
const changes = db.prepare(`DELETE FROM ${spec.table} WHERE ${spec.key} IN (
|
|
70
|
+
SELECT ${spec.key} FROM (
|
|
71
|
+
SELECT ${spec.key}, ROW_NUMBER() OVER (PARTITION BY ${spec.groupBy} ORDER BY ${spec.key} DESC) AS rn
|
|
72
|
+
FROM ${spec.table} WHERE ${spec.key} > ?${filter}
|
|
73
|
+
) WHERE rn > ?
|
|
74
|
+
)`).run(floor, spec.keep)
|
|
75
|
+
return Number((changes as { changes?: unknown })?.changes ?? 0)
|
|
76
|
+
}
|