Distinguish between missing models and registries in error messages (#32678)
Consolidates configuration error handling by moving the error type and logic from assistant_context_editor to language_model::registry. The registry now provides a single method to check for configuration errors, making the error handling more consistent across the agent panel and context editor. This also now checks if the issue is that we don't have any providers, or if we just can't find the model. Previously, an incorrect model name showed up as having no providers, which is very confusing. Release Notes: - N/A
This commit is contained in:
parent
fc7c106b2a
commit
9427833fdf
4 changed files with 218 additions and 207 deletions
|
@ -10,9 +10,9 @@ use serde::{Deserialize, Serialize};
|
|||
use agent_settings::{AgentDockPosition, AgentSettings, CompletionMode, DefaultView};
|
||||
use anyhow::{Result, anyhow};
|
||||
use assistant_context_editor::{
|
||||
AgentPanelDelegate, AssistantContext, ConfigurationError, ContextEditor, ContextEvent,
|
||||
ContextSummary, SlashCommandCompletionProvider, humanize_token_count,
|
||||
make_lsp_adapter_delegate, render_remaining_tokens,
|
||||
AgentPanelDelegate, AssistantContext, ContextEditor, ContextEvent, ContextSummary,
|
||||
SlashCommandCompletionProvider, humanize_token_count, make_lsp_adapter_delegate,
|
||||
render_remaining_tokens,
|
||||
};
|
||||
use assistant_slash_command::SlashCommandWorkingSet;
|
||||
use assistant_tool::ToolWorkingSet;
|
||||
|
@ -29,7 +29,8 @@ use gpui::{
|
|||
};
|
||||
use language::LanguageRegistry;
|
||||
use language_model::{
|
||||
LanguageModelProviderTosView, LanguageModelRegistry, RequestUsage, ZED_CLOUD_PROVIDER_ID,
|
||||
ConfigurationError, LanguageModelProviderTosView, LanguageModelRegistry, RequestUsage,
|
||||
ZED_CLOUD_PROVIDER_ID,
|
||||
};
|
||||
use project::{Project, ProjectPath, Worktree};
|
||||
use prompt_store::{PromptBuilder, PromptStore, UserPromptId};
|
||||
|
@ -2353,24 +2354,6 @@ impl AgentPanel {
|
|||
self.thread.clone().into_any_element()
|
||||
}
|
||||
|
||||
fn configuration_error(&self, cx: &App) -> Option<ConfigurationError> {
|
||||
let Some(model) = LanguageModelRegistry::read_global(cx).default_model() else {
|
||||
return Some(ConfigurationError::NoProvider);
|
||||
};
|
||||
|
||||
if !model.provider.is_authenticated(cx) {
|
||||
return Some(ConfigurationError::ProviderNotAuthenticated);
|
||||
}
|
||||
|
||||
if model.provider.must_accept_terms(cx) {
|
||||
return Some(ConfigurationError::ProviderPendingTermsAcceptance(
|
||||
model.provider,
|
||||
));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn render_thread_empty_state(
|
||||
&self,
|
||||
window: &mut Window,
|
||||
|
@ -2380,7 +2363,9 @@ impl AgentPanel {
|
|||
.history_store
|
||||
.update(cx, |this, cx| this.recent_entries(6, cx));
|
||||
|
||||
let configuration_error = self.configuration_error(cx);
|
||||
let model_registry = LanguageModelRegistry::read_global(cx);
|
||||
let configuration_error =
|
||||
model_registry.configuration_error(model_registry.default_model(), cx);
|
||||
let no_error = configuration_error.is_none();
|
||||
let focus_handle = self.focus_handle(cx);
|
||||
|
||||
|
@ -2397,11 +2382,7 @@ impl AgentPanel {
|
|||
.justify_center()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.child(
|
||||
h_flex().child(
|
||||
Headline::new("Welcome to the Agent Panel")
|
||||
),
|
||||
)
|
||||
.child(h_flex().child(Headline::new("Welcome to the Agent Panel")))
|
||||
.when(no_error, |parent| {
|
||||
parent
|
||||
.child(
|
||||
|
@ -2425,7 +2406,10 @@ impl AgentPanel {
|
|||
cx,
|
||||
))
|
||||
.on_click(|_event, window, cx| {
|
||||
window.dispatch_action(NewThread::default().boxed_clone(), cx)
|
||||
window.dispatch_action(
|
||||
NewThread::default().boxed_clone(),
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
|
@ -2442,7 +2426,10 @@ impl AgentPanel {
|
|||
cx,
|
||||
))
|
||||
.on_click(|_event, window, cx| {
|
||||
window.dispatch_action(ToggleContextPicker.boxed_clone(), cx)
|
||||
window.dispatch_action(
|
||||
ToggleContextPicker.boxed_clone(),
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
|
@ -2459,7 +2446,10 @@ impl AgentPanel {
|
|||
cx,
|
||||
))
|
||||
.on_click(|_event, window, cx| {
|
||||
window.dispatch_action(ToggleModelSelector.boxed_clone(), cx)
|
||||
window.dispatch_action(
|
||||
ToggleModelSelector.boxed_clone(),
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
|
@ -2476,51 +2466,50 @@ impl AgentPanel {
|
|||
cx,
|
||||
))
|
||||
.on_click(|_event, window, cx| {
|
||||
window.dispatch_action(OpenConfiguration.boxed_clone(), cx)
|
||||
window.dispatch_action(
|
||||
OpenConfiguration.boxed_clone(),
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
)
|
||||
})
|
||||
.map(|parent| {
|
||||
match configuration_error_ref {
|
||||
Some(ConfigurationError::ProviderNotAuthenticated)
|
||||
| Some(ConfigurationError::NoProvider) => {
|
||||
parent
|
||||
.child(
|
||||
h_flex().child(
|
||||
Label::new("To start using the agent, configure at least one LLM provider.")
|
||||
.color(Color::Muted)
|
||||
.mb_2p5()
|
||||
)
|
||||
)
|
||||
.child(
|
||||
Button::new("settings", "Configure a Provider")
|
||||
.icon(IconName::Settings)
|
||||
.icon_position(IconPosition::Start)
|
||||
.icon_size(IconSize::Small)
|
||||
.icon_color(Color::Muted)
|
||||
.full_width()
|
||||
.key_binding(KeyBinding::for_action_in(
|
||||
&OpenConfiguration,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
))
|
||||
.on_click(|_event, window, cx| {
|
||||
window.dispatch_action(OpenConfiguration.boxed_clone(), cx)
|
||||
}),
|
||||
)
|
||||
}
|
||||
Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => {
|
||||
parent.children(
|
||||
provider.render_accept_terms(
|
||||
LanguageModelProviderTosView::ThreadFreshStart,
|
||||
.map(|parent| match configuration_error_ref {
|
||||
Some(
|
||||
err @ (ConfigurationError::ModelNotFound
|
||||
| ConfigurationError::ProviderNotAuthenticated(_)
|
||||
| ConfigurationError::NoProvider),
|
||||
) => parent
|
||||
.child(h_flex().child(
|
||||
Label::new(err.to_string()).color(Color::Muted).mb_2p5(),
|
||||
))
|
||||
.child(
|
||||
Button::new("settings", "Configure a Provider")
|
||||
.icon(IconName::Settings)
|
||||
.icon_position(IconPosition::Start)
|
||||
.icon_size(IconSize::Small)
|
||||
.icon_color(Color::Muted)
|
||||
.full_width()
|
||||
.key_binding(KeyBinding::for_action_in(
|
||||
&OpenConfiguration,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
)
|
||||
}
|
||||
None => parent,
|
||||
))
|
||||
.on_click(|_event, window, cx| {
|
||||
window.dispatch_action(
|
||||
OpenConfiguration.boxed_clone(),
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
),
|
||||
Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => {
|
||||
parent.children(provider.render_accept_terms(
|
||||
LanguageModelProviderTosView::ThreadFreshStart,
|
||||
cx,
|
||||
))
|
||||
}
|
||||
})
|
||||
None => parent,
|
||||
}),
|
||||
)
|
||||
})
|
||||
.when(!recent_history.is_empty(), |parent| {
|
||||
|
@ -2555,7 +2544,8 @@ impl AgentPanel {
|
|||
&self.focus_handle(cx),
|
||||
window,
|
||||
cx,
|
||||
).map(|kb| kb.size(rems_from_px(12.))),
|
||||
)
|
||||
.map(|kb| kb.size(rems_from_px(12.))),
|
||||
)
|
||||
.on_click(move |_event, window, cx| {
|
||||
window.dispatch_action(OpenHistory.boxed_clone(), cx);
|
||||
|
@ -2565,79 +2555,68 @@ impl AgentPanel {
|
|||
.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.children(
|
||||
recent_history.into_iter().enumerate().map(|(index, entry)| {
|
||||
.children(recent_history.into_iter().enumerate().map(
|
||||
|(index, entry)| {
|
||||
// TODO: Add keyboard navigation.
|
||||
let is_hovered = self.hovered_recent_history_item == Some(index);
|
||||
let is_hovered =
|
||||
self.hovered_recent_history_item == Some(index);
|
||||
HistoryEntryElement::new(entry.clone(), cx.entity().downgrade())
|
||||
.hovered(is_hovered)
|
||||
.on_hover(cx.listener(move |this, is_hovered, _window, cx| {
|
||||
if *is_hovered {
|
||||
this.hovered_recent_history_item = Some(index);
|
||||
} else if this.hovered_recent_history_item == Some(index) {
|
||||
this.hovered_recent_history_item = None;
|
||||
}
|
||||
cx.notify();
|
||||
}))
|
||||
.on_hover(cx.listener(
|
||||
move |this, is_hovered, _window, cx| {
|
||||
if *is_hovered {
|
||||
this.hovered_recent_history_item = Some(index);
|
||||
} else if this.hovered_recent_history_item
|
||||
== Some(index)
|
||||
{
|
||||
this.hovered_recent_history_item = None;
|
||||
}
|
||||
cx.notify();
|
||||
},
|
||||
))
|
||||
.into_any_element()
|
||||
}),
|
||||
)
|
||||
},
|
||||
)),
|
||||
)
|
||||
.map(|parent| {
|
||||
match configuration_error_ref {
|
||||
Some(ConfigurationError::ProviderNotAuthenticated)
|
||||
| Some(ConfigurationError::NoProvider) => {
|
||||
parent
|
||||
.child(
|
||||
Banner::new()
|
||||
.severity(ui::Severity::Warning)
|
||||
.child(
|
||||
Label::new(
|
||||
"Configure at least one LLM provider to start using the panel.",
|
||||
)
|
||||
.size(LabelSize::Small),
|
||||
.map(|parent| match configuration_error_ref {
|
||||
Some(
|
||||
err @ (ConfigurationError::ModelNotFound
|
||||
| ConfigurationError::ProviderNotAuthenticated(_)
|
||||
| ConfigurationError::NoProvider),
|
||||
) => parent.child(
|
||||
Banner::new()
|
||||
.severity(ui::Severity::Warning)
|
||||
.child(Label::new(err.to_string()).size(LabelSize::Small))
|
||||
.action_slot(
|
||||
Button::new("settings", "Configure Provider")
|
||||
.style(ButtonStyle::Tinted(ui::TintColor::Warning))
|
||||
.label_size(LabelSize::Small)
|
||||
.key_binding(
|
||||
KeyBinding::for_action_in(
|
||||
&OpenConfiguration,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.action_slot(
|
||||
Button::new("settings", "Configure Provider")
|
||||
.style(ButtonStyle::Tinted(ui::TintColor::Warning))
|
||||
.label_size(LabelSize::Small)
|
||||
.key_binding(
|
||||
KeyBinding::for_action_in(
|
||||
&OpenConfiguration,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.map(|kb| kb.size(rems_from_px(12.))),
|
||||
)
|
||||
.on_click(|_event, window, cx| {
|
||||
window.dispatch_action(
|
||||
OpenConfiguration.boxed_clone(),
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => {
|
||||
parent
|
||||
.child(
|
||||
Banner::new()
|
||||
.severity(ui::Severity::Warning)
|
||||
.child(
|
||||
h_flex()
|
||||
.w_full()
|
||||
.children(
|
||||
provider.render_accept_terms(
|
||||
LanguageModelProviderTosView::ThreadtEmptyState,
|
||||
cx,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
None => parent,
|
||||
.map(|kb| kb.size(rems_from_px(12.))),
|
||||
)
|
||||
.on_click(|_event, window, cx| {
|
||||
window.dispatch_action(
|
||||
OpenConfiguration.boxed_clone(),
|
||||
cx,
|
||||
)
|
||||
}),
|
||||
),
|
||||
),
|
||||
Some(ConfigurationError::ProviderPendingTermsAcceptance(provider)) => {
|
||||
parent.child(Banner::new().severity(ui::Severity::Warning).child(
|
||||
h_flex().w_full().children(provider.render_accept_terms(
|
||||
LanguageModelProviderTosView::ThreadtEmptyState,
|
||||
cx,
|
||||
)),
|
||||
))
|
||||
}
|
||||
None => parent,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ use gpui::{
|
|||
WeakEntity, Window, point,
|
||||
};
|
||||
use language::{Buffer, Point, Selection, TransactionId};
|
||||
use language_model::ConfigurationError;
|
||||
use language_model::ConfiguredModel;
|
||||
use language_model::{LanguageModelRegistry, report_assistant_event};
|
||||
use multi_buffer::MultiBufferRow;
|
||||
|
@ -232,10 +233,9 @@ impl InlineAssistant {
|
|||
return;
|
||||
};
|
||||
|
||||
let is_authenticated = || {
|
||||
LanguageModelRegistry::read_global(cx)
|
||||
.inline_assistant_model()
|
||||
.map_or(false, |model| model.provider.is_authenticated(cx))
|
||||
let configuration_error = || {
|
||||
let model_registry = LanguageModelRegistry::read_global(cx);
|
||||
model_registry.configuration_error(model_registry.inline_assistant_model(), cx)
|
||||
};
|
||||
|
||||
let Some(agent_panel) = workspace.panel::<AgentPanel>(cx) else {
|
||||
|
@ -283,20 +283,23 @@ impl InlineAssistant {
|
|||
}
|
||||
};
|
||||
|
||||
if is_authenticated() {
|
||||
handle_assist(window, cx);
|
||||
} else {
|
||||
cx.spawn_in(window, async move |_workspace, cx| {
|
||||
let Some(task) = cx.update(|_, cx| {
|
||||
LanguageModelRegistry::read_global(cx)
|
||||
.inline_assistant_model()
|
||||
.map_or(None, |model| Some(model.provider.authenticate(cx)))
|
||||
})?
|
||||
else {
|
||||
if let Some(error) = configuration_error() {
|
||||
if let ConfigurationError::ProviderNotAuthenticated(provider) = error {
|
||||
cx.spawn(async move |_, cx| {
|
||||
cx.update(|cx| provider.authenticate(cx))?.await?;
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
|
||||
if configuration_error().is_none() {
|
||||
handle_assist(window, cx);
|
||||
}
|
||||
} else {
|
||||
cx.spawn_in(window, async move |_, cx| {
|
||||
let answer = cx
|
||||
.prompt(
|
||||
gpui::PromptLevel::Warning,
|
||||
"No language model provider configured",
|
||||
&error.to_string(),
|
||||
None,
|
||||
&["Configure", "Cancel"],
|
||||
)
|
||||
|
@ -310,17 +313,12 @@ impl InlineAssistant {
|
|||
.ok();
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
};
|
||||
task.await?;
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
|
||||
if is_authenticated() {
|
||||
handle_assist(window, cx);
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
} else {
|
||||
handle_assist(window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue