acp: Handle Gemini Auth Better (#36631)

Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
This commit is contained in:
Conrad Irwin 2025-08-20 16:12:41 -06:00 committed by GitHub
parent c9c708ff08
commit 5120b6b7f9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 195 additions and 19 deletions

View file

@ -12,9 +12,9 @@ use gpui::{
};
use http_client::HttpClient;
use language_model::{
AuthenticateError, LanguageModelCompletionError, LanguageModelCompletionEvent,
LanguageModelToolChoice, LanguageModelToolSchemaFormat, LanguageModelToolUse,
LanguageModelToolUseId, MessageContent, StopReason,
AuthenticateError, ConfigurationViewTargetAgent, LanguageModelCompletionError,
LanguageModelCompletionEvent, LanguageModelToolChoice, LanguageModelToolSchemaFormat,
LanguageModelToolUse, LanguageModelToolUseId, MessageContent, StopReason,
};
use language_model::{
LanguageModel, LanguageModelId, LanguageModelName, LanguageModelProvider,
@ -37,6 +37,8 @@ use util::ResultExt;
use crate::AllLanguageModelSettings;
use crate::ui::InstructionListItem;
use super::anthropic::ApiKey;
const PROVIDER_ID: LanguageModelProviderId = language_model::GOOGLE_PROVIDER_ID;
const PROVIDER_NAME: LanguageModelProviderName = language_model::GOOGLE_PROVIDER_NAME;
@ -198,6 +200,33 @@ impl GoogleLanguageModelProvider {
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)
.google
.api_url
.clone();
if let Ok(key) = std::env::var(GEMINI_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 GoogleLanguageModelProvider {
@ -279,11 +308,11 @@ impl LanguageModelProvider for GoogleLanguageModelProvider {
fn configuration_view(
&self,
_target_agent: language_model::ConfigurationViewTargetAgent,
target_agent: language_model::ConfigurationViewTargetAgent,
window: &mut Window,
cx: &mut App,
) -> AnyView {
cx.new(|cx| ConfigurationView::new(self.state.clone(), window, cx))
cx.new(|cx| ConfigurationView::new(self.state.clone(), target_agent, window, cx))
.into()
}
@ -776,11 +805,17 @@ fn convert_usage(usage: &UsageMetadata) -> language_model::TokenUsage {
struct ConfigurationView {
api_key_editor: Entity<Editor>,
state: gpui::Entity<State>,
target_agent: language_model::ConfigurationViewTargetAgent,
load_credentials_task: Option<Task<()>>,
}
impl ConfigurationView {
fn new(state: gpui::Entity<State>, window: &mut Window, cx: &mut Context<Self>) -> Self {
fn new(
state: gpui::Entity<State>,
target_agent: language_model::ConfigurationViewTargetAgent,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
cx.observe(&state, |_, _, cx| {
cx.notify();
})
@ -810,6 +845,7 @@ impl ConfigurationView {
editor.set_placeholder_text("AIzaSy...", cx);
editor
}),
target_agent,
state,
load_credentials_task,
}
@ -885,7 +921,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 Google AI, 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 Google AI",
ConfigurationViewTargetAgent::Other(agent) => agent,
})))
.child(
List::new()
.child(InstructionListItem::new(