cortexm 0.3.0__py3-none-any.whl
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.
- context_m.py +17 -0
- cortexm/__init__.py +45 -0
- cortexm/accel.py +403 -0
- cortexm/api/__init__.py +0 -0
- cortexm/api/chaos.py +118 -0
- cortexm/api/memory.py +635 -0
- cortexm/bench/__init__.py +0 -0
- cortexm/bench/abilities.py +311 -0
- cortexm/bench/baselines.py +89 -0
- cortexm/bench/beam_loader.py +317 -0
- cortexm/bench/generator.py +376 -0
- cortexm/bench/harness.py +211 -0
- cortexm/bench/messy.py +218 -0
- cortexm/bench/micro.py +251 -0
- cortexm/bench/ood.py +443 -0
- cortexm/bench/run.py +137 -0
- cortexm/bridge/__init__.py +0 -0
- cortexm/bridge/dates.py +178 -0
- cortexm/bridge/decoders.py +204 -0
- cortexm/bridge/enrich.py +255 -0
- cortexm/bridge/extractor.py +316 -0
- cortexm/bridge/fallback.py +332 -0
- cortexm/bridge/onnx_runtime.py +158 -0
- cortexm/bridge/patterns.py +760 -0
- cortexm/bridge/ppr.py +104 -0
- cortexm/bridge/prefilter.py +188 -0
- cortexm/bridge/query_extract.py +420 -0
- cortexm/bridge/reader.py +1174 -0
- cortexm/bridge/rerank.py +204 -0
- cortexm/bridge/writer.py +492 -0
- cortexm/cli.py +295 -0
- cortexm/cognition/__init__.py +53 -0
- cortexm/cognition/abstraction.py +192 -0
- cortexm/cognition/analogy.py +159 -0
- cortexm/cognition/engine.py +204 -0
- cortexm/cognition/gaps.py +365 -0
- cortexm/cognition/scanner.py +204 -0
- cortexm/config.py +375 -0
- cortexm/cortexm.py +8 -0
- cortexm/enterprise/__init__.py +0 -0
- cortexm/enterprise/audit.py +178 -0
- cortexm/enterprise/governance.py +239 -0
- cortexm/errors.py +35 -0
- cortexm/features/__init__.py +0 -0
- cortexm/features/git.py +204 -0
- cortexm/features/prefetch.py +88 -0
- cortexm/features/zk.py +105 -0
- cortexm/federation/__init__.py +39 -0
- cortexm/federation/crdt.py +275 -0
- cortexm/federation/fabric.py +109 -0
- cortexm/federation/hlc.py +80 -0
- cortexm/federation/node.py +145 -0
- cortexm/federation/schema_report.py +73 -0
- cortexm/federation/transport.py +164 -0
- cortexm/index/__init__.py +19 -0
- cortexm/index/nsg.py +386 -0
- cortexm/mcp/__init__.py +0 -0
- cortexm/mcp/server.py +985 -0
- cortexm/metrics.py +62 -0
- cortexm/migrate/__init__.py +0 -0
- cortexm/migrate/importers.py +192 -0
- cortexm/provenance/__init__.py +78 -0
- cortexm/provenance/agent.py +214 -0
- cortexm/provenance/cose.py +201 -0
- cortexm/provenance/scitt.py +258 -0
- cortexm/provenance/vc.py +250 -0
- cortexm/security/__init__.py +0 -0
- cortexm/security/crypto.py +162 -0
- cortexm/security/hashes.py +140 -0
- cortexm/security/injection.py +149 -0
- cortexm/security/mind.py +154 -0
- cortexm/security/pii.py +265 -0
- cortexm/security/rbac.py +169 -0
- cortexm/security/sandbox.py +131 -0
- cortexm/security/zk_hamming.py +142 -0
- cortexm/security/zk_sql.py +485 -0
- cortexm/server/__init__.py +0 -0
- cortexm/server/metrics.py +88 -0
- cortexm/server/rest.py +936 -0
- cortexm/server/sparql.py +984 -0
- cortexm/text/__init__.py +0 -0
- cortexm/text/dissim.py +252 -0
- cortexm/text/embedder.py +155 -0
- cortexm/text/fuzzy.py +218 -0
- cortexm/text/idiolect.py +253 -0
- cortexm/text/labse.py +374 -0
- cortexm/text/tokenizer.py +79 -0
- cortexm/trace/__init__.py +0 -0
- cortexm/trace/blob_arena.py +277 -0
- cortexm/trace/consolidate.py +337 -0
- cortexm/trace/contradictions.py +69 -0
- cortexm/trace/dedup.py +114 -0
- cortexm/trace/edges.py +214 -0
- cortexm/trace/fact.py +121 -0
- cortexm/trace/fade.py +245 -0
- cortexm/trace/lifecycle.py +112 -0
- cortexm/trace/rebuild.py +173 -0
- cortexm/trace/rules.py +171 -0
- cortexm/trace/store.py +680 -0
- cortexm/trace/structural.py +183 -0
- cortexm/trace/tmt.py +335 -0
- cortexm/util.py +148 -0
- cortexm/vsa/__init__.py +0 -0
- cortexm/vsa/attribution.py +149 -0
- cortexm/vsa/cleanup.py +161 -0
- cortexm/vsa/codecs.py +397 -0
- cortexm/vsa/hologram_overlay.py +139 -0
- cortexm/vsa/index.py +163 -0
- cortexm/vsa/ops.py +149 -0
- cortexm/vsa/palace.py +446 -0
- cortexm/vsa/role_vectors.py +236 -0
- cortexm/vsa/slb.py +78 -0
- cortexm/vsa/tlsh_trie.py +137 -0
- cortexm/vsa/working_memory.py +249 -0
- cortexm-0.3.0.dist-info/METADATA +482 -0
- cortexm-0.3.0.dist-info/RECORD +120 -0
- cortexm-0.3.0.dist-info/WHEEL +5 -0
- cortexm-0.3.0.dist-info/entry_points.txt +2 -0
- cortexm-0.3.0.dist-info/licenses/LICENSE +190 -0
- cortexm-0.3.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""AnalogyDetector — finds structurally isomorphic domains via bipartite
|
|
2
|
+
relation mapping.
|
|
3
|
+
|
|
4
|
+
Fourth stage of the HMS cognition engine. Given two domains (sets of
|
|
5
|
+
entities + relations), find an isomorphism between their relation
|
|
6
|
+
graphs that maximizes structural overlap.
|
|
7
|
+
|
|
8
|
+
Classic example from Hofstadter/Mitchell's Copycat:
|
|
9
|
+
"The atom is to the solar system as the cell is to the city."
|
|
10
|
+
The detector finds that the (nucleus, orbits, electron) relation
|
|
11
|
+
triple in atoms has the same SHAPE as (sun, orbits, planet) in solar
|
|
12
|
+
systems and (mayor, governs, citizen) in cities.
|
|
13
|
+
|
|
14
|
+
For Context-M, we implement a simpler version: for each pair of
|
|
15
|
+
relations (r_a, r_b) with the same fanout pattern across subjects,
|
|
16
|
+
report an analogy. E.g.:
|
|
17
|
+
(father, male_parent) and (mother, female_parent) have identical
|
|
18
|
+
fanout → report analogy: father ≈ mother in shape (under sex-swap).
|
|
19
|
+
|
|
20
|
+
This is the seed for cross-domain transfer learning — once we know
|
|
21
|
+
the structural mapping, retrieval can find facts in domain A that
|
|
22
|
+
have analogues in domain B.
|
|
23
|
+
|
|
24
|
+
Hypotheses are emitted as derived facts:
|
|
25
|
+
(r_a, ANALOGOUS_TO, r_b) with confidence < 0.5
|
|
26
|
+
|
|
27
|
+
So a reader can traverse the analogy graph to find structural
|
|
28
|
+
equivalents across domains.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import json
|
|
34
|
+
from collections import defaultdict
|
|
35
|
+
from dataclasses import dataclass, field
|
|
36
|
+
from datetime import datetime, timezone
|
|
37
|
+
|
|
38
|
+
from cortexm.cognition.scanner import ScanResult
|
|
39
|
+
from cortexm.trace.store import TraceStore
|
|
40
|
+
from cortexm.util import iso, new_id
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# Edge kind used for analogy relations
|
|
44
|
+
ANALOGOUS_TO = "ANALOGOUS_TO"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class Analogy:
|
|
49
|
+
"""A structural analogy between two relations."""
|
|
50
|
+
relation_a: str
|
|
51
|
+
relation_b: str
|
|
52
|
+
shared_fanout: int = 0 # how many subjects share the fanout pattern
|
|
53
|
+
overlap_score: float = 0.0 # jaccard similarity of subject sets
|
|
54
|
+
confidence: float = 0.0
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class AnalogyResult:
|
|
59
|
+
analogies: list[Analogy] = field(default_factory=list)
|
|
60
|
+
edges_added: int = 0
|
|
61
|
+
duration_ms: float = 0.0
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class AnalogyDetector:
|
|
65
|
+
"""Finds structurally isomorphic relation pairs."""
|
|
66
|
+
|
|
67
|
+
def __init__(self, store: TraceStore,
|
|
68
|
+
min_overlap: float = 0.40,
|
|
69
|
+
min_support: int = 2) -> None:
|
|
70
|
+
self.store = store
|
|
71
|
+
self.min_overlap = min_overlap
|
|
72
|
+
self.min_support = min_support
|
|
73
|
+
|
|
74
|
+
def run(self, scan: ScanResult, *,
|
|
75
|
+
dry_run: bool = False,
|
|
76
|
+
commit_id: str | None = None,
|
|
77
|
+
user_id: str | None = None) -> AnalogyResult:
|
|
78
|
+
"""Find analogies by comparing relation subject sets."""
|
|
79
|
+
import time
|
|
80
|
+
t0 = time.perf_counter()
|
|
81
|
+
|
|
82
|
+
# build a per-relation subject set
|
|
83
|
+
rel_subjects: dict[str, set[str]] = defaultdict(set)
|
|
84
|
+
where = "is_active=1 AND quarantined=0"
|
|
85
|
+
args: tuple = ()
|
|
86
|
+
if user_id is not None:
|
|
87
|
+
where += " AND user_id=?"
|
|
88
|
+
args = (user_id,)
|
|
89
|
+
rows = self.store.conn.execute(
|
|
90
|
+
f"SELECT relation, subject FROM facts WHERE {where}", args)
|
|
91
|
+
for r in rows:
|
|
92
|
+
rel_subjects[r[0]].add(r[1])
|
|
93
|
+
|
|
94
|
+
# for each pair of relations with sufficient support, compute
|
|
95
|
+
# the jaccard similarity of their subject sets
|
|
96
|
+
relations = list(rel_subjects.keys())
|
|
97
|
+
analogies: list[Analogy] = []
|
|
98
|
+
for i, ra in enumerate(relations):
|
|
99
|
+
sa = rel_subjects[ra]
|
|
100
|
+
if len(sa) < self.min_support:
|
|
101
|
+
continue
|
|
102
|
+
for rb in relations[i + 1:]:
|
|
103
|
+
sb = rel_subjects[rb]
|
|
104
|
+
if len(sb) < self.min_support:
|
|
105
|
+
continue
|
|
106
|
+
inter = len(sa & sb)
|
|
107
|
+
if inter < self.min_support:
|
|
108
|
+
continue
|
|
109
|
+
union = len(sa | sb)
|
|
110
|
+
if union == 0:
|
|
111
|
+
continue
|
|
112
|
+
jac = inter / union
|
|
113
|
+
if jac < self.min_overlap:
|
|
114
|
+
continue
|
|
115
|
+
analogies.append(Analogy(
|
|
116
|
+
relation_a=ra,
|
|
117
|
+
relation_b=rb,
|
|
118
|
+
shared_fanout=inter,
|
|
119
|
+
overlap_score=jac,
|
|
120
|
+
confidence=min(0.49, 0.10 + 0.20 * jac),
|
|
121
|
+
))
|
|
122
|
+
|
|
123
|
+
# emit edges as derived facts: (ra, ANALOGOUS_TO, rb)
|
|
124
|
+
edges_added = 0
|
|
125
|
+
if not dry_run and analogies:
|
|
126
|
+
ts = iso(_now())
|
|
127
|
+
for a in analogies:
|
|
128
|
+
fid = new_id()
|
|
129
|
+
self.store.conn.execute(
|
|
130
|
+
"INSERT INTO facts "
|
|
131
|
+
"(id, subject, relation, value, valid_from, tx_from, "
|
|
132
|
+
" confidence, user_id, memory_type, is_derived, "
|
|
133
|
+
" is_active, birth_commit, provenance) "
|
|
134
|
+
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
|
135
|
+
(fid, a.relation_a, ANALOGOUS_TO, a.relation_b,
|
|
136
|
+
ts, ts, a.confidence,
|
|
137
|
+
user_id or "default", "long_term",
|
|
138
|
+
1, 1, commit_id,
|
|
139
|
+
json.dumps({
|
|
140
|
+
"kind": "analogy",
|
|
141
|
+
"overlap_score": round(a.overlap_score, 4),
|
|
142
|
+
"shared_fanout": a.shared_fanout,
|
|
143
|
+
"generated_by": "cognition.analogy",
|
|
144
|
+
})))
|
|
145
|
+
edges_added += 1
|
|
146
|
+
if commit_id:
|
|
147
|
+
self.store.update_commit_n_facts(commit_id, edges_added)
|
|
148
|
+
|
|
149
|
+
return AnalogyResult(
|
|
150
|
+
analogies=analogies,
|
|
151
|
+
edges_added=edges_added,
|
|
152
|
+
duration_ms=(time.perf_counter() - t0) * 1000.0)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _now():
|
|
156
|
+
return datetime.now(timezone.utc)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
__all__ = ["AnalogyDetector", "Analogy", "AnalogyResult", "ANALOGOUS_TO"]
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""CognitionEngine — orchestrates the 5-stage self-organization pipeline.
|
|
2
|
+
|
|
3
|
+
Triggered from `cortexm consolidate` (or `Memory.consolidate()`) so
|
|
4
|
+
the engine is deterministic, auditable, and never produces surprise
|
|
5
|
+
writes in the foreground path. Each pass:
|
|
6
|
+
|
|
7
|
+
1. PatternScanner.run() — surface structural regularities
|
|
8
|
+
2. AbstractionEngine.run() — build prototype categories
|
|
9
|
+
3. GapDetector.run() — find missing relations
|
|
10
|
+
4. HypothesisEngine.run() — propose fillers for gaps
|
|
11
|
+
5. AnalogyDetector.run() — find cross-domain analogies
|
|
12
|
+
|
|
13
|
+
Output is committed as derived facts with confidence < 0.5 and tagged
|
|
14
|
+
is_derived=1, so they don't pollute the active fact count and don't
|
|
15
|
+
fire in retrieval unless explicitly promoted (via
|
|
16
|
+
`Memory.promote_hypothesis(fact_id)`).
|
|
17
|
+
|
|
18
|
+
The HYPOTHESIZED_BY edge kind is registered here so
|
|
19
|
+
`cortexm.trace.edges.ALL_KINDS` includes it. PROMOTED_FROM is the
|
|
20
|
+
inverse — when a hypothesis is confirmed by user input, the new fact
|
|
21
|
+
points back to its hypothesis origin.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
from dataclasses import dataclass, field
|
|
27
|
+
from typing import Any
|
|
28
|
+
|
|
29
|
+
from cortexm.cognition.scanner import PatternScanner, ScanResult
|
|
30
|
+
from cortexm.cognition.abstraction import AbstractionEngine, AbstractionResult
|
|
31
|
+
from cortexm.cognition.gaps import (
|
|
32
|
+
GapDetector, HypothesisEngine, GapResult, HypothesisResult, HYPOTHESIZED_BY)
|
|
33
|
+
from cortexm.cognition.analogy import (
|
|
34
|
+
AnalogyDetector, AnalogyResult, ANALOGOUS_TO)
|
|
35
|
+
from cortexm.trace.store import TraceStore
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Edge kinds produced by the cognition engine. These are added to the
|
|
39
|
+
# trace.edges vocabulary. We register them lazily on first cognition
|
|
40
|
+
# pass so a Trace that never ran cognition doesn't see these kinds in
|
|
41
|
+
# queries (avoiding schema pollution).
|
|
42
|
+
PROMOTED_FROM = "PROMOTED_FROM" # user-confirmed fact -> hypothesis origin
|
|
43
|
+
ABSTRACTS = "ABSTRACTS" # abstraction -> member
|
|
44
|
+
INSTANTIATES = "INSTANTIATES" # member -> abstraction
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class CognitionReport:
|
|
49
|
+
"""Full report of one cognition pass."""
|
|
50
|
+
scan: dict = field(default_factory=dict)
|
|
51
|
+
abstraction: dict = field(default_factory=dict)
|
|
52
|
+
gaps: dict = field(default_factory=dict)
|
|
53
|
+
hypotheses: dict = field(default_factory=dict)
|
|
54
|
+
analogies: dict = field(default_factory=dict)
|
|
55
|
+
commit_id: str | None = None
|
|
56
|
+
dry_run: bool = False
|
|
57
|
+
total_derived_facts: int = 0
|
|
58
|
+
duration_ms: float = 0.0
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class CognitionEngine:
|
|
62
|
+
"""Orchestrates the 5-stage self-organization pipeline."""
|
|
63
|
+
|
|
64
|
+
def __init__(self, store: TraceStore, palace=None) -> None:
|
|
65
|
+
self.store = store
|
|
66
|
+
self.palace = palace
|
|
67
|
+
self.scanner = PatternScanner(store)
|
|
68
|
+
self.abstraction = AbstractionEngine(store)
|
|
69
|
+
self.gap_detector = GapDetector(store)
|
|
70
|
+
# pass palace for Hopfield cleanup path (currently not used
|
|
71
|
+
# by the simple strategies but the API accepts it)
|
|
72
|
+
self.hypothesis = HypothesisEngine(store, palace=palace)
|
|
73
|
+
self.analogy = AnalogyDetector(store)
|
|
74
|
+
|
|
75
|
+
def run(self, *,
|
|
76
|
+
dry_run: bool = False,
|
|
77
|
+
user_id: str | None = None) -> CognitionReport:
|
|
78
|
+
"""Run the full 5-stage pipeline.
|
|
79
|
+
|
|
80
|
+
All writes happen inside a single commit if not dry_run.
|
|
81
|
+
Returns a CognitionReport with per-stage stats.
|
|
82
|
+
"""
|
|
83
|
+
import time
|
|
84
|
+
t0 = time.perf_counter()
|
|
85
|
+
|
|
86
|
+
# if not dry_run, we expect the caller to have already started
|
|
87
|
+
# a batch + commit (consolidate() does this). We accept an
|
|
88
|
+
# optional commit_id from the caller via store._cognition_commit
|
|
89
|
+
# (stored as a session-level hint). If not present, we create
|
|
90
|
+
# our own commit.
|
|
91
|
+
own_commit = False
|
|
92
|
+
commit_id = None
|
|
93
|
+
if not dry_run:
|
|
94
|
+
try:
|
|
95
|
+
self.store.begin_batch()
|
|
96
|
+
commit_id = self.store.create_commit(
|
|
97
|
+
"cognition: scan+abstract+gap+hypothesis+analogy",
|
|
98
|
+
n_facts=0)
|
|
99
|
+
own_commit = True
|
|
100
|
+
except Exception:
|
|
101
|
+
# caller already in a batch — use their commit
|
|
102
|
+
commit_id = getattr(self.store, "_active_commit_id", None)
|
|
103
|
+
|
|
104
|
+
report = CognitionReport(dry_run=dry_run, commit_id=commit_id)
|
|
105
|
+
|
|
106
|
+
# 1. PatternScanner
|
|
107
|
+
scan = self.scanner.run(user_id=user_id)
|
|
108
|
+
report.scan = {
|
|
109
|
+
"patterns": len(scan.patterns),
|
|
110
|
+
"n_facts_scanned": scan.n_facts_scanned,
|
|
111
|
+
"n_relations": scan.n_relations,
|
|
112
|
+
"n_subjects": scan.n_subjects,
|
|
113
|
+
"n_values": scan.n_values,
|
|
114
|
+
"duration_ms": round(scan.duration_ms, 2),
|
|
115
|
+
"by_kind": _count_by_kind(scan.patterns),
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# 2. AbstractionEngine
|
|
119
|
+
ab = self.abstraction.run(scan, dry_run=dry_run,
|
|
120
|
+
commit_id=commit_id, user_id=user_id)
|
|
121
|
+
report.abstraction = {
|
|
122
|
+
"abstractions": len(ab.abstractions),
|
|
123
|
+
"membership_edges_added": ab.membership_edges_added,
|
|
124
|
+
"duration_ms": round(ab.duration_ms, 2),
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# 3. GapDetector
|
|
128
|
+
gaps = self.gap_detector.run(scan, user_id=user_id)
|
|
129
|
+
report.gaps = {
|
|
130
|
+
"gaps": len(gaps.gaps),
|
|
131
|
+
"n_subjects_compared": gaps.n_subjects_compared,
|
|
132
|
+
"duration_ms": round(gaps.duration_ms, 2),
|
|
133
|
+
"by_basis": _count_gaps_by_basis(gaps.gaps),
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# 4. HypothesisEngine
|
|
137
|
+
hyp = self.hypothesis.run(gaps.gaps, dry_run=dry_run,
|
|
138
|
+
commit_id=commit_id, user_id=user_id)
|
|
139
|
+
report.hypotheses = {
|
|
140
|
+
"hypotheses": len(hyp.hypotheses),
|
|
141
|
+
"facts_added": hyp.facts_added,
|
|
142
|
+
"duration_ms": round(hyp.duration_ms, 2),
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
# 5. AnalogyDetector
|
|
146
|
+
ana = self.analogy.run(scan, dry_run=dry_run,
|
|
147
|
+
commit_id=commit_id, user_id=user_id)
|
|
148
|
+
report.analogies = {
|
|
149
|
+
"analogies": len(ana.analogies),
|
|
150
|
+
"edges_added": ana.edges_added,
|
|
151
|
+
"duration_ms": round(ana.duration_ms, 2),
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
report.total_derived_facts = (
|
|
155
|
+
ab.membership_edges_added
|
|
156
|
+
+ hyp.facts_added
|
|
157
|
+
+ ana.edges_added
|
|
158
|
+
)
|
|
159
|
+
report.duration_ms = (time.perf_counter() - t0) * 1000.0
|
|
160
|
+
|
|
161
|
+
if not dry_run and own_commit:
|
|
162
|
+
# update commit n_facts to the actual count we wrote
|
|
163
|
+
try:
|
|
164
|
+
self.store.update_commit_n_facts(
|
|
165
|
+
commit_id, report.total_derived_facts)
|
|
166
|
+
except Exception:
|
|
167
|
+
pass
|
|
168
|
+
try:
|
|
169
|
+
self.store.end_batch()
|
|
170
|
+
except Exception:
|
|
171
|
+
pass
|
|
172
|
+
|
|
173
|
+
return report
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def run_cognition_pass(store: TraceStore, palace=None, *,
|
|
177
|
+
dry_run: bool = False,
|
|
178
|
+
user_id: str | None = None) -> CognitionReport:
|
|
179
|
+
"""Convenience function: one-shot cognition pass on a TraceStore."""
|
|
180
|
+
return CognitionEngine(store, palace=palace).run(
|
|
181
|
+
dry_run=dry_run, user_id=user_id)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _count_by_kind(patterns) -> dict[str, int]:
|
|
185
|
+
out: dict[str, int] = {}
|
|
186
|
+
for p in patterns:
|
|
187
|
+
out[p.kind] = out.get(p.kind, 0) + 1
|
|
188
|
+
return out
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _count_gaps_by_basis(gaps) -> dict[str, int]:
|
|
192
|
+
out: dict[str, int] = {}
|
|
193
|
+
for g in gaps:
|
|
194
|
+
out[g.basis] = out.get(g.basis, 0) + 1
|
|
195
|
+
return out
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
__all__ = [
|
|
199
|
+
"CognitionEngine", "CognitionReport",
|
|
200
|
+
"run_cognition_pass",
|
|
201
|
+
"HYPOTHESIZED_BY", "PROMOTED_FROM",
|
|
202
|
+
"ABSTRACTS", "INSTANTIATES",
|
|
203
|
+
"ANALOGOUS_TO",
|
|
204
|
+
]
|