dsh-data-domain-knowledge 0.1.0-alpha.9 → 0.1.1-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -567
- package/cordis.patch.yml +5 -418
- package/eval/README.md +19 -8
- package/eval/benchmark-extraction-format.mjs +20 -0
- package/eval/evaluate-retrieval.mjs +17 -8
- package/eval/queries.example.jsonl +1 -0
- package/lib/client.js +4181 -3239
- package/lib/contract-envelope.d.ts +91 -0
- package/lib/contract-envelope.js +106 -0
- package/lib/contract-layout.d.ts +130 -0
- package/lib/contract-layout.js +138 -0
- package/lib/contract-refs.d.ts +39 -0
- package/lib/contract-refs.js +9 -0
- package/lib/contract-version.d.ts +311 -0
- package/lib/contract-version.js +270 -0
- package/lib/contract-wire.d.ts +147 -0
- package/lib/contract-wire.js +99 -0
- package/lib/contracts.d.ts +12 -0
- package/lib/contracts.js +12 -0
- package/lib/doc-page.js +2 -2
- package/lib/pages.js +3329 -1296
- package/lib/types/agent-behavior-rules.d.ts +10 -0
- package/lib/types/agent-behavior-rules.d.ts.map +1 -0
- package/lib/types/agent-behavior-rules.js +28 -0
- package/lib/types/agent-behavior-rules.js.map +1 -0
- package/lib/types/axis-registry.d.ts +37 -5
- package/lib/types/axis-registry.d.ts.map +1 -1
- package/lib/types/axis-registry.js +234 -26
- package/lib/types/axis-registry.js.map +1 -1
- package/lib/types/card-organize.d.ts +27 -12
- package/lib/types/card-organize.d.ts.map +1 -1
- package/lib/types/card-organize.js +58 -73
- package/lib/types/card-organize.js.map +1 -1
- package/lib/types/compiler/embedding-identity.d.ts +3 -0
- package/lib/types/compiler/embedding-identity.d.ts.map +1 -0
- package/lib/types/compiler/embedding-identity.js +24 -0
- package/lib/types/compiler/embedding-identity.js.map +1 -0
- package/lib/types/compiler/snapshot.d.ts +16 -0
- package/lib/types/compiler/snapshot.d.ts.map +1 -0
- package/lib/types/compiler/snapshot.js +2 -0
- package/lib/types/compiler/snapshot.js.map +1 -0
- package/lib/types/config-schema.d.ts +6 -3
- package/lib/types/config-schema.d.ts.map +1 -1
- package/lib/types/config-schema.js +6 -2
- package/lib/types/config-schema.js.map +1 -1
- package/lib/types/convert/host.d.ts +6 -5
- package/lib/types/convert/host.d.ts.map +1 -1
- package/lib/types/convert/host.js +58 -18
- package/lib/types/convert/host.js.map +1 -1
- package/lib/types/convert/ingest.d.ts +10 -1
- 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/derive-contract.d.ts +3 -0
- package/lib/types/derive-contract.d.ts.map +1 -0
- package/lib/types/derive-contract.js +15 -0
- package/lib/types/derive-contract.js.map +1 -0
- 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 +24 -18
- package/lib/types/diagnosis/store.d.ts.map +1 -1
- package/lib/types/diagnosis/store.js +130 -65
- package/lib/types/diagnosis/store.js.map +1 -1
- package/lib/types/diagnosis/types.d.ts +47 -15
- 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 +15 -0
- package/lib/types/environment/install.d.ts.map +1 -0
- package/lib/types/environment/install.js +297 -0
- package/lib/types/environment/install.js.map +1 -0
- package/lib/types/environment/manifest.d.ts +208 -0
- package/lib/types/environment/manifest.d.ts.map +1 -0
- package/lib/types/environment/manifest.js +26 -0
- package/lib/types/environment/manifest.js.map +1 -0
- package/lib/types/environment/model-catalog.d.ts +223 -0
- package/lib/types/environment/model-catalog.d.ts.map +1 -0
- package/lib/types/environment/model-catalog.js +129 -0
- package/lib/types/environment/model-catalog.js.map +1 -0
- package/lib/types/environment/process.d.ts +10 -0
- package/lib/types/environment/process.d.ts.map +1 -0
- package/lib/types/environment/process.js +54 -0
- package/lib/types/environment/process.js.map +1 -0
- package/lib/types/environment/selection.d.ts +9 -0
- package/lib/types/environment/selection.d.ts.map +1 -0
- package/lib/types/environment/selection.js +29 -0
- package/lib/types/environment/selection.js.map +1 -0
- package/lib/types/environment/service.d.ts +64 -0
- package/lib/types/environment/service.d.ts.map +1 -0
- package/lib/types/environment/service.js +424 -0
- package/lib/types/environment/service.js.map +1 -0
- package/lib/types/environment/types.d.ts +56 -0
- package/lib/types/environment/types.d.ts.map +1 -0
- package/lib/types/environment/types.js +2 -0
- package/lib/types/environment/types.js.map +1 -0
- package/lib/types/events.d.ts +76 -0
- package/lib/types/events.d.ts.map +1 -0
- package/lib/types/events.js +52 -0
- package/lib/types/events.js.map +1 -0
- package/lib/types/extract-contract.d.ts +3 -0
- package/lib/types/extract-contract.d.ts.map +1 -0
- package/lib/types/extract-contract.js +41 -0
- package/lib/types/extract-contract.js.map +1 -0
- package/lib/types/extract-model.d.ts +5 -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 +27 -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 +158 -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 +26 -0
- package/lib/types/images.d.ts.map +1 -0
- package/lib/types/images.js +196 -0
- package/lib/types/images.js.map +1 -0
- package/lib/types/index.d.ts +2 -2
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +598 -209
- package/lib/types/index.js.map +1 -1
- package/lib/types/intent/engine.d.ts +12 -4
- package/lib/types/intent/engine.d.ts.map +1 -1
- package/lib/types/intent/engine.js +43 -5
- package/lib/types/intent/engine.js.map +1 -1
- package/lib/types/intent/host.d.ts +5 -8
- package/lib/types/intent/host.d.ts.map +1 -1
- package/lib/types/intent/host.js +56 -11
- package/lib/types/intent/host.js.map +1 -1
- package/lib/types/intent/scope.d.ts +25 -0
- package/lib/types/intent/scope.d.ts.map +1 -0
- package/lib/types/intent/scope.js +116 -0
- package/lib/types/intent/scope.js.map +1 -0
- package/lib/types/intent/tools.d.ts +24 -20
- package/lib/types/intent/tools.d.ts.map +1 -1
- package/lib/types/intent/tools.js +65 -23
- package/lib/types/intent/tools.js.map +1 -1
- package/lib/types/intent/types.d.ts +11 -4
- package/lib/types/intent/types.d.ts.map +1 -1
- package/lib/types/knowledge-bases.d.ts +11 -5
- package/lib/types/knowledge-bases.d.ts.map +1 -1
- package/lib/types/knowledge-bases.js +52 -20
- package/lib/types/knowledge-bases.js.map +1 -1
- package/lib/types/knowledge-contract.d.ts +32 -0
- package/lib/types/knowledge-contract.d.ts.map +1 -0
- package/lib/types/knowledge-contract.js +116 -0
- package/lib/types/knowledge-contract.js.map +1 -0
- package/lib/types/knowledge.d.ts +31 -9
- package/lib/types/knowledge.d.ts.map +1 -1
- package/lib/types/knowledge.js +137 -60
- package/lib/types/knowledge.js.map +1 -1
- package/lib/types/layout.d.ts +10 -29
- package/lib/types/layout.d.ts.map +1 -1
- package/lib/types/layout.js +17 -36
- package/lib/types/layout.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/migrate.d.ts +11 -0
- package/lib/types/migrate.d.ts.map +1 -1
- package/lib/types/migrate.js +83 -2
- package/lib/types/migrate.js.map +1 -1
- package/lib/types/model-text.d.ts +58 -1
- package/lib/types/model-text.d.ts.map +1 -1
- package/lib/types/model-text.js +85 -30
- 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/organize-contract.d.ts +18 -0
- package/lib/types/organize-contract.d.ts.map +1 -0
- package/lib/types/organize-contract.js +82 -0
- package/lib/types/organize-contract.js.map +1 -0
- package/lib/types/pipeline/engine.d.ts +66 -0
- package/lib/types/pipeline/engine.d.ts.map +1 -0
- package/lib/types/pipeline/engine.js +352 -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 +102 -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/preset-ids.d.ts +4 -0
- package/lib/types/preset-ids.d.ts.map +1 -0
- package/lib/types/preset-ids.js +6 -0
- package/lib/types/preset-ids.js.map +1 -0
- package/lib/types/query-analysis.d.ts +13 -1
- package/lib/types/query-analysis.d.ts.map +1 -1
- package/lib/types/query-analysis.js +62 -6
- package/lib/types/query-analysis.js.map +1 -1
- package/lib/types/recall-rank.d.ts +3 -3
- package/lib/types/recall-rank.d.ts.map +1 -1
- package/lib/types/recall-rank.js +4 -4
- package/lib/types/recall-rank.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/retrieval/vector-index.d.ts +32 -0
- package/lib/types/retrieval/vector-index.d.ts.map +1 -0
- package/lib/types/retrieval/vector-index.js +15 -0
- package/lib/types/retrieval/vector-index.js.map +1 -0
- package/lib/types/retrieval/visibility.d.ts +6 -0
- package/lib/types/retrieval/visibility.d.ts.map +1 -0
- package/lib/types/retrieval/visibility.js +11 -0
- package/lib/types/retrieval/visibility.js.map +1 -0
- package/lib/types/runtime.d.ts +272 -27
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/runtime.js +2751 -537
- package/lib/types/runtime.js.map +1 -1
- package/lib/types/settings-schema.d.ts +17 -15
- package/lib/types/settings-schema.d.ts.map +1 -1
- package/lib/types/settings-schema.js +50 -47
- package/lib/types/settings-schema.js.map +1 -1
- package/lib/types/settings-volatile.d.ts +4 -4
- package/lib/types/settings-volatile.js +4 -4
- package/lib/types/source-evidence.d.ts +14 -0
- package/lib/types/source-evidence.d.ts.map +1 -0
- package/lib/types/source-evidence.js +36 -0
- package/lib/types/source-evidence.js.map +1 -0
- package/lib/types/store/axis-references.d.ts +5 -0
- package/lib/types/store/axis-references.d.ts.map +1 -0
- package/lib/types/store/axis-references.js +106 -0
- package/lib/types/store/axis-references.js.map +1 -0
- package/lib/types/store/derived.d.ts +12 -1
- package/lib/types/store/derived.d.ts.map +1 -1
- package/lib/types/store/derived.js +71 -12
- package/lib/types/store/derived.js.map +1 -1
- package/lib/types/store/knowledge-db.d.ts +31 -1
- package/lib/types/store/knowledge-db.d.ts.map +1 -1
- package/lib/types/store/knowledge-db.js +252 -18
- package/lib/types/store/knowledge-db.js.map +1 -1
- package/lib/types/store/migrate.d.ts.map +1 -1
- package/lib/types/store/migrate.js +17 -6
- 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 +59 -27
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/types.js +6 -15
- 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 +4 -0
- package/lib/types/wiki-materialize.d.ts.map +1 -1
- package/lib/types/wiki-materialize.js +21 -7
- package/lib/types/wiki-materialize.js.map +1 -1
- package/lib/types/wiki-trial-types.d.ts +87 -0
- package/lib/types/wiki-trial-types.d.ts.map +1 -1
- package/lib/types/wiki-trial-types.js +0 -16
- package/lib/types/wiki-trial-types.js.map +1 -1
- package/lib/types/wiki-trial.d.ts +40 -16
- package/lib/types/wiki-trial.d.ts.map +1 -1
- package/lib/types/wiki-trial.js +337 -224
- package/lib/types/wiki-trial.js.map +1 -1
- package/package.json +29 -20
- package/python/prepare-models.py +19 -0
- package/python/requirements.txt +8 -6
- package/scripts/doctor.mjs +13 -19
- package/scripts/environment.mjs +29 -0
- package/scripts/setup-embedding.sh +15 -5
- package/src/agent-behavior-rules.ts +37 -0
- package/src/axis-registry.ts +234 -27
- package/src/card-organize.ts +69 -75
- package/src/client/AgentBehaviorRulesCard.tsx +65 -0
- package/src/client/AttributionEventGuide.tsx +41 -39
- package/src/client/AxisRegistrySection.tsx +204 -164
- package/src/client/CompileDialogs.tsx +44 -29
- package/src/client/DeleteDocumentDialog.tsx +36 -5
- package/src/client/DirectoryPicker.tsx +0 -10
- package/src/client/EnvironmentSetup.tsx +243 -0
- package/src/client/ExpansionGraph.tsx +13 -7
- package/src/client/ExtractionTasksPage.tsx +578 -0
- package/src/client/GuideArchitectureCanvas.tsx +470 -0
- package/src/client/ImportFilesDialog.tsx +2 -13
- package/src/client/IntentLogic.tsx +2 -2
- package/src/client/IntentTab.tsx +118 -47
- package/src/client/KnowledgeBaseTab.tsx +277 -197
- package/src/client/KnowledgeLayerSection.tsx +24 -6
- package/src/client/KnowledgePanel.tsx +58 -93
- package/src/client/KnowledgeSettingsDrawer.tsx +90 -0
- package/src/client/ListPager.tsx +15 -5
- package/src/client/MaterializeTrajectoryView.tsx +124 -0
- package/src/client/NodeSettings.tsx +210 -89
- package/src/client/NormalizeTrajectoryView.tsx +136 -0
- package/src/client/PipelineNotice.tsx +26 -0
- package/src/client/PreviewSections.tsx +118 -51
- package/src/client/RecallPanel.tsx +89 -85
- package/src/client/TabGuide.tsx +159 -184
- package/src/client/TrialReportView.tsx +391 -214
- package/src/client/TypeDefinitionDrawer.tsx +150 -23
- package/src/client/WikiCardPreview.tsx +62 -16
- package/src/client/WikiFlowPanel.tsx +4 -4
- package/src/client/arch/NodeLogicPanel.tsx +3 -4
- package/src/client/arch/model.ts +6 -9
- package/src/client/architecture.tsx +23 -33
- package/src/client/attribution/AttributionGraph.tsx +10 -6
- package/src/client/attribution/AttributionTab.tsx +136 -84
- package/src/client/attribution/CaseView.tsx +3 -1
- package/src/client/attribution/EventView.tsx +108 -0
- package/src/client/attribution/RunView.tsx +2 -2
- package/src/client/attribution/StageChrome.tsx +34 -3
- package/src/client/attribution/ViewSwitch.tsx +20 -5
- package/src/client/attribution/graph-options.ts +47 -39
- package/src/client/attribution/model.ts +17 -10
- package/src/client/config-scope.ts +3 -3
- package/src/client/graph/palette.ts +165 -0
- package/src/client/index.ts +49 -42
- package/src/client/pages.css +459 -135
- package/src/client/pages.tsx +37 -9
- package/src/client/problem-domains.ts +1 -1
- package/src/client/session-remote.ts +9 -0
- package/src/client/styles.css +205 -62
- package/src/client/wiki-trial.ts +44 -3
- package/src/compiler/embedding-identity.ts +21 -0
- package/src/compiler/snapshot.ts +17 -0
- package/src/config-schema.ts +8 -3
- package/src/convert/host.ts +56 -21
- package/src/convert/ingest.ts +74 -10
- package/src/derive-contract.ts +14 -0
- 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 +138 -69
- package/src/diagnosis/types.ts +34 -15
- package/src/environment/dsh-python.ts +156 -0
- package/src/environment/install.ts +244 -0
- package/src/environment/manifest.ts +28 -0
- package/src/environment/model-catalog.ts +147 -0
- package/src/environment/process.ts +54 -0
- package/src/environment/selection.ts +29 -0
- package/src/environment/service.ts +348 -0
- package/src/environment/types.ts +45 -0
- package/src/events.ts +108 -0
- package/src/extract-contract.ts +42 -0
- package/src/extract-model.ts +10 -1
- package/src/extract-table-contract.ts +26 -0
- package/src/extract-table.ts +132 -0
- package/src/extraction-runs.ts +70 -11
- package/src/fs-atomic.ts +19 -2
- package/src/images.ts +203 -0
- package/src/index.ts +553 -205
- package/src/intent/README.md +8 -6
- package/src/intent/engine.ts +44 -6
- package/src/intent/host.ts +59 -18
- package/src/intent/scope.ts +125 -0
- package/src/intent/tools.ts +70 -28
- package/src/intent/types.ts +11 -4
- package/src/knowledge-bases.ts +56 -20
- package/src/knowledge-contract.ts +114 -0
- package/src/knowledge.ts +138 -69
- package/src/layout.ts +17 -37
- package/src/materialize-snapshot.ts +46 -0
- package/src/migrate.ts +75 -2
- package/src/model-text.ts +109 -4
- package/src/oplog.ts +7 -0
- package/src/organize-contract.ts +83 -0
- package/src/pipeline/engine.ts +323 -0
- package/src/pipeline/store.ts +147 -0
- package/src/pipeline/types.ts +93 -0
- package/src/present.ts +10 -4
- package/src/preset-ids.ts +6 -0
- package/src/python/embedding_worker.py +66 -2
- package/src/query-analysis.ts +68 -8
- package/src/recall-rank.ts +5 -5
- package/src/retention.ts +76 -0
- package/src/retrieval/vector-index.ts +45 -0
- package/src/retrieval/visibility.ts +14 -0
- package/src/runtime.ts +2708 -567
- package/src/settings-schema.ts +11 -8
- package/src/settings-volatile.ts +4 -4
- package/src/source-evidence.ts +37 -0
- package/src/store/axis-references.ts +96 -0
- package/src/store/derived.ts +70 -12
- package/src/store/knowledge-db.ts +248 -17
- package/src/store/migrate.ts +16 -6
- package/src/trajectory-types.ts +142 -0
- package/src/types.ts +61 -35
- package/src/wiki-finalize.ts +28 -0
- package/src/wiki-materialize.ts +25 -6
- package/src/wiki-trial-types.ts +83 -1
- package/src/wiki-trial.ts +357 -185
- package/lib/types/intent/preset-tools.d.ts +0 -37
- package/lib/types/intent/preset-tools.d.ts.map +0 -1
- package/lib/types/intent/preset-tools.js +0 -45
- package/lib/types/intent/preset-tools.js.map +0 -1
- package/preset/tita-data-agent/agent.cordis.yml +0 -394
- package/preset/tita-data-agent/preset.yml +0 -3
- package/scripts/install-agent-preset.mjs +0 -92
- package/scripts/sync-agent-preset.mjs +0 -188
- package/src/client/AgentPresetCard.tsx +0 -247
- package/src/client/agent-preset.ts +0 -121
- package/src/client/convert/ConvertPanel.tsx +0 -583
- package/src/client/convert/styles.css +0 -138
- package/src/client/convert/styles.ts +0 -49
- package/src/intent/preset-tools.ts +0 -48
|
@@ -31,8 +31,10 @@ import { createHash } from 'node:crypto'
|
|
|
31
31
|
import { mkdir } from 'node:fs/promises'
|
|
32
32
|
import { dirname } from 'node:path'
|
|
33
33
|
import type { SqliteDatabase, SqliteStatement } from '../fts.js'
|
|
34
|
+
import { applyRetention } from '../retention.js'
|
|
34
35
|
import { KNOWLEDGE_DB_FILE } from '../layout.js'
|
|
35
36
|
import { cardCoordinateKey, legacyCardStorageKey, type CardCoordinate, type KnowledgeCard, type SourceRaw } from '../types.js'
|
|
37
|
+
import type { EventEntity } from '../events.js'
|
|
36
38
|
|
|
37
39
|
/** 知识库里那个文件的名字。定义在 `layout.ts`(布局的唯一真源),这里只是本模块的别名。 */
|
|
38
40
|
export { KNOWLEDGE_DB_FILE }
|
|
@@ -236,7 +238,32 @@ function looksLikeAssetRef(ref: string): boolean {
|
|
|
236
238
|
return /(?:^|\/)_?assets\//.test(ref)
|
|
237
239
|
}
|
|
238
240
|
|
|
239
|
-
/**
|
|
241
|
+
/** Event 与坐标 WikiCard 之间的表结构。 */
|
|
242
|
+
const EVENT_SCHEMA_SQL = `
|
|
243
|
+
CREATE TABLE IF NOT EXISTS event (
|
|
244
|
+
id TEXT PRIMARY KEY,
|
|
245
|
+
identity_key TEXT NOT NULL UNIQUE,
|
|
246
|
+
identity_type TEXT NOT NULL,
|
|
247
|
+
external_id TEXT,
|
|
248
|
+
object_id TEXT NOT NULL DEFAULT '',
|
|
249
|
+
action_id TEXT NOT NULL DEFAULT '',
|
|
250
|
+
scene_id TEXT NOT NULL DEFAULT '',
|
|
251
|
+
occurred_at TEXT,
|
|
252
|
+
payload TEXT NOT NULL,
|
|
253
|
+
created_at TEXT NOT NULL,
|
|
254
|
+
updated_at TEXT NOT NULL
|
|
255
|
+
);
|
|
256
|
+
CREATE INDEX IF NOT EXISTS event_coordinate_idx ON event(object_id, action_id, scene_id);
|
|
257
|
+
CREATE INDEX IF NOT EXISTS event_occurred_at_idx ON event(occurred_at);
|
|
258
|
+
CREATE TABLE IF NOT EXISTS card_event (
|
|
259
|
+
card_id TEXT NOT NULL,
|
|
260
|
+
event_id TEXT NOT NULL,
|
|
261
|
+
ordinal INTEGER NOT NULL DEFAULT 0,
|
|
262
|
+
PRIMARY KEY (card_id, event_id)
|
|
263
|
+
);
|
|
264
|
+
CREATE INDEX IF NOT EXISTS card_event_event_idx ON card_event(event_id);
|
|
265
|
+
`
|
|
266
|
+
|
|
240
267
|
function schemaSql(): string {
|
|
241
268
|
return `
|
|
242
269
|
CREATE TABLE IF NOT EXISTS blob (
|
|
@@ -310,6 +337,8 @@ CREATE TABLE IF NOT EXISTS card_knowledge (
|
|
|
310
337
|
);
|
|
311
338
|
CREATE INDEX IF NOT EXISTS card_knowledge_knowledge_idx ON card_knowledge(knowledge_id);
|
|
312
339
|
|
|
340
|
+
${EVENT_SCHEMA_SQL}
|
|
341
|
+
|
|
313
342
|
CREATE TABLE IF NOT EXISTS revision (
|
|
314
343
|
entity_kind TEXT NOT NULL,
|
|
315
344
|
entity_id TEXT NOT NULL,
|
|
@@ -417,15 +446,25 @@ function rebuildCardTableForSchemaMigration(db: SqliteDatabase): void {
|
|
|
417
446
|
head_version, created_at, updated_at, object_id, action_id, scene_id)
|
|
418
447
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
419
448
|
`)
|
|
449
|
+
/** 已经用掉的落位键。同坐标多行时后面那些要退回身份键(见下面的说明)。 */
|
|
450
|
+
const used = new Set<string>()
|
|
420
451
|
for (const row of rows) {
|
|
421
452
|
const entityId = String(row.entity_id ?? '')
|
|
422
453
|
const objectId = String(row.object_id ?? '')
|
|
423
454
|
const actionId = String(row.action_id ?? '')
|
|
424
455
|
const sceneId = String(row.scene_id ?? '')
|
|
425
456
|
const coordinate = coordinateOfRow({ object_id: objectId, action_id: actionId, scene_id: sceneId })
|
|
426
|
-
|
|
457
|
+
let key = coordinate === undefined
|
|
427
458
|
? legacyCardStorageKey(entityId)
|
|
428
459
|
: cardCoordinateKey(coordinate)
|
|
460
|
+
/**
|
|
461
|
+
* 同一坐标在旧表里可能有多行(那时还没有坐标唯一约束)。`coordinate_key` 是主键,
|
|
462
|
+
* 一旦冲突整次迁移就回滚 —— 而迁移在打开库时跑,于是**每次启动都打不开这个库**,
|
|
463
|
+
* 也没有修复路径。落在后面的那一行退回按 `entityId` 生成的身份键:两张卡都留住,
|
|
464
|
+
* 坐标冲突交给既有的审计路径去报。`entity_id` 在旧表里是唯一的,所以这个键不会重。
|
|
465
|
+
*/
|
|
466
|
+
if (used.has(key)) key = legacyCardStorageKey(entityId)
|
|
467
|
+
used.add(key)
|
|
429
468
|
insert.run(
|
|
430
469
|
key,
|
|
431
470
|
entityId,
|
|
@@ -587,6 +626,8 @@ interface ColumnInfo {
|
|
|
587
626
|
name: string
|
|
588
627
|
notNull: boolean
|
|
589
628
|
hasDefault: boolean
|
|
629
|
+
/** 主键里的序号(1 起);0 = 不是主键的一部分。判「是不是同一行」要用它。 */
|
|
630
|
+
pk: number
|
|
590
631
|
}
|
|
591
632
|
|
|
592
633
|
/**
|
|
@@ -685,7 +726,7 @@ export class KnowledgeTx {
|
|
|
685
726
|
* 会把「只改备注」「只补图」那几条路径变成一次静默的关联丢失 —— 它们本来就不该
|
|
686
727
|
* 碰落位(那件事只有编译知道)。
|
|
687
728
|
*/
|
|
688
|
-
putCard(card: KnowledgeCard, frontmatter: Record<string, unknown>, journal: Journal, placements?: CardKnowledgeRow[]): WriteOutcome {
|
|
729
|
+
putCard(card: KnowledgeCard, frontmatter: Record<string, unknown>, journal: Journal, placements?: CardKnowledgeRow[], allowCoordinateMove = false): WriteOutcome {
|
|
689
730
|
const outcome = this.putEntity('card', card.entityId, card.content, frontmatter, journal, () => {
|
|
690
731
|
const existing = this.raw('SELECT coordinate_key, object_id, action_id, scene_id FROM card WHERE entity_id = ?').get(card.entityId) as Record<string, unknown> | undefined
|
|
691
732
|
/*
|
|
@@ -698,7 +739,7 @@ export class KnowledgeTx {
|
|
|
698
739
|
: cardCoordinateKey(storedCoordinate)
|
|
699
740
|
const oldKey = typeof existing?.coordinate_key === 'string' ? existing.coordinate_key : undefined
|
|
700
741
|
if (existing !== undefined) {
|
|
701
|
-
if (oldKey !== undefined && oldKey !== coordinateKey && !oldKey.startsWith('legacy:')) {
|
|
742
|
+
if (!allowCoordinateMove && oldKey !== undefined && oldKey !== coordinateKey && !oldKey.startsWith('legacy:')) {
|
|
702
743
|
throw new Error(`CARD_COORDINATE_IMMUTABLE: ${card.entityId} 已绑定坐标,不能直接改主键`)
|
|
703
744
|
}
|
|
704
745
|
this.raw(`UPDATE card SET coordinate_key = ?, entity_type = ?, title = ?, status = ?, owner = ?,
|
|
@@ -739,6 +780,7 @@ export class KnowledgeTx {
|
|
|
739
780
|
/** 删掉一张卡时它的关联行一起走(**代码责任**,这里没有外键)。 */
|
|
740
781
|
dropPlacements(cardId: string): void {
|
|
741
782
|
this.raw('DELETE FROM card_knowledge WHERE card_id = ?').run(cardId)
|
|
783
|
+
this.raw('DELETE FROM card_event WHERE card_id = ?').run(cardId)
|
|
742
784
|
}
|
|
743
785
|
|
|
744
786
|
/**
|
|
@@ -804,7 +846,7 @@ export class KnowledgeTx {
|
|
|
804
846
|
* 下一次编译会把这批行当成「这张卡引用的知识」,而那张卡已经不存在了:表现是
|
|
805
847
|
* 对账里冒出一批 `missingCard`,以及一条知识的「属于哪张卡」指向 404。
|
|
806
848
|
*/
|
|
807
|
-
if (kind === 'card') this.
|
|
849
|
+
if (kind === 'card') this.dropPlacements(id)
|
|
808
850
|
this.manifest.set(`${kind}/${id}`, 0)
|
|
809
851
|
}
|
|
810
852
|
|
|
@@ -821,6 +863,9 @@ export class KnowledgeTx {
|
|
|
821
863
|
* 短事务。异步化只会把调用方的顺序打散,换不到任何东西。
|
|
822
864
|
*/
|
|
823
865
|
export class KnowledgeDb {
|
|
866
|
+
private transactionSequence = 0
|
|
867
|
+
/** 距离上一次保留裁剪又提交了几次写事务。 */
|
|
868
|
+
private writesSincePrune = 0
|
|
824
869
|
private constructor(private readonly db: SqliteDatabase, readonly file: string) {}
|
|
825
870
|
|
|
826
871
|
/**
|
|
@@ -849,7 +894,6 @@ export class KnowledgeDb {
|
|
|
849
894
|
return { ok: false, code: 'OPEN_FAILED', message: `打开失败:${cause instanceof Error ? cause.message : String(cause)}` }
|
|
850
895
|
}
|
|
851
896
|
try {
|
|
852
|
-
db.exec('PRAGMA journal_mode = WAL')
|
|
853
897
|
const owner = readPragma(db, 'application_id')
|
|
854
898
|
const version = readPragma(db, 'user_version')
|
|
855
899
|
/*
|
|
@@ -873,11 +917,12 @@ export class KnowledgeDb {
|
|
|
873
917
|
* 先开的那个模块就会让另一个模块判成「这不是知识库」—— 表现是 `addSource` 报
|
|
874
918
|
* 知识库不可用,而文件明明好好的。
|
|
875
919
|
*/
|
|
876
|
-
const ownTables = ['knowledge', 'knowledge_sources', 'axis_entities', 'axis_aliases']
|
|
920
|
+
const ownTables = ['knowledge', 'knowledge_sources', 'axis_entities', 'axis_aliases', 'event', 'card_event']
|
|
877
921
|
const hasUserTables = userTableCount(db) > 0
|
|
878
|
-
const fresh = version === 0 && !hasUserTables
|
|
879
|
-
const unmarkedKnowledgeDb = version === 0 && ownTables.some(name => tableExists(db, name))
|
|
922
|
+
const fresh = owner === 0 && version === 0 && !hasUserTables
|
|
923
|
+
const unmarkedKnowledgeDb = owner === 0 && version === 0 && ownTables.some(name => tableExists(db, name))
|
|
880
924
|
if (fresh) {
|
|
925
|
+
db.exec('PRAGMA journal_mode = WAL')
|
|
881
926
|
db.exec(schemaSql())
|
|
882
927
|
db.exec(`PRAGMA application_id = ${KNOWLEDGE_DB_APPLICATION_ID}`)
|
|
883
928
|
db.exec(`PRAGMA user_version = ${KNOWLEDGE_DB_SCHEMA}`)
|
|
@@ -891,9 +936,12 @@ export class KnowledgeDb {
|
|
|
891
936
|
db.close()
|
|
892
937
|
return { ok: false, code: 'SCHEMA_TOO_NEW', message: `知识库的 schema 是 ${version},本版本只认到 ${KNOWLEDGE_DB_SCHEMA}:${file}` }
|
|
893
938
|
}
|
|
939
|
+
db.exec('PRAGMA journal_mode = WAL')
|
|
894
940
|
if (version < KNOWLEDGE_DB_SCHEMA) {
|
|
895
941
|
migrateSchema(db, version)
|
|
896
942
|
}
|
|
943
|
+
// 确保知识库包含 Event 实体表和 WikiCard 聚合关系表。
|
|
944
|
+
db.exec(EVENT_SCHEMA_SQL)
|
|
897
945
|
return { ok: true, db: new KnowledgeDb(db, file) }
|
|
898
946
|
} catch (cause) {
|
|
899
947
|
try { db.close() } catch { /* already closed */ }
|
|
@@ -902,11 +950,16 @@ export class KnowledgeDb {
|
|
|
902
950
|
}
|
|
903
951
|
|
|
904
952
|
close(): void {
|
|
953
|
+
// 关库前收一次尾:否则「最后一次裁剪之后新增的行」会一直留到下次打开。
|
|
954
|
+
this.pruneRetention()
|
|
905
955
|
try { this.db.close() } catch { /* already closed */ }
|
|
906
956
|
}
|
|
907
957
|
|
|
908
958
|
raw(sql: string): SqliteStatement { return this.db.prepare(sql) }
|
|
909
959
|
|
|
960
|
+
/** Repositories share this validated connection; only its owner closes it. */
|
|
961
|
+
connection(): SqliteDatabase { return this.db }
|
|
962
|
+
|
|
910
963
|
/**
|
|
911
964
|
* 一次操作 = 一个事务 = 一行 `tree` + 若干行 `revision`。
|
|
912
965
|
*
|
|
@@ -920,7 +973,8 @@ export class KnowledgeDb {
|
|
|
920
973
|
transact<T>(entry: TreeEntry, fn: (tx: KnowledgeTx) => T): T {
|
|
921
974
|
const tx = new KnowledgeTx(this)
|
|
922
975
|
const at = entry.at ?? new Date().toISOString()
|
|
923
|
-
this.
|
|
976
|
+
const savepoint = `knowledge_op_${++this.transactionSequence}`
|
|
977
|
+
this.db.exec(`SAVEPOINT ${savepoint}`)
|
|
924
978
|
try {
|
|
925
979
|
/*
|
|
926
980
|
* `INSERT OR REPLACE`:`op_id` 是**幂等键**。
|
|
@@ -933,14 +987,66 @@ export class KnowledgeDb {
|
|
|
933
987
|
.run(entry.opId, at, entry.actor, entry.summary, '{}')
|
|
934
988
|
const result = fn(tx)
|
|
935
989
|
this.raw('UPDATE tree SET manifest = ? WHERE op_id = ?').run(tx.manifestJson(), entry.opId)
|
|
936
|
-
this.db.exec(
|
|
990
|
+
this.db.exec(`RELEASE ${savepoint}`)
|
|
991
|
+
/**
|
|
992
|
+
* 裁剪**每 50 次写入**做一次,不是每次。
|
|
993
|
+
*
|
|
994
|
+
* 每次都做的话,一次卡片写入要额外跑一遍 `ROW_NUMBER()` 按实体分区的窗口(水位以上
|
|
995
|
+
* 收敛到约 20 版/实体,5000 个实体就是十万行排序)外加一次 blob 引用集比对 ——
|
|
996
|
+
* 那是把维护成本摊到每一次交互上。每 50 次一次,界面上感觉不到,库也照样有界。
|
|
997
|
+
*/
|
|
998
|
+
if (++this.writesSincePrune >= 50) {
|
|
999
|
+
this.writesSincePrune = 0
|
|
1000
|
+
this.pruneRetention()
|
|
1001
|
+
}
|
|
937
1002
|
return result
|
|
938
1003
|
} catch (cause) {
|
|
939
|
-
try { this.db.exec(
|
|
1004
|
+
try { this.db.exec(`ROLLBACK TO ${savepoint}; RELEASE ${savepoint}`) } catch { /* rollback of a dead tx */ }
|
|
940
1005
|
throw cause
|
|
941
1006
|
}
|
|
942
1007
|
}
|
|
943
1008
|
|
|
1009
|
+
/**
|
|
1010
|
+
* 保留上限(0070)。
|
|
1011
|
+
*
|
|
1012
|
+
* 文档里写了保留策略,代码里一条 `DELETE` 都没有。这里补上**新数据**的上限,
|
|
1013
|
+
* 且只裁「水位以上」的行 —— 升级那一刻已有的历史一行不动(见 `retention.ts`)。
|
|
1014
|
+
*
|
|
1015
|
+
* - `revision`:机器版本(`auto`)每个实体留最近 20 版;**命名版本永不裁**
|
|
1016
|
+
* (`named` 是人对内容做的决定,保留策略里它是永久的)。
|
|
1017
|
+
* - `pipeline_extractions`:抽取检查点同时是复用缓存,每份来源留最近 5 份;
|
|
1018
|
+
* `pipeline_sources` 指着的那两份(当前抽取 / 当前归一)绝不删 ——
|
|
1019
|
+
* 删了会让那份来源显示成「还没抽取」。
|
|
1020
|
+
* - `blob`:水位以上、已经没有任何 revision 引用的才删。水位以下的 blob 一律留着,
|
|
1021
|
+
* 因为老版本可能还在引用它们。
|
|
1022
|
+
* - `tree`:操作台账,与处理日志同一量级。
|
|
1023
|
+
*/
|
|
1024
|
+
private pruneRetention(): void {
|
|
1025
|
+
/*
|
|
1026
|
+
* 每一张都要先问在不在:`pipeline_extractions` 与 `pipeline_sources` 是**管线模块**
|
|
1027
|
+
* 懒建的(`PipelineStore` 的构造函数),而 knowledge.db 可能被别的模块先打开、
|
|
1028
|
+
* 也可能根本没跑过管线。少了这一步,这里会以「no such table」把**每一次写入**都打挂。
|
|
1029
|
+
*
|
|
1030
|
+
* 整个方法包在 try 里还有一层理由:裁剪是**尽力而为**的维护动作,它绝不能成为
|
|
1031
|
+
* 一次已提交写入的失败原因。真出错时宁可不裁(库继续变大),也不让用户写不进去。
|
|
1032
|
+
*/
|
|
1033
|
+
try {
|
|
1034
|
+
if (tableExists(this.db, 'revision')) {
|
|
1035
|
+
applyRetention(this.db, 'revision', { table: 'revision', key: 'rowid', keep: 20, where: "kind = 'auto'", groupBy: "entity_kind || '/' || entity_id" })
|
|
1036
|
+
}
|
|
1037
|
+
if (tableExists(this.db, 'pipeline_extractions') && tableExists(this.db, 'pipeline_sources')) {
|
|
1038
|
+
applyRetention(this.db, 'pipeline_extractions', {
|
|
1039
|
+
table: 'pipeline_extractions', key: 'rowid', keep: 5, groupBy: 'source_id',
|
|
1040
|
+
where: "id NOT IN (SELECT extracted_id FROM pipeline_sources WHERE extracted_id IS NOT NULL) AND id NOT IN (SELECT normalized_id FROM pipeline_sources WHERE normalized_id IS NOT NULL)",
|
|
1041
|
+
})
|
|
1042
|
+
}
|
|
1043
|
+
if (tableExists(this.db, 'blob') && tableExists(this.db, 'revision')) {
|
|
1044
|
+
applyRetention(this.db, 'blob', { table: 'blob', key: 'rowid', keep: 0, where: 'hash NOT IN (SELECT blob_hash FROM revision)' })
|
|
1045
|
+
}
|
|
1046
|
+
if (tableExists(this.db, 'tree')) applyRetention(this.db, 'tree', { table: 'tree', key: 'rowid', keep: 2000 })
|
|
1047
|
+
} catch { /* 裁剪是尽力而为:不让它成为写入失败的原因 */ }
|
|
1048
|
+
}
|
|
1049
|
+
|
|
944
1050
|
/** 记一次**没有内容变更**的操作(迁移收尾、审核结论这一类)。 */
|
|
945
1051
|
recordOperation(entry: TreeEntry, manifest: Record<string, number> = {}): void {
|
|
946
1052
|
this.raw('INSERT OR REPLACE INTO tree(op_id, at, actor, summary, manifest) VALUES (?, ?, ?, ?, ?)')
|
|
@@ -1008,6 +1114,93 @@ export class KnowledgeDb {
|
|
|
1008
1114
|
sources(): SourceRecord[] { return this.readEntities('source') as SourceRecord[] }
|
|
1009
1115
|
cards(): CardRecord[] { return this.readEntities('card') as CardRecord[] }
|
|
1010
1116
|
|
|
1117
|
+
/** Knowledge 的同层事实实体。事件正文是结构化 JSON,不经过 WikiCard 的内容版本表。 */
|
|
1118
|
+
events(): EventEntity[] {
|
|
1119
|
+
const rows = this.raw('SELECT payload FROM event ORDER BY occurred_at DESC, id').all() as Array<{ payload?: unknown }>
|
|
1120
|
+
const out: EventEntity[] = []
|
|
1121
|
+
for (const row of rows) {
|
|
1122
|
+
if (typeof row.payload !== 'string') continue
|
|
1123
|
+
try {
|
|
1124
|
+
const parsed: unknown = JSON.parse(row.payload)
|
|
1125
|
+
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) out.push({ ...(parsed as EventEntity), kind: 'case', version: (parsed as EventEntity).version ?? 1 })
|
|
1126
|
+
} catch { /* 单条坏记录不阻断其他 Event 的读取 */ }
|
|
1127
|
+
}
|
|
1128
|
+
return out
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
event(id: string): EventEntity | undefined {
|
|
1132
|
+
const row = this.raw('SELECT payload FROM event WHERE id = ?').get(id) as { payload?: unknown } | undefined
|
|
1133
|
+
if (typeof row?.payload !== 'string') return undefined
|
|
1134
|
+
try {
|
|
1135
|
+
const parsed: unknown = JSON.parse(row.payload)
|
|
1136
|
+
return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed) ? { ...(parsed as EventEntity), kind: 'case', version: (parsed as EventEntity).version ?? 1 } : undefined
|
|
1137
|
+
} catch { return undefined }
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
writeEvent(event: EventEntity): void {
|
|
1141
|
+
const previous = this.event(event.id)
|
|
1142
|
+
const contentOf = (value: EventEntity): string => JSON.stringify({ ...value, kind: 'case', version: undefined, createdAt: undefined, updatedAt: undefined })
|
|
1143
|
+
event.kind = 'case'
|
|
1144
|
+
const latest = this.raw("SELECT MAX(version) AS v FROM revision WHERE entity_kind='case' AND entity_id=?").get(event.id) as { v?: number } | undefined
|
|
1145
|
+
event.version = previous && contentOf(previous) === contentOf(event) ? previous.version ?? 1 : Math.max(previous?.version ?? 0, latest?.v ?? 0) + 1
|
|
1146
|
+
const sql = this.connection()
|
|
1147
|
+
sql.exec('SAVEPOINT write_case')
|
|
1148
|
+
try {
|
|
1149
|
+
this.raw(`INSERT INTO event(id, identity_key, identity_type, external_id, object_id, action_id, scene_id, occurred_at, payload, created_at, updated_at)
|
|
1150
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1151
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
1152
|
+
identity_key = excluded.identity_key,
|
|
1153
|
+
identity_type = excluded.identity_type,
|
|
1154
|
+
external_id = excluded.external_id,
|
|
1155
|
+
object_id = excluded.object_id,
|
|
1156
|
+
action_id = excluded.action_id,
|
|
1157
|
+
scene_id = excluded.scene_id,
|
|
1158
|
+
occurred_at = excluded.occurred_at,
|
|
1159
|
+
payload = excluded.payload,
|
|
1160
|
+
updated_at = excluded.updated_at`)
|
|
1161
|
+
.run(
|
|
1162
|
+
event.id, event.identityKey, event.identityType, event.externalId,
|
|
1163
|
+
event.coordinateIds.objectId, event.coordinateIds.actionId, event.coordinateIds.sceneId,
|
|
1164
|
+
event.occurredAt, JSON.stringify(event), event.createdAt, event.updatedAt,
|
|
1165
|
+
)
|
|
1166
|
+
const alreadySaved = this.raw("SELECT 1 FROM revision WHERE entity_kind='case' AND entity_id=? AND version=?").get(event.id, event.version)
|
|
1167
|
+
if (!alreadySaved) {
|
|
1168
|
+
const body = JSON.stringify(event)
|
|
1169
|
+
const blobHash = createHash('sha256').update(body).digest('hex')
|
|
1170
|
+
this.raw('INSERT OR IGNORE INTO blob(hash,body,bytes) VALUES (?,?,?)').run(blobHash, body, Buffer.byteLength(body))
|
|
1171
|
+
this.raw("INSERT OR IGNORE INTO revision(entity_kind,entity_id,version,blob_hash,actor,kind,summary,at,parent_version,frontmatter) VALUES ('case',?,?,?,?,?,?,?,?,?)")
|
|
1172
|
+
.run(event.id, event.version, blobHash, 'compiler', 'auto', '保存 Case 过程与证据', event.updatedAt, previous?.version ?? null, '{}')
|
|
1173
|
+
}
|
|
1174
|
+
sql.exec('RELEASE write_case')
|
|
1175
|
+
} catch (cause) { sql.exec('ROLLBACK TO write_case; RELEASE write_case'); throw cause }
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
caseVersion(id: string, version: number): EventEntity | undefined {
|
|
1179
|
+
const row = this.raw("SELECT b.body FROM revision r JOIN blob b ON b.hash=r.blob_hash WHERE r.entity_kind='case' AND r.entity_id=? AND r.version=?").get(id, version) as { body?: unknown } | undefined
|
|
1180
|
+
if (typeof row?.body !== 'string') return undefined
|
|
1181
|
+
try { return JSON.parse(row.body) as EventEntity } catch { return undefined }
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
removeEvent(id: string): void {
|
|
1185
|
+
this.raw('DELETE FROM card_event WHERE event_id = ?').run(id)
|
|
1186
|
+
this.raw('DELETE FROM event WHERE id = ?').run(id)
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
cardEventIds(cardId: string): string[] {
|
|
1190
|
+
return (this.raw('SELECT event_id FROM card_event WHERE card_id = ? ORDER BY ordinal, event_id').all(cardId) as Array<{ event_id?: unknown }>)
|
|
1191
|
+
.flatMap(row => typeof row.event_id === 'string' ? [row.event_id] : [])
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
setCardEvents(cardId: string, eventIds: string[]): boolean {
|
|
1195
|
+
const next = [...new Set(eventIds)].sort()
|
|
1196
|
+
const current = this.cardEventIds(cardId)
|
|
1197
|
+
if (JSON.stringify(current) === JSON.stringify(next)) return false
|
|
1198
|
+
this.raw('DELETE FROM card_event WHERE card_id = ?').run(cardId)
|
|
1199
|
+
const insert = this.raw('INSERT INTO card_event(card_id, event_id, ordinal) VALUES (?, ?, ?)')
|
|
1200
|
+
next.forEach((eventId, ordinal) => insert.run(cardId, eventId, ordinal))
|
|
1201
|
+
return true
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1011
1204
|
source(id: string): SourceRecord | undefined {
|
|
1012
1205
|
return (this.readEntities('source', id) as SourceRecord[])[0]
|
|
1013
1206
|
}
|
|
@@ -1184,12 +1377,13 @@ export class KnowledgeDb {
|
|
|
1184
1377
|
* 名字拼成语法错误。
|
|
1185
1378
|
*/
|
|
1186
1379
|
private columnsOf(schema: 'main' | 'legacy', table: string): ColumnInfo[] {
|
|
1187
|
-
const rows = this.raw('SELECT name, "notnull" AS not_null, dflt_value AS dflt FROM pragma_table_info(?, ?)')
|
|
1380
|
+
const rows = this.raw('SELECT name, "notnull" AS not_null, dflt_value AS dflt, pk FROM pragma_table_info(?, ?)')
|
|
1188
1381
|
.all(table, schema) as Array<Record<string, unknown>>
|
|
1189
1382
|
return rows.map(row => ({
|
|
1190
1383
|
name: String(row.name ?? ''),
|
|
1191
1384
|
notNull: Number(row.not_null ?? 0) === 1,
|
|
1192
1385
|
hasDefault: row.dflt !== null && row.dflt !== undefined,
|
|
1386
|
+
pk: Number(row.pk ?? 0),
|
|
1193
1387
|
}))
|
|
1194
1388
|
}
|
|
1195
1389
|
|
|
@@ -1266,7 +1460,8 @@ export class KnowledgeDb {
|
|
|
1266
1460
|
const mainNames = new Set(mainColumns.map(column => column.name))
|
|
1267
1461
|
const legacyNames = new Set(legacyColumns.map(column => column.name))
|
|
1268
1462
|
const shared = legacyColumns.filter(column => mainNames.has(column.name)).map(column => column.name)
|
|
1269
|
-
const dropped = legacyColumns.filter(column => !mainNames.has(column.name)
|
|
1463
|
+
const dropped = legacyColumns.filter(column => !mainNames.has(column.name)
|
|
1464
|
+
&& !((name === 'knowledge_sources' || name === 'source') && column.name === 'source_type')).map(column => column.name)
|
|
1270
1465
|
const required = mainColumns.filter(column => !legacyNames.has(column.name) && column.notNull && !column.hasDefault)
|
|
1271
1466
|
if (dropped.length > 0) problems.push(`${name} 的这几列在目标表里没有,内容搬不过来:${dropped.join('、')}`)
|
|
1272
1467
|
if (required.length > 0) {
|
|
@@ -1276,11 +1471,47 @@ export class KnowledgeDb {
|
|
|
1276
1471
|
if (shared.length === 0) { problems.push(`${name} 与目标表没有同名的列,行搬不动`); continue }
|
|
1277
1472
|
const columns = shared.map(quoteIdentifier).join(', ')
|
|
1278
1473
|
const expected = Number((this.db.prepare(`SELECT count(*) AS c FROM legacy.${quoteIdentifier(name)}`).get() as { c?: unknown } | undefined)?.c ?? 0)
|
|
1279
|
-
|
|
1474
|
+
/**
|
|
1475
|
+
* 「有没有丢行」要按**三个账**算(0070):`already`(插入**之前**主键就已存在)、
|
|
1476
|
+
* `inserted`(本次真的插进去的)、以及两者之外的 `unaccounted`
|
|
1477
|
+
* —— 被唯一索引 / 非空 / 检查约束挡掉的那些才是真的丢。
|
|
1478
|
+
*
|
|
1479
|
+
* **顺序很要紧**:`already` 必须在 `INSERT` 之前数。插完再数会把本次插入的行
|
|
1480
|
+
* 也算进去,重复计数恰好把丢行掩盖成 0(试过,就是这样漏掉一行的)。
|
|
1481
|
+
*
|
|
1482
|
+
* 原来那条 `copied < expected`(拿目标**总行数**比旧文件行数)则**永远不成立** ——
|
|
1483
|
+
* 每一次主键冲突都对应一行早就存在 —— 于是丢行之后调用方还会把旧库删掉。
|
|
1484
|
+
*/
|
|
1485
|
+
const keyColumns = this.columnsOf('main', name).filter(column => column.pk > 0).map(column => column.name)
|
|
1486
|
+
const comparable = shared.filter(column => !keyColumns.includes(column))
|
|
1487
|
+
const already = keyColumns.length === 0 ? 0 : Number((this.db.prepare(
|
|
1488
|
+
`SELECT COUNT(*) AS c FROM legacy.${quoteIdentifier(name)} l WHERE EXISTS (
|
|
1489
|
+
SELECT 1 FROM main.${quoteIdentifier(name)} m WHERE ${keyColumns.map(column => `m.${quoteIdentifier(column)} IS l.${quoteIdentifier(column)}`).join(' AND ')}
|
|
1490
|
+
)`,
|
|
1491
|
+
).get() as { c?: unknown } | undefined)?.c ?? 0)
|
|
1492
|
+
const run = this.db.prepare(`INSERT OR IGNORE INTO main.${quoteIdentifier(name)} (${columns}) SELECT ${columns} FROM legacy.${quoteIdentifier(name)}`).run() as { changes?: unknown } | undefined
|
|
1493
|
+
const inserted = Number(run?.changes ?? 0)
|
|
1280
1494
|
const copied = Number((this.db.prepare(`SELECT count(*) AS c FROM main.${quoteIdentifier(name)}`).get() as { c?: unknown } | undefined)?.c ?? 0)
|
|
1281
1495
|
tables[name] = copied
|
|
1282
|
-
|
|
1283
|
-
|
|
1496
|
+
if (keyColumns.length === 0) {
|
|
1497
|
+
if (inserted < expected) problems.push(`${name} 只拷进 ${inserted} 行(旧文件 ${expected} 行):这张表没有主键,无法逐行核对,旧文件不删`)
|
|
1498
|
+
} else {
|
|
1499
|
+
const unaccounted = expected - already - inserted
|
|
1500
|
+
if (unaccounted > 0) problems.push(`${name} 有 ${unaccounted} 行没能进来(主键之外还有唯一索引 / 非空 / 检查约束挡住的行):旧文件不删`)
|
|
1501
|
+
/**
|
|
1502
|
+
* 纯主键表(例如 `entity_asset(entity_kind, entity_id, ref)`)没有可比的非键列:
|
|
1503
|
+
* 主键相同就是同一行。重跑时报「无法比对内容」是误报,会让旧文件永久留在磁盘上。
|
|
1504
|
+
*/
|
|
1505
|
+
if (comparable.length > 0) {
|
|
1506
|
+
const onEq = keyColumns.map(column => `m.${quoteIdentifier(column)} = l.${quoteIdentifier(column)}`).join(' AND ')
|
|
1507
|
+
// `IS NOT` 而不是 `<>`:NULL 与任何值都不相等,用 `<>` 会把「一边是 NULL」漏判。
|
|
1508
|
+
const differs = Number((this.db.prepare(
|
|
1509
|
+
`SELECT COUNT(*) AS c FROM legacy.${quoteIdentifier(name)} l JOIN main.${quoteIdentifier(name)} m ON ${onEq}
|
|
1510
|
+
WHERE ${comparable.map(column => `m.${quoteIdentifier(column)} IS NOT l.${quoteIdentifier(column)}`).join(' OR ')}`,
|
|
1511
|
+
).get() as { c?: unknown } | undefined)?.c ?? 0)
|
|
1512
|
+
if (differs > 0) problems.push(`${name} 有 ${differs} 行与目标库主键相同但内容不同:旧文件不删`)
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1284
1515
|
} catch (cause) {
|
|
1285
1516
|
problems.push(`${name} 的行没拷成:${cause instanceof Error ? cause.message : String(cause)}`)
|
|
1286
1517
|
}
|
package/src/store/migrate.ts
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
import { existsSync } from 'node:fs'
|
|
36
36
|
import { cp, mkdir, readFile, readdir, rename, rm, writeFile } from 'node:fs/promises'
|
|
37
37
|
import { basename, dirname, join, relative } from 'node:path'
|
|
38
|
-
import { ASSETS_DIR, SOURCE_RAW_DIR } from '../layout.js'
|
|
38
|
+
import { ASSETS_DIR, PUBLISHED_IMAGE_MARKER, SOURCE_RAW_DIR } from '../layout.js'
|
|
39
39
|
import type { KnowledgeCard, SourceRaw } from '../types.js'
|
|
40
40
|
import type { KnowledgeDb } from './knowledge-db.js'
|
|
41
41
|
|
|
@@ -240,7 +240,17 @@ export async function migrateKnowledgeTree(input: MigrationInput): Promise<Migra
|
|
|
240
240
|
})
|
|
241
241
|
}
|
|
242
242
|
db.transact({ opId: sourceOp(opId, 'source', sourceId), actor: 'migration', summary: '迁移来源', at: record.updatedAt }, tx => {
|
|
243
|
-
|
|
243
|
+
const frontmatter = { ...parsed.frontmatter }
|
|
244
|
+
const origin = typeof record.metadata.source_url === 'string' ? record.metadata.source_url.trim() : ''
|
|
245
|
+
if (origin) {
|
|
246
|
+
frontmatter.resource ??= origin
|
|
247
|
+
frontmatter.sources ??= [{ id: 'origin', resource: origin, title: record.title }]
|
|
248
|
+
}
|
|
249
|
+
const parser = typeof record.metadata.parser === 'string' ? record.metadata.parser.trim() : ''
|
|
250
|
+
if (parser && typeof parsed.frontmatter.created_at === 'string') {
|
|
251
|
+
frontmatter.generated ??= { by: parser.startsWith('convert/') ? parser : `convert/${parser}`, at: parsed.frontmatter.created_at }
|
|
252
|
+
}
|
|
253
|
+
tx.putSource(record, frontmatter, { actor: 'migration', kind: 'named', at: record.createdAt })
|
|
244
254
|
})
|
|
245
255
|
report.sources += 1
|
|
246
256
|
}
|
|
@@ -290,7 +300,7 @@ export async function migrateKnowledgeTree(input: MigrationInput): Promise<Migra
|
|
|
290
300
|
const to = join(kbRoot, ASSETS_DIR, bucket)
|
|
291
301
|
report.assetBuckets += 1
|
|
292
302
|
if (dryRun) {
|
|
293
|
-
report.assetFiles += (await listFiles(from, '')).length
|
|
303
|
+
report.assetFiles += (await listFiles(from, '')).filter(path => basename(path) !== PUBLISHED_IMAGE_MARKER).length
|
|
294
304
|
continue
|
|
295
305
|
}
|
|
296
306
|
try {
|
|
@@ -306,7 +316,7 @@ export async function migrateKnowledgeTree(input: MigrationInput): Promise<Migra
|
|
|
306
316
|
*/
|
|
307
317
|
if (!existsSync(to)) await rename(from, to)
|
|
308
318
|
else await mergeAssetBucket(from, to)
|
|
309
|
-
report.assetFiles += (await listFiles(to, '')).length
|
|
319
|
+
report.assetFiles += (await listFiles(to, '')).filter(path => basename(path) !== PUBLISHED_IMAGE_MARKER).length
|
|
310
320
|
} catch (cause) {
|
|
311
321
|
problems.push(`搬不动图片桶 ${bucket}:${cause instanceof Error ? cause.message : String(cause)}`)
|
|
312
322
|
}
|
|
@@ -515,7 +525,7 @@ export async function exportKnowledgeTree(input: {
|
|
|
515
525
|
for (const item of db.sources()) {
|
|
516
526
|
if (only !== undefined && item.record.sourceId !== only) continue
|
|
517
527
|
const path = join(targetRoot, LEGACY_SOURCE, sourceName(item.record.title, item.record.sourceId))
|
|
518
|
-
if (await write(path, serialize(item.frontmatter, item.record.content))) sources += 1
|
|
528
|
+
if (await write(path, serialize({ ...item.frontmatter, source_id: item.record.sourceId }, item.record.content))) sources += 1
|
|
519
529
|
}
|
|
520
530
|
for (const item of db.cards()) {
|
|
521
531
|
const path = join(targetRoot, LEGACY_WIKI, item.card.entityType, cardName(item.card.entityId))
|
|
@@ -555,7 +565,7 @@ async function copyContentTree(from: string, to: string, overwrite: boolean): Pr
|
|
|
555
565
|
if (!overwrite && existsSync(target)) continue
|
|
556
566
|
await mkdir(dirname(target), { recursive: true })
|
|
557
567
|
await cp(source, target, { force: true })
|
|
558
|
-
copied += 1
|
|
568
|
+
if (basename(source) !== PUBLISHED_IMAGE_MARKER) copied += 1
|
|
559
569
|
}
|
|
560
570
|
return copied
|
|
561
571
|
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 三段轨迹的**传输形状**(抽取 / 归一 / 物化)。
|
|
3
|
+
*
|
|
4
|
+
* 抽取那段本来就有报告(`TrialReport`,见 `wiki-trial-types.ts`),形状没动。这一份只补
|
|
5
|
+
* 新分开的两段 —— 它们此前**没有轨迹**:只有任务里一段流式输出和一张结果表,跑完就查不到
|
|
6
|
+
* 「这条知识是怎么从模型给的名字变成库里的坐标的」「这张卡是按什么排出来的」。
|
|
7
|
+
*
|
|
8
|
+
* ## 为什么这些类型住在浏览器安全的一层
|
|
9
|
+
*
|
|
10
|
+
* 服务端按这个形状写,客户端按同一个形状渲染。写两份就会漂。它们不 import 任何
|
|
11
|
+
* `node:*`,所以可以直接被两边引用 —— 这与 `pipeline/types.ts` 是同一条规矩。
|
|
12
|
+
* (`client/wiki-trial.ts` 里那份手写镜像之所以必须手写,是因为它需要引用的服务端模块
|
|
13
|
+
* import 了 `node:fs`;这里没有那个问题,所以不重复。)
|
|
14
|
+
*
|
|
15
|
+
* ## 一份轨迹对应「一次工作」,不是一次任务
|
|
16
|
+
*
|
|
17
|
+
* - 抽取:一份来源 × 一次运行。
|
|
18
|
+
* - 归一:一份来源 × 一次运行(复用的那一次不落轨迹 —— 那一次什么都没做)。
|
|
19
|
+
* - 物化:一个卡片分组 × 一次运行。
|
|
20
|
+
*
|
|
21
|
+
* 粒度定在这里,是因为这三件事的失败与复用手忙脚乱的程度不同:一份文档抽取失败不影响
|
|
22
|
+
* 另一份;一张卡物化失败也不该让同批其它卡的轨迹看起来像没跑。
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** 轨迹属于哪一段。与 `PipelineStage` 的前三段同形(`all` 不是轨迹)。 */
|
|
26
|
+
export type TrajectoryStage = 'extract' | 'normalize' | 'materialize'
|
|
27
|
+
|
|
28
|
+
/** 三个名字只在这里写一次:页标题、列表徽标、日志都用它。 */
|
|
29
|
+
export const TRAJECTORY_STAGE_LABEL: Record<TrajectoryStage, string> = {
|
|
30
|
+
extract: '抽取轨迹',
|
|
31
|
+
normalize: '归一轨迹',
|
|
32
|
+
materialize: '物化轨迹',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** 挤在行内时用的短名(列表徽标、筛选器)。 */
|
|
36
|
+
export const TRAJECTORY_STAGE_SHORT: Record<TrajectoryStage, string> = {
|
|
37
|
+
extract: '抽取',
|
|
38
|
+
normalize: '归一',
|
|
39
|
+
materialize: '物化',
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** 每条轨迹都要有的那段公共头。 */
|
|
43
|
+
export interface TrajectoryHead {
|
|
44
|
+
at: string
|
|
45
|
+
/** 模型接缝当时通不通。抽取以外的两段也可能根本没有模型调用。 */
|
|
46
|
+
model: { ok: boolean; why?: string; provider?: string; model?: string; reasoningEffort?: string }
|
|
47
|
+
warnings: string[]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** 坐标三个分量在轨迹里只作为**展示文本**出现(真源是轴注册表的 id)。 */
|
|
51
|
+
export type TrajectoryAxisKind = 'object' | 'action' | 'scene'
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 归一的坐标解析结论。
|
|
55
|
+
*
|
|
56
|
+
* `outcome` 的取值与 `TrialAxis.outcome` 同源:命中已有实体 / 新建候选 / 不唯一没猜 /
|
|
57
|
+
* 只做了文档内归一(`no-registry`)。
|
|
58
|
+
*/
|
|
59
|
+
export interface NormalizeAxisRow {
|
|
60
|
+
kind: TrajectoryAxisKind
|
|
61
|
+
/** 文档里的写法。 */
|
|
62
|
+
name: string
|
|
63
|
+
outcome: 'matched' | 'new' | 'ambiguous' | 'no-registry' | string
|
|
64
|
+
/** 落到的实体 id(新建的是候选 id)。歧义时没有。 */
|
|
65
|
+
id?: string
|
|
66
|
+
/** 判成这个结果的理由 / 途径,模型或规则给的。 */
|
|
67
|
+
how?: string
|
|
68
|
+
aliases: string[]
|
|
69
|
+
parentMention?: string
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** 一条写进库的 Knowledge 在轨迹里的投影。 */
|
|
73
|
+
export interface NormalizeFactRow {
|
|
74
|
+
text: string
|
|
75
|
+
/** 「对象 · 动作 · 场景」的展示文本。 */
|
|
76
|
+
coordinate: string
|
|
77
|
+
/** 几条出处。 */
|
|
78
|
+
sources: number
|
|
79
|
+
/** 坐标仍有歧义:事实与出处都留着,但暂不进任何卡片分组。 */
|
|
80
|
+
unresolved: boolean
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** 归一轨迹:一份来源的 mention 名字 → 库内坐标,以及随之写入的事实。 */
|
|
84
|
+
export interface NormalizeTrajectoryReport extends TrajectoryHead {
|
|
85
|
+
stage: 'normalize'
|
|
86
|
+
source: { id: string; title: string; version: number }
|
|
87
|
+
/** 依据哪一条抽取记录做的归一。 */
|
|
88
|
+
extractionId: string
|
|
89
|
+
/** 归一之后存下的指纹(下一份文档归一能不能复用,看它)。 */
|
|
90
|
+
fingerprint: string
|
|
91
|
+
/** 模型这次给了多少个坐标提及。 */
|
|
92
|
+
mentions: number
|
|
93
|
+
/**
|
|
94
|
+
* 解析结论的分布。`mechanical` = 这一轮没接注册表,只做了文档内归一
|
|
95
|
+
* (三个分量各自计数,所以它是「多少条提及只做了机械归一」而不是一个布尔)。
|
|
96
|
+
*/
|
|
97
|
+
counts: { matched: number; created: number; ambiguous: number; mechanical: number }
|
|
98
|
+
axes: NormalizeAxisRow[]
|
|
99
|
+
knowledge: { saved: number; merged: number; skipped: number; failures: number; unresolved: number }
|
|
100
|
+
events: { saved: number; merged: number; skipped: number }
|
|
101
|
+
rows: NormalizeFactRow[]
|
|
102
|
+
/** 已有坐标有没有下发进提示词,以及发了多少(0068)。 */
|
|
103
|
+
axesContext?: { injected: number; totals: number; truncated: number; selection?: string }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** 物化轨迹里的一节。 */
|
|
107
|
+
export interface MaterializeSectionRow {
|
|
108
|
+
label: string
|
|
109
|
+
count: number
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** 物化轨迹:一个卡片分组 → 一张卡,以及它是怎么排出来的。 */
|
|
113
|
+
export interface MaterializeTrajectoryReport extends TrajectoryHead {
|
|
114
|
+
stage: 'materialize'
|
|
115
|
+
group: { key: string; title: string; coordinate: string; state: string; fingerprint: string }
|
|
116
|
+
action: 'created' | 'updated' | 'withdrawn'
|
|
117
|
+
card: {
|
|
118
|
+
id: string
|
|
119
|
+
title: string
|
|
120
|
+
status: string
|
|
121
|
+
/** 正文规模,字符数。 */
|
|
122
|
+
characters: number
|
|
123
|
+
/** 进卡的知识条数 / 事件数 / 图数。 */
|
|
124
|
+
knowledge: number
|
|
125
|
+
events: number
|
|
126
|
+
images: number
|
|
127
|
+
sections: MaterializeSectionRow[]
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* 卡片组织用了没有。
|
|
131
|
+
*
|
|
132
|
+
* `used: false` = 这一库没开卡片组织(`cardOrganize` 关着),正文是按坐标机械拼的;
|
|
133
|
+
* 关着不是失败,但两件事的正文长得不一样,报告必须分得清。
|
|
134
|
+
*/
|
|
135
|
+
organize: { used: boolean; origin?: 'model' | 'fallback'; layoutHash?: string }
|
|
136
|
+
/** 进卡的每一条知识(带出处数),让人对着原文核。 */
|
|
137
|
+
rows: Array<{ id: string; text: string; sources: number }>
|
|
138
|
+
/** 这一组的来源文档。 */
|
|
139
|
+
sources: string[]
|
|
140
|
+
/** 正文里可以指向的其它卡片,以及为什么(`coordinate` / `relation` / `source`)。 */
|
|
141
|
+
related: Array<{ title: string; reason: string }>
|
|
142
|
+
}
|