claude-smart 0.2.49 → 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 +10 -43
- package/bin/claude-smart.js +105 -0
- 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/pyproject.toml +1 -1
- package/plugin/scripts/_lib.sh +197 -1
- package/plugin/scripts/backend-python-runner.py +46 -0
- package/plugin/scripts/backend-service.sh +757 -119
- package/plugin/scripts/codex-hook.js +63 -225
- package/plugin/scripts/dashboard-open.sh +6 -4
- package/plugin/scripts/dashboard-service.sh +117 -136
- package/plugin/scripts/hook_entry.sh +3 -0
- package/plugin/scripts/smart-install.sh +15 -1
- package/plugin/src/claude_smart/cli.py +14 -0
- package/plugin/src/claude_smart/context_inject.py +3 -0
- package/plugin/src/claude_smart/env_config.py +4 -1
- 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 +1 -1
- 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/client/client.py +126 -3
- 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 +177 -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 +66 -3
- 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 +62 -5
- package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +19 -52
- 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 +20 -5
- 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/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/worker.py +85 -16
- 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_worker.py +66 -0
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +510 -63
- 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 +122 -44
- 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 +1 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +50 -0
- 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 +36 -1
- 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/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 +43 -0
- 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/_user.py +45 -7
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_interaction_store.py +146 -6
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_profile_store.py +42 -7
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +1 -1
- 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 +50 -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 +59 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_profile_store.py +52 -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
|
@@ -233,3 +233,31 @@ def test_safe_session_ids_accepted():
|
|
|
233
233
|
path = state.session_path(sid)
|
|
234
234
|
assert path is not None, f"safe id {sid!r} should have been accepted"
|
|
235
235
|
assert path.name == f"{sid}.jsonl"
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def test_to_wire_citations_preserves_explicit_playbook_source_kind() -> None:
|
|
239
|
+
wire = state._to_wire_citations(
|
|
240
|
+
[
|
|
241
|
+
{
|
|
242
|
+
"id": "s1-1",
|
|
243
|
+
"kind": "playbook",
|
|
244
|
+
"source_kind": "agent_playbook",
|
|
245
|
+
"real_id": "20",
|
|
246
|
+
"title": "Agent",
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"id": "s2-1",
|
|
250
|
+
"kind": "playbook",
|
|
251
|
+
"source_kind": "user_playbook",
|
|
252
|
+
"real_id": "101",
|
|
253
|
+
"title": "User",
|
|
254
|
+
},
|
|
255
|
+
{"id": "p1-1", "kind": "profile", "real_id": "p1", "title": "Profile"},
|
|
256
|
+
]
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
assert [item["kind"] for item in wire] == [
|
|
260
|
+
"agent_playbook",
|
|
261
|
+
"user_playbook",
|
|
262
|
+
"profile",
|
|
263
|
+
]
|
|
@@ -6,6 +6,8 @@ from reflexio.models.api_schema.retriever_schema import (
|
|
|
6
6
|
GetAgentSuccessEvaluationResultsResponse,
|
|
7
7
|
GetRequestsRequest,
|
|
8
8
|
GetRequestsResponse,
|
|
9
|
+
GetRetrievedLearningEvaluationResultsRequest,
|
|
10
|
+
GetRetrievedLearningEvaluationResultsResponse,
|
|
9
11
|
RequestData,
|
|
10
12
|
Session,
|
|
11
13
|
UnifiedSearchRequest,
|
|
@@ -72,6 +74,45 @@ class SearchMixin(ReflexioBase):
|
|
|
72
74
|
success=False, agent_success_evaluation_results=[], msg=str(e)
|
|
73
75
|
)
|
|
74
76
|
|
|
77
|
+
def get_retrieved_learning_evaluation_results(
|
|
78
|
+
self,
|
|
79
|
+
request: GetRetrievedLearningEvaluationResultsRequest | dict,
|
|
80
|
+
) -> GetRetrievedLearningEvaluationResultsResponse:
|
|
81
|
+
"""Get per-learning retrieved-learning evaluation verdicts.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
request (GetRetrievedLearningEvaluationResultsRequest | dict): The
|
|
85
|
+
read request (optional user/session filters + limit).
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
GetRetrievedLearningEvaluationResultsResponse: Matching verdicts
|
|
89
|
+
ordered by created_at DESC, result_id DESC.
|
|
90
|
+
"""
|
|
91
|
+
if not self._is_storage_configured():
|
|
92
|
+
return GetRetrievedLearningEvaluationResultsResponse(
|
|
93
|
+
success=True, results=[], msg=STORAGE_NOT_CONFIGURED_MSG
|
|
94
|
+
)
|
|
95
|
+
if isinstance(request, dict):
|
|
96
|
+
request = GetRetrievedLearningEvaluationResultsRequest(**request)
|
|
97
|
+
try:
|
|
98
|
+
results = self._get_storage().get_retrieved_learning_evaluation_results(
|
|
99
|
+
user_id=request.user_id,
|
|
100
|
+
session_id=request.session_id,
|
|
101
|
+
limit=request.limit,
|
|
102
|
+
)
|
|
103
|
+
return GetRetrievedLearningEvaluationResultsResponse(
|
|
104
|
+
success=True,
|
|
105
|
+
results=results,
|
|
106
|
+
msg=f"Found {len(results)} retrieved-learning evaluation result(s)",
|
|
107
|
+
)
|
|
108
|
+
except Exception:
|
|
109
|
+
_LOGGER.exception("Failed to read retrieved-learning evaluation results")
|
|
110
|
+
return GetRetrievedLearningEvaluationResultsResponse(
|
|
111
|
+
success=False,
|
|
112
|
+
results=[],
|
|
113
|
+
msg="Failed to read retrieved-learning evaluation results",
|
|
114
|
+
)
|
|
115
|
+
|
|
75
116
|
def get_requests(
|
|
76
117
|
self,
|
|
77
118
|
request: GetRequestsRequest | dict,
|
|
@@ -33,7 +33,11 @@ __all__ = [
|
|
|
33
33
|
"BlockingIssue",
|
|
34
34
|
"BlockingIssueKind",
|
|
35
35
|
"ToolUsed",
|
|
36
|
+
"CitationKind",
|
|
36
37
|
"Citation",
|
|
38
|
+
"RetrievedLearningKind",
|
|
39
|
+
"RetrievedLearning",
|
|
40
|
+
"LearningImpact",
|
|
37
41
|
"Interaction",
|
|
38
42
|
"Request",
|
|
39
43
|
"UserProfile",
|
|
@@ -41,6 +45,7 @@ __all__ = [
|
|
|
41
45
|
"ProfileChangeLog",
|
|
42
46
|
"AgentPlaybook",
|
|
43
47
|
"AgentSuccessEvaluationResult",
|
|
48
|
+
"RetrievedLearningEvaluationResult",
|
|
44
49
|
"DeleteUserProfileRequest",
|
|
45
50
|
"DeleteUserProfileResponse",
|
|
46
51
|
"DeleteUserInteractionRequest",
|
|
@@ -130,6 +135,8 @@ __all__ = [
|
|
|
130
135
|
# Data Models
|
|
131
136
|
# ===============================
|
|
132
137
|
|
|
138
|
+
type CitationKind = Literal["playbook", "profile", "user_playbook", "agent_playbook"]
|
|
139
|
+
|
|
133
140
|
|
|
134
141
|
class Citation(BaseModel):
|
|
135
142
|
"""A playbook or profile item the agent cited as influential.
|
|
@@ -141,22 +148,52 @@ class Citation(BaseModel):
|
|
|
141
148
|
it was applied?).
|
|
142
149
|
|
|
143
150
|
Attributes:
|
|
144
|
-
kind (
|
|
145
|
-
|
|
151
|
+
kind (CitationKind): Which kind of cited item this references.
|
|
152
|
+
``"playbook"`` is the legacy compatibility value.
|
|
153
|
+
``"user_playbook"`` is the direct tuner target.
|
|
154
|
+
``"agent_playbook"`` references an org-level playbook row.
|
|
155
|
+
``"profile"`` references a user profile row.
|
|
146
156
|
real_id (str): Stable storage id — ``user_playbook_id`` for
|
|
147
|
-
playbooks, ``
|
|
157
|
+
user playbooks, ``agent_playbook_id`` for agent playbooks,
|
|
158
|
+
and ``profile_id`` for profiles.
|
|
148
159
|
tag (str): Injection-time rank tag (e.g. ``"r1-301"``,
|
|
149
160
|
``"p1-0f37"``). Per-injection, not stable across sessions;
|
|
150
161
|
kept as a debug aid.
|
|
151
162
|
title (str): Short human-readable label for logs and UI.
|
|
152
163
|
"""
|
|
153
164
|
|
|
154
|
-
kind:
|
|
165
|
+
kind: CitationKind
|
|
155
166
|
real_id: str
|
|
156
167
|
tag: str = ""
|
|
157
168
|
title: str = ""
|
|
158
169
|
|
|
159
170
|
|
|
171
|
+
# Canonical kinds accepted and persisted by retrieved-learning evaluation.
|
|
172
|
+
# Unlike ``Citation.kind`` there is no legacy ``"playbook"`` alias.
|
|
173
|
+
type RetrievedLearningKind = Literal["profile", "user_playbook", "agent_playbook"]
|
|
174
|
+
|
|
175
|
+
type LearningImpact = Literal["positive", "negative", "neutral"]
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class RetrievedLearning(BaseModel):
|
|
179
|
+
"""A learning the caller retrieved and injected into the agent context.
|
|
180
|
+
|
|
181
|
+
Deliberately minimal — just the identity pair. It does NOT reuse
|
|
182
|
+
``Citation``: citations carry injection-time debug fields (``tag``,
|
|
183
|
+
``title``) that callers should not need to supply (or see) when declaring
|
|
184
|
+
what was retrieved.
|
|
185
|
+
|
|
186
|
+
Attributes:
|
|
187
|
+
kind (RetrievedLearningKind): Which kind of learning this references.
|
|
188
|
+
learning_id (str): Stable storage id — ``profile_id`` for profiles,
|
|
189
|
+
``user_playbook_id`` for user playbooks, ``agent_playbook_id``
|
|
190
|
+
for agent playbooks (numeric ids as decimal strings).
|
|
191
|
+
"""
|
|
192
|
+
|
|
193
|
+
kind: RetrievedLearningKind
|
|
194
|
+
learning_id: str = Field(min_length=1, max_length=1_000)
|
|
195
|
+
|
|
196
|
+
|
|
160
197
|
# information about the user interaction sent by the client
|
|
161
198
|
class Interaction(BaseModel):
|
|
162
199
|
interaction_id: int = 0 # 0 = placeholder for DB auto-increment
|
|
@@ -173,6 +210,10 @@ class Interaction(BaseModel):
|
|
|
173
210
|
expert_content: str = ""
|
|
174
211
|
tools_used: list[ToolUsed] = Field(default_factory=list)
|
|
175
212
|
citations: list[Citation] = Field(default_factory=list)
|
|
213
|
+
# Every learning retrieved and injected for this turn — including ones
|
|
214
|
+
# that did not end up influencing the response (contrast: ``citations``
|
|
215
|
+
# is the agent's claim of influence).
|
|
216
|
+
retrieved_learnings: list[RetrievedLearning] = Field(default_factory=list)
|
|
176
217
|
embedding: EmbeddingVector = []
|
|
177
218
|
|
|
178
219
|
@field_validator("interacted_image_url", mode="after")
|
|
@@ -399,17 +440,107 @@ class AgentSuccessEvaluationResult(BaseModel):
|
|
|
399
440
|
embedding: EmbeddingVector = []
|
|
400
441
|
|
|
401
442
|
|
|
443
|
+
class RetrievedLearningEvaluationResult(BaseModel):
|
|
444
|
+
"""Latest per-learning relevance/impact verdict for one evaluated session.
|
|
445
|
+
|
|
446
|
+
One row per ``(user_id, session_id, kind, learning_id)``. The table holds
|
|
447
|
+
the most recent successfully persisted evaluation set for a session, not
|
|
448
|
+
an append-only history.
|
|
449
|
+
|
|
450
|
+
Attributes:
|
|
451
|
+
result_id (int): DB auto-increment identifier (0 = placeholder).
|
|
452
|
+
user_id (str): Session owner.
|
|
453
|
+
session_id (str): Evaluated session.
|
|
454
|
+
agent_version (str): Version supplied to group evaluation;
|
|
455
|
+
informational, not part of the uniqueness key.
|
|
456
|
+
kind (RetrievedLearningKind): The learning kind.
|
|
457
|
+
learning_id (str): Stable storage id, matching
|
|
458
|
+
``RetrievedLearning.learning_id``.
|
|
459
|
+
is_relevant (bool | None): Whether the learning applies to the
|
|
460
|
+
session. ``None`` only when the relevance judge/chunk failed.
|
|
461
|
+
relevance_reason (str): Judge reasoning; empty when ``is_relevant``
|
|
462
|
+
is ``None``.
|
|
463
|
+
impact (LearningImpact | None): Whether the learning improved,
|
|
464
|
+
harmed, or did not materially change the response. ``None`` only
|
|
465
|
+
when the impact judge/chunk failed.
|
|
466
|
+
impact_reason (str): Judge reasoning; empty when ``impact`` is
|
|
467
|
+
``None``.
|
|
468
|
+
created_at (int): Earliest request timestamp in the evaluated
|
|
469
|
+
session.
|
|
470
|
+
"""
|
|
471
|
+
|
|
472
|
+
result_id: int = 0
|
|
473
|
+
user_id: str
|
|
474
|
+
session_id: str
|
|
475
|
+
agent_version: str = ""
|
|
476
|
+
kind: RetrievedLearningKind
|
|
477
|
+
learning_id: str
|
|
478
|
+
is_relevant: bool | None = None
|
|
479
|
+
relevance_reason: str = ""
|
|
480
|
+
impact: LearningImpact | None = None
|
|
481
|
+
impact_reason: str = ""
|
|
482
|
+
created_at: int = Field(default_factory=lambda: int(datetime.now(UTC).timestamp()))
|
|
483
|
+
|
|
484
|
+
|
|
402
485
|
class PlaybookRetrievalLogItem(BaseModel):
|
|
403
|
-
"""One retrieved
|
|
486
|
+
"""One retrieved playbook plus the serve-time attribution snapshot.
|
|
487
|
+
|
|
488
|
+
``agent_playbook_id`` remains as a nullable legacy compatibility field for
|
|
489
|
+
old agent-only rows. New rows should prefer ``target_kind`` + ``target_id``.
|
|
490
|
+
"""
|
|
404
491
|
|
|
405
492
|
retrieval_log_item_id: int = 0
|
|
406
493
|
retrieval_log_id: int = 0
|
|
407
494
|
ordinal: int
|
|
408
|
-
agent_playbook_id: int
|
|
495
|
+
agent_playbook_id: int | None = Field(default=None, gt=0)
|
|
496
|
+
target_kind: Literal["agent_playbook", "user_playbook"] | None = None
|
|
497
|
+
target_id: int | None = Field(default=None, gt=0)
|
|
498
|
+
target_title: str = ""
|
|
499
|
+
target_content: str = ""
|
|
500
|
+
target_trigger: str | None = None
|
|
409
501
|
source_user_playbook_ids: list[int] = Field(default_factory=list)
|
|
410
502
|
source_interaction_ids_by_user_playbook_id: dict[str, list[int]] = Field(
|
|
411
503
|
default_factory=dict
|
|
412
504
|
)
|
|
505
|
+
source_window_snapshot_mode: Literal["final_response", "live_lookup_compat"] = (
|
|
506
|
+
"live_lookup_compat"
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
@model_validator(mode="after")
|
|
510
|
+
def backfill_legacy_target_fields(self) -> Self:
|
|
511
|
+
if self.target_kind is None:
|
|
512
|
+
if self.agent_playbook_id is None:
|
|
513
|
+
raise ValueError(
|
|
514
|
+
"PlaybookRetrievalLogItem requires target_kind/target_id or"
|
|
515
|
+
" legacy agent_playbook_id"
|
|
516
|
+
)
|
|
517
|
+
self.target_kind = "agent_playbook"
|
|
518
|
+
self.target_id = self.agent_playbook_id
|
|
519
|
+
return self
|
|
520
|
+
|
|
521
|
+
if self.target_id is None:
|
|
522
|
+
if self.target_kind != "agent_playbook" or self.agent_playbook_id is None:
|
|
523
|
+
raise ValueError(
|
|
524
|
+
"PlaybookRetrievalLogItem.target_id is required for explicit"
|
|
525
|
+
" targets"
|
|
526
|
+
)
|
|
527
|
+
self.target_id = self.agent_playbook_id
|
|
528
|
+
|
|
529
|
+
if self.target_kind == "user_playbook":
|
|
530
|
+
if self.agent_playbook_id is not None:
|
|
531
|
+
raise ValueError(
|
|
532
|
+
"user_playbook retrieval-log items cannot carry agent_playbook_id"
|
|
533
|
+
)
|
|
534
|
+
return self
|
|
535
|
+
|
|
536
|
+
if self.agent_playbook_id is None:
|
|
537
|
+
self.agent_playbook_id = self.target_id
|
|
538
|
+
elif self.agent_playbook_id != self.target_id:
|
|
539
|
+
raise ValueError(
|
|
540
|
+
"agent_playbook retrieval-log item agent_playbook_id must match"
|
|
541
|
+
" target_id"
|
|
542
|
+
)
|
|
543
|
+
return self
|
|
413
544
|
|
|
414
545
|
|
|
415
546
|
class PlaybookRetrievalLog(BaseModel):
|
|
@@ -599,19 +730,19 @@ class BulkDeleteResponse(BaseModel):
|
|
|
599
730
|
|
|
600
731
|
|
|
601
732
|
class DeleteRequestsByIdsRequest(BaseModel):
|
|
602
|
-
request_ids: list[str] = Field(min_length=1)
|
|
733
|
+
request_ids: list[str] = Field(min_length=1, max_length=10_000)
|
|
603
734
|
|
|
604
735
|
|
|
605
736
|
class DeleteProfilesByIdsRequest(BaseModel):
|
|
606
|
-
profile_ids: list[str] = Field(min_length=1)
|
|
737
|
+
profile_ids: list[str] = Field(min_length=1, max_length=10_000)
|
|
607
738
|
|
|
608
739
|
|
|
609
740
|
class DeleteAgentPlaybooksByIdsRequest(BaseModel):
|
|
610
|
-
agent_playbook_ids: list[int] = Field(min_length=1)
|
|
741
|
+
agent_playbook_ids: list[int] = Field(min_length=1, max_length=10_000)
|
|
611
742
|
|
|
612
743
|
|
|
613
744
|
class DeleteUserPlaybooksByIdsRequest(BaseModel):
|
|
614
|
-
user_playbook_ids: list[int] = Field(min_length=1)
|
|
745
|
+
user_playbook_ids: list[int] = Field(min_length=1, max_length=10_000)
|
|
615
746
|
|
|
616
747
|
|
|
617
748
|
# Clear all data scoped to a single user_id (interactions, requests, user
|
|
@@ -632,16 +763,25 @@ class ClearUserDataResponse(BaseModel):
|
|
|
632
763
|
# user provided interaction data from the request
|
|
633
764
|
class InteractionData(BaseModel):
|
|
634
765
|
created_at: int = Field(default_factory=lambda: int(datetime.now(UTC).timestamp()))
|
|
635
|
-
role: str = "User"
|
|
636
|
-
content: str = ""
|
|
637
|
-
shadow_content: str = ""
|
|
638
|
-
expert_content: str = ""
|
|
766
|
+
role: str = Field(default="User", max_length=1_000)
|
|
767
|
+
content: str = Field(default="", max_length=1_000_000)
|
|
768
|
+
shadow_content: str = Field(default="", max_length=1_000_000)
|
|
769
|
+
expert_content: str = Field(default="", max_length=1_000_000)
|
|
639
770
|
user_action: UserActionType = UserActionType.NONE
|
|
640
|
-
user_action_description: str = ""
|
|
641
|
-
interacted_image_url: str = ""
|
|
642
|
-
image_encoding: str =
|
|
643
|
-
|
|
644
|
-
|
|
771
|
+
user_action_description: str = Field(default="", max_length=10_000)
|
|
772
|
+
interacted_image_url: str = Field(default="", max_length=2_048)
|
|
773
|
+
image_encoding: str = Field(
|
|
774
|
+
default="", max_length=15_000_000
|
|
775
|
+
) # base64 encoded image
|
|
776
|
+
tools_used: list[ToolUsed] = Field(default_factory=list, max_length=1_000)
|
|
777
|
+
citations: list[Citation] = Field(default_factory=list, max_length=1_000)
|
|
778
|
+
# Learnings (profiles / user playbooks / agent playbooks) the caller
|
|
779
|
+
# retrieved and injected into the agent context for this turn. Distinct
|
|
780
|
+
# from ``citations`` (agent-claimed influence): this is everything that
|
|
781
|
+
# was injected, whether or not it helped.
|
|
782
|
+
retrieved_learnings: list[RetrievedLearning] = Field(
|
|
783
|
+
default_factory=list, max_length=1_000
|
|
784
|
+
)
|
|
645
785
|
|
|
646
786
|
@field_validator("interacted_image_url", mode="after")
|
|
647
787
|
@classmethod
|
|
@@ -653,11 +793,10 @@ class InteractionData(BaseModel):
|
|
|
653
793
|
class PublishUserInteractionRequest(BaseModel):
|
|
654
794
|
request_id: NonEmptyStr | None = None
|
|
655
795
|
user_id: NonEmptyStr
|
|
656
|
-
interaction_data_list: list[InteractionData] = Field(min_length=1)
|
|
657
|
-
source: str = ""
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
)
|
|
796
|
+
interaction_data_list: list[InteractionData] = Field(min_length=1, max_length=1_000)
|
|
797
|
+
source: str = Field(default="", max_length=1_000)
|
|
798
|
+
# this is used for aggregating interactions for generating agent playbooks
|
|
799
|
+
agent_version: str = Field(default="", max_length=1_000)
|
|
661
800
|
session_id: NonEmptyStr # used for grouping requests together
|
|
662
801
|
skip_aggregation: bool = (
|
|
663
802
|
False # when True, extract profiles/playbooks but skip aggregation
|
|
@@ -674,6 +813,19 @@ class PublishUserInteractionRequest(BaseModel):
|
|
|
674
813
|
raise ValueError("evaluation_only publishes require session_id")
|
|
675
814
|
return self
|
|
676
815
|
|
|
816
|
+
@model_validator(mode="after")
|
|
817
|
+
def validate_retrieved_learnings_total(self) -> Self:
|
|
818
|
+
total = sum(
|
|
819
|
+
len(interaction.retrieved_learnings)
|
|
820
|
+
for interaction in self.interaction_data_list
|
|
821
|
+
)
|
|
822
|
+
if total > 1_000:
|
|
823
|
+
raise ValueError(
|
|
824
|
+
"a publish request may carry at most 1000 retrieved_learnings"
|
|
825
|
+
f" across all interactions (got {total})"
|
|
826
|
+
)
|
|
827
|
+
return self
|
|
828
|
+
|
|
677
829
|
|
|
678
830
|
# publish user interaction response
|
|
679
831
|
class PublishUserInteractionResponse(BaseModel):
|
|
@@ -730,7 +882,7 @@ class MyConfigResponse(BaseModel):
|
|
|
730
882
|
|
|
731
883
|
# add user playbook request/response
|
|
732
884
|
class AddUserPlaybookRequest(BaseModel):
|
|
733
|
-
user_playbooks: list[UserPlaybook] = Field(min_length=1)
|
|
885
|
+
user_playbooks: list[UserPlaybook] = Field(min_length=1, max_length=1_000)
|
|
734
886
|
|
|
735
887
|
@model_validator(mode="after")
|
|
736
888
|
def check_content_fields(self) -> Self:
|
|
@@ -11,6 +11,7 @@ from typing import Literal, Self
|
|
|
11
11
|
|
|
12
12
|
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
13
13
|
|
|
14
|
+
from reflexio.models.api_schema.domain import CitationKind
|
|
14
15
|
from reflexio.models.api_schema.validators import NonEmptyStr
|
|
15
16
|
from reflexio.models.structured_output import StrictStructuredOutput
|
|
16
17
|
|
|
@@ -79,7 +80,7 @@ class RuleAttributionRow(BaseModel):
|
|
|
79
80
|
"""One row in the "rules that moved the needle" panel."""
|
|
80
81
|
|
|
81
82
|
rule_id: str
|
|
82
|
-
kind:
|
|
83
|
+
kind: CitationKind
|
|
83
84
|
title: str = ""
|
|
84
85
|
successes_with: int = Field(ge=0)
|
|
85
86
|
failures_with: int = Field(ge=0)
|
|
@@ -416,12 +417,17 @@ class GradeOnDemandResponse(BaseModel):
|
|
|
416
417
|
window. False on a fresh grade.
|
|
417
418
|
skipped_reason (str | None): If grading was skipped, the reason
|
|
418
419
|
(e.g., "NO_REQUESTS"). None on success.
|
|
420
|
+
retrieved_learning_status (str | None): Outcome of the
|
|
421
|
+
retrieved-learning evaluation for this session (e.g.
|
|
422
|
+
"complete", "degraded", "failed", "not_applicable"). None on
|
|
423
|
+
legacy cache entries and skipped grades.
|
|
419
424
|
"""
|
|
420
425
|
|
|
421
426
|
session_id: str
|
|
422
427
|
result_id: int | None = None
|
|
423
428
|
cached: bool = False
|
|
424
429
|
skipped_reason: str | None = None
|
|
430
|
+
retrieved_learning_status: str | None = None
|
|
425
431
|
|
|
426
432
|
|
|
427
433
|
# ---------------------------------------------------------------------------
|
|
@@ -6,6 +6,7 @@ from typing import NamedTuple
|
|
|
6
6
|
|
|
7
7
|
from pydantic import BaseModel
|
|
8
8
|
|
|
9
|
+
from .domain import CitationKind
|
|
9
10
|
from .service_schemas import Interaction, Request
|
|
10
11
|
|
|
11
12
|
|
|
@@ -42,6 +43,6 @@ class SessionCitation(NamedTuple):
|
|
|
42
43
|
|
|
43
44
|
user_id: str
|
|
44
45
|
session_id: str
|
|
45
|
-
kind:
|
|
46
|
+
kind: CitationKind
|
|
46
47
|
real_id: str
|
|
47
48
|
title: str
|
|
@@ -6,12 +6,15 @@ from typing import Literal, Self
|
|
|
6
6
|
from pydantic import BaseModel, Field, model_validator
|
|
7
7
|
|
|
8
8
|
from ..config_schema import SearchMode
|
|
9
|
+
from ..structured_output import StrictStructuredOutput
|
|
10
|
+
from .domain import CitationKind
|
|
9
11
|
from .service_schemas import (
|
|
10
12
|
AgentPlaybook,
|
|
11
13
|
AgentSuccessEvaluationResult,
|
|
12
14
|
Interaction,
|
|
13
15
|
PlaybookStatus,
|
|
14
16
|
Request,
|
|
17
|
+
RetrievedLearningEvaluationResult,
|
|
15
18
|
Status,
|
|
16
19
|
UserPlaybook,
|
|
17
20
|
UserProfile,
|
|
@@ -433,6 +436,26 @@ class GetAgentSuccessEvaluationResultsResponse(BaseModel):
|
|
|
433
436
|
msg: str | None = None
|
|
434
437
|
|
|
435
438
|
|
|
439
|
+
class GetRetrievedLearningEvaluationResultsRequest(BaseModel):
|
|
440
|
+
"""Read per-learning retrieved-learning evaluation verdicts.
|
|
441
|
+
|
|
442
|
+
Attributes:
|
|
443
|
+
user_id (str | None): Filter by session owner.
|
|
444
|
+
session_id (str | None): Filter by session.
|
|
445
|
+
limit (int): Maximum rows, ordered by created_at DESC, result_id DESC.
|
|
446
|
+
"""
|
|
447
|
+
|
|
448
|
+
user_id: str | None = None
|
|
449
|
+
session_id: str | None = None
|
|
450
|
+
limit: int = Field(default=100, ge=1, le=1_000)
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
class GetRetrievedLearningEvaluationResultsResponse(BaseModel):
|
|
454
|
+
success: bool
|
|
455
|
+
results: list[RetrievedLearningEvaluationResult] = Field(default_factory=list)
|
|
456
|
+
msg: str | None = None
|
|
457
|
+
|
|
458
|
+
|
|
436
459
|
class GetRequestsRequest(BaseModel):
|
|
437
460
|
user_id: str | None = None
|
|
438
461
|
request_id: str | None = None
|
|
@@ -593,8 +616,10 @@ class PlaybookApplicationStat(BaseModel):
|
|
|
593
616
|
real_id (str): Stable id of the cited item — ``user_playbook_id``,
|
|
594
617
|
``agent_playbook_id``, or ``profile_id`` (always serialized as a
|
|
595
618
|
string).
|
|
596
|
-
kind (
|
|
597
|
-
|
|
619
|
+
kind (CitationKind): Citation kind recorded on
|
|
620
|
+
``Interaction.citations``. ``"playbook"`` is the legacy
|
|
621
|
+
compatibility value; ``"user_playbook"`` is the explicit
|
|
622
|
+
direct-tuner target.
|
|
598
623
|
title (str): Human-readable label for the rule. Empty string when
|
|
599
624
|
the underlying row has been deleted but old citations remain.
|
|
600
625
|
applied_count (int): Number of interactions in the window whose
|
|
@@ -609,7 +634,7 @@ class PlaybookApplicationStat(BaseModel):
|
|
|
609
634
|
"""
|
|
610
635
|
|
|
611
636
|
real_id: str
|
|
612
|
-
kind:
|
|
637
|
+
kind: CitationKind
|
|
613
638
|
title: str = ""
|
|
614
639
|
applied_count: int = Field(ge=0)
|
|
615
640
|
last_applied_at: int | None = None
|
|
@@ -659,15 +684,36 @@ class ConversationTurn(BaseModel):
|
|
|
659
684
|
content: NonEmptyStr
|
|
660
685
|
|
|
661
686
|
|
|
662
|
-
class ReformulationResult(
|
|
687
|
+
class ReformulationResult(StrictStructuredOutput):
|
|
663
688
|
"""Output of the query reformulation pipeline.
|
|
664
689
|
|
|
690
|
+
Besides the rewritten query, carries the query's TEMPORAL SIGNALS so the
|
|
691
|
+
search pipeline can be time-sensitive without any additional LLM call
|
|
692
|
+
(the reformulation call already runs before retrieval when
|
|
693
|
+
``enable_reformulation`` is set). Time windows are relative day offsets
|
|
694
|
+
(never absolute dates) so results don't rot with calendar time.
|
|
695
|
+
|
|
665
696
|
Args:
|
|
666
697
|
standalone_query (str): Clean, normalized natural language query with
|
|
667
698
|
conversation context resolved, abbreviations expanded, grammar fixed.
|
|
699
|
+
start_days_ago (float, optional): Older bound of a query time window
|
|
700
|
+
("in the last 7 days" → 7).
|
|
701
|
+
end_days_ago (float, optional): Newer bound ("before this month" →
|
|
702
|
+
~30, with no start bound).
|
|
703
|
+
recency_dominant (bool): The query asks for the CURRENT/LATEST value —
|
|
704
|
+
final ordering becomes timestamp-based.
|
|
705
|
+
wants_current (bool): Present-tense question about a mutable
|
|
706
|
+
fact/policy — near-duplicate competing facts collapse to the
|
|
707
|
+
freshest, while relevance ordering is otherwise preserved.
|
|
708
|
+
(Superseded/TTL-expired rows never reach results: storage search
|
|
709
|
+
excludes tombstone statuses and expired profiles at SQL level.)
|
|
668
710
|
"""
|
|
669
711
|
|
|
670
712
|
standalone_query: str
|
|
713
|
+
start_days_ago: float | None = Field(default=None, ge=0)
|
|
714
|
+
end_days_ago: float | None = Field(default=None, ge=0)
|
|
715
|
+
recency_dominant: bool = False
|
|
716
|
+
wants_current: bool = False
|
|
671
717
|
|
|
672
718
|
|
|
673
719
|
# ===============================
|
|
@@ -712,6 +758,9 @@ class UnifiedSearchRequest(BaseModel):
|
|
|
712
758
|
search_mode: SearchMode = SearchMode.HYBRID
|
|
713
759
|
# Caller correlation IDs for billing attribution on the Application line.
|
|
714
760
|
# Optional; consumed by _meter_applied_learnings in server/api.py.
|
|
761
|
+
# ``session_id`` additionally enables session-scoped result dedup: items
|
|
762
|
+
# already served to the same (org, session) are skipped and the next-best
|
|
763
|
+
# matches backfilled (see server/services/retrieval/session_dedup.py).
|
|
715
764
|
request_id: str | None = None
|
|
716
765
|
session_id: str | None = None
|
|
717
766
|
interaction_id: int | None = Field(default=None, gt=0)
|
|
@@ -729,6 +778,14 @@ class UnifiedSearchResponse(BaseModel):
|
|
|
729
778
|
msg (str, optional): Additional message
|
|
730
779
|
agent_answer (str, optional): LLM-synthesised answer populated by the agentic backend;
|
|
731
780
|
None for classic backend.
|
|
781
|
+
degraded (bool): True when the search silently fell back from the
|
|
782
|
+
requested vector/hybrid mode to full-text search because query
|
|
783
|
+
embedding generation failed. Results are still returned (via FTS),
|
|
784
|
+
but relevance may be lower than a healthy vector/hybrid run.
|
|
785
|
+
Defaults to False.
|
|
786
|
+
search_mode_effective (str, optional): The search mode actually used
|
|
787
|
+
when it differs from the requested mode — currently ``"fts"`` on
|
|
788
|
+
the degrade path. None when the requested mode was honored.
|
|
732
789
|
"""
|
|
733
790
|
|
|
734
791
|
success: bool
|
|
@@ -740,6 +797,8 @@ class UnifiedSearchResponse(BaseModel):
|
|
|
740
797
|
agent_answer: str | None = None
|
|
741
798
|
agent_trace: str | None = None
|
|
742
799
|
rehydrated_text: str | None = None
|
|
800
|
+
degraded: bool = False
|
|
801
|
+
search_mode_effective: str | None = None
|
|
743
802
|
|
|
744
803
|
|
|
745
804
|
# ===============================
|
|
@@ -53,6 +53,7 @@ def to_interaction_view(interaction: Interaction) -> InteractionView:
|
|
|
53
53
|
shadow_content=interaction.shadow_content,
|
|
54
54
|
expert_content=interaction.expert_content,
|
|
55
55
|
tools_used=interaction.tools_used,
|
|
56
|
+
retrieved_learnings=interaction.retrieved_learnings,
|
|
56
57
|
)
|
|
57
58
|
|
|
58
59
|
|
|
@@ -5,6 +5,7 @@ from datetime import UTC, datetime
|
|
|
5
5
|
from pydantic import BaseModel, Field, field_validator
|
|
6
6
|
|
|
7
7
|
from ..common import NEVER_EXPIRES_TIMESTAMP, ToolUsed
|
|
8
|
+
from ..domain.entities import RetrievedLearning
|
|
8
9
|
from ..validators import _validate_image_url
|
|
9
10
|
from .enums import (
|
|
10
11
|
PlaybookStatus,
|
|
@@ -29,7 +30,12 @@ __all__ = [
|
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
class InteractionView(BaseModel):
|
|
32
|
-
"""User-facing Interaction — excludes embedding and image_encoding.
|
|
33
|
+
"""User-facing Interaction — excludes embedding and image_encoding.
|
|
34
|
+
|
|
35
|
+
``retrieved_learnings`` (caller-supplied at publish) is exposed so callers
|
|
36
|
+
can round-trip what they attached; ``citations`` (agent-claimed influence,
|
|
37
|
+
server-side signal) is deliberately NOT exposed on this view.
|
|
38
|
+
"""
|
|
33
39
|
|
|
34
40
|
interaction_id: int = 0
|
|
35
41
|
user_id: str
|
|
@@ -43,6 +49,7 @@ class InteractionView(BaseModel):
|
|
|
43
49
|
shadow_content: str = ""
|
|
44
50
|
expert_content: str = ""
|
|
45
51
|
tools_used: list[ToolUsed] = Field(default_factory=list)
|
|
52
|
+
retrieved_learnings: list[RetrievedLearning] = Field(default_factory=list)
|
|
46
53
|
|
|
47
54
|
@field_validator("interacted_image_url", mode="after")
|
|
48
55
|
@classmethod
|
|
@@ -914,7 +914,7 @@ class Config(BaseModel):
|
|
|
914
914
|
),
|
|
915
915
|
)
|
|
916
916
|
shadow_comparison_judge_prompt_version: NonEmptyStr = Field(
|
|
917
|
-
default="v1.
|
|
917
|
+
default="v1.1.0",
|
|
918
918
|
description=(
|
|
919
919
|
"F1: pinned judge prompt version for per-turn shadow comparison. "
|
|
920
920
|
"Verdicts are stored with the version that produced them; the "
|