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.
- package/.claude-plugin/marketplace.json +1 -1
- package/README.md +19 -11
- package/bin/claude-smart.js +290 -68
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/README.md +11 -10
- package/plugin/dashboard/app/layout.tsx +20 -0
- package/plugin/dashboard/app/sessions/[sessionId]/page.tsx +36 -2
- package/plugin/dashboard/package-lock.json +61 -390
- package/plugin/dashboard/package.json +2 -2
- package/plugin/opencode/dist/server.mjs +76 -2
- package/plugin/opencode/server.mts +79 -2
- package/plugin/pyproject.toml +6 -2
- package/plugin/scripts/smart-install.sh +7 -1
- package/plugin/src/claude_smart/cli.py +210 -22
- package/plugin/src/claude_smart/context_format.py +9 -9
- package/plugin/src/claude_smart/cs_cite.py +66 -19
- package/plugin/uv.lock +5 -5
- package/plugin/vendor/reflexio/.env.example +7 -0
- package/plugin/vendor/reflexio/README.md +3 -3
- package/plugin/vendor/reflexio/pyproject.toml +2 -1
- package/plugin/vendor/reflexio/reflexio/README.md +11 -6
- package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +1 -1
- package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +2 -2
- package/plugin/vendor/reflexio/reflexio/cli/utils.py +44 -1
- package/plugin/vendor/reflexio/reflexio/client/client.py +97 -0
- package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +8 -0
- package/plugin/vendor/reflexio/reflexio/lib/_base.py +15 -0
- package/plugin/vendor/reflexio/reflexio/lib/_config.py +23 -18
- package/plugin/vendor/reflexio/reflexio/lib/_generation.py +9 -8
- package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +16 -1
- package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +27 -16
- package/plugin/vendor/reflexio/reflexio/lib/_search.py +27 -5
- package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +9 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +1 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/governance.py +117 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +45 -3
- package/plugin/vendor/reflexio/reflexio/models/config_schema.py +37 -0
- package/plugin/vendor/reflexio/reflexio/server/README.md +38 -9
- package/plugin/vendor/reflexio/reflexio/server/__init__.py +21 -2
- package/plugin/vendor/reflexio/reflexio/server/api.py +274 -3267
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/README.md +4 -3
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +16 -1
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +1 -1
- package/plugin/vendor/reflexio/reflexio/server/{_auth.py → auth.py} +2 -0
- package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +62 -36
- package/plugin/vendor/reflexio/reflexio/server/deployment_profile.py +69 -0
- package/plugin/vendor/reflexio/reflexio/server/extensions.py +213 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_embedding.py +424 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_json_extraction.py +249 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_structured_output.py +195 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_subprocess.py +152 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_text_generation.py +980 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_types.py +110 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +73 -1819
- package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +4 -4
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +57 -5
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +12 -1
- package/plugin/vendor/reflexio/reflexio/server/middleware.py +244 -0
- package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +9 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +14 -2
- package/plugin/vendor/reflexio/reflexio/server/rate_limit.py +79 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/__init__.py +6 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/_common.py +25 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/_metering.py +98 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/braintrust.py +129 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/config.py +210 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/evaluation.py +549 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/interactions.py +259 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/playbooks.py +578 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/profiles.py +423 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/provenance.py +345 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/search.py +349 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/system.py +261 -0
- package/plugin/vendor/reflexio/reflexio/server/scheduling.py +132 -0
- package/plugin/vendor/reflexio/reflexio/server/services/README.md +5 -2
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/__init__.py +38 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_batch_progress.py +298 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_config_filter.py +152 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_extraction_lifecycle.py +243 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_should_run.py +299 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_status_change.py +273 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_usage_billing.py +258 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +17 -1183
- package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +8 -1
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +26 -41
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +8 -0
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +232 -123
- package/plugin/vendor/reflexio/reflexio/server/services/governance/config.py +52 -0
- package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +378 -0
- package/plugin/vendor/reflexio/reflexio/server/services/governance/subject_refs.py +34 -0
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +385 -78
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +9 -1
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_prompt_processing.py +100 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator.py +121 -525
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_clustering.py +184 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_postprocessing.py +130 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_prompt_formatting.py +212 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/consolidator.py +258 -69
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/service.py +9 -9
- package/plugin/vendor/reflexio/reflexio/server/services/profile/components/extractor.py +3 -2
- package/plugin/vendor/reflexio/reflexio/server/services/publish_learning_worker.py +288 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/recency.py +211 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +29 -13
- package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +4 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +681 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +43 -6
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +10 -1167
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +58 -351
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +49 -19
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +452 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +11 -4
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1 -2133
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +7 -1126
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +73 -33
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +30 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/__init__.py +9 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_agent_run_store.py +506 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_pending_tool_call_store.py +704 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_run_tool_dependency_store.py +123 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/__init__.py +6 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_deletion.py +263 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_fts_vec.py +132 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_audit.py +122 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_erase_execution.py +465 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_purge.py +387 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_rebuild_hide.py +332 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_subject_barrier.py +511 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +955 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +189 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_optimization.py +247 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +145 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +844 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/__init__.py +9 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_interaction_store.py +263 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_profile_store.py +896 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_search.py +270 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +50 -9
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +64 -370
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +4 -4
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +0 -909
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +2 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +20 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/__init__.py +9 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_agent_run_store.py +86 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_models.py +195 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_pending_tool_call_store.py +148 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_run_tool_dependency_store.py +38 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_audit.py +29 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_erase_execution.py +30 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_purge.py +74 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_rebuild_hide.py +32 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_subject_barrier.py +49 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +365 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +124 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_optimization.py +85 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_source_linkage.py +47 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +333 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/__init__.py +9 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_interaction_store.py +73 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/{_profiles.py → profiles/_profile_store.py} +57 -86
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_search.py +32 -0
- package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +153 -12
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/user_detail_stripping.py +0 -84
package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Shared row/datetime helpers for SQLite resumable extraction agent runs.
|
|
2
|
+
|
|
3
|
+
Helpers-only residual for the ``_agent_run.py`` decomposition: all twenty-three
|
|
4
|
+
agent-run methods now live in the three ``agent_run`` sub-mixins
|
|
5
|
+
(``SQLiteAgentRunStoreMixin`` / ``SQLitePendingToolCallStoreMixin`` /
|
|
6
|
+
``SQLiteRunToolDependencyStoreMixin``), which import the ``_row_to_*`` /
|
|
7
|
+
``_dt`` / ``_dt_str`` helpers below rather than duplicating them.
|
|
8
|
+
"""
|
|
2
9
|
|
|
3
10
|
from __future__ import annotations
|
|
4
11
|
|
|
5
12
|
import sqlite3
|
|
6
|
-
from datetime import UTC, datetime
|
|
7
|
-
from typing import Any
|
|
13
|
+
from datetime import UTC, datetime
|
|
8
14
|
|
|
9
15
|
from reflexio.server.services.storage.storage_base import (
|
|
10
16
|
AgentBinding,
|
|
@@ -12,15 +18,13 @@ from reflexio.server.services.storage.storage_base import (
|
|
|
12
18
|
AgentRunStatus,
|
|
13
19
|
PendingToolCallRecord,
|
|
14
20
|
PendingToolCallStatus,
|
|
15
|
-
PendingToolCallUpsertResult,
|
|
16
21
|
PriorAnswerMatch,
|
|
17
22
|
RunToolDependencyKind,
|
|
18
23
|
RunToolDependencyRecord,
|
|
19
24
|
embedding_similarity,
|
|
20
|
-
not_applicable_tool_result,
|
|
21
25
|
)
|
|
22
26
|
|
|
23
|
-
from ._base import
|
|
27
|
+
from ._base import _json_loads
|
|
24
28
|
|
|
25
29
|
|
|
26
30
|
def _dt(value: str | None) -> datetime | None:
|
|
@@ -138,1164 +142,3 @@ def _row_to_run_tool_dependency(row: sqlite3.Row) -> RunToolDependencyRecord:
|
|
|
138
142
|
consumed_at=_dt(data.get("consumed_at")),
|
|
139
143
|
created_at=_dt(data.get("created_at")),
|
|
140
144
|
)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
class SQLiteAgentRunMixin:
|
|
144
|
-
"""SQLite-backed resumable extraction run storage."""
|
|
145
|
-
|
|
146
|
-
_lock: Any
|
|
147
|
-
conn: sqlite3.Connection
|
|
148
|
-
_fetchone: Any
|
|
149
|
-
_fetchall: Any
|
|
150
|
-
_current_timestamp: Any
|
|
151
|
-
org_id: str
|
|
152
|
-
|
|
153
|
-
def _finalize_runs_without_pending_dependencies_unlocked(self, now_s: str) -> None:
|
|
154
|
-
self.conn.execute(
|
|
155
|
-
"""
|
|
156
|
-
UPDATE _agent_runs
|
|
157
|
-
SET status = ?,
|
|
158
|
-
finalized_at = COALESCE(finalized_at, ?),
|
|
159
|
-
updated_at = ?
|
|
160
|
-
WHERE status = ?
|
|
161
|
-
AND NOT EXISTS (
|
|
162
|
-
SELECT 1
|
|
163
|
-
FROM _run_tool_dependencies d
|
|
164
|
-
JOIN _pending_tool_calls p
|
|
165
|
-
ON p.id = d.pending_tool_call_id
|
|
166
|
-
WHERE d.run_id = _agent_runs.id
|
|
167
|
-
AND d.resolved_at IS NULL
|
|
168
|
-
AND d.consumed_at IS NULL
|
|
169
|
-
AND p.status = ?
|
|
170
|
-
)
|
|
171
|
-
""",
|
|
172
|
-
(
|
|
173
|
-
AgentRunStatus.FINALIZED.value,
|
|
174
|
-
now_s,
|
|
175
|
-
now_s,
|
|
176
|
-
AgentRunStatus.FINALIZED_PENDING_TOOL.value,
|
|
177
|
-
PendingToolCallStatus.PENDING.value,
|
|
178
|
-
),
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
def _mark_runs_ready_with_actionable_dependencies_unlocked(
|
|
182
|
-
self, now_s: str, *, pending_tool_call_id: str
|
|
183
|
-
) -> None:
|
|
184
|
-
self.conn.execute(
|
|
185
|
-
"""
|
|
186
|
-
UPDATE _agent_runs
|
|
187
|
-
SET status = ?,
|
|
188
|
-
updated_at = ?
|
|
189
|
-
WHERE status IN (?, ?)
|
|
190
|
-
AND EXISTS (
|
|
191
|
-
SELECT 1
|
|
192
|
-
FROM _run_tool_dependencies changed
|
|
193
|
-
WHERE changed.run_id = _agent_runs.id
|
|
194
|
-
AND changed.pending_tool_call_id = ?
|
|
195
|
-
)
|
|
196
|
-
AND EXISTS (
|
|
197
|
-
SELECT 1
|
|
198
|
-
FROM _run_tool_dependencies d
|
|
199
|
-
JOIN _pending_tool_calls p
|
|
200
|
-
ON p.id = d.pending_tool_call_id
|
|
201
|
-
WHERE d.run_id = _agent_runs.id
|
|
202
|
-
AND d.resolved_at IS NOT NULL
|
|
203
|
-
AND d.consumed_at IS NULL
|
|
204
|
-
AND p.status = ?
|
|
205
|
-
AND COALESCE(json_extract(p.result, '$.not_applicable'), 0) != 1
|
|
206
|
-
)
|
|
207
|
-
""",
|
|
208
|
-
(
|
|
209
|
-
AgentRunStatus.RESUME_READY.value,
|
|
210
|
-
now_s,
|
|
211
|
-
AgentRunStatus.FINALIZED.value,
|
|
212
|
-
AgentRunStatus.FINALIZED_PENDING_TOOL.value,
|
|
213
|
-
pending_tool_call_id,
|
|
214
|
-
PendingToolCallStatus.RESOLVED.value,
|
|
215
|
-
),
|
|
216
|
-
)
|
|
217
|
-
|
|
218
|
-
def _finalize_runs_without_actionable_dependencies_unlocked(
|
|
219
|
-
self, now_s: str, *, pending_tool_call_id: str
|
|
220
|
-
) -> None:
|
|
221
|
-
self.conn.execute(
|
|
222
|
-
"""
|
|
223
|
-
UPDATE _agent_runs
|
|
224
|
-
SET status = ?,
|
|
225
|
-
finalized_at = COALESCE(finalized_at, ?),
|
|
226
|
-
updated_at = ?
|
|
227
|
-
WHERE status IN (?, ?)
|
|
228
|
-
AND EXISTS (
|
|
229
|
-
SELECT 1
|
|
230
|
-
FROM _run_tool_dependencies changed
|
|
231
|
-
WHERE changed.run_id = _agent_runs.id
|
|
232
|
-
AND changed.pending_tool_call_id = ?
|
|
233
|
-
)
|
|
234
|
-
AND NOT EXISTS (
|
|
235
|
-
SELECT 1
|
|
236
|
-
FROM _run_tool_dependencies d
|
|
237
|
-
JOIN _pending_tool_calls p
|
|
238
|
-
ON p.id = d.pending_tool_call_id
|
|
239
|
-
WHERE d.run_id = _agent_runs.id
|
|
240
|
-
AND d.resolved_at IS NULL
|
|
241
|
-
AND d.consumed_at IS NULL
|
|
242
|
-
AND p.status = ?
|
|
243
|
-
)
|
|
244
|
-
AND NOT EXISTS (
|
|
245
|
-
SELECT 1
|
|
246
|
-
FROM _run_tool_dependencies d
|
|
247
|
-
JOIN _pending_tool_calls p
|
|
248
|
-
ON p.id = d.pending_tool_call_id
|
|
249
|
-
WHERE d.run_id = _agent_runs.id
|
|
250
|
-
AND d.resolved_at IS NOT NULL
|
|
251
|
-
AND d.consumed_at IS NULL
|
|
252
|
-
AND p.status = ?
|
|
253
|
-
AND COALESCE(json_extract(p.result, '$.not_applicable'), 0) != 1
|
|
254
|
-
)
|
|
255
|
-
""",
|
|
256
|
-
(
|
|
257
|
-
AgentRunStatus.FINALIZED.value,
|
|
258
|
-
now_s,
|
|
259
|
-
now_s,
|
|
260
|
-
AgentRunStatus.FINALIZED_PENDING_TOOL.value,
|
|
261
|
-
AgentRunStatus.RESUME_READY.value,
|
|
262
|
-
pending_tool_call_id,
|
|
263
|
-
PendingToolCallStatus.PENDING.value,
|
|
264
|
-
PendingToolCallStatus.RESOLVED.value,
|
|
265
|
-
),
|
|
266
|
-
)
|
|
267
|
-
|
|
268
|
-
@SQLiteStorageBase.handle_exceptions
|
|
269
|
-
def create_agent_run(self, record: AgentRunRecord) -> AgentRunRecord:
|
|
270
|
-
binding = record.binding
|
|
271
|
-
with self._lock:
|
|
272
|
-
self.conn.execute(
|
|
273
|
-
"""
|
|
274
|
-
INSERT INTO _agent_runs (
|
|
275
|
-
id, org_id, extractor_kind, user_id,
|
|
276
|
-
request_id, agent_version, source, source_interaction_ids,
|
|
277
|
-
window_start_interaction_id, window_end_interaction_id,
|
|
278
|
-
extractor_config_hash, status, generation_request_snapshot,
|
|
279
|
-
service_config_snapshot, agent_context_snapshot,
|
|
280
|
-
committed_output, pending_tool_call_ids, max_steps_remaining,
|
|
281
|
-
resume_attempts, finalization_attempts, next_resume_at,
|
|
282
|
-
claimed_by, claimed_at, agent_completed_at, finalized_at,
|
|
283
|
-
expires_at, last_error
|
|
284
|
-
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
285
|
-
""",
|
|
286
|
-
(
|
|
287
|
-
record.id,
|
|
288
|
-
binding.org_id,
|
|
289
|
-
binding.extractor_kind,
|
|
290
|
-
binding.user_id,
|
|
291
|
-
binding.request_id,
|
|
292
|
-
binding.agent_version,
|
|
293
|
-
binding.source,
|
|
294
|
-
_json_dumps(binding.source_interaction_ids),
|
|
295
|
-
binding.window_start_interaction_id,
|
|
296
|
-
binding.window_end_interaction_id,
|
|
297
|
-
binding.extractor_config_hash,
|
|
298
|
-
record.status.value,
|
|
299
|
-
_json_dumps(record.generation_request_snapshot),
|
|
300
|
-
_json_dumps(record.service_config_snapshot),
|
|
301
|
-
record.agent_context_snapshot,
|
|
302
|
-
_json_dumps(record.committed_output),
|
|
303
|
-
_json_dumps(record.pending_tool_call_ids),
|
|
304
|
-
record.max_steps_remaining,
|
|
305
|
-
record.resume_attempts,
|
|
306
|
-
record.finalization_attempts,
|
|
307
|
-
_dt_str(record.next_resume_at),
|
|
308
|
-
record.claimed_by,
|
|
309
|
-
_dt_str(record.claimed_at),
|
|
310
|
-
_dt_str(record.agent_completed_at),
|
|
311
|
-
_dt_str(record.finalized_at),
|
|
312
|
-
_dt_str(record.expires_at),
|
|
313
|
-
record.last_error,
|
|
314
|
-
),
|
|
315
|
-
)
|
|
316
|
-
self.conn.commit()
|
|
317
|
-
stored = self.get_agent_run(record.id)
|
|
318
|
-
if stored is None: # pragma: no cover
|
|
319
|
-
raise RuntimeError(f"Failed to create agent run {record.id}")
|
|
320
|
-
return stored
|
|
321
|
-
|
|
322
|
-
@SQLiteStorageBase.handle_exceptions
|
|
323
|
-
def get_agent_run(self, run_id: str) -> AgentRunRecord | None:
|
|
324
|
-
row = self._fetchone("SELECT * FROM _agent_runs WHERE id = ?", (run_id,))
|
|
325
|
-
return _row_to_agent_run(row) if row else None
|
|
326
|
-
|
|
327
|
-
@SQLiteStorageBase.handle_exceptions
|
|
328
|
-
def update_agent_run_status(
|
|
329
|
-
self,
|
|
330
|
-
run_id: str,
|
|
331
|
-
status: AgentRunStatus,
|
|
332
|
-
*,
|
|
333
|
-
committed_output: dict[str, Any] | None = None,
|
|
334
|
-
pending_tool_call_ids: list[str] | None = None,
|
|
335
|
-
max_steps_remaining: int | None = None,
|
|
336
|
-
next_resume_at: datetime | None = None,
|
|
337
|
-
last_error: str | None = None,
|
|
338
|
-
increment_finalization_attempts: bool = False,
|
|
339
|
-
expected_statuses: tuple[AgentRunStatus, ...] | None = None,
|
|
340
|
-
) -> AgentRunRecord | None:
|
|
341
|
-
current_timestamp = self._current_timestamp()
|
|
342
|
-
assignments = ["status = ?", "updated_at = ?"]
|
|
343
|
-
params: list[Any] = [status.value, current_timestamp]
|
|
344
|
-
if committed_output is not None:
|
|
345
|
-
assignments.append("committed_output = ?")
|
|
346
|
-
params.append(_json_dumps(committed_output))
|
|
347
|
-
if pending_tool_call_ids is not None:
|
|
348
|
-
assignments.append("pending_tool_call_ids = ?")
|
|
349
|
-
params.append(_json_dumps(pending_tool_call_ids))
|
|
350
|
-
if max_steps_remaining is not None:
|
|
351
|
-
assignments.append("max_steps_remaining = ?")
|
|
352
|
-
params.append(max(0, max_steps_remaining))
|
|
353
|
-
if next_resume_at is not None:
|
|
354
|
-
assignments.append("next_resume_at = ?")
|
|
355
|
-
params.append(_dt_str(next_resume_at))
|
|
356
|
-
if last_error is not None:
|
|
357
|
-
assignments.append("last_error = ?")
|
|
358
|
-
params.append(last_error)
|
|
359
|
-
if increment_finalization_attempts:
|
|
360
|
-
assignments.append("finalization_attempts = finalization_attempts + 1")
|
|
361
|
-
if status == AgentRunStatus.AGENT_COMPLETED:
|
|
362
|
-
assignments.append("agent_completed_at = ?")
|
|
363
|
-
params.append(current_timestamp)
|
|
364
|
-
if status in (AgentRunStatus.FINALIZED, AgentRunStatus.FINALIZED_PENDING_TOOL):
|
|
365
|
-
assignments.append("finalized_at = ?")
|
|
366
|
-
params.append(current_timestamp)
|
|
367
|
-
params.append(run_id)
|
|
368
|
-
status_filter = ""
|
|
369
|
-
if expected_statuses:
|
|
370
|
-
placeholders = ",".join("?" for _ in expected_statuses)
|
|
371
|
-
status_filter = f" AND status IN ({placeholders})"
|
|
372
|
-
params.extend(expected.value for expected in expected_statuses)
|
|
373
|
-
with self._lock:
|
|
374
|
-
self.conn.execute(
|
|
375
|
-
f"UPDATE _agent_runs SET {', '.join(assignments)} WHERE id = ?{status_filter}",
|
|
376
|
-
params,
|
|
377
|
-
)
|
|
378
|
-
self.conn.commit()
|
|
379
|
-
return self.get_agent_run(run_id)
|
|
380
|
-
|
|
381
|
-
@SQLiteStorageBase.handle_exceptions
|
|
382
|
-
def fail_running_agent_runs_for_request(
|
|
383
|
-
self,
|
|
384
|
-
*,
|
|
385
|
-
org_id: str,
|
|
386
|
-
extractor_kind: str,
|
|
387
|
-
user_id: str | None,
|
|
388
|
-
request_id: str,
|
|
389
|
-
last_error: str,
|
|
390
|
-
) -> int:
|
|
391
|
-
current_timestamp = self._current_timestamp()
|
|
392
|
-
with self._lock:
|
|
393
|
-
cursor = self.conn.execute(
|
|
394
|
-
"""
|
|
395
|
-
UPDATE _agent_runs
|
|
396
|
-
SET status = ?,
|
|
397
|
-
updated_at = ?,
|
|
398
|
-
last_error = ?
|
|
399
|
-
WHERE org_id = ?
|
|
400
|
-
AND extractor_kind = ?
|
|
401
|
-
AND user_id IS ?
|
|
402
|
-
AND request_id = ?
|
|
403
|
-
AND status IN (?, ?)
|
|
404
|
-
""",
|
|
405
|
-
(
|
|
406
|
-
AgentRunStatus.FAILED.value,
|
|
407
|
-
current_timestamp,
|
|
408
|
-
last_error,
|
|
409
|
-
org_id,
|
|
410
|
-
extractor_kind,
|
|
411
|
-
user_id,
|
|
412
|
-
request_id,
|
|
413
|
-
AgentRunStatus.RUNNING.value,
|
|
414
|
-
AgentRunStatus.RESUMING.value,
|
|
415
|
-
),
|
|
416
|
-
)
|
|
417
|
-
self.conn.commit()
|
|
418
|
-
return cursor.rowcount
|
|
419
|
-
|
|
420
|
-
@SQLiteStorageBase.handle_exceptions
|
|
421
|
-
def create_pending_tool_call(
|
|
422
|
-
self, record: PendingToolCallRecord
|
|
423
|
-
) -> PendingToolCallRecord:
|
|
424
|
-
with self._lock:
|
|
425
|
-
self.conn.execute(
|
|
426
|
-
"""
|
|
427
|
-
INSERT INTO _pending_tool_calls (
|
|
428
|
-
id, org_id, user_id, scope, scope_hash, tool_name, dedup_key,
|
|
429
|
-
status, question_text, answer_format, args, tags, result,
|
|
430
|
-
embedding, superseded_by, resolved_at, expires_at, cache_until,
|
|
431
|
-
valid_until
|
|
432
|
-
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
433
|
-
""",
|
|
434
|
-
(
|
|
435
|
-
record.id,
|
|
436
|
-
record.org_id,
|
|
437
|
-
record.user_id,
|
|
438
|
-
_json_dumps(record.scope),
|
|
439
|
-
record.scope_hash,
|
|
440
|
-
record.tool_name,
|
|
441
|
-
record.dedup_key,
|
|
442
|
-
record.status.value,
|
|
443
|
-
record.question_text,
|
|
444
|
-
record.answer_format,
|
|
445
|
-
_json_dumps(record.args),
|
|
446
|
-
_json_dumps(record.tags),
|
|
447
|
-
_json_dumps(record.result),
|
|
448
|
-
_json_dumps(record.embedding),
|
|
449
|
-
record.superseded_by,
|
|
450
|
-
_dt_str(record.resolved_at),
|
|
451
|
-
_dt_str(record.expires_at),
|
|
452
|
-
_dt_str(record.cache_until),
|
|
453
|
-
_dt_str(record.valid_until),
|
|
454
|
-
),
|
|
455
|
-
)
|
|
456
|
-
self.conn.commit()
|
|
457
|
-
stored = self.get_pending_tool_call(record.id)
|
|
458
|
-
if stored is None: # pragma: no cover
|
|
459
|
-
raise RuntimeError(f"Failed to create pending tool call {record.id}")
|
|
460
|
-
return stored
|
|
461
|
-
|
|
462
|
-
def _insert_pending_tool_call_unlocked(self, record: PendingToolCallRecord) -> None:
|
|
463
|
-
self.conn.execute(
|
|
464
|
-
"""
|
|
465
|
-
INSERT INTO _pending_tool_calls (
|
|
466
|
-
id, org_id, user_id, scope, scope_hash, tool_name, dedup_key,
|
|
467
|
-
status, question_text, answer_format, args, tags, result,
|
|
468
|
-
embedding, superseded_by, resolved_at, expires_at, cache_until,
|
|
469
|
-
valid_until
|
|
470
|
-
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
471
|
-
""",
|
|
472
|
-
(
|
|
473
|
-
record.id,
|
|
474
|
-
record.org_id,
|
|
475
|
-
record.user_id,
|
|
476
|
-
_json_dumps(record.scope),
|
|
477
|
-
record.scope_hash,
|
|
478
|
-
record.tool_name,
|
|
479
|
-
record.dedup_key,
|
|
480
|
-
record.status.value,
|
|
481
|
-
record.question_text,
|
|
482
|
-
record.answer_format,
|
|
483
|
-
_json_dumps(record.args),
|
|
484
|
-
_json_dumps(record.tags),
|
|
485
|
-
_json_dumps(record.result),
|
|
486
|
-
_json_dumps(record.embedding),
|
|
487
|
-
record.superseded_by,
|
|
488
|
-
_dt_str(record.resolved_at),
|
|
489
|
-
_dt_str(record.expires_at),
|
|
490
|
-
_dt_str(record.cache_until),
|
|
491
|
-
_dt_str(record.valid_until),
|
|
492
|
-
),
|
|
493
|
-
)
|
|
494
|
-
|
|
495
|
-
@SQLiteStorageBase.handle_exceptions
|
|
496
|
-
def create_or_attach_pending_tool_call(
|
|
497
|
-
self,
|
|
498
|
-
*,
|
|
499
|
-
record: PendingToolCallRecord,
|
|
500
|
-
dependency: RunToolDependencyRecord,
|
|
501
|
-
now: datetime | None = None,
|
|
502
|
-
) -> PendingToolCallUpsertResult:
|
|
503
|
-
current = now or datetime.now(UTC)
|
|
504
|
-
created = False
|
|
505
|
-
pending_tool_call_id = record.id
|
|
506
|
-
with self._lock:
|
|
507
|
-
self.conn.execute("BEGIN IMMEDIATE")
|
|
508
|
-
try:
|
|
509
|
-
row = self.conn.execute(
|
|
510
|
-
"""
|
|
511
|
-
SELECT * FROM _pending_tool_calls
|
|
512
|
-
WHERE org_id = ?
|
|
513
|
-
AND scope_hash = ?
|
|
514
|
-
AND tool_name = ?
|
|
515
|
-
AND dedup_key = ?
|
|
516
|
-
AND status = ?
|
|
517
|
-
AND cache_until > ?
|
|
518
|
-
ORDER BY created_at ASC
|
|
519
|
-
LIMIT 1
|
|
520
|
-
""",
|
|
521
|
-
(
|
|
522
|
-
record.org_id,
|
|
523
|
-
record.scope_hash,
|
|
524
|
-
record.tool_name,
|
|
525
|
-
record.dedup_key,
|
|
526
|
-
PendingToolCallStatus.PENDING.value,
|
|
527
|
-
_dt_str(current),
|
|
528
|
-
),
|
|
529
|
-
).fetchone()
|
|
530
|
-
pending_tool_call_id = row["id"] if row is not None else record.id
|
|
531
|
-
if row is None:
|
|
532
|
-
self._insert_pending_tool_call_unlocked(record)
|
|
533
|
-
created = True
|
|
534
|
-
self.conn.execute(
|
|
535
|
-
"""
|
|
536
|
-
INSERT OR IGNORE INTO _run_tool_dependencies (
|
|
537
|
-
run_id, pending_tool_call_id, dependency_kind,
|
|
538
|
-
resolved_at, consumed_at
|
|
539
|
-
) VALUES (?,?,?,?,?)
|
|
540
|
-
""",
|
|
541
|
-
(
|
|
542
|
-
dependency.run_id,
|
|
543
|
-
pending_tool_call_id,
|
|
544
|
-
dependency.dependency_kind.value,
|
|
545
|
-
_dt_str(dependency.resolved_at),
|
|
546
|
-
_dt_str(dependency.consumed_at),
|
|
547
|
-
),
|
|
548
|
-
)
|
|
549
|
-
except Exception:
|
|
550
|
-
self.conn.rollback()
|
|
551
|
-
raise
|
|
552
|
-
else:
|
|
553
|
-
self.conn.commit()
|
|
554
|
-
|
|
555
|
-
stored = self.get_pending_tool_call(pending_tool_call_id)
|
|
556
|
-
if stored is None: # pragma: no cover
|
|
557
|
-
raise RuntimeError("Failed to create or attach pending tool call")
|
|
558
|
-
return PendingToolCallUpsertResult(pending_tool_call=stored, created=created)
|
|
559
|
-
|
|
560
|
-
@SQLiteStorageBase.handle_exceptions
|
|
561
|
-
def get_pending_tool_call(self, call_id: str) -> PendingToolCallRecord | None:
|
|
562
|
-
row = self._fetchone(
|
|
563
|
-
"SELECT * FROM _pending_tool_calls WHERE id = ?", (call_id,)
|
|
564
|
-
)
|
|
565
|
-
return _row_to_pending_tool_call(row) if row else None
|
|
566
|
-
|
|
567
|
-
@SQLiteStorageBase.handle_exceptions
|
|
568
|
-
def list_pending_tool_calls(
|
|
569
|
-
self,
|
|
570
|
-
*,
|
|
571
|
-
status: PendingToolCallStatus | None = None,
|
|
572
|
-
limit: int = 100,
|
|
573
|
-
) -> list[PendingToolCallRecord]:
|
|
574
|
-
bounded_limit = max(1, min(limit, 500))
|
|
575
|
-
params: list[Any] = [self.org_id]
|
|
576
|
-
status_clause = ""
|
|
577
|
-
if status is not None:
|
|
578
|
-
status_clause = "AND status = ?"
|
|
579
|
-
params.append(status.value)
|
|
580
|
-
params.append(bounded_limit)
|
|
581
|
-
rows = self._fetchall(
|
|
582
|
-
f"""
|
|
583
|
-
SELECT * FROM _pending_tool_calls
|
|
584
|
-
WHERE org_id = ?
|
|
585
|
-
{status_clause}
|
|
586
|
-
ORDER BY created_at DESC, id ASC
|
|
587
|
-
LIMIT ?
|
|
588
|
-
""",
|
|
589
|
-
tuple(params),
|
|
590
|
-
)
|
|
591
|
-
return [_row_to_pending_tool_call(row) for row in rows]
|
|
592
|
-
|
|
593
|
-
@SQLiteStorageBase.handle_exceptions
|
|
594
|
-
def cancel_pending_tool_call(
|
|
595
|
-
self,
|
|
596
|
-
call_id: str,
|
|
597
|
-
*,
|
|
598
|
-
cancelled_at: datetime | None = None,
|
|
599
|
-
) -> PendingToolCallRecord | None:
|
|
600
|
-
now = cancelled_at or datetime.now(UTC)
|
|
601
|
-
now_s = _dt_str(now)
|
|
602
|
-
with self._lock:
|
|
603
|
-
self.conn.execute(
|
|
604
|
-
"""
|
|
605
|
-
UPDATE _pending_tool_calls
|
|
606
|
-
SET status = ?
|
|
607
|
-
WHERE id = ?
|
|
608
|
-
AND status = ?
|
|
609
|
-
""",
|
|
610
|
-
(
|
|
611
|
-
PendingToolCallStatus.CANCELLED.value,
|
|
612
|
-
call_id,
|
|
613
|
-
PendingToolCallStatus.PENDING.value,
|
|
614
|
-
),
|
|
615
|
-
)
|
|
616
|
-
self.conn.execute(
|
|
617
|
-
"""
|
|
618
|
-
UPDATE _run_tool_dependencies
|
|
619
|
-
SET resolved_at = ?
|
|
620
|
-
WHERE pending_tool_call_id = ? AND resolved_at IS NULL
|
|
621
|
-
""",
|
|
622
|
-
(now_s, call_id),
|
|
623
|
-
)
|
|
624
|
-
self._finalize_runs_without_pending_dependencies_unlocked(now_s or "")
|
|
625
|
-
self.conn.commit()
|
|
626
|
-
return self.get_pending_tool_call(call_id)
|
|
627
|
-
|
|
628
|
-
@SQLiteStorageBase.handle_exceptions
|
|
629
|
-
def expire_pending_tool_calls(
|
|
630
|
-
self,
|
|
631
|
-
*,
|
|
632
|
-
now: datetime | None = None,
|
|
633
|
-
limit: int = 100,
|
|
634
|
-
) -> int:
|
|
635
|
-
current = now or datetime.now(UTC)
|
|
636
|
-
now_s = _dt_str(current)
|
|
637
|
-
bounded_limit = max(1, min(limit, 500))
|
|
638
|
-
with self._lock:
|
|
639
|
-
self.conn.execute("BEGIN IMMEDIATE")
|
|
640
|
-
try:
|
|
641
|
-
rows = self.conn.execute(
|
|
642
|
-
"""
|
|
643
|
-
SELECT id
|
|
644
|
-
FROM _pending_tool_calls
|
|
645
|
-
WHERE status = ?
|
|
646
|
-
AND expires_at IS NOT NULL
|
|
647
|
-
AND expires_at <= ?
|
|
648
|
-
ORDER BY expires_at ASC, created_at ASC, id ASC
|
|
649
|
-
LIMIT ?
|
|
650
|
-
""",
|
|
651
|
-
(
|
|
652
|
-
PendingToolCallStatus.PENDING.value,
|
|
653
|
-
now_s,
|
|
654
|
-
bounded_limit,
|
|
655
|
-
),
|
|
656
|
-
).fetchall()
|
|
657
|
-
call_ids = [row["id"] for row in rows]
|
|
658
|
-
if not call_ids:
|
|
659
|
-
self.conn.commit()
|
|
660
|
-
return 0
|
|
661
|
-
|
|
662
|
-
placeholders = ",".join("?" for _ in call_ids)
|
|
663
|
-
self.conn.execute(
|
|
664
|
-
f"""
|
|
665
|
-
UPDATE _pending_tool_calls
|
|
666
|
-
SET status = ?
|
|
667
|
-
WHERE id IN ({placeholders})
|
|
668
|
-
AND status = ?
|
|
669
|
-
""",
|
|
670
|
-
(
|
|
671
|
-
PendingToolCallStatus.EXPIRED.value,
|
|
672
|
-
*call_ids,
|
|
673
|
-
PendingToolCallStatus.PENDING.value,
|
|
674
|
-
),
|
|
675
|
-
)
|
|
676
|
-
self.conn.execute(
|
|
677
|
-
f"""
|
|
678
|
-
UPDATE _run_tool_dependencies
|
|
679
|
-
SET resolved_at = ?
|
|
680
|
-
WHERE pending_tool_call_id IN ({placeholders})
|
|
681
|
-
AND resolved_at IS NULL
|
|
682
|
-
""",
|
|
683
|
-
(now_s, *call_ids),
|
|
684
|
-
)
|
|
685
|
-
self._finalize_runs_without_pending_dependencies_unlocked(now_s or "")
|
|
686
|
-
except Exception:
|
|
687
|
-
self.conn.rollback()
|
|
688
|
-
raise
|
|
689
|
-
else:
|
|
690
|
-
self.conn.commit()
|
|
691
|
-
return len(call_ids)
|
|
692
|
-
|
|
693
|
-
@SQLiteStorageBase.handle_exceptions
|
|
694
|
-
def find_active_pending_tool_call(
|
|
695
|
-
self,
|
|
696
|
-
*,
|
|
697
|
-
org_id: str,
|
|
698
|
-
scope_hash: str,
|
|
699
|
-
tool_name: str,
|
|
700
|
-
dedup_key: str,
|
|
701
|
-
now: datetime | None = None,
|
|
702
|
-
) -> PendingToolCallRecord | None:
|
|
703
|
-
now_s = _dt_str(now or datetime.now(UTC))
|
|
704
|
-
row = self._fetchone(
|
|
705
|
-
"""
|
|
706
|
-
SELECT * FROM _pending_tool_calls
|
|
707
|
-
WHERE org_id = ?
|
|
708
|
-
AND scope_hash = ?
|
|
709
|
-
AND tool_name = ?
|
|
710
|
-
AND dedup_key = ?
|
|
711
|
-
AND status = ?
|
|
712
|
-
AND cache_until > ?
|
|
713
|
-
ORDER BY created_at ASC
|
|
714
|
-
LIMIT 1
|
|
715
|
-
""",
|
|
716
|
-
(
|
|
717
|
-
org_id,
|
|
718
|
-
scope_hash,
|
|
719
|
-
tool_name,
|
|
720
|
-
dedup_key,
|
|
721
|
-
PendingToolCallStatus.PENDING.value,
|
|
722
|
-
now_s,
|
|
723
|
-
),
|
|
724
|
-
)
|
|
725
|
-
return _row_to_pending_tool_call(row) if row else None
|
|
726
|
-
|
|
727
|
-
@SQLiteStorageBase.handle_exceptions
|
|
728
|
-
def search_prior_tool_calls(
|
|
729
|
-
self,
|
|
730
|
-
*,
|
|
731
|
-
org_id: str,
|
|
732
|
-
scope_hash: str,
|
|
733
|
-
tool_name: str,
|
|
734
|
-
query_embedding: list[float] | None = None,
|
|
735
|
-
now: datetime | None = None,
|
|
736
|
-
limit: int = 8,
|
|
737
|
-
) -> list[PriorAnswerMatch]:
|
|
738
|
-
current = now or datetime.now(UTC)
|
|
739
|
-
bounded_limit = max(1, min(limit, 50))
|
|
740
|
-
rows = self._fetchall(
|
|
741
|
-
"""
|
|
742
|
-
SELECT * FROM _pending_tool_calls
|
|
743
|
-
WHERE org_id = ?
|
|
744
|
-
AND scope_hash = ?
|
|
745
|
-
AND tool_name = ?
|
|
746
|
-
AND (
|
|
747
|
-
(
|
|
748
|
-
status = ?
|
|
749
|
-
AND (expires_at IS NULL OR expires_at > ?)
|
|
750
|
-
)
|
|
751
|
-
OR (
|
|
752
|
-
status = ?
|
|
753
|
-
AND (valid_until IS NULL OR valid_until > ?)
|
|
754
|
-
)
|
|
755
|
-
)
|
|
756
|
-
ORDER BY
|
|
757
|
-
CASE status WHEN ? THEN 0 ELSE 1 END,
|
|
758
|
-
COALESCE(resolved_at, created_at) DESC,
|
|
759
|
-
id ASC
|
|
760
|
-
""",
|
|
761
|
-
(
|
|
762
|
-
org_id,
|
|
763
|
-
scope_hash,
|
|
764
|
-
tool_name,
|
|
765
|
-
PendingToolCallStatus.PENDING.value,
|
|
766
|
-
_dt_str(current),
|
|
767
|
-
PendingToolCallStatus.RESOLVED.value,
|
|
768
|
-
_dt_str(current),
|
|
769
|
-
PendingToolCallStatus.RESOLVED.value,
|
|
770
|
-
),
|
|
771
|
-
)
|
|
772
|
-
seen_resolved_dedup_keys: set[str] = set()
|
|
773
|
-
records: list[PendingToolCallRecord] = []
|
|
774
|
-
for row in rows:
|
|
775
|
-
record = _row_to_pending_tool_call(row)
|
|
776
|
-
if record.status == PendingToolCallStatus.RESOLVED:
|
|
777
|
-
if record.dedup_key in seen_resolved_dedup_keys:
|
|
778
|
-
continue
|
|
779
|
-
seen_resolved_dedup_keys.add(record.dedup_key)
|
|
780
|
-
records.append(record)
|
|
781
|
-
matches = [
|
|
782
|
-
_record_to_prior_answer_match(record, query_embedding=query_embedding)
|
|
783
|
-
for record in records
|
|
784
|
-
]
|
|
785
|
-
if query_embedding:
|
|
786
|
-
matches.sort(
|
|
787
|
-
key=lambda match: (
|
|
788
|
-
match.similarity is not None,
|
|
789
|
-
match.similarity or -1.0,
|
|
790
|
-
match.resolved_at
|
|
791
|
-
or match.created_at
|
|
792
|
-
or datetime.min.replace(tzinfo=UTC),
|
|
793
|
-
),
|
|
794
|
-
reverse=True,
|
|
795
|
-
)
|
|
796
|
-
return matches[:bounded_limit]
|
|
797
|
-
|
|
798
|
-
@SQLiteStorageBase.handle_exceptions
|
|
799
|
-
def attach_run_tool_dependency(
|
|
800
|
-
self, record: RunToolDependencyRecord
|
|
801
|
-
) -> RunToolDependencyRecord:
|
|
802
|
-
with self._lock:
|
|
803
|
-
self.conn.execute(
|
|
804
|
-
"""
|
|
805
|
-
INSERT OR IGNORE INTO _run_tool_dependencies (
|
|
806
|
-
run_id, pending_tool_call_id, dependency_kind, resolved_at,
|
|
807
|
-
consumed_at
|
|
808
|
-
) VALUES (?,?,?,?,?)
|
|
809
|
-
""",
|
|
810
|
-
(
|
|
811
|
-
record.run_id,
|
|
812
|
-
record.pending_tool_call_id,
|
|
813
|
-
record.dependency_kind.value,
|
|
814
|
-
_dt_str(record.resolved_at),
|
|
815
|
-
_dt_str(record.consumed_at),
|
|
816
|
-
),
|
|
817
|
-
)
|
|
818
|
-
self.conn.commit()
|
|
819
|
-
row = self._fetchone(
|
|
820
|
-
"""
|
|
821
|
-
SELECT * FROM _run_tool_dependencies
|
|
822
|
-
WHERE run_id = ? AND pending_tool_call_id = ?
|
|
823
|
-
""",
|
|
824
|
-
(record.run_id, record.pending_tool_call_id),
|
|
825
|
-
)
|
|
826
|
-
if row is None: # pragma: no cover
|
|
827
|
-
raise RuntimeError("Failed to attach run tool dependency")
|
|
828
|
-
return _row_to_run_tool_dependency(row)
|
|
829
|
-
|
|
830
|
-
@SQLiteStorageBase.handle_exceptions
|
|
831
|
-
def count_unresolved_followup_dependencies(
|
|
832
|
-
self,
|
|
833
|
-
*,
|
|
834
|
-
org_id: str,
|
|
835
|
-
extractor_kind: str,
|
|
836
|
-
tool_name: str,
|
|
837
|
-
) -> int:
|
|
838
|
-
row = self._fetchone(
|
|
839
|
-
"""
|
|
840
|
-
SELECT COUNT(*) AS count
|
|
841
|
-
FROM _run_tool_dependencies d
|
|
842
|
-
JOIN _agent_runs r ON r.id = d.run_id
|
|
843
|
-
JOIN _pending_tool_calls p ON p.id = d.pending_tool_call_id
|
|
844
|
-
WHERE r.org_id = ?
|
|
845
|
-
AND r.extractor_kind = ?
|
|
846
|
-
AND p.tool_name = ?
|
|
847
|
-
AND p.status = ?
|
|
848
|
-
AND d.resolved_at IS NULL
|
|
849
|
-
AND d.consumed_at IS NULL
|
|
850
|
-
""",
|
|
851
|
-
(
|
|
852
|
-
org_id,
|
|
853
|
-
extractor_kind,
|
|
854
|
-
tool_name,
|
|
855
|
-
PendingToolCallStatus.PENDING.value,
|
|
856
|
-
),
|
|
857
|
-
)
|
|
858
|
-
return int(row["count"]) if row is not None else 0
|
|
859
|
-
|
|
860
|
-
@SQLiteStorageBase.handle_exceptions
|
|
861
|
-
def list_run_tool_dependencies(self, run_id: str) -> list[RunToolDependencyRecord]:
|
|
862
|
-
rows = self._fetchall(
|
|
863
|
-
"SELECT * FROM _run_tool_dependencies WHERE run_id = ?",
|
|
864
|
-
(run_id,),
|
|
865
|
-
)
|
|
866
|
-
return [_row_to_run_tool_dependency(row) for row in rows]
|
|
867
|
-
|
|
868
|
-
@SQLiteStorageBase.handle_exceptions
|
|
869
|
-
def resolve_pending_tool_call(
|
|
870
|
-
self,
|
|
871
|
-
call_id: str,
|
|
872
|
-
*,
|
|
873
|
-
result: dict[str, Any],
|
|
874
|
-
resolved_at: datetime | None = None,
|
|
875
|
-
valid_for_seconds: int,
|
|
876
|
-
) -> PendingToolCallRecord | None:
|
|
877
|
-
resolved = resolved_at or datetime.now(UTC)
|
|
878
|
-
valid_until = resolved + timedelta(seconds=valid_for_seconds)
|
|
879
|
-
with self._lock:
|
|
880
|
-
cur = self.conn.execute(
|
|
881
|
-
"""
|
|
882
|
-
UPDATE _pending_tool_calls
|
|
883
|
-
SET status = ?, result = ?, resolved_at = ?, valid_until = ?
|
|
884
|
-
WHERE id = ?
|
|
885
|
-
AND status = ?
|
|
886
|
-
""",
|
|
887
|
-
(
|
|
888
|
-
PendingToolCallStatus.RESOLVED.value,
|
|
889
|
-
_json_dumps(result),
|
|
890
|
-
_dt_str(resolved),
|
|
891
|
-
_dt_str(valid_until),
|
|
892
|
-
call_id,
|
|
893
|
-
PendingToolCallStatus.PENDING.value,
|
|
894
|
-
),
|
|
895
|
-
)
|
|
896
|
-
if cur.rowcount == 0:
|
|
897
|
-
self.conn.commit()
|
|
898
|
-
return self.get_pending_tool_call(call_id)
|
|
899
|
-
self.conn.execute(
|
|
900
|
-
"""
|
|
901
|
-
UPDATE _pending_tool_calls
|
|
902
|
-
SET status = ?,
|
|
903
|
-
superseded_by = ?
|
|
904
|
-
WHERE id != ?
|
|
905
|
-
AND status = ?
|
|
906
|
-
AND (valid_until IS NULL OR valid_until > ?)
|
|
907
|
-
AND (org_id, scope_hash, tool_name, dedup_key) = (
|
|
908
|
-
SELECT org_id, scope_hash, tool_name, dedup_key
|
|
909
|
-
FROM _pending_tool_calls
|
|
910
|
-
WHERE id = ?
|
|
911
|
-
)
|
|
912
|
-
""",
|
|
913
|
-
(
|
|
914
|
-
PendingToolCallStatus.SUPERSEDED.value,
|
|
915
|
-
call_id,
|
|
916
|
-
call_id,
|
|
917
|
-
PendingToolCallStatus.RESOLVED.value,
|
|
918
|
-
_dt_str(resolved),
|
|
919
|
-
call_id,
|
|
920
|
-
),
|
|
921
|
-
)
|
|
922
|
-
self.conn.execute(
|
|
923
|
-
"""
|
|
924
|
-
UPDATE _run_tool_dependencies
|
|
925
|
-
SET resolved_at = ?
|
|
926
|
-
WHERE pending_tool_call_id = ? AND resolved_at IS NULL
|
|
927
|
-
""",
|
|
928
|
-
(_dt_str(resolved), call_id),
|
|
929
|
-
)
|
|
930
|
-
self.conn.execute(
|
|
931
|
-
"""
|
|
932
|
-
UPDATE _agent_runs
|
|
933
|
-
SET status = ?, updated_at = ?
|
|
934
|
-
WHERE status IN (?, ?)
|
|
935
|
-
AND EXISTS (
|
|
936
|
-
SELECT 1
|
|
937
|
-
FROM _run_tool_dependencies d
|
|
938
|
-
WHERE d.run_id = _agent_runs.id
|
|
939
|
-
AND d.pending_tool_call_id = ?
|
|
940
|
-
AND d.resolved_at IS NOT NULL
|
|
941
|
-
AND d.consumed_at IS NULL
|
|
942
|
-
)
|
|
943
|
-
""",
|
|
944
|
-
(
|
|
945
|
-
AgentRunStatus.RESUME_READY.value,
|
|
946
|
-
self._current_timestamp(),
|
|
947
|
-
AgentRunStatus.FINALIZED.value,
|
|
948
|
-
AgentRunStatus.FINALIZED_PENDING_TOOL.value,
|
|
949
|
-
call_id,
|
|
950
|
-
),
|
|
951
|
-
)
|
|
952
|
-
self.conn.commit()
|
|
953
|
-
return self.get_pending_tool_call(call_id)
|
|
954
|
-
|
|
955
|
-
@SQLiteStorageBase.handle_exceptions
|
|
956
|
-
def update_resolved_pending_tool_call_result(
|
|
957
|
-
self,
|
|
958
|
-
call_id: str,
|
|
959
|
-
*,
|
|
960
|
-
result: dict[str, Any],
|
|
961
|
-
resolved_at: datetime | None = None,
|
|
962
|
-
valid_for_seconds: int,
|
|
963
|
-
) -> PendingToolCallRecord | None:
|
|
964
|
-
resolved = resolved_at or datetime.now(UTC)
|
|
965
|
-
valid_until = resolved + timedelta(seconds=valid_for_seconds)
|
|
966
|
-
now_s = _dt_str(resolved) or self._current_timestamp()
|
|
967
|
-
with self._lock:
|
|
968
|
-
self.conn.execute("BEGIN IMMEDIATE")
|
|
969
|
-
try:
|
|
970
|
-
cur = self.conn.execute(
|
|
971
|
-
"""
|
|
972
|
-
UPDATE _pending_tool_calls
|
|
973
|
-
SET result = ?, resolved_at = ?, valid_until = ?
|
|
974
|
-
WHERE id = ?
|
|
975
|
-
AND status = ?
|
|
976
|
-
""",
|
|
977
|
-
(
|
|
978
|
-
_json_dumps(result),
|
|
979
|
-
_dt_str(resolved),
|
|
980
|
-
_dt_str(valid_until),
|
|
981
|
-
call_id,
|
|
982
|
-
PendingToolCallStatus.RESOLVED.value,
|
|
983
|
-
),
|
|
984
|
-
)
|
|
985
|
-
if cur.rowcount == 0:
|
|
986
|
-
self.conn.commit()
|
|
987
|
-
return self.get_pending_tool_call(call_id)
|
|
988
|
-
self.conn.execute(
|
|
989
|
-
"""
|
|
990
|
-
UPDATE _pending_tool_calls
|
|
991
|
-
SET status = ?,
|
|
992
|
-
superseded_by = ?
|
|
993
|
-
WHERE id != ?
|
|
994
|
-
AND status = ?
|
|
995
|
-
AND (valid_until IS NULL OR valid_until > ?)
|
|
996
|
-
AND (org_id, scope_hash, tool_name, dedup_key) = (
|
|
997
|
-
SELECT org_id, scope_hash, tool_name, dedup_key
|
|
998
|
-
FROM _pending_tool_calls
|
|
999
|
-
WHERE id = ?
|
|
1000
|
-
)
|
|
1001
|
-
""",
|
|
1002
|
-
(
|
|
1003
|
-
PendingToolCallStatus.SUPERSEDED.value,
|
|
1004
|
-
call_id,
|
|
1005
|
-
call_id,
|
|
1006
|
-
PendingToolCallStatus.RESOLVED.value,
|
|
1007
|
-
_dt_str(resolved),
|
|
1008
|
-
call_id,
|
|
1009
|
-
),
|
|
1010
|
-
)
|
|
1011
|
-
self.conn.execute(
|
|
1012
|
-
"""
|
|
1013
|
-
UPDATE _run_tool_dependencies
|
|
1014
|
-
SET resolved_at = ?,
|
|
1015
|
-
consumed_at = NULL
|
|
1016
|
-
WHERE pending_tool_call_id = ?
|
|
1017
|
-
""",
|
|
1018
|
-
(_dt_str(resolved), call_id),
|
|
1019
|
-
)
|
|
1020
|
-
self._mark_runs_ready_with_actionable_dependencies_unlocked(
|
|
1021
|
-
now_s, pending_tool_call_id=call_id
|
|
1022
|
-
)
|
|
1023
|
-
except Exception:
|
|
1024
|
-
self.conn.rollback()
|
|
1025
|
-
raise
|
|
1026
|
-
else:
|
|
1027
|
-
self.conn.commit()
|
|
1028
|
-
return self.get_pending_tool_call(call_id)
|
|
1029
|
-
|
|
1030
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1031
|
-
def mark_pending_tool_call_not_applicable(
|
|
1032
|
-
self,
|
|
1033
|
-
call_id: str,
|
|
1034
|
-
*,
|
|
1035
|
-
resolved_at: datetime | None = None,
|
|
1036
|
-
valid_for_seconds: int,
|
|
1037
|
-
) -> PendingToolCallRecord | None:
|
|
1038
|
-
resolved = resolved_at or datetime.now(UTC)
|
|
1039
|
-
valid_until = resolved + timedelta(seconds=valid_for_seconds)
|
|
1040
|
-
now_s = _dt_str(resolved) or self._current_timestamp()
|
|
1041
|
-
with self._lock:
|
|
1042
|
-
self.conn.execute("BEGIN IMMEDIATE")
|
|
1043
|
-
try:
|
|
1044
|
-
cur = self.conn.execute(
|
|
1045
|
-
"""
|
|
1046
|
-
UPDATE _pending_tool_calls
|
|
1047
|
-
SET status = ?, result = ?, resolved_at = ?, valid_until = ?
|
|
1048
|
-
WHERE id = ?
|
|
1049
|
-
AND status IN (?, ?)
|
|
1050
|
-
""",
|
|
1051
|
-
(
|
|
1052
|
-
PendingToolCallStatus.RESOLVED.value,
|
|
1053
|
-
_json_dumps(not_applicable_tool_result()),
|
|
1054
|
-
_dt_str(resolved),
|
|
1055
|
-
_dt_str(valid_until),
|
|
1056
|
-
call_id,
|
|
1057
|
-
PendingToolCallStatus.PENDING.value,
|
|
1058
|
-
PendingToolCallStatus.RESOLVED.value,
|
|
1059
|
-
),
|
|
1060
|
-
)
|
|
1061
|
-
if cur.rowcount == 0:
|
|
1062
|
-
self.conn.commit()
|
|
1063
|
-
return self.get_pending_tool_call(call_id)
|
|
1064
|
-
self.conn.execute(
|
|
1065
|
-
"""
|
|
1066
|
-
UPDATE _run_tool_dependencies
|
|
1067
|
-
SET resolved_at = COALESCE(resolved_at, ?),
|
|
1068
|
-
consumed_at = ?
|
|
1069
|
-
WHERE pending_tool_call_id = ?
|
|
1070
|
-
AND consumed_at IS NULL
|
|
1071
|
-
""",
|
|
1072
|
-
(_dt_str(resolved), _dt_str(resolved), call_id),
|
|
1073
|
-
)
|
|
1074
|
-
self._mark_runs_ready_with_actionable_dependencies_unlocked(
|
|
1075
|
-
now_s, pending_tool_call_id=call_id
|
|
1076
|
-
)
|
|
1077
|
-
self._finalize_runs_without_actionable_dependencies_unlocked(
|
|
1078
|
-
now_s, pending_tool_call_id=call_id
|
|
1079
|
-
)
|
|
1080
|
-
except Exception:
|
|
1081
|
-
self.conn.rollback()
|
|
1082
|
-
raise
|
|
1083
|
-
else:
|
|
1084
|
-
self.conn.commit()
|
|
1085
|
-
return self.get_pending_tool_call(call_id)
|
|
1086
|
-
|
|
1087
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1088
|
-
def claim_ready_agent_run(
|
|
1089
|
-
self,
|
|
1090
|
-
*,
|
|
1091
|
-
org_id: str,
|
|
1092
|
-
worker_id: str,
|
|
1093
|
-
now: datetime | None = None,
|
|
1094
|
-
claim_ttl_seconds: int = 600,
|
|
1095
|
-
) -> AgentRunRecord | None:
|
|
1096
|
-
current = now or datetime.now(UTC)
|
|
1097
|
-
stale_before = current - timedelta(seconds=claim_ttl_seconds)
|
|
1098
|
-
with self._lock:
|
|
1099
|
-
row = self.conn.execute(
|
|
1100
|
-
"""
|
|
1101
|
-
SELECT r.*
|
|
1102
|
-
FROM _agent_runs r
|
|
1103
|
-
WHERE r.org_id = ?
|
|
1104
|
-
AND (
|
|
1105
|
-
r.status = ?
|
|
1106
|
-
OR (r.status = ? AND r.claimed_at < ?)
|
|
1107
|
-
)
|
|
1108
|
-
AND (r.next_resume_at IS NULL OR r.next_resume_at <= ?)
|
|
1109
|
-
AND EXISTS (
|
|
1110
|
-
SELECT 1
|
|
1111
|
-
FROM _run_tool_dependencies d
|
|
1112
|
-
JOIN _pending_tool_calls p
|
|
1113
|
-
ON p.id = d.pending_tool_call_id
|
|
1114
|
-
WHERE d.run_id = r.id
|
|
1115
|
-
AND d.resolved_at IS NOT NULL
|
|
1116
|
-
AND d.consumed_at IS NULL
|
|
1117
|
-
AND p.status = ?
|
|
1118
|
-
AND COALESCE(json_extract(p.result, '$.not_applicable'), 0) != 1
|
|
1119
|
-
)
|
|
1120
|
-
ORDER BY
|
|
1121
|
-
r.org_id ASC,
|
|
1122
|
-
r.extractor_kind ASC,
|
|
1123
|
-
COALESCE(r.user_id, '') ASC,
|
|
1124
|
-
COALESCE(r.window_start_interaction_id, 0) ASC,
|
|
1125
|
-
r.updated_at ASC
|
|
1126
|
-
LIMIT 1
|
|
1127
|
-
""",
|
|
1128
|
-
(
|
|
1129
|
-
org_id,
|
|
1130
|
-
AgentRunStatus.RESUME_READY.value,
|
|
1131
|
-
AgentRunStatus.RESUMING.value,
|
|
1132
|
-
_dt_str(stale_before),
|
|
1133
|
-
_dt_str(current),
|
|
1134
|
-
PendingToolCallStatus.RESOLVED.value,
|
|
1135
|
-
),
|
|
1136
|
-
).fetchone()
|
|
1137
|
-
if row is None:
|
|
1138
|
-
return None
|
|
1139
|
-
self.conn.execute(
|
|
1140
|
-
"""
|
|
1141
|
-
UPDATE _agent_runs
|
|
1142
|
-
SET status = ?,
|
|
1143
|
-
claimed_by = ?,
|
|
1144
|
-
claimed_at = ?,
|
|
1145
|
-
resume_attempts = resume_attempts + 1,
|
|
1146
|
-
updated_at = ?
|
|
1147
|
-
WHERE id = ?
|
|
1148
|
-
""",
|
|
1149
|
-
(
|
|
1150
|
-
AgentRunStatus.RESUMING.value,
|
|
1151
|
-
worker_id,
|
|
1152
|
-
_dt_str(current),
|
|
1153
|
-
self._current_timestamp(),
|
|
1154
|
-
row["id"],
|
|
1155
|
-
),
|
|
1156
|
-
)
|
|
1157
|
-
self.conn.commit()
|
|
1158
|
-
return self.get_agent_run(row["id"])
|
|
1159
|
-
|
|
1160
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1161
|
-
def claim_finalization_failed_agent_run(
|
|
1162
|
-
self,
|
|
1163
|
-
*,
|
|
1164
|
-
org_id: str,
|
|
1165
|
-
worker_id: str,
|
|
1166
|
-
now: datetime | None = None,
|
|
1167
|
-
claim_ttl_seconds: int = 600,
|
|
1168
|
-
) -> AgentRunRecord | None:
|
|
1169
|
-
current = now or datetime.now(UTC)
|
|
1170
|
-
stale_before = current - timedelta(seconds=claim_ttl_seconds)
|
|
1171
|
-
with self._lock:
|
|
1172
|
-
# Claim runs that still need their committed output finalized:
|
|
1173
|
-
# - FINALIZATION_FAILED: an explicit retry is due.
|
|
1174
|
-
# - stale FINALIZING: a worker crashed mid-finalize.
|
|
1175
|
-
# - stale AGENT_COMPLETED: publish-time finalization never ran or
|
|
1176
|
-
# crashed before flipping the status (the run row carries
|
|
1177
|
-
# committed_output but was orphaned); the staleness guard ensures
|
|
1178
|
-
# we never race an in-flight publish-time finalize.
|
|
1179
|
-
row = self.conn.execute(
|
|
1180
|
-
"""
|
|
1181
|
-
SELECT *
|
|
1182
|
-
FROM _agent_runs
|
|
1183
|
-
WHERE org_id = ?
|
|
1184
|
-
AND (
|
|
1185
|
-
status = ?
|
|
1186
|
-
OR (status = ? AND claimed_at < ?)
|
|
1187
|
-
OR (status = ? AND updated_at < ?)
|
|
1188
|
-
)
|
|
1189
|
-
AND committed_output IS NOT NULL
|
|
1190
|
-
AND (next_resume_at IS NULL OR next_resume_at <= ?)
|
|
1191
|
-
ORDER BY
|
|
1192
|
-
org_id ASC,
|
|
1193
|
-
extractor_kind ASC,
|
|
1194
|
-
COALESCE(user_id, '') ASC,
|
|
1195
|
-
COALESCE(window_start_interaction_id, 0) ASC,
|
|
1196
|
-
updated_at ASC
|
|
1197
|
-
LIMIT 1
|
|
1198
|
-
""",
|
|
1199
|
-
(
|
|
1200
|
-
org_id,
|
|
1201
|
-
AgentRunStatus.FINALIZATION_FAILED.value,
|
|
1202
|
-
AgentRunStatus.FINALIZING.value,
|
|
1203
|
-
_dt_str(stale_before),
|
|
1204
|
-
AgentRunStatus.AGENT_COMPLETED.value,
|
|
1205
|
-
_dt_str(stale_before),
|
|
1206
|
-
_dt_str(current),
|
|
1207
|
-
),
|
|
1208
|
-
).fetchone()
|
|
1209
|
-
if row is None:
|
|
1210
|
-
return None
|
|
1211
|
-
self.conn.execute(
|
|
1212
|
-
"""
|
|
1213
|
-
UPDATE _agent_runs
|
|
1214
|
-
SET status = ?,
|
|
1215
|
-
claimed_by = ?,
|
|
1216
|
-
claimed_at = ?,
|
|
1217
|
-
updated_at = ?
|
|
1218
|
-
WHERE id = ?
|
|
1219
|
-
""",
|
|
1220
|
-
(
|
|
1221
|
-
AgentRunStatus.FINALIZING.value,
|
|
1222
|
-
worker_id,
|
|
1223
|
-
_dt_str(current),
|
|
1224
|
-
self._current_timestamp(),
|
|
1225
|
-
row["id"],
|
|
1226
|
-
),
|
|
1227
|
-
)
|
|
1228
|
-
self.conn.commit()
|
|
1229
|
-
return self.get_agent_run(row["id"])
|
|
1230
|
-
|
|
1231
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1232
|
-
def consume_run_tool_dependencies(self, run_id: str) -> int:
|
|
1233
|
-
consumed_at = self._current_timestamp()
|
|
1234
|
-
with self._lock:
|
|
1235
|
-
cur = self.conn.execute(
|
|
1236
|
-
"""
|
|
1237
|
-
UPDATE _run_tool_dependencies
|
|
1238
|
-
SET consumed_at = ?
|
|
1239
|
-
WHERE run_id = ?
|
|
1240
|
-
AND resolved_at IS NOT NULL
|
|
1241
|
-
AND consumed_at IS NULL
|
|
1242
|
-
""",
|
|
1243
|
-
(consumed_at, run_id),
|
|
1244
|
-
)
|
|
1245
|
-
self.conn.commit()
|
|
1246
|
-
return int(cur.rowcount)
|
|
1247
|
-
|
|
1248
|
-
@SQLiteStorageBase.handle_exceptions
|
|
1249
|
-
def list_resumable_work_org_ids(
|
|
1250
|
-
self,
|
|
1251
|
-
*,
|
|
1252
|
-
now: datetime | None = None,
|
|
1253
|
-
limit: int = 1000,
|
|
1254
|
-
) -> list[str]:
|
|
1255
|
-
current = now or datetime.now(UTC)
|
|
1256
|
-
now_s = _dt_str(current)
|
|
1257
|
-
bounded_limit = max(1, min(limit, 10_000))
|
|
1258
|
-
# Cross-org maintenance query. Surfaces any org that has a run ready to
|
|
1259
|
-
# resume / awaiting finalization, or a pending tool call due to expire.
|
|
1260
|
-
rows = self._fetchall(
|
|
1261
|
-
"""
|
|
1262
|
-
SELECT DISTINCT org_id FROM (
|
|
1263
|
-
SELECT r.org_id
|
|
1264
|
-
FROM _agent_runs r
|
|
1265
|
-
WHERE r.status IN (?, ?)
|
|
1266
|
-
AND EXISTS (
|
|
1267
|
-
SELECT 1
|
|
1268
|
-
FROM _run_tool_dependencies d
|
|
1269
|
-
JOIN _pending_tool_calls p
|
|
1270
|
-
ON p.id = d.pending_tool_call_id
|
|
1271
|
-
WHERE d.run_id = r.id
|
|
1272
|
-
AND d.resolved_at IS NOT NULL
|
|
1273
|
-
AND d.consumed_at IS NULL
|
|
1274
|
-
AND p.status = ?
|
|
1275
|
-
AND COALESCE(json_extract(p.result, '$.not_applicable'), 0) != 1
|
|
1276
|
-
)
|
|
1277
|
-
UNION
|
|
1278
|
-
SELECT org_id FROM _agent_runs
|
|
1279
|
-
WHERE status IN (?, ?, ?)
|
|
1280
|
-
UNION
|
|
1281
|
-
SELECT org_id FROM _pending_tool_calls
|
|
1282
|
-
WHERE status = ?
|
|
1283
|
-
AND expires_at IS NOT NULL
|
|
1284
|
-
AND expires_at <= ?
|
|
1285
|
-
)
|
|
1286
|
-
ORDER BY org_id ASC
|
|
1287
|
-
LIMIT ?
|
|
1288
|
-
""",
|
|
1289
|
-
(
|
|
1290
|
-
AgentRunStatus.RESUME_READY.value,
|
|
1291
|
-
AgentRunStatus.RESUMING.value,
|
|
1292
|
-
PendingToolCallStatus.RESOLVED.value,
|
|
1293
|
-
AgentRunStatus.FINALIZATION_FAILED.value,
|
|
1294
|
-
AgentRunStatus.FINALIZING.value,
|
|
1295
|
-
AgentRunStatus.AGENT_COMPLETED.value,
|
|
1296
|
-
PendingToolCallStatus.PENDING.value,
|
|
1297
|
-
now_s,
|
|
1298
|
-
bounded_limit,
|
|
1299
|
-
),
|
|
1300
|
-
)
|
|
1301
|
-
return [row["org_id"] for row in rows]
|