claude-smart 0.2.46 → 0.2.48

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.
Files changed (170) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +19 -11
  3. package/bin/claude-smart.js +290 -68
  4. package/package.json +1 -1
  5. package/plugin/.claude-plugin/plugin.json +1 -1
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/README.md +11 -10
  8. package/plugin/dashboard/app/layout.tsx +20 -0
  9. package/plugin/dashboard/app/sessions/[sessionId]/page.tsx +36 -2
  10. package/plugin/dashboard/package-lock.json +61 -390
  11. package/plugin/dashboard/package.json +2 -2
  12. package/plugin/opencode/dist/server.mjs +76 -2
  13. package/plugin/opencode/server.mts +79 -2
  14. package/plugin/pyproject.toml +6 -2
  15. package/plugin/scripts/smart-install.sh +7 -1
  16. package/plugin/src/claude_smart/cli.py +210 -22
  17. package/plugin/src/claude_smart/context_format.py +9 -9
  18. package/plugin/src/claude_smart/cs_cite.py +66 -19
  19. package/plugin/uv.lock +5 -5
  20. package/plugin/vendor/reflexio/.env.example +7 -0
  21. package/plugin/vendor/reflexio/README.md +3 -3
  22. package/plugin/vendor/reflexio/pyproject.toml +2 -1
  23. package/plugin/vendor/reflexio/reflexio/README.md +11 -6
  24. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +1 -1
  25. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +2 -2
  26. package/plugin/vendor/reflexio/reflexio/cli/utils.py +44 -1
  27. package/plugin/vendor/reflexio/reflexio/client/client.py +97 -0
  28. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +8 -0
  29. package/plugin/vendor/reflexio/reflexio/lib/_base.py +15 -0
  30. package/plugin/vendor/reflexio/reflexio/lib/_config.py +23 -18
  31. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +9 -8
  32. package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +16 -1
  33. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +27 -16
  34. package/plugin/vendor/reflexio/reflexio/lib/_search.py +27 -5
  35. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +9 -0
  36. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +1 -0
  37. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +1 -0
  38. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/governance.py +117 -0
  39. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +45 -3
  40. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +37 -0
  41. package/plugin/vendor/reflexio/reflexio/server/README.md +38 -9
  42. package/plugin/vendor/reflexio/reflexio/server/__init__.py +21 -2
  43. package/plugin/vendor/reflexio/reflexio/server/api.py +274 -3267
  44. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/README.md +4 -3
  45. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +16 -1
  46. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +1 -1
  47. package/plugin/vendor/reflexio/reflexio/server/{_auth.py → auth.py} +2 -0
  48. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +62 -36
  49. package/plugin/vendor/reflexio/reflexio/server/deployment_profile.py +69 -0
  50. package/plugin/vendor/reflexio/reflexio/server/extensions.py +213 -0
  51. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_embedding.py +424 -0
  52. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_json_extraction.py +249 -0
  53. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_structured_output.py +195 -0
  54. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_subprocess.py +152 -0
  55. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_text_generation.py +980 -0
  56. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_types.py +110 -0
  57. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +73 -1819
  58. package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +4 -4
  59. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +57 -5
  60. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +12 -1
  61. package/plugin/vendor/reflexio/reflexio/server/middleware.py +244 -0
  62. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +9 -1
  63. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +14 -2
  64. package/plugin/vendor/reflexio/reflexio/server/rate_limit.py +79 -0
  65. package/plugin/vendor/reflexio/reflexio/server/routes/__init__.py +6 -0
  66. package/plugin/vendor/reflexio/reflexio/server/routes/_common.py +25 -0
  67. package/plugin/vendor/reflexio/reflexio/server/routes/_metering.py +98 -0
  68. package/plugin/vendor/reflexio/reflexio/server/routes/braintrust.py +129 -0
  69. package/plugin/vendor/reflexio/reflexio/server/routes/config.py +210 -0
  70. package/plugin/vendor/reflexio/reflexio/server/routes/evaluation.py +549 -0
  71. package/plugin/vendor/reflexio/reflexio/server/routes/interactions.py +259 -0
  72. package/plugin/vendor/reflexio/reflexio/server/routes/playbooks.py +578 -0
  73. package/plugin/vendor/reflexio/reflexio/server/routes/profiles.py +423 -0
  74. package/plugin/vendor/reflexio/reflexio/server/routes/provenance.py +345 -0
  75. package/plugin/vendor/reflexio/reflexio/server/routes/search.py +349 -0
  76. package/plugin/vendor/reflexio/reflexio/server/routes/system.py +261 -0
  77. package/plugin/vendor/reflexio/reflexio/server/scheduling.py +132 -0
  78. package/plugin/vendor/reflexio/reflexio/server/services/README.md +5 -2
  79. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/__init__.py +38 -0
  80. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_batch_progress.py +298 -0
  81. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_config_filter.py +152 -0
  82. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_extraction_lifecycle.py +243 -0
  83. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_should_run.py +299 -0
  84. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_status_change.py +273 -0
  85. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_usage_billing.py +258 -0
  86. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +17 -1183
  87. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +8 -1
  88. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +26 -41
  89. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +8 -0
  90. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +232 -123
  91. package/plugin/vendor/reflexio/reflexio/server/services/governance/config.py +52 -0
  92. package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +378 -0
  93. package/plugin/vendor/reflexio/reflexio/server/services/governance/subject_refs.py +34 -0
  94. package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +385 -78
  95. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +9 -1
  96. package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_prompt_processing.py +100 -0
  97. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator.py +121 -525
  98. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_clustering.py +184 -0
  99. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_postprocessing.py +130 -0
  100. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_prompt_formatting.py +212 -0
  101. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/consolidator.py +258 -69
  102. package/plugin/vendor/reflexio/reflexio/server/services/playbook/service.py +9 -9
  103. package/plugin/vendor/reflexio/reflexio/server/services/profile/components/extractor.py +3 -2
  104. package/plugin/vendor/reflexio/reflexio/server/services/publish_learning_worker.py +288 -0
  105. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/recency.py +211 -0
  106. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +29 -13
  107. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +4 -0
  108. package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +681 -0
  109. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +43 -6
  110. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +10 -1167
  111. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +58 -351
  112. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +49 -19
  113. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +452 -0
  114. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +11 -4
  115. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1 -2133
  116. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +7 -1126
  117. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +73 -33
  118. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +30 -0
  119. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/__init__.py +9 -0
  120. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_agent_run_store.py +506 -0
  121. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_pending_tool_call_store.py +704 -0
  122. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_run_tool_dependency_store.py +123 -0
  123. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/__init__.py +6 -0
  124. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_deletion.py +263 -0
  125. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_fts_vec.py +132 -0
  126. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/__init__.py +13 -0
  127. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_audit.py +122 -0
  128. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_erase_execution.py +465 -0
  129. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_purge.py +387 -0
  130. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_rebuild_hide.py +332 -0
  131. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_subject_barrier.py +511 -0
  132. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/__init__.py +13 -0
  133. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +955 -0
  134. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +189 -0
  135. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_optimization.py +247 -0
  136. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +145 -0
  137. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +844 -0
  138. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/__init__.py +9 -0
  139. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_interaction_store.py +263 -0
  140. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_profile_store.py +896 -0
  141. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_search.py +270 -0
  142. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +50 -9
  143. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +64 -370
  144. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +4 -4
  145. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +0 -909
  146. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +2 -0
  147. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +20 -0
  148. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/__init__.py +9 -0
  149. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_agent_run_store.py +86 -0
  150. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_models.py +195 -0
  151. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_pending_tool_call_store.py +148 -0
  152. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_run_tool_dependency_store.py +38 -0
  153. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/__init__.py +13 -0
  154. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_audit.py +29 -0
  155. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_erase_execution.py +30 -0
  156. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_purge.py +74 -0
  157. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_rebuild_hide.py +32 -0
  158. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_subject_barrier.py +49 -0
  159. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/__init__.py +13 -0
  160. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +365 -0
  161. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +124 -0
  162. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_optimization.py +85 -0
  163. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_source_linkage.py +47 -0
  164. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +333 -0
  165. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/__init__.py +9 -0
  166. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_interaction_store.py +73 -0
  167. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/{_profiles.py → profiles/_profile_store.py} +57 -86
  168. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_search.py +32 -0
  169. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +153 -12
  170. package/plugin/vendor/reflexio/reflexio/server/services/playbook/user_detail_stripping.py +0 -84
@@ -0,0 +1,9 @@
1
+ from ._interaction_store import InteractionStoreMixin
2
+ from ._profile_store import ProfileStoreMixin
3
+ from ._search import ProfileSearchMixin
4
+
5
+ __all__ = [
6
+ "InteractionStoreMixin",
7
+ "ProfileSearchMixin",
8
+ "ProfileStoreMixin",
9
+ ]
@@ -0,0 +1,263 @@
1
+ """Interaction store CRUD methods for SQLite storage.
2
+
3
+ Extracted verbatim from ``_profiles.py`` (the InteractionStore bucket). Profile
4
+ CRUD lives in ``profiles._profile_store`` (``ProfileStoreMixin``); search methods
5
+ live in ``profiles._search`` (``ProfileSearchMixin``).
6
+ """
7
+
8
+ import logging
9
+ import sqlite3
10
+ from typing import Any
11
+
12
+ from reflexio.models.api_schema.service_schemas import (
13
+ DeleteUserInteractionRequest,
14
+ Interaction,
15
+ )
16
+ from reflexio.server.llm.providers.embedding_service_provider import (
17
+ EmbeddingUnavailableError,
18
+ )
19
+
20
+ from .._base import (
21
+ SQLiteStorageBase,
22
+ _epoch_to_iso,
23
+ _json_dumps,
24
+ _row_to_interaction,
25
+ )
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ class InteractionStoreMixin:
31
+ """Mixin providing interaction-store CRUD for SQLite storage."""
32
+
33
+ # Type hints for instance attributes/methods provided by SQLiteStorageBase via MRO
34
+ _lock: Any
35
+ conn: sqlite3.Connection
36
+ _fetchone: Any
37
+ _fetchall: Any
38
+ _get_embedding: Any
39
+ _fts_upsert: Any
40
+ _vec_upsert: Any
41
+ _delete_in_chunks: Any
42
+ _has_sqlite_vec: bool
43
+ llm_client: Any
44
+ embedding_model_name: str
45
+ embedding_dimensions: int
46
+ _subject_ref_for_user_id: Any
47
+ _assert_subject_writable_locked: Any
48
+
49
+ # ------------------------------------------------------------------
50
+ # CRUD — Interactions
51
+ # ------------------------------------------------------------------
52
+
53
+ @SQLiteStorageBase.handle_exceptions
54
+ def get_all_interactions(self, limit: int = 100) -> list[Interaction]:
55
+ rows = self._fetchall(
56
+ "SELECT * FROM interactions ORDER BY created_at DESC LIMIT ?", (limit,)
57
+ )
58
+ return [_row_to_interaction(r) for r in rows]
59
+
60
+ @SQLiteStorageBase.handle_exceptions
61
+ def get_user_interaction(self, user_id: str) -> list[Interaction]:
62
+ rows = self._fetchall(
63
+ "SELECT * FROM interactions WHERE user_id = ?", (user_id,)
64
+ )
65
+ return [_row_to_interaction(r) for r in rows]
66
+
67
+ @SQLiteStorageBase.handle_exceptions
68
+ def get_all_user_ids(self) -> list[str]:
69
+ rows = self._fetchall("SELECT DISTINCT user_id FROM interactions")
70
+ return sorted(r["user_id"] for r in rows)
71
+
72
+ @SQLiteStorageBase.handle_exceptions
73
+ def add_user_interaction(self, user_id: str, interaction: Interaction) -> None: # noqa: ARG002
74
+ embedding = self._get_embedding(
75
+ f"{interaction.content}\n{interaction.user_action_description}"
76
+ )
77
+ interaction.embedding = embedding
78
+ self._insert_interaction(interaction)
79
+
80
+ def _insert_interaction(self, interaction: Interaction) -> int:
81
+ created_at_iso = _epoch_to_iso(interaction.created_at)
82
+ subject_ref = self._subject_ref_for_user_id(interaction.user_id)
83
+ with self._lock:
84
+ try:
85
+ self.conn.execute("BEGIN IMMEDIATE")
86
+ self._assert_subject_writable_locked(subject_ref)
87
+ if interaction.interaction_id:
88
+ self.conn.execute(
89
+ """INSERT OR REPLACE INTO interactions
90
+ (interaction_id, user_id, content, request_id, created_at,
91
+ role, user_action, user_action_description,
92
+ interacted_image_url, image_encoding, shadow_content,
93
+ expert_content, tools_used, citations, embedding, governance_subject_ref)
94
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
95
+ (
96
+ interaction.interaction_id,
97
+ interaction.user_id,
98
+ interaction.content,
99
+ interaction.request_id,
100
+ created_at_iso,
101
+ interaction.role,
102
+ interaction.user_action.value,
103
+ interaction.user_action_description,
104
+ interaction.interacted_image_url,
105
+ interaction.image_encoding,
106
+ interaction.shadow_content,
107
+ interaction.expert_content,
108
+ _json_dumps(
109
+ [t.model_dump() for t in interaction.tools_used]
110
+ ),
111
+ _json_dumps(
112
+ [c.model_dump() for c in interaction.citations]
113
+ ),
114
+ _json_dumps(interaction.embedding),
115
+ subject_ref,
116
+ ),
117
+ )
118
+ iid = interaction.interaction_id
119
+ else:
120
+ cur = self.conn.execute(
121
+ """INSERT INTO interactions
122
+ (user_id, content, request_id, created_at,
123
+ role, user_action, user_action_description,
124
+ interacted_image_url, image_encoding, shadow_content,
125
+ expert_content, tools_used, citations, embedding, governance_subject_ref)
126
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
127
+ (
128
+ interaction.user_id,
129
+ interaction.content,
130
+ interaction.request_id,
131
+ created_at_iso,
132
+ interaction.role,
133
+ interaction.user_action.value,
134
+ interaction.user_action_description,
135
+ interaction.interacted_image_url,
136
+ interaction.image_encoding,
137
+ interaction.shadow_content,
138
+ interaction.expert_content,
139
+ _json_dumps(
140
+ [t.model_dump() for t in interaction.tools_used]
141
+ ),
142
+ _json_dumps(
143
+ [c.model_dump() for c in interaction.citations]
144
+ ),
145
+ _json_dumps(interaction.embedding),
146
+ subject_ref,
147
+ ),
148
+ )
149
+ iid = cur.lastrowid or 0
150
+ interaction.interaction_id = iid
151
+ self.conn.commit()
152
+ except Exception:
153
+ self.conn.rollback()
154
+ raise
155
+ # Update FTS and vec
156
+ self._fts_upsert(
157
+ "interactions_fts",
158
+ iid,
159
+ content=interaction.content,
160
+ user_action_description=interaction.user_action_description,
161
+ )
162
+ if interaction.embedding:
163
+ self._vec_upsert("interactions_vec", iid, interaction.embedding)
164
+ return iid
165
+
166
+ @SQLiteStorageBase.handle_exceptions
167
+ def add_user_interactions_bulk(
168
+ self,
169
+ user_id: str, # noqa: ARG002
170
+ interactions: list[Interaction],
171
+ ) -> None:
172
+ if not interactions:
173
+ return
174
+ texts = [
175
+ "\n".join([i.content or "", i.user_action_description or ""])
176
+ for i in interactions
177
+ ]
178
+ try:
179
+ embeddings = self.llm_client.get_embeddings(
180
+ texts, self.embedding_model_name, self.embedding_dimensions
181
+ )
182
+ except EmbeddingUnavailableError as exc:
183
+ logger.warning(
184
+ "Embedding unavailable for interaction bulk insert; "
185
+ "continuing without vectors: %s",
186
+ exc,
187
+ )
188
+ embeddings = [[] for _ in texts]
189
+ for interaction, embedding in zip(interactions, embeddings, strict=False):
190
+ interaction.embedding = embedding
191
+ self._insert_interaction(interaction)
192
+
193
+ @SQLiteStorageBase.handle_exceptions
194
+ def delete_user_interaction(self, request: DeleteUserInteractionRequest) -> None:
195
+ with self._lock:
196
+ row = self.conn.execute(
197
+ "SELECT interaction_id FROM interactions WHERE user_id = ? AND interaction_id = ?",
198
+ (request.user_id, request.interaction_id),
199
+ ).fetchone()
200
+ if row is None:
201
+ return
202
+ self.conn.execute(
203
+ "DELETE FROM interactions_fts WHERE rowid = ?",
204
+ (request.interaction_id,),
205
+ )
206
+ if self._has_sqlite_vec:
207
+ self.conn.execute(
208
+ "DELETE FROM interactions_vec WHERE rowid = ?",
209
+ (request.interaction_id,),
210
+ )
211
+ self.conn.execute(
212
+ "DELETE FROM interactions WHERE user_id = ? AND interaction_id = ?",
213
+ (request.user_id, request.interaction_id),
214
+ )
215
+ self.conn.commit()
216
+
217
+ @SQLiteStorageBase.handle_exceptions
218
+ def delete_all_interactions_for_user(self, user_id: str) -> None:
219
+ with self._lock:
220
+ rows = self.conn.execute(
221
+ "SELECT interaction_id FROM interactions WHERE user_id = ?", (user_id,)
222
+ ).fetchall()
223
+ if not rows:
224
+ return
225
+ ids = [r["interaction_id"] for r in rows]
226
+ self._delete_in_chunks("interactions_fts", "rowid", ids)
227
+ if self._has_sqlite_vec:
228
+ self._delete_in_chunks("interactions_vec", "rowid", ids)
229
+ self.conn.execute("DELETE FROM interactions WHERE user_id = ?", (user_id,))
230
+ self.conn.commit()
231
+
232
+ @SQLiteStorageBase.handle_exceptions
233
+ def delete_all_interactions(self) -> None:
234
+ with self._lock:
235
+ self.conn.execute("DELETE FROM interactions_fts")
236
+ if self._has_sqlite_vec:
237
+ self.conn.execute("DELETE FROM interactions_vec")
238
+ self.conn.execute("DELETE FROM interactions")
239
+ self.conn.commit()
240
+
241
+ @SQLiteStorageBase.handle_exceptions
242
+ def count_all_interactions(self) -> int:
243
+ row = self._fetchone("SELECT COUNT(*) as cnt FROM interactions")
244
+ return row["cnt"] if row else 0
245
+
246
+ @SQLiteStorageBase.handle_exceptions
247
+ def delete_oldest_interactions(self, count: int) -> int:
248
+ if count <= 0:
249
+ return 0
250
+ with self._lock:
251
+ rows = self.conn.execute(
252
+ "SELECT interaction_id FROM interactions ORDER BY created_at ASC LIMIT ?",
253
+ (count,),
254
+ ).fetchall()
255
+ if not rows:
256
+ return 0
257
+ ids = [r["interaction_id"] for r in rows]
258
+ self._delete_in_chunks("interactions_fts", "rowid", ids)
259
+ if self._has_sqlite_vec:
260
+ self._delete_in_chunks("interactions_vec", "rowid", ids)
261
+ self._delete_in_chunks("interactions", "interaction_id", ids)
262
+ self.conn.commit()
263
+ return len(ids)