fixxxeeess
This commit is contained in:
parent
643203b8a5
commit
c4d9ca19ce
8 changed files with 142 additions and 54 deletions
14
src/agent.rs
14
src/agent.rs
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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!({
|
||||
|
|
|
|||
29
src/auth.rs
29
src/auth.rs
|
|
@ -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(¶ms)
|
||||
.send()
|
||||
.await?
|
||||
.json()
|
||||
.await?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
19
src/tools.rs
19
src/tools.rs
|
|
@ -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)
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue