Integrate Bedrock model catalog metadata

This commit is contained in:
2026-08-22 12:51:31 -05:00
parent f291cfe803
commit f17642fc62
24 changed files with 27662 additions and 26 deletions
+1
View File
@@ -210,6 +210,7 @@ galaxy_completer.workspace = true
galaxy_core.workspace = true
galaxy_agent_core.workspace = true
galaxy_agent_rig.workspace = true
galaxy_bedrock_model_catalog.workspace = true
galaxy_editor.workspace = true
galaxy_graphql.workspace = true
galaxy_js = { workspace = true, optional = true }
+5 -1
View File
@@ -413,10 +413,14 @@ impl RequestParams {
// server-side, drop the override; otherwise clamp it to the model's
// current `[min, max]` range. This closes the window between an
// in-flight model metadata refresh and the next request.
let llm_preferences = LLMPreferences::as_ref(app);
let selected_model = llm_preferences
.get_llm_info(&request_input.model_id)
.unwrap_or_else(|| llm_preferences.get_active_base_model(app, terminal_view_id));
let context_window_limit = AIExecutionProfilesModel::as_ref(app)
.active_profile(terminal_view_id, app)
.data()
.context_window_limit_for_request(app);
.context_window_limit_for_model_request(selected_model, app);
Self {
terminal_view_id,
+10 -6
View File
@@ -8,6 +8,7 @@ use aws_config::BehaviorVersion;
use aws_sdk_bedrock::Client;
use aws_sdk_bedrockruntime::config::Region;
use futures::{stream, StreamExt};
use galaxy_bedrock_model_catalog::model_metadata;
use super::client::{BedrockClientConfig, BedrockError};
use crate::settings::ai::BedrockModelConfig;
@@ -71,20 +72,23 @@ pub async fn discover_available_models(
return None;
}
let display_name = summary
.model_name()
.map(str::to_owned)
.unwrap_or_else(|| prettify_model_id(model_id));
let metadata = model_metadata(model_id);
let display_name = summary.model_name().map(str::to_owned).unwrap_or_else(|| {
metadata
.map(|metadata| metadata.model_name.clone())
.unwrap_or_else(|| prettify_model_id(model_id))
});
let vision_supported = summary
.input_modalities()
.iter()
.any(|modality| modality.as_str() == "IMAGE");
.any(|modality| modality.as_str() == "IMAGE")
|| metadata.is_some_and(|metadata| metadata.supports_vision_input());
Some(BedrockModelConfig {
model_id: model_id.to_owned(),
display_name,
vision_supported,
use_rig: false,
use_rig: true,
})
}
});
@@ -2050,7 +2050,7 @@ impl AgentInputFooter {
let profile_context = AIExecutionProfilesModel::as_ref(ctx)
.active_profile(Some(self.terminal_view_id), ctx)
.data()
.context_window_display_value(ctx);
.context_window_display_value_for_model(active_model, ctx);
let model_max_context = active_model
.context_window
.default_max
+48 -6
View File
@@ -117,7 +117,17 @@ pub trait AIExecutionProfileAppExt {
fn configurable_context_window(&self, app: &AppContext) -> Option<LLMContextWindow>;
fn context_window_display_value(&self, app: &AppContext) -> Option<u32>;
fn context_window_display_value_for_model(
&self,
model: &LLMInfo,
app: &AppContext,
) -> Option<u32>;
fn context_window_limit_for_request(&self, app: &AppContext) -> Option<u32>;
fn context_window_limit_for_model_request(
&self,
model: &LLMInfo,
app: &AppContext,
) -> Option<u32>;
fn should_show_long_context_pricing_warning(
&self,
context_window_limit: Option<u32>,
@@ -139,20 +149,52 @@ impl AIExecutionProfileAppExt for AIExecutionProfile {
}
fn context_window_display_value(&self, app: &AppContext) -> Option<u32> {
let cw = self.configurable_context_window(app)?;
Some(self.context_window_limit.unwrap_or(cw.default_max))
self.context_window_display_value_for_model(effective_base_model(self, app), app)
}
fn context_window_limit_for_request(&self, app: &AppContext) -> Option<u32> {
let llm = effective_base_model(self, app);
fn context_window_display_value_for_model(
&self,
model: &LLMInfo,
app: &AppContext,
) -> Option<u32> {
if !has_configurable_context_window(
llm,
model,
FeatureFlag::GPTConfigurableContextWindow.is_enabled(),
) {
return None;
}
let selected_limit = if effective_base_model(self, app).id == model.id {
self.context_window_limit
} else {
None
};
Some(
selected_limit
.unwrap_or(model.context_window.default_max)
.clamp(model.context_window.min, model.context_window.max),
)
}
fn context_window_limit_for_request(&self, app: &AppContext) -> Option<u32> {
self.context_window_limit_for_model_request(effective_base_model(self, app), app)
}
fn context_window_limit_for_model_request(
&self,
model: &LLMInfo,
app: &AppContext,
) -> Option<u32> {
if !has_configurable_context_window(
model,
FeatureFlag::GPTConfigurableContextWindow.is_enabled(),
) || effective_base_model(self, app).id != model.id
{
return None;
}
self.context_window_limit
.map(|limit| limit.clamp(llm.context_window.min, llm.context_window.max))
.map(|limit| limit.clamp(model.context_window.min, model.context_window.max))
}
fn should_show_long_context_pricing_warning(
+23 -2
View File
@@ -11,6 +11,8 @@ use galaxy_agent_rig::{
discover_anthropic_models, discover_gemini_models, validate_vertex_ai_credentials,
vertex_ai_model_catalog, RigModelInfo,
};
#[cfg(not(target_family = "wasm"))]
use galaxy_bedrock_model_catalog::model_metadata;
use galaxy_core::features::FeatureFlag;
use galaxy_core::ui::icons::Icon;
use galaxy_core::user_preferences::GetUserPreferences;
@@ -873,11 +875,29 @@ impl LLMPreferences {
let effective = effective;
for model in effective {
let metadata = model_metadata(&model.model_id);
if metadata.is_some_and(|metadata| metadata.supports_agent_runtime() == Some(false)) {
log::debug!(
"[bedrock] Excluding {} from Agent Mode because its catalog metadata marks it as incompatible with streaming Converse",
model.model_id
);
continue;
}
let model_id = if cross_region && !region.is_empty() {
super::bedrock::models::apply_cross_region_prefix(&model.model_id, &region)
} else {
model.model_id.clone()
};
let context_window = metadata
.and_then(|metadata| metadata.context_window_tokens)
.map(|tokens| LLMContextWindow {
is_configurable: false,
min: tokens,
max: tokens,
default_max: tokens,
})
.unwrap_or_default();
let llm_info = LLMInfo {
id: LLMId::from(model_id.as_str()),
@@ -890,7 +910,8 @@ impl LLMPreferences {
},
description: Some("AWS Bedrock".to_string()),
disable_reason: None,
vision_supported: model.vision_supported,
vision_supported: model.vision_supported
|| metadata.is_some_and(|metadata| metadata.supports_vision_input()),
spec: None,
provider: LLMProvider::Bedrock,
host_configs: HashMap::from([(
@@ -901,7 +922,7 @@ impl LLMPreferences {
},
)]),
discount_percentage: None,
context_window: LLMContextWindow::default(),
context_window,
};
self.models_by_feature
.agent_mode
+39 -10
View File
@@ -8,6 +8,7 @@ use galaxy_agent_rig::{
GeminiRuntimeConfig, OpenAICompatibleRuntime, OpenAICompatibleRuntimeConfig, VertexAiRuntime,
VertexAiRuntimeConfig,
};
use galaxy_bedrock_model_catalog::model_metadata;
use uuid::Uuid;
use warp_multi_agent_api::ToolType;
@@ -79,7 +80,11 @@ pub(crate) async fn prepare_provider_run(
let (supported_tools, supported_cli_agent_tools) =
crate::ai::agent::api::prepare_direct_provider_params(&mut params);
let skill_path_origin = params.session_context.skill_path_origin();
let max_context_tokens = params.context_window_limit;
let max_context_tokens = provider_context_window_tokens(
&base_provider_config,
params.model.as_str(),
params.context_window_limit,
);
let mut cli_params = params.clone();
let cli_model_is_placeholder = params.cli_agent_model.as_str().trim().is_empty()
|| params
@@ -186,14 +191,17 @@ async fn prepare_provider_profile(
),
None => prepare_rig_turn(config, params, supported_tools, supported_cli_agent_tools),
},
crate::ai::provider::ProviderConfig::Bedrock(_) => prepare_bedrock_rig_turn_for_mode(
model,
Some(64_000),
params,
supported_tools,
supported_cli_agent_tools,
mode,
),
crate::ai::provider::ProviderConfig::Bedrock(_) => {
let max_output_tokens = Some(bedrock_max_output_tokens(&model));
prepare_bedrock_rig_turn_for_mode(
model,
max_output_tokens,
params,
supported_tools,
supported_cli_agent_tools,
mode,
)
}
crate::ai::provider::ProviderConfig::None => {
anyhow::bail!(
"No AI runtime configured. Enable an agent runtime or model provider in settings."
@@ -250,7 +258,7 @@ pub(crate) async fn provider_runtime_for_request(
})),
},
crate::ai::provider::ProviderConfig::Bedrock(config) => {
let max_output_tokens = Some(64_000);
let max_output_tokens = Some(bedrock_max_output_tokens(&model));
let cross_region_inference = config.cross_region_inference;
let caching_config =
CachingConfig::from_external_config(&ExternalBedrockConfig::load());
@@ -275,6 +283,27 @@ pub(crate) async fn provider_runtime_for_request(
Ok(runtime)
}
fn bedrock_max_output_tokens(model: &str) -> u64 {
model_metadata(model)
.and_then(|metadata| metadata.max_output_tokens)
.map(u64::from)
.unwrap_or(64_000)
}
fn provider_context_window_tokens(
provider_config: &crate::ai::provider::ProviderConfig,
model: &str,
configured_limit: Option<u32>,
) -> Option<u32> {
configured_limit.or_else(|| match provider_config {
crate::ai::provider::ProviderConfig::Bedrock(_) => {
model_metadata(model).and_then(|metadata| metadata.context_window_tokens)
}
crate::ai::provider::ProviderConfig::OpenAI(_)
| crate::ai::provider::ProviderConfig::None => None,
})
}
#[cfg(test)]
#[path = "rig_tests.rs"]
mod tests;