add semantic index status, for non authenticated users (#3005)
Update project search semantic ui to accommodate for users who have not set the OPENAI_API_KEY in their environment variables. Release Notes: - Expand Semantic Index status to include status for non authenticated users - Update Search UI to illustrate this status.
This commit is contained in:
commit
3c2b05be90
4 changed files with 43 additions and 6 deletions
|
@ -330,7 +330,7 @@ impl View for ProjectSearchView {
|
||||||
// If Text -> Major: "Text search all files and folders", Minor: {...}
|
// If Text -> Major: "Text search all files and folders", Minor: {...}
|
||||||
|
|
||||||
let current_mode = self.current_mode;
|
let current_mode = self.current_mode;
|
||||||
let major_text = if model.pending_search.is_some() {
|
let mut major_text = if model.pending_search.is_some() {
|
||||||
Cow::Borrowed("Searching...")
|
Cow::Borrowed("Searching...")
|
||||||
} else if model.no_results.is_some_and(|v| v) {
|
} else if model.no_results.is_some_and(|v| v) {
|
||||||
Cow::Borrowed("No Results")
|
Cow::Borrowed("No Results")
|
||||||
|
@ -344,9 +344,18 @@ impl View for ProjectSearchView {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut show_minor_text = true;
|
||||||
let semantic_status = self.semantic_state.as_ref().and_then(|semantic| {
|
let semantic_status = self.semantic_state.as_ref().and_then(|semantic| {
|
||||||
let status = semantic.index_status;
|
let status = semantic.index_status;
|
||||||
match status {
|
match status {
|
||||||
|
SemanticIndexStatus::NotAuthenticated => {
|
||||||
|
major_text = Cow::Borrowed("Not Authenticated");
|
||||||
|
show_minor_text = false;
|
||||||
|
Some(
|
||||||
|
"API Key Missing: Please set 'OPENAI_API_KEY' in Environment Variables"
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
SemanticIndexStatus::Indexed => Some("Indexing complete".to_string()),
|
SemanticIndexStatus::Indexed => Some("Indexing complete".to_string()),
|
||||||
SemanticIndexStatus::Indexing {
|
SemanticIndexStatus::Indexing {
|
||||||
remaining_files,
|
remaining_files,
|
||||||
|
@ -388,10 +397,13 @@ impl View for ProjectSearchView {
|
||||||
let mut minor_text = Vec::new();
|
let mut minor_text = Vec::new();
|
||||||
minor_text.push("".into());
|
minor_text.push("".into());
|
||||||
minor_text.extend(semantic_status);
|
minor_text.extend(semantic_status);
|
||||||
minor_text.push("Simply explain the code you are looking to find.".into());
|
if show_minor_text {
|
||||||
minor_text.push(
|
minor_text
|
||||||
"ex. 'prompt user for permissions to index their project'".into(),
|
.push("Simply explain the code you are looking to find.".into());
|
||||||
);
|
minor_text.push(
|
||||||
|
"ex. 'prompt user for permissions to index their project'".into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
minor_text
|
minor_text
|
||||||
}
|
}
|
||||||
_ => vec![
|
_ => vec![
|
||||||
|
|
|
@ -117,6 +117,7 @@ struct OpenAIEmbeddingUsage {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait EmbeddingProvider: Sync + Send {
|
pub trait EmbeddingProvider: Sync + Send {
|
||||||
|
fn is_authenticated(&self) -> bool;
|
||||||
async fn embed_batch(&self, spans: Vec<String>) -> Result<Vec<Embedding>>;
|
async fn embed_batch(&self, spans: Vec<String>) -> Result<Vec<Embedding>>;
|
||||||
fn max_tokens_per_batch(&self) -> usize;
|
fn max_tokens_per_batch(&self) -> usize;
|
||||||
fn truncate(&self, span: &str) -> (String, usize);
|
fn truncate(&self, span: &str) -> (String, usize);
|
||||||
|
@ -127,6 +128,9 @@ pub struct DummyEmbeddings {}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl EmbeddingProvider for DummyEmbeddings {
|
impl EmbeddingProvider for DummyEmbeddings {
|
||||||
|
fn is_authenticated(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
fn rate_limit_expiration(&self) -> Option<Instant> {
|
fn rate_limit_expiration(&self) -> Option<Instant> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -229,6 +233,9 @@ impl OpenAIEmbeddings {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl EmbeddingProvider for OpenAIEmbeddings {
|
impl EmbeddingProvider for OpenAIEmbeddings {
|
||||||
|
fn is_authenticated(&self) -> bool {
|
||||||
|
OPENAI_API_KEY.as_ref().is_some()
|
||||||
|
}
|
||||||
fn max_tokens_per_batch(&self) -> usize {
|
fn max_tokens_per_batch(&self) -> usize {
|
||||||
50000
|
50000
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ use embedding_queue::{EmbeddingQueue, FileToEmbed};
|
||||||
use futures::{future, FutureExt, StreamExt};
|
use futures::{future, FutureExt, StreamExt};
|
||||||
use gpui::{AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle};
|
use gpui::{AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle};
|
||||||
use language::{Anchor, Bias, Buffer, Language, LanguageRegistry};
|
use language::{Anchor, Bias, Buffer, Language, LanguageRegistry};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use parsing::{CodeContextRetriever, Span, SpanDigest, PARSEABLE_ENTIRE_FILE_TYPES};
|
use parsing::{CodeContextRetriever, Span, SpanDigest, PARSEABLE_ENTIRE_FILE_TYPES};
|
||||||
|
@ -24,6 +25,7 @@ use project::{search::PathMatcher, Fs, PathChange, Project, ProjectEntryId, Work
|
||||||
use smol::channel;
|
use smol::channel;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Reverse,
|
cmp::Reverse,
|
||||||
|
env,
|
||||||
future::Future,
|
future::Future,
|
||||||
mem,
|
mem,
|
||||||
ops::Range,
|
ops::Range,
|
||||||
|
@ -38,6 +40,10 @@ const SEMANTIC_INDEX_VERSION: usize = 11;
|
||||||
const BACKGROUND_INDEXING_DELAY: Duration = Duration::from_secs(5 * 60);
|
const BACKGROUND_INDEXING_DELAY: Duration = Duration::from_secs(5 * 60);
|
||||||
const EMBEDDING_QUEUE_FLUSH_TIMEOUT: Duration = Duration::from_millis(250);
|
const EMBEDDING_QUEUE_FLUSH_TIMEOUT: Duration = Duration::from_millis(250);
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref OPENAI_API_KEY: Option<String> = env::var("OPENAI_API_KEY").ok();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
http_client: Arc<dyn HttpClient>,
|
http_client: Arc<dyn HttpClient>,
|
||||||
|
@ -100,6 +106,7 @@ pub fn init(
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum SemanticIndexStatus {
|
pub enum SemanticIndexStatus {
|
||||||
|
NotAuthenticated,
|
||||||
NotIndexed,
|
NotIndexed,
|
||||||
Indexed,
|
Indexed,
|
||||||
Indexing {
|
Indexing {
|
||||||
|
@ -275,6 +282,10 @@ impl SemanticIndex {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn status(&self, project: &ModelHandle<Project>) -> SemanticIndexStatus {
|
pub fn status(&self, project: &ModelHandle<Project>) -> SemanticIndexStatus {
|
||||||
|
if !self.embedding_provider.is_authenticated() {
|
||||||
|
return SemanticIndexStatus::NotAuthenticated;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(project_state) = self.projects.get(&project.downgrade()) {
|
if let Some(project_state) = self.projects.get(&project.downgrade()) {
|
||||||
if project_state
|
if project_state
|
||||||
.worktrees
|
.worktrees
|
||||||
|
@ -694,12 +705,12 @@ impl SemanticIndex {
|
||||||
let embedding_provider = self.embedding_provider.clone();
|
let embedding_provider = self.embedding_provider.clone();
|
||||||
|
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
|
index.await?;
|
||||||
let query = embedding_provider
|
let query = embedding_provider
|
||||||
.embed_batch(vec![query])
|
.embed_batch(vec![query])
|
||||||
.await?
|
.await?
|
||||||
.pop()
|
.pop()
|
||||||
.ok_or_else(|| anyhow!("could not embed query"))?;
|
.ok_or_else(|| anyhow!("could not embed query"))?;
|
||||||
index.await?;
|
|
||||||
|
|
||||||
let search_start = Instant::now();
|
let search_start = Instant::now();
|
||||||
let modified_buffer_results = this.update(&mut cx, |this, cx| {
|
let modified_buffer_results = this.update(&mut cx, |this, cx| {
|
||||||
|
@ -965,6 +976,10 @@ impl SemanticIndex {
|
||||||
project: ModelHandle<Project>,
|
project: ModelHandle<Project>,
|
||||||
cx: &mut ModelContext<Self>,
|
cx: &mut ModelContext<Self>,
|
||||||
) -> Task<Result<()>> {
|
) -> Task<Result<()>> {
|
||||||
|
if !self.embedding_provider.is_authenticated() {
|
||||||
|
return Task::ready(Err(anyhow!("user is not authenticated")));
|
||||||
|
}
|
||||||
|
|
||||||
if !self.projects.contains_key(&project.downgrade()) {
|
if !self.projects.contains_key(&project.downgrade()) {
|
||||||
let subscription = cx.subscribe(&project, |this, project, event, cx| match event {
|
let subscription = cx.subscribe(&project, |this, project, event, cx| match event {
|
||||||
project::Event::WorktreeAdded | project::Event::WorktreeRemoved(_) => {
|
project::Event::WorktreeAdded | project::Event::WorktreeRemoved(_) => {
|
||||||
|
|
|
@ -1267,6 +1267,9 @@ impl FakeEmbeddingProvider {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl EmbeddingProvider for FakeEmbeddingProvider {
|
impl EmbeddingProvider for FakeEmbeddingProvider {
|
||||||
|
fn is_authenticated(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
fn truncate(&self, span: &str) -> (String, usize) {
|
fn truncate(&self, span: &str) -> (String, usize) {
|
||||||
(span.to_string(), 1)
|
(span.to_string(), 1)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue