bot/src/domain/agent/api.rs
2026-02-11 16:43:05 +01:00

116 lines
2.9 KiB
Rust

use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Message {
pub role: String,
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_calls: Option<Vec<ToolCall>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_call_id: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ToolCall {
pub id: String,
#[serde(rename = "type")]
pub call_type: String,
pub function: FunctionCall,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FunctionCall {
pub name: String,
pub arguments: String,
}
#[derive(Serialize, Debug)]
pub struct ChatRequest {
pub model: String,
pub messages: Vec<Message>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<Tool>>,
}
#[derive(Deserialize, Debug)]
pub struct ChatResponse {
pub choices: Vec<Choice>,
}
#[derive(Deserialize, Debug)]
pub struct Choice {
pub message: Message,
}
#[derive(Serialize, Debug, Clone)]
pub struct Tool {
#[serde(rename = "type")]
pub tool_type: String,
pub function: FunctionDefinition,
}
#[derive(Serialize, Debug, Clone)]
pub struct FunctionDefinition {
pub name: String,
pub description: String,
pub parameters: serde_json::Value,
}
#[derive(Deserialize, Debug)]
struct TavilyResponse {
results: Vec<TavilyResult>,
}
#[derive(Deserialize, Debug)]
struct TavilyResult {
title: String,
url: String,
content: String,
}
pub async fn perform_search(
query: &str,
api_key: &str,
) -> Result<String, Box<dyn std::error::Error>> {
tracing::info!(query = %query, "Performing Tavily web search");
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!({
"api_key": api_key,
"query": query,
"search_depth": "basic",
"include_answer": false,
"include_images": false,
"include_raw_content": false,
"max_results": 5,
}))
.send()
.await?;
if !response.status().is_success() {
return Err(format!("Tavily API error: {}", response.status()).into());
}
let search_data: TavilyResponse = response.json().await?;
tracing::info!("Web search yielded {} results", search_data.results.len());
let mut results_text = String::new();
for (i, result) in search_data.results.iter().enumerate() {
results_text.push_str(&format!(
"{}. {} ({})\nSnippet: {}\n\n",
i + 1,
result.title,
result.url,
result.content
));
}
if results_text.is_empty() {
Ok("No results found.".to_string())
} else {
Ok(results_text)
}
}