Remove Grok OAuth and legacy BYOK support

This commit is contained in:
Ryan Ward
2026-07-28 02:30:22 -05:00
parent 2faeed7ac5
commit 87e0c83e9e
26 changed files with 153 additions and 4140 deletions
+4 -11
View File
@@ -112,7 +112,8 @@ pub struct RequestParams {
pub planning_enabled: bool,
should_redact_secrets: bool,
/// User-provided API keys for AI providers (BYO API Key).
/// User-provided API keys for AI providers, currently AWS Bedrock credentials
/// and/or Gemini Enterprise (GEAP) credentials.
pub api_keys: Option<warp_multi_agent_api::request::settings::ApiKeys>,
/// User-provided custom model providers (BYOK endpoints).
pub custom_model_providers:
@@ -121,7 +122,6 @@ pub struct RequestParams {
/// `custom_model_providers`: the selected model's `config_key` indexes into this
/// registry. `None` when no custom router is selected.
pub custom_model_routers: Option<warp_multi_agent_api::request::settings::CustomModelRouters>,
pub allow_use_of_warp_credits: bool,
pub autonomy_level: warp_multi_agent_api::AutonomyLevel,
pub isolation_level: warp_multi_agent_api::IsolationLevel,
pub web_search_enabled: bool,
@@ -203,7 +203,6 @@ impl RequestParams {
api_keys: None,
custom_model_providers: None,
custom_model_routers: None,
allow_use_of_warp_credits: false,
autonomy_level: Default::default(),
isolation_level: Default::default(),
web_search_enabled: false,
@@ -305,16 +304,12 @@ impl RequestParams {
let user_workspaces = UserWorkspaces::as_ref(app);
let api_key_manager = ApiKeyManager::as_ref(app);
let is_byo_enabled = user_workspaces.is_byo_api_key_enabled(app);
#[cfg(not(target_family = "wasm"))]
let geap_binding = crate::ai::geap_credentials::current_geap_policy(app).mint_binding();
#[cfg(target_family = "wasm")]
let geap_binding: Option<::ai::api_keys::GeapMintBinding> = None;
let api_keys = api_key_manager.api_keys_for_request(
is_byo_enabled,
user_workspaces.is_bedrock_enabled(app),
geap_binding,
);
let api_keys = api_key_manager
.api_keys_for_request(user_workspaces.is_bedrock_enabled(app), geap_binding);
let custom_model_providers = None;
let custom_model_routers = FeatureFlag::CustomModelRouters.is_enabled().then(|| {
LLMPreferences::as_ref(app).custom_model_routers_for_request(
@@ -322,7 +317,6 @@ impl RequestParams {
&request_input.coding_model_id,
)
});
let allow_use_of_warp_credits = *AISettings::as_ref(app).can_use_warp_credits_for_fallback;
let app_execution_mode = AppExecutionMode::as_ref(app);
let autonomy_level = if app_execution_mode.is_autonomous() {
@@ -399,7 +393,6 @@ impl RequestParams {
api_keys,
custom_model_providers,
custom_model_routers,
allow_use_of_warp_credits,
autonomy_level,
isolation_level,
web_search_enabled,
+1 -23
View File
@@ -57,11 +57,6 @@ pub async fn generate_multi_agent_output(
redaction::redact_inputs(&mut params.input);
}
let api_keys = api_keys_with_warp_credit_fallback_setting(
params.api_keys,
params.allow_use_of_warp_credits,
);
let mut request = api::Request {
task_context: Some(api::request::TaskContext {
tasks: params.tasks,
@@ -91,7 +86,7 @@ pub async fn generate_multi_agent_output(
supports_suggest_prompt: true,
supports_read_image_files: FeatureFlag::ReadImageFiles.is_enabled(),
supports_reasoning_message: true,
api_keys,
api_keys: params.api_keys,
autonomy_level: params.autonomy_level.into(),
isolation_level: params.isolation_level.into(),
web_search_enabled: params.web_search_enabled,
@@ -214,23 +209,6 @@ pub async fn generate_multi_agent_output(
}
}
fn api_keys_with_warp_credit_fallback_setting(
api_keys: Option<api::request::settings::ApiKeys>,
allow_use_of_warp_credits: bool,
) -> Option<api::request::settings::ApiKeys> {
match api_keys {
Some(mut api_keys) => {
api_keys.allow_use_of_warp_credits = allow_use_of_warp_credits;
Some(api_keys)
}
None if allow_use_of_warp_credits => Some(api::request::settings::ApiKeys {
allow_use_of_warp_credits: true,
..Default::default()
}),
None => None,
}
}
fn supports_orchestration_v2(orchestration_enabled: bool) -> bool {
orchestration_enabled
}
+1 -46
View File
@@ -2,10 +2,7 @@ use galaxy_core::features::FeatureFlag;
use galaxy_core::HostId;
use warp_multi_agent_api as api;
use super::{
api_keys_with_warp_credit_fallback_setting, get_supported_cli_agent_tools, get_supported_tools,
supports_orchestration_v2,
};
use super::{get_supported_cli_agent_tools, get_supported_tools, supports_orchestration_v2};
use crate::ai::agent::api::RequestParams;
use crate::ai::blocklist::SessionContext;
use crate::ai::llms::LLMId;
@@ -36,7 +33,6 @@ fn request_params_with_ask_user_question_enabled(ask_user_question_enabled: bool
api_keys: None,
custom_model_providers: None,
custom_model_routers: None,
allow_use_of_warp_credits: false,
autonomy_level: api::AutonomyLevel::Supervised,
isolation_level: api::IsolationLevel::None,
web_search_enabled: false,
@@ -64,47 +60,6 @@ fn request_params_for_remote(host_id: Option<HostId>) -> RequestParams {
params
}
#[test]
fn api_keys_with_warp_credit_fallback_setting_returns_none_without_keys_or_fallback() {
let api_keys = api_keys_with_warp_credit_fallback_setting(None, false);
assert!(api_keys.is_none());
}
#[test]
fn api_keys_with_warp_credit_fallback_setting_creates_fallback_only_api_keys() {
let api_keys = api_keys_with_warp_credit_fallback_setting(None, true)
.expect("fallback setting should create ApiKeys");
assert!(api_keys.allow_use_of_warp_credits);
assert!(api_keys.anthropic.is_empty());
assert!(api_keys.openai.is_empty());
assert!(api_keys.google.is_empty());
assert!(api_keys.open_router.is_empty());
assert!(api_keys.aws_credentials.is_none());
}
#[test]
fn api_keys_with_warp_credit_fallback_setting_preserves_existing_keys() {
let api_keys = api_keys_with_warp_credit_fallback_setting(
Some(api::request::settings::ApiKeys {
anthropic: "anthropic-key".to_string(),
openai: String::new(),
google: String::new(),
open_router: String::new(),
grok_oauth_access_token: String::new(),
allow_use_of_warp_credits: false,
aws_credentials: None,
google_cloud_credentials: None,
}),
true,
)
.expect("existing ApiKeys should be preserved");
assert_eq!(api_keys.anthropic, "anthropic-key");
assert!(api_keys.allow_use_of_warp_credits);
}
#[test]
fn supports_orchestration_v2_matches_request_orchestration_setting() {
assert!(supports_orchestration_v2(true));
+2 -259
View File
@@ -1,23 +1,14 @@
use std::collections::HashMap;
use ai::api_keys::ApiKeyManager;
use galaxy_core::features::FeatureFlag;
use warp_multi_agent_api as api;
use warpui::{App, SingletonEntity};
use super::{
artifact_from_fork_proto, footer_model_token_usage, AIConversation,
AIConversationAutoexecuteMode, AIConversationId, ConversationStatus, RestoreConversationError,
artifact_from_fork_proto, AIConversation, AIConversationAutoexecuteMode, AIConversationId,
ConversationStatus, RestoreConversationError,
};
use crate::ai::artifacts::Artifact;
use crate::ai::llms::LLMPreferences;
use crate::auth::auth_manager::AuthManager;
use crate::auth::AuthStateProvider;
use crate::network::NetworkStatus;
use crate::persistence::model::AgentConversationData;
use crate::server::server_api::ServerApiProvider;
use crate::test_util::settings::initialize_settings_for_tests;
use crate::workspaces::user_workspaces::UserWorkspaces;
fn restored_conversation(conversation_data: Option<AgentConversationData>) -> AIConversation {
AIConversation::new_restored(
@@ -115,43 +106,6 @@ fn restored_conversation_with_queries(queries: &[&str]) -> AIConversation {
.unwrap()
}
fn initialize_custom_endpoint_usage_test_app(app: &mut App) {
initialize_settings_for_tests(app);
app.add_singleton_model(|_| ServerApiProvider::new_for_test());
app.add_singleton_model(|_| NetworkStatus::new());
app.add_singleton_model(UserWorkspaces::default_mock);
app.add_singleton_model(|_| AuthStateProvider::new_for_test());
app.add_singleton_model(AuthManager::new_for_test);
}
#[allow(deprecated)]
fn custom_endpoint_usage_metadata(
config_key: &str,
total_tokens: u32,
) -> api::response_event::stream_finished::ConversationUsageMetadata {
let category = "primary_agent".to_string();
api::response_event::stream_finished::ConversationUsageMetadata {
context_window_usage: 0.0,
credits_spent: 0.0,
platform_credits_spent: 0.0,
summarized: false,
token_usage: vec![],
tool_usage_metadata: None,
total_input_tokens: 0,
warp_token_usage: HashMap::new(),
byok_token_usage: HashMap::new(),
context_window_segments: Vec::new(),
custom_endpoint_token_usage: HashMap::from([(
config_key.to_string(),
api::response_event::stream_finished::ModelTokenUsage {
model_id: config_key.to_string(),
total_tokens,
token_usage_by_category: HashMap::from([(category, total_tokens)]),
},
)]),
}
}
#[test]
fn latest_user_query_returns_latest_non_empty_user_query() {
let conversation =
@@ -291,217 +245,6 @@ fn restored_conversation_with_empty_task_list_creates_in_progress_optimistic_roo
assert!(conversation.status_error_message().is_none());
}
#[test]
fn update_cost_and_usage_resolves_custom_endpoint_alias_for_footer_usage() {
App::test((), |mut app| async move {
initialize_custom_endpoint_usage_test_app(&mut app);
ApiKeyManager::handle(&app).update(&mut app, |manager, ctx| {
manager.add_custom_endpoint(
"Endpoint".to_string(),
"https://custom.example".to_string(),
"key".to_string(),
vec![(
"raw-model".to_string(),
Some("Friendly alias".to_string()),
Some("config-key".to_string()),
)],
ctx,
);
});
app.add_singleton_model(LLMPreferences::new);
let mut conversation = AIConversation::new(false, false);
app.read(|ctx| {
conversation
.update_cost_and_usage_for_request(
None,
vec![],
Some(custom_endpoint_usage_metadata("config-key", 6)),
false,
ctx,
)
.expect("custom endpoint usage should update");
});
let usage = conversation
.token_usage()
.iter()
.find(|usage| usage.model_id == "Friendly alias")
.expect("custom endpoint alias should resolve into footer usage");
assert_eq!(usage.custom_endpoint_tokens, 6);
assert_eq!(usage.byok_tokens, 0);
assert_eq!(
usage
.custom_endpoint_token_usage_by_category
.get("primary_agent"),
Some(&6)
);
});
}
#[test]
fn update_cost_and_usage_uses_fallback_label_for_unknown_custom_endpoint() {
App::test((), |mut app| async move {
initialize_custom_endpoint_usage_test_app(&mut app);
app.add_singleton_model(LLMPreferences::new);
let mut conversation = AIConversation::new(false, false);
app.read(|ctx| {
conversation
.update_cost_and_usage_for_request(
None,
vec![],
Some(custom_endpoint_usage_metadata("missing-config-key", 9)),
false,
ctx,
)
.expect("fallback custom endpoint usage should update");
});
let usage = conversation
.token_usage()
.iter()
.find(|usage| usage.model_id == "Custom endpoint")
.expect("unknown custom endpoint usage should use the fallback label");
assert_eq!(usage.custom_endpoint_tokens, 9);
assert_eq!(usage.byok_tokens, 0);
assert_eq!(
usage
.custom_endpoint_token_usage_by_category
.get("primary_agent"),
Some(&9)
);
});
}
#[allow(deprecated)]
#[test]
fn footer_model_token_usage_keeps_custom_endpoint_usage_distinct_from_same_labeled_models() {
App::test((), |mut app| async move {
initialize_custom_endpoint_usage_test_app(&mut app);
ApiKeyManager::handle(&app).update(&mut app, |manager, ctx| {
manager.add_custom_endpoint(
"Endpoint".to_string(),
"https://custom.example".to_string(),
"key".to_string(),
vec![(
"raw-model".to_string(),
Some("Resolved custom".to_string()),
Some("config-key".to_string()),
)],
ctx,
);
});
app.add_singleton_model(LLMPreferences::new);
let category = "primary_agent".to_string();
let usage_metadata = api::response_event::stream_finished::ConversationUsageMetadata {
context_window_usage: 0.0,
credits_spent: 0.0,
platform_credits_spent: 0.0,
summarized: false,
#[allow(deprecated)]
token_usage: vec![],
tool_usage_metadata: None,
total_input_tokens: 0,
warp_token_usage: HashMap::new(),
byok_token_usage: HashMap::from([(
"Resolved custom".to_string(),
api::response_event::stream_finished::ModelTokenUsage {
model_id: "Resolved custom".to_string(),
total_tokens: 4,
token_usage_by_category: HashMap::from([(category.clone(), 4)]),
},
)]),
custom_endpoint_token_usage: HashMap::from([(
"config-key".to_string(),
api::response_event::stream_finished::ModelTokenUsage {
model_id: "config-key".to_string(),
total_tokens: 6,
token_usage_by_category: HashMap::from([(category.clone(), 6)]),
},
)]),
context_window_segments: Vec::new(),
};
let model_usage =
app.read(|ctx| footer_model_token_usage(&usage_metadata, LLMPreferences::as_ref(ctx)));
let byok_usage = model_usage
.iter()
.find(|usage| usage.model_id == "Resolved custom" && usage.byok_tokens == 4)
.expect("existing model usage should be present");
let custom_usage = model_usage
.iter()
.find(|usage| usage.model_id == "Resolved custom" && usage.custom_endpoint_tokens == 6)
.expect("custom endpoint usage should remain distinct");
assert_eq!(model_usage.len(), 2);
assert_eq!(
byok_usage.byok_token_usage_by_category.get(&category),
Some(&4)
);
assert_eq!(
custom_usage
.custom_endpoint_token_usage_by_category
.get(&category),
Some(&6)
);
assert_eq!(byok_usage.warp_tokens, 0);
assert_eq!(custom_usage.warp_tokens, 0);
assert_eq!(custom_usage.byok_tokens, 0);
});
}
#[allow(deprecated)]
#[test]
fn footer_model_token_usage_preserves_unresolved_custom_endpoint_usage_with_fallback_label() {
App::test((), |mut app| async move {
initialize_custom_endpoint_usage_test_app(&mut app);
app.add_singleton_model(LLMPreferences::new);
let category = "primary_agent".to_string();
let usage_metadata = api::response_event::stream_finished::ConversationUsageMetadata {
context_window_usage: 0.0,
credits_spent: 0.0,
platform_credits_spent: 0.0,
summarized: false,
#[allow(deprecated)]
token_usage: vec![],
tool_usage_metadata: None,
total_input_tokens: 0,
warp_token_usage: HashMap::new(),
byok_token_usage: HashMap::new(),
custom_endpoint_token_usage: HashMap::from([(
"missing-config-key".to_string(),
api::response_event::stream_finished::ModelTokenUsage {
model_id: "missing-config-key".to_string(),
total_tokens: 9,
token_usage_by_category: HashMap::from([(category.clone(), 9)]),
},
)]),
context_window_segments: Vec::new(),
};
let model_usage =
app.read(|ctx| footer_model_token_usage(&usage_metadata, LLMPreferences::as_ref(ctx)));
let custom_usage = model_usage
.iter()
.find(|usage| usage.model_id == "Custom endpoint")
.expect("fallback custom endpoint usage should be present");
assert_eq!(model_usage.len(), 1);
assert_eq!(custom_usage.custom_endpoint_tokens, 9);
assert_eq!(custom_usage.byok_tokens, 0);
assert_eq!(
custom_usage
.custom_endpoint_token_usage_by_category
.get(&category),
Some(&9)
);
assert_eq!(custom_usage.warp_tokens, 0);
});
}
/// The legacy `AgentConversationData.root_task_is_optimistic` flag must be
/// ignored on restore. A non-empty task list always produces a real
/// server-backed root regardless of whether the flag is set.