fixxxeeess

This commit is contained in:
pavel 2026-02-10 20:57:03 +01:00
commit c4d9ca19ce
8 changed files with 142 additions and 54 deletions

View file

@ -19,13 +19,13 @@ impl Agent {
zen_api_key: Option<String>,
tavily_api_key: Option<String>,
initial_message: String,
) -> Self {
) -> Result<Self, Box<dyn std::error::Error>> {
let intro = format!(
"You are an autonomous agent. You have access to tools that can help
you achieve your goals. Use them wisely. The user is unable to respond to you
so do not ask for clarification and use the
answer tool once you to give your final answer. current date is {}",
Utc::now().to_rfc3339()
Utc::now().format("%B %d, %Y %H:%M:%S UTC").to_string()
);
println!("initial_message: {}", intro);
let messages = vec![
@ -45,8 +45,12 @@ impl Agent {
let tools = Some(tools::get_tools());
Self {
client: reqwest::Client::new(),
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(60))
.build()?;
Ok(Self {
client,
url: "https://opencode.ai/zen/v1/chat/completions".to_string(),
zen_api_key,
tavily_api_key,
@ -54,7 +58,7 @@ impl Agent {
tools,
logs: String::new(),
answer: None,
}
})
}
fn log(&mut self, message: &str) {

View file

@ -72,7 +72,9 @@ pub async fn perform_search(
query: &str,
api_key: &str,
) -> Result<String, Box<dyn std::error::Error>> {
let client = reqwest::Client::new();
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(30))
.build()?;
let response = client
.post("https://api.tavily.com/search")
.json(&serde_json::json!({

View file

@ -15,11 +15,13 @@ pub struct Claims {
#[derive(Debug, Deserialize)]
struct Jwk {
kty: String,
#[serde(rename = "kty")]
_kty: String,
kid: String,
n: String,
e: String,
alg: Option<String>,
#[serde(rename = "alg")]
_alg: Option<String>,
}
#[derive(Debug, Deserialize)]
@ -146,4 +148,27 @@ impl Authenticator {
Ok(res)
}
pub async fn refresh_token(
&self,
refresh_token: String,
) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
let params = [
("grant_type", "refresh_token"),
("refresh_token", &refresh_token),
("client_id", &self.client_id),
("client_secret", &self.client_secret),
];
let res = self
.client
.post(&self.token_url)
.form(&params)
.send()
.await?
.json()
.await?;
Ok(res)
}
}

View file

@ -95,7 +95,7 @@ impl Scheduler {
run.insert(&db).await?;
// Start agent in background
let mut agent = Agent::new(zen_key, tavily_key, task.goal.clone());
let mut agent = Agent::new(zen_key, tavily_key, task.goal.clone())?;
tokio::spawn(async move {
let (logs, answer) = match agent.run().await {

View file

@ -127,6 +127,7 @@ pub async fn start(db_url: &str) -> Result<(), Box<dyn std::error::Error>> {
.route("/tasks/:id/runs", post(rerun_task))
.route("/runs/recent", get(get_recent_runs))
.route("/auth/callback", get(auth_callback))
.route("/auth/refresh", post(auth_refresh))
.layer(cors)
.with_state(state);
@ -150,21 +151,24 @@ async fn list_tasks(
let response = tasks
.into_iter()
.map(|(t, runs)| TaskResponse {
id: t.id,
goal: t.goal,
cron: t.cron,
created_at: t.created_at,
runs: runs
.into_iter()
.map(|r| TaskRunResponse {
id: r.id,
status: r.status,
logs: r.logs,
answer: r.answer,
created_at: r.created_at,
})
.collect(),
.map(|(t, mut runs)| {
runs.sort_by(|a, b| b.created_at.cmp(&a.created_at));
TaskResponse {
id: t.id,
goal: t.goal,
cron: t.cron,
created_at: t.created_at,
runs: runs
.into_iter()
.map(|r| TaskRunResponse {
id: r.id,
status: r.status,
logs: r.logs,
answer: r.answer,
created_at: r.created_at,
})
.collect(),
}
})
.collect();
@ -240,7 +244,8 @@ async fn execute_agent_run(
state.zen_api_key.clone(),
state.tavily_api_key.clone(),
goal.clone(),
);
)
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
let (logs, answer) = match agent.run().await {
Ok((logs, answer)) => (logs, answer),
@ -298,6 +303,7 @@ async fn update_task(
get_task_inner(id, &state).await.map(Json)
}
#[allow(dead_code)]
pub struct AuthenticatedUser(pub crate::auth::Claims);
#[axum::async_trait]
@ -342,6 +348,23 @@ pub struct AuthCallbackQuery {
pub redirect_uri: String,
}
#[derive(Deserialize)]
struct RefreshRequest {
refresh_token: String,
}
async fn auth_refresh(
State(state): State<Arc<AppState>>,
Json(payload): Json<RefreshRequest>,
) -> Result<Json<serde_json::Value>, (StatusCode, String)> {
state
.authenticator
.refresh_token(payload.refresh_token)
.await
.map(Json)
.map_err(|e| (StatusCode::UNAUTHORIZED, e.to_string()))
}
async fn auth_callback(
State(state): State<Arc<AppState>>,
Query(query): Query<AuthCallbackQuery>,
@ -374,11 +397,13 @@ async fn get_task_inner(id: Uuid, state: &AppState) -> Result<TaskResponse, (Sta
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
let (t, runs) = results
let (t, mut runs) = results
.into_iter()
.next()
.ok_or((StatusCode::NOT_FOUND, "Task not found".to_string()))?;
runs.sort_by(|a, b| b.created_at.cmp(&a.created_at));
Ok(TaskResponse {
id: t.id,
goal: t.goal,

View file

@ -44,7 +44,6 @@ pub async fn handle_tool_call(
tool_call: &ToolCall,
tavily_api_key: &Option<String>,
) -> Result<(Message, bool, Option<String>), Box<dyn std::error::Error>> {
let mut file_written = false;
let mut answer = None;
let name = &tool_call.function.name;
@ -66,30 +65,14 @@ pub async fn handle_tool_call(
"Error: TAVILY_API_KEY is not set. Cannot perform real search.".to_string()
};
(search_result, false)
} else if name == "write_file" {
let args: HashMap<String, String> = serde_json::from_str(&tool_call.function.arguments)?;
let path = args.get("path").ok_or("Missing path argument")?;
let content = args.get("content").ok_or("Missing content argument")?;
println!("--- Executing tool: write_file(path: \"{}\") ---", path);
let write_result = match std::fs::write(path, content) {
Ok(_) => {
file_written = true;
format!("Successfully wrote content to {}", path)
}
Err(e) => format!("Error writing to {}: {}", path, e),
};
(write_result, file_written)
} else if name == "finish" {
let args: HashMap<String, String> = serde_json::from_str(&tool_call.function.arguments)?;
let result = args.get("result").ok_or("Missing result argument")?;
println!("--- Finishing task: {}", result);
file_written = true;
answer = Some(result.clone());
(result.clone(), file_written)
(result.clone(), true)
} else {
(format!("Error: Unknown tool {}", name), false)
};