Compare commits

...

1 Commits

Author SHA1 Message Date
Richard Feldman
7983802d05 wip 2025-04-09 11:03:35 -04:00
10 changed files with 113 additions and 7 deletions

2
Cargo.lock generated
View File

@@ -739,6 +739,8 @@ dependencies = [
"assistant_tool",
"chrono",
"collections",
"editor",
"feature_flags",
"futures 0.3.31",
"gpui",
"html_to_markdown",

View File

@@ -451,6 +451,15 @@ fn supported_context_picker_modes(
modes
}
pub fn active_singleton_buffer_path(workspace: &Workspace, cx: &App) -> Option<PathBuf> {
let active_item = workspace.active_item(cx)?;
let editor = active_item.to_any().downcast::<Editor>().ok()?.read(cx);
let buffer = editor.buffer().read(cx).as_singleton()?;
let path = buffer.read(cx).file()?.path().to_path_buf();
Some(path)
}
fn recent_context_picker_entries(
context_store: Entity<ContextStore>,
thread_store: Option<WeakEntity<ThreadStore>>,

View File

@@ -21,6 +21,7 @@ use language_model::{
};
use project::git_store::{GitStore, GitStoreCheckpoint, RepositoryState};
use project::{Project, Worktree};
use workspace::Workspace;
use prompt_store::{
AssistantSystemPromptContext, PromptBuilder, RulesFile, WorktreeInfoForSystemPrompt,
};
@@ -31,6 +32,7 @@ use util::{ResultExt as _, TryFutureExt as _, maybe, post_inc};
use uuid::Uuid;
use crate::context::{AssistantContext, ContextId, format_context_as_string};
use crate::context_picker::active_singleton_buffer_path;
use crate::thread_store::{
SerializedMessage, SerializedMessageSegment, SerializedThread, SerializedToolResult,
SerializedToolUse,
@@ -261,6 +263,7 @@ pub struct Thread {
initial_project_snapshot: Shared<Task<Option<Arc<ProjectSnapshot>>>>,
cumulative_token_usage: TokenUsage,
feedback: Option<ThreadFeedback>,
workspace: Option<WeakEntity<Workspace>>,
}
impl Thread {
@@ -268,6 +271,7 @@ impl Thread {
project: Entity<Project>,
tools: Arc<ToolWorkingSet>,
prompt_builder: Arc<PromptBuilder>,
workspace: Option<WeakEntity<Workspace>>,
cx: &mut Context<Self>,
) -> Self {
Self {
@@ -299,6 +303,7 @@ impl Thread {
},
cumulative_token_usage: TokenUsage::default(),
feedback: None,
workspace,
}
}
@@ -308,6 +313,7 @@ impl Thread {
project: Entity<Project>,
tools: Arc<ToolWorkingSet>,
prompt_builder: Arc<PromptBuilder>,
workspace: Option<WeakEntity<Workspace>>,
cx: &mut Context<Self>,
) -> Self {
let next_message_id = MessageId(
@@ -362,6 +368,7 @@ impl Thread {
initial_project_snapshot: Task::ready(serialized.initial_project_snapshot).shared(),
cumulative_token_usage: serialized.cumulative_token_usage,
feedback: None,
workspace,
}
}
@@ -829,8 +836,15 @@ impl Thread {
)
})
.collect::<Vec<_>>();
// Try to get active tab path
let active_tab_path = self.workspace
.as_ref()
.and_then(|weak_workspace| weak_workspace.upgrade())
.and_then(|workspace| active_singleton_buffer_path(&workspace.read(cx), cx))
.map(|path| path.to_string_lossy().to_string());
cx.spawn(async |_cx| {
cx.spawn(async move |_cx| {
let results = futures::future::join_all(tasks).await;
let mut first_err = None;
let worktrees = results
@@ -842,7 +856,7 @@ impl Thread {
worktree
})
.collect::<Vec<_>>();
(AssistantSystemPromptContext::new(worktrees), first_err)
(AssistantSystemPromptContext::new(worktrees).with_active_tab_path(active_tab_path), first_err)
})
}

View File

@@ -107,6 +107,7 @@ impl ThreadStore {
self.project.clone(),
self.tools.clone(),
self.prompt_builder.clone(),
None,
cx,
)
})
@@ -134,6 +135,7 @@ impl ThreadStore {
this.project.clone(),
this.tools.clone(),
this.prompt_builder.clone(),
None,
cx,
)
})

View File

@@ -16,6 +16,8 @@ anyhow.workspace = true
assistant_tool.workspace = true
chrono.workspace = true
collections.workspace = true
editor.workspace = true
feature_flags.workspace = true
futures.workspace = true
gpui.workspace = true
html_to_markdown.workspace = true
@@ -31,6 +33,7 @@ serde.workspace = true
serde_json.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
worktree.workspace = true
open = { workspace = true }
workspace-hack.workspace = true

View File

@@ -0,0 +1,58 @@
use std::sync::Arc;
use anyhow::{anyhow, Result};
use assistant_tool::{ActionLog, Tool};
use gpui::{App, Entity, Task};
use language_model::{LanguageModelRequestMessage, LanguageModelToolSchemaFormat};
use project::Project;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use ui::IconName;
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct ActiveTabToolInput {}
pub struct ActiveTabTool;
impl Tool for ActiveTabTool {
fn name(&self) -> String {
"active-tab".into()
}
fn needs_confirmation(&self) -> bool {
false
}
fn description(&self) -> String {
include_str!("./active_tab_tool/description.md").into()
}
fn icon(&self) -> IconName {
IconName::Eye
}
fn input_schema(&self, _format: LanguageModelToolSchemaFormat) -> serde_json::Value {
let schema = schemars::schema_for!(ActiveTabToolInput);
serde_json::to_value(&schema).unwrap()
}
fn ui_text(&self, _input: &serde_json::Value) -> String {
"Get active tabs".to_string()
}
fn run(
self: Arc<Self>,
input: serde_json::Value,
_messages: &[LanguageModelRequestMessage],
_project: Entity<Project>,
_action_log: Entity<ActionLog>,
_cx: &mut App,
) -> Task<Result<String>> {
let _input = match serde_json::from_value::<ActiveTabToolInput>(input) {
Ok(input) => input,
Err(err) => return Task::ready(Err(anyhow!(err))),
};
Task::ready(Ok("Active tab information would be shown here".to_string()))
}
}

View File

@@ -0,0 +1 @@
Returns the path of the user's currently active tab.

View File

@@ -1,3 +1,4 @@
mod active_tab_tool;
mod bash_tool;
mod batch_tool;
mod code_symbol_iter;
@@ -23,6 +24,7 @@ mod thinking_tool;
use std::sync::Arc;
use active_tab_tool::ActiveTabTool;
use assistant_tool::ToolRegistry;
use copy_path_tool::CopyPathTool;
use gpui::App;
@@ -51,6 +53,7 @@ pub fn init(http_client: Arc<HttpClientWithUrl>, cx: &mut App) {
assistant_tool::init(cx);
let registry = ToolRegistry::global(cx);
registry.register_tool(ActiveTabTool);
registry.register_tool(BashTool);
registry.register_tool(BatchTool);
registry.register_tool(CreateDirectoryTool);

View File

@@ -548,15 +548,22 @@ pub fn into_anthropic(
}
}
// Create a StringOrContents value explicitly
let system_value = if !system_message.is_empty() {
Some({
// Use String variant directly with a qualified path for clarity
let string_variant = anthropic::StringOrContents::String(system_message);
string_variant
})
} else {
None
};
anthropic::Request {
model,
messages: new_messages,
max_tokens: max_output_tokens,
system: if system_message.is_empty() {
None
} else {
Some(anthropic::StringOrContents::String(system_message))
},
system: system_value,
thinking: if let AnthropicModelMode::Thinking { budget_tokens } = mode {
Some(anthropic::Thinking::Enabled { budget_tokens })
} else {

View File

@@ -20,6 +20,7 @@ use util::ResultExt;
pub struct AssistantSystemPromptContext {
pub worktrees: Vec<WorktreeInfoForSystemPrompt>,
pub has_rules: bool,
pub active_tab_path: Option<String>,
}
impl AssistantSystemPromptContext {
@@ -30,8 +31,14 @@ impl AssistantSystemPromptContext {
Self {
worktrees,
has_rules,
active_tab_path: None,
}
}
pub fn with_active_tab_path(mut self, path: Option<String>) -> Self {
self.active_tab_path = path;
self
}
}
#[derive(Serialize)]