add support for code-tool summary
This commit is contained in:
@@ -59,10 +59,10 @@ pub enum Source {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configuration for an application's source code access.
|
/// Configuration for a repository's source code access.
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||||
pub struct CodeToolConfig {
|
pub struct CodeToolConfig {
|
||||||
/// Name of the application (used to identify it in tool calls).
|
/// Name of the repository (used to identify it in tool calls).
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// Source of the repository code.
|
/// Source of the repository code.
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Application source code browsing via GitHub tarball downloads.
|
//! Repository source code browsing via GitHub tarball downloads.
|
||||||
//!
|
//!
|
||||||
//! This crate provides tools for browsing application source code by downloading
|
//! This crate provides tools for browsing repository source code by downloading
|
||||||
//! tarballs from GitHub and caching them in memory.
|
//! tarballs from GitHub and caching them in memory.
|
||||||
|
|
||||||
mod cached;
|
mod cached;
|
||||||
@@ -40,9 +40,9 @@ enum ResolvedSource {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Application source code browser.
|
/// Repository source code browser.
|
||||||
///
|
///
|
||||||
/// Downloads and caches GitHub tarballs for browsing application source code.
|
/// Downloads and caches GitHub tarballs for browsing repository source code.
|
||||||
pub struct CodeTool {
|
pub struct CodeTool {
|
||||||
config: CodeToolConfig,
|
config: CodeToolConfig,
|
||||||
source: ResolvedSource,
|
source: ResolvedSource,
|
||||||
@@ -110,7 +110,7 @@ impl CodeTool {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the application name.
|
/// Get the repository name.
|
||||||
pub fn name(&self) -> &str {
|
pub fn name(&self) -> &str {
|
||||||
&self.config.name
|
&self.config.name
|
||||||
}
|
}
|
||||||
@@ -477,56 +477,56 @@ impl CodeTool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tool executor for multiple application source code browsers.
|
/// Tool executor for multiple repository code browsers.
|
||||||
pub struct CodeToolTools {
|
pub struct CodeToolTools {
|
||||||
apps: Vec<CodeTool>,
|
repos: Vec<CodeTool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CodeToolTools {
|
impl CodeToolTools {
|
||||||
/// Create a new CodeToolTools instance.
|
/// Create a new CodeToolTools instance.
|
||||||
pub fn new(apps: Vec<CodeTool>) -> Self {
|
pub fn new(repos: Vec<CodeTool>) -> Self {
|
||||||
Self { apps }
|
Self { repos }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find an app by name.
|
/// Find a repo by name.
|
||||||
fn find_app(&self, name: &str) -> Option<&CodeTool> {
|
fn find_repo(&self, name: &str) -> Option<&CodeTool> {
|
||||||
self.apps.iter().find(|app| app.name() == name)
|
self.repos.iter().find(|repo| repo.name() == name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get list of app names for error messages.
|
/// Get list of repo names for error messages.
|
||||||
fn app_names(&self) -> String {
|
fn repo_names(&self) -> String {
|
||||||
self.apps
|
self.repos
|
||||||
.iter()
|
.iter()
|
||||||
.map(|a| a.name())
|
.map(|r| r.name())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get list of app names that have summaries available.
|
/// Get list of repo names that have summaries available.
|
||||||
fn apps_with_summaries(&self) -> Vec<&str> {
|
fn repos_with_summaries(&self) -> Vec<&str> {
|
||||||
self.apps
|
self.repos
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|a| a.has_summary())
|
.filter(|r| r.has_summary())
|
||||||
.map(|a| a.name())
|
.map(|r| r.name())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct LsInput {
|
struct LsInput {
|
||||||
app: String,
|
repo: String,
|
||||||
path: Option<String>,
|
path: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct FindInput {
|
struct FindInput {
|
||||||
app: String,
|
repo: String,
|
||||||
pattern: Option<String>,
|
pattern: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct ReadInput {
|
struct ReadInput {
|
||||||
app: String,
|
repo: String,
|
||||||
path: String,
|
path: String,
|
||||||
line_start: Option<usize>,
|
line_start: Option<usize>,
|
||||||
line_end: Option<usize>,
|
line_end: Option<usize>,
|
||||||
@@ -534,7 +534,7 @@ struct ReadInput {
|
|||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct SearchInput {
|
struct SearchInput {
|
||||||
app: String,
|
repo: String,
|
||||||
pattern: String,
|
pattern: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
context: u32,
|
context: u32,
|
||||||
@@ -543,7 +543,7 @@ struct SearchInput {
|
|||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct SummaryInput {
|
struct SummaryInput {
|
||||||
app: String,
|
repo: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -552,49 +552,49 @@ impl ToolExecutor for CodeToolTools {
|
|||||||
let mut tools = vec![
|
let mut tools = vec![
|
||||||
Tool {
|
Tool {
|
||||||
name: "code_ls",
|
name: "code_ls",
|
||||||
description: "List files in a directory of an application's source code.",
|
description: "List files in a directory of a repository's source code.",
|
||||||
input_schema: serde_json::json!({
|
input_schema: serde_json::json!({
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"app": {
|
"repo": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Name of the application"
|
"description": "Name of the repository"
|
||||||
},
|
},
|
||||||
"path": {
|
"path": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Directory path to list (optional, defaults to root)"
|
"description": "Directory path to list (optional, defaults to root)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["app"]
|
"required": ["repo"]
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
Tool {
|
Tool {
|
||||||
name: "code_find",
|
name: "code_find",
|
||||||
description: "Find files matching a glob pattern in an application's source code.",
|
description: "Find files matching a glob pattern in a repository's source code.",
|
||||||
input_schema: serde_json::json!({
|
input_schema: serde_json::json!({
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"app": {
|
"repo": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Name of the application"
|
"description": "Name of the repository"
|
||||||
},
|
},
|
||||||
"pattern": {
|
"pattern": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Glob pattern to match (e.g., '*.rs', 'src/*.py')"
|
"description": "Glob pattern to match (e.g., '*.rs', 'src/*.py')"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["app"]
|
"required": ["repo"]
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
Tool {
|
Tool {
|
||||||
name: "code_read",
|
name: "code_read",
|
||||||
description: "Read a file from an application's source code.",
|
description: "Read a file from a repository's source code.",
|
||||||
input_schema: serde_json::json!({
|
input_schema: serde_json::json!({
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"app": {
|
"repo": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Name of the application"
|
"description": "Name of the repository"
|
||||||
},
|
},
|
||||||
"path": {
|
"path": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -609,18 +609,18 @@ impl ToolExecutor for CodeToolTools {
|
|||||||
"description": "Ending line number (inclusive, optional)"
|
"description": "Ending line number (inclusive, optional)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["app", "path"]
|
"required": ["repo", "path"]
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
Tool {
|
Tool {
|
||||||
name: "code_search",
|
name: "code_search",
|
||||||
description: "Search for a regex pattern in an application's source code (like grep).",
|
description: "Search for a regex pattern in a repository's source code (like grep).",
|
||||||
input_schema: serde_json::json!({
|
input_schema: serde_json::json!({
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"app": {
|
"repo": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Name of the application"
|
"description": "Name of the repository"
|
||||||
},
|
},
|
||||||
"pattern": {
|
"pattern": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -635,26 +635,26 @@ impl ToolExecutor for CodeToolTools {
|
|||||||
"description": "Optional path prefix to limit search scope"
|
"description": "Optional path prefix to limit search scope"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["app", "pattern"]
|
"required": ["repo", "pattern"]
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// Only include summary tool if at least one app has a summary
|
// Only include summary tool if at least one repo has a summary
|
||||||
let apps_with_summaries = self.apps_with_summaries();
|
let repos_with_summaries = self.repos_with_summaries();
|
||||||
if !apps_with_summaries.is_empty() {
|
if !repos_with_summaries.is_empty() {
|
||||||
tools.push(Tool {
|
tools.push(Tool {
|
||||||
name: "code_summary",
|
name: "code_summary",
|
||||||
description: "Get a summary/overview of an application's codebase.",
|
description: "Get a summary/overview of a repository's codebase.",
|
||||||
input_schema: serde_json::json!({
|
input_schema: serde_json::json!({
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"app": {
|
"repo": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": format!("Name of the application. Apps with summaries: {}", apps_with_summaries.join(", "))
|
"description": format!("Name of the repository. Repos with summaries: {}", repos_with_summaries.join(", "))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["app"]
|
"required": ["repo"]
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -674,40 +674,40 @@ impl ToolExecutor for CodeToolTools {
|
|||||||
"code_ls" => {
|
"code_ls" => {
|
||||||
let input: LsInput = serde_json::from_value(input.clone())
|
let input: LsInput = serde_json::from_value(input.clone())
|
||||||
.map_err(|e| format!("Invalid input: {e}"))?;
|
.map_err(|e| format!("Invalid input: {e}"))?;
|
||||||
let app = self.find_app(&input.app).ok_or_else(|| {
|
let repo = self.find_repo(&input.repo).ok_or_else(|| {
|
||||||
format!(
|
format!(
|
||||||
"Unknown app '{}'. Available: {}",
|
"Unknown repo '{}'. Available: {}",
|
||||||
input.app,
|
input.repo,
|
||||||
self.app_names()
|
self.repo_names()
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let result = app.ls(input.path.as_deref()).await?;
|
let result = repo.ls(input.path.as_deref()).await?;
|
||||||
Ok(ToolResult::new(result))
|
Ok(ToolResult::new(result))
|
||||||
}
|
}
|
||||||
"code_find" => {
|
"code_find" => {
|
||||||
let input: FindInput = serde_json::from_value(input.clone())
|
let input: FindInput = serde_json::from_value(input.clone())
|
||||||
.map_err(|e| format!("Invalid input: {e}"))?;
|
.map_err(|e| format!("Invalid input: {e}"))?;
|
||||||
let app = self.find_app(&input.app).ok_or_else(|| {
|
let repo = self.find_repo(&input.repo).ok_or_else(|| {
|
||||||
format!(
|
format!(
|
||||||
"Unknown app '{}'. Available: {}",
|
"Unknown repo '{}'. Available: {}",
|
||||||
input.app,
|
input.repo,
|
||||||
self.app_names()
|
self.repo_names()
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let result = app.find(input.pattern.as_deref()).await?;
|
let result = repo.find(input.pattern.as_deref()).await?;
|
||||||
Ok(ToolResult::new(result))
|
Ok(ToolResult::new(result))
|
||||||
}
|
}
|
||||||
"code_read" => {
|
"code_read" => {
|
||||||
let input: ReadInput = serde_json::from_value(input.clone())
|
let input: ReadInput = serde_json::from_value(input.clone())
|
||||||
.map_err(|e| format!("Invalid input: {e}"))?;
|
.map_err(|e| format!("Invalid input: {e}"))?;
|
||||||
let app = self.find_app(&input.app).ok_or_else(|| {
|
let repo = self.find_repo(&input.repo).ok_or_else(|| {
|
||||||
format!(
|
format!(
|
||||||
"Unknown app '{}'. Available: {}",
|
"Unknown repo '{}'. Available: {}",
|
||||||
input.app,
|
input.repo,
|
||||||
self.app_names()
|
self.repo_names()
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let result = app
|
let result = repo
|
||||||
.read(&input.path, input.line_start, input.line_end)
|
.read(&input.path, input.line_start, input.line_end)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(ToolResult::new(result))
|
Ok(ToolResult::new(result))
|
||||||
@@ -715,14 +715,14 @@ impl ToolExecutor for CodeToolTools {
|
|||||||
"code_search" => {
|
"code_search" => {
|
||||||
let input: SearchInput = serde_json::from_value(input.clone())
|
let input: SearchInput = serde_json::from_value(input.clone())
|
||||||
.map_err(|e| format!("Invalid input: {e}"))?;
|
.map_err(|e| format!("Invalid input: {e}"))?;
|
||||||
let app = self.find_app(&input.app).ok_or_else(|| {
|
let repo = self.find_repo(&input.repo).ok_or_else(|| {
|
||||||
format!(
|
format!(
|
||||||
"Unknown app '{}'. Available: {}",
|
"Unknown repo '{}'. Available: {}",
|
||||||
input.app,
|
input.repo,
|
||||||
self.app_names()
|
self.repo_names()
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let result = app
|
let result = repo
|
||||||
.search(&input.pattern, input.context, input.path_prefix.as_deref())
|
.search(&input.pattern, input.context, input.path_prefix.as_deref())
|
||||||
.await?;
|
.await?;
|
||||||
Ok(ToolResult::new(result))
|
Ok(ToolResult::new(result))
|
||||||
@@ -730,18 +730,18 @@ impl ToolExecutor for CodeToolTools {
|
|||||||
"code_summary" => {
|
"code_summary" => {
|
||||||
let input: SummaryInput = serde_json::from_value(input.clone())
|
let input: SummaryInput = serde_json::from_value(input.clone())
|
||||||
.map_err(|e| format!("Invalid input: {e}"))?;
|
.map_err(|e| format!("Invalid input: {e}"))?;
|
||||||
let app = self.find_app(&input.app).ok_or_else(|| {
|
let repo = self.find_repo(&input.repo).ok_or_else(|| {
|
||||||
format!(
|
format!(
|
||||||
"Unknown app '{}'. Available: {}",
|
"Unknown repo '{}'. Available: {}",
|
||||||
input.app,
|
input.repo,
|
||||||
self.app_names()
|
self.repo_names()
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let summary = app.summary().ok_or_else(|| {
|
let summary = repo.summary().ok_or_else(|| {
|
||||||
format!(
|
format!(
|
||||||
"No summary available for '{}'. Apps with summaries: {}",
|
"No summary available for '{}'. Repos with summaries: {}",
|
||||||
input.app,
|
input.repo,
|
||||||
self.apps_with_summaries().join(", ")
|
self.repos_with_summaries().join(", ")
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
Ok(ToolResult::new(summary.to_string()))
|
Ok(ToolResult::new(summary.to_string()))
|
||||||
|
|||||||
Reference in New Issue
Block a user