Skip to content

Commit be5b39a

Browse files
moirahuangoz-agent
andauthored
[5/5] Handle remote codebase auto-index follow-ups (#11092)
## Description 1. Allow for going from no auto indexing of repo -> auto starting indexing when the setting gets changed. Calling out that even if indexing hasn't happened for the repo, the agent knows that it has the search codebase tool available to itself (this is true for local) 2. Pass the number of allowed indexed repos ## Testing Added unit tests + local tests - [x] I have manually tested my changes locally with `./script/run` ### Screenshots / Videos https://www.loom.com/share/a2e572c9338e43d0b329ef241256b91d ## Agent Mode - [x] Warp Agent Mode - This PR was created via Warp's AI Agent Mode CHANGELOG-BUG-FIX: Fix remote codebase indexing incremental sync and queued-status reporting for indexes that do not actually start. _Conversation: https://staging.warp.dev/conversation/cdd3638d-f1c3-479f-ab50-f62018beee7e_ _Run: https://oz.staging.warp.dev/runs/019e2e95-b78f-79f5-892a-56aa7d09b25a_ _This PR was generated with [Oz](https://warp.dev/oz)._ --------- Co-authored-by: Oz <oz-agent@warp.dev>
1 parent 1f314ff commit be5b39a

19 files changed

Lines changed: 837 additions & 106 deletions

File tree

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
use std::collections::HashSet;
2+
use std::hash::Hash;
3+
4+
use warp_core::features::FeatureFlag;
5+
use warpui::{AppContext, SingletonEntity};
6+
7+
use crate::settings::CodeSettings;
8+
use crate::workspaces::user_workspaces::UserWorkspaces;
9+
10+
#[derive(Clone, Copy, Debug)]
11+
pub(crate) enum CodebaseAutoIndexingSurface {
12+
Local,
13+
Remote,
14+
}
15+
16+
impl CodebaseAutoIndexingSurface {
17+
fn required_feature_enabled(self) -> bool {
18+
match self {
19+
Self::Local => true,
20+
Self::Remote => FeatureFlag::RemoteCodebaseIndexing.is_enabled(),
21+
}
22+
}
23+
}
24+
25+
pub(crate) fn should_auto_index_codebase(
26+
surface: CodebaseAutoIndexingSurface,
27+
ctx: &AppContext,
28+
) -> bool {
29+
codebase_auto_indexing_enabled(
30+
surface,
31+
UserWorkspaces::as_ref(ctx).is_codebase_context_enabled(ctx),
32+
*CodeSettings::as_ref(ctx).auto_indexing_enabled,
33+
)
34+
}
35+
36+
pub(crate) fn codebase_auto_indexing_enabled(
37+
surface: CodebaseAutoIndexingSurface,
38+
codebase_context_enabled: bool,
39+
auto_indexing_enabled: bool,
40+
) -> bool {
41+
FeatureFlag::FullSourceCodeEmbedding.is_enabled()
42+
&& surface.required_feature_enabled()
43+
&& codebase_context_enabled
44+
&& auto_indexing_enabled
45+
}
46+
47+
pub(crate) fn auto_index_candidate_roots<Root>(
48+
roots: impl IntoIterator<Item = Root>,
49+
mut should_request_index: impl FnMut(&Root) -> bool,
50+
) -> Vec<Root>
51+
where
52+
Root: Clone + Eq + Hash,
53+
{
54+
let mut seen = HashSet::new();
55+
let mut candidates = Vec::new();
56+
for root in roots {
57+
if seen.insert(root.clone()) && should_request_index(&root) {
58+
candidates.push(root);
59+
}
60+
}
61+
candidates
62+
}
63+
64+
#[cfg(test)]
65+
mod tests {
66+
use super::*;
67+
68+
#[test]
69+
fn local_auto_indexing_requires_full_source_code_embedding_codebase_context_and_auto_indexing()
70+
{
71+
{
72+
let _flag = FeatureFlag::FullSourceCodeEmbedding.override_enabled(false);
73+
assert!(!codebase_auto_indexing_enabled(
74+
CodebaseAutoIndexingSurface::Local,
75+
true,
76+
true,
77+
));
78+
}
79+
{
80+
let _flag = FeatureFlag::FullSourceCodeEmbedding.override_enabled(true);
81+
assert!(codebase_auto_indexing_enabled(
82+
CodebaseAutoIndexingSurface::Local,
83+
true,
84+
true,
85+
));
86+
assert!(!codebase_auto_indexing_enabled(
87+
CodebaseAutoIndexingSurface::Local,
88+
false,
89+
true,
90+
));
91+
assert!(!codebase_auto_indexing_enabled(
92+
CodebaseAutoIndexingSurface::Local,
93+
true,
94+
false,
95+
));
96+
assert!(!codebase_auto_indexing_enabled(
97+
CodebaseAutoIndexingSurface::Local,
98+
false,
99+
false,
100+
));
101+
}
102+
}
103+
104+
#[test]
105+
fn remote_auto_indexing_requires_remote_feature() {
106+
{
107+
let _remote_flag = FeatureFlag::RemoteCodebaseIndexing.override_enabled(false);
108+
let _flag = FeatureFlag::FullSourceCodeEmbedding.override_enabled(true);
109+
assert!(!codebase_auto_indexing_enabled(
110+
CodebaseAutoIndexingSurface::Remote,
111+
true,
112+
true,
113+
));
114+
}
115+
{
116+
let _remote_flag = FeatureFlag::RemoteCodebaseIndexing.override_enabled(true);
117+
let _flag = FeatureFlag::FullSourceCodeEmbedding.override_enabled(true);
118+
assert!(codebase_auto_indexing_enabled(
119+
CodebaseAutoIndexingSurface::Remote,
120+
true,
121+
true,
122+
));
123+
}
124+
}
125+
126+
#[test]
127+
fn candidate_roots_are_deduped_before_filtering() {
128+
let roots = vec!["/repo", "/repo", "/other"];
129+
let candidates = auto_index_candidate_roots(roots, |root| *root != "/other");
130+
131+
assert_eq!(candidates, vec!["/repo"]);
132+
}
133+
}

app/src/ai/get_relevant_files/remote_search/native.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ async fn execute_remote_codebase_search(
141141
.codebase_context_config()
142142
.await?
143143
.embedding_config;
144-
log::info!(
144+
log::debug!(
145145
"[Remote codebase indexing] Remote codebase search using embedding config: repo_path={repo_path} embedding_config={embedding_config:?}"
146146
);
147147
let candidate_hashes = store_client

app/src/ai/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ pub mod auth_secret_types;
1818
pub mod aws_credentials;
1919
pub(crate) mod block_context;
2020
pub(crate) mod blocklist;
21+
#[cfg(any(feature = "local_fs", not(target_family = "wasm")))]
22+
pub(crate) mod codebase_auto_indexing;
2123
pub mod control_code_parser;
2224
pub(crate) mod conversation_details_panel;
2325
pub(crate) mod conversation_navigation;

app/src/ai/persisted_workspace.rs

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ use repo_metadata::repositories::{DetectedRepositories, DetectedRepositoriesEven
1111
use serde::{Deserialize, Serialize};
1212

1313
use crate::ai::blocklist::{BlocklistAIHistoryEvent, BlocklistAIHistoryModel};
14+
#[cfg(feature = "local_fs")]
15+
use crate::ai::codebase_auto_indexing::{
16+
auto_index_candidate_roots, should_auto_index_codebase, CodebaseAutoIndexingSurface,
17+
};
1418
use crate::ai::AIRequestUsageModel;
1519
use crate::persistence::ModelEvent;
1620
use crate::report_if_error;
@@ -29,6 +33,8 @@ use itertools::Itertools;
2933
use lsp::supported_servers::LSPServerType;
3034
use warp_core::features::FeatureFlag;
3135
#[cfg(feature = "local_fs")]
36+
use warp_util::local_or_remote_path::LocalOrRemotePath;
37+
#[cfg(feature = "local_fs")]
3238
use warpui::windowing::WindowManager;
3339
use warpui::{AppContext, Entity, ModelContext, SingletonEntity};
3440

@@ -651,19 +657,14 @@ impl PersistedWorkspace {
651657
);
652658

653659
#[cfg(feature = "local_fs")]
654-
for dir in all_working_directories(ctx) {
655-
// Auto-index working directory ONLY if the user has "Read files" set to "Always allow" OR this directory is in the allowlist.
656-
let auto_indexing_enabled = *CodeSettings::as_ref(ctx).auto_indexing_enabled;
657-
658-
if auto_indexing_enabled {
659-
if let Some(root) = DetectedRepositories::as_ref(ctx)
660-
.get_root_for_path(&warp_util::local_or_remote_path::LocalOrRemotePath::Local(
661-
dir.clone(),
662-
))
663-
.and_then(|r| r.to_local_path().map(std::path::Path::to_path_buf))
664-
{
665-
manager.index_directory(root, ctx);
666-
}
660+
if should_auto_index_codebase(CodebaseAutoIndexingSurface::Local, ctx) {
661+
let roots = all_working_directories(ctx).into_iter().filter_map(|dir| {
662+
DetectedRepositories::as_ref(ctx)
663+
.get_root_for_path(&LocalOrRemotePath::Local(dir))
664+
.and_then(|root| root.to_local_path().map(Path::to_path_buf))
665+
});
666+
for root in auto_index_candidate_roots(roots, |_| true) {
667+
manager.index_directory(root, ctx);
667668
}
668669
}
669670
}
@@ -816,13 +817,21 @@ impl PersistedWorkspace {
816817
for terminal_view in terminal_views.into_iter().flatten() {
817818
let terminal_view_ref = terminal_view.as_ref(ctx);
818819
if terminal_view_ref.view_id() == terminal_view_id {
819-
if let Some(pwd) = terminal_view_ref.pwd() {
820-
let directory_path = Path::new(&pwd);
820+
if terminal_view_ref.active_session_is_local(ctx) != Some(true) {
821+
log::info!(
822+
"Skipping local codebase incremental sync for non-local agent conversation"
823+
);
824+
return;
825+
}
826+
827+
let pwd = terminal_view_ref.pwd();
828+
if let Some(pwd) = pwd {
829+
let directory_path = PathBuf::from(pwd);
821830

822831
// Trigger an incremental sync through the CodebaseIndexManager
823832
CodebaseIndexManager::handle(ctx).update(ctx, |codebase_manager, ctx| {
824833
if let Err(e) = codebase_manager
825-
.trigger_incremental_sync_for_path(directory_path, ctx)
834+
.trigger_incremental_sync_for_path(&directory_path, ctx)
826835
{
827836
log::warn!("Failed to trigger incremental sync {e}");
828837
}

0 commit comments

Comments
 (0)