@avantf/dsh-mem 0.7.0 → 0.8.0
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 +4 -0
- package/lib/client.js +548 -1195
- package/lib/dsh-build.json +1 -0
- package/lib/engine/graph-recall/in-memory.d.ts +93 -0
- package/lib/engine/graph-recall/index.d.ts +22 -0
- package/lib/engine/graph-recall/traverse.d.ts +119 -0
- package/lib/engine/graph-recall/types.d.ts +188 -0
- package/lib/engine/mem/config/config_files.d.ts +1 -1
- package/lib/engine/mem/config/loader.d.ts +2 -3
- package/lib/engine/mem/config/paths.d.ts +2 -13
- package/lib/engine/mem/db/dao/contradictions.d.ts +4 -22
- package/lib/engine/mem/db/dao/entities.d.ts +80 -7
- package/lib/engine/mem/db/dao/fact_sources.d.ts +3 -13
- package/lib/engine/mem/db/dao/facts.d.ts +311 -56
- package/lib/engine/mem/db/dao/triples.d.ts +1 -26
- package/lib/engine/mem/db/graph.d.ts +249 -0
- package/lib/engine/mem/db/journal_notes.d.ts +35 -0
- package/lib/engine/mem/db/migrations.d.ts +9 -0
- package/lib/engine/mem/db/port.d.ts +22 -3
- package/lib/engine/mem/db/schema.d.ts +119 -0
- package/lib/engine/mem/db/tokenizer.d.ts +9 -0
- package/lib/engine/mem/dbdrivers/fake/driver.d.ts +75 -0
- package/lib/engine/mem/dbdrivers/fake/index.d.ts +7 -0
- package/lib/engine/mem/dbdrivers/fake/surface.d.ts +34 -0
- package/lib/engine/mem/dbdrivers/fake/transport.d.ts +61 -0
- package/lib/engine/mem/dbdrivers/sqlite/connection.d.ts +14 -0
- package/lib/engine/mem/dbdrivers/sqlite/contradictions.d.ts +35 -0
- package/lib/engine/mem/dbdrivers/sqlite/dao/index.d.ts +16 -0
- package/lib/engine/mem/dbdrivers/sqlite/driver.d.ts +54 -0
- package/lib/engine/mem/dbdrivers/sqlite/facts.d.ts +117 -0
- package/lib/engine/mem/dbdrivers/sqlite/index.d.ts +6 -0
- package/lib/engine/mem/dbdrivers/sqlite/migrations.d.ts +15 -0
- package/lib/engine/mem/dbdrivers/sqlite/relations.d.ts +62 -0
- package/lib/engine/mem/dbdrivers/sqlite/search.d.ts +56 -0
- package/lib/engine/mem/dbdrivers/sqlite/sources.d.ts +37 -0
- package/lib/engine/mem/defaults.d.ts +3 -4
- package/lib/engine/mem/dispatch.d.ts +7 -32
- package/lib/engine/mem/entities/extract.d.ts +44 -47
- package/lib/engine/mem/eval/derived_runner.d.ts +6 -1
- package/lib/engine/mem/eval/relation_gold.d.ts +251 -0
- package/lib/engine/mem/eval/write_metrics.d.ts +1 -1
- package/lib/engine/mem/facts_io.d.ts +50 -10
- package/lib/engine/mem/index.d.ts +6 -2
- package/lib/engine/mem/interfaces/capabilities.d.ts +30 -0
- package/lib/engine/mem/interfaces/common.d.ts +11 -0
- package/lib/engine/mem/interfaces/errors.d.ts +65 -0
- package/lib/engine/mem/interfaces/index.d.ts +18 -0
- package/lib/engine/mem/interfaces/migrations.d.ts +21 -0
- package/lib/engine/mem/interfaces/repositories.d.ts +471 -0
- package/lib/engine/mem/interfaces/search_index.d.ts +113 -0
- package/lib/engine/mem/interfaces/storage_backend.d.ts +84 -0
- package/lib/engine/mem/interfaces/storage_driver.d.ts +43 -0
- package/lib/engine/mem/interfaces/version.d.ts +34 -0
- package/lib/engine/mem/lifecycle/contradiction.d.ts +2 -2
- package/lib/engine/mem/modelBootstrap.d.ts +1 -1
- package/lib/engine/mem/relations/confidence.d.ts +39 -0
- package/lib/engine/mem/relations/extract.d.ts +128 -0
- package/lib/engine/mem/relations/index.d.ts +32 -0
- package/lib/engine/mem/relations/name-quality.d.ts +78 -0
- package/lib/engine/mem/relations/normalize.d.ts +275 -0
- package/lib/engine/mem/relations/patterns.d.ts +67 -0
- package/lib/engine/mem/relations/statement.d.ts +88 -0
- package/lib/engine/mem/relations/types.d.ts +79 -0
- package/lib/engine/mem/runtime.d.ts +61 -88
- package/lib/engine/mem/storage/create.d.ts +42 -0
- package/lib/engine/mem/storage/errors.d.ts +65 -0
- package/lib/engine/mem/storage/index.d.ts +15 -0
- package/lib/engine/mem/storage/ports.d.ts +472 -0
- package/lib/engine/mem/storage/registry.d.ts +37 -0
- package/lib/engine/mem/storage/sqlite.d.ts +28 -0
- package/lib/engine/mem/store/common.d.ts +1 -1
- package/lib/engine/mem/store/entity_leg.d.ts +59 -2
- package/lib/engine/mem/store/entity_sweep.d.ts +4 -3
- package/lib/engine/mem/store/explicit_relations.d.ts +52 -0
- package/lib/engine/mem/store/facts_restore.d.ts +10 -2
- package/lib/engine/mem/store/floors.d.ts +4 -1
- package/lib/engine/mem/store/graph_reader.d.ts +146 -0
- package/lib/engine/mem/store/hybrid.d.ts +50 -13
- package/lib/engine/mem/store/kinds.d.ts +47 -0
- package/lib/engine/mem/store/legs.d.ts +24 -29
- package/lib/engine/mem/store/lexical.d.ts +36 -45
- package/lib/engine/mem/store/memory.d.ts +471 -83
- package/lib/engine/mem/store/recall_policy.d.ts +49 -0
- package/lib/engine/mem/store/relation_backfill.d.ts +210 -0
- package/lib/engine/mem/store/relation_planner.d.ts +191 -0
- package/lib/engine/mem/store/secret_guard.d.ts +0 -1
- package/lib/engine/mem/store/self_query.d.ts +4 -2
- package/lib/engine/mem/store/time_filter.d.ts +121 -0
- package/lib/engine/mem/store/titles.d.ts +52 -0
- package/lib/engine/mem/store/vector_repair.d.ts +44 -54
- package/lib/engine/mem-contract/config.d.ts +44 -38
- package/lib/engine/mem-contract/text.d.ts +1 -1
- package/lib/engine/mem-contract/tools.d.ts +145 -132
- package/lib/engine/mem-contract/types.d.ts +364 -288
- package/lib/index.js +12048 -11337
- package/lib/interface-version.json +1 -1
- package/lib/types/client/index.d.ts +2 -2
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/paging.d.ts +3 -12
- package/lib/types/client/paging.d.ts.map +1 -1
- package/lib/types/client/summarize.d.ts.map +1 -1
- package/lib/types/envinit-bootstrap.d.ts +1 -1
- package/lib/types/envinit.d.ts +2 -6
- package/lib/types/envinit.d.ts.map +1 -1
- package/lib/types/hints.d.ts +28 -24
- package/lib/types/hints.d.ts.map +1 -1
- package/lib/types/index.d.ts +21 -40
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/inject.d.ts +3 -2
- package/lib/types/inject.d.ts.map +1 -1
- package/lib/types/interface_gate.d.ts +1 -1
- package/lib/types/prompt.d.ts +17 -41
- package/lib/types/prompt.d.ts.map +1 -1
- package/lib/types/provision.d.ts +3 -0
- package/lib/types/provision.d.ts.map +1 -1
- package/lib/types/remote.d.ts +14 -7
- package/lib/types/remote.d.ts.map +1 -1
- package/lib/types/tool_schema.d.ts +4 -0
- package/lib/types/tool_schema.d.ts.map +1 -1
- package/lib/types/vectorMigration.d.ts +5 -7
- package/lib/types/vectorMigration.d.ts.map +1 -1
- package/package.json +15 -14
- package/lib/engine/mem/config/domains.d.ts +0 -20
- package/lib/engine/mem/db/dao/chunks.d.ts +0 -231
- package/lib/engine/mem/db/dao/documents.d.ts +0 -51
- package/lib/engine/mem/db/hash.d.ts +0 -2
- package/lib/engine/mem/db/knowledge.d.ts +0 -17
- package/lib/engine/mem/eval/index.d.ts +0 -4
- package/lib/engine/mem/git.d.ts +0 -73
- package/lib/engine/mem/router.d.ts +0 -51
- package/lib/engine/mem/store/doc_files.d.ts +0 -125
- package/lib/engine/mem/store/document_text.d.ts +0 -47
- package/lib/engine/mem/store/ingest_guard.d.ts +0 -25
- package/lib/engine/mem/store/knowledge.d.ts +0 -598
- package/lib/engine/mem/store/source_picker.d.ts +0 -43
- package/lib/engine/mem-retrieval/adapters/local_reranker.d.ts +0 -52
- package/lib/engine/mem-retrieval/adapters/none_reranker.d.ts +0 -10
- package/lib/engine/mem-retrieval/rerank.d.ts +0 -13
- package/lib/types/client/domains.d.ts +0 -53
- package/lib/types/client/domains.d.ts.map +0 -1
- package/lib/types/client/ingest.d.ts +0 -24
- package/lib/types/client/ingest.d.ts.map +0 -1
- package/lib/types/open.d.ts +0 -77
- package/lib/types/open.d.ts.map +0 -1
- package/lib/types/reconcile.d.ts +0 -64
- package/lib/types/reconcile.d.ts.map +0 -1
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Entity
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Entity extraction: POS tagging and the noun-ish names that become `entities` rows.
|
|
3
|
+
*
|
|
4
|
+
* Uses nodejieba (optional dependency) for POS; degrades gracefully to a character/regex fallback
|
|
5
|
+
* when unavailable, exactly like the `jieba_available()` guard.
|
|
5
6
|
*
|
|
6
7
|
* Note: nodejieba's `tag()` returns `{ word, tag }` objects, and its POS flag set
|
|
7
8
|
* differs from Python jieba (e.g. proper nouns → `nr`, 名动词 → `vn`).
|
|
9
|
+
*
|
|
10
|
+
* WHAT THIS MODULE IS NOT. It owns what is an entity NAME — nothing about relations. The relation
|
|
11
|
+
* half (SVO candidate triples, explicit patterns, the name/statement/confidence gates and the
|
|
12
|
+
* predicate vocabulary) lives in `relations/`; the two halves share exactly one thing, the tagging
|
|
13
|
+
* pass ({@link tagText}), so a write pays for the dictionary once and drives both.
|
|
8
14
|
*/
|
|
9
15
|
export interface ExtractedEntity {
|
|
10
16
|
name: string;
|
|
11
17
|
type: string;
|
|
12
18
|
method: string;
|
|
13
19
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
pred: string;
|
|
17
|
-
obj: string;
|
|
18
|
-
confidence: number;
|
|
19
|
-
source: string;
|
|
20
|
-
}
|
|
21
|
-
type PosToken = {
|
|
20
|
+
/** One tagged token — the tokenizer's shape, shared with `relations/extract.ts`. */
|
|
21
|
+
export interface PosToken {
|
|
22
22
|
word: string;
|
|
23
23
|
tag: string;
|
|
24
|
-
}
|
|
24
|
+
}
|
|
25
25
|
/**
|
|
26
26
|
* Attempts made so far — the mechanism, not the wall time.
|
|
27
27
|
*
|
|
@@ -33,52 +33,49 @@ type PosToken = {
|
|
|
33
33
|
export declare function jiebaLoadAttempts(): number;
|
|
34
34
|
export declare function jiebaAvailable(): Promise<boolean>;
|
|
35
35
|
/**
|
|
36
|
-
* Version of the extraction
|
|
36
|
+
* Version of the ENTITY extraction rules below (accepted tags, the regex fallback).
|
|
37
|
+
*
|
|
38
|
+
* Persisted per chunk so a reindex can skip rows whose text is unchanged while still rebuilding the
|
|
39
|
+
* ones produced by an older rule set. Bump this whenever a change here would yield different
|
|
40
|
+
* ENTITIES for the same text — it is the only thing that separates "already extracted" from
|
|
41
|
+
* "extracted by the previous version" (DESIGN §20).
|
|
42
|
+
*
|
|
43
|
+
* The relation rules have their OWN version (`db/graph.ts#EDGE_EXTRACTOR_VERSION` in `edges`):
|
|
44
|
+
* an entity-rule change is not an edge-policy change, and vice versa.
|
|
45
|
+
*
|
|
46
|
+
* 2 (Phase 5b, docs/RELATIONAL_RECALL.md §十七): punctuation is an ARGUMENT BOUNDARY instead of an
|
|
47
|
+
* argument in the relation half; `nrt`/`zg` count as arguments like they already counted as entities
|
|
48
|
+
* (`阿里巴巴`); an opening bracket at an argument's edge is skipped (`(架构评审)`); more negations
|
|
49
|
+
* are kept on the predicate (`不再/不会/不能/未能`); and triples are stamped `source: 'svo'` so the
|
|
50
|
+
* graph can require a STATED relation from this producer (`relations/statement.ts`).
|
|
51
|
+
*
|
|
52
|
+
* 3 (2026-10-25 relation review, docs/REVIEW-RELATION-EXTRACTION.md): the relation half's TRIPLES
|
|
53
|
+
* change, not the entity names — curated-predicate surface matching (`主管/隶属于/汇报/部署在/工作于/
|
|
54
|
+
* 属于/合作` are nouns or non-verbs to the tagger, so they were unreachable), `p`/`c` as a merge
|
|
55
|
+
* BOUNDARY instead of tokens to step over, strict + weak predicate candidates merged instead of
|
|
56
|
+
* fallbacks, and a triple-contiguity filter (`isContiguousTriple`). The bump is what makes
|
|
57
|
+
* `reindexEntities` re-run the extractor over existing facts — `reextractEdges` alone only
|
|
58
|
+
* re-derives edges from STORED triples and would keep the old, partial extraction.
|
|
37
59
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* "already extracted" from "extracted by the previous version" (DESIGN §20).
|
|
60
|
+
* The relation half's naming rules themselves are unchanged by 3; `triples` move because the
|
|
61
|
+
* identifier of "what the text extractor saw" is this stamp, and `MemoryStore.reindexEntities`
|
|
62
|
+
* rewrites entities, triples and edges together under it.
|
|
42
63
|
*/
|
|
43
|
-
export declare const ENTITY_EXTRACTOR_VERSION =
|
|
44
|
-
export interface TriplePattern {
|
|
45
|
-
subj?: string;
|
|
46
|
-
pred?: string;
|
|
47
|
-
obj?: string;
|
|
48
|
-
}
|
|
64
|
+
export declare const ENTITY_EXTRACTOR_VERSION = 3;
|
|
49
65
|
/**
|
|
50
66
|
* POS-tag `text` ONCE, or `null` when nodejieba is unavailable (callers fall back to regex).
|
|
51
67
|
*
|
|
52
|
-
* Exists so one write can drive BOTH
|
|
53
|
-
* used to tag the same content twice
|
|
54
|
-
*
|
|
68
|
+
* Exists so one write can drive BOTH halves from a single `jieba.tag()` call: entity names and
|
|
69
|
+
* relation candidates used to tag the same content twice, which is pure repeated work — measured
|
|
70
|
+
* ~1.1 ms per 1000 characters per pass.
|
|
55
71
|
*/
|
|
56
72
|
export declare function tagText(text: string): Promise<PosToken[] | null>;
|
|
57
73
|
/**
|
|
58
74
|
* Entities from ALREADY-TAGGED tokens (`null` = tagger unavailable → regex fallback on `text`).
|
|
59
75
|
*
|
|
60
|
-
* The token array is passed in rather than re-derived, so a caller that needs entities AND
|
|
61
|
-
* pays for one tagging pass (see `tagText`).
|
|
76
|
+
* The token array is passed in rather than re-derived, so a caller that needs entities AND
|
|
77
|
+
* relations pays for one tagging pass (see `tagText`).
|
|
62
78
|
*/
|
|
63
79
|
export declare function entitiesFromTokens(tokens: PosToken[] | null, text: string): ExtractedEntity[];
|
|
64
80
|
/** Extract entity names using nodejieba POS when available, else a regex fallback. */
|
|
65
81
|
export declare function extractEntities(text: string): Promise<ExtractedEntity[]>;
|
|
66
|
-
/**
|
|
67
|
-
* SPO triples from ALREADY-TAGGED tokens (`null` = tagger unavailable → no triples, as before).
|
|
68
|
-
*
|
|
69
|
-
* `pos` is exported alongside `entitiesFromTokens` so the write path can tag once and drive both.
|
|
70
|
-
*/
|
|
71
|
-
export declare function triplesFromTokens(rawTokens: PosToken[] | null): ExtractedTriple[];
|
|
72
|
-
/** Extract a few SPO triples via a simplified SVO heuristic. Returns [] when jieba unavailable. */
|
|
73
|
-
export declare function extractTriples(text: string): Promise<ExtractedTriple[]>;
|
|
74
|
-
/**
|
|
75
|
-
* Parse a natural-language question into a direction-aware TriplePattern where
|
|
76
|
-
* interrogative slots are wildcards (`undefined`).
|
|
77
|
-
* `parse_query_pattern`: predicates anchor to the interrogative, and no literal
|
|
78
|
-
* interrogative word fills a slot.
|
|
79
|
-
*
|
|
80
|
-
* 「李娜管理谁」 → { subj: '李娜', pred: '管理' }
|
|
81
|
-
* 「谁管理李娜」 → { pred: '管理', obj: '李娜' }
|
|
82
|
-
*/
|
|
83
|
-
export declare function parseQueryPattern(query: string): Promise<TriplePattern>;
|
|
84
|
-
export {};
|
|
@@ -69,12 +69,17 @@ export interface DerivedRuntimePort {
|
|
|
69
69
|
}): Promise<{
|
|
70
70
|
fact_id: number;
|
|
71
71
|
}>;
|
|
72
|
+
/**
|
|
73
|
+
* `search` as this harness needs it. `hits` is OPTIONAL because the answer has three modes since
|
|
74
|
+
* Phase 4 (a `mode:"relation"` answer carries paths + evidence instead); a relation answer ranks
|
|
75
|
+
* nothing here — this harness grades content retrieval.
|
|
76
|
+
*/
|
|
72
77
|
recall(req: {
|
|
73
78
|
action: 'search';
|
|
74
79
|
query: string;
|
|
75
80
|
limit?: number;
|
|
76
81
|
}): Promise<{
|
|
77
|
-
hits
|
|
82
|
+
hits?: {
|
|
78
83
|
ref_id: number;
|
|
79
84
|
}[];
|
|
80
85
|
}>;
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The relation-recall gold set and its evaluator (docs/RELATIONAL_RECALL.md R6 / Phase 5a).
|
|
3
|
+
*
|
|
4
|
+
* WHAT THIS FILE IS. One deterministic corpus, one list of labelled queries, and a runner that
|
|
5
|
+
* seeds a THROWAWAY database through the production write path and measures the rule the relation
|
|
6
|
+
* stage promises (RELATIONAL_RECALL §6–7):
|
|
7
|
+
*
|
|
8
|
+
* - a COMPLETE relation question (one-hop / reverse / two-hop) is answered with structure;
|
|
9
|
+
* - a PURE multi-entity connection ("张伟和李娜") is answered at the FACT level (`mode:"facts"`,
|
|
10
|
+
* docs/GRAPH_RECALL_AND_SEMANTICS.md §7.9), including journal entries and notes without edges;
|
|
11
|
+
* - an incomplete or unanswerable one (no plan, no path/fact, archived evidence, filter mismatch)
|
|
12
|
+
* falls back to fuzzy and never returns an empty `mode:"relation"` / `mode:"facts"`;
|
|
13
|
+
* - exact content and `titles` still win / stay untouched;
|
|
14
|
+
* - aliases (entity + predicate), `kind` / `source` / `time` filters narrow the graph.
|
|
15
|
+
*
|
|
16
|
+
* WHY A GOLD SET AND NOT MORE SPECS. A spec asserts one behaviour per test; an eval fixture assigns
|
|
17
|
+
* every query an expected ROUTE and an expected ANSWER, then reports route accuracy, path
|
|
18
|
+
* precision/recall and the fallback rate over the whole set. The vitest spec
|
|
19
|
+
* (`test/relation_eval.spec.ts`) freezes the numbers, while `scripts/relation_gold_eval.mjs`
|
|
20
|
+
* prints the same report for the docs — both import THIS module, so the table cannot drift from
|
|
21
|
+
* the assertion.
|
|
22
|
+
*
|
|
23
|
+
* HOW THE FIXTURE IS SEEDED. Facts go through `rt.remember` (real FTS rows, entity links, triples,
|
|
24
|
+
* edges). The facts whose object surface the STRING EXTRACTOR's tokenizer would decide differently
|
|
25
|
+
* (`李娜负责支付网关` yields `网关`) get `writeEdgesForFact` afterwards: extraction quality is R3's
|
|
26
|
+
* subject, while the planner/walk is what this fixture evaluates. `writeEdgesForFact` is still the
|
|
27
|
+
* production edge writer (normalization, confidence policy, version stamp), so the seeded graph is
|
|
28
|
+
* the shape production builds. `archive` is the real admin action.
|
|
29
|
+
*
|
|
30
|
+
* WHAT IS DELIBERATELY NOT HERE. No timing assertions (those are `scripts/bench-relation.mjs`,
|
|
31
|
+
* because a CI runner's p95 is not a specification), no live database, no model, no new action.
|
|
32
|
+
*
|
|
33
|
+
* @module eval/relation_gold
|
|
34
|
+
*/
|
|
35
|
+
import type { MemoryKind, TimeFilter } from '../../mem-contract/index.js';
|
|
36
|
+
import type { AvantfRuntime } from '../runtime.js';
|
|
37
|
+
/** One triple the fixture wants in the graph, as the production edge writer takes it. */
|
|
38
|
+
export interface RelationGoldEdge {
|
|
39
|
+
subj: string;
|
|
40
|
+
pred: string;
|
|
41
|
+
obj: string;
|
|
42
|
+
}
|
|
43
|
+
/** One fact of the synthetic corpus. `key` is the only handle expectations use. */
|
|
44
|
+
export interface RelationGoldFact {
|
|
45
|
+
key: string;
|
|
46
|
+
content: string;
|
|
47
|
+
kind?: MemoryKind;
|
|
48
|
+
title?: string;
|
|
49
|
+
eventDate?: string;
|
|
50
|
+
sourceRef?: string;
|
|
51
|
+
/**
|
|
52
|
+
* `facts.created_at` to force after the write — the time axis is a caller-visible filter, and
|
|
53
|
+
* "yesterday's fact" must be constructible without waiting a day.
|
|
54
|
+
*/
|
|
55
|
+
createdAt?: string;
|
|
56
|
+
/** Edges to (re)write through `writeEdgesForFact`; omitted = whatever the text extractor produced. */
|
|
57
|
+
edges?: readonly RelationGoldEdge[];
|
|
58
|
+
/** Archive this fact after the corpus is seeded (the edge is written first, then hidden). */
|
|
59
|
+
archived?: boolean;
|
|
60
|
+
}
|
|
61
|
+
/** Which behaviour a query is labelled with. One primary label per query keeps the table readable. */
|
|
62
|
+
export type RelationGoldCategory = 'one-hop' | 'reverse' | 'two-hop' | 'facts' | 'alias' | 'archived' | 'kind' | 'source' | 'time' | 'negative' | 'exact';
|
|
63
|
+
/** One expected path, by entity names and the ordered fact keys behind its edges. */
|
|
64
|
+
export interface RelationGoldPath {
|
|
65
|
+
start: string;
|
|
66
|
+
end: string;
|
|
67
|
+
hops: number;
|
|
68
|
+
facts: readonly string[];
|
|
69
|
+
}
|
|
70
|
+
/** The four routes a gold query may be graded on ('time' is not one: every gold query has a `query`). */
|
|
71
|
+
export type RelationGoldMode = 'relation' | 'facts' | 'fuzzy' | 'exact';
|
|
72
|
+
/** What a query must produce. */
|
|
73
|
+
export interface RelationGoldExpectation {
|
|
74
|
+
mode: RelationGoldMode;
|
|
75
|
+
/** Relation answers: the COMPLETE path set (no extra, no missing). */
|
|
76
|
+
paths?: readonly RelationGoldPath[];
|
|
77
|
+
/**
|
|
78
|
+
* The facts the answer must cite. For `relation` this is the evidence set; for `facts` it is the
|
|
79
|
+
* WHOLE fact page (`facts` requires equality, in any order); for `exact`/`fuzzy` it is the hit set
|
|
80
|
+
* (`exact` requires equality, `fuzzy` requires inclusion in its top-5 page).
|
|
81
|
+
*/
|
|
82
|
+
facts?: readonly string[];
|
|
83
|
+
}
|
|
84
|
+
/** One labelled query. */
|
|
85
|
+
export interface RelationGoldQuery {
|
|
86
|
+
id: string;
|
|
87
|
+
query: string;
|
|
88
|
+
category: RelationGoldCategory;
|
|
89
|
+
kind?: MemoryKind;
|
|
90
|
+
source?: string;
|
|
91
|
+
time?: TimeFilter;
|
|
92
|
+
expect: RelationGoldExpectation;
|
|
93
|
+
}
|
|
94
|
+
/** A `mem_recall.titles` regression check (the title search must not see the planner). */
|
|
95
|
+
export interface RelationGoldTitleCheck {
|
|
96
|
+
id: string;
|
|
97
|
+
query: string;
|
|
98
|
+
/** Title-holder fact keys, in the order the exact title page returns them. */
|
|
99
|
+
facts: readonly string[];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The corpus.
|
|
103
|
+
*
|
|
104
|
+
* The entity sets are DELIBERATELY disjoint enough that each query has one unambiguous answer:
|
|
105
|
+
*
|
|
106
|
+
* ```text
|
|
107
|
+
* 张伟 ──管理──▶ 李娜 ──管理(负责)──▶ 支付网关 note
|
|
108
|
+
* 张伟 ──管理──▶ 王强 ──管理(负责)──▶ 风控引擎 note (distilled from the journal below) / note
|
|
109
|
+
* 张伟 ──管理──▶ 赵敏 note, ARCHIVED
|
|
110
|
+
* 张伟, 李娜 ──参与(参加)──▶ 架构评审 note
|
|
111
|
+
* 赵敏 ──管理(负责)──▶ 数据平台 note, created_at 2020
|
|
112
|
+
* 周杰, 陈静 note, NO edge (fact-level AND only)
|
|
113
|
+
* 钱伟, 孙丽 note, NO edge, created_at 2020
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* `zhang-zhao` is archived AFTER its edge is written, which is the state the reader must hide.
|
|
117
|
+
*
|
|
118
|
+
* `zhang-wang` is the corpus's JOURNAL, and it states the same relation the note `wang-note`
|
|
119
|
+
* distils — that pair is STRUCTURED_RELATIONS §一's promotion rule in fixture form, and it is what
|
|
120
|
+
* makes the `kind` dimension mean something: the journal keeps its `triples` and produces NO edge,
|
|
121
|
+
* so `kind: "journal"` cannot answer a relation question, while the note that carries the stable
|
|
122
|
+
* fact can. (Before Phase 5c both rows were one journal fact with an edge; the graph is note-only
|
|
123
|
+
* now, so the relation moved to the note it belongs to.)
|
|
124
|
+
*
|
|
125
|
+
* The fact-level AND stage (§7.9) reads `fact_entities`, not `edges`, and that is exactly why
|
|
126
|
+
* `zhang-wang` (the journal, no edge), `note-consensus` and `old-pair` (notes whose text produced
|
|
127
|
+
* no relation) are in the corpus: they are the rows a graph-only answer would silently miss.
|
|
128
|
+
*/
|
|
129
|
+
export declare const RELATION_GOLD_FACTS: readonly RelationGoldFact[];
|
|
130
|
+
/** An entity alias registered through the production entry point (explicit, never inferred). */
|
|
131
|
+
export declare const RELATION_GOLD_ALIASES: readonly {
|
|
132
|
+
alias: string;
|
|
133
|
+
canonical: string;
|
|
134
|
+
}[];
|
|
135
|
+
/**
|
|
136
|
+
* The labelled queries.
|
|
137
|
+
*
|
|
138
|
+
* 17 relation questions, 9 fact-level AND questions, 16 negative controls, 1 exact-precedence probe
|
|
139
|
+
* (STRUCTURED_RELATIONS §四 moved the `kind: "journal"` relation question into the negative set: a
|
|
140
|
+
* journal produces no edge, so the layer filter can only answer with fuzzy). The breakdown is what
|
|
141
|
+
* makes the report diagnosable: a route-accuracy regression can be read per category, and the
|
|
142
|
+
* negative controls are the half that catches a planner that guesses.
|
|
143
|
+
*/
|
|
144
|
+
export declare const RELATION_GOLD_QUERIES: readonly RelationGoldQuery[];
|
|
145
|
+
/** `titles` must keep answering the title index, never the planner. */
|
|
146
|
+
export declare const RELATION_GOLD_TITLE_CHECKS: readonly RelationGoldTitleCheck[];
|
|
147
|
+
/** The runtime surface the fixture needs (the real `AvantfRuntime` satisfies it). */
|
|
148
|
+
export type RelationGoldRuntime = Pick<AvantfRuntime, 'db' | 'remember' | 'recall' | 'admin' | 'memory'>;
|
|
149
|
+
/** Result of seeding: fact key → `fact_id`. */
|
|
150
|
+
export type RelationGoldIds = Readonly<Record<string, number>>;
|
|
151
|
+
/**
|
|
152
|
+
* Seed the corpus into an EMPTY store through the production write path.
|
|
153
|
+
*
|
|
154
|
+
* Order is load-bearing: every fact is written first (so text extraction has run and the FTS rows
|
|
155
|
+
* exist), then explicit edges replace what the extractor produced, then the clock/alias/archive
|
|
156
|
+
* adjustments are applied. Archiving last is what makes `zhang-zhao` a fact whose edge EXISTS but
|
|
157
|
+
* must not be walkable — the exact state the reader's active filter is for.
|
|
158
|
+
*
|
|
159
|
+
* @param rt - a runtime on a throwaway database.
|
|
160
|
+
* @returns fact key → id, for expectations that name facts.
|
|
161
|
+
*/
|
|
162
|
+
export declare function seedRelationGold(rt: RelationGoldRuntime): Promise<RelationGoldIds>;
|
|
163
|
+
/** One query's measured route + answer. */
|
|
164
|
+
export interface RelationGoldRow {
|
|
165
|
+
id: string;
|
|
166
|
+
query: string;
|
|
167
|
+
category: RelationGoldCategory;
|
|
168
|
+
expected_mode: RelationGoldMode;
|
|
169
|
+
mode: RelationGoldMode;
|
|
170
|
+
/** What the planner decided BEFORE any read (`null` = neither planner formed a question). */
|
|
171
|
+
plan: 'one-hop' | 'reverse' | 'two-hop' | 'facts' | null;
|
|
172
|
+
/**
|
|
173
|
+
* For a fuzzy answer: why no structured stage answered (diagnosis, not a failure). `no-fact`
|
|
174
|
+
* means the fact-level plan formed but no active fact mentions every entity; `no-path` means the
|
|
175
|
+
* relation plan formed but the graph had no walk; `planner-refused` means neither did.
|
|
176
|
+
*/
|
|
177
|
+
fallback: 'planner-refused' | 'no-fact' | 'no-path' | null;
|
|
178
|
+
expected_facts: string[];
|
|
179
|
+
returned_facts: string[];
|
|
180
|
+
missing_facts: string[];
|
|
181
|
+
extra_facts: string[];
|
|
182
|
+
fact_precision: number | null;
|
|
183
|
+
fact_recall: number | null;
|
|
184
|
+
paths_expected: number | null;
|
|
185
|
+
paths_returned: number | null;
|
|
186
|
+
path_matches: number | null;
|
|
187
|
+
path_precision: number | null;
|
|
188
|
+
path_recall: number | null;
|
|
189
|
+
pass: boolean;
|
|
190
|
+
failure: string | null;
|
|
191
|
+
}
|
|
192
|
+
/** Per-category roll-up. */
|
|
193
|
+
export interface RelationGoldCategoryRow {
|
|
194
|
+
total: number;
|
|
195
|
+
passed: number;
|
|
196
|
+
route_accuracy: number;
|
|
197
|
+
}
|
|
198
|
+
/** The whole report. `failures` is what a reviewer reads when `pass` is not 1. */
|
|
199
|
+
export interface RelationGoldReport {
|
|
200
|
+
rows: RelationGoldRow[];
|
|
201
|
+
title_checks: {
|
|
202
|
+
id: string;
|
|
203
|
+
query: string;
|
|
204
|
+
mode: 'exact' | 'fuzzy';
|
|
205
|
+
passed: boolean;
|
|
206
|
+
expected_facts: string[];
|
|
207
|
+
returned_facts: string[];
|
|
208
|
+
}[];
|
|
209
|
+
summary: {
|
|
210
|
+
total: number;
|
|
211
|
+
passed: number;
|
|
212
|
+
route_accuracy: number;
|
|
213
|
+
relation_queries: number;
|
|
214
|
+
relation_answered: number;
|
|
215
|
+
relation_route_accuracy: number;
|
|
216
|
+
facts_queries: number;
|
|
217
|
+
facts_answered: number;
|
|
218
|
+
facts_route_accuracy: number;
|
|
219
|
+
negative_queries: number;
|
|
220
|
+
negative_fallback_rate: number;
|
|
221
|
+
exact_queries: number;
|
|
222
|
+
exact_answered: number;
|
|
223
|
+
plan_formed: number;
|
|
224
|
+
plan_rate: number;
|
|
225
|
+
/** Relations only: `path_matches / paths_returned`, and `path_matches / paths_expected`. */
|
|
226
|
+
path_precision: number | null;
|
|
227
|
+
path_recall: number | null;
|
|
228
|
+
/** Relations only: fact-set precision/recall over evidence. */
|
|
229
|
+
fact_precision: number | null;
|
|
230
|
+
fact_recall: number | null;
|
|
231
|
+
/** Fraction of ALL queries the store answered with fuzzy (diagnosis of the middle layer). */
|
|
232
|
+
fuzzy_fallback_rate: number;
|
|
233
|
+
titles_total: number;
|
|
234
|
+
titles_passed: number;
|
|
235
|
+
by_category: Record<string, RelationGoldCategoryRow>;
|
|
236
|
+
failures: string[];
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Evaluate the whole gold set against one seeded runtime.
|
|
241
|
+
*
|
|
242
|
+
* Both planners are invoked SEPARATELY from the store's answer — the relation planner through the
|
|
243
|
+
* same `memory.graphReader(filter)` the store uses in `relationPage`, the fact-level planner through
|
|
244
|
+
* `memory.factEntitiesIn` — so the report can distinguish "the planner refused" from "the planner
|
|
245
|
+
* planned and the read found nothing". That distinction is the point of a routing metric: both fall
|
|
246
|
+
* back to fuzzy, but only one of them is a coverage gap.
|
|
247
|
+
*
|
|
248
|
+
* @param rt - the runtime seeded by {@link seedRelationGold} (or an equivalent fixture).
|
|
249
|
+
* @param ids - fact key → id returned by the seeder.
|
|
250
|
+
*/
|
|
251
|
+
export declare function evaluateRelationGold(rt: RelationGoldRuntime, ids: RelationGoldIds): Promise<RelationGoldReport>;
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* it in the text looks natural and is a trap: the token ends up inside the subject/predicate/
|
|
23
23
|
* object the detector parses, and the case silently stops exercising the signal it exists for
|
|
24
24
|
* (measured: an in-text token suppressed the polarity conflict in 4 of 5 placements). A label
|
|
25
|
-
* the store never interprets —
|
|
25
|
+
* the store never interprets — the fact text's own label — is stable across extractor changes.
|
|
26
26
|
*/
|
|
27
27
|
/** What a case is about. `expect_action` is structural only; `live` is content only. */
|
|
28
28
|
export interface WriteCase {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EXPORT_STATUSES, type ExportStatus, type RememberRequest } from '../mem-contract/index.js';
|
|
1
|
+
import { EXPORT_STATUSES, type ExportStatus, type MemoryKind, type RememberRequest } from '../mem-contract/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* The runtime slice these flows actually touch.
|
|
4
4
|
*
|
|
@@ -21,15 +21,18 @@ export interface FactsIoRuntime {
|
|
|
21
21
|
/** The document's discriminator. A file without it is not an export of this tool. */
|
|
22
22
|
export declare const FACTS_EXPORT_FORMAT = "avantf-mem-facts";
|
|
23
23
|
/**
|
|
24
|
-
* The version THIS build writes:
|
|
24
|
+
* The version THIS build writes: 5 (v4's `title` plus the note-only `relations`).
|
|
25
25
|
*
|
|
26
|
-
* Versions are additive by design: v2
|
|
27
|
-
* shape minus the new keys. A version outside {@link FACTS_EXPORT_VERSIONS} must be refused,
|
|
28
|
-
* guessed at.
|
|
26
|
+
* Versions are additive by design: v2/v3/v4/v5 each add OPTIONAL fields, so an older document is the
|
|
27
|
+
* same shape minus the new keys. A version outside {@link FACTS_EXPORT_VERSIONS} must be refused,
|
|
28
|
+
* never guessed at.
|
|
29
29
|
*/
|
|
30
|
-
export declare const FACTS_EXPORT_VERSION =
|
|
31
|
-
/**
|
|
32
|
-
|
|
30
|
+
export declare const FACTS_EXPORT_VERSION = 5;
|
|
31
|
+
/**
|
|
32
|
+
* A version this build can READ. v1–v4 documents are what users already have on disk, and a v5
|
|
33
|
+
* document adds only the OPTIONAL `relations` field — so all five parse into the same type.
|
|
34
|
+
*/
|
|
35
|
+
export declare const FACTS_EXPORT_VERSIONS: readonly [1, 2, 3, 4, 5];
|
|
33
36
|
/** The version literal, so a parsed document can say which field set it came from. */
|
|
34
37
|
export type FactsExportVersion = (typeof FACTS_EXPORT_VERSIONS)[number];
|
|
35
38
|
/**
|
|
@@ -54,7 +57,21 @@ export interface ExportedFact {
|
|
|
54
57
|
/** Source-local row id. For reports/debugging ONLY; content is the identity. */
|
|
55
58
|
source_id: number;
|
|
56
59
|
content: string;
|
|
57
|
-
category
|
|
60
|
+
/** The memory layer (v3). ABSENT on a v1/v2 document, which carries `category` instead. */
|
|
61
|
+
kind?: MemoryKind;
|
|
62
|
+
/**
|
|
63
|
+
* v4: the journal title (JOURNAL_TITLE_SEARCH §7). ABSENT on a v1/v2/v3 document — those rows had
|
|
64
|
+
* no title column at all, and an import restores them as NULL rather than inventing one.
|
|
65
|
+
*/
|
|
66
|
+
title?: string | null;
|
|
67
|
+
/** The old free-form label, kept for audit only; `null` when the row predates the migration. */
|
|
68
|
+
legacy_category?: string | null;
|
|
69
|
+
/**
|
|
70
|
+
* v1/v2 ONLY: the old free-form category. Read (and mapped) for backward compatibility, never
|
|
71
|
+
* written by this build — an export from a v10 source database still carries it, and a v1/v2
|
|
72
|
+
* document on disk is exactly that shape.
|
|
73
|
+
*/
|
|
74
|
+
category?: string;
|
|
58
75
|
status: string;
|
|
59
76
|
created_at: string | null;
|
|
60
77
|
updated_at: string | null;
|
|
@@ -93,11 +110,27 @@ export interface ExportedFact {
|
|
|
93
110
|
*/
|
|
94
111
|
entities_version?: number | null;
|
|
95
112
|
conflict_checked?: number | null;
|
|
113
|
+
/**
|
|
114
|
+
* v5: the relations the CALLER stated on this note (`edges.source = 'explicit'`), in insertion
|
|
115
|
+
* order. ABSENT on a v1–v4 document (those rows were written before the field existed) and on
|
|
116
|
+
* every `journal` row — a journal refuses relations by contract, so the key there would assert a
|
|
117
|
+
* relation set a journal cannot have.
|
|
118
|
+
*
|
|
119
|
+
* These are NOT the extracted (`svo`) edges: those are re-derived from `content`, while an
|
|
120
|
+
* explicit relation is the one graph fact the text does not contain. That is why it travels.
|
|
121
|
+
*/
|
|
122
|
+
relations?: ExportedRelation[];
|
|
123
|
+
}
|
|
124
|
+
/** One caller-stated relation inside an export document (v5). */
|
|
125
|
+
export interface ExportedRelation {
|
|
126
|
+
subject: string;
|
|
127
|
+
predicate: string;
|
|
128
|
+
object: string;
|
|
96
129
|
}
|
|
97
130
|
/** The single JSON document `export` writes (and `import`/`restore` read). */
|
|
98
131
|
export interface FactsExportDocument {
|
|
99
132
|
format: typeof FACTS_EXPORT_FORMAT;
|
|
100
|
-
/** The version the document was WRITTEN as (1 or
|
|
133
|
+
/** The version the document was WRITTEN as (1, 2 or 3); this build writes 3. */
|
|
101
134
|
version: FactsExportVersion;
|
|
102
135
|
exported_at: string;
|
|
103
136
|
source: {
|
|
@@ -274,6 +307,13 @@ export declare function parseFactsExport(text: string): FactsExportDocument;
|
|
|
274
307
|
* Order of decisions per fact: archived → skipped_archived (v1 never revives); content already in
|
|
275
308
|
* the target (or planned by an earlier row of this same document) → skipped_existing; otherwise the
|
|
276
309
|
* store's add path runs. A row the store refuses is collected in `failed` and the loop continues.
|
|
310
|
+
*
|
|
311
|
+
* A v5 row's `relations` travel through that same add path, so the imported fact gets its declared
|
|
312
|
+
* relations as `source: 'explicit'` edges (normalized, name-gated, four-tuple deduped against the
|
|
313
|
+
* extracted ones). Two consequences worth knowing: a row that is SKIPPED as already-present keeps
|
|
314
|
+
* the target's own relation set (this is a content union, not a merge of graphs), and a `journal`
|
|
315
|
+
* row carrying relations is refused per row with {@link RELATIONS_NEED_NOTE} — that combination is
|
|
316
|
+
* unrepresentable, so it must be reported rather than silently stripped.
|
|
277
317
|
*/
|
|
278
318
|
export declare function importFacts(rt: FactsIoRuntime, doc: FactsExportDocument, options?: ImportOptions): Promise<ImportReport>;
|
|
279
319
|
/**
|
|
@@ -19,15 +19,19 @@ export * from './modelBootstrap.js';
|
|
|
19
19
|
export * from './dispatch.js';
|
|
20
20
|
export * from './trustHeartbeat.js';
|
|
21
21
|
export * from './runtime.js';
|
|
22
|
+
export * from './storage/index.js';
|
|
23
|
+
export * from './interfaces/index.js';
|
|
24
|
+
export { isSqliteStorageBackend, sqliteDriver, SqliteStorageBackend, SQLITE_CAPABILITIES } from './dbdrivers/sqlite/index.js';
|
|
25
|
+
export { FAKE_CAPABILITIES, FakeStorageBackend, fakeDriver, isFakeStorageBackend, MockRemoteTransport, type MockRemoteTurnaround, type StorageTransport, type TransportRequestRecord, } from './dbdrivers/fake/index.js';
|
|
22
26
|
export * from './store/memory.js';
|
|
23
|
-
export * from './store/knowledge.js';
|
|
24
27
|
export { normalizeWrite } from './store/common.js';
|
|
25
28
|
export * from './facts_io.js';
|
|
26
29
|
export * from './maintenance_paths.js';
|
|
27
30
|
export * from './store/facts_restore.js';
|
|
28
31
|
export * from './store/vector_repair.js';
|
|
29
32
|
export * from './store/hybrid.js';
|
|
30
|
-
export * from './store/source_picker.js';
|
|
31
33
|
export * from './store/lexical.js';
|
|
32
34
|
export * from './store/floors.js';
|
|
33
35
|
export { registerSemanticBackend, registerVectorStore, type SemanticBackend, type VectorStore, } from '../mem-retrieval/index.js';
|
|
36
|
+
export * from './store/relation_backfill.js';
|
|
37
|
+
export { extractRelationCandidates, normalizeRelation, evaluateRelation, type RelationCandidate, type RelationEvaluation, type RelationExtractor, } from './relations/index.js';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a storage driver DECLARES it can do (docs/DB_DRIVER_LAYOUT.md §二).
|
|
3
|
+
*
|
|
4
|
+
* Capabilities are how the HAL stays honest about optional behaviour without a second interface per
|
|
5
|
+
* driver: a backend that cannot do full-text search says so once, here, instead of the store probing
|
|
6
|
+
* it with a query that fails. Adding a flag is a MINOR contract change (an old driver that does not
|
|
7
|
+
* know the flag simply does not declare it; the host must tolerate unknown flags).
|
|
8
|
+
*
|
|
9
|
+
* @module interfaces/capabilities
|
|
10
|
+
*/
|
|
11
|
+
export interface StorageCapabilities {
|
|
12
|
+
/** Async unit-of-work transactions (`LifecycleService.transaction`). */
|
|
13
|
+
transactions: boolean;
|
|
14
|
+
/** Content full-text search (`SearchIndex.searchContent`). */
|
|
15
|
+
fullText: boolean;
|
|
16
|
+
/** Title search (`SearchIndex.searchTitles` + the exact-title face). */
|
|
17
|
+
titles: boolean;
|
|
18
|
+
/** The relation graph (entities / triples / edges / graph reader). */
|
|
19
|
+
relations: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Persisted vector BLOBS (`facts.semantic_vector` / `hrr_vector`) and their inventory reads. The
|
|
22
|
+
* live vector INDEX is a retrieval concern resolved above the storage layer; this flag is about
|
|
23
|
+
* storing and reading the rows a driver owns.
|
|
24
|
+
*/
|
|
25
|
+
vectors: boolean;
|
|
26
|
+
/** The backend applies its own schema migrations on open (`MigrationRunner`). */
|
|
27
|
+
migrations: boolean;
|
|
28
|
+
/** The contract is async (always `true` for contract major 1 — see `interfaces/version.ts`). */
|
|
29
|
+
async: true;
|
|
30
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The storage HAL's async shape (DB-2).
|
|
3
|
+
*
|
|
4
|
+
* Every port method that touches stored state returns this: the in-process SQLite driver fulfils the
|
|
5
|
+
* contract by returning a plain value (it has no I/O to wait for), while a remote driver returns a
|
|
6
|
+
* promise. Callers ALWAYS `await`, so no store, no retrieval leg and no lifecycle caller has to know
|
|
7
|
+
* which kind of driver it holds — and a new driver never rewrites a call site.
|
|
8
|
+
*
|
|
9
|
+
* @module interfaces/common
|
|
10
|
+
*/
|
|
11
|
+
export type Awaitable<T> = T | Promise<T>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The storage error boundary (DB-3).
|
|
3
|
+
*
|
|
4
|
+
* SQLite reports failures as a driver error object with a numeric `errcode`; PostgreSQL has SQLSTATE,
|
|
5
|
+
* an HTTP backend has status codes, and none of those belong above the port. The store has exactly
|
|
6
|
+
* three things it can usefully do with a failed read/write: retry (the backend is temporarily
|
|
7
|
+
* unavailable), report a conflict (a uniqueness/constraint rule refused the write), or fail (anything
|
|
8
|
+
* else). This module names those three, and `db/sqlite.ts` — the one place that sees a driver error —
|
|
9
|
+
* maps into them.
|
|
10
|
+
*
|
|
11
|
+
* WHAT THIS IS NOT: a replacement for the domain refusals. `TITLE_IMMUTABLE`, `RELATIONS_NEED_NOTE`
|
|
12
|
+
* and the other `Error`s the store throws are decisions about the caller's request, not backend
|
|
13
|
+
* failures; they stay plain and unchanged. Only errors that come out of the database layer are
|
|
14
|
+
* classified here.
|
|
15
|
+
*
|
|
16
|
+
* The messages are preserved verbatim (the classification is metadata), so every existing caller and
|
|
17
|
+
* test that asserts on a driver message keeps working.
|
|
18
|
+
*
|
|
19
|
+
* @module interfaces/errors
|
|
20
|
+
*/
|
|
21
|
+
/** What a caller can do about a failed storage operation. */
|
|
22
|
+
export type StorageFailureKind = 'unavailable' | 'constraint' | 'corrupt' | 'unknown';
|
|
23
|
+
/** A database-layer failure, classified so the boundary does not leak a driver's error shape. */
|
|
24
|
+
export declare class StorageError extends Error {
|
|
25
|
+
readonly kind: StorageFailureKind;
|
|
26
|
+
constructor(message: string, kind: StorageFailureKind, options?: {
|
|
27
|
+
code?: string | number;
|
|
28
|
+
cause?: unknown;
|
|
29
|
+
});
|
|
30
|
+
/** The driver's own code, kept for a log line — never branched on above the adapter. */
|
|
31
|
+
readonly code?: string | number;
|
|
32
|
+
/** Whether the same call may succeed if repeated later (a lock timeout, a restarting server). */
|
|
33
|
+
get retryable(): boolean;
|
|
34
|
+
}
|
|
35
|
+
/** The backend is temporarily unable to serve (SQLite: `SQLITE_BUSY`/`SQLITE_LOCKED`). */
|
|
36
|
+
export declare class StorageUnavailableError extends StorageError {
|
|
37
|
+
constructor(message: string, options?: {
|
|
38
|
+
code?: string | number;
|
|
39
|
+
cause?: unknown;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/** The backend refused the write because of one of its integrity rules. */
|
|
43
|
+
export declare class StorageConstraintError extends StorageError {
|
|
44
|
+
constructor(message: string, options?: {
|
|
45
|
+
code?: string | number;
|
|
46
|
+
cause?: unknown;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/** The stored data cannot be read (SQLite: `SQLITE_CORRUPT`/`SQLITE_NOTADB`). */
|
|
50
|
+
export declare class StorageCorruptError extends StorageError {
|
|
51
|
+
constructor(message: string, options?: {
|
|
52
|
+
code?: string | number;
|
|
53
|
+
cause?: unknown;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/** Whether a caught value is already one of ours (a second mapping must not wrap it again). */
|
|
57
|
+
export declare function isStorageError(error: unknown): error is StorageError;
|
|
58
|
+
/**
|
|
59
|
+
* Classify a driver failure into the storage vocabulary.
|
|
60
|
+
*
|
|
61
|
+
* Takes `unknown` because that is what a `catch` binds; anything already classified is returned
|
|
62
|
+
* untouched (mapping is idempotent), and a non-error value becomes a generic `StorageError` carrying
|
|
63
|
+
* its string form, so callers never have to re-check.
|
|
64
|
+
*/
|
|
65
|
+
export declare function mapStorageError(error: unknown): StorageError;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The storage HAL contract (docs/DB_DRIVER_LAYOUT.md).
|
|
3
|
+
*
|
|
4
|
+
* Nothing in this directory may import `dbdrivers/`, `db/dao` or `node:sqlite`: these files are the
|
|
5
|
+
* vocabulary a driver implements and the store consumes, and they have to stay loadable in a host
|
|
6
|
+
* that has no SQLite at all.
|
|
7
|
+
*
|
|
8
|
+
* @module interfaces
|
|
9
|
+
*/
|
|
10
|
+
export * from './common.js';
|
|
11
|
+
export * from './version.js';
|
|
12
|
+
export * from './capabilities.js';
|
|
13
|
+
export * from './migrations.js';
|
|
14
|
+
export * from './repositories.js';
|
|
15
|
+
export * from './search_index.js';
|
|
16
|
+
export * from './errors.js';
|
|
17
|
+
export * from './storage_backend.js';
|
|
18
|
+
export * from './storage_driver.js';
|