assistant_tool: Decouple Tool from Workspace (#26309)

This PR decouples the `Tool` trait from the `Workspace` (and from the
UI, in general).

`Tool::run` now takes a `WeakEntity<Project>` instead of a
`WeakEntity<Workspace>` and a `Window`.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2025-03-07 17:41:56 -05:00 committed by GitHub
parent 4f6682c7fe
commit 18f3f8097f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 35 additions and 52 deletions

5
Cargo.lock generated
View file

@ -658,9 +658,9 @@ dependencies = [
"derive_more", "derive_more",
"gpui", "gpui",
"parking_lot", "parking_lot",
"project",
"serde", "serde",
"serde_json", "serde_json",
"workspace",
] ]
[[package]] [[package]]
@ -675,7 +675,6 @@ dependencies = [
"schemars", "schemars",
"serde", "serde",
"serde_json", "serde_json",
"workspace",
] ]
[[package]] [[package]]
@ -3140,7 +3139,6 @@ dependencies = [
"smol", "smol",
"url", "url",
"util", "util",
"workspace",
] ]
[[package]] [[package]]
@ -11930,7 +11928,6 @@ dependencies = [
"serde_json", "serde_json",
"settings", "settings",
"util", "util",
"workspace",
] ]
[[package]] [[package]]

View file

@ -11,11 +11,11 @@ use gpui::{
use language::{Buffer, LanguageRegistry}; use language::{Buffer, LanguageRegistry};
use language_model::{LanguageModelRegistry, LanguageModelToolUseId, Role}; use language_model::{LanguageModelRegistry, LanguageModelToolUseId, Role};
use markdown::{Markdown, MarkdownStyle}; use markdown::{Markdown, MarkdownStyle};
use project::Project;
use settings::Settings as _; use settings::Settings as _;
use theme::ThemeSettings; use theme::ThemeSettings;
use ui::{prelude::*, Disclosure, KeyBinding}; use ui::{prelude::*, Disclosure, KeyBinding};
use util::ResultExt as _; use util::ResultExt as _;
use workspace::Workspace;
use crate::thread::{MessageId, RequestKind, Thread, ThreadError, ThreadEvent}; use crate::thread::{MessageId, RequestKind, Thread, ThreadError, ThreadEvent};
use crate::thread_store::ThreadStore; use crate::thread_store::ThreadStore;
@ -23,7 +23,7 @@ use crate::tool_use::{ToolUse, ToolUseStatus};
use crate::ui::ContextPill; use crate::ui::ContextPill;
pub struct ActiveThread { pub struct ActiveThread {
workspace: WeakEntity<Workspace>, project: WeakEntity<Project>,
language_registry: Arc<LanguageRegistry>, language_registry: Arc<LanguageRegistry>,
tools: Arc<ToolWorkingSet>, tools: Arc<ToolWorkingSet>,
thread_store: Entity<ThreadStore>, thread_store: Entity<ThreadStore>,
@ -46,7 +46,7 @@ impl ActiveThread {
pub fn new( pub fn new(
thread: Entity<Thread>, thread: Entity<Thread>,
thread_store: Entity<ThreadStore>, thread_store: Entity<ThreadStore>,
workspace: WeakEntity<Workspace>, project: WeakEntity<Project>,
language_registry: Arc<LanguageRegistry>, language_registry: Arc<LanguageRegistry>,
tools: Arc<ToolWorkingSet>, tools: Arc<ToolWorkingSet>,
window: &mut Window, window: &mut Window,
@ -58,7 +58,7 @@ impl ActiveThread {
]; ];
let mut this = Self { let mut this = Self {
workspace, project,
language_registry, language_registry,
tools, tools,
thread_store, thread_store,
@ -311,7 +311,7 @@ impl ActiveThread {
for tool_use in pending_tool_uses { for tool_use in pending_tool_uses {
if let Some(tool) = self.tools.tool(&tool_use.name, cx) { if let Some(tool) = self.tools.tool(&tool_use.name, cx) {
let task = tool.run(tool_use.input, self.workspace.clone(), window, cx); let task = tool.run(tool_use.input, self.project.clone(), cx);
self.thread.update(cx, |thread, cx| { self.thread.update(cx, |thread, cx| {
thread.insert_tool_output(tool_use.id.clone(), task, cx); thread.insert_tool_output(tool_use.id.clone(), task, cx);

View file

@ -170,8 +170,8 @@ impl AssistantPanel {
Self { Self {
active_view: ActiveView::Thread, active_view: ActiveView::Thread,
workspace: workspace.clone(), workspace,
project, project: project.clone(),
fs: fs.clone(), fs: fs.clone(),
language_registry: language_registry.clone(), language_registry: language_registry.clone(),
thread_store: thread_store.clone(), thread_store: thread_store.clone(),
@ -179,7 +179,7 @@ impl AssistantPanel {
ActiveThread::new( ActiveThread::new(
thread.clone(), thread.clone(),
thread_store.clone(), thread_store.clone(),
workspace, project.downgrade(),
language_registry, language_registry,
tools.clone(), tools.clone(),
window, window,
@ -246,7 +246,7 @@ impl AssistantPanel {
ActiveThread::new( ActiveThread::new(
thread.clone(), thread.clone(),
self.thread_store.clone(), self.thread_store.clone(),
self.workspace.clone(), self.project.downgrade(),
self.language_registry.clone(), self.language_registry.clone(),
self.tools.clone(), self.tools.clone(),
window, window,
@ -381,7 +381,7 @@ impl AssistantPanel {
ActiveThread::new( ActiveThread::new(
thread.clone(), thread.clone(),
this.thread_store.clone(), this.thread_store.clone(),
this.workspace.clone(), this.project.downgrade(),
this.language_registry.clone(), this.language_registry.clone(),
this.tools.clone(), this.tools.clone(),
window, window,

View file

@ -17,6 +17,6 @@ collections.workspace = true
derive_more.workspace = true derive_more.workspace = true
gpui.workspace = true gpui.workspace = true
parking_lot.workspace = true parking_lot.workspace = true
project.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
workspace.workspace = true

View file

@ -4,8 +4,8 @@ mod tool_working_set;
use std::sync::Arc; use std::sync::Arc;
use anyhow::Result; use anyhow::Result;
use gpui::{App, Task, WeakEntity, Window}; use gpui::{App, Task, WeakEntity};
use workspace::Workspace; use project::Project;
pub use crate::tool_registry::*; pub use crate::tool_registry::*;
pub use crate::tool_working_set::*; pub use crate::tool_working_set::*;
@ -31,8 +31,7 @@ pub trait Tool: 'static + Send + Sync {
fn run( fn run(
self: Arc<Self>, self: Arc<Self>,
input: serde_json::Value, input: serde_json::Value,
workspace: WeakEntity<Workspace>, project: WeakEntity<Project>,
window: &mut Window,
cx: &mut App, cx: &mut App,
) -> Task<Result<String>>; ) -> Task<Result<String>>;
} }

View file

@ -20,4 +20,3 @@ project.workspace = true
schemars.workspace = true schemars.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
workspace.workspace = true

View file

@ -2,10 +2,10 @@ use std::sync::Arc;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use assistant_tool::Tool; use assistant_tool::Tool;
use gpui::{App, Task, WeakEntity, Window}; use gpui::{App, Task, WeakEntity};
use project::Project;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use workspace::Workspace;
#[derive(Debug, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct ListWorktreesToolInput {} pub struct ListWorktreesToolInput {}
@ -34,16 +34,13 @@ impl Tool for ListWorktreesTool {
fn run( fn run(
self: Arc<Self>, self: Arc<Self>,
_input: serde_json::Value, _input: serde_json::Value,
workspace: WeakEntity<Workspace>, project: WeakEntity<Project>,
_window: &mut Window,
cx: &mut App, cx: &mut App,
) -> Task<Result<String>> { ) -> Task<Result<String>> {
let Some(workspace) = workspace.upgrade() else { let Some(project) = project.upgrade() else {
return Task::ready(Err(anyhow!("workspace dropped"))); return Task::ready(Err(anyhow!("project dropped")));
}; };
let project = workspace.read(cx).project().clone();
cx.spawn(|cx| async move { cx.spawn(|cx| async move {
cx.update(|cx| { cx.update(|cx| {
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]

View file

@ -3,7 +3,8 @@ use std::sync::Arc;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use assistant_tool::Tool; use assistant_tool::Tool;
use chrono::{Local, Utc}; use chrono::{Local, Utc};
use gpui::{App, Task, WeakEntity, Window}; use gpui::{App, Task, WeakEntity};
use project::Project;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -41,8 +42,7 @@ impl Tool for NowTool {
fn run( fn run(
self: Arc<Self>, self: Arc<Self>,
input: serde_json::Value, input: serde_json::Value,
_workspace: WeakEntity<workspace::Workspace>, _project: WeakEntity<Project>,
_window: &mut Window,
_cx: &mut App, _cx: &mut App,
) -> Task<Result<String>> { ) -> Task<Result<String>> {
let input: NowToolInput = match serde_json::from_value(input) { let input: NowToolInput = match serde_json::from_value(input) {

View file

@ -3,11 +3,10 @@ use std::sync::Arc;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use assistant_tool::Tool; use assistant_tool::Tool;
use gpui::{App, Task, WeakEntity, Window}; use gpui::{App, Task, WeakEntity};
use project::{ProjectPath, WorktreeId}; use project::{Project, ProjectPath, WorktreeId};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use workspace::Workspace;
#[derive(Debug, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct ReadFileToolInput { pub struct ReadFileToolInput {
@ -38,12 +37,11 @@ impl Tool for ReadFileTool {
fn run( fn run(
self: Arc<Self>, self: Arc<Self>,
input: serde_json::Value, input: serde_json::Value,
workspace: WeakEntity<Workspace>, project: WeakEntity<Project>,
_window: &mut Window,
cx: &mut App, cx: &mut App,
) -> Task<Result<String>> { ) -> Task<Result<String>> {
let Some(workspace) = workspace.upgrade() else { let Some(project) = project.upgrade() else {
return Task::ready(Err(anyhow!("workspace dropped"))); return Task::ready(Err(anyhow!("project dropped")));
}; };
let input = match serde_json::from_value::<ReadFileToolInput>(input) { let input = match serde_json::from_value::<ReadFileToolInput>(input) {
@ -51,7 +49,6 @@ impl Tool for ReadFileTool {
Err(err) => return Task::ready(Err(anyhow!(err))), Err(err) => return Task::ready(Err(anyhow!(err))),
}; };
let project = workspace.read(cx).project().clone();
let project_path = ProjectPath { let project_path = ProjectPath {
worktree_id: WorktreeId::from_usize(input.worktree_id), worktree_id: WorktreeId::from_usize(input.worktree_id),
path: input.path, path: input.path,

View file

@ -31,4 +31,3 @@ settings.workspace = true
smol.workspace = true smol.workspace = true
url = { workspace = true, features = ["serde"] } url = { workspace = true, features = ["serde"] }
util.workspace = true util.workspace = true
workspace.workspace = true

View file

@ -2,7 +2,7 @@ use std::sync::Arc;
use anyhow::{anyhow, bail}; use anyhow::{anyhow, bail};
use assistant_tool::Tool; use assistant_tool::Tool;
use gpui::{App, Entity, Task, Window}; use gpui::{App, Entity, Task};
use crate::manager::ContextServerManager; use crate::manager::ContextServerManager;
use crate::types; use crate::types;
@ -51,8 +51,7 @@ impl Tool for ContextServerTool {
fn run( fn run(
self: std::sync::Arc<Self>, self: std::sync::Arc<Self>,
input: serde_json::Value, input: serde_json::Value,
_workspace: gpui::WeakEntity<workspace::Workspace>, _project: gpui::WeakEntity<project::Project>,
_: &mut Window,
cx: &mut App, cx: &mut App,
) -> gpui::Task<gpui::Result<String>> { ) -> gpui::Task<gpui::Result<String>> {
if let Some(server) = self.server_manager.read(cx).get_server(&self.server_id) { if let Some(server) = self.server_manager.read(cx).get_server(&self.server_id) {

View file

@ -27,11 +27,9 @@ serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
settings.workspace = true settings.workspace = true
util.workspace = true util.workspace = true
workspace.workspace = true
[dev-dependencies] [dev-dependencies]
collections = { workspace = true, features = ["test-support"] } collections = { workspace = true, features = ["test-support"] }
gpui = { workspace = true, features = ["test-support"] } gpui = { workspace = true, features = ["test-support"] }
project = { workspace = true, features = ["test-support"] } project = { workspace = true, features = ["test-support"] }
settings = { workspace = true, features = ["test-support"] } settings = { workspace = true, features = ["test-support"] }
workspace = { workspace = true, features = ["test-support"] }

View file

@ -1,13 +1,13 @@
mod session; mod session;
use project::Project;
pub(crate) use session::*; pub(crate) use session::*;
use assistant_tool::{Tool, ToolRegistry}; use assistant_tool::{Tool, ToolRegistry};
use gpui::{App, AppContext as _, Task, WeakEntity, Window}; use gpui::{App, AppContext as _, Task, WeakEntity};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Deserialize; use serde::Deserialize;
use std::sync::Arc; use std::sync::Arc;
use workspace::Workspace;
pub fn init(cx: &App) { pub fn init(cx: &App) {
let registry = ToolRegistry::global(cx); let registry = ToolRegistry::global(cx);
@ -38,17 +38,15 @@ impl Tool for ScriptingTool {
fn run( fn run(
self: Arc<Self>, self: Arc<Self>,
input: serde_json::Value, input: serde_json::Value,
workspace: WeakEntity<Workspace>, project: WeakEntity<Project>,
_window: &mut Window,
cx: &mut App, cx: &mut App,
) -> Task<anyhow::Result<String>> { ) -> Task<anyhow::Result<String>> {
let input = match serde_json::from_value::<ScriptingToolInput>(input) { let input = match serde_json::from_value::<ScriptingToolInput>(input) {
Err(err) => return Task::ready(Err(err.into())), Err(err) => return Task::ready(Err(err.into())),
Ok(input) => input, Ok(input) => input,
}; };
let Ok(project) = workspace.read_with(cx, |workspace, _cx| workspace.project().clone()) let Some(project) = project.upgrade() else {
else { return Task::ready(Err(anyhow::anyhow!("project dropped")));
return Task::ready(Err(anyhow::anyhow!("No project found")));
}; };
let session = cx.new(|cx| Session::new(project, cx)); let session = cx.new(|cx| Session::new(project, cx));