When the user attached context in the thread, the editor model request would fail because its tool use wouldn't be removed properly leading to an API error. Also, after an edit, we'd keep the old file snapshot in the context. This would make the model think that the edits didn't apply and make it go in a loop. Release Notes: - N/A
110 lines
3.4 KiB
Rust
110 lines
3.4 KiB
Rust
use std::sync::Arc;
|
|
|
|
use anyhow::{anyhow, bail, Result};
|
|
use assistant_tool::{ActionLog, Tool, ToolSource};
|
|
use gpui::{App, Entity, Task};
|
|
use language_model::LanguageModelRequestMessage;
|
|
use project::Project;
|
|
|
|
use crate::manager::ContextServerManager;
|
|
use crate::types;
|
|
|
|
pub struct ContextServerTool {
|
|
server_manager: Entity<ContextServerManager>,
|
|
server_id: Arc<str>,
|
|
tool: types::Tool,
|
|
}
|
|
|
|
impl ContextServerTool {
|
|
pub fn new(
|
|
server_manager: Entity<ContextServerManager>,
|
|
server_id: impl Into<Arc<str>>,
|
|
tool: types::Tool,
|
|
) -> Self {
|
|
Self {
|
|
server_manager,
|
|
server_id: server_id.into(),
|
|
tool,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Tool for ContextServerTool {
|
|
fn name(&self) -> String {
|
|
self.tool.name.clone()
|
|
}
|
|
|
|
fn description(&self) -> String {
|
|
self.tool.description.clone().unwrap_or_default()
|
|
}
|
|
|
|
fn source(&self) -> ToolSource {
|
|
ToolSource::ContextServer {
|
|
id: self.server_id.clone().into(),
|
|
}
|
|
}
|
|
|
|
fn input_schema(&self) -> serde_json::Value {
|
|
match &self.tool.input_schema {
|
|
serde_json::Value::Null => {
|
|
serde_json::json!({ "type": "object", "properties": [] })
|
|
}
|
|
serde_json::Value::Object(map) if map.is_empty() => {
|
|
serde_json::json!({ "type": "object", "properties": [] })
|
|
}
|
|
_ => self.tool.input_schema.clone(),
|
|
}
|
|
}
|
|
|
|
fn run(
|
|
self: Arc<Self>,
|
|
input: serde_json::Value,
|
|
_messages: &[LanguageModelRequestMessage],
|
|
_project: Entity<Project>,
|
|
_action_log: Entity<ActionLog>,
|
|
cx: &mut App,
|
|
) -> Task<Result<String>> {
|
|
if let Some(server) = self.server_manager.read(cx).get_server(&self.server_id) {
|
|
cx.foreground_executor().spawn({
|
|
let tool_name = self.tool.name.clone();
|
|
async move {
|
|
let Some(protocol) = server.client() else {
|
|
bail!("Context server not initialized");
|
|
};
|
|
|
|
let arguments = if let serde_json::Value::Object(map) = input {
|
|
Some(map.into_iter().collect())
|
|
} else {
|
|
None
|
|
};
|
|
|
|
log::trace!(
|
|
"Running tool: {} with arguments: {:?}",
|
|
tool_name,
|
|
arguments
|
|
);
|
|
let response = protocol.run_tool(tool_name, arguments).await?;
|
|
|
|
let mut result = String::new();
|
|
for content in response.content {
|
|
match content {
|
|
types::ToolResponseContent::Text { text } => {
|
|
result.push_str(&text);
|
|
}
|
|
types::ToolResponseContent::Image { .. } => {
|
|
log::warn!("Ignoring image content from tool response");
|
|
}
|
|
types::ToolResponseContent::Resource { .. } => {
|
|
log::warn!("Ignoring resource content from tool response");
|
|
}
|
|
}
|
|
}
|
|
Ok(result)
|
|
}
|
|
})
|
|
} else {
|
|
Task::ready(Err(anyhow!("Context server not found")))
|
|
}
|
|
}
|
|
}
|