Handle auth for claude (#36442)
We'll now use the anthropic provider to get credentials for `claude` and embed its configuration view in the panel when they are not present. Release Notes: - N/A
This commit is contained in:
parent
50819a9d20
commit
8b89ea1a80
32 changed files with 400 additions and 124 deletions
|
@ -15,11 +15,11 @@ use gpui::{
|
|||
};
|
||||
use http_client::HttpClient;
|
||||
use language_model::{
|
||||
AuthenticateError, LanguageModel, LanguageModelCacheConfiguration,
|
||||
LanguageModelCompletionError, LanguageModelId, LanguageModelName, LanguageModelProvider,
|
||||
LanguageModelProviderId, LanguageModelProviderName, LanguageModelProviderState,
|
||||
LanguageModelRequest, LanguageModelToolChoice, LanguageModelToolResultContent, MessageContent,
|
||||
RateLimiter, Role,
|
||||
AuthenticateError, ConfigurationViewTargetAgent, LanguageModel,
|
||||
LanguageModelCacheConfiguration, LanguageModelCompletionError, LanguageModelId,
|
||||
LanguageModelName, LanguageModelProvider, LanguageModelProviderId, LanguageModelProviderName,
|
||||
LanguageModelProviderState, LanguageModelRequest, LanguageModelToolChoice,
|
||||
LanguageModelToolResultContent, MessageContent, RateLimiter, Role,
|
||||
};
|
||||
use language_model::{LanguageModelCompletionEvent, LanguageModelToolUse, StopReason};
|
||||
use schemars::JsonSchema;
|
||||
|
@ -153,29 +153,14 @@ impl State {
|
|||
return Task::ready(Ok(()));
|
||||
}
|
||||
|
||||
let credentials_provider = <dyn CredentialsProvider>::global(cx);
|
||||
let api_url = AllLanguageModelSettings::get_global(cx)
|
||||
.anthropic
|
||||
.api_url
|
||||
.clone();
|
||||
let key = AnthropicLanguageModelProvider::api_key(cx);
|
||||
|
||||
cx.spawn(async move |this, cx| {
|
||||
let (api_key, from_env) = if let Ok(api_key) = std::env::var(ANTHROPIC_API_KEY_VAR) {
|
||||
(api_key, true)
|
||||
} else {
|
||||
let (_, api_key) = credentials_provider
|
||||
.read_credentials(&api_url, &cx)
|
||||
.await?
|
||||
.ok_or(AuthenticateError::CredentialsNotFound)?;
|
||||
(
|
||||
String::from_utf8(api_key).context("invalid {PROVIDER_NAME} API key")?,
|
||||
false,
|
||||
)
|
||||
};
|
||||
let key = key.await?;
|
||||
|
||||
this.update(cx, |this, cx| {
|
||||
this.api_key = Some(api_key);
|
||||
this.api_key_from_env = from_env;
|
||||
this.api_key = Some(key.key);
|
||||
this.api_key_from_env = key.from_env;
|
||||
cx.notify();
|
||||
})?;
|
||||
|
||||
|
@ -184,6 +169,11 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ApiKey {
|
||||
pub key: String,
|
||||
pub from_env: bool,
|
||||
}
|
||||
|
||||
impl AnthropicLanguageModelProvider {
|
||||
pub fn new(http_client: Arc<dyn HttpClient>, cx: &mut App) -> Self {
|
||||
let state = cx.new(|cx| State {
|
||||
|
@ -206,6 +196,33 @@ impl AnthropicLanguageModelProvider {
|
|||
request_limiter: RateLimiter::new(4),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn api_key(cx: &mut App) -> Task<Result<ApiKey>> {
|
||||
let credentials_provider = <dyn CredentialsProvider>::global(cx);
|
||||
let api_url = AllLanguageModelSettings::get_global(cx)
|
||||
.anthropic
|
||||
.api_url
|
||||
.clone();
|
||||
|
||||
if let Ok(key) = std::env::var(ANTHROPIC_API_KEY_VAR) {
|
||||
Task::ready(Ok(ApiKey {
|
||||
key,
|
||||
from_env: true,
|
||||
}))
|
||||
} else {
|
||||
cx.spawn(async move |cx| {
|
||||
let (_, api_key) = credentials_provider
|
||||
.read_credentials(&api_url, &cx)
|
||||
.await?
|
||||
.ok_or(AuthenticateError::CredentialsNotFound)?;
|
||||
|
||||
Ok(ApiKey {
|
||||
key: String::from_utf8(api_key).context("invalid {PROVIDER_NAME} API key")?,
|
||||
from_env: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LanguageModelProviderState for AnthropicLanguageModelProvider {
|
||||
|
@ -299,8 +316,13 @@ impl LanguageModelProvider for AnthropicLanguageModelProvider {
|
|||
self.state.update(cx, |state, cx| state.authenticate(cx))
|
||||
}
|
||||
|
||||
fn configuration_view(&self, window: &mut Window, cx: &mut App) -> AnyView {
|
||||
cx.new(|cx| ConfigurationView::new(self.state.clone(), window, cx))
|
||||
fn configuration_view(
|
||||
&self,
|
||||
target_agent: ConfigurationViewTargetAgent,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
cx.new(|cx| ConfigurationView::new(self.state.clone(), target_agent, window, cx))
|
||||
.into()
|
||||
}
|
||||
|
||||
|
@ -902,12 +924,18 @@ struct ConfigurationView {
|
|||
api_key_editor: Entity<Editor>,
|
||||
state: gpui::Entity<State>,
|
||||
load_credentials_task: Option<Task<()>>,
|
||||
target_agent: ConfigurationViewTargetAgent,
|
||||
}
|
||||
|
||||
impl ConfigurationView {
|
||||
const PLACEHOLDER_TEXT: &'static str = "sk-ant-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
|
||||
|
||||
fn new(state: gpui::Entity<State>, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
fn new(
|
||||
state: gpui::Entity<State>,
|
||||
target_agent: ConfigurationViewTargetAgent,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
cx.observe(&state, |_, _, cx| {
|
||||
cx.notify();
|
||||
})
|
||||
|
@ -939,6 +967,7 @@ impl ConfigurationView {
|
|||
}),
|
||||
state,
|
||||
load_credentials_task,
|
||||
target_agent,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1012,7 +1041,10 @@ impl Render for ConfigurationView {
|
|||
v_flex()
|
||||
.size_full()
|
||||
.on_action(cx.listener(Self::save_api_key))
|
||||
.child(Label::new("To use Zed's agent with Anthropic, you need to add an API key. Follow these steps:"))
|
||||
.child(Label::new(format!("To use {}, you need to add an API key. Follow these steps:", match self.target_agent {
|
||||
ConfigurationViewTargetAgent::ZedAgent => "Zed's agent with Anthropic",
|
||||
ConfigurationViewTargetAgent::Other(agent) => agent,
|
||||
})))
|
||||
.child(
|
||||
List::new()
|
||||
.child(
|
||||
|
@ -1023,7 +1055,7 @@ impl Render for ConfigurationView {
|
|||
)
|
||||
)
|
||||
.child(
|
||||
InstructionListItem::text_only("Paste your API key below and hit enter to start using the assistant")
|
||||
InstructionListItem::text_only("Paste your API key below and hit enter to start using the agent")
|
||||
)
|
||||
)
|
||||
.child(
|
||||
|
|
|
@ -348,7 +348,12 @@ impl LanguageModelProvider for BedrockLanguageModelProvider {
|
|||
self.state.update(cx, |state, cx| state.authenticate(cx))
|
||||
}
|
||||
|
||||
fn configuration_view(&self, window: &mut Window, cx: &mut App) -> AnyView {
|
||||
fn configuration_view(
|
||||
&self,
|
||||
_target_agent: language_model::ConfigurationViewTargetAgent,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
cx.new(|cx| ConfigurationView::new(self.state.clone(), window, cx))
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -391,7 +391,12 @@ impl LanguageModelProvider for CloudLanguageModelProvider {
|
|||
Task::ready(Ok(()))
|
||||
}
|
||||
|
||||
fn configuration_view(&self, _: &mut Window, cx: &mut App) -> AnyView {
|
||||
fn configuration_view(
|
||||
&self,
|
||||
_target_agent: language_model::ConfigurationViewTargetAgent,
|
||||
_: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
cx.new(|_| ConfigurationView::new(self.state.clone()))
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -176,7 +176,12 @@ impl LanguageModelProvider for CopilotChatLanguageModelProvider {
|
|||
Task::ready(Err(err.into()))
|
||||
}
|
||||
|
||||
fn configuration_view(&self, _: &mut Window, cx: &mut App) -> AnyView {
|
||||
fn configuration_view(
|
||||
&self,
|
||||
_target_agent: language_model::ConfigurationViewTargetAgent,
|
||||
_: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
let state = self.state.clone();
|
||||
cx.new(|cx| ConfigurationView::new(state, cx)).into()
|
||||
}
|
||||
|
|
|
@ -229,7 +229,12 @@ impl LanguageModelProvider for DeepSeekLanguageModelProvider {
|
|||
self.state.update(cx, |state, cx| state.authenticate(cx))
|
||||
}
|
||||
|
||||
fn configuration_view(&self, window: &mut Window, cx: &mut App) -> AnyView {
|
||||
fn configuration_view(
|
||||
&self,
|
||||
_target_agent: language_model::ConfigurationViewTargetAgent,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
cx.new(|cx| ConfigurationView::new(self.state.clone(), window, cx))
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -277,7 +277,12 @@ impl LanguageModelProvider for GoogleLanguageModelProvider {
|
|||
self.state.update(cx, |state, cx| state.authenticate(cx))
|
||||
}
|
||||
|
||||
fn configuration_view(&self, window: &mut Window, cx: &mut App) -> AnyView {
|
||||
fn configuration_view(
|
||||
&self,
|
||||
_target_agent: language_model::ConfigurationViewTargetAgent,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
cx.new(|cx| ConfigurationView::new(self.state.clone(), window, cx))
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -226,7 +226,12 @@ impl LanguageModelProvider for LmStudioLanguageModelProvider {
|
|||
self.state.update(cx, |state, cx| state.authenticate(cx))
|
||||
}
|
||||
|
||||
fn configuration_view(&self, _window: &mut Window, cx: &mut App) -> AnyView {
|
||||
fn configuration_view(
|
||||
&self,
|
||||
_target_agent: language_model::ConfigurationViewTargetAgent,
|
||||
_window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
let state = self.state.clone();
|
||||
cx.new(|cx| ConfigurationView::new(state, cx)).into()
|
||||
}
|
||||
|
|
|
@ -243,7 +243,12 @@ impl LanguageModelProvider for MistralLanguageModelProvider {
|
|||
self.state.update(cx, |state, cx| state.authenticate(cx))
|
||||
}
|
||||
|
||||
fn configuration_view(&self, window: &mut Window, cx: &mut App) -> AnyView {
|
||||
fn configuration_view(
|
||||
&self,
|
||||
_target_agent: language_model::ConfigurationViewTargetAgent,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
cx.new(|cx| ConfigurationView::new(self.state.clone(), window, cx))
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -255,7 +255,12 @@ impl LanguageModelProvider for OllamaLanguageModelProvider {
|
|||
self.state.update(cx, |state, cx| state.authenticate(cx))
|
||||
}
|
||||
|
||||
fn configuration_view(&self, window: &mut Window, cx: &mut App) -> AnyView {
|
||||
fn configuration_view(
|
||||
&self,
|
||||
_target_agent: language_model::ConfigurationViewTargetAgent,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
let state = self.state.clone();
|
||||
cx.new(|cx| ConfigurationView::new(state, window, cx))
|
||||
.into()
|
||||
|
|
|
@ -233,7 +233,12 @@ impl LanguageModelProvider for OpenAiLanguageModelProvider {
|
|||
self.state.update(cx, |state, cx| state.authenticate(cx))
|
||||
}
|
||||
|
||||
fn configuration_view(&self, window: &mut Window, cx: &mut App) -> AnyView {
|
||||
fn configuration_view(
|
||||
&self,
|
||||
_target_agent: language_model::ConfigurationViewTargetAgent,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
cx.new(|cx| ConfigurationView::new(self.state.clone(), window, cx))
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -243,7 +243,12 @@ impl LanguageModelProvider for OpenAiCompatibleLanguageModelProvider {
|
|||
self.state.update(cx, |state, cx| state.authenticate(cx))
|
||||
}
|
||||
|
||||
fn configuration_view(&self, window: &mut Window, cx: &mut App) -> AnyView {
|
||||
fn configuration_view(
|
||||
&self,
|
||||
_target_agent: language_model::ConfigurationViewTargetAgent,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
cx.new(|cx| ConfigurationView::new(self.state.clone(), window, cx))
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -306,7 +306,12 @@ impl LanguageModelProvider for OpenRouterLanguageModelProvider {
|
|||
self.state.update(cx, |state, cx| state.authenticate(cx))
|
||||
}
|
||||
|
||||
fn configuration_view(&self, window: &mut Window, cx: &mut App) -> AnyView {
|
||||
fn configuration_view(
|
||||
&self,
|
||||
_target_agent: language_model::ConfigurationViewTargetAgent,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
cx.new(|cx| ConfigurationView::new(self.state.clone(), window, cx))
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -230,7 +230,12 @@ impl LanguageModelProvider for VercelLanguageModelProvider {
|
|||
self.state.update(cx, |state, cx| state.authenticate(cx))
|
||||
}
|
||||
|
||||
fn configuration_view(&self, window: &mut Window, cx: &mut App) -> AnyView {
|
||||
fn configuration_view(
|
||||
&self,
|
||||
_target_agent: language_model::ConfigurationViewTargetAgent,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
cx.new(|cx| ConfigurationView::new(self.state.clone(), window, cx))
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -230,7 +230,12 @@ impl LanguageModelProvider for XAiLanguageModelProvider {
|
|||
self.state.update(cx, |state, cx| state.authenticate(cx))
|
||||
}
|
||||
|
||||
fn configuration_view(&self, window: &mut Window, cx: &mut App) -> AnyView {
|
||||
fn configuration_view(
|
||||
&self,
|
||||
_target_agent: language_model::ConfigurationViewTargetAgent,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> AnyView {
|
||||
cx.new(|cx| ConfigurationView::new(self.state.clone(), window, cx))
|
||||
.into()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue