development
rs 77 lines 2.28 KB
Raw
1 //! SplitFire's tools: the music theory engine, audio analysis and the separation library.
2 //! Workspace files and shell, MCP and approval come from `ed-acp`.
3
4 pub mod audio;
5 pub mod library;
6 pub mod theory;
7
8 use agent_client_protocol::schema::v1::{ToolCallLocation, ToolKind};
9 use anyhow::{Result, anyhow};
10 use async_trait::async_trait;
11 use serde_json::Value;
12
13 pub use ed_acp::{
14 DescribeCtx, ToolCtx, ToolOutcome, Toolset, absolutize, function_def, resolve_in,
15 };
16
17 /// The music tools as one [`Toolset`]: six `theory_*` tools, `analyze_audio` and
18 /// `list_separations`. All are read-only and never ask for approval.
19 #[derive(Debug, Default, Clone, Copy)]
20 pub struct MusicTools;
21
22 #[async_trait]
23 impl Toolset for MusicTools {
24 fn definitions(&self) -> Vec<Value> {
25 let mut defs = theory::definitions();
26 defs.push(audio::definition());
27 defs.push(library::definition());
28 defs
29 }
30
31 fn handles(&self, name: &str) -> bool {
32 name.starts_with("theory_") || name == audio::NAME || name == library::NAME
33 }
34
35 fn describe(
36 &self,
37 name: &str,
38 args: &Value,
39 ctx: &DescribeCtx<'_>,
40 ) -> (String, ToolKind, Vec<ToolCallLocation>) {
41 if name.starts_with("theory_") {
42 return (theory::title(name, args), ToolKind::Think, vec![]);
43 }
44 if name == library::NAME {
45 return ("List separated songs".to_string(), ToolKind::Search, vec![]);
46 }
47 let path = args.get("path").and_then(Value::as_str);
48 let loc = path
49 .map(|p| absolutize(&resolve_in(ctx.cwd, p)))
50 .map(ToolCallLocation::new)
51 .into_iter()
52 .collect();
53 (
54 format!("Analyze {}", path.unwrap_or("audio file")),
55 ToolKind::Read,
56 loc,
57 )
58 }
59
60 async fn execute(
61 &self,
62 ctx: &ToolCtx,
63 _tool_call_id: &str,
64 name: &str,
65 args: Value,
66 ) -> Result<ToolOutcome> {
67 if name.starts_with("theory_") {
68 theory::execute(name, args)
69 } else if name == audio::NAME {
70 audio::execute(ctx, args).await
71 } else if name == library::NAME {
72 library::execute(ctx, args).await
73 } else {
74 Err(anyhow!("unknown tool `{name}`"))
75 }
76 }
77 }