Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion app/src/ai/blocklist/action_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub(crate) mod recording_controller;
#[cfg(not(target_family = "wasm"))]
pub(crate) mod recording_finalize;
pub(crate) mod recording_telemetry;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet, VecDeque};
use std::path::PathBuf;
use std::sync::Arc;
Expand Down Expand Up @@ -53,7 +54,9 @@ use self::execute::{
#[cfg(not(target_family = "wasm"))]
use self::recording_finalize::{FinalizeReason, finalize_recording_for_conversation};
use super::BlocklistAIHistoryModel;
use crate::ai::agent::conversation::{AIConversationId, ConversationStatus};
use crate::ai::agent::conversation::{
AIConversation, AIConversationId, ConversationStatus, RecordingSpanInfo,
};
use crate::ai::agent::{
AIAgentAction, AIAgentActionId, AIAgentActionResult, AIAgentActionResultType,
AIAgentActionType, AIAgentActionTypeDiscriminants, AIAgentExchange, AIAgentInput,
Expand Down Expand Up @@ -239,6 +242,8 @@ pub struct BlocklistAIActionModel {

/// Past actions and their corresponding statuses from previous AI exchanges.
past_action_results: HashMap<AIAgentActionId, Arc<AIAgentActionResult>>,
recording_spans_by_conversation:
RefCell<HashMap<AIConversationId, Arc<HashMap<AIAgentActionId, RecordingSpanInfo>>>>,

/// The ID of the terminal view this controller is associated with.
terminal_view_id: EntityId,
Expand Down Expand Up @@ -307,6 +312,7 @@ impl BlocklistAIActionModel {
finished_action_results: Default::default(),
executor,
past_action_results: HashMap::new(),
recording_spans_by_conversation: Default::default(),
running_actions: Default::default(),
action_order: Default::default(),
terminal_view_id,
Expand Down Expand Up @@ -463,6 +469,7 @@ impl BlocklistAIActionModel {
/// Clears action results restored from a previous conversation transcript.
pub fn clear_restored_action_results(&mut self) {
self.past_action_results.clear();
self.recording_spans_by_conversation.get_mut().clear();
}

fn try_to_execute_available_actions(
Expand Down Expand Up @@ -656,8 +663,36 @@ impl BlocklistAIActionModel {
.or_else(|| self.past_action_results.get(id))
}

pub fn recording_spans_for_conversation(
&self,
conversation: &AIConversation,
) -> Arc<HashMap<AIAgentActionId, RecordingSpanInfo>> {
let conversation_id = conversation.id();
if let Some(spans) = self
.recording_spans_by_conversation
.borrow()
.get(&conversation_id)
.cloned()
{
return spans;
}

let spans = Arc::new(conversation.recording_spans_by_action_id(Some(self)));
self.recording_spans_by_conversation
.borrow_mut()
.insert(conversation_id, spans.clone());
spans
}

pub fn invalidate_recording_spans(&self, conversation_id: AIConversationId) {
self.recording_spans_by_conversation
.borrow_mut()
.remove(&conversation_id);
}

/// Bulk restore action results from a list of exchanges (used when loading conversations from tasks)
pub fn restore_action_results_from_exchanges(&mut self, exchanges: Vec<&AIAgentExchange>) {
self.recording_spans_by_conversation.get_mut().clear();
for exchange in exchanges.iter() {
for input in &exchange.input {
if let AIAgentInput::ActionResult { result, .. } = input {
Expand Down Expand Up @@ -1262,6 +1297,9 @@ impl BlocklistAIActionModel {
pub(super) fn clear_finished_action_results(&mut self, conversation_id: AIConversationId) {
self.action_order.remove(&conversation_id);
self.finished_action_results.remove(&conversation_id);
self.recording_spans_by_conversation
.get_mut()
.remove(&conversation_id);
}

/// The control flow for initiating cancellations across suggested plans, requested commands,
Expand Down Expand Up @@ -1346,6 +1384,9 @@ impl BlocklistAIActionModel {
.entry(conversation_id)
.or_default()
.push(action_result);
self.recording_spans_by_conversation
.get_mut()
.remove(&conversation_id);

ctx.emit(BlocklistAIActionEvent::FinishedAction {
action_id,
Expand Down
35 changes: 27 additions & 8 deletions app/src/ai/blocklist/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2001,14 +2001,21 @@ impl AIBlock {
}
}

self.has_recording_related_actions = output.actions().any(|action| {
let has_recording_related_actions = output.actions().any(|action| {
matches!(
&action.action,
AIAgentActionType::StartRecording { .. }
| AIAgentActionType::StopRecording { .. }
| AIAgentActionType::UseComputer(_)
)
});
if self.has_recording_related_actions || has_recording_related_actions {
let conversation_id = self.client_ids.conversation_id;
self.action_model.update(ctx, |action_model, _ctx| {
action_model.invalidate_recording_spans(conversation_id);
});
}
self.has_recording_related_actions = has_recording_related_actions;

if FeatureFlag::WebSearchUI.is_enabled() {
// Handle WebSearch messages
Expand Down Expand Up @@ -5264,13 +5271,24 @@ impl AIBlock {
}

pub fn dismiss_ai_tooltips(&mut self, ctx: &mut ViewContext<Self>) {
self.detected_links_state.link_location_open_tooltip = None;
ctx.emit(AIBlockEvent::DismissLinkTooltip);
self.secret_redaction_state.dismiss_tooltip();
ctx.emit(AIBlockEvent::DismissSecretTooltip);
let dismissed_link_tooltip = self
.detected_links_state
.link_location_open_tooltip
.take()
.is_some();
if dismissed_link_tooltip {
ctx.emit(AIBlockEvent::DismissLinkTooltip);
}

let dismissed_secret_tooltip = self.secret_redaction_state.dismiss_tooltip();
if dismissed_secret_tooltip {
ctx.emit(AIBlockEvent::DismissSecretTooltip);
}

let mut dismissed_search_tooltip = false;
for search_view in self.search_codebase_view.values() {
search_view.update(ctx, |view, ctx| {
view.clear_link_tooltip(ctx);
dismissed_search_tooltip |= view.clear_link_tooltip(ctx);
});
}

Expand All @@ -5282,8 +5300,9 @@ impl AIBlock {
{
button_handles.reset_hover_state_on_focus_change();
}

ctx.notify();
if dismissed_link_tooltip || dismissed_secret_tooltip || dismissed_search_tooltip {
ctx.notify();
}
}

fn open_link(
Expand Down
4 changes: 2 additions & 2 deletions app/src/ai/blocklist/block/secret_redaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,8 @@ impl SecretRedactionState {
self.get_secret_mut(location, secret_range)
}

pub fn dismiss_tooltip(&mut self) {
self.secret_location_open_tooltip = None;
pub fn dismiss_tooltip(&mut self) -> bool {
self.secret_location_open_tooltip.take().is_some()
}

pub fn set_obfuscated(
Expand Down
24 changes: 10 additions & 14 deletions app/src/ai/blocklist/block/view_impl/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,21 +261,15 @@ pub(super) fn render(props: Props, app: &AppContext) -> Box<dyn Element> {
| AIBlockOutputStatus::Failed { .. } => {
if let Some(output) = status.output_to_render() {
let output = output.get();
// TODO(vkodithala): Blocks with recording-related actions still
// recompute this conversation-wide map on every render. Cache
// spans on BlocklistAIActionModel keyed by conversation and
// refresh on action/result mutations instead.
let recording_spans_by_action_id = if props.has_recording_related_actions {
props
.model
.conversation(app)
.map(|conversation| {
conversation
.recording_spans_by_action_id(Some(props.action_model.as_ref(app)))
})
.unwrap_or_default()
props.model.conversation(app).map(|conversation| {
props
.action_model
.as_ref(app)
.recording_spans_for_conversation(conversation)
})
} else {
HashMap::new()
None
};
let is_complete = matches!(status, AIBlockOutputStatus::Complete { .. });
let is_output_for_static_prompt_suggestions =
Expand Down Expand Up @@ -781,7 +775,9 @@ pub(super) fn render(props: Props, app: &AppContext) -> Box<dyn Element> {
props,
id,
request,
recording_spans_by_action_id.get(id),
recording_spans_by_action_id
.as_ref()
.and_then(|spans| spans.get(id)),
app,
));
}
Expand Down
13 changes: 10 additions & 3 deletions app/src/ai/blocklist/inline_action/search_codebase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,9 +327,16 @@ impl SearchCodebaseView {
super::search_results_common::render_status_header(text, icon, app)
}

pub fn clear_link_tooltip(&mut self, ctx: &mut ViewContext<Self>) {
self.detected_links_state.link_location_open_tooltip = None;
ctx.notify();
pub fn clear_link_tooltip(&mut self, ctx: &mut ViewContext<Self>) -> bool {
let did_clear = self
.detected_links_state
.link_location_open_tooltip
.take()
.is_some();
if did_clear {
ctx.notify();
}
did_clear
}

pub fn clear_selection(&mut self, _ctx: &mut ViewContext<Self>) {
Expand Down