claude-smart 0.2.46 → 0.2.47
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 +1 -1
- package/README.md +19 -11
- package/bin/claude-smart.js +290 -68
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/README.md +11 -10
- package/plugin/dashboard/app/layout.tsx +20 -0
- package/plugin/opencode/dist/server.mjs +76 -2
- package/plugin/opencode/server.mts +79 -2
- package/plugin/pyproject.toml +6 -2
- package/plugin/scripts/smart-install.sh +7 -1
- package/plugin/src/claude_smart/cli.py +210 -22
- package/plugin/src/claude_smart/context_format.py +9 -9
- package/plugin/src/claude_smart/cs_cite.py +66 -19
- package/plugin/uv.lock +5 -5
- package/plugin/vendor/reflexio/README.md +3 -3
- package/plugin/vendor/reflexio/pyproject.toml +1 -1
- package/plugin/vendor/reflexio/reflexio/README.md +3 -1
- package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +1 -1
- package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +2 -2
- package/plugin/vendor/reflexio/reflexio/cli/utils.py +44 -1
- package/plugin/vendor/reflexio/reflexio/client/client.py +97 -0
- package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +8 -0
- package/plugin/vendor/reflexio/reflexio/lib/_base.py +15 -0
- package/plugin/vendor/reflexio/reflexio/lib/_generation.py +9 -8
- package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +27 -16
- package/plugin/vendor/reflexio/reflexio/lib/_search.py +23 -5
- package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +9 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/governance.py +117 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +45 -3
- package/plugin/vendor/reflexio/reflexio/models/config_schema.py +16 -0
- package/plugin/vendor/reflexio/reflexio/server/README.md +14 -2
- package/plugin/vendor/reflexio/reflexio/server/api.py +176 -29
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +1 -1
- package/plugin/vendor/reflexio/reflexio/server/{_auth.py → auth.py} +2 -0
- package/plugin/vendor/reflexio/reflexio/server/extensions.py +213 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +4 -4
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1 -1
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +12 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +14 -2
- package/plugin/vendor/reflexio/reflexio/server/services/README.md +3 -1
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +8 -0
- package/plugin/vendor/reflexio/reflexio/server/services/governance/config.py +52 -0
- package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +378 -0
- package/plugin/vendor/reflexio/reflexio/server/services/governance/subject_refs.py +34 -0
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +45 -19
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +9 -1
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_prompt_processing.py +100 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator.py +129 -111
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/service.py +9 -9
- package/plugin/vendor/reflexio/reflexio/server/services/profile/components/extractor.py +3 -2
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/recency.py +211 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +29 -13
- package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +4 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +681 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +14 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +2 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +49 -19
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +1965 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +5 -3
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1 -2133
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +262 -107
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +73 -33
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +952 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +189 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_optimization.py +247 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +145 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +838 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +19 -3
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +4 -4
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_governance.py +148 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +0 -909
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +2 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +365 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +124 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_optimization.py +85 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_source_linkage.py +47 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +333 -0
- package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +153 -12
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/user_detail_stripping.py +0 -84
|
@@ -38,22 +38,23 @@ class GenerationMixin(ReflexioBase):
|
|
|
38
38
|
"""
|
|
39
39
|
if not self._is_storage_configured():
|
|
40
40
|
raise ValueError(STORAGE_NOT_CONFIGURED_MSG)
|
|
41
|
+
from reflexio.server.extensions import get_service
|
|
42
|
+
from reflexio.server.services.playbook.aggregation_prompt_processing import (
|
|
43
|
+
AGGREGATION_PROMPT_PROCESSOR,
|
|
44
|
+
)
|
|
41
45
|
from reflexio.server.services.playbook.components.aggregator import (
|
|
42
46
|
PlaybookAggregator,
|
|
43
47
|
)
|
|
44
48
|
from reflexio.server.services.playbook.playbook_service_utils import (
|
|
45
49
|
PlaybookAggregatorRequest,
|
|
46
50
|
)
|
|
47
|
-
from reflexio.server.services.playbook.user_detail_stripping import (
|
|
48
|
-
create_aggregation_user_detail_stripper,
|
|
49
|
-
)
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
self.request_context.configurator
|
|
53
|
-
)
|
|
52
|
+
aggregation_prompt_processor = get_service(AGGREGATION_PROMPT_PROCESSOR)
|
|
54
53
|
aggregator_kwargs = {}
|
|
55
|
-
if
|
|
56
|
-
aggregator_kwargs["
|
|
54
|
+
if aggregation_prompt_processor is not None:
|
|
55
|
+
aggregator_kwargs["aggregation_prompt_processor"] = (
|
|
56
|
+
aggregation_prompt_processor
|
|
57
|
+
)
|
|
57
58
|
|
|
58
59
|
playbook_aggregator = PlaybookAggregator(
|
|
59
60
|
llm_client=self.llm_client,
|
|
@@ -414,26 +414,22 @@ class ProfilesMixin(ReflexioBase):
|
|
|
414
414
|
status_filter = [None] # Default to current profiles
|
|
415
415
|
|
|
416
416
|
profiles = self._get_storage().get_user_profile(
|
|
417
|
-
request.user_id,
|
|
417
|
+
request.user_id,
|
|
418
|
+
status_filter=status_filter,
|
|
419
|
+
tags=request.tags,
|
|
420
|
+
profile_id=request.profile_id,
|
|
421
|
+
query=request.query,
|
|
422
|
+
source=request.source,
|
|
423
|
+
profile_time_to_live=request.profile_time_to_live,
|
|
424
|
+
start_time=(
|
|
425
|
+
int(request.start_time.timestamp()) if request.start_time else None
|
|
426
|
+
),
|
|
427
|
+
end_time=int(request.end_time.timestamp()) if request.end_time else None,
|
|
418
428
|
)
|
|
419
429
|
profiles = sorted(
|
|
420
430
|
profiles, key=lambda x: x.last_modified_timestamp, reverse=True
|
|
421
431
|
)
|
|
422
432
|
|
|
423
|
-
# Apply time filters
|
|
424
|
-
if request.start_time:
|
|
425
|
-
profiles = [
|
|
426
|
-
p
|
|
427
|
-
for p in profiles
|
|
428
|
-
if p.last_modified_timestamp >= int(request.start_time.timestamp())
|
|
429
|
-
]
|
|
430
|
-
if request.end_time:
|
|
431
|
-
profiles = [
|
|
432
|
-
p
|
|
433
|
-
for p in profiles
|
|
434
|
-
if p.last_modified_timestamp <= int(request.end_time.timestamp())
|
|
435
|
-
]
|
|
436
|
-
|
|
437
433
|
# Apply top_k limit
|
|
438
434
|
if request.top_k:
|
|
439
435
|
profiles = sorted(
|
|
@@ -450,6 +446,13 @@ class ProfilesMixin(ReflexioBase):
|
|
|
450
446
|
self,
|
|
451
447
|
limit: int = 100,
|
|
452
448
|
status_filter: list[Status | None] | None = None,
|
|
449
|
+
user_id: str | None = None,
|
|
450
|
+
profile_id: str | None = None,
|
|
451
|
+
query: str | None = None,
|
|
452
|
+
source: str | None = None,
|
|
453
|
+
profile_time_to_live: str | None = None,
|
|
454
|
+
start_time: int | None = None,
|
|
455
|
+
end_time: int | None = None,
|
|
453
456
|
) -> GetUserProfilesResponse:
|
|
454
457
|
"""Get all user profiles across all users.
|
|
455
458
|
|
|
@@ -467,7 +470,15 @@ class ProfilesMixin(ReflexioBase):
|
|
|
467
470
|
if status_filter is None:
|
|
468
471
|
status_filter = [None] # Default to current profiles
|
|
469
472
|
profiles = self._get_storage().get_all_profiles(
|
|
470
|
-
limit=limit,
|
|
473
|
+
limit=limit,
|
|
474
|
+
status_filter=status_filter,
|
|
475
|
+
user_id=user_id,
|
|
476
|
+
profile_id=profile_id,
|
|
477
|
+
query=query,
|
|
478
|
+
source=source,
|
|
479
|
+
profile_time_to_live=profile_time_to_live,
|
|
480
|
+
start_time=start_time,
|
|
481
|
+
end_time=end_time,
|
|
471
482
|
)
|
|
472
483
|
profiles = sorted(
|
|
473
484
|
profiles, key=lambda x: x.last_modified_timestamp, reverse=True
|
|
@@ -41,9 +41,23 @@ class SearchMixin(ReflexioBase):
|
|
|
41
41
|
request = GetAgentSuccessEvaluationResultsRequest(**request)
|
|
42
42
|
|
|
43
43
|
try:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
if request.start_time or request.end_time:
|
|
45
|
+
results = self._get_storage().get_agent_success_evaluation_results_in_window(
|
|
46
|
+
from_ts=(
|
|
47
|
+
int(request.start_time.timestamp()) if request.start_time else 0
|
|
48
|
+
),
|
|
49
|
+
to_ts=(
|
|
50
|
+
int(request.end_time.timestamp())
|
|
51
|
+
if request.end_time
|
|
52
|
+
else 2**31 - 1
|
|
53
|
+
),
|
|
54
|
+
limit=request.limit or 100,
|
|
55
|
+
agent_version=request.agent_version,
|
|
56
|
+
)
|
|
57
|
+
else:
|
|
58
|
+
results = self._get_storage().get_agent_success_evaluation_results(
|
|
59
|
+
limit=request.limit or 100, agent_version=request.agent_version
|
|
60
|
+
)
|
|
47
61
|
return GetAgentSuccessEvaluationResultsResponse(
|
|
48
62
|
success=True,
|
|
49
63
|
agent_success_evaluation_results=results,
|
|
@@ -79,6 +93,7 @@ class SearchMixin(ReflexioBase):
|
|
|
79
93
|
user_id=request.user_id,
|
|
80
94
|
request_id=request.request_id,
|
|
81
95
|
session_id=request.session_id,
|
|
96
|
+
source=request.source,
|
|
82
97
|
start_time=(
|
|
83
98
|
int(request.start_time.timestamp()) if request.start_time else None
|
|
84
99
|
),
|
|
@@ -104,10 +119,11 @@ class SearchMixin(ReflexioBase):
|
|
|
104
119
|
Session(session_id=group_name, requests=request_data_list)
|
|
105
120
|
)
|
|
106
121
|
|
|
107
|
-
#
|
|
122
|
+
# Pagination is session-based (top_k counts sessions): there may be
|
|
123
|
+
# more pages when this page filled the session limit.
|
|
108
124
|
total_returned = sum(len(s.requests) for s in sessions)
|
|
109
125
|
effective_limit = request.top_k or 100
|
|
110
|
-
has_more =
|
|
126
|
+
has_more = len(sessions) >= effective_limit
|
|
111
127
|
|
|
112
128
|
return GetRequestsResponse(
|
|
113
129
|
success=True,
|
|
@@ -162,6 +178,7 @@ class SearchMixin(ReflexioBase):
|
|
|
162
178
|
storage = self._get_storage()
|
|
163
179
|
prompt_manager = self.request_context.prompt_manager
|
|
164
180
|
retrieval_floor = config.retrieval_floor if config else None
|
|
181
|
+
recency = getattr(storage, "recency", None)
|
|
165
182
|
|
|
166
183
|
from reflexio.server.services.unified_search_service import run_unified_search
|
|
167
184
|
|
|
@@ -173,4 +190,5 @@ class SearchMixin(ReflexioBase):
|
|
|
173
190
|
prompt_manager=prompt_manager,
|
|
174
191
|
pre_retrieval_model_name=pre_retrieval_model_name,
|
|
175
192
|
retrieval_floor=retrieval_floor,
|
|
193
|
+
recency=recency,
|
|
176
194
|
)
|
|
@@ -53,10 +53,19 @@ class UserPlaybookMixin(ReflexioBase):
|
|
|
53
53
|
try:
|
|
54
54
|
user_playbooks = self._get_storage().get_user_playbooks(
|
|
55
55
|
limit=request.limit or 100,
|
|
56
|
+
user_playbook_id=request.user_playbook_id,
|
|
56
57
|
user_id=request.user_id,
|
|
58
|
+
request_id=request.request_id,
|
|
59
|
+
query=request.query,
|
|
57
60
|
playbook_name=request.playbook_name,
|
|
58
61
|
agent_version=request.agent_version,
|
|
59
62
|
status_filter=request.status_filter,
|
|
63
|
+
start_time=(
|
|
64
|
+
int(request.start_time.timestamp()) if request.start_time else None
|
|
65
|
+
),
|
|
66
|
+
end_time=(
|
|
67
|
+
int(request.end_time.timestamp()) if request.end_time else None
|
|
68
|
+
),
|
|
60
69
|
tags=request.tags,
|
|
61
70
|
)
|
|
62
71
|
return GetUserPlaybooksResponse(
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import UTC, datetime
|
|
4
|
+
from typing import Any, Literal
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
|
|
8
|
+
AuditActorType = Literal["api_token", "jwt", "system"]
|
|
9
|
+
AuditOperation = Literal["READ", "EXPORT", "ERASE", "CREATE", "UPDATE", "DELETE"]
|
|
10
|
+
AuditEntityType = Literal[
|
|
11
|
+
"profile",
|
|
12
|
+
"user_playbook",
|
|
13
|
+
"agent_playbook",
|
|
14
|
+
"interaction",
|
|
15
|
+
"request",
|
|
16
|
+
"session",
|
|
17
|
+
"agent_success_evaluation_result",
|
|
18
|
+
"playbook_retrieval_log",
|
|
19
|
+
"org",
|
|
20
|
+
]
|
|
21
|
+
AuditStatus = Literal["ok", "error"]
|
|
22
|
+
PurgeOperationType = Literal["user_erasure", "org_purge"]
|
|
23
|
+
PurgeScopeType = Literal["user", "org"]
|
|
24
|
+
PurgeStatus = Literal["pending", "running", "failed", "complete"]
|
|
25
|
+
PurgeTargetStatus = Literal["pending", "running", "failed", "complete"]
|
|
26
|
+
SubjectBarrierStatus = Literal["erasing", "erased", "failed"]
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"AuditActorType",
|
|
30
|
+
"AuditOperation",
|
|
31
|
+
"AuditEntityType",
|
|
32
|
+
"AuditStatus",
|
|
33
|
+
"PurgeOperationType",
|
|
34
|
+
"PurgeScopeType",
|
|
35
|
+
"PurgeStatus",
|
|
36
|
+
"PurgeTargetStatus",
|
|
37
|
+
"SubjectBarrierStatus",
|
|
38
|
+
"AuditEvent",
|
|
39
|
+
"PurgeOperation",
|
|
40
|
+
"PurgeOperationTarget",
|
|
41
|
+
"SubjectWriteBarrier",
|
|
42
|
+
"UserExportResult",
|
|
43
|
+
"UserEraseResult",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _now_epoch() -> int:
|
|
48
|
+
return int(datetime.now(UTC).timestamp())
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class AuditEvent(BaseModel):
|
|
52
|
+
org_id: str
|
|
53
|
+
actor_type: AuditActorType = "system"
|
|
54
|
+
actor_ref: str | None = None
|
|
55
|
+
operation: AuditOperation
|
|
56
|
+
entity_type: AuditEntityType
|
|
57
|
+
entity_id: str | None = None
|
|
58
|
+
subject_ref: str | None = None
|
|
59
|
+
request_ref: str
|
|
60
|
+
idempotency_key: str | None = None
|
|
61
|
+
status: AuditStatus = "ok"
|
|
62
|
+
detail: dict[str, Any] | None = None
|
|
63
|
+
created_at: int = Field(default_factory=_now_epoch)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class PurgeOperation(BaseModel):
|
|
67
|
+
purge_id: str
|
|
68
|
+
org_id: str
|
|
69
|
+
operation_type: PurgeOperationType
|
|
70
|
+
scope_type: PurgeScopeType
|
|
71
|
+
subject_ref: str | None = None
|
|
72
|
+
request_ref: str
|
|
73
|
+
idempotency_key: str
|
|
74
|
+
status: PurgeStatus = "pending"
|
|
75
|
+
error_code: str | None = None
|
|
76
|
+
error_detail: str | None = None
|
|
77
|
+
created_at: int = Field(default_factory=_now_epoch)
|
|
78
|
+
updated_at: int = Field(default_factory=_now_epoch)
|
|
79
|
+
completed_at: int | None = None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class PurgeOperationTarget(BaseModel):
|
|
83
|
+
purge_id: str
|
|
84
|
+
target_name: str
|
|
85
|
+
target_ref: str = ""
|
|
86
|
+
phase: str
|
|
87
|
+
status: PurgeTargetStatus = "pending"
|
|
88
|
+
detail: dict[str, Any] | None = None
|
|
89
|
+
deleted_count: int = 0
|
|
90
|
+
error_detail: str | None = None
|
|
91
|
+
started_at: int | None = None
|
|
92
|
+
completed_at: int | None = None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class SubjectWriteBarrier(BaseModel):
|
|
96
|
+
org_id: str
|
|
97
|
+
subject_ref: str
|
|
98
|
+
purge_id: str
|
|
99
|
+
status: SubjectBarrierStatus
|
|
100
|
+
error_code: str | None = None
|
|
101
|
+
error_detail: str | None = None
|
|
102
|
+
created_at: int = Field(default_factory=_now_epoch)
|
|
103
|
+
updated_at: int = Field(default_factory=_now_epoch)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class UserExportResult(BaseModel):
|
|
107
|
+
subject_ref: str
|
|
108
|
+
export_id: str
|
|
109
|
+
bundle: dict[str, Any]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class UserEraseResult(BaseModel):
|
|
113
|
+
subject_ref: str
|
|
114
|
+
purge_id: str
|
|
115
|
+
status: PurgeStatus
|
|
116
|
+
deleted_counts: dict[str, int] = Field(default_factory=dict)
|
|
117
|
+
rebuilt_agent_playbook_ids: list[int] = Field(default_factory=list)
|
|
@@ -187,9 +187,13 @@ class GetInteractionsResponse(BaseModel):
|
|
|
187
187
|
|
|
188
188
|
class GetUserProfilesRequest(BaseModel):
|
|
189
189
|
user_id: NonEmptyStr
|
|
190
|
+
profile_id: str | None = None
|
|
191
|
+
query: str | None = None
|
|
190
192
|
start_time: datetime | None = None
|
|
191
193
|
end_time: datetime | None = None
|
|
192
194
|
top_k: int | None = Field(default=30, gt=0)
|
|
195
|
+
source: str | None = None
|
|
196
|
+
profile_time_to_live: str | None = None
|
|
193
197
|
status_filter: list[Status | None] | None = None
|
|
194
198
|
tags: list[str] | None = None
|
|
195
199
|
|
|
@@ -222,12 +226,23 @@ class SetConfigResponse(BaseModel):
|
|
|
222
226
|
|
|
223
227
|
class GetUserPlaybooksRequest(BaseModel):
|
|
224
228
|
limit: int | None = Field(default=100, gt=0)
|
|
229
|
+
user_playbook_id: int | None = Field(default=None, gt=0)
|
|
225
230
|
user_id: str | None = None
|
|
231
|
+
request_id: str | None = None
|
|
232
|
+
query: str | None = None
|
|
226
233
|
playbook_name: str | None = None
|
|
227
234
|
agent_version: str | None = None
|
|
235
|
+
start_time: datetime | None = None
|
|
236
|
+
end_time: datetime | None = None
|
|
228
237
|
status_filter: list[Status | None] | None = None
|
|
229
238
|
tags: list[str] | None = None
|
|
230
239
|
|
|
240
|
+
@model_validator(mode="after")
|
|
241
|
+
def check_time_range(self) -> Self:
|
|
242
|
+
"""Validate that end_time is after start_time."""
|
|
243
|
+
TimeRangeValidatorMixin.validate_time_range(self.start_time, self.end_time)
|
|
244
|
+
return self
|
|
245
|
+
|
|
231
246
|
|
|
232
247
|
class GetUserPlaybooksResponse(BaseModel):
|
|
233
248
|
success: bool
|
|
@@ -237,8 +252,12 @@ class GetUserPlaybooksResponse(BaseModel):
|
|
|
237
252
|
|
|
238
253
|
class GetAgentPlaybooksRequest(BaseModel):
|
|
239
254
|
limit: int | None = Field(default=100, gt=0)
|
|
255
|
+
agent_playbook_id: int | None = Field(default=None, gt=0)
|
|
256
|
+
query: str | None = None
|
|
240
257
|
playbook_name: str | None = None
|
|
241
258
|
agent_version: str | None = None
|
|
259
|
+
start_time: datetime | None = None
|
|
260
|
+
end_time: datetime | None = None
|
|
242
261
|
status_filter: list[Status | None] | None = None
|
|
243
262
|
playbook_status_filter: PlaybookStatus | None = None
|
|
244
263
|
tags: list[str] | None = None
|
|
@@ -247,6 +266,12 @@ class GetAgentPlaybooksRequest(BaseModel):
|
|
|
247
266
|
request_id: str | None = None
|
|
248
267
|
session_id: str | None = None
|
|
249
268
|
|
|
269
|
+
@model_validator(mode="after")
|
|
270
|
+
def check_time_range(self) -> Self:
|
|
271
|
+
"""Validate that end_time is after start_time."""
|
|
272
|
+
TimeRangeValidatorMixin.validate_time_range(self.start_time, self.end_time)
|
|
273
|
+
return self
|
|
274
|
+
|
|
250
275
|
|
|
251
276
|
class GetAgentPlaybooksResponse(BaseModel):
|
|
252
277
|
success: bool
|
|
@@ -392,6 +417,14 @@ class SearchAgentPlaybookResponse(BaseModel):
|
|
|
392
417
|
class GetAgentSuccessEvaluationResultsRequest(BaseModel):
|
|
393
418
|
limit: int | None = Field(default=100, gt=0)
|
|
394
419
|
agent_version: str | None = None
|
|
420
|
+
start_time: datetime | None = None
|
|
421
|
+
end_time: datetime | None = None
|
|
422
|
+
|
|
423
|
+
@model_validator(mode="after")
|
|
424
|
+
def check_time_range(self) -> Self:
|
|
425
|
+
"""Validate that end_time is after start_time."""
|
|
426
|
+
TimeRangeValidatorMixin.validate_time_range(self.start_time, self.end_time)
|
|
427
|
+
return self
|
|
395
428
|
|
|
396
429
|
|
|
397
430
|
class GetAgentSuccessEvaluationResultsResponse(BaseModel):
|
|
@@ -404,10 +437,18 @@ class GetRequestsRequest(BaseModel):
|
|
|
404
437
|
user_id: str | None = None
|
|
405
438
|
request_id: str | None = None
|
|
406
439
|
session_id: str | None = None
|
|
440
|
+
source: str | None = None
|
|
407
441
|
start_time: datetime | None = None
|
|
408
442
|
end_time: datetime | None = None
|
|
409
|
-
top_k: int | None = Field(
|
|
410
|
-
|
|
443
|
+
top_k: int | None = Field(
|
|
444
|
+
default=30,
|
|
445
|
+
gt=0,
|
|
446
|
+
description="Maximum number of sessions to return. Pagination is "
|
|
447
|
+
"per-session: every returned session includes all of its requests.",
|
|
448
|
+
)
|
|
449
|
+
offset: int | None = Field(
|
|
450
|
+
default=0, ge=0, description="Number of sessions to skip for pagination."
|
|
451
|
+
)
|
|
411
452
|
|
|
412
453
|
@model_validator(mode="after")
|
|
413
454
|
def check_time_range(self) -> Self:
|
|
@@ -497,7 +538,8 @@ class TimeSeriesDataPoint(BaseModel):
|
|
|
497
538
|
"""A single data point in a time series."""
|
|
498
539
|
|
|
499
540
|
timestamp: int = Field(gt=0) # Unix timestamp
|
|
500
|
-
value:
|
|
541
|
+
value: float = Field(ge=0) # Count or metric value
|
|
542
|
+
count: int | None = Field(default=None, ge=0) # Optional weight for rate metrics
|
|
501
543
|
|
|
502
544
|
|
|
503
545
|
class PeriodStats(BaseModel):
|
|
@@ -708,6 +708,18 @@ class LineageGCConfig(BaseModel):
|
|
|
708
708
|
poll_interval_seconds: int = Field(default=86400, gt=0)
|
|
709
709
|
|
|
710
710
|
|
|
711
|
+
class GovernanceRetentionConfig(BaseModel):
|
|
712
|
+
"""Audit-event retention policy. **Enterprise-only:** reclamation is performed
|
|
713
|
+
by the reflexio_ext GovernanceRetentionCapability. In an OSS-only deployment
|
|
714
|
+
these knobs are accepted but inert (the OSS lineage scheduler does not reclaim
|
|
715
|
+
audit events) and the server logs a startup warning when retention is enabled.
|
|
716
|
+
"""
|
|
717
|
+
|
|
718
|
+
audit_events_retention_enabled: bool = False
|
|
719
|
+
audit_events_retention_days: int = Field(default=365, gt=0)
|
|
720
|
+
audit_events_delete_batch_limit: int = Field(default=500, gt=0)
|
|
721
|
+
|
|
722
|
+
|
|
711
723
|
@dataclass(frozen=True)
|
|
712
724
|
class EffectivePendingToolCallConfig:
|
|
713
725
|
"""Resolved pending-tool-call settings after applying tool overrides."""
|
|
@@ -847,6 +859,9 @@ class Config(BaseModel):
|
|
|
847
859
|
)
|
|
848
860
|
# Tombstone GC job gate (opt-in, off by default — see LineageGCConfig)
|
|
849
861
|
lineage_gc: LineageGCConfig = Field(default_factory=LineageGCConfig)
|
|
862
|
+
governance_retention: GovernanceRetentionConfig = Field(
|
|
863
|
+
default_factory=GovernanceRetentionConfig
|
|
864
|
+
)
|
|
850
865
|
# Optional non-blocking async information tools for classic extraction.
|
|
851
866
|
pending_tool_call_config: PendingToolCallConfig = Field(
|
|
852
867
|
default_factory=PendingToolCallConfig
|
|
@@ -905,6 +920,7 @@ class Config(BaseModel):
|
|
|
905
920
|
"reflection_config",
|
|
906
921
|
"playbook_optimizer_config",
|
|
907
922
|
"lineage_gc",
|
|
923
|
+
"governance_retention",
|
|
908
924
|
"pending_tool_call_config",
|
|
909
925
|
"retrieval_floor",
|
|
910
926
|
):
|
|
@@ -6,6 +6,7 @@ Description: FastAPI backend server that processes user interactions to generate
|
|
|
6
6
|
- [Main Entry Points](#main-entry-points)
|
|
7
7
|
- [Cache](#cache)
|
|
8
8
|
- [API Endpoints](#api-endpoints)
|
|
9
|
+
- [Extension Registry](#extension-registry)
|
|
9
10
|
- [LLM Client](#llm-client)
|
|
10
11
|
- [Prompts](#prompts)
|
|
11
12
|
- [Site Variables](#site-variables)
|
|
@@ -34,6 +35,7 @@ Description: FastAPI backend server that processes user interactions to generate
|
|
|
34
35
|
|
|
35
36
|
- **API**: `api.py` - FastAPI routes (only place to expose endpoints)
|
|
36
37
|
- **Endpoint Helpers**: `api_endpoints/` - Bridge between routes and business logic
|
|
38
|
+
- **Extension Registry**: `extensions.py` - Capability and service registry for optional OSS/enterprise integrations
|
|
37
39
|
- **Core Service**: `services/generation_service.py` - Main orchestrator
|
|
38
40
|
|
|
39
41
|
## Cache
|
|
@@ -84,6 +86,14 @@ Description: FastAPI backend server that processes user interactions to generate
|
|
|
84
86
|
|
|
85
87
|
**Pattern**: Core route handlers call `Reflexio` through `get_reflexio(org_id)`; endpoint helper files should not instantiate `Reflexio` directly.
|
|
86
88
|
|
|
89
|
+
## Extension Registry
|
|
90
|
+
|
|
91
|
+
**File**: `extensions.py`
|
|
92
|
+
|
|
93
|
+
`CapabilityRegistry` lets deployments register optional routers, startup/shutdown hooks, and cross-cutting services without hardcoding enterprise-only imports into the OSS app. `create_app()` builds the active registry, stores it on `app.state.capability_registry`, installs capability routers/startup/shutdown hooks, and exposes typed service lookup through `ServiceKey`.
|
|
94
|
+
|
|
95
|
+
**Pattern**: Optional integrations should register capabilities/services at app construction time and consume them through the registry; avoid importing enterprise implementations directly in OSS modules.
|
|
96
|
+
|
|
87
97
|
## LLM Client
|
|
88
98
|
|
|
89
99
|
**Directory**: `llm/`
|
|
@@ -188,6 +198,7 @@ python -m reflexio.server.scripts.manage_invitation_codes list --show-used
|
|
|
188
198
|
- **Search preparation**: `pre_retrieval/` and `unified_search_service.py` handle query reformulation, document expansion, embeddings, and cross-entity search orchestration.
|
|
189
199
|
- **Optimization/integrations**: `playbook_optimizer/` and `braintrust/` run candidate playbook optimization, rollout support, and Braintrust export/sync.
|
|
190
200
|
- **Lineage**: `lineage/` resolves active records across superseded chains and schedules tombstone garbage collection for profile/playbook storage.
|
|
201
|
+
- **Governance**: `governance/` defines subject-reference contracts and retention/barrier policy helpers used by storage and lineage paths.
|
|
191
202
|
- **Persistence/config**: `storage/`, `configurator/`, and `operation_state_utils.py` provide storage abstractions, config loading, locks, bookmarks, progress, and cancellation.
|
|
192
203
|
|
|
193
204
|
### Orchestrator
|
|
@@ -478,7 +489,8 @@ Pre-computed embeddings passed to storage methods via `query_embedding` paramete
|
|
|
478
489
|
| File | Purpose |
|
|
479
490
|
|------|---------|
|
|
480
491
|
| `storage_base/` | BaseStorage interface split by domain (`_profiles.py`, `_playbook.py`, `_requests.py`, `_operations.py`, `_agent_run.py`, `_lineage.py`, `_shadow_verdicts.py`, `_stall_state.py`, `_share_links.py`) |
|
|
481
|
-
| `sqlite_storage/` | SQLite-backed implementation split across the same domains, including lineage/tombstone support in `_lineage.py` |
|
|
492
|
+
| `sqlite_storage/` | SQLite-backed implementation split across the same domains, including governance-aware retention/barrier handling and lineage/tombstone support in `_governance.py` / `_lineage.py` |
|
|
493
|
+
| `governance_validation.py` | Shared validation helpers for subject references and governance contracts before storage writes. |
|
|
482
494
|
| `retention.py`, `retention_mixin.py` | Data retention and cleanup helpers |
|
|
483
495
|
| `constants.py`, `error.py` | Storage constants and shared errors |
|
|
484
496
|
|
|
@@ -486,7 +498,7 @@ Pre-computed embeddings passed to storage methods via `query_embedding` paramete
|
|
|
486
498
|
|
|
487
499
|
**Key Methods**:
|
|
488
500
|
- CRUD: profiles, interactions, playbooks, results, requests, playbook aggregation change logs
|
|
489
|
-
- `get_sessions(offset, top_k, session_id)` → `dict[str, list[RequestInteractionDataModel]]` (groups by session_id
|
|
501
|
+
- `get_sessions(offset, top_k, session_id)` → `dict[str, list[RequestInteractionDataModel]]` (groups by session_id; paginates per-session — `top_k`/`offset` count sessions, and each returned session includes all of its requests)
|
|
490
502
|
- `get_rerun_user_ids(user_id, start_time, end_time, source, agent_version)` → `list[str]` - Get distinct user IDs matching filters for rerun workflows (pushes filtering to storage layer)
|
|
491
503
|
- `get_feedbacks(status_filter, feedback_status_filter)` - Filter by playbook status and approval status
|
|
492
504
|
- `save_feedbacks()` → returns `list[Feedback]` with `feedback_id` populated (callers can ignore return)
|