rememberstack 0.1.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.
- rememberstack/__init__.py +9 -0
- rememberstack/adapters/__init__.py +42 -0
- rememberstack/adapters/codex_writer.py +221 -0
- rememberstack/adapters/markitdown_converter.py +42 -0
- rememberstack/adapters/openrouter.py +136 -0
- rememberstack/adapters/selfhost/__init__.py +54 -0
- rememberstack/adapters/selfhost/forget.py +66 -0
- rememberstack/adapters/selfhost/git.py +374 -0
- rememberstack/adapters/selfhost/lance.py +328 -0
- rememberstack/adapters/selfhost/minio.py +279 -0
- rememberstack/adapters/selfhost/mounts.py +249 -0
- rememberstack/adapters/selfhost/object_store.py +130 -0
- rememberstack/adapters/selfhost/projection.py +80 -0
- rememberstack/adapters/selfhost/queue.py +137 -0
- rememberstack/adapters/selfhost/telemetry.py +45 -0
- rememberstack/adapters/selfhost/watcher.py +70 -0
- rememberstack/adapters/testing/__init__.py +15 -0
- rememberstack/adapters/testing/cost_meter.py +13 -0
- rememberstack/adapters/testing/model_provider.py +83 -0
- rememberstack/adapters/testing/queue.py +43 -0
- rememberstack/adapters/testing/telemetry.py +22 -0
- rememberstack/client.py +19 -0
- rememberstack/core/__init__.py +127 -0
- rememberstack/core/blockizer.py +189 -0
- rememberstack/core/chunker.py +216 -0
- rememberstack/core/consumption_skill.py +275 -0
- rememberstack/core/conversion.py +76 -0
- rememberstack/core/core_manifest.py +598 -0
- rememberstack/core/extension_packs.py +124 -0
- rememberstack/core/forget.py +17 -0
- rememberstack/core/knowledge_authored.py +276 -0
- rememberstack/core/knowledge_compile.py +215 -0
- rememberstack/core/knowledge_fact_sheet.py +210 -0
- rememberstack/core/knowledge_hashing.py +68 -0
- rememberstack/core/knowledge_planner.py +64 -0
- rememberstack/core/knowledge_writer.py +175 -0
- rememberstack/core/ranking.py +200 -0
- rememberstack/core/recipe_linter.py +149 -0
- rememberstack/core/section_snap.py +209 -0
- rememberstack/core/storage_routing.py +27 -0
- rememberstack/eval/__init__.py +53 -0
- rememberstack/eval/consumption.py +141 -0
- rememberstack/eval/contradiction.py +184 -0
- rememberstack/eval/harness.py +136 -0
- rememberstack/eval/lifecycle.py +400 -0
- rememberstack/eval/operational_scale.py +49 -0
- rememberstack/eval/resolution.py +255 -0
- rememberstack/eval/retrieval_spikes.py +50 -0
- rememberstack/eval/skeleton.py +231 -0
- rememberstack/llm/__init__.py +1 -0
- rememberstack/model/__init__.py +589 -0
- rememberstack/model/adjudication.py +100 -0
- rememberstack/model/auth.py +27 -0
- rememberstack/model/blocks.py +30 -0
- rememberstack/model/chunks.py +190 -0
- rememberstack/model/claims.py +162 -0
- rememberstack/model/client.py +98 -0
- rememberstack/model/clustering.py +54 -0
- rememberstack/model/component_version.py +124 -0
- rememberstack/model/consumption.py +88 -0
- rememberstack/model/conversion.py +31 -0
- rememberstack/model/deployment.py +53 -0
- rememberstack/model/documents.py +168 -0
- rememberstack/model/envelope.py +513 -0
- rememberstack/model/evaluation.py +72 -0
- rememberstack/model/forget.py +143 -0
- rememberstack/model/git.py +13 -0
- rememberstack/model/knowledge.py +840 -0
- rememberstack/model/knowledge_authored.py +325 -0
- rememberstack/model/knowledge_planner.py +431 -0
- rememberstack/model/lifecycle.py +42 -0
- rememberstack/model/model_provider.py +78 -0
- rememberstack/model/mounts.py +24 -0
- rememberstack/model/object_store.py +21 -0
- rememberstack/model/operational_scale.py +59 -0
- rememberstack/model/operations.py +153 -0
- rememberstack/model/processing.py +228 -0
- rememberstack/model/queue.py +73 -0
- rememberstack/model/recipes.py +83 -0
- rememberstack/model/relations.py +79 -0
- rememberstack/model/resolution.py +83 -0
- rememberstack/model/retrieval_spikes.py +62 -0
- rememberstack/model/sections.py +120 -0
- rememberstack/model/telemetry.py +30 -0
- rememberstack/ports/__init__.py +29 -0
- rememberstack/ports/auth.py +16 -0
- rememberstack/ports/connector.py +23 -0
- rememberstack/ports/cost_meter.py +17 -0
- rememberstack/ports/forget.py +20 -0
- rememberstack/ports/git.py +20 -0
- rememberstack/ports/model_provider.py +28 -0
- rememberstack/ports/mounts.py +16 -0
- rememberstack/ports/object_store.py +27 -0
- rememberstack/ports/p1_index.py +92 -0
- rememberstack/ports/purge.py +93 -0
- rememberstack/ports/queue.py +23 -0
- rememberstack/ports/telemetry.py +21 -0
- rememberstack/profiles/__init__.py +22 -0
- rememberstack/profiles/selfhost.py +324 -0
- rememberstack/profiles/selfhost_forget.py +158 -0
- rememberstack/profiles/selfhost_operations.py +95 -0
- rememberstack/py.typed +1 -0
- rememberstack/spine/__init__.py +93 -0
- rememberstack/spine/admission.py +26 -0
- rememberstack/spine/backfill.py +168 -0
- rememberstack/spine/catalog_contract.py +742 -0
- rememberstack/spine/chunk_catalog.py +237 -0
- rememberstack/spine/claim_catalog.py +298 -0
- rememberstack/spine/clustering.py +740 -0
- rememberstack/spine/component_versions.py +208 -0
- rememberstack/spine/consumption.py +81 -0
- rememberstack/spine/deployment_bootstrap.py +445 -0
- rememberstack/spine/document_catalog.py +621 -0
- rememberstack/spine/entity_registry.py +205 -0
- rememberstack/spine/extension_packs.py +220 -0
- rememberstack/spine/fact_catalog.py +571 -0
- rememberstack/spine/forget.py +1753 -0
- rememberstack/spine/knowledge.py +5467 -0
- rememberstack/spine/lifecycle.py +1071 -0
- rememberstack/spine/migrations/__init__.py +1 -0
- rememberstack/spine/migrations/_helpers.py +153 -0
- rememberstack/spine/migrations/env.py +58 -0
- rememberstack/spine/migrations/script.py.mako +27 -0
- rememberstack/spine/migrations/versions/__init__.py +1 -0
- rememberstack/spine/migrations/versions/p0_02_0001_extensions_enums.py +189 -0
- rememberstack/spine/migrations/versions/p0_02_0002_infrastructure_registries.py +321 -0
- rememberstack/spine/migrations/versions/p0_02_0003_entities_evaluation_e0_e1.py +631 -0
- rememberstack/spine/migrations/versions/p0_02_0004_claims_facts_evidence.py +411 -0
- rememberstack/spine/migrations/versions/p0_02_0005_projection_knowledge_retrieval.py +391 -0
- rememberstack/spine/migrations/versions/p0_02_0006_partitions_views.py +158 -0
- rememberstack/spine/migrations/versions/p2_06_0007_invalidated_outcome.py +26 -0
- rememberstack/spine/migrations/versions/p3_01_0008_document_version_target.py +58 -0
- rememberstack/spine/migrations/versions/p3_05_0009_reconcile_stage.py +27 -0
- rememberstack/spine/migrations/versions/p3_07_0010_lifecycle_eval_suite.py +25 -0
- rememberstack/spine/migrations/versions/p4_01_0011_survivor_view_rewrite.py +57 -0
- rememberstack/spine/migrations/versions/p6_02_0012_knowledge_compile_recovery.py +58 -0
- rememberstack/spine/migrations/versions/p6_04_0013_knowledge_writer_ledger.py +46 -0
- rememberstack/spine/migrations/versions/p6_05_0014_knowledge_planner_runtime.py +217 -0
- rememberstack/spine/migrations/versions/p6_06_0015_authored_dispatch_runtime.py +38 -0
- rememberstack/spine/migrations/versions/p7_02_0016_operational_eval_suite.py +19 -0
- rememberstack/spine/migrations/versions/p7_05_0017_hard_forget.py +55 -0
- rememberstack/spine/observation_adjudication.py +778 -0
- rememberstack/spine/operations.py +298 -0
- rememberstack/spine/projection.py +662 -0
- rememberstack/spine/recipes.py +276 -0
- rememberstack/spine/resolver.py +763 -0
- rememberstack/spine/review.py +650 -0
- rememberstack/spine/settings.py +22 -0
- rememberstack/spine/supersession.py +510 -0
- rememberstack/spine/sync.py +128 -0
- rememberstack/spine/work_ledger.py +816 -0
- rememberstack/surfaces/__init__.py +110 -0
- rememberstack/surfaces/cli.py +447 -0
- rememberstack/surfaces/consumption_skill.py +87 -0
- rememberstack/surfaces/graph_queries.py +698 -0
- rememberstack/surfaces/http_api.py +377 -0
- rememberstack/surfaces/mcp.py +67 -0
- rememberstack/surfaces/query_engine.py +1591 -0
- rememberstack/surfaces/recipe_executor.py +185 -0
- rememberstack/surfaces/recipe_surface.py +219 -0
- rememberstack/surfaces/remote_mcp.py +133 -0
- rememberstack/surfaces/sdk.py +324 -0
- rememberstack/workers/__init__.py +155 -0
- rememberstack/workers/base.py +312 -0
- rememberstack/workers/e0.py +577 -0
- rememberstack/workers/e1.py +425 -0
- rememberstack/workers/e2.py +525 -0
- rememberstack/workers/e3.py +434 -0
- rememberstack/workers/forget.py +299 -0
- rememberstack/workers/knowledge_authored.py +146 -0
- rememberstack/workers/knowledge_driver.py +735 -0
- rememberstack/workers/knowledge_fact_sheet.py +123 -0
- rememberstack/workers/knowledge_planner.py +325 -0
- rememberstack/workers/knowledge_writer.py +393 -0
- rememberstack/workers/operations.py +42 -0
- rememberstack/workers/p1.py +234 -0
- rememberstack/workers/p2.py +513 -0
- rememberstack/workers/p2_analytics.py +276 -0
- rememberstack/workers/p3.py +673 -0
- rememberstack/workers/reconcile.py +485 -0
- rememberstack/workers/sync.py +168 -0
- rememberstack-0.1.0.dist-info/METADATA +213 -0
- rememberstack-0.1.0.dist-info/RECORD +186 -0
- rememberstack-0.1.0.dist-info/WHEEL +4 -0
- rememberstack-0.1.0.dist-info/entry_points.txt +2 -0
- rememberstack-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""D74 purge acknowledgement for self-host projection bytes and caches."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from pathlib import PurePosixPath
|
|
5
|
+
import shutil
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
|
|
8
|
+
from rememberstack.model import ObjectKey
|
|
9
|
+
from rememberstack.ports import ObjectPurgePort
|
|
10
|
+
from rememberstack.spine import ProjectionCatalog
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SelfHostProjectionPurger:
|
|
14
|
+
"""Erase old durable P2/P3 prefixes, registry rows, and local serving copies."""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
*,
|
|
19
|
+
object_purger: ObjectPurgePort,
|
|
20
|
+
catalog: ProjectionCatalog,
|
|
21
|
+
p2_cache_root: Path,
|
|
22
|
+
mount_root: Path,
|
|
23
|
+
) -> None:
|
|
24
|
+
"""Bind explicit durable and local-cache roots without inventing providers."""
|
|
25
|
+
self._object_purger = object_purger
|
|
26
|
+
self._catalog = catalog
|
|
27
|
+
self._p2_cache_root = p2_cache_root.resolve()
|
|
28
|
+
self._mount_root = mount_root.resolve()
|
|
29
|
+
|
|
30
|
+
def purge_projections(
|
|
31
|
+
self, *, deployment_id: UUID, prefixes: tuple[ObjectKey, ...]
|
|
32
|
+
) -> None:
|
|
33
|
+
"""Idempotently erase every manifest-nominated old projection copy."""
|
|
34
|
+
self._object_purger.purge_objects(keys=(), prefixes=prefixes)
|
|
35
|
+
self._catalog.purge_snapshot_prefixes(
|
|
36
|
+
deployment_id=deployment_id,
|
|
37
|
+
prefixes=tuple(prefix.root for prefix in prefixes),
|
|
38
|
+
)
|
|
39
|
+
for prefix in prefixes:
|
|
40
|
+
for path in self._serving_paths(deployment_id=deployment_id, prefix=prefix):
|
|
41
|
+
self._remove(path=path)
|
|
42
|
+
|
|
43
|
+
def verify_projections_purged(
|
|
44
|
+
self, *, deployment_id: UUID, prefixes: tuple[ObjectKey, ...]
|
|
45
|
+
) -> None:
|
|
46
|
+
"""Prove durable bytes, registry rows, caches, and mounts are absent."""
|
|
47
|
+
self._object_purger.verify_objects_purged(keys=(), prefixes=prefixes)
|
|
48
|
+
roots = tuple(prefix.root for prefix in prefixes)
|
|
49
|
+
if self._catalog.snapshot_prefixes_exist(
|
|
50
|
+
deployment_id=deployment_id, prefixes=roots
|
|
51
|
+
):
|
|
52
|
+
raise RuntimeError("projection purge verification found registry rows")
|
|
53
|
+
remaining = [
|
|
54
|
+
str(path)
|
|
55
|
+
for prefix in prefixes
|
|
56
|
+
for path in self._serving_paths(deployment_id=deployment_id, prefix=prefix)
|
|
57
|
+
if path.exists() or path.is_symlink()
|
|
58
|
+
]
|
|
59
|
+
if remaining:
|
|
60
|
+
raise RuntimeError(
|
|
61
|
+
f"projection purge verification found serving copies: {remaining!r}"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def _serving_paths(
|
|
65
|
+
self, *, deployment_id: UUID, prefix: ObjectKey
|
|
66
|
+
) -> tuple[Path, Path]:
|
|
67
|
+
"""Return the exact P2 cache and P3 versioned mount for one prefix."""
|
|
68
|
+
version = PurePosixPath(prefix.root).name
|
|
69
|
+
return (
|
|
70
|
+
self._p2_cache_root / str(deployment_id) / version,
|
|
71
|
+
self._mount_root / str(deployment_id) / f"p3-{version}",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
def _remove(*, path: Path) -> None:
|
|
76
|
+
"""Remove one exact serving copy, treating absence as success."""
|
|
77
|
+
if path.is_symlink() or path.is_file():
|
|
78
|
+
path.unlink(missing_ok=True)
|
|
79
|
+
elif path.is_dir():
|
|
80
|
+
shutil.rmtree(path)
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""The self-host delivery shell: LISTEN/NOTIFY wake-ups + SKIP LOCKED claiming (D62/D67).
|
|
2
|
+
|
|
3
|
+
Work is Postgres rows; this shell only delivers wake-ups. The schema-owned
|
|
4
|
+
insert trigger provides the transactional initial wake; `announce` re-announces
|
|
5
|
+
existing rows through the spine's notification primitive; the worker loop
|
|
6
|
+
sleeps on LISTEN with a slow fallback poll and claims only its configured
|
|
7
|
+
route, consulting a token bucket around each claim.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
import logging
|
|
12
|
+
import time
|
|
13
|
+
from uuid import UUID
|
|
14
|
+
|
|
15
|
+
import psycopg
|
|
16
|
+
|
|
17
|
+
from rememberstack.model import PipelineStage
|
|
18
|
+
from rememberstack.model import ProcessingLane
|
|
19
|
+
from rememberstack.model import QueueRoute
|
|
20
|
+
from rememberstack.model import RunResultOutcome
|
|
21
|
+
from rememberstack.model import UTCDateTime
|
|
22
|
+
from rememberstack.spine.work_ledger import WorkLedger
|
|
23
|
+
from rememberstack.workers import RunResult
|
|
24
|
+
from rememberstack.workers import Worker
|
|
25
|
+
|
|
26
|
+
_logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
_WAKE_CHANNEL = "queue_wake"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SelfHostTaskQueue:
|
|
32
|
+
"""The delivery-only announce adapter: wake an existing committed row.
|
|
33
|
+
|
|
34
|
+
Satisfies the task-queue port. Route and due-time snapshots are accepted
|
|
35
|
+
per the port contract but carry no state — the woken worker re-reads
|
|
36
|
+
Postgres, which is the only authority (D67).
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, *, ledger: WorkLedger) -> None:
|
|
40
|
+
"""Bind the adapter to the spine's notification primitive."""
|
|
41
|
+
self._ledger = ledger
|
|
42
|
+
|
|
43
|
+
def announce(
|
|
44
|
+
self,
|
|
45
|
+
*,
|
|
46
|
+
processing_id: UUID,
|
|
47
|
+
route_snapshot: QueueRoute,
|
|
48
|
+
not_before_snapshot: UTCDateTime,
|
|
49
|
+
) -> None:
|
|
50
|
+
"""Schedule at-least-once delivery by waking listeners for the row."""
|
|
51
|
+
del route_snapshot, not_before_snapshot # snapshots are hints, never state
|
|
52
|
+
self._ledger.wake(processing_id=processing_id)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class TokenBucket:
|
|
56
|
+
"""A per-route claim rate limit: refill at a fixed rate up to a capacity."""
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
*,
|
|
61
|
+
rate_per_s: float,
|
|
62
|
+
capacity: float,
|
|
63
|
+
clock: Callable[[], float] = time.monotonic,
|
|
64
|
+
) -> None:
|
|
65
|
+
"""Start full; `clock` is injectable for deterministic tests."""
|
|
66
|
+
self._rate_per_s = rate_per_s
|
|
67
|
+
self._capacity = capacity
|
|
68
|
+
self._clock = clock
|
|
69
|
+
self._tokens = capacity
|
|
70
|
+
self._refilled_at = clock()
|
|
71
|
+
|
|
72
|
+
def try_acquire(self) -> bool:
|
|
73
|
+
"""Take one token if available; never blocks."""
|
|
74
|
+
now = self._clock()
|
|
75
|
+
self._tokens = min(
|
|
76
|
+
self._capacity, self._tokens + (now - self._refilled_at) * self._rate_per_s
|
|
77
|
+
)
|
|
78
|
+
self._refilled_at = now
|
|
79
|
+
if self._tokens < 1.0:
|
|
80
|
+
return False
|
|
81
|
+
self._tokens -= 1.0
|
|
82
|
+
return True
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class SelfHostWorkerLoop:
|
|
86
|
+
"""One route's worker loop: sleep on LISTEN, drain due work, fall back to polling."""
|
|
87
|
+
|
|
88
|
+
def __init__(
|
|
89
|
+
self,
|
|
90
|
+
*,
|
|
91
|
+
worker: Worker,
|
|
92
|
+
deployment_id: UUID,
|
|
93
|
+
stage: PipelineStage,
|
|
94
|
+
lane: ProcessingLane | None,
|
|
95
|
+
bucket: TokenBucket,
|
|
96
|
+
database_url: str,
|
|
97
|
+
fallback_poll_s: float = 30.0,
|
|
98
|
+
) -> None:
|
|
99
|
+
"""Bind the loop to its route, rate limit, and LISTEN connection source."""
|
|
100
|
+
self._worker = worker
|
|
101
|
+
self._deployment_id = deployment_id
|
|
102
|
+
self._stage = stage
|
|
103
|
+
self._lane = lane
|
|
104
|
+
self._bucket = bucket
|
|
105
|
+
self._database_url = database_url
|
|
106
|
+
self._fallback_poll_s = fallback_poll_s
|
|
107
|
+
|
|
108
|
+
def drain_due(self) -> tuple[RunResult, ...]:
|
|
109
|
+
"""Claim and run due work on the route until none remains or tokens run out."""
|
|
110
|
+
results: list[RunResult] = []
|
|
111
|
+
while self._bucket.try_acquire():
|
|
112
|
+
result = self._worker.run_one(
|
|
113
|
+
deployment_id=self._deployment_id, stage=self._stage, lane=self._lane
|
|
114
|
+
)
|
|
115
|
+
if result.outcome is RunResultOutcome.NO_WORK:
|
|
116
|
+
break
|
|
117
|
+
results.append(result)
|
|
118
|
+
return tuple(results)
|
|
119
|
+
|
|
120
|
+
def run_for(self, *, duration_s: float) -> tuple[RunResult, ...]:
|
|
121
|
+
"""Listen, drain on every wake, and fall back to polling, for a bounded time.
|
|
122
|
+
|
|
123
|
+
The production loop is this method called with a large duration under a
|
|
124
|
+
supervisor; tests call it with a short one. Missed notifications are
|
|
125
|
+
covered by the fallback poll, per the packaging design.
|
|
126
|
+
"""
|
|
127
|
+
results: list[RunResult] = list(self.drain_due())
|
|
128
|
+
deadline = time.monotonic() + duration_s
|
|
129
|
+
with psycopg.connect(self._database_url, autocommit=True) as connection:
|
|
130
|
+
connection.execute(f"LISTEN {_WAKE_CHANNEL}")
|
|
131
|
+
while (remaining := deadline - time.monotonic()) > 0:
|
|
132
|
+
timeout = min(remaining, self._fallback_poll_s)
|
|
133
|
+
woken = list(connection.notifies(timeout=timeout, stop_after=1))
|
|
134
|
+
if woken:
|
|
135
|
+
_logger.debug("wake received: %s", woken[0].payload)
|
|
136
|
+
results.extend(self.drain_due())
|
|
137
|
+
return tuple(results)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Simple JSON-lines telemetry for self-hosted process logs."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
from threading import Lock
|
|
6
|
+
import traceback
|
|
7
|
+
from typing import TextIO
|
|
8
|
+
|
|
9
|
+
from rememberstack.model import TelemetryEvent
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class JsonLineTelemetry:
|
|
13
|
+
"""Write one complete structured event per line to a process-owned stream."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, *, stream: TextIO | None = None) -> None:
|
|
16
|
+
"""Use stdout by default and serialize concurrent worker writes."""
|
|
17
|
+
self._stream = stream or sys.stdout
|
|
18
|
+
self._lock = Lock()
|
|
19
|
+
|
|
20
|
+
def export_event(self, *, event: TelemetryEvent) -> None:
|
|
21
|
+
"""Write and flush one ordinary event."""
|
|
22
|
+
self._write(payload=event.model_dump(mode="json"))
|
|
23
|
+
|
|
24
|
+
def export_exception(
|
|
25
|
+
self, *, event: TelemetryEvent, exception: BaseException
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Write the event plus the supplied exception's complete cause chain."""
|
|
28
|
+
payload = event.model_dump(mode="json")
|
|
29
|
+
payload["exception"] = {
|
|
30
|
+
"type": type(exception).__qualname__,
|
|
31
|
+
"message": str(exception),
|
|
32
|
+
"traceback": "".join(
|
|
33
|
+
traceback.TracebackException.from_exception(exception).format(
|
|
34
|
+
chain=True
|
|
35
|
+
)
|
|
36
|
+
),
|
|
37
|
+
}
|
|
38
|
+
self._write(payload=payload)
|
|
39
|
+
|
|
40
|
+
def _write(self, *, payload: dict[str, object]) -> None:
|
|
41
|
+
"""Keep every event atomic at the stream boundary."""
|
|
42
|
+
line = json.dumps(payload, sort_keys=True, separators=(",", ":"))
|
|
43
|
+
with self._lock:
|
|
44
|
+
self._stream.write(line + "\n")
|
|
45
|
+
self._stream.flush()
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""The local-directory watched source: watch a folder on the self-host tier.
|
|
2
|
+
|
|
3
|
+
The genuinely useful self-host connector (and the lifecycle machinery's test
|
|
4
|
+
double with real semantics): source_ref = the file's relative path, revision
|
|
5
|
+
= mtime+size, deletion = the file is gone. Cloud connectors (Drive) implement
|
|
6
|
+
the same port behind their provider adapters.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Mapping
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from datetime import UTC
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from rememberstack.model import SourceItem
|
|
15
|
+
|
|
16
|
+
_MIME_BY_SUFFIX = {".md": "text/markdown", ".txt": "text/plain", ".html": "text/html"}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class LocalDirectoryWatcher:
|
|
20
|
+
"""Watch one directory tree of text documents."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, *, root: Path) -> None:
|
|
23
|
+
"""Bind the watcher to its directory (created if absent)."""
|
|
24
|
+
self._root = root
|
|
25
|
+
self._root.mkdir(parents=True, exist_ok=True)
|
|
26
|
+
|
|
27
|
+
def poll(self, *, known: Mapping[str, str]) -> tuple[SourceItem, ...]:
|
|
28
|
+
"""Every current file, plus deletion markers for vanished refs.
|
|
29
|
+
|
|
30
|
+
Paths that resolve outside the root (symlinks pointing elsewhere)
|
|
31
|
+
are skipped: a writable watched directory must not become a lever
|
|
32
|
+
for ingesting arbitrary readable files into memory.
|
|
33
|
+
"""
|
|
34
|
+
base = self._root.resolve()
|
|
35
|
+
items: list[SourceItem] = []
|
|
36
|
+
seen: set[str] = set()
|
|
37
|
+
for path in sorted(self._root.rglob("*")):
|
|
38
|
+
if not path.is_file() or path.suffix not in _MIME_BY_SUFFIX:
|
|
39
|
+
continue
|
|
40
|
+
if not path.resolve().is_relative_to(base):
|
|
41
|
+
continue # a symlink escaping the root is not a watched file
|
|
42
|
+
ref = path.relative_to(self._root).as_posix()
|
|
43
|
+
seen.add(ref)
|
|
44
|
+
stat = path.stat()
|
|
45
|
+
items.append(
|
|
46
|
+
SourceItem(
|
|
47
|
+
source_ref=ref,
|
|
48
|
+
revision=f"{stat.st_mtime_ns}:{stat.st_size}",
|
|
49
|
+
modified_at=datetime.fromtimestamp(stat.st_mtime, tz=UTC),
|
|
50
|
+
filename=path.name,
|
|
51
|
+
mime=_MIME_BY_SUFFIX[path.suffix],
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
for ref in sorted(set(known) - seen):
|
|
55
|
+
items.append(
|
|
56
|
+
SourceItem(
|
|
57
|
+
source_ref=ref,
|
|
58
|
+
revision="deleted",
|
|
59
|
+
modified_at=datetime.now(tz=UTC),
|
|
60
|
+
deleted=True,
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
return tuple(items)
|
|
64
|
+
|
|
65
|
+
def fetch(self, *, source_ref: str) -> bytes:
|
|
66
|
+
"""The file's current bytes; refs escaping the root are refused."""
|
|
67
|
+
path = (self._root / source_ref).resolve()
|
|
68
|
+
if not path.is_relative_to(self._root.resolve()):
|
|
69
|
+
raise PermissionError(f"{source_ref!r} escapes the watched root")
|
|
70
|
+
return path.read_bytes()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Test-tier adapters: in-memory doubles outside the two-maintained-adapter set."""
|
|
2
|
+
|
|
3
|
+
from rememberstack.adapters.testing.cost_meter import NoopCostMeter
|
|
4
|
+
from rememberstack.adapters.testing.model_provider import FakeModelProvider
|
|
5
|
+
from rememberstack.adapters.testing.queue import RecordedAnnouncement
|
|
6
|
+
from rememberstack.adapters.testing.queue import RecordingTaskQueue
|
|
7
|
+
from rememberstack.adapters.testing.telemetry import RecordingTelemetry
|
|
8
|
+
|
|
9
|
+
__all__ = (
|
|
10
|
+
"FakeModelProvider",
|
|
11
|
+
"NoopCostMeter",
|
|
12
|
+
"RecordedAnnouncement",
|
|
13
|
+
"RecordingTaskQueue",
|
|
14
|
+
"RecordingTelemetry",
|
|
15
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""A no-op cost meter for tests that invoke stage handlers directly."""
|
|
2
|
+
|
|
3
|
+
from rememberstack.model import ProviderCallUsage
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class NoopCostMeter:
|
|
7
|
+
"""Accept provider accounting without persisting it."""
|
|
8
|
+
|
|
9
|
+
def record(
|
|
10
|
+
self, *, call_key: str, tier: str | None, usage: ProviderCallUsage
|
|
11
|
+
) -> None:
|
|
12
|
+
"""Discard one test-only call record."""
|
|
13
|
+
del call_key, tier, usage
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""A deterministic in-memory model provider for behavior tests (no network)."""
|
|
2
|
+
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
import hashlib
|
|
5
|
+
|
|
6
|
+
from rememberstack.model import EmbeddingRequest
|
|
7
|
+
from rememberstack.model import EmbeddingResponse
|
|
8
|
+
from rememberstack.model import GeneratedResponse
|
|
9
|
+
from rememberstack.model import ModelRequest
|
|
10
|
+
from rememberstack.model import ProviderCallUsage
|
|
11
|
+
from rememberstack.model import StructuredResponseModel
|
|
12
|
+
|
|
13
|
+
_EMBEDDING_DIMENSION = 8
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class FakeModelProvider:
|
|
17
|
+
"""Deterministic embeddings and canned structured generations for proofs."""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
*,
|
|
22
|
+
generate_payload: dict[str, object] | None = None,
|
|
23
|
+
generate_payloads: dict[str, dict[str, object]] | None = None,
|
|
24
|
+
generate_router: object | None = None,
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Bind canned payloads: one default, one per response-type name, or a
|
|
27
|
+
router callable (prompt, type_name) -> payload for per-call behavior."""
|
|
28
|
+
self._generate_payload = generate_payload
|
|
29
|
+
self._generate_payloads = generate_payloads or {}
|
|
30
|
+
self._generate_router = generate_router
|
|
31
|
+
self.embedded_texts: list[str] = []
|
|
32
|
+
self.generated_prompts: list[str] = []
|
|
33
|
+
|
|
34
|
+
def generate[ResponseT: StructuredResponseModel](
|
|
35
|
+
self, *, request: ModelRequest, response_type: type[ResponseT]
|
|
36
|
+
) -> GeneratedResponse[ResponseT]:
|
|
37
|
+
"""Return the canned payload validated as the caller's declared type."""
|
|
38
|
+
self.generated_prompts.append(request.prompt)
|
|
39
|
+
if callable(self._generate_router):
|
|
40
|
+
output = response_type.model_validate(
|
|
41
|
+
self._generate_router(request.prompt, response_type.__name__)
|
|
42
|
+
)
|
|
43
|
+
else:
|
|
44
|
+
payload = self._generate_payloads.get(
|
|
45
|
+
response_type.__name__, self._generate_payload
|
|
46
|
+
)
|
|
47
|
+
if payload is None:
|
|
48
|
+
raise AssertionError(f"no canned payload for {response_type.__name__}")
|
|
49
|
+
output = response_type.model_validate(payload)
|
|
50
|
+
return GeneratedResponse(
|
|
51
|
+
output=output,
|
|
52
|
+
usage=_fake_usage(
|
|
53
|
+
model_name=request.model, tokens_in=len(request.prompt.split())
|
|
54
|
+
),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def embed(self, *, request: EmbeddingRequest) -> EmbeddingResponse:
|
|
58
|
+
"""Return one deterministic content-derived vector per input text."""
|
|
59
|
+
self.embedded_texts.extend(request.texts)
|
|
60
|
+
return EmbeddingResponse(
|
|
61
|
+
vectors=tuple(_vector_for(text=text) for text in request.texts),
|
|
62
|
+
usage=_fake_usage(
|
|
63
|
+
model_name=request.model,
|
|
64
|
+
tokens_in=sum(len(text.split()) for text in request.texts),
|
|
65
|
+
),
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _fake_usage(*, model_name: str, tokens_in: int) -> ProviderCallUsage:
|
|
70
|
+
"""Return deterministic zero-cost accounting for one in-memory provider call."""
|
|
71
|
+
return ProviderCallUsage(
|
|
72
|
+
model_name=model_name,
|
|
73
|
+
tokens_in=tokens_in,
|
|
74
|
+
tokens_out=0,
|
|
75
|
+
cost_usd=Decimal(0),
|
|
76
|
+
latency_ms=0,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _vector_for(*, text: str) -> tuple[float, ...]:
|
|
81
|
+
"""Derive a stable pseudo-vector from the text content."""
|
|
82
|
+
digest = hashlib.sha256(text.encode("utf-8")).digest()
|
|
83
|
+
return tuple(byte / 255.0 for byte in digest[:_EMBEDDING_DIMENSION])
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""The test-tier task queue: records announcements in memory, delivers nothing."""
|
|
2
|
+
|
|
3
|
+
from uuid import UUID
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
from pydantic import ConfigDict
|
|
7
|
+
|
|
8
|
+
from rememberstack.model import QueueRoute
|
|
9
|
+
from rememberstack.model import UTCDateTime
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RecordedAnnouncement(BaseModel):
|
|
13
|
+
"""One announce call as the port received it, for assertions."""
|
|
14
|
+
|
|
15
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
16
|
+
|
|
17
|
+
processing_id: UUID
|
|
18
|
+
route_snapshot: QueueRoute
|
|
19
|
+
not_before_snapshot: UTCDateTime
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class RecordingTaskQueue:
|
|
23
|
+
"""Satisfies the task-queue port; tests assert on the recorded announcements."""
|
|
24
|
+
|
|
25
|
+
def __init__(self) -> None:
|
|
26
|
+
"""Start with an empty announcement log."""
|
|
27
|
+
self.announcements: list[RecordedAnnouncement] = []
|
|
28
|
+
|
|
29
|
+
def announce(
|
|
30
|
+
self,
|
|
31
|
+
*,
|
|
32
|
+
processing_id: UUID,
|
|
33
|
+
route_snapshot: QueueRoute,
|
|
34
|
+
not_before_snapshot: UTCDateTime,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Record the announcement; delivery is the test's job, if any."""
|
|
37
|
+
self.announcements.append(
|
|
38
|
+
RecordedAnnouncement(
|
|
39
|
+
processing_id=processing_id,
|
|
40
|
+
route_snapshot=route_snapshot,
|
|
41
|
+
not_before_snapshot=not_before_snapshot,
|
|
42
|
+
)
|
|
43
|
+
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""In-memory telemetry exporter preserving exception identity for tests."""
|
|
2
|
+
|
|
3
|
+
from rememberstack.model import TelemetryEvent
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class RecordingTelemetry:
|
|
7
|
+
"""Record structured events and original exception objects in call order."""
|
|
8
|
+
|
|
9
|
+
def __init__(self) -> None:
|
|
10
|
+
"""Start with no exports."""
|
|
11
|
+
self.events: list[TelemetryEvent] = []
|
|
12
|
+
self.exceptions: list[tuple[TelemetryEvent, BaseException]] = []
|
|
13
|
+
|
|
14
|
+
def export_event(self, *, event: TelemetryEvent) -> None:
|
|
15
|
+
"""Record a non-exception event."""
|
|
16
|
+
self.events.append(event)
|
|
17
|
+
|
|
18
|
+
def export_exception(
|
|
19
|
+
self, *, event: TelemetryEvent, exception: BaseException
|
|
20
|
+
) -> None:
|
|
21
|
+
"""Record the exact exception instance supplied by the worker."""
|
|
22
|
+
self.exceptions.append((event, exception))
|
rememberstack/client.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Public dependency-light client API."""
|
|
2
|
+
|
|
3
|
+
from rememberstack.model.client import ConnectorCreate
|
|
4
|
+
from rememberstack.model.client import ConnectorDescriptor
|
|
5
|
+
from rememberstack.model.client import ConnectorNotFoundError
|
|
6
|
+
from rememberstack.model.client import ToolDescriptor
|
|
7
|
+
from rememberstack.surfaces.sdk import ClientSettings
|
|
8
|
+
from rememberstack.surfaces.sdk import MemoryApiError
|
|
9
|
+
from rememberstack.surfaces.sdk import MemoryClient
|
|
10
|
+
|
|
11
|
+
__all__ = (
|
|
12
|
+
"ClientSettings",
|
|
13
|
+
"ConnectorCreate",
|
|
14
|
+
"ConnectorDescriptor",
|
|
15
|
+
"ConnectorNotFoundError",
|
|
16
|
+
"MemoryApiError",
|
|
17
|
+
"MemoryClient",
|
|
18
|
+
"ToolDescriptor",
|
|
19
|
+
)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Pure domain logic package."""
|
|
2
|
+
|
|
3
|
+
from rememberstack.core.blockizer import block_hash
|
|
4
|
+
from rememberstack.core.blockizer import blockize
|
|
5
|
+
from rememberstack.core.blockizer import BLOCKIZER_VERSION
|
|
6
|
+
from rememberstack.core.blockizer import normalized_block_text
|
|
7
|
+
from rememberstack.core.chunker import chunk_content_hash
|
|
8
|
+
from rememberstack.core.chunker import CHUNKER_VERSION
|
|
9
|
+
from rememberstack.core.chunker import chunker_version
|
|
10
|
+
from rememberstack.core.chunker import ChunkerParams
|
|
11
|
+
from rememberstack.core.chunker import count_tokens
|
|
12
|
+
from rememberstack.core.chunker import extraction_input_hash
|
|
13
|
+
from rememberstack.core.chunker import is_anchor
|
|
14
|
+
from rememberstack.core.chunker import pack_blocks
|
|
15
|
+
from rememberstack.core.consumption_skill import CONSUMPTION_SKILL_VERSION
|
|
16
|
+
from rememberstack.core.consumption_skill import render_consumption_skill
|
|
17
|
+
from rememberstack.core.conversion import ConversionRouter
|
|
18
|
+
from rememberstack.core.conversion import Converter
|
|
19
|
+
from rememberstack.core.conversion import MarkdownPassthroughConverter
|
|
20
|
+
from rememberstack.core.conversion import PASSTHROUGH_CONVERTER_VERSION
|
|
21
|
+
from rememberstack.core.core_manifest import CORE_MANIFEST
|
|
22
|
+
from rememberstack.core.core_manifest import CoreManifest
|
|
23
|
+
from rememberstack.core.core_manifest import EntityTypeDefinition
|
|
24
|
+
from rememberstack.core.core_manifest import PredicateDefinition
|
|
25
|
+
from rememberstack.core.core_manifest import PredicateSignatureDefinition
|
|
26
|
+
from rememberstack.core.extension_packs import ExtensionPack
|
|
27
|
+
from rememberstack.core.extension_packs import PackEntityType
|
|
28
|
+
from rememberstack.core.extension_packs import PackPredicate
|
|
29
|
+
from rememberstack.core.extension_packs import WORK_PACK
|
|
30
|
+
from rememberstack.core.forget import source_identity_hash
|
|
31
|
+
from rememberstack.core.knowledge_authored import authored_declaration_is_empty
|
|
32
|
+
from rememberstack.core.knowledge_authored import knowledge_citation_reference
|
|
33
|
+
from rememberstack.core.knowledge_authored import KnowledgeAuthoredDeclarationError
|
|
34
|
+
from rememberstack.core.knowledge_authored import parse_knowledge_authored_frontmatter
|
|
35
|
+
from rememberstack.core.knowledge_compile import knowledge_compile_order
|
|
36
|
+
from rememberstack.core.knowledge_compile import KnowledgeCompileGraphError
|
|
37
|
+
from rememberstack.core.knowledge_compile import KnowledgePageValidationError
|
|
38
|
+
from rememberstack.core.knowledge_compile import validate_knowledge_page_output
|
|
39
|
+
from rememberstack.core.knowledge_fact_sheet import compose_knowledge_page
|
|
40
|
+
from rememberstack.core.knowledge_fact_sheet import KnowledgeFactLifecycle
|
|
41
|
+
from rememberstack.core.knowledge_fact_sheet import render_knowledge_fact_sheet
|
|
42
|
+
from rememberstack.core.knowledge_hashing import knowledge_content_hash
|
|
43
|
+
from rememberstack.core.knowledge_hashing import knowledge_inputs_hash
|
|
44
|
+
from rememberstack.core.knowledge_hashing import knowledge_summary_hash
|
|
45
|
+
from rememberstack.core.knowledge_planner import knowledge_planning_input_hash
|
|
46
|
+
from rememberstack.core.knowledge_planner import primary_knowledge_plan_trigger
|
|
47
|
+
from rememberstack.core.knowledge_planner import route_knowledge_plan
|
|
48
|
+
from rememberstack.core.knowledge_writer import cap_knowledge_writer_bundle
|
|
49
|
+
from rememberstack.core.knowledge_writer import knowledge_writer_coverage
|
|
50
|
+
from rememberstack.core.knowledge_writer import render_knowledge_writer_bundle
|
|
51
|
+
from rememberstack.core.ranking import DEFAULT_EVIDENCE_COUNT_WEIGHT
|
|
52
|
+
from rememberstack.core.ranking import DEFAULT_GRAPH_DISTANCE_WEIGHT
|
|
53
|
+
from rememberstack.core.ranking import DEFAULT_RRF_K
|
|
54
|
+
from rememberstack.core.ranking import reciprocal_rank_fusion
|
|
55
|
+
from rememberstack.core.ranking import rerank_by_signal
|
|
56
|
+
from rememberstack.core.ranking import rerank_by_weighted_signals
|
|
57
|
+
from rememberstack.core.recipe_linter import KNOWN_OPS
|
|
58
|
+
from rememberstack.core.recipe_linter import lint_recipe
|
|
59
|
+
from rememberstack.core.recipe_linter import RecipeLintError
|
|
60
|
+
from rememberstack.core.section_snap import SECTION_ROLES
|
|
61
|
+
from rememberstack.core.section_snap import snap_sections
|
|
62
|
+
from rememberstack.core.storage_routing import HOT_MIME_PREFIXES
|
|
63
|
+
from rememberstack.core.storage_routing import storage_class_for
|
|
64
|
+
|
|
65
|
+
__all__ = (
|
|
66
|
+
"BLOCKIZER_VERSION",
|
|
67
|
+
"CHUNKER_VERSION",
|
|
68
|
+
"ChunkerParams",
|
|
69
|
+
"chunk_content_hash",
|
|
70
|
+
"chunker_version",
|
|
71
|
+
"count_tokens",
|
|
72
|
+
"extraction_input_hash",
|
|
73
|
+
"is_anchor",
|
|
74
|
+
"pack_blocks",
|
|
75
|
+
"CORE_MANIFEST",
|
|
76
|
+
"ExtensionPack",
|
|
77
|
+
"PackEntityType",
|
|
78
|
+
"PackPredicate",
|
|
79
|
+
"WORK_PACK",
|
|
80
|
+
"ConversionRouter",
|
|
81
|
+
"CONSUMPTION_SKILL_VERSION",
|
|
82
|
+
"Converter",
|
|
83
|
+
"CoreManifest",
|
|
84
|
+
"EntityTypeDefinition",
|
|
85
|
+
"MarkdownPassthroughConverter",
|
|
86
|
+
"PASSTHROUGH_CONVERTER_VERSION",
|
|
87
|
+
"PredicateDefinition",
|
|
88
|
+
"PredicateSignatureDefinition",
|
|
89
|
+
"block_hash",
|
|
90
|
+
"blockize",
|
|
91
|
+
"normalized_block_text",
|
|
92
|
+
"HOT_MIME_PREFIXES",
|
|
93
|
+
"storage_class_for",
|
|
94
|
+
"source_identity_hash",
|
|
95
|
+
"SECTION_ROLES",
|
|
96
|
+
"snap_sections",
|
|
97
|
+
"DEFAULT_EVIDENCE_COUNT_WEIGHT",
|
|
98
|
+
"DEFAULT_GRAPH_DISTANCE_WEIGHT",
|
|
99
|
+
"DEFAULT_RRF_K",
|
|
100
|
+
"KNOWN_OPS",
|
|
101
|
+
"KnowledgeCompileGraphError",
|
|
102
|
+
"KnowledgeAuthoredDeclarationError",
|
|
103
|
+
"KnowledgeFactLifecycle",
|
|
104
|
+
"KnowledgePageValidationError",
|
|
105
|
+
"knowledge_content_hash",
|
|
106
|
+
"knowledge_citation_reference",
|
|
107
|
+
"knowledge_compile_order",
|
|
108
|
+
"knowledge_inputs_hash",
|
|
109
|
+
"knowledge_planning_input_hash",
|
|
110
|
+
"knowledge_summary_hash",
|
|
111
|
+
"primary_knowledge_plan_trigger",
|
|
112
|
+
"parse_knowledge_authored_frontmatter",
|
|
113
|
+
"compose_knowledge_page",
|
|
114
|
+
"RecipeLintError",
|
|
115
|
+
"lint_recipe",
|
|
116
|
+
"reciprocal_rank_fusion",
|
|
117
|
+
"render_consumption_skill",
|
|
118
|
+
"render_knowledge_fact_sheet",
|
|
119
|
+
"cap_knowledge_writer_bundle",
|
|
120
|
+
"knowledge_writer_coverage",
|
|
121
|
+
"render_knowledge_writer_bundle",
|
|
122
|
+
"route_knowledge_plan",
|
|
123
|
+
"rerank_by_signal",
|
|
124
|
+
"rerank_by_weighted_signals",
|
|
125
|
+
"validate_knowledge_page_output",
|
|
126
|
+
"authored_declaration_is_empty",
|
|
127
|
+
)
|