mod mutation;
mod pob;
mod trade;
use std::collections::HashMap;
use async_trait::async_trait;
use crate::llm::ToolDefinition;
use crate::pob_parser::PobParser;
use crate::trade::TradeClient;
pub struct ToolContext<'a> {
pub parser: &'a PobParser,
pub build_xml: &'a [u8],
pub trade: Option<&'a TradeClient>,
}
pub struct BuildMutation {
pub xml: String,
pub label: String,
}
pub struct ToolResult {
pub response: serde_json::Value,
pub mutation: Option<BuildMutation>,
}
#[async_trait]
pub trait Tool: Send + Sync {
fn definition(&self) -> ToolDefinition;
async fn execute(&self, ctx: &ToolContext<'_>, args: &str) -> Result<ToolResult, String>;
}
pub struct ToolRegistry {
tools: Vec<Box<dyn Tool>>,
index: HashMap<String, usize>,
}
impl ToolRegistry {
pub fn new(has_trade: bool) -> Self {
let mut tools: Vec<Box<dyn Tool>> = Vec::new();
pob::register(&mut tools);
mutation::register(&mut tools);
if has_trade {
trade::register(&mut tools);
}
let index = tools
.iter()
.enumerate()
.map(|(i, t)| (t.definition().name.clone(), i))
.collect();
Self { tools, index }
}
pub fn definitions(&self) -> Vec<ToolDefinition> {
self.tools.iter().map(|t| t.definition()).collect()
}
pub async fn execute(
&self,
ctx: &ToolContext<'_>,
tool_name: &str,
args: &str,
) -> Result<ToolResult, String> {
let idx = self
.index
.get(tool_name)
.ok_or_else(|| format!("unknown tool: {tool_name}"))?;
self.tools[*idx].execute(ctx, args).await
}
}
fn parse_args(args: &str) -> Result<serde_json::Value, String> {
serde_json::from_str(args).map_err(|e| format!("invalid arguments: {e}"))
}
async fn pob_query(
ctx: &ToolContext<'_>,
query: crate::pob_parser::PobQuery,
) -> Result<ToolResult, String> {
ctx.parser
.query(ctx.build_xml, query)
.await
.map(|v| ToolResult {
response: v,
mutation: None,
})
.map_err(|e| e.to_string())
}