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
|
@@ -7,8 +7,13 @@ Each Claude Code session gets one file at
|
|
|
7
7
|
- ``{"role": "Assistant", ...}`` — a finalized assistant turn
|
|
8
8
|
- ``{"role": "Assistant_tool", ...}`` — a single tool invocation, attached
|
|
9
9
|
to the next assistant turn at ``Stop`` time
|
|
10
|
-
- ``{"published_up_to": N}`` — high-water mark so Stop /
|
|
11
|
-
re-publish rows already sent to reflexio
|
|
10
|
+
- ``{"published_up_to": N, "request_id": "..."}`` — high-water mark so Stop /
|
|
11
|
+
SessionEnd don't re-publish rows already sent to reflexio; a confirmed
|
|
12
|
+
request ID preserves local learning lineage for the dashboard when available
|
|
13
|
+
- ``{"retrieved_learning_refs": [...]}`` — identity pairs shown to the agent,
|
|
14
|
+
attached by event order to the next Assistant turn
|
|
15
|
+
- ``{"publish_attempt": {"start": N, "end": M}}`` — frozen retry boundary for
|
|
16
|
+
one in-flight publish
|
|
12
17
|
|
|
13
18
|
The buffer exists for offline resilience: when reflexio is unreachable,
|
|
14
19
|
Stop appends without publishing and the next successful hook drains.
|
|
@@ -19,8 +24,9 @@ from __future__ import annotations
|
|
|
19
24
|
import json
|
|
20
25
|
import logging
|
|
21
26
|
import os
|
|
27
|
+
from collections.abc import Iterable
|
|
22
28
|
from pathlib import Path
|
|
23
|
-
from typing import Any
|
|
29
|
+
from typing import Any
|
|
24
30
|
|
|
25
31
|
try:
|
|
26
32
|
import fcntl # POSIX only — Windows hooks fall back to append-without-lock.
|
|
@@ -34,7 +40,12 @@ _DEFAULT_STATE_DIR = Path.home() / ".claude-smart" / "sessions"
|
|
|
34
40
|
|
|
35
41
|
_TOOL_DATA_FIELD_MAX_LEN = 256
|
|
36
42
|
|
|
37
|
-
_VALID_CITATION_KINDS = frozenset(
|
|
43
|
+
_VALID_CITATION_KINDS = frozenset(
|
|
44
|
+
{"playbook", "profile", "user_playbook", "agent_playbook"}
|
|
45
|
+
)
|
|
46
|
+
_VALID_RETRIEVED_PLAYBOOK_KINDS = frozenset({"user_playbook", "agent_playbook"})
|
|
47
|
+
_VALID_RETRIEVED_KINDS = _VALID_RETRIEVED_PLAYBOOK_KINDS | {"profile"}
|
|
48
|
+
_RETRIEVED_LEARNINGS_PUBLISH_CAP = 1000
|
|
38
49
|
|
|
39
50
|
|
|
40
51
|
def _truncate_tool_data_field(value: Any) -> Any:
|
|
@@ -81,17 +92,37 @@ def injected_path(session_id: str) -> Path:
|
|
|
81
92
|
return state_dir() / f"{session_id}.injected.jsonl"
|
|
82
93
|
|
|
83
94
|
|
|
95
|
+
def _normalize_registry_ref(entry: dict[str, Any]) -> dict[str, str] | None:
|
|
96
|
+
"""Return the identity pair carried by one citation-registry entry."""
|
|
97
|
+
|
|
98
|
+
learning_id = entry.get("real_id")
|
|
99
|
+
kind = entry.get("kind")
|
|
100
|
+
if not isinstance(learning_id, str) or not learning_id:
|
|
101
|
+
return None
|
|
102
|
+
if kind == "profile":
|
|
103
|
+
wire_kind = "profile"
|
|
104
|
+
elif (
|
|
105
|
+
kind == "playbook"
|
|
106
|
+
and entry.get("source_kind") in _VALID_RETRIEVED_PLAYBOOK_KINDS
|
|
107
|
+
):
|
|
108
|
+
wire_kind = entry["source_kind"]
|
|
109
|
+
else:
|
|
110
|
+
return None
|
|
111
|
+
return {"kind": wire_kind, "learning_id": learning_id}
|
|
112
|
+
|
|
113
|
+
|
|
84
114
|
def append_injected(session_id: str, entries: Iterable[dict[str, Any]]) -> None:
|
|
85
|
-
"""
|
|
115
|
+
"""Record injected items for citations and the next Assistant publish.
|
|
86
116
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
Silently no-ops when ``entries`` is empty.
|
|
117
|
+
The citation sidecar keeps the display metadata used by Stop. The ordered
|
|
118
|
+
session event stores identity pairs only, so publishing never has to join
|
|
119
|
+
two files or infer event order from timestamps.
|
|
91
120
|
"""
|
|
121
|
+
|
|
92
122
|
records = list(entries)
|
|
93
123
|
if not records:
|
|
94
124
|
return
|
|
125
|
+
|
|
95
126
|
path = injected_path(session_id)
|
|
96
127
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
97
128
|
with path.open("a", encoding="utf-8") as fh:
|
|
@@ -100,8 +131,22 @@ def append_injected(session_id: str, entries: Iterable[dict[str, Any]]) -> None:
|
|
|
100
131
|
fcntl.flock(fh.fileno(), fcntl.LOCK_EX)
|
|
101
132
|
except OSError as exc:
|
|
102
133
|
_LOGGER.debug("flock failed on %s: %s", path, exc)
|
|
103
|
-
for
|
|
104
|
-
fh.write(json.dumps(
|
|
134
|
+
for record in records:
|
|
135
|
+
fh.write(json.dumps(record, ensure_ascii=False) + "\n")
|
|
136
|
+
|
|
137
|
+
refs: list[dict[str, str]] = []
|
|
138
|
+
seen: set[tuple[str, str]] = set()
|
|
139
|
+
for record in records:
|
|
140
|
+
ref = _normalize_registry_ref(record)
|
|
141
|
+
if ref is None:
|
|
142
|
+
continue
|
|
143
|
+
key = (ref["kind"], ref["learning_id"])
|
|
144
|
+
if key in seen:
|
|
145
|
+
continue
|
|
146
|
+
seen.add(key)
|
|
147
|
+
refs.append(ref)
|
|
148
|
+
if refs:
|
|
149
|
+
append(session_id, {"retrieved_learning_refs": refs})
|
|
105
150
|
|
|
106
151
|
|
|
107
152
|
def read_injected(session_id: str) -> dict[str, dict[str, Any]]:
|
|
@@ -111,6 +156,7 @@ def read_injected(session_id: str) -> dict[str, dict[str, Any]]:
|
|
|
111
156
|
(identical content produces the same hash-derived id, so the extra
|
|
112
157
|
record only refreshes metadata).
|
|
113
158
|
"""
|
|
159
|
+
|
|
114
160
|
path = injected_path(session_id)
|
|
115
161
|
if not path.exists():
|
|
116
162
|
return {}
|
|
@@ -206,13 +252,18 @@ def _to_wire_citations(cited_items: Any) -> list[dict[str, str]]:
|
|
|
206
252
|
kind = item.get("kind")
|
|
207
253
|
if not isinstance(real_id, str) or not real_id:
|
|
208
254
|
continue
|
|
209
|
-
|
|
255
|
+
wire_kind = kind
|
|
256
|
+
if kind == "playbook":
|
|
257
|
+
source_kind = item.get("source_kind")
|
|
258
|
+
if source_kind in {"user_playbook", "agent_playbook"}:
|
|
259
|
+
wire_kind = source_kind
|
|
260
|
+
if wire_kind not in _VALID_CITATION_KINDS:
|
|
210
261
|
continue
|
|
211
262
|
tag = item.get("id")
|
|
212
263
|
title = item.get("title")
|
|
213
264
|
out.append(
|
|
214
265
|
{
|
|
215
|
-
"kind":
|
|
266
|
+
"kind": wire_kind,
|
|
216
267
|
"real_id": real_id,
|
|
217
268
|
"tag": tag if isinstance(tag, str) else "",
|
|
218
269
|
"title": title if isinstance(title, str) else "",
|
|
@@ -221,44 +272,127 @@ def _to_wire_citations(cited_items: Any) -> list[dict[str, str]]:
|
|
|
221
272
|
return out
|
|
222
273
|
|
|
223
274
|
|
|
275
|
+
def published_record_offset(records: list[dict[str, Any]]) -> int:
|
|
276
|
+
"""Return the highest valid publish watermark in the session buffer."""
|
|
277
|
+
|
|
278
|
+
limit = len(records)
|
|
279
|
+
return max(
|
|
280
|
+
(
|
|
281
|
+
value
|
|
282
|
+
for record in records
|
|
283
|
+
if isinstance((value := record.get("published_up_to")), int)
|
|
284
|
+
and 0 <= value <= limit
|
|
285
|
+
),
|
|
286
|
+
default=0,
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def pending_publish_end(
|
|
291
|
+
records: list[dict[str, Any]], published: int
|
|
292
|
+
) -> int | None:
|
|
293
|
+
"""Return the frozen end of the current publish attempt, if any."""
|
|
294
|
+
|
|
295
|
+
for marker_index, record in enumerate(records[published:], start=published):
|
|
296
|
+
attempt = record.get("publish_attempt")
|
|
297
|
+
if not isinstance(attempt, dict):
|
|
298
|
+
continue
|
|
299
|
+
start = attempt.get("start")
|
|
300
|
+
end = attempt.get("end")
|
|
301
|
+
if (
|
|
302
|
+
start == published
|
|
303
|
+
and isinstance(end, int)
|
|
304
|
+
and published < end <= marker_index
|
|
305
|
+
):
|
|
306
|
+
_, interactions = unpublished_slice(
|
|
307
|
+
records[:end], published_override=published
|
|
308
|
+
)
|
|
309
|
+
if interactions:
|
|
310
|
+
return end
|
|
311
|
+
return None
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _wire_retrieved_ref(value: Any) -> dict[str, str] | None:
|
|
315
|
+
"""Validate one identity pair read from the ordered session buffer."""
|
|
316
|
+
|
|
317
|
+
if not isinstance(value, dict):
|
|
318
|
+
return None
|
|
319
|
+
kind = value.get("kind")
|
|
320
|
+
learning_id = value.get("learning_id")
|
|
321
|
+
if kind not in _VALID_RETRIEVED_KINDS:
|
|
322
|
+
return None
|
|
323
|
+
if not isinstance(learning_id, str) or not learning_id:
|
|
324
|
+
return None
|
|
325
|
+
return {"kind": kind, "learning_id": learning_id}
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def safe_publish_end(records: list[dict[str, Any]], published: int) -> int:
|
|
329
|
+
"""Stop before identity refs that do not yet have an Assistant target."""
|
|
330
|
+
|
|
331
|
+
unmatched_start: int | None = None
|
|
332
|
+
for index, record in enumerate(records[published:], start=published):
|
|
333
|
+
refs = record.get("retrieved_learning_refs")
|
|
334
|
+
if (
|
|
335
|
+
unmatched_start is None
|
|
336
|
+
and isinstance(refs, list)
|
|
337
|
+
and any(_wire_retrieved_ref(value) is not None for value in refs)
|
|
338
|
+
):
|
|
339
|
+
unmatched_start = index
|
|
340
|
+
if record.get("role") == "Assistant":
|
|
341
|
+
unmatched_start = None
|
|
342
|
+
return len(records) if unmatched_start is None else unmatched_start
|
|
343
|
+
|
|
344
|
+
|
|
224
345
|
def unpublished_slice(
|
|
225
346
|
records: Iterable[dict[str, Any]],
|
|
347
|
+
*,
|
|
348
|
+
published_override: int | None = None,
|
|
226
349
|
) -> tuple[int, list[dict[str, Any]]]:
|
|
227
|
-
"""
|
|
350
|
+
"""Return the current watermark and wire-ready unpublished interactions.
|
|
228
351
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
turn's ``tools_used``.
|
|
233
|
-
|
|
234
|
-
Returns:
|
|
235
|
-
tuple[int, list[dict]]: ``(published_up_to, interactions)``. The
|
|
236
|
-
integer is the watermark after which all turns are unpublished;
|
|
237
|
-
the list is formatted for ``InteractionData`` construction.
|
|
352
|
+
Metadata records are interpreted by their numeric boundary rather than
|
|
353
|
+
their physical position in the append-only file. This preserves turns that
|
|
354
|
+
arrive while an earlier network request is in flight.
|
|
238
355
|
"""
|
|
239
|
-
|
|
356
|
+
|
|
357
|
+
buffered = list(records)
|
|
358
|
+
if published_override is None:
|
|
359
|
+
published = published_record_offset(buffered)
|
|
360
|
+
elif 0 <= published_override <= len(buffered):
|
|
361
|
+
published = published_override
|
|
362
|
+
else:
|
|
363
|
+
raise ValueError("published_override is outside the supplied record range")
|
|
240
364
|
pending_tools: list[dict[str, Any]] = []
|
|
365
|
+
pending_refs: list[dict[str, str]] = []
|
|
241
366
|
turns: list[dict[str, Any]] = []
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
turns = []
|
|
367
|
+
attached_total = skipped = truncated = 0
|
|
368
|
+
|
|
369
|
+
for index, record in enumerate(buffered):
|
|
370
|
+
if index < published:
|
|
247
371
|
continue
|
|
248
|
-
|
|
372
|
+
|
|
373
|
+
refs = record.get("retrieved_learning_refs")
|
|
374
|
+
if "role" not in record and isinstance(refs, list):
|
|
375
|
+
for value in refs:
|
|
376
|
+
ref = _wire_retrieved_ref(value)
|
|
377
|
+
if ref is None:
|
|
378
|
+
skipped += 1
|
|
379
|
+
else:
|
|
380
|
+
pending_refs.append(ref)
|
|
249
381
|
continue
|
|
250
|
-
|
|
382
|
+
|
|
383
|
+
role = record.get("role")
|
|
251
384
|
if role == "Assistant_tool":
|
|
252
|
-
tool_input =
|
|
253
|
-
tool_output =
|
|
385
|
+
tool_input = record.get("tool_input") or {}
|
|
386
|
+
tool_output = record.get("tool_output") or ""
|
|
254
387
|
tool_entry: dict[str, Any] = {
|
|
255
|
-
"tool_name":
|
|
256
|
-
"status":
|
|
388
|
+
"tool_name": record.get("tool_name", ""),
|
|
389
|
+
"status": record.get("status", "success"),
|
|
257
390
|
}
|
|
258
391
|
tool_data: dict[str, Any] = {}
|
|
259
392
|
if tool_input:
|
|
260
393
|
tool_data["input"] = {
|
|
261
|
-
|
|
394
|
+
key: _truncate_tool_data_field(value)
|
|
395
|
+
for key, value in tool_input.items()
|
|
262
396
|
}
|
|
263
397
|
if tool_output:
|
|
264
398
|
tool_data["output"] = _truncate_tool_data_field(tool_output)
|
|
@@ -266,20 +400,45 @@ def unpublished_slice(
|
|
|
266
400
|
tool_entry["tool_data"] = tool_data
|
|
267
401
|
pending_tools.append(tool_entry)
|
|
268
402
|
continue
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
403
|
+
|
|
404
|
+
if role not in {"User", "Assistant"}:
|
|
405
|
+
continue
|
|
406
|
+
|
|
407
|
+
turn = {
|
|
408
|
+
key: value
|
|
409
|
+
for key, value in record.items()
|
|
410
|
+
if key not in {"role", "ts", "cited_items", "host"}
|
|
411
|
+
}
|
|
412
|
+
turn["role"] = role
|
|
413
|
+
if role == "Assistant":
|
|
414
|
+
citations = _to_wire_citations(record.get("cited_items"))
|
|
415
|
+
if citations:
|
|
416
|
+
turn["citations"] = citations
|
|
417
|
+
if pending_tools:
|
|
418
|
+
turn["tools_used"] = pending_tools
|
|
419
|
+
pending_tools = []
|
|
420
|
+
|
|
421
|
+
retrieved: list[dict[str, str]] = []
|
|
422
|
+
seen: set[tuple[str, str]] = set()
|
|
423
|
+
for ref in pending_refs:
|
|
424
|
+
key = (ref["kind"], ref["learning_id"])
|
|
425
|
+
if key in seen:
|
|
426
|
+
continue
|
|
427
|
+
seen.add(key)
|
|
428
|
+
if attached_total >= _RETRIEVED_LEARNINGS_PUBLISH_CAP:
|
|
429
|
+
truncated += 1
|
|
430
|
+
continue
|
|
431
|
+
retrieved.append(ref)
|
|
432
|
+
attached_total += 1
|
|
433
|
+
pending_refs = []
|
|
434
|
+
if retrieved:
|
|
435
|
+
turn["retrieved_learnings"] = retrieved
|
|
436
|
+
turns.append(turn)
|
|
437
|
+
|
|
438
|
+
if skipped:
|
|
439
|
+
_LOGGER.debug("Skipped %d malformed retrieved-learning refs", skipped)
|
|
440
|
+
if truncated:
|
|
441
|
+
_LOGGER.warning(
|
|
442
|
+
"Dropped %d retrieved-learning refs at the publish request cap", truncated
|
|
443
|
+
)
|
|
285
444
|
return published, turns
|
package/plugin/uv.lock
CHANGED
|
@@ -77,6 +77,19 @@ REFLEXIO_PUBLISH_LEARNING_WORKERS=
|
|
|
77
77
|
# Inactivity delay in seconds before a quiet session is evaluated by agent-success
|
|
78
78
|
# evaluation. (optional; default 600 = 10 minutes)
|
|
79
79
|
GROUP_EVALUATION_DELAY_SECONDS=600
|
|
80
|
+
# Opt-in background sweep that re-embeds interactions whose embedding failed at
|
|
81
|
+
# ingest (empty vector, no vector row -> invisible to vector/hybrid search). Runs
|
|
82
|
+
# per-org on the shared lineage-GC scheduler cadence. Off by default so the GC
|
|
83
|
+
# scheduler's start conditions are unchanged unless you opt in.
|
|
84
|
+
# options: true | false (optional; default false)
|
|
85
|
+
REFLEXIO_MISSING_VECTOR_BACKFILL_ENABLED=false
|
|
86
|
+
# Max interactions re-embedded per org per sweep tick (bounds embedding cost and
|
|
87
|
+
# log volume). (optional; default 200; values <= 0 fall back to the default)
|
|
88
|
+
REFLEXIO_MISSING_VECTOR_BACKFILL_CAP=200
|
|
89
|
+
# Bounded per-tick org fan-out width for the lineage-GC / expiry-reclamation
|
|
90
|
+
# scheduler. SQLite storage is pinned to serial (1) regardless of this value.
|
|
91
|
+
# (optional; default 8; non-numeric or <=0 falls back to default)
|
|
92
|
+
REFLEXIO_SCHEDULER_ORG_WORKERS=
|
|
80
93
|
|
|
81
94
|
# =============================================================================
|
|
82
95
|
# 5. TESTING & LOGGING
|
|
@@ -56,8 +56,9 @@ Description: Python SDK for interacting with Reflexio API remotely
|
|
|
56
56
|
Remote API client for applications to:
|
|
57
57
|
1. **Publish interactions** - Send user interactions to server for processing
|
|
58
58
|
2. **Search/retrieve data** - Query profiles, interactions, playbooks, evaluations, and context
|
|
59
|
-
3. **
|
|
60
|
-
4. **
|
|
59
|
+
3. **Track deferred learning** - Poll `get_learning_status(request_id)` after `publish_interaction(..., wait_for_response=False)` queues extraction
|
|
60
|
+
4. **Manage profiles/playbooks** - Delete, regenerate, and update status where supported by API endpoints
|
|
61
|
+
5. **Configure** - Set/get organization configuration
|
|
61
62
|
|
|
62
63
|
#### Architecture Pattern
|
|
63
64
|
Async HTTP client wrapping typed models from `models/api_schema/`. Automatically handles authentication via Bearer tokens.
|
|
@@ -123,6 +124,7 @@ client (Python SDK)
|
|
|
123
124
|
-> services/generation_service.py (orchestrator)
|
|
124
125
|
├─> services/profile/ -> storage (BaseStorage)
|
|
125
126
|
├─> services/playbook/ (playbook extraction) -> storage (BaseStorage)
|
|
127
|
+
├─> services/durable_learning/ -> learning_jobs queue -> deferred extraction
|
|
126
128
|
└─> services/agent_success_evaluation/ -> storage (BaseStorage)
|
|
127
129
|
```
|
|
128
130
|
|
|
@@ -138,6 +140,7 @@ client (Python SDK)
|
|
|
138
140
|
- `profile/` - Profile extraction & updates
|
|
139
141
|
- `playbook/` - Playbook extraction, consolidation, and aggregation
|
|
140
142
|
- `agent_success_evaluation/` - Success evaluation
|
|
143
|
+
- `durable_learning/` - Claim/drain durable `learning_jobs` for deferred extraction when `REFLEXIO_DURABLE_LEARNING_QUEUE` is enabled
|
|
141
144
|
- `reflection/` - Post-horizon reflection extraction
|
|
142
145
|
- `extraction/` - Resumable async extraction agent infrastructure
|
|
143
146
|
- `shadow_comparison/` - Per-turn regular vs shadow verdict judge
|
|
@@ -146,9 +149,10 @@ client (Python SDK)
|
|
|
146
149
|
- `braintrust/` - Braintrust eval export/sync support
|
|
147
150
|
- `lineage/` - Resolve current records and schedule tombstone garbage collection for superseded profile/playbook rows
|
|
148
151
|
- `governance/` - Subject-reference contracts and retention/barrier helpers used by storage and lineage
|
|
149
|
-
- `storage/` - Abstract layer (SQLite prod, LocalJSON test) with governance-aware write validation
|
|
152
|
+
- `storage/` - Abstract layer (SQLite prod, LocalJSON test) with governance-aware write validation and durable `learning_jobs` contracts
|
|
150
153
|
- `pre_retrieval/` - Query rewriting and document expansion helpers
|
|
151
154
|
- `configurator/` - YAML config loader
|
|
155
|
+
- **`billing_meter.py`**: OSS usage-event facade for learning/search metering; keep imports function-local at call sites so enterprise emitters remain optional
|
|
152
156
|
- **`site_var/`**: Global settings singleton
|
|
153
157
|
|
|
154
158
|
### Architecture Patterns
|
|
@@ -5,6 +5,13 @@ try:
|
|
|
5
5
|
except PackageNotFoundError:
|
|
6
6
|
__version__ = "0.0.0-dev"
|
|
7
7
|
|
|
8
|
+
from reflexio.models.api_schema.eval_overview_schema import (
|
|
9
|
+
GradeOnDemandRequest,
|
|
10
|
+
GradeOnDemandResponse,
|
|
11
|
+
RegenerateRequest,
|
|
12
|
+
RegenerateStartResponse,
|
|
13
|
+
RegenerateStatusResponse,
|
|
14
|
+
)
|
|
8
15
|
from reflexio.models.api_schema.retriever_schema import (
|
|
9
16
|
ConversationTurn,
|
|
10
17
|
GetAgentPlaybooksViewResponse,
|
|
@@ -125,6 +132,8 @@ __all__ = [
|
|
|
125
132
|
"DeleteProfilesByIdsRequest",
|
|
126
133
|
"DeleteAgentPlaybooksByIdsRequest",
|
|
127
134
|
"DeleteUserPlaybooksByIdsRequest",
|
|
135
|
+
"GradeOnDemandRequest",
|
|
136
|
+
"RegenerateRequest",
|
|
128
137
|
# Response types (internal)
|
|
129
138
|
"PublishUserInteractionResponse",
|
|
130
139
|
"DeleteUserProfileResponse",
|
|
@@ -135,6 +144,9 @@ __all__ = [
|
|
|
135
144
|
"SearchInteractionResponse",
|
|
136
145
|
"SearchUserProfileResponse",
|
|
137
146
|
"BulkDeleteResponse",
|
|
147
|
+
"GradeOnDemandResponse",
|
|
148
|
+
"RegenerateStartResponse",
|
|
149
|
+
"RegenerateStatusResponse",
|
|
138
150
|
# View response types (user-facing)
|
|
139
151
|
"GetInteractionsViewResponse",
|
|
140
152
|
"GetProfilesViewResponse",
|
|
@@ -15,6 +15,13 @@ import requests
|
|
|
15
15
|
from pydantic import ConfigDict
|
|
16
16
|
|
|
17
17
|
from reflexio.defaults import DEFAULT_AGENT_VERSION
|
|
18
|
+
from reflexio.models.api_schema.eval_overview_schema import (
|
|
19
|
+
GradeOnDemandRequest,
|
|
20
|
+
GradeOnDemandResponse,
|
|
21
|
+
RegenerateRequest,
|
|
22
|
+
RegenerateStartResponse,
|
|
23
|
+
RegenerateStatusResponse,
|
|
24
|
+
)
|
|
18
25
|
from reflexio.models.api_schema.retriever_schema import (
|
|
19
26
|
ConversationTurn,
|
|
20
27
|
GetAgentPlaybooksRequest,
|
|
@@ -26,6 +33,8 @@ from reflexio.models.api_schema.retriever_schema import (
|
|
|
26
33
|
GetProfilesViewResponse,
|
|
27
34
|
GetRequestsRequest,
|
|
28
35
|
GetRequestsViewResponse,
|
|
36
|
+
GetRetrievedLearningEvaluationResultsRequest,
|
|
37
|
+
GetRetrievedLearningEvaluationResultsResponse,
|
|
29
38
|
GetUserPlaybooksRequest,
|
|
30
39
|
GetUserPlaybooksViewResponse,
|
|
31
40
|
GetUserProfilesRequest,
|
|
@@ -306,9 +315,8 @@ class ReflexioClient:
|
|
|
306
315
|
if headers:
|
|
307
316
|
request_headers.update(headers)
|
|
308
317
|
|
|
309
|
-
self.session.headers.update(request_headers)
|
|
310
318
|
kwargs.setdefault("timeout", self.timeout)
|
|
311
|
-
response = self.session.request(method, url, **kwargs)
|
|
319
|
+
response = self.session.request(method, url, headers=request_headers, **kwargs)
|
|
312
320
|
response.raise_for_status()
|
|
313
321
|
|
|
314
322
|
# Empty body on a successful response (e.g. 204 No Content).
|
|
@@ -1920,6 +1928,116 @@ class ReflexioClient:
|
|
|
1920
1928
|
)
|
|
1921
1929
|
return GetEvaluationResultsViewResponse(**response)
|
|
1922
1930
|
|
|
1931
|
+
def get_retrieved_learning_evaluation_results(
|
|
1932
|
+
self,
|
|
1933
|
+
request: GetRetrievedLearningEvaluationResultsRequest | dict | None = None,
|
|
1934
|
+
*,
|
|
1935
|
+
user_id: str | None = None,
|
|
1936
|
+
session_id: str | None = None,
|
|
1937
|
+
limit: int | None = None,
|
|
1938
|
+
) -> GetRetrievedLearningEvaluationResultsResponse:
|
|
1939
|
+
"""Get per-learning relevance and impact verdicts for retrieved context."""
|
|
1940
|
+
req = self._build_request(
|
|
1941
|
+
request,
|
|
1942
|
+
GetRetrievedLearningEvaluationResultsRequest,
|
|
1943
|
+
user_id=user_id,
|
|
1944
|
+
session_id=session_id,
|
|
1945
|
+
limit=limit,
|
|
1946
|
+
)
|
|
1947
|
+
response = self._make_request(
|
|
1948
|
+
"POST",
|
|
1949
|
+
"/api/get_retrieved_learning_evaluation_results",
|
|
1950
|
+
json=req.model_dump(),
|
|
1951
|
+
)
|
|
1952
|
+
return GetRetrievedLearningEvaluationResultsResponse(**response)
|
|
1953
|
+
|
|
1954
|
+
def regenerate_evaluations(
|
|
1955
|
+
self,
|
|
1956
|
+
request: RegenerateRequest | dict | None = None,
|
|
1957
|
+
*,
|
|
1958
|
+
from_ts: int | None = None,
|
|
1959
|
+
to_ts: int | None = None,
|
|
1960
|
+
evaluation_name: str | None = None,
|
|
1961
|
+
) -> RegenerateStartResponse:
|
|
1962
|
+
"""Start a replay-the-judge regeneration job over a time window.
|
|
1963
|
+
|
|
1964
|
+
Args:
|
|
1965
|
+
request: Optional ``RegenerateRequest`` or dict. If omitted,
|
|
1966
|
+
keyword arguments are used.
|
|
1967
|
+
from_ts: Inclusive lower bound of the window as Unix seconds.
|
|
1968
|
+
to_ts: Inclusive upper bound of the window as Unix seconds.
|
|
1969
|
+
evaluation_name: Deprecated compatibility field accepted by the
|
|
1970
|
+
API but ignored by the singleton evaluator.
|
|
1971
|
+
|
|
1972
|
+
Returns:
|
|
1973
|
+
RegenerateStartResponse: The job id and number of queued sessions.
|
|
1974
|
+
"""
|
|
1975
|
+
req = self._build_request(
|
|
1976
|
+
request,
|
|
1977
|
+
RegenerateRequest,
|
|
1978
|
+
from_ts=from_ts,
|
|
1979
|
+
to_ts=to_ts,
|
|
1980
|
+
evaluation_name=evaluation_name,
|
|
1981
|
+
)
|
|
1982
|
+
response = self._make_request(
|
|
1983
|
+
"POST",
|
|
1984
|
+
"/api/evaluations/regenerate",
|
|
1985
|
+
json=req.model_dump(),
|
|
1986
|
+
)
|
|
1987
|
+
return RegenerateStartResponse(**response)
|
|
1988
|
+
|
|
1989
|
+
def get_evaluation_regeneration_status(
|
|
1990
|
+
self, job_id: str
|
|
1991
|
+
) -> RegenerateStatusResponse:
|
|
1992
|
+
"""Get status for an evaluation regeneration job."""
|
|
1993
|
+
response = self._make_request(
|
|
1994
|
+
"GET",
|
|
1995
|
+
f"/api/evaluations/regenerate/{job_id}",
|
|
1996
|
+
)
|
|
1997
|
+
return RegenerateStatusResponse(**response)
|
|
1998
|
+
|
|
1999
|
+
def cancel_evaluation_regeneration(self, job_id: str) -> dict[str, str]:
|
|
2000
|
+
"""Request cancellation for an evaluation regeneration job."""
|
|
2001
|
+
return self._make_request(
|
|
2002
|
+
"DELETE",
|
|
2003
|
+
f"/api/evaluations/regenerate/{job_id}",
|
|
2004
|
+
)
|
|
2005
|
+
|
|
2006
|
+
def grade_on_demand(
|
|
2007
|
+
self,
|
|
2008
|
+
request: GradeOnDemandRequest | dict | None = None,
|
|
2009
|
+
*,
|
|
2010
|
+
session_id: str | None = None,
|
|
2011
|
+
agent_version: str | None = None,
|
|
2012
|
+
evaluation_name: str | None = None,
|
|
2013
|
+
) -> GradeOnDemandResponse:
|
|
2014
|
+
"""Grade a single session synchronously.
|
|
2015
|
+
|
|
2016
|
+
Args:
|
|
2017
|
+
request: Optional ``GradeOnDemandRequest`` or dict. If omitted,
|
|
2018
|
+
keyword arguments are used.
|
|
2019
|
+
session_id: Session to grade.
|
|
2020
|
+
agent_version: Agent version to grade.
|
|
2021
|
+
evaluation_name: Deprecated compatibility field accepted by the
|
|
2022
|
+
API but ignored by the singleton evaluator.
|
|
2023
|
+
|
|
2024
|
+
Returns:
|
|
2025
|
+
GradeOnDemandResponse: Result id, cache flag, or skipped reason.
|
|
2026
|
+
"""
|
|
2027
|
+
req = self._build_request(
|
|
2028
|
+
request,
|
|
2029
|
+
GradeOnDemandRequest,
|
|
2030
|
+
session_id=session_id,
|
|
2031
|
+
agent_version=agent_version,
|
|
2032
|
+
evaluation_name=evaluation_name,
|
|
2033
|
+
)
|
|
2034
|
+
response = self._make_request(
|
|
2035
|
+
"POST",
|
|
2036
|
+
"/api/evaluations/grade_on_demand",
|
|
2037
|
+
json=req.model_dump(),
|
|
2038
|
+
)
|
|
2039
|
+
return GradeOnDemandResponse(**response)
|
|
2040
|
+
|
|
1923
2041
|
def _poll_operation_status(
|
|
1924
2042
|
self,
|
|
1925
2043
|
service_name: str,
|
|
@@ -2399,7 +2517,12 @@ class ReflexioClient:
|
|
|
2399
2517
|
"profiles", "user_playbooks", "agent_playbooks".
|
|
2400
2518
|
agent_playbook_status_filter (Optional[list[Union[PlaybookStatus, str]]]):
|
|
2401
2519
|
Agent-playbook approval statuses to include.
|
|
2402
|
-
enable_reformulation (Optional[bool]): Enable
|
|
2520
|
+
enable_reformulation (Optional[bool]): Enable the pre-search LLM
|
|
2521
|
+
query reformulation call (default: False). Besides rewriting
|
|
2522
|
+
the query, it extracts temporal signals — time windows
|
|
2523
|
+
("this week"), as-of boundaries, and current-value intent —
|
|
2524
|
+
that make retrieval time-sensitive (window filters,
|
|
2525
|
+
freshness-preferred ranking for superseded facts).
|
|
2403
2526
|
enable_agent_answer (Optional[bool]): Enable agentic answer synthesis when
|
|
2404
2527
|
the configured search backend supports it (default: False).
|
|
2405
2528
|
conversation_history (Optional[list[ConversationTurn] | list[dict]]): Prior conversation turns for context-aware query reformulation. Accepts ConversationTurn objects or dicts with "role" and "content" keys.
|
package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/state.py
CHANGED
|
@@ -36,7 +36,9 @@ _DEFAULT_STATE_DIR = Path.home() / ".openclaw-smart" / "sessions"
|
|
|
36
36
|
|
|
37
37
|
_TOOL_DATA_FIELD_MAX_LEN = 256
|
|
38
38
|
|
|
39
|
-
_VALID_CITATION_KINDS = frozenset(
|
|
39
|
+
_VALID_CITATION_KINDS = frozenset(
|
|
40
|
+
{"playbook", "profile", "user_playbook", "agent_playbook"}
|
|
41
|
+
)
|
|
40
42
|
|
|
41
43
|
|
|
42
44
|
def _truncate_tool_data_field(value: Any) -> Any:
|
|
@@ -240,13 +242,18 @@ def _to_wire_citations(cited_items: Any) -> list[dict[str, str]]:
|
|
|
240
242
|
kind = item.get("kind")
|
|
241
243
|
if not isinstance(real_id, str) or not real_id:
|
|
242
244
|
continue
|
|
243
|
-
|
|
245
|
+
wire_kind = kind
|
|
246
|
+
if kind == "playbook":
|
|
247
|
+
source_kind = item.get("source_kind")
|
|
248
|
+
if source_kind in {"user_playbook", "agent_playbook"}:
|
|
249
|
+
wire_kind = source_kind
|
|
250
|
+
if wire_kind not in _VALID_CITATION_KINDS:
|
|
244
251
|
continue
|
|
245
252
|
tag = item.get("id")
|
|
246
253
|
title = item.get("title")
|
|
247
254
|
out.append(
|
|
248
255
|
{
|
|
249
|
-
"kind":
|
|
256
|
+
"kind": wire_kind,
|
|
250
257
|
"real_id": real_id,
|
|
251
258
|
"tag": tag if isinstance(tag, str) else "",
|
|
252
259
|
"title": title if isinstance(title, str) else "",
|