Add Rig native model providers
This commit is contained in:
@@ -7564,13 +7564,13 @@ impl SettingsWidget for ModelsOverviewWidget {
|
||||
.with_spacing(8.)
|
||||
.with_child(build_sub_header(appearance, "Models", None).finish())
|
||||
.with_child(render_ai_setting_description(
|
||||
"Configure Galaxy's direct model providers and agent runtimes in one place. OpenAI-compatible endpoints and Bedrock models run through Rig. ACP coding agents use the same Galaxy runtime boundary while retaining their own model, login, session, and tool loop.",
|
||||
"Configure Galaxy's direct model providers and agent runtimes in one place. OpenAI-compatible, Anthropic, Gemini, Vertex AI, and Bedrock models run through Rig. ACP coding agents use the same Galaxy runtime boundary while retaining their own model, login, session, and tool loop.",
|
||||
true,
|
||||
app,
|
||||
))
|
||||
.with_child(render_ai_setting_description(
|
||||
format!(
|
||||
"{endpoint_count} OpenAI-compatible provider(s) with {endpoint_model_count} model(s); {bedrock_model_count} Bedrock model(s); {agent_runtime_count} enabled agent runtime(s)."
|
||||
"{endpoint_count} configured provider(s) with {endpoint_model_count} model(s); {bedrock_model_count} Bedrock model(s); {agent_runtime_count} enabled agent runtime(s)."
|
||||
),
|
||||
true,
|
||||
app,
|
||||
@@ -7680,6 +7680,9 @@ impl OpenAIProviderSettingsWidget {
|
||||
match provider.kind {
|
||||
OpenAIProviderKind::OpenAICompatible => "OpenAI-compatible API",
|
||||
OpenAIProviderKind::ChatGPTSubscription => "ChatGPT subscription",
|
||||
OpenAIProviderKind::Anthropic => "Anthropic",
|
||||
OpenAIProviderKind::Gemini => "Google Gemini",
|
||||
OpenAIProviderKind::VertexAI => "Google Vertex AI",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7905,33 +7908,37 @@ impl SettingsWidget for OpenAIProviderSettingsWidget {
|
||||
app,
|
||||
));
|
||||
column.add_child(render_ai_setting_description(
|
||||
"Connect a ChatGPT subscription, OpenAI-compatible endpoint, AWS Bedrock account, or ACP agent runtime. Each provider can be enabled independently.",
|
||||
"Connect a ChatGPT subscription, OpenAI-compatible endpoint, Anthropic, Gemini, Vertex AI, AWS Bedrock account, or ACP agent runtime. Each provider can be enabled independently.",
|
||||
true,
|
||||
app,
|
||||
));
|
||||
|
||||
column.add_child(self.render_builtin_provider_card(
|
||||
settings.bedrock_connection_name.value().as_str(),
|
||||
"Use AWS credentials to access Bedrock foundation models directly.",
|
||||
*settings.bedrock_enabled.value(),
|
||||
self.bedrock_enabled_toggle.clone(),
|
||||
AISettingsPageAction::ToggleBedrockEnabled,
|
||||
&self.bedrock_edit_button,
|
||||
&self.bedrock_remove_button,
|
||||
appearance,
|
||||
));
|
||||
if cfg!(unix) && FeatureFlag::AgentClientProtocol.is_enabled() {
|
||||
if !settings.bedrock_models.value().is_empty() {
|
||||
column.add_child(self.render_builtin_provider_card(
|
||||
settings.acp_connection_name.value().as_str(),
|
||||
"Use a local session-oriented agent that owns its model and authentication.",
|
||||
*settings.acp_enabled.value(),
|
||||
self.acp_enabled_toggle.clone(),
|
||||
AISettingsPageAction::ToggleAcpEnabled,
|
||||
&self.acp_edit_button,
|
||||
&self.acp_remove_button,
|
||||
settings.bedrock_connection_name.value().as_str(),
|
||||
"Use AWS credentials to access Bedrock foundation models directly.",
|
||||
*settings.bedrock_enabled.value(),
|
||||
self.bedrock_enabled_toggle.clone(),
|
||||
AISettingsPageAction::ToggleBedrockEnabled,
|
||||
&self.bedrock_edit_button,
|
||||
&self.bedrock_remove_button,
|
||||
appearance,
|
||||
));
|
||||
}
|
||||
if cfg!(unix) && FeatureFlag::AgentClientProtocol.is_enabled() {
|
||||
if *settings.acp_enabled.value() {
|
||||
column.add_child(self.render_builtin_provider_card(
|
||||
settings.acp_connection_name.value().as_str(),
|
||||
"Use a local session-oriented agent that owns its model and authentication.",
|
||||
*settings.acp_enabled.value(),
|
||||
self.acp_enabled_toggle.clone(),
|
||||
AISettingsPageAction::ToggleAcpEnabled,
|
||||
&self.acp_edit_button,
|
||||
&self.acp_remove_button,
|
||||
appearance,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if providers.is_empty() {
|
||||
column.add_child(render_ai_setting_description(
|
||||
|
||||
@@ -1167,9 +1167,6 @@ impl SettingsView {
|
||||
// Warp Drive page
|
||||
let warp_drive_page_handle =
|
||||
ctx.add_typed_action_view(warp_drive_page::WarpDriveSettingsPageView::new);
|
||||
ctx.subscribe_to_view(&warp_drive_page_handle, |me, _, event, ctx| {
|
||||
me.handle_warp_drive_page_event(event, ctx);
|
||||
});
|
||||
|
||||
let platform_page_handle = ctx.add_typed_action_view(platform_page::PlatformPageView::new);
|
||||
ctx.subscribe_to_view(&platform_page_handle, |me, _, event, ctx| {
|
||||
@@ -1738,18 +1735,6 @@ impl SettingsView {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_warp_drive_page_event(
|
||||
&mut self,
|
||||
event: &warp_drive_page::WarpDriveSettingsPageEvent,
|
||||
ctx: &mut ViewContext<Self>,
|
||||
) {
|
||||
match event {
|
||||
warp_drive_page::WarpDriveSettingsPageEvent::SignUp => {
|
||||
ctx.emit(SettingsViewEvent::SignupAnonymousUser)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_ai_page_event(&mut self, event: &AISettingsPageEvent, ctx: &mut ViewContext<Self>) {
|
||||
match event {
|
||||
AISettingsPageEvent::FocusModal => ctx.focus(&self.search_editor),
|
||||
|
||||
@@ -46,10 +46,51 @@ enum ProviderSetupStep {
|
||||
pub enum ProviderSetupProviderType {
|
||||
ChatGPTSubscription,
|
||||
OpenAICompatible,
|
||||
Anthropic,
|
||||
Gemini,
|
||||
VertexAI,
|
||||
Bedrock,
|
||||
Acp,
|
||||
}
|
||||
|
||||
const PROVIDER_TYPE_OPTIONS: &[(ProviderSetupProviderType, &str, &str)] = &[
|
||||
(
|
||||
ProviderSetupProviderType::ChatGPTSubscription,
|
||||
"ChatGPT subscription",
|
||||
"Use your ChatGPT Plus or Pro subscription with native OAuth.",
|
||||
),
|
||||
(
|
||||
ProviderSetupProviderType::OpenAICompatible,
|
||||
"OpenAI-compatible API",
|
||||
"Connect LiteLLM, Ollama, vLLM, or another compatible endpoint.",
|
||||
),
|
||||
(
|
||||
ProviderSetupProviderType::Anthropic,
|
||||
"Anthropic",
|
||||
"Connect directly to Anthropic's native Messages API with an API key.",
|
||||
),
|
||||
(
|
||||
ProviderSetupProviderType::Gemini,
|
||||
"Google Gemini",
|
||||
"Connect directly to Google's Gemini API with an API key.",
|
||||
),
|
||||
(
|
||||
ProviderSetupProviderType::VertexAI,
|
||||
"Google Vertex AI",
|
||||
"Use Google Cloud Application Default Credentials for Vertex-hosted Gemini models.",
|
||||
),
|
||||
(
|
||||
ProviderSetupProviderType::Bedrock,
|
||||
"AWS Bedrock",
|
||||
"Use the AWS Bedrock credentials and model configuration already managed by Galaxy.",
|
||||
),
|
||||
(
|
||||
ProviderSetupProviderType::Acp,
|
||||
"ACP agent runtime",
|
||||
"Use a session-oriented ACP agent that owns its model and authentication.",
|
||||
),
|
||||
];
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BedrockProviderDraft {
|
||||
pub name: String,
|
||||
@@ -114,6 +155,8 @@ pub struct ProviderSetupModalBody {
|
||||
draft_name: String,
|
||||
draft_base_url: String,
|
||||
draft_api_key: Option<String>,
|
||||
draft_project_id: String,
|
||||
draft_location: String,
|
||||
draft_models: Vec<OpenAIModelConfig>,
|
||||
draft_bedrock: BedrockProviderDraft,
|
||||
draft_acp: AcpProviderDraft,
|
||||
@@ -122,6 +165,8 @@ pub struct ProviderSetupModalBody {
|
||||
name_editor: ViewHandle<EditorView>,
|
||||
base_url_editor: ViewHandle<EditorView>,
|
||||
api_key_editor: ViewHandle<EditorView>,
|
||||
project_id_editor: ViewHandle<EditorView>,
|
||||
location_editor: ViewHandle<EditorView>,
|
||||
bedrock_profile_editor: ViewHandle<EditorView>,
|
||||
bedrock_region_editor: ViewHandle<EditorView>,
|
||||
bedrock_refresh_command_editor: ViewHandle<EditorView>,
|
||||
@@ -134,6 +179,7 @@ pub struct ProviderSetupModalBody {
|
||||
bedrock_cross_region_toggle: SwitchStateHandle,
|
||||
bedrock_auto_login_toggle: SwitchStateHandle,
|
||||
model_switches: Vec<SwitchStateHandle>,
|
||||
provider_type_scroll_state: ClippedScrollStateHandle,
|
||||
models_scroll_state: ClippedScrollStateHandle,
|
||||
back_button: ViewHandle<ActionButton>,
|
||||
cancel_button: ViewHandle<ActionButton>,
|
||||
@@ -142,35 +188,28 @@ pub struct ProviderSetupModalBody {
|
||||
|
||||
impl ProviderSetupModalBody {
|
||||
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
|
||||
let provider_type_buttons = [
|
||||
(
|
||||
ProviderSetupProviderType::ChatGPTSubscription,
|
||||
"ChatGPT subscription",
|
||||
),
|
||||
(
|
||||
ProviderSetupProviderType::OpenAICompatible,
|
||||
"OpenAI-compatible API",
|
||||
),
|
||||
(ProviderSetupProviderType::Bedrock, "AWS Bedrock"),
|
||||
(ProviderSetupProviderType::Acp, "ACP agent runtime"),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(kind, label)| {
|
||||
ctx.add_typed_action_view(move |_| {
|
||||
ActionButton::new(label, NakedTheme)
|
||||
.with_full_width(true)
|
||||
.on_click(move |ctx| {
|
||||
ctx.dispatch_typed_action(ProviderSetupModalBodyAction::SelectProvider(
|
||||
kind,
|
||||
));
|
||||
})
|
||||
let provider_type_buttons = PROVIDER_TYPE_OPTIONS
|
||||
.iter()
|
||||
.map(|(kind, label, _)| {
|
||||
let kind = *kind;
|
||||
let label = *label;
|
||||
ctx.add_typed_action_view(move |_| {
|
||||
ActionButton::new(label, NakedTheme)
|
||||
.with_full_width(true)
|
||||
.on_click(move |ctx| {
|
||||
ctx.dispatch_typed_action(
|
||||
ProviderSetupModalBodyAction::SelectProvider(kind),
|
||||
);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
.collect();
|
||||
|
||||
let name_editor = Self::create_editor("Connection name", false, ctx);
|
||||
let base_url_editor = Self::create_editor("https://api.example.com/v1", false, ctx);
|
||||
let api_key_editor = Self::create_editor("sk-... (optional)", true, ctx);
|
||||
let project_id_editor = Self::create_editor("my-google-cloud-project", false, ctx);
|
||||
let location_editor = Self::create_editor("global", false, ctx);
|
||||
let bedrock_profile_editor = Self::create_editor("default", false, ctx);
|
||||
let bedrock_region_editor = Self::create_editor("us-east-1", false, ctx);
|
||||
let bedrock_refresh_command_editor = Self::create_editor("aws sso login", false, ctx);
|
||||
@@ -218,6 +257,19 @@ impl ProviderSetupModalBody {
|
||||
ctx.notify();
|
||||
}
|
||||
});
|
||||
ctx.subscribe_to_view(&project_id_editor, |me, editor, event, ctx| {
|
||||
if matches!(event, EditorEvent::Edited(_)) {
|
||||
me.draft_project_id = editor.as_ref(ctx).buffer_text(ctx);
|
||||
me.update_next_button(ctx);
|
||||
ctx.notify();
|
||||
}
|
||||
});
|
||||
ctx.subscribe_to_view(&location_editor, |me, editor, event, ctx| {
|
||||
if matches!(event, EditorEvent::Edited(_)) {
|
||||
me.draft_location = editor.as_ref(ctx).buffer_text(ctx);
|
||||
ctx.notify();
|
||||
}
|
||||
});
|
||||
for (editor, update) in [
|
||||
(bedrock_profile_editor.clone(), 0),
|
||||
(bedrock_region_editor.clone(), 1),
|
||||
@@ -286,6 +338,8 @@ impl ProviderSetupModalBody {
|
||||
draft_name: String::new(),
|
||||
draft_base_url: String::new(),
|
||||
draft_api_key: None,
|
||||
draft_project_id: String::new(),
|
||||
draft_location: "global".to_string(),
|
||||
draft_models: Vec::new(),
|
||||
draft_bedrock: BedrockProviderDraft {
|
||||
name: String::new(),
|
||||
@@ -310,6 +364,8 @@ impl ProviderSetupModalBody {
|
||||
name_editor,
|
||||
base_url_editor,
|
||||
api_key_editor,
|
||||
project_id_editor,
|
||||
location_editor,
|
||||
bedrock_profile_editor,
|
||||
bedrock_region_editor,
|
||||
bedrock_refresh_command_editor,
|
||||
@@ -322,6 +378,7 @@ impl ProviderSetupModalBody {
|
||||
bedrock_cross_region_toggle: SwitchStateHandle::default(),
|
||||
bedrock_auto_login_toggle: SwitchStateHandle::default(),
|
||||
model_switches: Vec::new(),
|
||||
provider_type_scroll_state: ClippedScrollStateHandle::default(),
|
||||
models_scroll_state: ClippedScrollStateHandle::default(),
|
||||
back_button,
|
||||
cancel_button,
|
||||
@@ -363,6 +420,8 @@ impl ProviderSetupModalBody {
|
||||
self.draft_name.clear();
|
||||
self.draft_base_url.clear();
|
||||
self.draft_api_key = None;
|
||||
self.draft_project_id.clear();
|
||||
self.draft_location = "global".to_string();
|
||||
self.draft_models.clear();
|
||||
self.draft_bedrock = BedrockProviderDraft {
|
||||
name: String::new(),
|
||||
@@ -405,10 +464,15 @@ impl ProviderSetupModalBody {
|
||||
ProviderSetupProviderType::ChatGPTSubscription
|
||||
}
|
||||
OpenAIProviderKind::OpenAICompatible => ProviderSetupProviderType::OpenAICompatible,
|
||||
OpenAIProviderKind::Anthropic => ProviderSetupProviderType::Anthropic,
|
||||
OpenAIProviderKind::Gemini => ProviderSetupProviderType::Gemini,
|
||||
OpenAIProviderKind::VertexAI => ProviderSetupProviderType::VertexAI,
|
||||
};
|
||||
self.draft_name = provider.name;
|
||||
self.draft_base_url = provider.base_url;
|
||||
self.draft_api_key = provider.api_key;
|
||||
self.draft_project_id = provider.project_id.unwrap_or_default();
|
||||
self.draft_location = provider.location.unwrap_or_else(|| "global".to_string());
|
||||
self.draft_models = provider.models;
|
||||
self.discovery_state = DiscoveryState::Idle;
|
||||
self.sync_editors(ctx);
|
||||
@@ -483,6 +547,12 @@ impl ProviderSetupModalBody {
|
||||
self.api_key_editor.update(ctx, |editor, ctx| {
|
||||
editor.system_reset_buffer_text(self.draft_api_key.as_deref().unwrap_or_default(), ctx);
|
||||
});
|
||||
self.project_id_editor.update(ctx, |editor, ctx| {
|
||||
editor.system_reset_buffer_text(&self.draft_project_id, ctx);
|
||||
});
|
||||
self.location_editor.update(ctx, |editor, ctx| {
|
||||
editor.system_reset_buffer_text(&self.draft_location, ctx);
|
||||
});
|
||||
self.bedrock_profile_editor.update(ctx, |editor, ctx| {
|
||||
editor.system_reset_buffer_text(&self.draft_bedrock.profile, ctx);
|
||||
});
|
||||
@@ -513,15 +583,12 @@ impl ProviderSetupModalBody {
|
||||
}
|
||||
|
||||
fn sync_provider_type_buttons(&self, ctx: &mut ViewContext<Self>) {
|
||||
for (index, button) in self.provider_type_buttons.iter().enumerate() {
|
||||
let button_kind = match index {
|
||||
0 => ProviderSetupProviderType::ChatGPTSubscription,
|
||||
1 => ProviderSetupProviderType::OpenAICompatible,
|
||||
2 => ProviderSetupProviderType::Bedrock,
|
||||
_ => ProviderSetupProviderType::Acp,
|
||||
};
|
||||
for ((button_kind, _, _), button) in PROVIDER_TYPE_OPTIONS
|
||||
.iter()
|
||||
.zip(self.provider_type_buttons.iter())
|
||||
{
|
||||
button.update(ctx, |button, ctx| {
|
||||
button.set_active(button_kind == self.provider_type, ctx);
|
||||
button.set_active(*button_kind == self.provider_type, ctx);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -555,6 +622,12 @@ impl ProviderSetupModalBody {
|
||||
ProviderSetupProviderType::OpenAICompatible => {
|
||||
self.draft_base_url.trim().is_empty()
|
||||
}
|
||||
ProviderSetupProviderType::Anthropic | ProviderSetupProviderType::Gemini => {
|
||||
self.draft_api_key
|
||||
.as_deref()
|
||||
.is_none_or(|key| key.trim().is_empty())
|
||||
}
|
||||
ProviderSetupProviderType::VertexAI => self.draft_project_id.trim().is_empty(),
|
||||
ProviderSetupProviderType::Acp => self.draft_acp.agent_id.trim().is_empty(),
|
||||
ProviderSetupProviderType::ChatGPTSubscription
|
||||
| ProviderSetupProviderType::Bedrock => false,
|
||||
@@ -571,7 +644,10 @@ impl ProviderSetupModalBody {
|
||||
),
|
||||
ProviderSetupStep::Models => match self.provider_type {
|
||||
ProviderSetupProviderType::OpenAICompatible
|
||||
| ProviderSetupProviderType::ChatGPTSubscription => (
|
||||
| ProviderSetupProviderType::ChatGPTSubscription
|
||||
| ProviderSetupProviderType::Anthropic
|
||||
| ProviderSetupProviderType::Gemini
|
||||
| ProviderSetupProviderType::VertexAI => (
|
||||
"Save",
|
||||
self.draft_name.trim().is_empty()
|
||||
|| !self.draft_models.iter().any(|model| model.enabled),
|
||||
@@ -601,19 +677,48 @@ impl ProviderSetupModalBody {
|
||||
ProviderSetupProviderType::OpenAICompatible
|
||||
| ProviderSetupProviderType::Bedrock
|
||||
| ProviderSetupProviderType::Acp => OpenAIProviderKind::OpenAICompatible,
|
||||
ProviderSetupProviderType::Anthropic => OpenAIProviderKind::Anthropic,
|
||||
ProviderSetupProviderType::Gemini => OpenAIProviderKind::Gemini,
|
||||
ProviderSetupProviderType::VertexAI => OpenAIProviderKind::VertexAI,
|
||||
},
|
||||
enabled: true,
|
||||
name: self.draft_name.trim().to_string(),
|
||||
base_url: if self.provider_type == ProviderSetupProviderType::ChatGPTSubscription {
|
||||
base_url: if matches!(
|
||||
self.provider_type,
|
||||
ProviderSetupProviderType::ChatGPTSubscription
|
||||
| ProviderSetupProviderType::Anthropic
|
||||
| ProviderSetupProviderType::Gemini
|
||||
| ProviderSetupProviderType::VertexAI
|
||||
) {
|
||||
String::new()
|
||||
} else {
|
||||
self.draft_base_url.trim().trim_end_matches('/').to_string()
|
||||
},
|
||||
api_key: self
|
||||
.draft_api_key
|
||||
.as_deref()
|
||||
.filter(|key| !key.trim().is_empty())
|
||||
.map(str::to_string),
|
||||
api_key: matches!(
|
||||
self.provider_type,
|
||||
ProviderSetupProviderType::OpenAICompatible
|
||||
| ProviderSetupProviderType::Anthropic
|
||||
| ProviderSetupProviderType::Gemini
|
||||
)
|
||||
.then(|| {
|
||||
self.draft_api_key
|
||||
.as_deref()
|
||||
.filter(|key| !key.trim().is_empty())
|
||||
.map(str::to_string)
|
||||
})
|
||||
.flatten(),
|
||||
project_id: matches!(self.provider_type, ProviderSetupProviderType::VertexAI)
|
||||
.then(|| self.draft_project_id.trim().to_string()),
|
||||
location: matches!(self.provider_type, ProviderSetupProviderType::VertexAI).then(
|
||||
|| {
|
||||
let location = self.draft_location.trim();
|
||||
if location.is_empty() {
|
||||
"global".to_string()
|
||||
} else {
|
||||
location.to_string()
|
||||
}
|
||||
},
|
||||
),
|
||||
models: self.draft_models.clone(),
|
||||
}
|
||||
}
|
||||
@@ -668,7 +773,10 @@ impl ProviderSetupModalBody {
|
||||
));
|
||||
return;
|
||||
}
|
||||
ProviderSetupProviderType::OpenAICompatible => {}
|
||||
ProviderSetupProviderType::OpenAICompatible
|
||||
| ProviderSetupProviderType::Anthropic
|
||||
| ProviderSetupProviderType::Gemini
|
||||
| ProviderSetupProviderType::VertexAI => {}
|
||||
}
|
||||
|
||||
let provider = self.draft_provider();
|
||||
@@ -776,51 +884,53 @@ impl ProviderSetupModalBody {
|
||||
.with_color(appearance.theme().nonactive_ui_text_color().into())
|
||||
.finish();
|
||||
|
||||
let cards = [
|
||||
(
|
||||
"ChatGPT subscription",
|
||||
"Use your ChatGPT Plus or Pro subscription with native OAuth.",
|
||||
),
|
||||
(
|
||||
"OpenAI-compatible API",
|
||||
"Connect LiteLLM, Ollama, vLLM, or another compatible endpoint.",
|
||||
),
|
||||
(
|
||||
"AWS Bedrock",
|
||||
"Use the AWS Bedrock credentials and model configuration already managed by Galaxy.",
|
||||
),
|
||||
(
|
||||
"ACP agent runtime",
|
||||
"Use a session-oriented ACP agent that owns its model and authentication.",
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(index, (label, description))| {
|
||||
let button = ChildView::new(&self.provider_type_buttons[index]).finish();
|
||||
Container::new(
|
||||
Flex::column()
|
||||
.with_spacing(8.)
|
||||
.with_child(button)
|
||||
.with_child(
|
||||
Text::new(description, appearance.ui_font_family(), INPUT_FONT_SIZE)
|
||||
.with_color(appearance.theme().nonactive_ui_text_color().into())
|
||||
.soft_wrap(true)
|
||||
.finish(),
|
||||
)
|
||||
.finish(),
|
||||
)
|
||||
.with_padding(Padding::uniform(12.))
|
||||
.with_border(Border::all(1.).with_border_fill(appearance.theme().outline()))
|
||||
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(6.)))
|
||||
.finish()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let cards = PROVIDER_TYPE_OPTIONS
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, (_, _, description))| {
|
||||
let button = ChildView::new(&self.provider_type_buttons[index]).finish();
|
||||
Container::new(
|
||||
Flex::column()
|
||||
.with_spacing(8.)
|
||||
.with_child(button)
|
||||
.with_child(
|
||||
Text::new(*description, appearance.ui_font_family(), INPUT_FONT_SIZE)
|
||||
.with_color(appearance.theme().nonactive_ui_text_color().into())
|
||||
.soft_wrap(true)
|
||||
.finish(),
|
||||
)
|
||||
.finish(),
|
||||
)
|
||||
.with_padding(Padding::uniform(12.))
|
||||
.with_border(Border::all(1.).with_border_fill(appearance.theme().outline()))
|
||||
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(6.)))
|
||||
.finish()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let list = Flex::column()
|
||||
.with_spacing(10.)
|
||||
.with_children(cards)
|
||||
.finish();
|
||||
let scrollable = ClippedScrollable::vertical(
|
||||
self.provider_type_scroll_state.clone(),
|
||||
list,
|
||||
ScrollbarWidth::Auto,
|
||||
appearance.theme().nonactive_ui_detail().into(),
|
||||
appearance.theme().active_ui_detail().into(),
|
||||
appearance.theme().surface_1().into(),
|
||||
)
|
||||
.with_overlayed_scrollbar()
|
||||
.finish();
|
||||
|
||||
Flex::column()
|
||||
.with_spacing(16.)
|
||||
.with_child(description)
|
||||
.with_children(cards)
|
||||
.with_child(
|
||||
ConstrainedBox::new(scrollable)
|
||||
.with_max_height(360.)
|
||||
.finish(),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
|
||||
@@ -989,6 +1099,50 @@ impl ProviderSetupModalBody {
|
||||
.finish(),
|
||||
);
|
||||
}
|
||||
ProviderSetupProviderType::Anthropic => {
|
||||
children.push(self.render_input(appearance, "API key", &self.api_key_editor));
|
||||
children.push(
|
||||
Text::new(
|
||||
"The key is stored locally and is never synced to the cloud. Models will be discovered from Anthropic after the connection test.",
|
||||
appearance.ui_font_family(),
|
||||
INPUT_FONT_SIZE,
|
||||
)
|
||||
.with_color(appearance.theme().nonactive_ui_text_color().into())
|
||||
.soft_wrap(true)
|
||||
.finish(),
|
||||
);
|
||||
}
|
||||
ProviderSetupProviderType::Gemini => {
|
||||
children.push(self.render_input(appearance, "API key", &self.api_key_editor));
|
||||
children.push(
|
||||
Text::new(
|
||||
"The key is stored locally and is never synced to the cloud. Models will be discovered from Google's Gemini API after the connection test.",
|
||||
appearance.ui_font_family(),
|
||||
INPUT_FONT_SIZE,
|
||||
)
|
||||
.with_color(appearance.theme().nonactive_ui_text_color().into())
|
||||
.soft_wrap(true)
|
||||
.finish(),
|
||||
);
|
||||
}
|
||||
ProviderSetupProviderType::VertexAI => {
|
||||
children.push(self.render_input(
|
||||
appearance,
|
||||
"Google Cloud project ID",
|
||||
&self.project_id_editor,
|
||||
));
|
||||
children.push(self.render_input(appearance, "Location", &self.location_editor));
|
||||
children.push(
|
||||
Text::new(
|
||||
"Vertex AI uses Google Application Default Credentials. Run `gcloud auth application-default login` before testing the connection.",
|
||||
appearance.ui_font_family(),
|
||||
INPUT_FONT_SIZE,
|
||||
)
|
||||
.with_color(appearance.theme().nonactive_ui_text_color().into())
|
||||
.soft_wrap(true)
|
||||
.finish(),
|
||||
);
|
||||
}
|
||||
ProviderSetupProviderType::Bedrock => {
|
||||
children.push(Self::render_label(appearance, "Authentication method"));
|
||||
children.push(
|
||||
@@ -1422,7 +1576,10 @@ impl TypedActionView for ProviderSetupModalBody {
|
||||
}
|
||||
ProviderSetupStep::Models => match self.provider_type {
|
||||
ProviderSetupProviderType::OpenAICompatible
|
||||
| ProviderSetupProviderType::ChatGPTSubscription => {
|
||||
| ProviderSetupProviderType::ChatGPTSubscription
|
||||
| ProviderSetupProviderType::Anthropic
|
||||
| ProviderSetupProviderType::Gemini
|
||||
| ProviderSetupProviderType::VertexAI => {
|
||||
if self.draft_name.trim().is_empty()
|
||||
|| !self.draft_models.iter().any(|model| model.enabled)
|
||||
{
|
||||
@@ -1529,6 +1686,9 @@ fn provider_type_label(kind: ProviderSetupProviderType) -> &'static str {
|
||||
match kind {
|
||||
ProviderSetupProviderType::OpenAICompatible => "OpenAI-compatible API",
|
||||
ProviderSetupProviderType::ChatGPTSubscription => "ChatGPT subscription",
|
||||
ProviderSetupProviderType::Anthropic => "Anthropic",
|
||||
ProviderSetupProviderType::Gemini => "Google Gemini",
|
||||
ProviderSetupProviderType::VertexAI => "Google Vertex AI",
|
||||
ProviderSetupProviderType::Bedrock => "AWS Bedrock",
|
||||
ProviderSetupProviderType::Acp => "ACP agent runtime",
|
||||
}
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
use galaxy_core::features::FeatureFlag;
|
||||
use galaxy_core::report_if_error;
|
||||
use galaxy_core::settings::ToggleableSetting as _;
|
||||
use galaxyui::fonts::Weight;
|
||||
use galaxyui::keymap::ContextPredicate;
|
||||
use galaxyui::ui_components::button::ButtonVariant;
|
||||
use galaxyui::ui_components::components::{Coords, UiComponent, UiComponentStyles};
|
||||
use galaxyui::ui_components::components::UiComponent;
|
||||
use galaxyui::ui_components::switch::SwitchStateHandle;
|
||||
use galaxyui::{
|
||||
id, Action, AppContext, Entity, SingletonEntity, TypedActionView, View, ViewContext, ViewHandle,
|
||||
};
|
||||
use warpui::elements::{
|
||||
Container, Element, Flex, MouseStateHandle, ParentElement, Shrinkable, Text,
|
||||
};
|
||||
use warpui::elements::{Element, MouseStateHandle};
|
||||
|
||||
use super::settings_page::{
|
||||
render_body_item, AdditionalInfo, MatchData, PageType, SettingsPageMeta,
|
||||
@@ -22,13 +18,11 @@ use super::{
|
||||
SettingsAction, SettingsSection, ToggleSettingActionPair, ToggleState,
|
||||
};
|
||||
use crate::appearance::Appearance;
|
||||
use crate::auth::AuthStateProvider;
|
||||
use crate::drive::settings::WarpDriveSettings;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum WarpDriveSettingsPageAction {
|
||||
ToggleShowWarpDrive,
|
||||
SignUp,
|
||||
OpenUrl(String),
|
||||
}
|
||||
|
||||
@@ -44,8 +38,8 @@ pub fn init_actions_from_parent_view<T: Action + Clone>(
|
||||
WarpDriveSettingsPageAction::ToggleShowWarpDrive,
|
||||
)),
|
||||
SettingActionPairContexts::new(
|
||||
context.clone() & !id!(flags::ENABLE_WARP_DRIVE) & !id!("IsAnonymousUser"),
|
||||
context.clone() & id!(flags::ENABLE_WARP_DRIVE) & !id!("IsAnonymousUser"),
|
||||
context.clone() & !id!(flags::ENABLE_WARP_DRIVE),
|
||||
context.clone() & id!(flags::ENABLE_WARP_DRIVE),
|
||||
),
|
||||
None,
|
||||
)
|
||||
@@ -54,10 +48,6 @@ pub fn init_actions_from_parent_view<T: Action + Clone>(
|
||||
);
|
||||
}
|
||||
|
||||
pub enum WarpDriveSettingsPageEvent {
|
||||
SignUp,
|
||||
}
|
||||
|
||||
pub struct WarpDriveSettingsPageView {
|
||||
page: PageType<Self>,
|
||||
}
|
||||
@@ -66,10 +56,7 @@ impl WarpDriveSettingsPageView {
|
||||
pub fn new(_ctx: &mut ViewContext<Self>) -> Self {
|
||||
Self {
|
||||
page: PageType::new_uncategorized(
|
||||
vec![
|
||||
Box::new(WarpDriveHeaderWidget::default()),
|
||||
Box::new(WarpDriveToggleWidget::default()),
|
||||
],
|
||||
vec![Box::new(WarpDriveToggleWidget::default())],
|
||||
None,
|
||||
),
|
||||
}
|
||||
@@ -77,7 +64,7 @@ impl WarpDriveSettingsPageView {
|
||||
}
|
||||
|
||||
impl Entity for WarpDriveSettingsPageView {
|
||||
type Event = WarpDriveSettingsPageEvent;
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl TypedActionView for WarpDriveSettingsPageView {
|
||||
@@ -91,9 +78,6 @@ impl TypedActionView for WarpDriveSettingsPageView {
|
||||
});
|
||||
ctx.notify();
|
||||
}
|
||||
WarpDriveSettingsPageAction::SignUp => {
|
||||
ctx.emit(WarpDriveSettingsPageEvent::SignUp);
|
||||
}
|
||||
WarpDriveSettingsPageAction::OpenUrl(url) => {
|
||||
ctx.open_url(url.as_str());
|
||||
}
|
||||
@@ -139,88 +123,6 @@ impl From<ViewHandle<WarpDriveSettingsPageView>> for SettingsPageViewHandle {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct WarpDriveHeaderWidget {
|
||||
sign_up_button: MouseStateHandle,
|
||||
}
|
||||
|
||||
impl SettingsWidget for WarpDriveHeaderWidget {
|
||||
type View = WarpDriveSettingsPageView;
|
||||
|
||||
fn search_terms(&self) -> &str {
|
||||
"warp drive sign up"
|
||||
}
|
||||
|
||||
fn should_render(&self, app: &AppContext) -> bool {
|
||||
FeatureFlag::SkipFirebaseAnonymousUser.is_enabled()
|
||||
&& AuthStateProvider::as_ref(app)
|
||||
.get()
|
||||
.is_anonymous_or_logged_out()
|
||||
}
|
||||
|
||||
fn render(
|
||||
&self,
|
||||
_view: &Self::View,
|
||||
appearance: &Appearance,
|
||||
_app: &AppContext,
|
||||
) -> Box<dyn Element> {
|
||||
let ui_builder = appearance.ui_builder();
|
||||
|
||||
let message = Container::new(
|
||||
Text::new_inline(
|
||||
"To use Galaxy Drive, please create an account.".to_string(),
|
||||
appearance.ui_font_family(),
|
||||
14.,
|
||||
)
|
||||
.with_color(
|
||||
appearance
|
||||
.theme()
|
||||
.sub_text_color(appearance.theme().surface_2())
|
||||
.into_solid(),
|
||||
)
|
||||
.finish(),
|
||||
)
|
||||
.with_margin_right(16.)
|
||||
.finish();
|
||||
|
||||
let button = Container::new(
|
||||
ui_builder
|
||||
.button(ButtonVariant::Accent, self.sign_up_button.clone())
|
||||
.with_style(UiComponentStyles {
|
||||
font_size: Some(14.),
|
||||
font_weight: Some(Weight::Semibold),
|
||||
border_radius: Some(galaxyui::elements::CornerRadius::with_all(
|
||||
galaxyui::elements::Radius::Pixels(4.),
|
||||
)),
|
||||
padding: Some(Coords {
|
||||
top: 8.,
|
||||
bottom: 8.,
|
||||
left: 24.,
|
||||
right: 24.,
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
.with_text_label("Sign up".to_owned())
|
||||
.build()
|
||||
.on_click(move |ctx, _, _| {
|
||||
ctx.dispatch_typed_action(WarpDriveSettingsPageAction::SignUp);
|
||||
})
|
||||
.finish(),
|
||||
)
|
||||
.finish();
|
||||
|
||||
Container::new(
|
||||
Flex::row()
|
||||
.with_cross_axis_alignment(galaxyui::elements::CrossAxisAlignment::Center)
|
||||
.with_child(Shrinkable::new(1., message).finish())
|
||||
.with_child(button)
|
||||
.finish(),
|
||||
)
|
||||
.with_padding_bottom(15.)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct WarpDriveToggleWidget {
|
||||
switch_state: SwitchStateHandle,
|
||||
@@ -241,10 +143,6 @@ impl SettingsWidget for WarpDriveToggleWidget {
|
||||
app: &AppContext,
|
||||
) -> Box<dyn Element> {
|
||||
let settings = WarpDriveSettings::as_ref(app);
|
||||
let is_anonymous_or_logged_out = FeatureFlag::SkipFirebaseAnonymousUser.is_enabled()
|
||||
&& AuthStateProvider::as_ref(app)
|
||||
.get()
|
||||
.is_anonymous_or_logged_out();
|
||||
|
||||
render_body_item::<WarpDriveSettingsPageAction>(
|
||||
"Galaxy Drive".into(),
|
||||
@@ -257,24 +155,15 @@ impl SettingsWidget for WarpDriveToggleWidget {
|
||||
tooltip_override_text: None,
|
||||
}),
|
||||
LocalOnlyIconState::Hidden,
|
||||
if is_anonymous_or_logged_out {
|
||||
ToggleState::Disabled
|
||||
} else {
|
||||
ToggleState::Enabled
|
||||
},
|
||||
ToggleState::Enabled,
|
||||
appearance,
|
||||
appearance
|
||||
.ui_builder()
|
||||
.switch(self.switch_state.clone())
|
||||
.check(*settings.enable_warp_drive && !is_anonymous_or_logged_out)
|
||||
.with_disabled(is_anonymous_or_logged_out)
|
||||
.check(*settings.enable_warp_drive)
|
||||
.build()
|
||||
.on_click(move |ctx, _, _| {
|
||||
if !is_anonymous_or_logged_out {
|
||||
ctx.dispatch_typed_action(
|
||||
WarpDriveSettingsPageAction::ToggleShowWarpDrive,
|
||||
);
|
||||
}
|
||||
ctx.dispatch_typed_action(WarpDriveSettingsPageAction::ToggleShowWarpDrive);
|
||||
})
|
||||
.finish(),
|
||||
Some("Galaxy Drive is a workspace in your terminal where you can save Workflows, Notebooks, Prompts, and Environment Variables for personal use or to share with a team.".into()),
|
||||
|
||||
Reference in New Issue
Block a user