@@ -85,25 +85,6 @@ impl Embedding {
}
}
-// impl FromSql for Embedding {
-// fn column_result(value: ValueRef) -> FromSqlResult<Self> {
-// let bytes = value.as_blob()?;
-// let embedding: Result<Vec<f32>, Box<bincode::ErrorKind>> = bincode::deserialize(bytes);
-// if embedding.is_err() {
-// return Err(rusqlite::types::FromSqlError::Other(embedding.unwrap_err()));
-// }
-// Ok(Embedding(embedding.unwrap()))
-// }
-// }
-
-// impl ToSql for Embedding {
-// fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
-// let bytes = bincode::serialize(&self.0)
-// .map_err(|err| rusqlite::Error::ToSqlConversionFailure(Box::new(err)))?;
-// Ok(ToSqlOutput::Owned(rusqlite::types::Value::Blob(bytes)))
-// }
-// }
-
#[derive(Clone)]
pub struct OpenAIEmbeddings {
pub client: Arc<dyn HttpClient>,
@@ -290,7 +271,7 @@ impl EmbeddingProvider for OpenAIEmbeddings {
let mut request_number = 0;
let mut rate_limiting = false;
- let mut request_timeout: u64 = 30;
+ let mut request_timeout: u64 = 15;
let mut response: Response<AsyncBody>;
while request_number < MAX_RETRIES {
response = self
@@ -300,6 +281,7 @@ impl EmbeddingProvider for OpenAIEmbeddings {
request_timeout,
)
.await?;
+
request_number += 1;
match response.status() {
@@ -52,7 +52,7 @@ use std::{
};
use theme::{
components::{action_button::Button, ComponentExt},
- AssistantStyle,
+ AssistantStyle, Icon,
};
use util::{paths::CONVERSATIONS_DIR, post_inc, ResultExt, TryFutureExt};
use uuid::Uuid;
@@ -2857,10 +2857,7 @@ impl View for InlineAssistant {
.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(),
- ))
+ .with_children(self.retrieve_context_status(cx))
.flex(1., true)
.aligned(),
)
@@ -3110,40 +3107,149 @@ impl InlineAssistant {
anyhow::Ok(())
}
- fn retrieve_context_status(&self, cx: &mut ViewContext<Self>) -> String {
+ fn retrieve_context_status(
+ &self,
+ cx: &mut ViewContext<Self>,
+ ) -> Option<AnyElement<InlineAssistant>> {
+ enum ContextStatusIcon {}
let project = self.project.clone();
- if let Some(semantic_index) = self.semantic_index.clone() {
- let status = semantic_index.update(cx, |index, cx| index.status(&project));
- return match status {
- // This theoretically shouldnt be a valid code path
- semantic_index::SemanticIndexStatus::NotAuthenticated => {
- "Not Authenticated!\nPlease ensure you have an `OPENAI_API_KEY` in your environment variables.".to_string()
- }
- semantic_index::SemanticIndexStatus::Indexed => {
- "Indexing Complete!".to_string()
- }
- semantic_index::SemanticIndexStatus::Indexing { remaining_files, rate_limit_expiry } => {
-
- let mut status = format!("Remaining files to index for Context Retrieval: {remaining_files}");
+ if let Some(semantic_index) = SemanticIndex::global(cx) {
+ let status = semantic_index.update(cx, |index, _| index.status(&project));
+ let theme = theme::current(cx);
+ match status {
+ SemanticIndexStatus::NotAuthenticated {} => 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::<ContextStatusIcon>(
+ self.id,
+ "Not Authenticated. Please ensure you have a valid 'OPENAI_API_KEY' in your environment variables.",
+ None,
+ theme.tooltip.clone(),
+ cx,
+ )
+ .aligned()
+ .into_any(),
+ ),
+ SemanticIndexStatus::NotIndexed {} => Some(
+ Svg::new("icons/error.svg")
+ .with_color(theme.assistant.inline.context_status.error_icon.color)
+ .constrained()
+ .with_width(theme.assistant.inline.context_status.error_icon.width)
+ .contained()
+ .with_style(theme.assistant.inline.context_status.error_icon.container)
+ .with_tooltip::<ContextStatusIcon>(
+ self.id,
+ "Not Indexed",
+ None,
+ theme.tooltip.clone(),
+ cx,
+ )
+ .aligned()
+ .into_any(),
+ ),
+ SemanticIndexStatus::Indexing {
+ remaining_files,
+ rate_limit_expiry,
+ } => {
+
+ let mut status_text = if remaining_files == 0 {
+ "Indexing...".to_string()
+ } else {
+ format!("Remaining files to index: {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) {
- write!(status, " (rate limit resets in {}s)", remaining_seconds.as_secs()).unwrap();
+ let remaining_seconds = rate_limit_expiry.duration_since(Instant::now());
+ if remaining_seconds > Duration::from_secs(0) && remaining_files > 0 {
+ write!(
+ status_text,
+ " (rate limit expires in {}s)",
+ remaining_seconds.as_secs()
+ )
+ .unwrap();
}
}
- status
- }
- semantic_index::SemanticIndexStatus::NotIndexed => {
- "Not Indexed for Context Retrieval".to_string()
+ Some(
+ Svg::new("icons/bolt.svg")
+ .with_color(theme.assistant.inline.context_status.in_progress_icon.color)
+ .constrained()
+ .with_width(theme.assistant.inline.context_status.in_progress_icon.width)
+ .contained()
+ .with_style(theme.assistant.inline.context_status.in_progress_icon.container)
+ .with_tooltip::<ContextStatusIcon>(
+ self.id,
+ status_text,
+ None,
+ theme.tooltip.clone(),
+ cx,
+ )
+ .aligned()
+ .into_any(),
+ )
}
- };
+ SemanticIndexStatus::Indexed {} => Some(
+ Svg::new("icons/circle_check.svg")
+ .with_color(theme.assistant.inline.context_status.complete_icon.color)
+ .constrained()
+ .with_width(theme.assistant.inline.context_status.complete_icon.width)
+ .contained()
+ .with_style(theme.assistant.inline.context_status.complete_icon.container)
+ .with_tooltip::<ContextStatusIcon>(
+ self.id,
+ "Indexing Complete",
+ None,
+ theme.tooltip.clone(),
+ cx,
+ )
+ .aligned()
+ .into_any(),
+ ),
+ }
+ } else {
+ None
}
-
- "".to_string()
}
+ // fn retrieve_context_status(&self, cx: &mut ViewContext<Self>) -> String {
+ // let project = self.project.clone();
+ // if let Some(semantic_index) = self.semantic_index.clone() {
+ // let status = semantic_index.update(cx, |index, cx| index.status(&project));
+ // return match status {
+ // // This theoretically shouldnt be a valid code path
+ // // As the inline assistant cant be launched without an API key
+ // // We keep it here for safety
+ // semantic_index::SemanticIndexStatus::NotAuthenticated => {
+ // "Not Authenticated!\nPlease ensure you have an `OPENAI_API_KEY` in your environment variables.".to_string()
+ // }
+ // semantic_index::SemanticIndexStatus::Indexed => {
+ // "Indexing Complete!".to_string()
+ // }
+ // semantic_index::SemanticIndexStatus::Indexing { remaining_files, rate_limit_expiry } => {
+
+ // 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) {
+ // write!(status, " (rate limit resets in {}s)", remaining_seconds.as_secs()).unwrap();
+ // }
+ // }
+ // status
+ // }
+ // semantic_index::SemanticIndexStatus::NotIndexed => {
+ // "Not Indexed for Context Retrieval".to_string()
+ // }
+ // };
+ // }
+
+ // "".to_string()
+ // }
+
fn toggle_include_conversation(
&mut self,
_: &ToggleIncludeConversation,
@@ -1191,7 +1191,14 @@ pub struct InlineAssistantStyle {
pub pending_edit_background: Color,
pub include_conversation: ToggleIconButtonStyle,
pub retrieve_context: ToggleIconButtonStyle,
- pub context_status: ContainedText,
+ pub context_status: ContextStatusStyle,
+}
+
+#[derive(Clone, Deserialize, Default, JsonSchema)]
+pub struct ContextStatusStyle {
+ pub error_icon: Icon,
+ pub in_progress_icon: Icon,
+ pub complete_icon: Icon,
}
#[derive(Clone, Deserialize, Default, JsonSchema)]
@@ -80,7 +80,21 @@ export default function assistant(): any {
},
pending_edit_background: background(theme.highest, "positive"),
context_status: {
- ...text(theme.highest, "mono", "disabled", { size: "sm" }),
+ error_icon: {
+ margin: { left: 8, right: 8 },
+ color: foreground(theme.highest, "negative"),
+ width: 12,
+ },
+ in_progress_icon: {
+ margin: { left: 8, right: 8 },
+ color: foreground(theme.highest, "warning"),
+ width: 12,
+ },
+ complete_icon: {
+ margin: { left: 8, right: 8 },
+ color: foreground(theme.highest, "positive"),
+ width: 12,
+ }
},
retrieve_context: toggleable({
base: interactive({
@@ -94,6 +108,7 @@ export default function assistant(): any {
border: {
width: 1., color: background(theme.highest, "on")
},
+ margin: { left: 2 },
padding: {
left: 4,
right: 4,