add summary tool
This commit is contained in:
@@ -76,6 +76,10 @@ pub struct CodeToolConfig {
|
||||
/// By default (false), non-UTF-8 files are skipped entirely.
|
||||
#[serde(default)]
|
||||
pub include_non_utf8: bool,
|
||||
/// Path to a file containing a summary/description of the codebase.
|
||||
/// If present, the agent can request a summary of the repository.
|
||||
#[serde(default)]
|
||||
pub summary_file: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -47,6 +47,7 @@ pub struct CodeTool {
|
||||
config: CodeToolConfig,
|
||||
source: ResolvedSource,
|
||||
glob_filter: Option<GlobSet>,
|
||||
summary: Option<Box<str>>,
|
||||
get_sha: ShaCallback,
|
||||
client: reqwest::Client,
|
||||
cache: Mutex<Option<CachedTarball>>,
|
||||
@@ -91,10 +92,18 @@ impl CodeTool {
|
||||
})?)
|
||||
};
|
||||
|
||||
// Load summary from file if configured
|
||||
let summary = config
|
||||
.summary_file
|
||||
.as_ref()
|
||||
.map(|path| std::fs::read_to_string(path).map(|s| s.into_boxed_str()))
|
||||
.transpose()?;
|
||||
|
||||
Ok(Self {
|
||||
config,
|
||||
source,
|
||||
glob_filter,
|
||||
summary,
|
||||
get_sha,
|
||||
client: reqwest::Client::new(),
|
||||
cache: Mutex::new(None),
|
||||
@@ -106,6 +115,16 @@ impl CodeTool {
|
||||
&self.config.name
|
||||
}
|
||||
|
||||
/// Check if this code tool has a summary available.
|
||||
pub fn has_summary(&self) -> bool {
|
||||
self.summary.is_some()
|
||||
}
|
||||
|
||||
/// Get the summary of the codebase, if available.
|
||||
pub fn summary(&self) -> Option<&str> {
|
||||
self.summary.as_deref()
|
||||
}
|
||||
|
||||
/// Get the current tarball, downloading or reading from file as needed.
|
||||
///
|
||||
/// Returns a mutex guard containing the cached tarball. This method never fails;
|
||||
@@ -482,6 +501,15 @@ impl CodeToolTools {
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
|
||||
/// Get list of app names that have summaries available.
|
||||
fn apps_with_summaries(&self) -> Vec<&str> {
|
||||
self.apps
|
||||
.iter()
|
||||
.filter(|a| a.has_summary())
|
||||
.map(|a| a.name())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -513,10 +541,15 @@ struct SearchInput {
|
||||
path_prefix: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SummaryInput {
|
||||
app: String,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ToolExecutor for CodeToolTools {
|
||||
fn tools(&self) -> Vec<Tool> {
|
||||
vec![
|
||||
let mut tools = vec![
|
||||
Tool {
|
||||
name: "code_ls",
|
||||
description: "List files in a directory of an application's source code.",
|
||||
@@ -605,11 +638,35 @@ impl ToolExecutor for CodeToolTools {
|
||||
"required": ["app", "pattern"]
|
||||
}),
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
// Only include summary tool if at least one app has a summary
|
||||
let apps_with_summaries = self.apps_with_summaries();
|
||||
if !apps_with_summaries.is_empty() {
|
||||
tools.push(Tool {
|
||||
name: "code_summary",
|
||||
description: "Get a summary/overview of an application's codebase.",
|
||||
input_schema: serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"app": {
|
||||
"type": "string",
|
||||
"description": format!("Name of the application. Apps with summaries: {}", apps_with_summaries.join(", "))
|
||||
}
|
||||
},
|
||||
"required": ["app"]
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
tools
|
||||
}
|
||||
|
||||
fn has_tool(&self, name: &str) -> bool {
|
||||
matches!(name, "code_ls" | "code_find" | "code_read" | "code_search")
|
||||
matches!(
|
||||
name,
|
||||
"code_ls" | "code_find" | "code_read" | "code_search" | "code_summary"
|
||||
)
|
||||
}
|
||||
|
||||
async fn execute(&self, name: &str, input: &serde_json::Value) -> Result<ToolResult, String> {
|
||||
@@ -670,6 +727,25 @@ impl ToolExecutor for CodeToolTools {
|
||||
.await?;
|
||||
Ok(ToolResult::new(result))
|
||||
}
|
||||
"code_summary" => {
|
||||
let input: SummaryInput = serde_json::from_value(input.clone())
|
||||
.map_err(|e| format!("Invalid input: {e}"))?;
|
||||
let app = self.find_app(&input.app).ok_or_else(|| {
|
||||
format!(
|
||||
"Unknown app '{}'. Available: {}",
|
||||
input.app,
|
||||
self.app_names()
|
||||
)
|
||||
})?;
|
||||
let summary = app.summary().ok_or_else(|| {
|
||||
format!(
|
||||
"No summary available for '{}'. Apps with summaries: {}",
|
||||
input.app,
|
||||
self.apps_with_summaries().join(", ")
|
||||
)
|
||||
})?;
|
||||
Ok(ToolResult::new(summary.to_string()))
|
||||
}
|
||||
_ => Err(format!("Unknown tool: {name}")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ fn create_test_repo_code_with_glob(glob: Vec<String>) -> CodeTool {
|
||||
},
|
||||
glob,
|
||||
include_non_utf8: false,
|
||||
summary_file: None,
|
||||
};
|
||||
|
||||
let sha = TEST_SHA.to_string();
|
||||
|
||||
Reference in New Issue
Block a user