mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-27 08:00:17 +00:00
* refactor: split large files and consolidate test stubs for contributor velocity - Extract 7 Database sub-traits (ConversationStore, JobStore, SandboxStore, RoutineStore, ToolFailureStore, SettingsStore, WorkspaceStore) with Database as a supertrait combining them all - Split libsql_backend.rs (2769 lines) into src/db/libsql/ directory with one file per sub-trait implementation - Split config.rs (1753 lines) into src/config/ directory with 16 domain files - Consolidate 3 duplicate test LLM stubs into shared StubLlm in src/testing.rs - Split server.rs handlers into src/channels/web/handlers/ directory - Extract main.rs init phases into AppBuilder (src/app.rs) - Add developer setup script (scripts/dev-setup.sh) Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: move heartbeat test from examples/ to tests/ Convert standalone example binary into a proper #[ignore] integration test, matching the convention of the other integration tests. Co-Authored-By: Claude Opus 4.6 <[email protected]> * style: fix rustfmt formatting for CI Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address PR review comments from Copilot - tunnel.rs: replace .ok().flatten() with ? to propagate env var errors - secrets.rs: remove misleading "process-wide cache" comment - database.rs: use uppercase "DATABASE_URL" in error key - testing.rs: gate harness tests with #[cfg(feature = "libsql")] Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Illia Polosukhin <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]>
98 lines
3.3 KiB
Rust
98 lines
3.3 KiB
Rust
//! Tool failure-related ToolFailureStore implementation for LibSqlBackend.
|
|
|
|
use async_trait::async_trait;
|
|
use libsql::params;
|
|
use uuid::Uuid;
|
|
|
|
use super::{LibSqlBackend, fmt_ts, get_i64, get_opt_text, get_text, get_ts};
|
|
use crate::agent::BrokenTool;
|
|
use crate::db::ToolFailureStore;
|
|
use crate::error::DatabaseError;
|
|
|
|
use chrono::Utc;
|
|
|
|
#[async_trait]
|
|
impl ToolFailureStore for LibSqlBackend {
|
|
async fn record_tool_failure(
|
|
&self,
|
|
tool_name: &str,
|
|
error_message: &str,
|
|
) -> Result<(), DatabaseError> {
|
|
let conn = self.connect().await?;
|
|
let now = fmt_ts(&Utc::now());
|
|
conn.execute(
|
|
r#"
|
|
INSERT INTO tool_failures (id, tool_name, error_message, error_count, last_failure)
|
|
VALUES (?1, ?2, ?3, 1, ?4)
|
|
ON CONFLICT (tool_name) DO UPDATE SET
|
|
error_message = ?3,
|
|
error_count = tool_failures.error_count + 1,
|
|
last_failure = ?4
|
|
"#,
|
|
params![Uuid::new_v4().to_string(), tool_name, error_message, now],
|
|
)
|
|
.await
|
|
.map_err(|e| DatabaseError::Query(e.to_string()))?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn get_broken_tools(&self, threshold: i32) -> Result<Vec<BrokenTool>, DatabaseError> {
|
|
let conn = self.connect().await?;
|
|
let mut rows = conn
|
|
.query(
|
|
r#"
|
|
SELECT tool_name, error_message, error_count, first_failure, last_failure,
|
|
last_build_result, repair_attempts
|
|
FROM tool_failures
|
|
WHERE error_count >= ?1 AND repaired_at IS NULL
|
|
ORDER BY error_count DESC
|
|
"#,
|
|
params![threshold as i64],
|
|
)
|
|
.await
|
|
.map_err(|e| DatabaseError::Query(e.to_string()))?;
|
|
|
|
let mut tools = Vec::new();
|
|
while let Some(row) = rows
|
|
.next()
|
|
.await
|
|
.map_err(|e| DatabaseError::Query(e.to_string()))?
|
|
{
|
|
tools.push(BrokenTool {
|
|
name: get_text(&row, 0),
|
|
last_error: get_opt_text(&row, 1),
|
|
failure_count: get_i64(&row, 2) as u32,
|
|
first_failure: get_ts(&row, 3),
|
|
last_failure: get_ts(&row, 4),
|
|
last_build_result: get_opt_text(&row, 5)
|
|
.and_then(|s| serde_json::from_str(&s).ok()),
|
|
repair_attempts: get_i64(&row, 6) as u32,
|
|
});
|
|
}
|
|
Ok(tools)
|
|
}
|
|
|
|
async fn mark_tool_repaired(&self, tool_name: &str) -> Result<(), DatabaseError> {
|
|
let conn = self.connect().await?;
|
|
let now = fmt_ts(&Utc::now());
|
|
conn.execute(
|
|
"UPDATE tool_failures SET repaired_at = ?2, error_count = 0 WHERE tool_name = ?1",
|
|
params![tool_name, now],
|
|
)
|
|
.await
|
|
.map_err(|e| DatabaseError::Query(e.to_string()))?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn increment_repair_attempts(&self, tool_name: &str) -> Result<(), DatabaseError> {
|
|
let conn = self.connect().await?;
|
|
conn.execute(
|
|
"UPDATE tool_failures SET repair_attempts = repair_attempts + 1 WHERE tool_name = ?1",
|
|
params![tool_name],
|
|
)
|
|
.await
|
|
.map_err(|e| DatabaseError::Query(e.to_string()))?;
|
|
Ok(())
|
|
}
|
|
}
|