Integrate Bedrock model catalog metadata
This commit is contained in:
Generated
+9
@@ -5748,6 +5748,7 @@ dependencies = [
|
|||||||
"galaxy_acp",
|
"galaxy_acp",
|
||||||
"galaxy_agent_core",
|
"galaxy_agent_core",
|
||||||
"galaxy_agent_rig",
|
"galaxy_agent_rig",
|
||||||
|
"galaxy_bedrock_model_catalog",
|
||||||
"galaxy_cli",
|
"galaxy_cli",
|
||||||
"galaxy_completer",
|
"galaxy_completer",
|
||||||
"galaxy_core",
|
"galaxy_core",
|
||||||
@@ -5993,6 +5994,14 @@ dependencies = [
|
|||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "galaxy_bedrock_model_catalog"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "galaxy_cli"
|
name = "galaxy_cli"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ publish = false
|
|||||||
galaxy_acp = { path = "crates/acp" }
|
galaxy_acp = { path = "crates/acp" }
|
||||||
galaxy_agent_core = { path = "crates/galaxy_agent_core" }
|
galaxy_agent_core = { path = "crates/galaxy_agent_core" }
|
||||||
galaxy_agent_rig = { path = "crates/galaxy_agent_rig" }
|
galaxy_agent_rig = { path = "crates/galaxy_agent_rig" }
|
||||||
|
galaxy_bedrock_model_catalog = { path = "crates/galaxy_bedrock_model_catalog" }
|
||||||
ai = { path = "crates/ai" }
|
ai = { path = "crates/ai" }
|
||||||
app-installation-detection = { path = "crates/app-installation-detection" }
|
app-installation-detection = { path = "crates/app-installation-detection" }
|
||||||
asset_cache = { path = "crates/asset_cache" }
|
asset_cache = { path = "crates/asset_cache" }
|
||||||
|
|||||||
@@ -210,6 +210,7 @@ galaxy_completer.workspace = true
|
|||||||
galaxy_core.workspace = true
|
galaxy_core.workspace = true
|
||||||
galaxy_agent_core.workspace = true
|
galaxy_agent_core.workspace = true
|
||||||
galaxy_agent_rig.workspace = true
|
galaxy_agent_rig.workspace = true
|
||||||
|
galaxy_bedrock_model_catalog.workspace = true
|
||||||
galaxy_editor.workspace = true
|
galaxy_editor.workspace = true
|
||||||
galaxy_graphql.workspace = true
|
galaxy_graphql.workspace = true
|
||||||
galaxy_js = { workspace = true, optional = true }
|
galaxy_js = { workspace = true, optional = true }
|
||||||
|
|||||||
@@ -413,10 +413,14 @@ impl RequestParams {
|
|||||||
// server-side, drop the override; otherwise clamp it to the model's
|
// server-side, drop the override; otherwise clamp it to the model's
|
||||||
// current `[min, max]` range. This closes the window between an
|
// current `[min, max]` range. This closes the window between an
|
||||||
// in-flight model metadata refresh and the next request.
|
// 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)
|
let context_window_limit = AIExecutionProfilesModel::as_ref(app)
|
||||||
.active_profile(terminal_view_id, app)
|
.active_profile(terminal_view_id, app)
|
||||||
.data()
|
.data()
|
||||||
.context_window_limit_for_request(app);
|
.context_window_limit_for_model_request(selected_model, app);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
terminal_view_id,
|
terminal_view_id,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use aws_config::BehaviorVersion;
|
|||||||
use aws_sdk_bedrock::Client;
|
use aws_sdk_bedrock::Client;
|
||||||
use aws_sdk_bedrockruntime::config::Region;
|
use aws_sdk_bedrockruntime::config::Region;
|
||||||
use futures::{stream, StreamExt};
|
use futures::{stream, StreamExt};
|
||||||
|
use galaxy_bedrock_model_catalog::model_metadata;
|
||||||
|
|
||||||
use super::client::{BedrockClientConfig, BedrockError};
|
use super::client::{BedrockClientConfig, BedrockError};
|
||||||
use crate::settings::ai::BedrockModelConfig;
|
use crate::settings::ai::BedrockModelConfig;
|
||||||
@@ -71,20 +72,23 @@ pub async fn discover_available_models(
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let display_name = summary
|
let metadata = model_metadata(model_id);
|
||||||
.model_name()
|
let display_name = summary.model_name().map(str::to_owned).unwrap_or_else(|| {
|
||||||
.map(str::to_owned)
|
metadata
|
||||||
.unwrap_or_else(|| prettify_model_id(model_id));
|
.map(|metadata| metadata.model_name.clone())
|
||||||
|
.unwrap_or_else(|| prettify_model_id(model_id))
|
||||||
|
});
|
||||||
let vision_supported = summary
|
let vision_supported = summary
|
||||||
.input_modalities()
|
.input_modalities()
|
||||||
.iter()
|
.iter()
|
||||||
.any(|modality| modality.as_str() == "IMAGE");
|
.any(|modality| modality.as_str() == "IMAGE")
|
||||||
|
|| metadata.is_some_and(|metadata| metadata.supports_vision_input());
|
||||||
|
|
||||||
Some(BedrockModelConfig {
|
Some(BedrockModelConfig {
|
||||||
model_id: model_id.to_owned(),
|
model_id: model_id.to_owned(),
|
||||||
display_name,
|
display_name,
|
||||||
vision_supported,
|
vision_supported,
|
||||||
use_rig: false,
|
use_rig: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2050,7 +2050,7 @@ impl AgentInputFooter {
|
|||||||
let profile_context = AIExecutionProfilesModel::as_ref(ctx)
|
let profile_context = AIExecutionProfilesModel::as_ref(ctx)
|
||||||
.active_profile(Some(self.terminal_view_id), ctx)
|
.active_profile(Some(self.terminal_view_id), ctx)
|
||||||
.data()
|
.data()
|
||||||
.context_window_display_value(ctx);
|
.context_window_display_value_for_model(active_model, ctx);
|
||||||
let model_max_context = active_model
|
let model_max_context = active_model
|
||||||
.context_window
|
.context_window
|
||||||
.default_max
|
.default_max
|
||||||
|
|||||||
@@ -117,7 +117,17 @@ pub trait AIExecutionProfileAppExt {
|
|||||||
fn configurable_context_window(&self, app: &AppContext) -> Option<LLMContextWindow>;
|
fn configurable_context_window(&self, app: &AppContext) -> Option<LLMContextWindow>;
|
||||||
|
|
||||||
fn context_window_display_value(&self, app: &AppContext) -> Option<u32>;
|
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_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(
|
fn should_show_long_context_pricing_warning(
|
||||||
&self,
|
&self,
|
||||||
context_window_limit: Option<u32>,
|
context_window_limit: Option<u32>,
|
||||||
@@ -139,20 +149,52 @@ impl AIExecutionProfileAppExt for AIExecutionProfile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn context_window_display_value(&self, app: &AppContext) -> Option<u32> {
|
fn context_window_display_value(&self, app: &AppContext) -> Option<u32> {
|
||||||
let cw = self.configurable_context_window(app)?;
|
self.context_window_display_value_for_model(effective_base_model(self, app), app)
|
||||||
Some(self.context_window_limit.unwrap_or(cw.default_max))
|
|
||||||
}
|
}
|
||||||
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(
|
if !has_configurable_context_window(
|
||||||
llm,
|
model,
|
||||||
FeatureFlag::GPTConfigurableContextWindow.is_enabled(),
|
FeatureFlag::GPTConfigurableContextWindow.is_enabled(),
|
||||||
) {
|
) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let selected_limit = if effective_base_model(self, app).id == model.id {
|
||||||
self.context_window_limit
|
self.context_window_limit
|
||||||
.map(|limit| limit.clamp(llm.context_window.min, llm.context_window.max))
|
} 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(model.context_window.min, model.context_window.max))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_show_long_context_pricing_warning(
|
fn should_show_long_context_pricing_warning(
|
||||||
|
|||||||
+23
-2
@@ -11,6 +11,8 @@ use galaxy_agent_rig::{
|
|||||||
discover_anthropic_models, discover_gemini_models, validate_vertex_ai_credentials,
|
discover_anthropic_models, discover_gemini_models, validate_vertex_ai_credentials,
|
||||||
vertex_ai_model_catalog, RigModelInfo,
|
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::features::FeatureFlag;
|
||||||
use galaxy_core::ui::icons::Icon;
|
use galaxy_core::ui::icons::Icon;
|
||||||
use galaxy_core::user_preferences::GetUserPreferences;
|
use galaxy_core::user_preferences::GetUserPreferences;
|
||||||
@@ -873,11 +875,29 @@ impl LLMPreferences {
|
|||||||
|
|
||||||
let effective = effective;
|
let effective = effective;
|
||||||
for model in 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() {
|
let model_id = if cross_region && !region.is_empty() {
|
||||||
super::bedrock::models::apply_cross_region_prefix(&model.model_id, ®ion)
|
super::bedrock::models::apply_cross_region_prefix(&model.model_id, ®ion)
|
||||||
} else {
|
} else {
|
||||||
model.model_id.clone()
|
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 {
|
let llm_info = LLMInfo {
|
||||||
id: LLMId::from(model_id.as_str()),
|
id: LLMId::from(model_id.as_str()),
|
||||||
@@ -890,7 +910,8 @@ impl LLMPreferences {
|
|||||||
},
|
},
|
||||||
description: Some("AWS Bedrock".to_string()),
|
description: Some("AWS Bedrock".to_string()),
|
||||||
disable_reason: None,
|
disable_reason: None,
|
||||||
vision_supported: model.vision_supported,
|
vision_supported: model.vision_supported
|
||||||
|
|| metadata.is_some_and(|metadata| metadata.supports_vision_input()),
|
||||||
spec: None,
|
spec: None,
|
||||||
provider: LLMProvider::Bedrock,
|
provider: LLMProvider::Bedrock,
|
||||||
host_configs: HashMap::from([(
|
host_configs: HashMap::from([(
|
||||||
@@ -901,7 +922,7 @@ impl LLMPreferences {
|
|||||||
},
|
},
|
||||||
)]),
|
)]),
|
||||||
discount_percentage: None,
|
discount_percentage: None,
|
||||||
context_window: LLMContextWindow::default(),
|
context_window,
|
||||||
};
|
};
|
||||||
self.models_by_feature
|
self.models_by_feature
|
||||||
.agent_mode
|
.agent_mode
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use galaxy_agent_rig::{
|
|||||||
GeminiRuntimeConfig, OpenAICompatibleRuntime, OpenAICompatibleRuntimeConfig, VertexAiRuntime,
|
GeminiRuntimeConfig, OpenAICompatibleRuntime, OpenAICompatibleRuntimeConfig, VertexAiRuntime,
|
||||||
VertexAiRuntimeConfig,
|
VertexAiRuntimeConfig,
|
||||||
};
|
};
|
||||||
|
use galaxy_bedrock_model_catalog::model_metadata;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use warp_multi_agent_api::ToolType;
|
use warp_multi_agent_api::ToolType;
|
||||||
|
|
||||||
@@ -79,7 +80,11 @@ pub(crate) async fn prepare_provider_run(
|
|||||||
let (supported_tools, supported_cli_agent_tools) =
|
let (supported_tools, supported_cli_agent_tools) =
|
||||||
crate::ai::agent::api::prepare_direct_provider_params(&mut params);
|
crate::ai::agent::api::prepare_direct_provider_params(&mut params);
|
||||||
let skill_path_origin = params.session_context.skill_path_origin();
|
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 mut cli_params = params.clone();
|
||||||
let cli_model_is_placeholder = params.cli_agent_model.as_str().trim().is_empty()
|
let cli_model_is_placeholder = params.cli_agent_model.as_str().trim().is_empty()
|
||||||
|| params
|
|| params
|
||||||
@@ -186,14 +191,17 @@ async fn prepare_provider_profile(
|
|||||||
),
|
),
|
||||||
None => prepare_rig_turn(config, params, supported_tools, supported_cli_agent_tools),
|
None => prepare_rig_turn(config, params, supported_tools, supported_cli_agent_tools),
|
||||||
},
|
},
|
||||||
crate::ai::provider::ProviderConfig::Bedrock(_) => prepare_bedrock_rig_turn_for_mode(
|
crate::ai::provider::ProviderConfig::Bedrock(_) => {
|
||||||
|
let max_output_tokens = Some(bedrock_max_output_tokens(&model));
|
||||||
|
prepare_bedrock_rig_turn_for_mode(
|
||||||
model,
|
model,
|
||||||
Some(64_000),
|
max_output_tokens,
|
||||||
params,
|
params,
|
||||||
supported_tools,
|
supported_tools,
|
||||||
supported_cli_agent_tools,
|
supported_cli_agent_tools,
|
||||||
mode,
|
mode,
|
||||||
),
|
)
|
||||||
|
}
|
||||||
crate::ai::provider::ProviderConfig::None => {
|
crate::ai::provider::ProviderConfig::None => {
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
"No AI runtime configured. Enable an agent runtime or model provider in settings."
|
"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) => {
|
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 cross_region_inference = config.cross_region_inference;
|
||||||
let caching_config =
|
let caching_config =
|
||||||
CachingConfig::from_external_config(&ExternalBedrockConfig::load());
|
CachingConfig::from_external_config(&ExternalBedrockConfig::load());
|
||||||
@@ -275,6 +283,27 @@ pub(crate) async fn provider_runtime_for_request(
|
|||||||
Ok(runtime)
|
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)]
|
#[cfg(test)]
|
||||||
#[path = "rig_tests.rs"]
|
#[path = "rig_tests.rs"]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "galaxy_bedrock_model_catalog"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
publish.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Galaxy Bedrock Model Catalog
|
||||||
|
|
||||||
|
This crate embeds an offline snapshot of the public Amazon Bedrock Models
|
||||||
|
Catalog. It intentionally exposes the upstream JSON verbatim so Galaxy can
|
||||||
|
adopt newly added fields without changing the snapshot layer first.
|
||||||
|
|
||||||
|
Refresh the snapshot from the workspace root:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./script/update_bedrock_model_catalog
|
||||||
|
```
|
||||||
|
|
||||||
|
The updater resolves the current upstream `main` commit, downloads all catalog
|
||||||
|
documents at that immutable commit, validates their JSON shapes, and updates
|
||||||
|
`data/UPSTREAM_COMMIT`.
|
||||||
|
|
||||||
|
Normal Cargo builds never require network access. The checked-in snapshot is
|
||||||
|
embedded with `include_str!` and rebuilt whenever one of the data files changes.
|
||||||
|
|
||||||
|
See [UPSTREAM.md](UPSTREAM.md) for provenance and licensing notes.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Upstream provenance
|
||||||
|
|
||||||
|
- Source: https://github.com/amazonbedrockmodels/amazonbedrockmodels.github.io
|
||||||
|
- Data directory: https://github.com/amazonbedrockmodels/amazonbedrockmodels.github.io/tree/main/data
|
||||||
|
- Snapshot commit: recorded in `data/UPSTREAM_COMMIT`
|
||||||
|
- Upstream license declaration: MIT, as stated in the upstream README
|
||||||
|
|
||||||
|
The upstream project is an unofficial, community-maintained catalog refreshed
|
||||||
|
from AWS Bedrock APIs. Its data is useful for discovery and enrichment, but AWS
|
||||||
|
documentation and live account/region responses remain authoritative for model
|
||||||
|
availability and access.
|
||||||
|
|
||||||
|
The JSON files in `data/` are preserved verbatim from upstream. Refresh them
|
||||||
|
with `./script/update_bedrock_model_catalog` rather than editing them manually.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
df190def1e3301a479a386f8eeaddd5f49bef7a4
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0",
|
||||||
|
"description": "Public API for the Amazon Bedrock Models Catalog",
|
||||||
|
"endpoints": {
|
||||||
|
"models": "/data/models.json",
|
||||||
|
"profiles": "/data/profiles.json",
|
||||||
|
"beta_models": "/data/beta_models.json"
|
||||||
|
},
|
||||||
|
"usage": "Fetch any of the endpoints above to get the latest model data in JSON format."
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "writer.palmyra-vision-7b",
|
||||||
|
"name": "Writer Palmyra Vision 7B",
|
||||||
|
"provider": "Writer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "zai.glm-4.6",
|
||||||
|
"name": "GLM 4.6",
|
||||||
|
"provider": "Z.AI"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,739 @@
|
|||||||
|
{
|
||||||
|
"anthropic.claude-fable-5": {
|
||||||
|
"created": 1780272000,
|
||||||
|
"regions": [
|
||||||
|
"us-east-1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"anthropic.claude-haiku-4-5": {
|
||||||
|
"created": 1759276800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"anthropic.claude-opus-4-7": {
|
||||||
|
"created": 1775692800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"us-east-1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"anthropic.claude-opus-4-8": {
|
||||||
|
"created": 1779148800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"us-east-1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"anthropic.claude-opus-5": {
|
||||||
|
"created": 1782950400,
|
||||||
|
"regions": [
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"us-east-1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"anthropic.claude-sonnet-5": {
|
||||||
|
"created": 1781654400,
|
||||||
|
"regions": [
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"us-east-1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"deepseek.v3.1": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"deepseek.v3.2": {
|
||||||
|
"created": 1769385600,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"google.gemma-3-12b-it": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"google.gemma-3-27b-it": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"google.gemma-3-4b-it": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"google.gemma-4-26b-a4b": {
|
||||||
|
"created": 1777852800,
|
||||||
|
"regions": [
|
||||||
|
"eu-central-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"google.gemma-4-31b": {
|
||||||
|
"created": 1778112000,
|
||||||
|
"regions": [
|
||||||
|
"eu-central-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"google.gemma-4-e2b": {
|
||||||
|
"created": 1778112000,
|
||||||
|
"regions": [
|
||||||
|
"eu-central-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"minimax.minimax-m2": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"minimax.minimax-m2.1": {
|
||||||
|
"created": 1769396433,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"minimax.minimax-m2.5": {
|
||||||
|
"created": 1769396433,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mistral.devstral-2-123b": {
|
||||||
|
"created": 1765843200,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mistral.magistral-small-2509": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mistral.ministral-3-14b-instruct": {
|
||||||
|
"created": 1763923865,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mistral.ministral-3-3b-instruct": {
|
||||||
|
"created": 1763923654,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mistral.ministral-3-8b-instruct": {
|
||||||
|
"created": 1763923750,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mistral.mistral-large-3-675b-instruct": {
|
||||||
|
"created": 1763923896,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mistral.voxtral-mini-3b-2507": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mistral.voxtral-small-24b-2507": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"moonshotai.kimi-k2-thinking": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"moonshotai.kimi-k2.5": {
|
||||||
|
"created": 1769558400,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nvidia.nemotron-nano-12b-v2": {
|
||||||
|
"created": 1763769600,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nvidia.nemotron-nano-3-30b": {
|
||||||
|
"created": 1765065600,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nvidia.nemotron-nano-9b-v2": {
|
||||||
|
"created": 1763769600,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nvidia.nemotron-super-3-120b": {
|
||||||
|
"created": 1768780800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"openai.gpt-5.4": {
|
||||||
|
"created": 1777507200,
|
||||||
|
"regions": [
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"openai.gpt-5.4-2026-03-05": {
|
||||||
|
"created": 1777507200,
|
||||||
|
"regions": [
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"openai.gpt-5.5": {
|
||||||
|
"created": 1779321600,
|
||||||
|
"regions": [
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"openai.gpt-5.5-2026-04-23": {
|
||||||
|
"created": 1779321600,
|
||||||
|
"regions": [
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"openai.gpt-5.6-luna": {
|
||||||
|
"created": 1782345600,
|
||||||
|
"regions": [
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"openai.gpt-5.6-sol": {
|
||||||
|
"created": 1781827200,
|
||||||
|
"regions": [
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"openai.gpt-5.6-terra": {
|
||||||
|
"created": 1781827200,
|
||||||
|
"regions": [
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"openai.gpt-oss-120b": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"openai.gpt-oss-20b": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"openai.gpt-oss-safeguard-120b": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"openai.gpt-oss-safeguard-20b": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"qwen.qwen3-235b-a22b-2507": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"qwen.qwen3-32b": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"qwen.qwen3-coder-30b-a3b-instruct": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"qwen.qwen3-coder-480b-a35b-instruct": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"qwen.qwen3-coder-next": {
|
||||||
|
"created": 1770163200,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"qwen.qwen3-next-80b-a3b-instruct": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"qwen.qwen3-vl-235b-a22b-instruct": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"writer.palmyra-vision-7b": {
|
||||||
|
"created": 1771804800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"xai.grok-4.3": {
|
||||||
|
"created": 1778630400,
|
||||||
|
"regions": [
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"xai.grok-4.6": {
|
||||||
|
"created": 1786492800,
|
||||||
|
"regions": [
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"zai.glm-4.6": {
|
||||||
|
"created": 1764460800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"zai.glm-4.7": {
|
||||||
|
"created": 1769558400,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"zai.glm-4.7-flash": {
|
||||||
|
"created": 1769644800,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-central-1",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"zai.glm-5": {
|
||||||
|
"created": 1771770206,
|
||||||
|
"regions": [
|
||||||
|
"ap-northeast-1",
|
||||||
|
"ap-south-1",
|
||||||
|
"ap-southeast-2",
|
||||||
|
"eu-north-1",
|
||||||
|
"eu-west-2",
|
||||||
|
"sa-east-1",
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2",
|
||||||
|
"us-west-2"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,65 @@
|
|||||||
|
//! A versioned, offline snapshot of the public Amazon Bedrock model catalog.
|
||||||
|
//!
|
||||||
|
//! The JSON is kept verbatim so consumers can adopt new upstream fields without
|
||||||
|
//! first changing this crate. Run `./script/update_bedrock_model_catalog` from
|
||||||
|
//! the workspace root to refresh the snapshot.
|
||||||
|
|
||||||
|
mod metadata;
|
||||||
|
|
||||||
|
pub use metadata::{ModelMetadata, model_metadata, models};
|
||||||
|
|
||||||
|
/// Repository from which the bundled snapshot was downloaded.
|
||||||
|
pub const UPSTREAM_REPOSITORY: &str =
|
||||||
|
"https://github.com/amazonbedrockmodels/amazonbedrockmodels.github.io";
|
||||||
|
|
||||||
|
/// The catalog's public API manifest.
|
||||||
|
pub const API_MANIFEST_JSON: &str = include_str!("../data/api.json");
|
||||||
|
|
||||||
|
/// Foundation models discovered through the AWS-native Bedrock APIs.
|
||||||
|
pub const FOUNDATION_MODELS_JSON: &str = include_str!("../data/models.json");
|
||||||
|
|
||||||
|
/// System-defined inference profiles discovered across AWS regions.
|
||||||
|
pub const INFERENCE_PROFILES_JSON: &str = include_str!("../data/profiles.json");
|
||||||
|
|
||||||
|
/// Models discovered before they appear in the main AWS documentation.
|
||||||
|
pub const BETA_MODELS_JSON: &str = include_str!("../data/beta_models.json");
|
||||||
|
|
||||||
|
/// Models available through the Bedrock Mantle endpoint.
|
||||||
|
pub const MANTLE_MODELS_JSON: &str = include_str!("../data/mantle_models.json");
|
||||||
|
|
||||||
|
/// Enriched model-card metadata maintained by the upstream catalog.
|
||||||
|
pub const MODEL_CARDS_JSON: &str = include_str!("../data/model_cards.json");
|
||||||
|
|
||||||
|
/// Returns the exact upstream commit used for the bundled snapshot.
|
||||||
|
pub fn upstream_commit() -> &'static str {
|
||||||
|
include_str!("../data/UPSTREAM_COMMIT").trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// All catalog documents embedded in this crate.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct CatalogSnapshot {
|
||||||
|
pub api_manifest: &'static str,
|
||||||
|
pub foundation_models: &'static str,
|
||||||
|
pub inference_profiles: &'static str,
|
||||||
|
pub beta_models: &'static str,
|
||||||
|
pub mantle_models: &'static str,
|
||||||
|
pub model_cards: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The bundled catalog snapshot.
|
||||||
|
pub const SNAPSHOT: CatalogSnapshot = CatalogSnapshot {
|
||||||
|
api_manifest: API_MANIFEST_JSON,
|
||||||
|
foundation_models: FOUNDATION_MODELS_JSON,
|
||||||
|
inference_profiles: INFERENCE_PROFILES_JSON,
|
||||||
|
beta_models: BETA_MODELS_JSON,
|
||||||
|
mantle_models: MANTLE_MODELS_JSON,
|
||||||
|
model_cards: MODEL_CARDS_JSON,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "lib_tests.rs"]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "metadata_tests.rs"]
|
||||||
|
mod metadata_tests;
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bundled_catalog_documents_are_valid_json() {
|
||||||
|
for document in [
|
||||||
|
API_MANIFEST_JSON,
|
||||||
|
FOUNDATION_MODELS_JSON,
|
||||||
|
INFERENCE_PROFILES_JSON,
|
||||||
|
BETA_MODELS_JSON,
|
||||||
|
MANTLE_MODELS_JSON,
|
||||||
|
MODEL_CARDS_JSON,
|
||||||
|
] {
|
||||||
|
serde_json::from_str::<Value>(document).expect("catalog document should be valid JSON");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bundled_catalog_has_models_and_provenance() {
|
||||||
|
let foundation_models: Value =
|
||||||
|
serde_json::from_str(FOUNDATION_MODELS_JSON).expect("foundation models should parse");
|
||||||
|
let mantle_models: Value =
|
||||||
|
serde_json::from_str(MANTLE_MODELS_JSON).expect("Mantle models should parse");
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
foundation_models
|
||||||
|
.as_array()
|
||||||
|
.is_some_and(|models| !models.is_empty())
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
mantle_models
|
||||||
|
.as_object()
|
||||||
|
.is_some_and(|models| !models.is_empty())
|
||||||
|
);
|
||||||
|
assert_eq!(upstream_commit().len(), 40);
|
||||||
|
assert!(
|
||||||
|
upstream_commit()
|
||||||
|
.bytes()
|
||||||
|
.all(|byte| byte.is_ascii_hexdigit())
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::FOUNDATION_MODELS_JSON;
|
||||||
|
|
||||||
|
const CROSS_REGION_PREFIXES: &[&str] = &["global.", "apac.", "us.", "eu.", "jp.", "au."];
|
||||||
|
|
||||||
|
/// Provider-neutral metadata for one Bedrock foundation model.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct ModelMetadata {
|
||||||
|
pub model_id: String,
|
||||||
|
pub model_name: String,
|
||||||
|
pub provider_name: String,
|
||||||
|
pub input_modalities: Vec<String>,
|
||||||
|
pub output_modalities: Vec<String>,
|
||||||
|
pub response_streaming_supported: Option<bool>,
|
||||||
|
pub regions: Vec<String>,
|
||||||
|
pub context_window_tokens: Option<u32>,
|
||||||
|
pub max_output_tokens: Option<u32>,
|
||||||
|
pub converse_supported: Option<bool>,
|
||||||
|
pub invoke_supported: Option<bool>,
|
||||||
|
pub bedrock_runtime_supported: Option<bool>,
|
||||||
|
pub bedrock_mantle_supported: Option<bool>,
|
||||||
|
pub mantle_regions: Vec<String>,
|
||||||
|
pub model_card_url: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModelMetadata {
|
||||||
|
pub fn supports_vision_input(&self) -> bool {
|
||||||
|
self.input_modalities
|
||||||
|
.iter()
|
||||||
|
.any(|modality| modality == "IMAGE")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reports whether the catalog has enough evidence to classify this as an
|
||||||
|
/// Agent Mode model. `None` deliberately leaves new or incomplete catalog
|
||||||
|
/// entries usable until their metadata catches up.
|
||||||
|
pub fn supports_agent_runtime(&self) -> Option<bool> {
|
||||||
|
if !self.output_modalities.is_empty()
|
||||||
|
&& !self
|
||||||
|
.output_modalities
|
||||||
|
.iter()
|
||||||
|
.any(|modality| modality == "TEXT")
|
||||||
|
{
|
||||||
|
return Some(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
for support in [
|
||||||
|
self.response_streaming_supported,
|
||||||
|
self.converse_supported,
|
||||||
|
self.bedrock_runtime_supported,
|
||||||
|
] {
|
||||||
|
if support == Some(false) {
|
||||||
|
return Some(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.output_modalities.is_empty()
|
||||||
|
|| self.response_streaming_supported.is_none()
|
||||||
|
|| self.converse_supported.is_none()
|
||||||
|
|| self.bedrock_runtime_supported.is_none()
|
||||||
|
{
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns all parsed foundation-model metadata in the bundled snapshot.
|
||||||
|
pub fn models() -> &'static [ModelMetadata] {
|
||||||
|
&catalog_index().models
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds metadata for a foundation model, accepting Bedrock ARNs,
|
||||||
|
/// cross-region inference prefixes, and Galaxy's optional `[1m]` suffix.
|
||||||
|
pub fn model_metadata(model_id: &str) -> Option<&'static ModelMetadata> {
|
||||||
|
let index = catalog_index();
|
||||||
|
let normalized = normalize_model_id(model_id);
|
||||||
|
index
|
||||||
|
.by_id
|
||||||
|
.get(normalized)
|
||||||
|
.and_then(|position| index.models.get(*position))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn catalog_index() -> &'static CatalogIndex {
|
||||||
|
static INDEX: OnceLock<CatalogIndex> = OnceLock::new();
|
||||||
|
INDEX.get_or_init(CatalogIndex::load)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CatalogIndex {
|
||||||
|
models: Vec<ModelMetadata>,
|
||||||
|
by_id: HashMap<String, usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CatalogIndex {
|
||||||
|
fn load() -> Self {
|
||||||
|
let raw_models =
|
||||||
|
serde_json::from_str::<Vec<RawModel>>(FOUNDATION_MODELS_JSON).unwrap_or_default();
|
||||||
|
let models = raw_models
|
||||||
|
.into_iter()
|
||||||
|
.map(ModelMetadata::from)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let by_id = models
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(position, model)| (model.model_id.clone(), position))
|
||||||
|
.collect();
|
||||||
|
Self { models, by_id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RawModel> for ModelMetadata {
|
||||||
|
fn from(raw: RawModel) -> Self {
|
||||||
|
let card = raw.model_card.unwrap_or_default();
|
||||||
|
Self {
|
||||||
|
model_id: raw.model_id,
|
||||||
|
model_name: raw.model_name,
|
||||||
|
provider_name: raw.provider_name,
|
||||||
|
input_modalities: raw.input_modalities,
|
||||||
|
output_modalities: raw.output_modalities,
|
||||||
|
response_streaming_supported: raw.response_streaming_supported,
|
||||||
|
regions: raw.regions,
|
||||||
|
context_window_tokens: card.context_window.as_deref().and_then(parse_token_count),
|
||||||
|
max_output_tokens: card
|
||||||
|
.max_output_tokens
|
||||||
|
.as_deref()
|
||||||
|
.and_then(parse_token_count),
|
||||||
|
converse_supported: card.apis_supported.as_ref().and_then(|apis| apis.converse),
|
||||||
|
invoke_supported: card.apis_supported.as_ref().and_then(|apis| apis.invoke),
|
||||||
|
bedrock_runtime_supported: card
|
||||||
|
.endpoints_supported
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|endpoints| endpoints.bedrock_runtime),
|
||||||
|
bedrock_mantle_supported: card
|
||||||
|
.endpoints_supported
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|endpoints| endpoints.bedrock_mantle),
|
||||||
|
mantle_regions: card.mantle_regions,
|
||||||
|
model_card_url: card.model_card_url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn normalize_model_id(model_id: &str) -> &str {
|
||||||
|
let model_id = model_id.rsplit('/').next().unwrap_or(model_id);
|
||||||
|
let model_id = model_id
|
||||||
|
.strip_suffix("[1m]")
|
||||||
|
.or_else(|| model_id.strip_suffix("[1M]"))
|
||||||
|
.unwrap_or(model_id);
|
||||||
|
CROSS_REGION_PREFIXES
|
||||||
|
.iter()
|
||||||
|
.find_map(|prefix| model_id.strip_prefix(prefix))
|
||||||
|
.unwrap_or(model_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_token_count(value: &str) -> Option<u32> {
|
||||||
|
let value = value
|
||||||
|
.trim()
|
||||||
|
.strip_suffix("tokens")
|
||||||
|
.or_else(|| value.trim().strip_suffix("token"))
|
||||||
|
.unwrap_or(value)
|
||||||
|
.trim();
|
||||||
|
let suffix_start = value
|
||||||
|
.find(|character: char| !character.is_ascii_digit() && character != ',' && character != '.')
|
||||||
|
.unwrap_or(value.len());
|
||||||
|
let (number, suffix) = value.split_at(suffix_start);
|
||||||
|
let multiplier = match suffix.trim().to_ascii_uppercase().as_str() {
|
||||||
|
"" => 1.0,
|
||||||
|
"K" => 1_000.0,
|
||||||
|
"M" => 1_000_000.0,
|
||||||
|
"B" => 1_000_000_000.0,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
let number = number.replace(',', "").parse::<f64>().ok()?;
|
||||||
|
let tokens = number * multiplier;
|
||||||
|
if !tokens.is_finite() || tokens < 0.0 || tokens > u32::MAX as f64 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(tokens.round() as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct RawModel {
|
||||||
|
model_id: String,
|
||||||
|
#[serde(default)]
|
||||||
|
model_name: String,
|
||||||
|
#[serde(default)]
|
||||||
|
provider_name: String,
|
||||||
|
#[serde(default)]
|
||||||
|
input_modalities: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
output_modalities: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
response_streaming_supported: Option<bool>,
|
||||||
|
#[serde(default)]
|
||||||
|
regions: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
model_card: Option<RawModelCard>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct RawModelCard {
|
||||||
|
context_window: Option<String>,
|
||||||
|
max_output_tokens: Option<String>,
|
||||||
|
apis_supported: Option<RawApisSupported>,
|
||||||
|
endpoints_supported: Option<RawEndpointsSupported>,
|
||||||
|
#[serde(default)]
|
||||||
|
mantle_regions: Vec<String>,
|
||||||
|
model_card_url: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct RawApisSupported {
|
||||||
|
converse: Option<bool>,
|
||||||
|
invoke: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct RawEndpointsSupported {
|
||||||
|
bedrock_runtime: Option<bool>,
|
||||||
|
bedrock_mantle: Option<bool>,
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reads_context_and_capabilities_for_an_agent_model() {
|
||||||
|
let metadata = model_metadata("anthropic.claude-opus-4-8").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(metadata.model_name, "Claude Opus 4.8");
|
||||||
|
assert_eq!(metadata.provider_name, "Anthropic");
|
||||||
|
assert_eq!(metadata.context_window_tokens, Some(1_000_000));
|
||||||
|
assert_eq!(metadata.max_output_tokens, Some(128_000));
|
||||||
|
assert!(metadata.supports_vision_input());
|
||||||
|
assert_eq!(metadata.supports_agent_runtime(), Some(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn normalizes_inference_profile_ids_and_galaxy_suffixes() {
|
||||||
|
let metadata = model_metadata(
|
||||||
|
"arn:aws:bedrock:us-east-1:123456789012:inference-profile/us.anthropic.claude-opus-4-8[1M]",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(metadata.model_id, "anthropic.claude-opus-4-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preserves_unknown_and_invalid_metadata_as_unknown() {
|
||||||
|
assert!(model_metadata("example.future-model-v1").is_none());
|
||||||
|
assert_eq!(
|
||||||
|
model_metadata("cohere.embed-english-v3")
|
||||||
|
.unwrap()
|
||||||
|
.context_window_tokens,
|
||||||
|
None
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rejects_known_non_agent_models() {
|
||||||
|
assert_eq!(
|
||||||
|
model_metadata("cohere.embed-english-v3")
|
||||||
|
.unwrap()
|
||||||
|
.supports_agent_runtime(),
|
||||||
|
Some(false)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
model_metadata("amazon.nova-2-sonic-v1:0")
|
||||||
|
.unwrap()
|
||||||
|
.supports_agent_runtime(),
|
||||||
|
Some(false)
|
||||||
|
);
|
||||||
|
}
|
||||||
Executable
+103
@@ -0,0 +1,103 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Refresh Galaxy's checked-in Amazon Bedrock model catalog snapshot."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
UPSTREAM_GIT_URL = (
|
||||||
|
"https://github.com/amazonbedrockmodels/amazonbedrockmodels.github.io.git"
|
||||||
|
)
|
||||||
|
UPSTREAM_RAW_URL = (
|
||||||
|
"https://raw.githubusercontent.com/amazonbedrockmodels/"
|
||||||
|
"amazonbedrockmodels.github.io"
|
||||||
|
)
|
||||||
|
UPSTREAM_REF = "refs/heads/main"
|
||||||
|
CATALOG_FILES = {
|
||||||
|
"api.json": dict,
|
||||||
|
"beta_models.json": list,
|
||||||
|
"mantle_models.json": dict,
|
||||||
|
"model_cards.json": dict,
|
||||||
|
"models.json": list,
|
||||||
|
"profiles.json": list,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_upstream_commit() -> str:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "ls-remote", UPSTREAM_GIT_URL, UPSTREAM_REF],
|
||||||
|
check=True,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=30,
|
||||||
|
)
|
||||||
|
fields = result.stdout.split()
|
||||||
|
if len(fields) != 2 or fields[1] != UPSTREAM_REF or len(fields[0]) != 40:
|
||||||
|
raise RuntimeError(f"unexpected git ls-remote response: {result.stdout!r}")
|
||||||
|
return fields[0]
|
||||||
|
|
||||||
|
|
||||||
|
def download_file(commit: str, filename: str) -> bytes:
|
||||||
|
url = f"{UPSTREAM_RAW_URL}/{commit}/data/{filename}"
|
||||||
|
request = urllib.request.Request(url, headers={"User-Agent": "Galaxy catalog updater"})
|
||||||
|
with urllib.request.urlopen(request, timeout=30) as response:
|
||||||
|
if response.status != 200:
|
||||||
|
raise RuntimeError(f"downloading {filename} returned HTTP {response.status}")
|
||||||
|
return response.read()
|
||||||
|
|
||||||
|
|
||||||
|
def validate_document(filename: str, contents: bytes, expected_type: type) -> None:
|
||||||
|
try:
|
||||||
|
document = json.loads(contents)
|
||||||
|
except (UnicodeDecodeError, json.JSONDecodeError) as error:
|
||||||
|
raise RuntimeError(f"{filename} is not valid UTF-8 JSON: {error}") from error
|
||||||
|
|
||||||
|
if not isinstance(document, expected_type):
|
||||||
|
raise RuntimeError(
|
||||||
|
f"{filename} has type {type(document).__name__}; "
|
||||||
|
f"expected {expected_type.__name__}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
workspace_root = Path(__file__).resolve().parent.parent
|
||||||
|
destination = workspace_root / "crates" / "galaxy_bedrock_model_catalog" / "data"
|
||||||
|
commit_file = destination / "UPSTREAM_COMMIT"
|
||||||
|
|
||||||
|
print("Resolving the latest Amazon Bedrock model catalog commit...")
|
||||||
|
commit = resolve_upstream_commit()
|
||||||
|
|
||||||
|
if commit_file.exists() and commit_file.read_text().strip() == commit:
|
||||||
|
print(f"Catalog is already current at {commit}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory(prefix="galaxy-bedrock-catalog-") as temp_dir:
|
||||||
|
staged_directory = Path(temp_dir)
|
||||||
|
for filename, expected_type in CATALOG_FILES.items():
|
||||||
|
print(f"Downloading {filename}...")
|
||||||
|
contents = download_file(commit, filename)
|
||||||
|
validate_document(filename, contents, expected_type)
|
||||||
|
(staged_directory / filename).write_bytes(contents)
|
||||||
|
|
||||||
|
destination.mkdir(parents=True, exist_ok=True)
|
||||||
|
for filename in CATALOG_FILES:
|
||||||
|
(staged_directory / filename).replace(destination / filename)
|
||||||
|
commit_file.write_text(f"{commit}\n")
|
||||||
|
|
||||||
|
print(f"Updated Galaxy's Bedrock model catalog to {commit}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
raise SystemExit(main())
|
||||||
|
except (OSError, RuntimeError, subprocess.SubprocessError, urllib.error.URLError) as error:
|
||||||
|
print(f"error: {error}", file=sys.stderr)
|
||||||
|
raise SystemExit(1) from error
|
||||||
Reference in New Issue
Block a user