claude-smart 0.2.48 → 0.2.50
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/.claude-plugin/marketplace.json +2 -2
- package/README.md +11 -40
- package/bin/claude-smart.js +393 -55
- package/package.json +3 -2
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.coverage +0 -0
- package/plugin/README.md +4 -3
- package/plugin/dashboard/app/api/reflexio/[...path]/route.ts +4 -2
- package/plugin/dashboard/app/dashboard/page.tsx +6 -1
- package/plugin/dashboard/app/preferences/[id]/page.tsx +18 -6
- package/plugin/dashboard/app/preferences/page.tsx +32 -35
- package/plugin/dashboard/app/sessions/[sessionId]/page.tsx +16 -1
- package/plugin/dashboard/app/sessions/page.tsx +2 -0
- package/plugin/dashboard/app/skills/page.tsx +65 -50
- package/plugin/dashboard/app/skills/project/[id]/page.tsx +17 -8
- package/plugin/dashboard/app/skills/shared/[id]/page.tsx +1 -6
- package/plugin/dashboard/components/common/host-badge.tsx +118 -0
- package/plugin/dashboard/components/common/learning-application-badge.tsx +34 -0
- package/plugin/dashboard/components/common/learnings-badge.tsx +1 -1
- package/plugin/dashboard/components/common/page-header.tsx +3 -3
- package/plugin/dashboard/lib/config-file.ts +5 -1
- package/plugin/dashboard/lib/host-attribution.ts +62 -0
- package/plugin/dashboard/lib/session-reader.ts +40 -2
- package/plugin/dashboard/lib/types.ts +7 -1
- package/plugin/opencode/dist/server.mjs +60 -2
- package/plugin/opencode/server.mts +64 -2
- package/plugin/pyproject.toml +2 -2
- package/plugin/scripts/_lib.sh +255 -7
- package/plugin/scripts/backend-python-runner.py +46 -0
- package/plugin/scripts/backend-service.sh +766 -123
- package/plugin/scripts/codex-hook.js +79 -229
- package/plugin/scripts/dashboard-open.sh +6 -4
- package/plugin/scripts/dashboard-service.sh +118 -137
- package/plugin/scripts/ensure-plugin-root.sh +106 -8
- package/plugin/scripts/hook_entry.sh +3 -0
- package/plugin/scripts/opencode-claude-compat.js +8 -1
- package/plugin/scripts/smart-install.sh +116 -21
- package/plugin/src/README.md +1 -1
- package/plugin/src/claude_smart/cli.py +93 -29
- package/plugin/src/claude_smart/context_inject.py +3 -0
- package/plugin/src/claude_smart/env_config.py +56 -11
- package/plugin/src/claude_smart/events/post_tool.py +2 -1
- package/plugin/src/claude_smart/events/session_end.py +2 -1
- package/plugin/src/claude_smart/events/stop.py +3 -0
- package/plugin/src/claude_smart/events/user_prompt.py +2 -1
- package/plugin/src/claude_smart/internal_call.py +5 -2
- package/plugin/src/claude_smart/optimizer_assistant.py +59 -13
- package/plugin/src/claude_smart/publish.py +59 -7
- package/plugin/src/claude_smart/reflexio_adapter.py +137 -14
- package/plugin/src/claude_smart/runtime.py +15 -6
- package/plugin/src/claude_smart/state.py +211 -52
- package/plugin/uv.lock +5 -5
- package/plugin/vendor/reflexio/.env.example +13 -0
- package/plugin/vendor/reflexio/reflexio/README.md +7 -3
- package/plugin/vendor/reflexio/reflexio/__init__.py +12 -0
- package/plugin/vendor/reflexio/reflexio/cli/README.md +3 -0
- package/plugin/vendor/reflexio/reflexio/client/client.py +166 -9
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/state.py +10 -3
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_state.py +28 -0
- package/plugin/vendor/reflexio/reflexio/lib/_search.py +41 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +195 -25
- package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +7 -1
- package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +2 -1
- package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +63 -4
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +1 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +8 -1
- package/plugin/vendor/reflexio/reflexio/models/config_schema.py +1 -1
- package/plugin/vendor/reflexio/reflexio/server/README.md +22 -4
- package/plugin/vendor/reflexio/reflexio/server/__init__.py +18 -8
- package/plugin/vendor/reflexio/reflexio/server/__main__.py +6 -0
- package/plugin/vendor/reflexio/reflexio/server/api.py +79 -5
- package/plugin/vendor/reflexio/reflexio/server/billing_meter.py +263 -3
- package/plugin/vendor/reflexio/reflexio/server/callback_executor.py +164 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_embedding.py +81 -81
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_subprocess.py +28 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_text_generation.py +63 -6
- package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +19 -52
- package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +28 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +9 -1
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedder_warmup.py +329 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +85 -10
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/local_embedding_provider.py +199 -2
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/nomic_embedding_provider.py +77 -9
- package/plugin/vendor/reflexio/reflexio/server/org_fanout.py +184 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.0.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.1.0.prompt.md +32 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.3.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.4.0.prompt.md +63 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v1.0.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v2.0.0.prompt.md +30 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/retrieved_learning_impact/v1.0.0.prompt.md +51 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/retrieved_learning_relevance/v1.0.0.prompt.md +39 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.0.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.1.0.prompt.md +43 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/config.py +3 -3
- package/plugin/vendor/reflexio/reflexio/server/routes/evaluation.py +122 -28
- package/plugin/vendor/reflexio/reflexio/server/routes/interactions.py +63 -2
- package/plugin/vendor/reflexio/reflexio/server/routes/system.py +22 -3
- package/plugin/vendor/reflexio/reflexio/server/scheduling.py +64 -3
- package/plugin/vendor/reflexio/reflexio/server/services/README.md +6 -4
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/README.md +3 -2
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/_eval_health.py +41 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/components/retrieved_learning_evaluator.py +554 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +35 -1
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/runner.py +253 -101
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/scheduler.py +5 -7
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/service.py +27 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_extraction_lifecycle.py +11 -5
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_should_run.py +4 -4
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_usage_billing.py +6 -2
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +255 -74
- package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +72 -0
- package/plugin/vendor/reflexio/reflexio/server/services/deferred_learning_plan.py +270 -0
- package/plugin/vendor/reflexio/reflexio/server/services/durable_learning/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/durable_learning/scheduler.py +142 -0
- package/plugin/vendor/reflexio/reflexio/server/services/durable_learning/worker.py +262 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/hero_state.py +2 -11
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/rule_attribution.py +6 -2
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +17 -13
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/agent_run_records.py +18 -3
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +12 -1
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/prior_answer_search.py +1 -1
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +2 -1
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +1 -3
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +66 -0
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +640 -80
- package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +2 -0
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +179 -99
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/vector_backfill_sweep.py +139 -0
- package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +66 -27
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_trigger.py +177 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator.py +68 -2
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/consolidator.py +360 -49
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/extractor.py +27 -30
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_edit_apply.py +8 -1
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/service.py +137 -69
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +20 -1
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scheduler.py +13 -6
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_query_reformulator.py +40 -25
- package/plugin/vendor/reflexio/reflexio/server/services/profile/components/consolidator.py +12 -39
- package/plugin/vendor/reflexio/reflexio/server/services/profile/components/extractor.py +30 -35
- package/plugin/vendor/reflexio/reflexio/server/services/profile/service.py +166 -66
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/service.py +457 -107
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/session_dedup.py +127 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/temporal.py +104 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/dispatcher.py +139 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/judge.py +16 -6
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/worker.py +137 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +12 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/lifecycle_filters.py +54 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +41 -3
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention_mixin.py +40 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +2 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +190 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +11 -4
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +28 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_learning_jobs.py +414 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +11 -5
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +8 -3
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +4 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_deletion.py +16 -5
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_fts_vec.py +86 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_erase_execution.py +104 -42
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_subject_barrier.py +7 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +59 -7
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +430 -5
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +8 -3
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +57 -12
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_interaction_store.py +197 -12
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_profile_store.py +70 -11
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +17 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_commit_scope.py +9 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +9 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_learning_jobs.py +269 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +7 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_retrieval_log.py +3 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +4 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_models.py +12 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/evaluation_state_keys.py +78 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +38 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +171 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +52 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_interaction_store.py +82 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_profile_store.py +74 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/retrieved_learning_state.py +226 -0
- package/plugin/vendor/reflexio/reflexio/server/services/tagging/tagging_scheduler.py +5 -6
- package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +209 -29
- package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +3 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +61 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +33 -0
- package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_learning_jobs.py
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"""ABC, dataclass, and enum for the durable learning-job queue (Task 3)."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from enum import StrEnum
|
|
6
|
+
from typing import Literal
|
|
7
|
+
|
|
8
|
+
# Upper bound of the done-row retention window.
|
|
9
|
+
# Shared by all backends so the value cannot drift between implementations.
|
|
10
|
+
# Err toward "not done" until we are sure a done row would have been GC'd.
|
|
11
|
+
_ABSENCE_DONE_AFTER_SECONDS = 72 * 3600
|
|
12
|
+
|
|
13
|
+
# Coverage-based status reported for a single request (§3.6). This is a
|
|
14
|
+
# collapsed view of the raw job statuses (e.g. claimed -> processing,
|
|
15
|
+
# reclaimable failed -> pending) and matches LearningStatusResponse.status.
|
|
16
|
+
LearningStatus = Literal["pending", "processing", "done", "failed"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class LearningJobStatus(StrEnum):
|
|
20
|
+
PENDING = "pending"
|
|
21
|
+
CLAIMED = "claimed"
|
|
22
|
+
DONE = "done"
|
|
23
|
+
FAILED = "failed"
|
|
24
|
+
DEAD = "dead"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True)
|
|
28
|
+
class LearningJob:
|
|
29
|
+
job_id: str
|
|
30
|
+
org_id: str
|
|
31
|
+
user_id: str
|
|
32
|
+
job_type: str
|
|
33
|
+
latest_request_id: str | None
|
|
34
|
+
status: str
|
|
35
|
+
attempts: int
|
|
36
|
+
claim_token: str | None
|
|
37
|
+
covers_through: (
|
|
38
|
+
float | None
|
|
39
|
+
) # epoch seconds (converted from stored ISO/timestamptz)
|
|
40
|
+
force_extraction: bool = False
|
|
41
|
+
skip_aggregation: bool = False
|
|
42
|
+
max_attempts: int = 3
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class LearningJobStoreABC(ABC):
|
|
46
|
+
"""Abstract interface for durable learning-job queue operations.
|
|
47
|
+
|
|
48
|
+
All methods are scoped to the storage instance's own org (``self.org_id``).
|
|
49
|
+
``enqueue_learning_job`` and ``complete_learning_job`` are safe to call
|
|
50
|
+
inside a ``commit_scope`` — they issue plain SQL on the scope's connection
|
|
51
|
+
without owning a separate BEGIN/COMMIT.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def enqueue_learning_job(
|
|
56
|
+
self,
|
|
57
|
+
*,
|
|
58
|
+
org_id: str,
|
|
59
|
+
user_id: str,
|
|
60
|
+
request_id: str,
|
|
61
|
+
covers_through: float,
|
|
62
|
+
job_type: str = "learning",
|
|
63
|
+
force_extraction: bool = False,
|
|
64
|
+
skip_aggregation: bool = False,
|
|
65
|
+
) -> str:
|
|
66
|
+
"""Coalescing upsert into the learning_jobs queue.
|
|
67
|
+
|
|
68
|
+
If a pending job for ``(org_id, user_id, job_type)`` already exists,
|
|
69
|
+
update its ``latest_request_id`` and keep the max ``covers_through``;
|
|
70
|
+
otherwise insert a new ``pending`` row. Returns the ``job_id`` of the
|
|
71
|
+
pending row (existing or newly inserted).
|
|
72
|
+
|
|
73
|
+
Safe to call inside a ``commit_scope`` — no own BEGIN/COMMIT issued.
|
|
74
|
+
"""
|
|
75
|
+
raise NotImplementedError
|
|
76
|
+
|
|
77
|
+
@abstractmethod
|
|
78
|
+
def claim_learning_jobs(
|
|
79
|
+
self,
|
|
80
|
+
*,
|
|
81
|
+
claimed_by: str,
|
|
82
|
+
limit: int,
|
|
83
|
+
lease_seconds: int,
|
|
84
|
+
) -> list[LearningJob]:
|
|
85
|
+
"""Atomically claim up to ``limit`` of this org's claimable jobs.
|
|
86
|
+
|
|
87
|
+
A job is claimable when it is ``pending``, ``failed`` (reclaimable —
|
|
88
|
+
attempts < max_attempts), OR (``claimed`` AND
|
|
89
|
+
``claim_expires_at < now``). Each claimed job receives a fresh
|
|
90
|
+
``claim_token``, ``claimed_by``, and ``claim_expires_at``.
|
|
91
|
+
|
|
92
|
+
Postgres/Supabase: uses ``FOR UPDATE SKIP LOCKED`` via the
|
|
93
|
+
``claim_learning_jobs`` SQL function. SQLite: ``BEGIN IMMEDIATE``.
|
|
94
|
+
The claim predicate uses the DB's ``now()`` to avoid app/DB clock skew.
|
|
95
|
+
"""
|
|
96
|
+
raise NotImplementedError
|
|
97
|
+
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def heartbeat_learning_job(
|
|
100
|
+
self,
|
|
101
|
+
*,
|
|
102
|
+
job_id: str,
|
|
103
|
+
claim_token: str,
|
|
104
|
+
lease_seconds: int,
|
|
105
|
+
) -> bool:
|
|
106
|
+
"""Extend the lease on a claimed job.
|
|
107
|
+
|
|
108
|
+
Updates ``claim_expires_at = now + lease_seconds`` only when
|
|
109
|
+
``claim_token`` matches and status is still ``claimed``.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
True if the lease was extended (rowcount == 1), False if the token
|
|
113
|
+
was superseded or the job is no longer claimed.
|
|
114
|
+
"""
|
|
115
|
+
raise NotImplementedError
|
|
116
|
+
|
|
117
|
+
@abstractmethod
|
|
118
|
+
def complete_learning_job(
|
|
119
|
+
self,
|
|
120
|
+
*,
|
|
121
|
+
job_id: str,
|
|
122
|
+
claim_token: str,
|
|
123
|
+
) -> int:
|
|
124
|
+
"""Fenced transition to ``done``.
|
|
125
|
+
|
|
126
|
+
Executes::
|
|
127
|
+
|
|
128
|
+
UPDATE learning_jobs
|
|
129
|
+
SET status='done', updated_at=now()
|
|
130
|
+
WHERE job_id=:job_id AND claim_token=:claim_token AND status='claimed'
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
rowcount — 0 if the token was superseded or the job is already
|
|
134
|
+
done/dead/failed; 1 on success. Callers MUST raise on 0 to roll
|
|
135
|
+
back the enclosing ``commit_scope``.
|
|
136
|
+
|
|
137
|
+
Safe to call inside a ``commit_scope`` — no own BEGIN/COMMIT issued.
|
|
138
|
+
"""
|
|
139
|
+
raise NotImplementedError
|
|
140
|
+
|
|
141
|
+
@abstractmethod
|
|
142
|
+
def fail_learning_job(
|
|
143
|
+
self,
|
|
144
|
+
*,
|
|
145
|
+
job_id: str,
|
|
146
|
+
claim_token: str,
|
|
147
|
+
dead: bool,
|
|
148
|
+
refund_attempt: bool = False,
|
|
149
|
+
) -> None:
|
|
150
|
+
"""Fenced fail/dead transition — sets status, does NOT increment attempts.
|
|
151
|
+
|
|
152
|
+
Fenced on ``claim_token`` and ``status='claimed'``. Sets
|
|
153
|
+
``status='dead'`` when ``dead=True``, else ``status='failed'`` (and
|
|
154
|
+
clears ``claim_token``/``claim_expires_at`` so the job is reclaimable).
|
|
155
|
+
|
|
156
|
+
``attempts`` is intentionally NOT incremented here. It is incremented
|
|
157
|
+
once per delivery attempt exclusively by ``claim_learning_jobs``; the
|
|
158
|
+
worker's dead-gate (``job.attempts >= max_attempts``) depends on that
|
|
159
|
+
single-increment-per-claim invariant. Incrementing here would
|
|
160
|
+
double-count and misfire the dead transition.
|
|
161
|
+
|
|
162
|
+
``refund_attempt`` (default ``False``): when ``True``, decrement
|
|
163
|
+
``attempts`` by one (floored at 0) as part of this transition. This is
|
|
164
|
+
the same-user-contention requeue path (F4): the worker claimed the job
|
|
165
|
+
(``claim_learning_jobs`` did ``attempts += 1``) but could not run because
|
|
166
|
+
another same-user job holds the per-user lock, so it releases the job
|
|
167
|
+
WITHOUT having done any real work. Refunding the claim's increment makes
|
|
168
|
+
a contention cycle (claim +1, contention-release -1) net to zero, so a
|
|
169
|
+
job that loses the same-user race repeatedly (every ~2s poll while the
|
|
170
|
+
holder runs its ~60s compute) does NOT inflate ``attempts`` past
|
|
171
|
+
``max_attempts`` and dead-letter with zero real retries. It is a no-op
|
|
172
|
+
unless ``refund_attempt=True``, so the ``dead`` retry path is unchanged.
|
|
173
|
+
|
|
174
|
+
# TASK 10: the enterprise Supabase (``reflexio_ext/server/services/
|
|
175
|
+
# storage/supabase_storage/_learning_jobs.py``) and native-Postgres
|
|
176
|
+
# ``fail_learning_job`` impls (and their ``claim_learning_jobs`` SQL
|
|
177
|
+
# functions) need this same ``refund_attempt`` decrement. The param is
|
|
178
|
+
# optional with a default so this base signature stays back-compatible
|
|
179
|
+
# until they are updated.
|
|
180
|
+
"""
|
|
181
|
+
raise NotImplementedError
|
|
182
|
+
|
|
183
|
+
@abstractmethod
|
|
184
|
+
def list_org_ids_with_pending_learning_jobs(self) -> list[str]:
|
|
185
|
+
"""Return distinct org_ids that have actionable learning jobs.
|
|
186
|
+
|
|
187
|
+
Cross-org discovery query for the :class:`DurableLearningScheduler`:
|
|
188
|
+
surfaces every org with at least one job that is ``pending``, ``failed``
|
|
189
|
+
(reclaimable), or ``claimed`` with an expired lease (a stolen/abandoned
|
|
190
|
+
claim). Terminal ``done``/``dead`` rows and live claims are excluded.
|
|
191
|
+
|
|
192
|
+
Mirrors ``list_resumable_work_org_ids``: NOT scoped to ``self.org_id`` —
|
|
193
|
+
on the shared global table (Postgres ``public.learning_jobs``, or a
|
|
194
|
+
SQLite DB shared across orgs) it returns every org with work in one
|
|
195
|
+
query so the scheduler need not enumerate all orgs.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
list[str]: Distinct org_ids with actionable work, ordered ascending.
|
|
199
|
+
"""
|
|
200
|
+
raise NotImplementedError
|
|
201
|
+
|
|
202
|
+
@abstractmethod
|
|
203
|
+
def get_oldest_pending_learning_job_age_seconds(self) -> float | None:
|
|
204
|
+
"""Age in seconds of the oldest ``status='pending'`` job on this storage ref.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
The age in seconds (``now - created_at``) of the oldest pending job,
|
|
208
|
+
or ``None`` if there are no pending jobs.
|
|
209
|
+
|
|
210
|
+
Resolves the table via the same placement resolver used by
|
|
211
|
+
``list_org_ids_with_pending_learning_jobs`` — never hardcodes
|
|
212
|
+
``public.`` or ``reflexio_ops.``.
|
|
213
|
+
"""
|
|
214
|
+
raise NotImplementedError
|
|
215
|
+
|
|
216
|
+
@abstractmethod
|
|
217
|
+
def count_learning_jobs_by_status(self, status: str) -> int:
|
|
218
|
+
"""Count of learning jobs with the given ``status`` on this storage ref.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
status: One of ``'pending'``, ``'claimed'``, ``'done'``,
|
|
222
|
+
``'failed'``, or ``'dead'``.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
The integer count of matching rows (0 if none).
|
|
226
|
+
|
|
227
|
+
Resolves the table via the same placement resolver used by the other
|
|
228
|
+
query methods — never hardcodes schema prefix.
|
|
229
|
+
"""
|
|
230
|
+
raise NotImplementedError
|
|
231
|
+
|
|
232
|
+
@abstractmethod
|
|
233
|
+
def get_learning_status_for_request(
|
|
234
|
+
self,
|
|
235
|
+
*,
|
|
236
|
+
user_id: str,
|
|
237
|
+
request_created_at: float,
|
|
238
|
+
) -> LearningStatus:
|
|
239
|
+
"""Coverage-based status for a single request (§3.6 rule).
|
|
240
|
+
|
|
241
|
+
Returns one of ``"done" | "processing" | "pending" | "failed"``.
|
|
242
|
+
|
|
243
|
+
Coverage rule (evaluated in priority order):
|
|
244
|
+
|
|
245
|
+
1. Any ``done`` job with ``covers_through >= request_created_at``
|
|
246
|
+
→ ``"done"`` (correct even for zero-yield windows).
|
|
247
|
+
2. Any ``claimed`` job (any ``covers_through``)
|
|
248
|
+
→ ``"processing"``.
|
|
249
|
+
3. Any ``pending`` job for this user
|
|
250
|
+
→ ``"pending"``.
|
|
251
|
+
4. Any ``failed`` (reclaimable) job
|
|
252
|
+
→ ``"pending"``.
|
|
253
|
+
5. Any ``dead`` job with ``covers_through >= request_created_at``
|
|
254
|
+
→ ``"failed"``.
|
|
255
|
+
6. No rows AND ``now - request_created_at >= 72 h`` (retention window)
|
|
256
|
+
→ ``"done"`` (done row would have been GC'd by now).
|
|
257
|
+
7. No rows AND request is recent
|
|
258
|
+
→ ``"pending"`` (err toward not-done until retention window passes).
|
|
259
|
+
"""
|
|
260
|
+
raise NotImplementedError
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
__all__ = [
|
|
264
|
+
"_ABSENCE_DONE_AFTER_SECONDS",
|
|
265
|
+
"LearningJob",
|
|
266
|
+
"LearningJobStatus",
|
|
267
|
+
"LearningStatus",
|
|
268
|
+
"LearningJobStoreABC",
|
|
269
|
+
]
|
|
@@ -146,6 +146,10 @@ class OperationMixin:
|
|
|
146
146
|
the current holder) are dropped to keep the queue idempotent under
|
|
147
147
|
publish retries.
|
|
148
148
|
|
|
149
|
+
``request_id`` is the intentional storage-boundary name here. Higher
|
|
150
|
+
layers may call the same value ``lock_request_id``, but persisted lock
|
|
151
|
+
rows and queued entries still use the legacy ``request_id`` key.
|
|
152
|
+
|
|
149
153
|
The queue is a FIFO drained one entry at a time when the holder
|
|
150
154
|
releases the lock. It replaces the older single-slot
|
|
151
155
|
``pending_request_id`` field, which silently dropped earlier blocked
|
|
@@ -178,6 +182,9 @@ class OperationMixin:
|
|
|
178
182
|
) -> bool:
|
|
179
183
|
"""Atomically clear an in-progress lock only if ``request_id`` still owns it.
|
|
180
184
|
|
|
185
|
+
The storage boundary keeps the legacy ``request_id`` naming for the
|
|
186
|
+
persisted lock holder even when callers use clearer local names.
|
|
187
|
+
|
|
181
188
|
Args:
|
|
182
189
|
state_key: Operation-state row key for the lock.
|
|
183
190
|
request_id: Request attempting to release the lock.
|
package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_retrieval_log.py
CHANGED
|
@@ -7,7 +7,9 @@ class RetrievalLogMixin:
|
|
|
7
7
|
These methods are optional retrieval-capture storage hooks. They are
|
|
8
8
|
intentionally concrete (not ``@abstractmethod``) so concrete storage classes
|
|
9
9
|
remain instantiable; each raises ``NotImplementedError`` until a backend
|
|
10
|
-
provides a real implementation.
|
|
10
|
+
provides a real implementation. Stored item rows may use either the legacy
|
|
11
|
+
``agent_playbook_id`` field or the explicit ``target_kind`` / ``target_id``
|
|
12
|
+
pair; readers should preserve backward compatibility for both.
|
|
11
13
|
"""
|
|
12
14
|
|
|
13
15
|
def save_playbook_retrieval_log(self, log: PlaybookRetrievalLog) -> int:
|
package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py
CHANGED
|
@@ -34,6 +34,10 @@ class ShadowVerdictsMixin:
|
|
|
34
34
|
clear error message naming the backend rather than ``AttributeError``.
|
|
35
35
|
"""
|
|
36
36
|
|
|
37
|
+
def supports_shadow_comparison_verdicts(self) -> bool:
|
|
38
|
+
"""Return whether this backend can persist shadow comparison verdicts."""
|
|
39
|
+
return False
|
|
40
|
+
|
|
37
41
|
def save_shadow_comparison_verdict(
|
|
38
42
|
self, verdict: ShadowComparisonVerdict
|
|
39
43
|
) -> ShadowComparisonVerdict:
|
package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_models.py
CHANGED
|
@@ -44,7 +44,12 @@ class RunToolDependencyKind(StrEnum):
|
|
|
44
44
|
|
|
45
45
|
@dataclass(frozen=True)
|
|
46
46
|
class AgentBinding:
|
|
47
|
-
"""Logical run binding flattened into `_agent_runs` storage columns.
|
|
47
|
+
"""Logical run binding flattened into `_agent_runs` storage columns.
|
|
48
|
+
|
|
49
|
+
``request_id`` remains the persisted provenance field name at this storage
|
|
50
|
+
boundary even when higher-level helpers refer to the same value as a
|
|
51
|
+
generation request id.
|
|
52
|
+
"""
|
|
48
53
|
|
|
49
54
|
org_id: str
|
|
50
55
|
extractor_kind: str
|
|
@@ -60,6 +65,12 @@ class AgentBinding:
|
|
|
60
65
|
|
|
61
66
|
@dataclass(frozen=True)
|
|
62
67
|
class AgentRunRecord:
|
|
68
|
+
"""Durable agent run row plus snapshots used for resume/finalization.
|
|
69
|
+
|
|
70
|
+
``generation_request_snapshot`` intentionally keeps a legacy ``request_id``
|
|
71
|
+
key for stored provenance.
|
|
72
|
+
"""
|
|
73
|
+
|
|
63
74
|
id: str
|
|
64
75
|
binding: AgentBinding
|
|
65
76
|
status: AgentRunStatus
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Single source of truth for evaluation ``_operation_state`` key formats.
|
|
2
|
+
|
|
3
|
+
Three evaluation namespaces store per-session state in ``_operation_state``:
|
|
4
|
+
|
|
5
|
+
1. ``agent_success_group_eval`` — the runner's "evaluated" marker.
|
|
6
|
+
2. ``grade_on_demand`` — the 24h on-demand grading cache.
|
|
7
|
+
3. ``retrieved_learning_eval`` — retrieved-learning generation/completion
|
|
8
|
+
state (see ``retrieved_learning_state.build_retrieved_learning_state_key``).
|
|
9
|
+
|
|
10
|
+
Governance erasure must scrub all three for an erased user's sessions, so the
|
|
11
|
+
key builders live here where both the producers (runner / evaluation route)
|
|
12
|
+
and the erasers (sqlite + supabase governance) can import them without
|
|
13
|
+
layering cycles.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
AGENT_SUCCESS_MARKER_PREFIX = "agent_success_group_eval"
|
|
19
|
+
GRADE_ON_DEMAND_CACHE_PREFIX = "grade_on_demand"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def build_agent_success_marker_key(org_id: str, user_id: str, session_id: str) -> str:
|
|
23
|
+
"""Build the agent-success "evaluated" marker key for a session.
|
|
24
|
+
|
|
25
|
+
Historical plain ``::`` join — kept byte-identical so existing markers
|
|
26
|
+
stay addressable.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
org_id (str): Organization ID.
|
|
30
|
+
user_id (str): Session owner.
|
|
31
|
+
session_id (str): Evaluated session.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
str: The marker key.
|
|
35
|
+
"""
|
|
36
|
+
return f"{AGENT_SUCCESS_MARKER_PREFIX}::{org_id}::{user_id}::{session_id}"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def build_grade_on_demand_cache_key(
|
|
40
|
+
org_id: str, session_id: str, agent_version: str, evaluation_name: str
|
|
41
|
+
) -> str:
|
|
42
|
+
"""Build the grade-on-demand cache key (length-prefixed components).
|
|
43
|
+
|
|
44
|
+
Length-prefixing each free-form component keeps the join injective even
|
|
45
|
+
when a component contains the ``::`` delimiter.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
org_id (str): Organization ID.
|
|
49
|
+
session_id (str): Target session.
|
|
50
|
+
agent_version (str): Agent version filter.
|
|
51
|
+
evaluation_name (str): Evaluator/result namespace.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
str: The cache key.
|
|
55
|
+
"""
|
|
56
|
+
parts = "::".join(
|
|
57
|
+
f"{len(s)}:{s}" for s in (org_id, session_id, agent_version, evaluation_name)
|
|
58
|
+
)
|
|
59
|
+
return f"{GRADE_ON_DEMAND_CACHE_PREFIX}::{parts}"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def build_grade_on_demand_session_prefix(org_id: str, session_id: str) -> str:
|
|
63
|
+
"""Prefix matching every grade-on-demand cache key for one session.
|
|
64
|
+
|
|
65
|
+
Used by governance erasure to find a session's cache rows regardless of
|
|
66
|
+
agent_version / evaluation_name.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
org_id (str): Organization ID.
|
|
70
|
+
session_id (str): Target session.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
str: The per-session key prefix (ends with ``::``).
|
|
74
|
+
"""
|
|
75
|
+
return (
|
|
76
|
+
f"{GRADE_ON_DEMAND_CACHE_PREFIX}"
|
|
77
|
+
f"::{len(org_id)}:{org_id}::{len(session_id)}:{session_id}::"
|
|
78
|
+
)
|
package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py
CHANGED
|
@@ -161,6 +161,44 @@ class AgentPlaybookStoreMixin:
|
|
|
161
161
|
"""
|
|
162
162
|
raise NotImplementedError
|
|
163
163
|
|
|
164
|
+
@abstractmethod
|
|
165
|
+
def get_agent_playbooks_by_ids(
|
|
166
|
+
self,
|
|
167
|
+
agent_playbook_ids: list[int],
|
|
168
|
+
*,
|
|
169
|
+
status_filter: list[Status | None] | None = None,
|
|
170
|
+
playbook_status_filter: list[PlaybookStatus] | None = None,
|
|
171
|
+
include_inactive: bool = False,
|
|
172
|
+
) -> list[AgentPlaybook]:
|
|
173
|
+
"""Fetch agent playbooks in bulk with lifecycle filters.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
agent_playbook_ids (list[int]): Playbook ids to fetch. Empty list
|
|
177
|
+
returns ``[]`` without hitting storage.
|
|
178
|
+
status_filter (list[Status | None] | None): Lifecycle statuses to
|
|
179
|
+
include. ``None`` (default) means CURRENT only.
|
|
180
|
+
playbook_status_filter (list[PlaybookStatus] | None): Approval
|
|
181
|
+
statuses to include. ``None`` (default) means no approval filter.
|
|
182
|
+
include_inactive (bool): Return every matching row regardless of
|
|
183
|
+
lifecycle status *or* approval status. This is the *historical
|
|
184
|
+
resolution* mode (see ``RetrievedLearningEvaluator``): it answers
|
|
185
|
+
"what did this id point at", not "what is retrievable now", so an
|
|
186
|
+
archived or never-approved playbook that was actually served is
|
|
187
|
+
still returned. It is a strict superset of ``include_tombstones``
|
|
188
|
+
on ``get_agent_playbook_by_id``, which only unhides
|
|
189
|
+
MERGED/SUPERSEDED for lineage walks. The default preserves
|
|
190
|
+
retrieval behavior.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
list[AgentPlaybook]: Matching playbooks. Order is unspecified.
|
|
194
|
+
|
|
195
|
+
Raises:
|
|
196
|
+
StorageError: If ``include_inactive`` is combined with an explicit
|
|
197
|
+
``status_filter`` or ``playbook_status_filter`` — the two are
|
|
198
|
+
contradictory.
|
|
199
|
+
"""
|
|
200
|
+
raise NotImplementedError
|
|
201
|
+
|
|
164
202
|
@abstractmethod
|
|
165
203
|
def delete_all_agent_playbooks(self) -> None:
|
|
166
204
|
"""Delete all agent playbooks from storage."""
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
"""Abstract agent success evaluation result store declarations."""
|
|
2
2
|
|
|
3
3
|
from abc import abstractmethod
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
5
6
|
from reflexio.models.api_schema.domain import (
|
|
6
7
|
AgentSuccessEvaluationResult,
|
|
8
|
+
RetrievedLearningEvaluationResult,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from ..retrieved_learning_state import (
|
|
12
|
+
DEFAULT_TRANSCRIPT_CHAR_LIMIT,
|
|
13
|
+
BoundedRetrievedLearningSnapshot,
|
|
14
|
+
RetrievedLearningCommitResult,
|
|
7
15
|
)
|
|
8
16
|
|
|
9
17
|
|
|
@@ -122,3 +130,166 @@ class AgentEvaluationResultStoreMixin:
|
|
|
122
130
|
int: Number of rows deleted.
|
|
123
131
|
"""
|
|
124
132
|
raise NotImplementedError
|
|
133
|
+
|
|
134
|
+
# ==============================
|
|
135
|
+
# Retrieved-learning evaluation methods
|
|
136
|
+
# ==============================
|
|
137
|
+
#
|
|
138
|
+
# Concurrency contract (see storage_base/retrieved_learning_state.py):
|
|
139
|
+
# generation fencing + session-fingerprint CAS recomputed under the
|
|
140
|
+
# replacement transaction's lock. No interaction write/delete path is
|
|
141
|
+
# instrumented.
|
|
142
|
+
|
|
143
|
+
@abstractmethod
|
|
144
|
+
def begin_retrieved_learning_evaluation_run(
|
|
145
|
+
self, user_id: str, session_id: str
|
|
146
|
+
) -> int:
|
|
147
|
+
"""Allocate the next evaluation generation for one session.
|
|
148
|
+
|
|
149
|
+
Atomically increments (creating on first use) the ``generation``
|
|
150
|
+
counter in the session's ``_operation_state`` row after asserting the
|
|
151
|
+
governance subject is writable. Concurrent callers receive distinct
|
|
152
|
+
generations.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
user_id (str): Session owner.
|
|
156
|
+
session_id (str): Evaluated session.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
int: The allocated generation (monotonically increasing per
|
|
160
|
+
session).
|
|
161
|
+
"""
|
|
162
|
+
raise NotImplementedError
|
|
163
|
+
|
|
164
|
+
@abstractmethod
|
|
165
|
+
def load_bounded_retrieved_learning_snapshot(
|
|
166
|
+
self,
|
|
167
|
+
user_id: str,
|
|
168
|
+
session_id: str,
|
|
169
|
+
raw_ref_limit: int = 5_000,
|
|
170
|
+
transcript_char_limit: int = DEFAULT_TRANSCRIPT_CHAR_LIMIT,
|
|
171
|
+
) -> BoundedRetrievedLearningSnapshot:
|
|
172
|
+
"""Load the bounded session projection for evaluation.
|
|
173
|
+
|
|
174
|
+
Streams interaction rows (no ``Interaction`` objects, no embeddings or
|
|
175
|
+
image encodings) and counts raw attachment occurrences before
|
|
176
|
+
appending; on occurrence ``raw_ref_limit + 1`` the scan aborts with
|
|
177
|
+
``attachment_limit_exceeded=True``.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
user_id (str): Session owner.
|
|
181
|
+
session_id (str): Evaluated session.
|
|
182
|
+
raw_ref_limit (int): Maximum raw attachment occurrences to parse.
|
|
183
|
+
transcript_char_limit (int): Maximum transcript characters retained.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
BoundedRetrievedLearningSnapshot: The bounded projection.
|
|
187
|
+
"""
|
|
188
|
+
raise NotImplementedError
|
|
189
|
+
|
|
190
|
+
@abstractmethod
|
|
191
|
+
def get_matching_retrieved_learning_terminal_state(
|
|
192
|
+
self, user_id: str, session_id: str, session_fingerprint: str
|
|
193
|
+
) -> dict[str, Any] | None:
|
|
194
|
+
"""Return the session's terminal evaluation state if still fresh.
|
|
195
|
+
|
|
196
|
+
Implementations must use a writer transaction and recompute the live
|
|
197
|
+
session fingerprint in that transaction. A match requires terminal
|
|
198
|
+
status and equality among the supplied, persisted, and live
|
|
199
|
+
fingerprints.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
user_id (str): Session owner.
|
|
203
|
+
session_id (str): Evaluated session.
|
|
204
|
+
session_fingerprint (str): Fingerprint of the current session.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
dict | None: The persisted state JSON on a match, else None.
|
|
208
|
+
"""
|
|
209
|
+
raise NotImplementedError
|
|
210
|
+
|
|
211
|
+
@abstractmethod
|
|
212
|
+
def replace_retrieved_learning_evaluation_results(
|
|
213
|
+
self,
|
|
214
|
+
user_id: str,
|
|
215
|
+
session_id: str,
|
|
216
|
+
generation: int,
|
|
217
|
+
session_fingerprint: str,
|
|
218
|
+
proposed_status: str,
|
|
219
|
+
diagnostics: dict[str, Any],
|
|
220
|
+
results: list[RetrievedLearningEvaluationResult],
|
|
221
|
+
) -> RetrievedLearningCommitResult:
|
|
222
|
+
"""Atomically replace one session's evaluation result set.
|
|
223
|
+
|
|
224
|
+
In one transaction: locks the session's state row, requires the
|
|
225
|
+
current generation to equal ``generation`` (else ``superseded``),
|
|
226
|
+
recomputes the session fingerprint from live rows and requires it to
|
|
227
|
+
equal ``session_fingerprint`` (else ``stale``), rechecks every
|
|
228
|
+
result's source row for retrieval eligibility (ineligible rows are
|
|
229
|
+
dropped), then deletes the prior session set, inserts the filtered
|
|
230
|
+
set, and persists completion state — all or nothing.
|
|
231
|
+
|
|
232
|
+
When commit-time eligibility removes every candidate, prior rows are
|
|
233
|
+
cleared and the final status is ``not_applicable`` regardless of
|
|
234
|
+
``proposed_status``.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
user_id (str): Session owner.
|
|
238
|
+
session_id (str): Evaluated session.
|
|
239
|
+
generation (int): Generation allocated by
|
|
240
|
+
:meth:`begin_retrieved_learning_evaluation_run`.
|
|
241
|
+
session_fingerprint (str): Fingerprint of the judged snapshot.
|
|
242
|
+
proposed_status (str): ``"complete"`` or ``"degraded"``.
|
|
243
|
+
diagnostics (dict): Sanitized counters/timestamps persisted in
|
|
244
|
+
operation state (never prompt content or raw exception text).
|
|
245
|
+
results (list[RetrievedLearningEvaluationResult]): Proposed rows.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
RetrievedLearningCommitResult: Disposition plus authoritative
|
|
249
|
+
final status/count when applied.
|
|
250
|
+
"""
|
|
251
|
+
raise NotImplementedError
|
|
252
|
+
|
|
253
|
+
@abstractmethod
|
|
254
|
+
def finish_retrieved_learning_evaluation_run(
|
|
255
|
+
self,
|
|
256
|
+
user_id: str,
|
|
257
|
+
session_id: str,
|
|
258
|
+
generation: int,
|
|
259
|
+
status: str,
|
|
260
|
+
diagnostics: dict[str, Any],
|
|
261
|
+
) -> None:
|
|
262
|
+
"""Record a non-applied run outcome (``failed`` or ``pending``).
|
|
263
|
+
|
|
264
|
+
Generation-guarded CAS: updates state only while ``generation`` is
|
|
265
|
+
still current, so an older failure cannot overwrite a newer run.
|
|
266
|
+
Result rows are never touched.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
user_id (str): Session owner.
|
|
270
|
+
session_id (str): Evaluated session.
|
|
271
|
+
generation (int): Generation of the finishing run.
|
|
272
|
+
status (str): ``"failed"`` or ``"pending"``.
|
|
273
|
+
diagnostics (dict): Sanitized counters and error type.
|
|
274
|
+
"""
|
|
275
|
+
raise NotImplementedError
|
|
276
|
+
|
|
277
|
+
@abstractmethod
|
|
278
|
+
def get_retrieved_learning_evaluation_results(
|
|
279
|
+
self,
|
|
280
|
+
user_id: str | None = None,
|
|
281
|
+
session_id: str | None = None,
|
|
282
|
+
limit: int = 100,
|
|
283
|
+
) -> list[RetrievedLearningEvaluationResult]:
|
|
284
|
+
"""Read persisted per-learning verdicts.
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
user_id (str, optional): Filter by session owner.
|
|
288
|
+
session_id (str, optional): Filter by session.
|
|
289
|
+
limit (int): Maximum rows, ordered by
|
|
290
|
+
``created_at DESC, result_id DESC``.
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
list[RetrievedLearningEvaluationResult]: Matching rows.
|
|
294
|
+
"""
|
|
295
|
+
raise NotImplementedError
|