added initial working status in inline assistant prompt

KCaverly created

Change summary

crates/ai/src/embedding.rs              |   2 
crates/assistant/src/assistant_panel.rs | 200 ++++++++++++++++----------
crates/theme/src/theme.rs               |   1 
styles/src/style_tree/assistant.ts      |   3 
4 files changed, 129 insertions(+), 77 deletions(-)

Detailed changes

crates/ai/src/embedding.rs 🔗

@@ -290,7 +290,7 @@ impl EmbeddingProvider for OpenAIEmbeddings {
 
         let mut request_number = 0;
         let mut rate_limiting = false;
-        let mut request_timeout: u64 = 15;
+        let mut request_timeout: u64 = 30;
         let mut response: Response<AsyncBody>;
         while request_number < MAX_RETRIES {
             response = self

crates/assistant/src/assistant_panel.rs 🔗

@@ -24,10 +24,10 @@ use futures::StreamExt;
 use gpui::{
     actions,
     elements::{
-        ChildView, Component, Empty, Flex, Label, MouseEventHandler, ParentElement, SafeStylable,
-        Stack, Svg, Text, UniformList, UniformListState,
+        ChildView, Component, Empty, Flex, Label, LabelStyle, MouseEventHandler, ParentElement,
+        SafeStylable, Stack, Svg, Text, UniformList, UniformListState,
     },
-    fonts::HighlightStyle,
+    fonts::{HighlightStyle, TextStyle},
     geometry::vector::{vec2f, Vector2F},
     platform::{CursorStyle, MouseButton, PromptLevel},
     Action, AnyElement, AppContext, AsyncAppContext, ClipboardItem, Element, Entity, ModelContext,
@@ -37,7 +37,7 @@ use gpui::{
 use language::{language_settings::SoftWrap, Buffer, LanguageRegistry, ToOffset as _};
 use project::Project;
 use search::BufferSearchBar;
-use semantic_index::SemanticIndex;
+use semantic_index::{SemanticIndex, SemanticIndexStatus};
 use settings::SettingsStore;
 use std::{
     cell::{Cell, RefCell},
@@ -2756,6 +2756,7 @@ struct InlineAssistant {
     semantic_index: Option<ModelHandle<SemanticIndex>>,
     semantic_permissioned: Option<bool>,
     project: ModelHandle<Project>,
+    maintain_rate_limit: Option<Task<()>>,
 }
 
 impl Entity for InlineAssistant {
@@ -2772,67 +2773,65 @@ impl View for InlineAssistant {
         let theme = theme::current(cx);
 
         Flex::row()
-            .with_child(
-                Flex::row()
-                    .with_child(
-                        Button::action(ToggleIncludeConversation)
-                            .with_tooltip("Include Conversation", theme.tooltip.clone())
+            .with_children([Flex::row()
+                .with_child(
+                    Button::action(ToggleIncludeConversation)
+                        .with_tooltip("Include Conversation", theme.tooltip.clone())
+                        .with_id(self.id)
+                        .with_contents(theme::components::svg::Svg::new("icons/ai.svg"))
+                        .toggleable(self.include_conversation)
+                        .with_style(theme.assistant.inline.include_conversation.clone())
+                        .element()
+                        .aligned(),
+                )
+                .with_children(if SemanticIndex::enabled(cx) {
+                    Some(
+                        Button::action(ToggleRetrieveContext)
+                            .with_tooltip("Retrieve Context", theme.tooltip.clone())
                             .with_id(self.id)
-                            .with_contents(theme::components::svg::Svg::new("icons/ai.svg"))
-                            .toggleable(self.include_conversation)
-                            .with_style(theme.assistant.inline.include_conversation.clone())
+                            .with_contents(theme::components::svg::Svg::new(
+                                "icons/magnifying_glass.svg",
+                            ))
+                            .toggleable(self.retrieve_context)
+                            .with_style(theme.assistant.inline.retrieve_context.clone())
                             .element()
                             .aligned(),
                     )
-                    .with_children(if SemanticIndex::enabled(cx) {
-                        Some(
-                            Button::action(ToggleRetrieveContext)
-                                .with_tooltip("Retrieve Context", theme.tooltip.clone())
-                                .with_id(self.id)
-                                .with_contents(theme::components::svg::Svg::new(
-                                    "icons/magnifying_glass.svg",
-                                ))
-                                .toggleable(self.retrieve_context)
-                                .with_style(theme.assistant.inline.retrieve_context.clone())
-                                .element()
-                                .aligned(),
-                        )
-                    } else {
-                        None
-                    })
-                    .with_children(if let Some(error) = self.codegen.read(cx).error() {
-                        Some(
-                            Svg::new("icons/error.svg")
-                                .with_color(theme.assistant.error_icon.color)
-                                .constrained()
-                                .with_width(theme.assistant.error_icon.width)
-                                .contained()
-                                .with_style(theme.assistant.error_icon.container)
-                                .with_tooltip::<ErrorIcon>(
-                                    self.id,
-                                    error.to_string(),
-                                    None,
-                                    theme.tooltip.clone(),
-                                    cx,
-                                )
-                                .aligned(),
-                        )
-                    } else {
-                        None
-                    })
-                    .aligned()
-                    .constrained()
-                    .dynamically({
-                        let measurements = self.measurements.clone();
-                        move |constraint, _, _| {
-                            let measurements = measurements.get();
-                            SizeConstraint {
-                                min: vec2f(measurements.gutter_width, constraint.min.y()),
-                                max: vec2f(measurements.gutter_width, constraint.max.y()),
-                            }
+                } else {
+                    None
+                })
+                .with_children(if let Some(error) = self.codegen.read(cx).error() {
+                    Some(
+                        Svg::new("icons/error.svg")
+                            .with_color(theme.assistant.error_icon.color)
+                            .constrained()
+                            .with_width(theme.assistant.error_icon.width)
+                            .contained()
+                            .with_style(theme.assistant.error_icon.container)
+                            .with_tooltip::<ErrorIcon>(
+                                self.id,
+                                error.to_string(),
+                                None,
+                                theme.tooltip.clone(),
+                                cx,
+                            )
+                            .aligned(),
+                    )
+                } else {
+                    None
+                })
+                .aligned()
+                .constrained()
+                .dynamically({
+                    let measurements = self.measurements.clone();
+                    move |constraint, _, _| {
+                        let measurements = measurements.get();
+                        SizeConstraint {
+                            min: vec2f(measurements.gutter_width, constraint.min.y()),
+                            max: vec2f(measurements.gutter_width, constraint.max.y()),
                         }
-                    }),
-            )
+                    }
+                })])
             .with_child(Empty::new().constrained().dynamically({
                 let measurements = self.measurements.clone();
                 move |constraint, _, _| {
@@ -2855,6 +2854,19 @@ impl View for InlineAssistant {
                     .left()
                     .flex(1., true),
             )
+            .with_children(if self.retrieve_context {
+                Some(
+                    Flex::row()
+                        .with_child(Label::new(
+                            self.retrieve_context_status(cx),
+                            theme.assistant.inline.context_status.text.clone(),
+                        ))
+                        .flex(1., true)
+                        .aligned(),
+                )
+            } else {
+                None
+            })
             .contained()
             .with_style(theme.assistant.inline.container)
             .into_any()
@@ -2896,11 +2908,15 @@ impl InlineAssistant {
             editor.set_placeholder_text(placeholder, cx);
             editor
         });
-        let subscriptions = vec![
+        let mut subscriptions = vec![
             cx.observe(&codegen, Self::handle_codegen_changed),
             cx.subscribe(&prompt_editor, Self::handle_prompt_editor_events),
         ];
 
+        if let Some(semantic_index) = semantic_index.clone() {
+            subscriptions.push(cx.observe(&semantic_index, Self::semantic_index_changed));
+        }
+
         Self {
             id,
             prompt_editor,
@@ -2918,6 +2934,7 @@ impl InlineAssistant {
             semantic_permissioned: None,
             semantic_index,
             project,
+            maintain_rate_limit: None,
         }
     }
 
@@ -2947,6 +2964,34 @@ impl InlineAssistant {
         }
     }
 
+    fn semantic_index_changed(
+        &mut self,
+        semantic_index: ModelHandle<SemanticIndex>,
+        cx: &mut ViewContext<Self>,
+    ) {
+        let project = self.project.clone();
+        let status = semantic_index.read(cx).status(&project);
+        match status {
+            SemanticIndexStatus::Indexing {
+                rate_limit_expiry: Some(_),
+                ..
+            } => {
+                if self.maintain_rate_limit.is_none() {
+                    self.maintain_rate_limit = Some(cx.spawn(|this, mut cx| async move {
+                        loop {
+                            cx.background().timer(Duration::from_secs(1)).await;
+                            this.update(&mut cx, |_, cx| cx.notify()).log_err();
+                        }
+                    }));
+                }
+                return;
+            }
+            _ => {
+                self.maintain_rate_limit = None;
+            }
+        }
+    }
+
     fn handle_codegen_changed(&mut self, _: ModelHandle<Codegen>, cx: &mut ViewContext<Self>) {
         let is_read_only = !self.codegen.read(cx).idle();
         self.prompt_editor.update(cx, |editor, cx| {
@@ -3044,16 +3089,7 @@ impl InlineAssistant {
                 cx.emit(InlineAssistantEvent::RetrieveContextToggled {
                     retrieve_context: this.retrieve_context,
                 });
-
-                if this.retrieve_context {
-                    let context_status = this.retrieve_context_status(cx);
-                    if let Some(workspace) = this.workspace.upgrade(cx) {
-                        workspace.update(cx, |workspace, cx| {
-                            workspace.show_toast(Toast::new(0, context_status), cx)
-                        });
-                    }
-                }
-
+                this.index_project(project, cx).log_err();
                 cx.notify();
             })?;
 
@@ -3062,6 +3098,18 @@ impl InlineAssistant {
         .detach_and_log_err(cx);
     }
 
+    fn index_project(
+        &self,
+        project: ModelHandle<Project>,
+        cx: &mut ViewContext<Self>,
+    ) -> anyhow::Result<()> {
+        if let Some(semantic_index) = self.semantic_index.clone() {
+            let _ = semantic_index.update(cx, |index, cx| index.index_project(project, cx));
+        }
+
+        anyhow::Ok(())
+    }
+
     fn retrieve_context_status(&self, cx: &mut ViewContext<Self>) -> String {
         let project = self.project.clone();
         if let Some(semantic_index) = self.semantic_index.clone() {
@@ -3072,23 +3120,23 @@ impl InlineAssistant {
                     "Not Authenticated!\nPlease ensure you have an `OPENAI_API_KEY` in your environment variables.".to_string()
                 }
                 semantic_index::SemanticIndexStatus::Indexed => {
-                    "Indexing for Context Retrieval Complete!".to_string()
+                    "Indexing Complete!".to_string()
                 }
                 semantic_index::SemanticIndexStatus::Indexing { remaining_files, rate_limit_expiry } => {
 
-                    let mut status = format!("Indexing for Context Retrieval...\nRemaining files to index: {remaining_files}");
+                    let mut status = format!("Remaining files to index for Context Retrieval: {remaining_files}");
 
                     if let Some(rate_limit_expiry) = rate_limit_expiry {
                         let remaining_seconds =
                                 rate_limit_expiry.duration_since(Instant::now());
                         if remaining_seconds > Duration::from_secs(0) {
-                            writeln!(status, "Rate limit resets in {}s", remaining_seconds.as_secs()).unwrap();
+                            write!(status, " (rate limit resets in {}s)", remaining_seconds.as_secs()).unwrap();
                         }
                     }
                     status
                 }
-                _ => {
-                    "Indexing for Context Retrieval...\nRemaining files to index: 48".to_string()
+                semantic_index::SemanticIndexStatus::NotIndexed => {
+                    "Not Indexed for Context Retrieval".to_string()
                 }
             };
         }

crates/theme/src/theme.rs 🔗

@@ -1191,6 +1191,7 @@ pub struct InlineAssistantStyle {
     pub pending_edit_background: Color,
     pub include_conversation: ToggleIconButtonStyle,
     pub retrieve_context: ToggleIconButtonStyle,
+    pub context_status: ContainedText,
 }
 
 #[derive(Clone, Deserialize, Default, JsonSchema)]

styles/src/style_tree/assistant.ts 🔗

@@ -79,6 +79,9 @@ export default function assistant(): any {
                 },
             },
             pending_edit_background: background(theme.highest, "positive"),
+            context_status: {
+                ...text(theme.highest, "mono", "disabled", { size: "sm" }),
+            },
             retrieve_context: toggleable({
                 base: interactive({
                     base: {