From d862bbb0484a7058d28a07bae877063b085db61b Mon Sep 17 00:00:00 2001 From: Josh Woodcock Date: Mon, 1 Jun 2026 13:01:57 -0500 Subject: [PATCH 1/2] load and show claude settings including 1hr cache settings --- app/src/ai/bedrock/client.rs | 10 +- app/src/ai/bedrock/convert.rs | 71 +++++-- app/src/ai/bedrock/convert_tests.rs | 150 +++++++++++-- app/src/ai/bedrock/external_config.rs | 38 ++++ app/src/ai/bedrock/external_config_tests.rs | 67 ++++++ app/src/ai/bedrock/mod.rs | 1 + app/src/ai/bedrock/settings_view.rs | 200 ++++++++++++++++++ .../static_commands/commands.rs | 10 + app/src/terminal/input/slash_commands/mod.rs | 3 + app/src/terminal/view.rs | 38 ++++ app/src/terminal/view/action.rs | 3 + 11 files changed, 560 insertions(+), 31 deletions(-) create mode 100644 app/src/ai/bedrock/settings_view.rs diff --git a/app/src/ai/bedrock/client.rs b/app/src/ai/bedrock/client.rs index 9756a974..8aabda29 100644 --- a/app/src/ai/bedrock/client.rs +++ b/app/src/ai/bedrock/client.rs @@ -8,7 +8,7 @@ use aws_sdk_bedrockruntime::Client as BedrockRuntimeClient; use crate::settings::ai::BedrockAuthMethod; use super::external_config::ExternalBedrockConfig; -use super::convert::{build_converse_request, ConversationMessage, ToolDefinition}; +use super::convert::{build_converse_request, CachingConfig, ConversationMessage, ToolDefinition}; use super::diagnostic::BedrockDiagnosticLogger; use super::models::apply_cross_region_prefix; use super::response_translator::bedrock_stream_to_response_events; @@ -148,6 +148,13 @@ impl BedrockClient { model_id.to_string() }; + let external_config = ExternalBedrockConfig::load(); + let caching_config = CachingConfig::from_external_config(&external_config); + + if !caching_config.enabled { + log::info!("[bedrock] Prompt caching disabled (DISABLE_PROMPT_CACHING=1)"); + } + log::info!( "[bedrock] converse_stream: model={effective_model_id}, region={}, messages={}, tools={}", self.region, @@ -163,6 +170,7 @@ impl BedrockClient { temperature, None, None, + caching_config, ); if let Some(ref logger) = diagnostic_logger { diff --git a/app/src/ai/bedrock/convert.rs b/app/src/ai/bedrock/convert.rs index 58884f1a..9fe2e1a3 100644 --- a/app/src/ai/bedrock/convert.rs +++ b/app/src/ai/bedrock/convert.rs @@ -8,6 +8,32 @@ use aws_sdk_bedrockruntime::types::{ use aws_smithy_types::Document; use serde_json::Value as JsonValue; +use super::external_config::ExternalBedrockConfig; + +#[derive(Clone, Debug)] +pub struct CachingConfig { + pub enabled: bool, + pub extended_ttl_requested: bool, +} + +impl Default for CachingConfig { + fn default() -> Self { + Self { + enabled: true, + extended_ttl_requested: false, + } + } +} + +impl CachingConfig { + pub fn from_external_config(external: &ExternalBedrockConfig) -> Self { + Self { + enabled: !external.disable_prompt_caching, + extended_ttl_requested: external.enable_prompt_caching_1h, + } + } +} + pub struct ConvertedRequest { pub messages: Vec, pub system: Vec, @@ -73,11 +99,12 @@ pub fn build_converse_request( temperature: Option, top_p: Option, stop_sequences: Option>, + caching_config: CachingConfig, ) -> ConvertedRequest { - let bedrock_messages = convert_messages(messages); - let system = convert_system_prompt(system_prompt); + let bedrock_messages = convert_messages(messages, &caching_config); + let system = convert_system_prompt(system_prompt, &caching_config); let inference_config = build_inference_config(max_tokens, temperature, top_p, stop_sequences); - let tool_config = build_tool_config(tools); + let tool_config = build_tool_config(tools, &caching_config); ConvertedRequest { messages: bedrock_messages, @@ -112,7 +139,7 @@ fn json_to_document(value: JsonValue) -> Document { } } -fn convert_messages(messages: Vec) -> Vec { +fn convert_messages(messages: Vec, caching_config: &CachingConfig) -> Vec { let mut result = Vec::new(); for msg in messages { @@ -213,7 +240,7 @@ fn convert_messages(messages: Vec) -> Vec { // Add a cache point to the second-to-last message (the conversation prefix // that is stable between requests). This allows Bedrock to cache all prior // context and only process the latest message as new input tokens. - if messages.len() >= 2 { + if caching_config.enabled && messages.len() >= 2 { let cache_idx = messages.len() - 2; let msg = messages.remove(cache_idx); let mut content = msg.content().to_vec(); @@ -229,6 +256,11 @@ fn convert_messages(messages: Vec) -> Vec { .build() .expect("valid message with cache point"); messages.insert(cache_idx, cached_msg); + + if caching_config.extended_ttl_requested { + log::info!("[bedrock] Extended 1-hour caching requested (ENABLE_PROMPT_CACHING_1H=1)"); + // TODO: Use explicit TTL when AWS SDK supports it + } } messages @@ -265,18 +297,19 @@ fn coalesce_consecutive_roles(messages: Vec) -> Vec) -> Vec { +fn convert_system_prompt(system_prompt: Option, caching_config: &CachingConfig) -> Vec { match system_prompt { Some(prompt) if !prompt.is_empty() => { - vec![ - SystemContentBlock::Text(prompt), - SystemContentBlock::CachePoint( + let mut blocks = vec![SystemContentBlock::Text(prompt)]; + if caching_config.enabled { + blocks.push(SystemContentBlock::CachePoint( CachePointBlock::builder() .r#type(CachePointType::Default) .build() .expect("valid cache point"), - ), - ] + )); + } + blocks } _ => vec![], } @@ -303,7 +336,7 @@ fn build_inference_config( builder.build() } -fn build_tool_config(tools: Vec) -> Option { +fn build_tool_config(tools: Vec, caching_config: &CachingConfig) -> Option { if tools.is_empty() { return None; } @@ -323,12 +356,14 @@ fn build_tool_config(tools: Vec) -> Option { }) .collect(); - tool_specs.push(Tool::CachePoint( - CachePointBlock::builder() - .r#type(CachePointType::Default) - .build() - .expect("valid cache point"), - )); + if caching_config.enabled { + tool_specs.push(Tool::CachePoint( + CachePointBlock::builder() + .r#type(CachePointType::Default) + .build() + .expect("valid cache point"), + )); + } Some( ToolConfiguration::builder() diff --git a/app/src/ai/bedrock/convert_tests.rs b/app/src/ai/bedrock/convert_tests.rs index 26389a80..e5f4c0cf 100644 --- a/app/src/ai/bedrock/convert_tests.rs +++ b/app/src/ai/bedrock/convert_tests.rs @@ -10,7 +10,7 @@ fn test_text_message_converts_to_single_block() { content: MessageContent::Text("Hello".to_string()), }]; - let result = build_converse_request(messages, None, vec![], 4096, None, None, None); + let result = build_converse_request(messages, None, vec![], 4096, None, None, None, CachingConfig::default()); assert_eq!(result.messages.len(), 1); assert_eq!(result.messages[0].role(), &ConversationRole::User); @@ -29,7 +29,7 @@ fn test_tool_use_produces_valid_json_input() { }, }]; - let result = build_converse_request(messages, None, vec![], 4096, None, None, None); + let result = build_converse_request(messages, None, vec![], 4096, None, None, None, CachingConfig::default()); assert_eq!(result.messages.len(), 1); assert_eq!(result.messages[0].role(), &ConversationRole::Assistant); @@ -53,7 +53,7 @@ fn test_tool_result_with_matching_id() { }, }]; - let result = build_converse_request(messages, None, vec![], 4096, None, None, None); + let result = build_converse_request(messages, None, vec![], 4096, None, None, None, CachingConfig::default()); assert_eq!(result.messages.len(), 1); match &result.messages[0].content()[0] { @@ -75,7 +75,7 @@ fn test_tool_result_error_status() { }, }]; - let result = build_converse_request(messages, None, vec![], 4096, None, None, None); + let result = build_converse_request(messages, None, vec![], 4096, None, None, None, CachingConfig::default()); match &result.messages[0].content()[0] { ContentBlock::ToolResult(block) => { @@ -101,7 +101,7 @@ fn test_consecutive_same_role_messages_coalesced() { }, ]; - let result = build_converse_request(messages, None, vec![], 4096, None, None, None); + let result = build_converse_request(messages, None, vec![], 4096, None, None, None, CachingConfig::default()); assert_eq!(result.messages.len(), 1); assert_eq!(result.messages[0].content().len(), 2); @@ -126,7 +126,7 @@ fn test_alternating_roles_not_coalesced() { }, ]; - let result = build_converse_request(messages, None, vec![], 4096, None, None, None); + let result = build_converse_request(messages, None, vec![], 4096, None, None, None, CachingConfig::default()); assert_eq!(result.messages.len(), 3); assert_eq!(result.messages[0].role(), &ConversationRole::User); @@ -149,6 +149,7 @@ fn test_system_prompt_separated_from_messages() { None, None, None, + CachingConfig::default(), ); assert_eq!(result.system.len(), 1); @@ -159,16 +160,16 @@ fn test_system_prompt_separated_from_messages() { #[test] fn test_empty_system_prompt_produces_empty_vec() { let result = - build_converse_request(vec![], Some("".to_string()), vec![], 4096, None, None, None); + build_converse_request(vec![], Some("".to_string()), vec![], 4096, None, None, None, CachingConfig::default()); assert!(result.system.is_empty()); - let result2 = build_converse_request(vec![], None, vec![], 4096, None, None, None); + let result2 = build_converse_request(vec![], None, vec![], 4096, None, None, None, CachingConfig::default()); assert!(result2.system.is_empty()); } #[test] fn test_empty_tools_produce_none_config() { - let result = build_converse_request(vec![], None, vec![], 4096, None, None, None); + let result = build_converse_request(vec![], None, vec![], 4096, None, None, None, CachingConfig::default()); assert!(result.tool_config.is_none()); } @@ -195,7 +196,7 @@ fn test_tool_definitions_produce_tool_config() { #[test] fn test_inference_config_max_tokens_only() { - let result = build_converse_request(vec![], None, vec![], 8192, None, None, None); + let result = build_converse_request(vec![], None, vec![], 8192, None, None, None, CachingConfig::default()); assert_eq!(result.inference_config.max_tokens(), Some(8192)); assert_eq!(result.inference_config.temperature(), None); assert_eq!(result.inference_config.top_p(), None); @@ -212,6 +213,7 @@ fn test_inference_config_all_params() { Some(0.7), Some(0.9), Some(vec!["STOP".to_string()]), + CachingConfig::default(), ); assert_eq!(result.inference_config.max_tokens(), Some(4096)); assert_eq!(result.inference_config.temperature(), Some(0.7)); @@ -233,7 +235,7 @@ fn test_multipart_content_produces_multiple_blocks() { ]), }]; - let result = build_converse_request(messages, None, vec![], 4096, None, None, None); + let result = build_converse_request(messages, None, vec![], 4096, None, None, None, CachingConfig::default()); assert_eq!(result.messages[0].content().len(), 2); assert!(matches!( @@ -271,7 +273,7 @@ fn test_tool_result_after_tool_use_coalesced_into_user_message() { }, ]; - let result = build_converse_request(messages, None, vec![], 4096, None, None, None); + let result = build_converse_request(messages, None, vec![], 4096, None, None, None, CachingConfig::default()); assert_eq!(result.messages.len(), 3); assert_eq!(result.messages[0].role(), &ConversationRole::User); @@ -282,3 +284,127 @@ fn test_tool_result_after_tool_use_coalesced_into_user_message() { ContentBlock::ToolResult(_) )); } + +#[test] +fn test_caching_disabled_no_cache_points() { + let config = CachingConfig { + enabled: false, + extended_ttl_requested: false, + }; + + let messages = vec![ + ConversationMessage { + role: MessageRole::User, + content: MessageContent::Text("first message".to_string()), + }, + ConversationMessage { + role: MessageRole::Assistant, + content: MessageContent::Text("response".to_string()), + }, + ConversationMessage { + role: MessageRole::User, + content: MessageContent::Text("second message".to_string()), + }, + ]; + + let tools = vec![ToolDefinition { + name: "test_tool".to_string(), + description: "Test tool".to_string(), + input_schema: json!({"type": "object", "properties": {}}), + }]; + + let result = build_converse_request( + messages, + Some("System prompt".to_string()), + tools, + 4096, + None, + None, + None, + config, + ); + + // Check that no cache points exist in messages + for msg in &result.messages { + for content in msg.content() { + assert!(!matches!(content, ContentBlock::CachePoint(_))); + } + } + + // Check that no cache points exist in system + for block in &result.system { + use aws_sdk_bedrockruntime::types::SystemContentBlock; + assert!(!matches!(block, SystemContentBlock::CachePoint(_))); + } + + // Check that no cache points exist in tools + if let Some(tool_config) = result.tool_config { + for tool in tool_config.tools() { + use aws_sdk_bedrockruntime::types::Tool; + assert!(!matches!(tool, Tool::CachePoint(_))); + } + } +} + +#[test] +fn test_caching_enabled_has_cache_points() { + let config = CachingConfig::default(); // enabled by default + + let messages = vec![ + ConversationMessage { + role: MessageRole::User, + content: MessageContent::Text("first message".to_string()), + }, + ConversationMessage { + role: MessageRole::Assistant, + content: MessageContent::Text("response".to_string()), + }, + ConversationMessage { + role: MessageRole::User, + content: MessageContent::Text("second message".to_string()), + }, + ]; + + let tools = vec![ToolDefinition { + name: "test_tool".to_string(), + description: "Test tool".to_string(), + input_schema: json!({"type": "object", "properties": {}}), + }]; + + let result = build_converse_request( + messages, + Some("System prompt".to_string()), + tools, + 4096, + None, + None, + None, + config, + ); + + // Check that cache point exists in second-to-last message (index 1 of 3) + let second_to_last_msg = &result.messages[1]; + let has_cache_point = second_to_last_msg + .content() + .iter() + .any(|c| matches!(c, ContentBlock::CachePoint(_))); + assert!(has_cache_point, "Second-to-last message should have cache point"); + + // Check that cache point exists in system + use aws_sdk_bedrockruntime::types::SystemContentBlock; + let has_system_cache = result + .system + .iter() + .any(|b| matches!(b, SystemContentBlock::CachePoint(_))); + assert!(has_system_cache, "System should have cache point"); + + // Check that cache point exists in tools + if let Some(tool_config) = result.tool_config { + use aws_sdk_bedrockruntime::types::Tool; + let has_tool_cache = tool_config + .tools() + .iter() + .any(|t| matches!(t, Tool::CachePoint(_))); + assert!(has_tool_cache, "Tools should have cache point"); + } +} diff --git a/app/src/ai/bedrock/external_config.rs b/app/src/ai/bedrock/external_config.rs index abfd0158..96d3bfd5 100644 --- a/app/src/ai/bedrock/external_config.rs +++ b/app/src/ai/bedrock/external_config.rs @@ -9,6 +9,10 @@ pub struct ExternalBedrockConfig { pub region: Option, pub models: Vec, pub auth_refresh_command: Option, + pub disable_prompt_caching: bool, + pub enable_prompt_caching_1h: bool, + pub anthropic_model: Option, + pub anthropic_small_fast_model: Option, } impl ExternalBedrockConfig { @@ -30,6 +34,10 @@ impl ExternalBedrockConfig { claude_config.models }, auth_refresh_command: claude_config.auth_refresh_command, + disable_prompt_caching: claude_config.disable_prompt_caching, + enable_prompt_caching_1h: claude_config.enable_prompt_caching_1h, + anthropic_model: claude_config.anthropic_model, + anthropic_small_fast_model: claude_config.anthropic_small_fast_model, } } @@ -83,11 +91,37 @@ fn parse_claude_code_config(path: PathBuf) -> ExternalBedrockConfig { let models = parse_claude_code_model_map(env); + let disable_prompt_caching = env + .get("DISABLE_PROMPT_CACHING") + .and_then(|v| v.as_str()) + .map(|s| s == "1") + .unwrap_or(false); + + let enable_prompt_caching_1h = env + .get("ENABLE_PROMPT_CACHING_1H") + .and_then(|v| v.as_str()) + .map(|s| s == "1") + .unwrap_or(false); + + let anthropic_model = env + .get("ANTHROPIC_MODEL") + .and_then(|v| v.as_str()) + .map(|s| s.to_string()); + + let anthropic_small_fast_model = env + .get("ANTHROPIC_SMALL_FAST_MODEL") + .and_then(|v| v.as_str()) + .map(|s| s.to_string()); + ExternalBedrockConfig { profile, region, models, auth_refresh_command, + disable_prompt_caching, + enable_prompt_caching_1h, + anthropic_model, + anthropic_small_fast_model, } } @@ -234,6 +268,10 @@ fn parse_opencode_config(path: PathBuf) -> ExternalBedrockConfig { region, models: Vec::new(), auth_refresh_command: None, + disable_prompt_caching: false, + enable_prompt_caching_1h: false, + anthropic_model: None, + anthropic_small_fast_model: None, } } diff --git a/app/src/ai/bedrock/external_config_tests.rs b/app/src/ai/bedrock/external_config_tests.rs index 355e2850..9dc55131 100644 --- a/app/src/ai/bedrock/external_config_tests.rs +++ b/app/src/ai/bedrock/external_config_tests.rs @@ -48,6 +48,73 @@ fn test_parse_claude_code_config_extracts_model_map() { assert!(config.models[0].vision_supported); } +#[test] +fn test_parse_claude_code_config_extracts_caching_flags() { + let mut file = NamedTempFile::new().unwrap(); + write!( + file, + r#"{{ + "env": {{ + "AWS_PROFILE": "test", + "AWS_REGION": "us-east-1", + "DISABLE_PROMPT_CACHING": "1", + "ENABLE_PROMPT_CACHING_1H": "1" + }} + }}"# + ) + .unwrap(); + + let config = parse_claude_code_config(file.path().to_path_buf()); + assert!(config.disable_prompt_caching); + assert!(config.enable_prompt_caching_1h); +} + +#[test] +fn test_parse_claude_code_config_caching_flags_default_false() { + let mut file = NamedTempFile::new().unwrap(); + write!( + file, + r#"{{ + "env": {{ + "AWS_PROFILE": "test", + "AWS_REGION": "us-east-1" + }} + }}"# + ) + .unwrap(); + + let config = parse_claude_code_config(file.path().to_path_buf()); + assert!(!config.disable_prompt_caching); + assert!(!config.enable_prompt_caching_1h); +} + +#[test] +fn test_parse_claude_code_config_extracts_model_env_vars() { + let mut file = NamedTempFile::new().unwrap(); + write!( + file, + r#"{{ + "env": {{ + "AWS_PROFILE": "test", + "AWS_REGION": "us-east-1", + "ANTHROPIC_MODEL": "us.anthropic.claude-sonnet-4-5-20250929-v1:0", + "ANTHROPIC_SMALL_FAST_MODEL": "us.anthropic.claude-haiku-4-5-20251001-v1:0" + }} + }}"# + ) + .unwrap(); + + let config = parse_claude_code_config(file.path().to_path_buf()); + assert_eq!( + config.anthropic_model, + Some("us.anthropic.claude-sonnet-4-5-20250929-v1:0".to_string()) + ); + assert_eq!( + config.anthropic_small_fast_model, + Some("us.anthropic.claude-haiku-4-5-20251001-v1:0".to_string()) + ); +} + #[test] fn test_parse_claude_code_config_missing_file() { let config = parse_claude_code_config(PathBuf::from("/nonexistent/path/settings.json")); diff --git a/app/src/ai/bedrock/mod.rs b/app/src/ai/bedrock/mod.rs index 546070f6..885614b9 100644 --- a/app/src/ai/bedrock/mod.rs +++ b/app/src/ai/bedrock/mod.rs @@ -6,6 +6,7 @@ pub mod external_config; pub mod models; pub mod request_translator; pub mod response_translator; +pub mod settings_view; pub mod translator; #[cfg(test)] diff --git a/app/src/ai/bedrock/settings_view.rs b/app/src/ai/bedrock/settings_view.rs new file mode 100644 index 00000000..fa01f031 --- /dev/null +++ b/app/src/ai/bedrock/settings_view.rs @@ -0,0 +1,200 @@ +use crate::ai::bedrock::convert::CachingConfig; +use crate::ai::bedrock::external_config::ExternalBedrockConfig; +use crate::appearance::Appearance; +use crate::ui_components::blended_colors; +use galaxyui::{ + elements::{ + Container, CornerRadius, CrossAxisAlignment, Flex, ParentElement, Radius, Text, + }, + AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext, +}; + +pub struct SettingsView { + external_config: ExternalBedrockConfig, + caching_config: CachingConfig, +} + +impl SettingsView { + pub fn new() -> Self { + let external_config = ExternalBedrockConfig::load(); + let caching_config = CachingConfig::from_external_config(&external_config); + Self { + external_config, + caching_config, + } + } +} + +impl View for SettingsView { + fn ui_name() -> &'static str { + "SettingsView" + } + + fn render(&self, app: &AppContext) -> Box { + let appearance = Appearance::as_ref(app); + let theme = appearance.theme(); + let font_size = appearance.ui_font_size(); + let text_color = blended_colors::text_main(theme, theme.surface_2()); + let label_color = blended_colors::text_sub(theme, theme.surface_2()); + + let mut column = Flex::column() + .with_cross_axis_alignment(CrossAxisAlignment::Stretch) + .with_spacing(8.0); + + // Header + column = column.with_child( + Text::new( + "Galaxy Bedrock Settings", + appearance.ui_font_family(), + font_size + 2.0, + ) + .with_color(text_color) + .finish(), + ); + + // Separator + column = column.with_child( + Text::new( + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", + appearance.ui_font_family(), + font_size, + ) + .with_color(label_color) + .finish(), + ); + + // AWS Configuration + if let Some(profile) = &self.external_config.profile { + column = column.with_child( + Text::new( + format!("AWS Profile: {}", profile), + appearance.ui_font_family(), + font_size, + ) + .with_color(text_color) + .finish(), + ); + } + + if let Some(region) = &self.external_config.region { + column = column.with_child( + Text::new( + format!("AWS Region: {}", region), + appearance.ui_font_family(), + font_size, + ) + .with_color(text_color) + .finish(), + ); + } + + // Model Configuration + if let Some(model) = &self.external_config.anthropic_model { + column = column.with_child( + Text::new( + format!("Primary Model: {}", model), + appearance.ui_font_family(), + font_size, + ) + .with_color(text_color) + .soft_wrap(true) + .finish(), + ); + } + + if let Some(model) = &self.external_config.anthropic_small_fast_model { + column = column.with_child( + Text::new( + format!("Small/Fast Model: {}", model), + appearance.ui_font_family(), + font_size, + ) + .with_color(text_color) + .soft_wrap(true) + .finish(), + ); + } + + // Caching Configuration + let caching_status = if self.caching_config.enabled { + "✓ Enabled" + } else { + "✗ Disabled" + }; + column = column.with_child( + Text::new( + format!("Prompt Caching: {}", caching_status), + appearance.ui_font_family(), + font_size, + ) + .with_color(text_color) + .finish(), + ); + + if self.caching_config.extended_ttl_requested { + column = column.with_child( + Text::new( + "Extended 1h TTL: ✓ Requested", + appearance.ui_font_family(), + font_size, + ) + .with_color(text_color) + .finish(), + ); + } + + // Additional Models + if !self.external_config.models.is_empty() { + column = column.with_child( + Text::new( + format!("\nAdditional models: {}", self.external_config.models.len()), + appearance.ui_font_family(), + font_size, + ) + .with_color(label_color) + .finish(), + ); + } + + // Auth Refresh Command + if let Some(cmd) = &self.external_config.auth_refresh_command { + column = column.with_child( + Text::new( + format!("\nAuth Refresh: {}", cmd), + appearance.ui_font_family(), + font_size - 1.0, + ) + .with_color(label_color) + .soft_wrap(true) + .finish(), + ); + } + + // Help text + column = column.with_child( + Text::new( + "\nSettings loaded from ~/.claude/settings.json", + appearance.ui_font_family(), + font_size - 1.0, + ) + .with_color(label_color) + .finish(), + ); + + Container::new(column.finish()) + .with_uniform_padding(12.0) + .with_background(theme.surface_2()) + .with_corner_radius(CornerRadius::with_all(Radius::Pixels(8.0))) + .finish() + } +} + +impl Entity for SettingsView { + type Event = (); +} + +impl TypedActionView for SettingsView { + type Action = (); + + fn handle_action(&mut self, _action: &Self::Action, _ctx: &mut ViewContext) {} +} diff --git a/app/src/search/slash_command_menu/static_commands/commands.rs b/app/src/search/slash_command_menu/static_commands/commands.rs index 86c4c53c..38c6e622 100644 --- a/app/src/search/slash_command_menu/static_commands/commands.rs +++ b/app/src/search/slash_command_menu/static_commands/commands.rs @@ -396,6 +396,15 @@ pub const CONTEXT: StaticCommand = StaticCommand { argument: None, }; +pub const SETTINGS: StaticCommand = StaticCommand { + name: "/settings", + description: "Show Galaxy configuration (Bedrock, models, caching)", + icon_path: "bundled/svg/settings-06.svg", + availability: Availability::ALWAYS, + auto_enter_ai_mode: false, + argument: None, +}; + pub const CONVERSATIONS: StaticCommand = StaticCommand { name: "/conversations", description: "Open conversation history", @@ -531,6 +540,7 @@ fn all_commands() -> Vec { CONVERSATIONS, EXPORT_TO_CLIPBOARD, MODEL.clone(), + SETTINGS, ]; if FeatureFlag::LocalDockerSandbox.is_enabled() { diff --git a/app/src/terminal/input/slash_commands/mod.rs b/app/src/terminal/input/slash_commands/mod.rs index a375e98c..55b34078 100644 --- a/app/src/terminal/input/slash_commands/mod.rs +++ b/app/src/terminal/input/slash_commands/mod.rs @@ -712,6 +712,9 @@ impl Input { ctx.dispatch_typed_action(&TerminalAction::ToggleContextView); } } + _settings if command.name == commands::SETTINGS.name => { + ctx.dispatch_typed_action(&TerminalAction::ToggleSettingsView); + } _fork if command.name == commands::FORK.name => { let Some(conversation_id) = self .ai_context_model diff --git a/app/src/terminal/view.rs b/app/src/terminal/view.rs index 19da5ad9..a604e480 100644 --- a/app/src/terminal/view.rs +++ b/app/src/terminal/view.rs @@ -2589,6 +2589,9 @@ pub struct TerminalView { /// View ID of the context window debug view, if visible. context_view_id: Option, + /// View ID of the settings view, if visible. + settings_view_id: Option, + // Whether the block onboarding view is active or not. block_onboarding_active: bool, @@ -4093,6 +4096,7 @@ impl TerminalView { rich_content_views: Vec::new(), usage_footer_view_ids: Default::default(), context_view_id: None, + settings_view_id: None, block_onboarding_active: false, onboarding_agentic_suggestions_block: None, onboarding_prompt_block: None, @@ -5792,6 +5796,36 @@ impl TerminalView { ctx.notify(); } + fn toggle_settings_view(&mut self, ctx: &mut ViewContext) { + use crate::ai::bedrock::settings_view::SettingsView; + + // If already showing, remove it + if let Some(view_id) = self.settings_view_id.take() { + let mut model = self.model.lock(); + model.block_list_mut().remove_rich_content(view_id); + drop(model); + self.rich_content_views.retain(|rc| rc.view_id() != view_id); + ctx.notify(); + return; + } + + // Create and insert the settings view + let settings_view = ctx.add_view(|_| SettingsView::new()); + let view_id = settings_view.id(); + self.settings_view_id = Some(view_id); + + self.insert_rich_content( + None, + settings_view, + None, + RichContentInsertionPosition::Append { + insert_below_long_running_block: true, + }, + ctx, + ); + ctx.notify(); + } + /// Returns true if the window is wide enough to auto-open side panels. pub fn can_auto_open_panel(&self) -> bool { self.size_info.pane_width_px().as_f32() > MINIMUM_WIDTH_TO_AUTO_OPEN_PANE @@ -24532,6 +24566,7 @@ impl TypedActionView for TerminalView { | ExecuteRewindFromInlineMenu { .. } | ToggleUsageFooter | ToggleContextView + | ToggleSettingsView | RevealChildAgent { .. } | OpenCLIAgentRichInput | ToggleSessionRecording => Empty, @@ -25556,6 +25591,9 @@ impl TypedActionView for TerminalView { ToggleContextView => { self.toggle_context_view(ctx); } + ToggleSettingsView => { + self.toggle_settings_view(ctx); + } RevealChildAgent { conversation_id } => { ctx.emit(Event::RevealChildAgent { conversation_id: *conversation_id, diff --git a/app/src/terminal/view/action.rs b/app/src/terminal/view/action.rs index 9f4e949c..db3a685e 100644 --- a/app/src/terminal/view/action.rs +++ b/app/src/terminal/view/action.rs @@ -424,6 +424,8 @@ pub enum TerminalAction { ToggleUsageFooter, /// Toggle the context window debug view showing bedrock_message_history. ToggleContextView, + /// Toggle the settings view showing Galaxy Bedrock configuration. + ToggleSettingsView, /// Reveal a hidden child agent pane from the orchestrator status card. RevealChildAgent { conversation_id: AIConversationId, @@ -705,6 +707,7 @@ impl fmt::Debug for TerminalAction { AwsCliNotInstalledBanner(action) => write!(f, "AwsCliNotInstalledBanner({action:?})"), ToggleUsageFooter => write!(f, "ToggleUsageFooter"), ToggleContextView => write!(f, "ToggleContextView"), + ToggleSettingsView => write!(f, "ToggleSettingsView"), RevealChildAgent { .. } => write!(f, "RevealChildAgent"), ToggleSessionRecording => write!(f, "ToggleSessionRecording"), OpenCLIAgentRichInput => write!(f, "OpenCLIAgentRichInput"), From 3762e861852b9219b34bb963f16926c34d67b98f Mon Sep 17 00:00:00 2001 From: Josh Woodcock Date: Mon, 1 Jun 2026 15:13:14 -0500 Subject: [PATCH 2/2] add missing ttl setting and filter dropdown models when 1hr ttl is set in settings.json --- Cargo.lock | 497 ++++++++++++++++++---------- app/Cargo.toml | 2 +- app/src/ai/bedrock/convert.rs | 42 +-- app/src/ai/bedrock/settings_view.rs | 74 ++++- app/src/ai/bedrock/translator.rs | 2 + app/src/ai/llms.rs | 83 ++++- 6 files changed, 485 insertions(+), 215 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f50c1118..7ab1e2a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -201,7 +201,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "crypto-common", + "crypto-common 0.1.6", "generic-array", ] @@ -213,7 +213,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.9", ] [[package]] @@ -257,7 +257,7 @@ dependencies = [ "cmac", "ctr", "dbl", - "digest", + "digest 0.10.7", "zeroize", ] @@ -342,7 +342,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml", - "sha2", + "sha2 0.10.9", "shellexpand", "streaming-iterator", "string-offset", @@ -1153,7 +1153,7 @@ dependencies = [ "galaxy_util", "proc-macro2", "quote", - "sha2", + "sha2 0.10.9", "syn 2.0.117", ] @@ -1484,8 +1484,8 @@ dependencies = [ "aws-sdk-ssooidc", "aws-sdk-sts", "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-http 0.62.6", + "aws-smithy-json 0.61.9", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -1494,11 +1494,11 @@ dependencies = [ "bytes", "fastrand 2.3.0", "hex", - "http 1.1.0", + "http 1.4.1", "p256", "rand 0.8.5", "ring", - "sha2", + "sha2 0.10.9", "time", "tokio", "tracing", @@ -1509,9 +1509,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.11" +version = "1.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd362783681b15d136480ad555a099e82ecd8e2d10a841e14dfd0078d67fee3" +checksum = "8f20799b373a1be121fe3005fba0c2090af9411573878f224df44b42727fcaf7" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -1543,23 +1543,24 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.5.17" +version = "1.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d81b5b2898f6798ad58f484856768bca817e3cd9de0974c24ae0f1113fe88f1b" +checksum = "77ed8e8c52d2dc2390ad9f15647fe663f71e9780b4262c190fbb823a32721566" dependencies = [ "aws-credential-types", "aws-sigv4", "aws-smithy-async", "aws-smithy-eventstream", - "aws-smithy-http", + "aws-smithy-http 0.63.6", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", "aws-types", "bytes", + "bytes-utils", "fastrand 2.3.0", - "http 0.2.12", - "http-body 0.4.6", + "http 1.4.1", + "http-body 1.0.1", "percent-encoding", "pin-project-lite", "tracing", @@ -1575,8 +1576,8 @@ dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-http 0.62.6", + "aws-smithy-json 0.61.9", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -1590,17 +1591,19 @@ dependencies = [ [[package]] name = "aws-sdk-bedrockruntime" -version = "1.120.0" +version = "1.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b8dcf42378ab2d5accac1652cdd059114fb071baf53250ceafb76fcdde347f" +checksum = "41a2940faeb61f4f579a434bc3a546e9ab49a89596e94527d329281ef55fd44d" dependencies = [ + "arc-swap", "aws-credential-types", "aws-runtime", "aws-sigv4", "aws-smithy-async", "aws-smithy-eventstream", - "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-http 0.63.6", + "aws-smithy-json 0.62.7", + "aws-smithy-observability", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -1608,7 +1611,8 @@ dependencies = [ "bytes", "fastrand 2.3.0", "http 0.2.12", - "hyper 0.14.32", + "http 1.4.1", + "http-body-util", "regex-lite", "tracing", ] @@ -1622,8 +1626,8 @@ dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-http 0.62.6", + "aws-smithy-json 0.61.9", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -1644,8 +1648,8 @@ dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-http 0.62.6", + "aws-smithy-json 0.61.9", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -1666,8 +1670,8 @@ dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-http 0.62.6", + "aws-smithy-json 0.61.9", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -1688,8 +1692,8 @@ dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", - "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-http 0.62.6", + "aws-smithy-json 0.61.9", "aws-smithy-query", "aws-smithy-runtime", "aws-smithy-runtime-api", @@ -1704,32 +1708,32 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.3.7" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e523e1c4e8e7e8ff219d732988e22bfeae8a1cafdbe6d9eca1546fa080be7c" +checksum = "b7083fb918b38474ac65ffbf8a69fc8792d36879f4ac5f1667b43aec61efe9a5" dependencies = [ "aws-credential-types", "aws-smithy-eventstream", - "aws-smithy-http", + "aws-smithy-http 0.63.6", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", "form_urlencoded", "hex", - "hmac", + "hmac 0.13.0", "http 0.2.12", - "http 1.1.0", + "http 1.4.1", "percent-encoding", - "sha2", + "sha2 0.11.0", "time", "tracing", ] [[package]] name = "aws-smithy-async" -version = "1.2.7" +version = "1.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee19095c7c4dda59f1697d028ce704c24b2d33c6718790c7f1d5a3015b4107c" +checksum = "2ffcaf626bdda484571968400c326a244598634dc75fd451325a54ad1a59acfc" dependencies = [ "futures-util", "pin-project-lite", @@ -1738,9 +1742,9 @@ dependencies = [ [[package]] name = "aws-smithy-eventstream" -version = "0.60.14" +version = "0.60.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc12f8b310e38cad85cf3bef45ad236f470717393c613266ce0a89512286b650" +checksum = "faf09d74e5e32f76b8762da505a3cd59303e367a664ca67295387baa8c1d7548" dependencies = [ "aws-smithy-types", "bytes", @@ -1753,7 +1757,6 @@ version = "0.62.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826141069295752372f8203c17f28e30c464d22899a43a0c9fd9c458d469c88b" dependencies = [ - "aws-smithy-eventstream", "aws-smithy-runtime-api", "aws-smithy-types", "bytes", @@ -1761,7 +1764,7 @@ dependencies = [ "futures-core", "futures-util", "http 0.2.12", - "http 1.1.0", + "http 1.4.1", "http-body 0.4.6", "percent-encoding", "pin-project-lite", @@ -1770,10 +1773,32 @@ dependencies = [ ] [[package]] -name = "aws-smithy-http-client" -version = "1.1.5" +name = "aws-smithy-http" +version = "0.63.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e62db736db19c488966c8d787f52e6270be565727236fd5579eaa301e7bc4a" +checksum = "ba1ab2dc1c2c3749ead27180d333c42f11be8b0e934058fb4b2258ee8dbe5231" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "futures-util", + "http 1.4.1", + "http-body 1.0.1", + "http-body-util", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-http-client" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3ef8931ad1c98aa6a55b4256f847f3116090819844e0dd41ea682cac5dd2d3" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -1781,7 +1806,7 @@ dependencies = [ "h2 0.3.27", "h2 0.4.12", "http 0.2.12", - "http 1.1.0", + "http 1.4.1", "http-body 0.4.6", "hyper 0.14.32", "hyper 1.8.1", @@ -1809,10 +1834,21 @@ dependencies = [ ] [[package]] -name = "aws-smithy-observability" -version = "0.2.0" +name = "aws-smithy-json" +version = "0.62.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1fcbefc7ece1d70dcce29e490f269695dfca2d2bacdeaf9e5c3f799e4e6a42" +checksum = "701a947f4797e52a911e114a898667c746c39feea467bbd1abd7b3721f702ffa" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-observability" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06c2315d173edbf1920da8ba3a7189695827002e4c0fc961973ab1c54abca9c" dependencies = [ "aws-smithy-runtime-api", ] @@ -1829,22 +1865,24 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.9.8" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb5b6167fcdf47399024e81ac08e795180c576a20e4d4ce67949f9a88ae37dc1" +checksum = "b8e6f5caf6fea86f8c2206541ab5857cfcda9013426cdbe8fa0098b9e2d32182" dependencies = [ "aws-smithy-async", - "aws-smithy-http", + "aws-smithy-http 0.63.6", "aws-smithy-http-client", "aws-smithy-observability", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "bytes", "fastrand 2.3.0", "http 0.2.12", - "http 1.1.0", + "http 1.4.1", "http-body 0.4.6", - "http-body 1.0.0", + "http-body 1.0.1", + "http-body-util", "pin-project-lite", "pin-utils", "tokio", @@ -1853,15 +1891,16 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.10.0" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efce7aaaf59ad53c5412f14fc19b2d5c6ab2c3ec688d272fd31f76ec12f44fb0" +checksum = "9db177daa6ba8afb9ee1aefcf548c907abcf52065e394ee11a92780057fe0e8c" dependencies = [ "aws-smithy-async", + "aws-smithy-runtime-api-macros", "aws-smithy-types", "bytes", "http 0.2.12", - "http 1.1.0", + "http 1.4.1", "pin-project-lite", "tokio", "tracing", @@ -1869,19 +1908,41 @@ dependencies = [ ] [[package]] -name = "aws-smithy-types" -version = "1.3.6" +name = "aws-smithy-runtime-api-macros" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65f172bcb02424eb94425db8aed1b6d583b5104d4d5ddddf22402c661a320048" +checksum = "8d7396fd9500589e62e460e987ecb671bad374934e55ec3b5f498cc7a8a8a7b7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "aws-smithy-schema" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7442cb268338f0eb8278140a107c046756aa01093d8ef5e99628d34ae09c94f5" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "http 1.4.1", +] + +[[package]] +name = "aws-smithy-types" +version = "1.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53f93074121a1be41317b9aa607143ae17900631f7f59a99f2b905d519d6783b" dependencies = [ "base64-simd", "bytes", "bytes-utils", "futures-core", "http 0.2.12", - "http 1.1.0", + "http 1.4.1", "http-body 0.4.6", - "http-body 1.0.0", + "http-body 1.0.1", "http-body-util", "itoa", "num-integer", @@ -1905,13 +1966,14 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.3.11" +version = "1.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d980627d2dd7bfc32a3c025685a033eeab8d365cc840c631ef59d1b8f428164" +checksum = "d16bf10b03a3c01e6b3b7d47cd964e873ffe9e7d4e80fad16bd4c077cb068531" dependencies = [ "aws-credential-types", "aws-smithy-async", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "rustc_version", "tracing", @@ -1927,8 +1989,8 @@ dependencies = [ "bytes", "form_urlencoded", "futures-util", - "http 1.1.0", - "http-body 1.0.0", + "http 1.4.1", + "http-body 1.0.1", "http-body-util", "hyper 1.8.1", "hyper-util", @@ -1959,8 +2021,8 @@ checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" dependencies = [ "bytes", "futures-core", - "http 1.1.0", - "http-body 1.0.0", + "http 1.4.1", + "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", @@ -1981,8 +2043,8 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http 1.1.0", - "http-body 1.0.0", + "http 1.4.1", + "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", @@ -2233,6 +2295,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + [[package]] name = "block-padding" version = "0.3.3" @@ -2677,7 +2748,7 @@ checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.9", ] [[package]] @@ -2755,7 +2826,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "crypto-common", + "crypto-common 0.1.6", "inout", "zeroize", ] @@ -2837,7 +2908,7 @@ checksum = "8543454e3c3f5126effff9cd44d562af4e31fb8ce1cc0d3dcd8f084515dbc1aa" dependencies = [ "cipher", "dbl", - "digest", + "digest 0.10.7", ] [[package]] @@ -2849,6 +2920,12 @@ dependencies = [ "cc", ] +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + [[package]] name = "cocoa" version = "0.25.0" @@ -3125,6 +3202,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + [[package]] name = "constant_time_eq" version = "0.3.1" @@ -3377,6 +3460,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crash-context" version = "0.6.3" @@ -3557,6 +3649,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + [[package]] name = "cstr" version = "0.2.11" @@ -3617,6 +3718,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + [[package]] name = "cursor-icon" version = "1.1.0" @@ -3630,7 +3740,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.9", "curve25519-dalek-derive", "fiat-crypto", "rustc_version", @@ -3875,7 +3985,7 @@ version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ - "const-oid", + "const-oid 0.9.6", "pem-rfc7468", "zeroize", ] @@ -4083,12 +4193,24 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", + "block-buffer 0.10.4", + "const-oid 0.9.6", + "crypto-common 0.1.6", "subtle", ] +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.0", + "const-oid 0.10.2", + "crypto-common 0.2.2", + "ctutils", +] + [[package]] name = "directories" version = "6.0.0" @@ -4294,7 +4416,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", - "digest", + "digest 0.10.7", "elliptic-curve", "rfc6979", "signature", @@ -4315,7 +4437,7 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest", + "digest 0.10.7", "ff", "generic-array", "group", @@ -5305,7 +5427,7 @@ dependencies = [ "gloo", "handlebars", "hex", - "http 1.1.0", + "http 1.4.1", "http_client", "http_server", "hyper 1.8.1", @@ -5401,7 +5523,7 @@ dependencies = [ "session-sharing-protocol", "settings", "settings_value", - "sha2", + "sha2 0.10.9", "shell-words", "shellexpand", "shlex", @@ -5545,7 +5667,7 @@ dependencies = [ "galaxyui", "galaxyui_extras", "getset", - "http 1.1.0", + "http 1.4.1", "instant", "inventory", "itertools 0.14.0", @@ -5669,7 +5791,7 @@ dependencies = [ "galaxy_core", "galaxy_graphql_schema", "graphql-ws-client", - "http 1.1.0", + "http 1.4.1", "http_client", "instant", "log", @@ -6071,7 +6193,7 @@ dependencies = [ "security-framework 2.9.2", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "tempfile", "thiserror 2.0.17", "toml_edit 0.25.6+spec-1.1.0", @@ -6719,7 +6841,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.1.0", + "http 1.4.1", "indexmap 2.12.0", "slab", "tokio", @@ -6864,7 +6986,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "hmac", + "hmac 0.12.1", ] [[package]] @@ -6873,7 +6995,16 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", +] + +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", ] [[package]] @@ -6911,12 +7042,12 @@ dependencies = [ "aead", "aes-gcm", "chacha20poly1305", - "digest", + "digest 0.10.7", "generic-array", "hkdf", - "hmac", + "hmac 0.12.1", "rand_core 0.9.3", - "sha2", + "sha2 0.10.9", "subtle", "x25519-dalek", "zeroize", @@ -6961,12 +7092,11 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -6983,12 +7113,12 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http 1.4.1", ] [[package]] @@ -6999,8 +7129,8 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.1.0", - "http-body 1.0.0", + "http 1.4.1", + "http-body 1.0.1", "pin-project-lite", ] @@ -7022,7 +7152,7 @@ dependencies = [ "futures", "galaxy_core", "gloo", - "http 1.1.0", + "http 1.4.1", "log", "oauth2", "prevent_sleep", @@ -7065,6 +7195,15 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" version = "0.14.32" @@ -7100,8 +7239,8 @@ dependencies = [ "futures-channel", "futures-core", "h2 0.4.12", - "http 1.1.0", - "http-body 1.0.0", + "http 1.4.1", + "http-body 1.0.1", "httparse", "httpdate", "itoa", @@ -7133,7 +7272,7 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.1.0", + "http 1.4.1", "hyper 1.8.1", "hyper-util", "rustls 0.23.39", @@ -7156,8 +7295,8 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "http 1.1.0", - "http-body 1.0.0", + "http 1.4.1", + "http-body 1.0.1", "hyper 1.8.1", "ipnet", "libc", @@ -7646,17 +7785,6 @@ dependencies = [ "rustversion", ] -[[package]] -name = "io-uring" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "libc", -] - [[package]] name = "ipc" version = "0.1.0" @@ -7776,9 +7904,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jaq-all" @@ -8422,7 +8550,7 @@ dependencies = [ "repo_metadata", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "simple_logger", "strum", "strum_macros", @@ -8929,8 +9057,8 @@ dependencies = [ "bytes", "colored", "futures-core", - "http 1.1.0", - "http-body 1.0.0", + "http 1.4.1", + "http-body 1.0.1", "http-body-util", "hyper 1.8.1", "hyper-util", @@ -9176,7 +9304,7 @@ dependencies = [ "semver", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "tar", "zip 2.4.2", ] @@ -9427,13 +9555,13 @@ dependencies = [ "base64 0.22.1", "chrono", "getrandom 0.2.16", - "http 1.1.0", + "http 1.4.1", "rand 0.8.5", "reqwest", "serde", "serde_json", "serde_path_to_error", - "sha2", + "sha2 0.10.9", "thiserror 1.0.63", "url", ] @@ -10058,7 +10186,7 @@ checksum = "e2aba9f5c7c479925205799216e7e5d07cc1d4fa76ea8058c60a9a30f6a4e890" dependencies = [ "flate2", "pkg-config", - "sha2", + "sha2 0.10.9", "tar", "ureq", ] @@ -10142,7 +10270,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -10254,8 +10382,8 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "digest", - "hmac", + "digest 0.10.7", + "hmac 0.12.1", ] [[package]] @@ -10511,7 +10639,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ - "cpufeatures", + "cpufeatures 0.2.9", "opaque-debug", "universal-hash", ] @@ -10523,7 +10651,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.9", "opaque-debug", "universal-hash", ] @@ -10750,9 +10878,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -11592,8 +11720,8 @@ dependencies = [ "futures-core", "futures-util", "h2 0.4.12", - "http 1.1.0", - "http-body 1.0.0", + "http 1.4.1", + "http-body 1.0.1", "http-body-util", "hyper 1.8.1", "hyper-rustls 0.27.7", @@ -11664,7 +11792,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "hmac", + "hmac 0.12.1", "subtle", ] @@ -11729,7 +11857,7 @@ dependencies = [ "base64 0.22.1", "chrono", "futures", - "http 1.1.0", + "http 1.4.1", "oauth2", "pastey 0.2.1", "pin-project-lite", @@ -11883,7 +12011,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d38ff6bf570dc3bb7100fce9f7b60c33fa71d80e88da3f2580df4ff2bdded74" dependencies = [ "globset", - "sha2", + "sha2 0.10.9", "walkdir", ] @@ -11894,7 +12022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594" dependencies = [ "globset", - "sha2", + "sha2 0.10.9", "walkdir", ] @@ -12126,9 +12254,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "safe_arch" @@ -12301,7 +12429,7 @@ dependencies = [ "num", "once_cell", "serde", - "sha2", + "sha2 0.10.9", "zbus", ] @@ -12799,8 +12927,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.9", + "digest 0.10.7", ] [[package]] @@ -12810,8 +12938,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.9", + "digest 0.10.7", ] [[package]] @@ -12821,8 +12949,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.9", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -12891,7 +13030,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", + "digest 0.10.7", "rand_core 0.6.4", ] @@ -13165,7 +13304,7 @@ checksum = "eb4dc4d33c68ec1f27d386b5610a351922656e1fdf5c05bbaad930cd1519479a" dependencies = [ "bytes", "futures-util", - "http-body 1.0.0", + "http-body 1.0.1", "http-body-util", "pin-project-lite", ] @@ -13890,14 +14029,14 @@ name = "tink-core" version = "0.3.0" source = "git+https://github.com/warpdotdev/tink-rust?branch=warpdotdev%2Fmain#0141035f04a5e262b955c450857b689cff877469" dependencies = [ - "digest", + "digest 0.10.7", "hkdf", "lazy_static", "rand 0.8.5", "serde", "serde_json", "sha-1", - "sha2", + "sha2 0.10.9", "subtle", "tink-proto", ] @@ -13945,11 +14084,11 @@ checksum = "78d1cabf040b08759a32d8b2a830c707b7c578fc92f68a3eeca4231cd6dfca33" dependencies = [ "aes", "cmac", - "digest", + "digest 0.10.7", "hkdf", - "hmac", + "hmac 0.12.1", "sha-1", - "sha2", + "sha2 0.10.9", "tink-core", "tink-proto", ] @@ -14103,29 +14242,26 @@ dependencies = [ [[package]] name = "tokio" -version = "1.47.1" +version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", "socket2 0.6.0", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" dependencies = [ "proc-macro2", "quote", @@ -14165,9 +14301,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.9" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -14175,7 +14311,6 @@ dependencies = [ "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -14347,8 +14482,8 @@ dependencies = [ "bytes", "futures-core", "futures-util", - "http 1.1.0", - "http-body 1.0.0", + "http 1.4.1", + "http-body 1.0.1", "http-body-util", "http-range-header", "httpdate", @@ -14379,9 +14514,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -14391,9 +14526,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -14402,9 +14537,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -14501,7 +14636,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.1.0", + "http 1.4.1", "httparse", "log", "rand 0.8.5", @@ -14544,9 +14679,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.17.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "typetag" @@ -14744,7 +14879,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "crypto-common", + "crypto-common 0.1.6", "subtle", ] @@ -14782,7 +14917,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59db78ad1923f2b1be62b6da81fe80b173605ca0d57f85da2e005382adf693f7" dependencies = [ "base64 0.22.1", - "http 1.1.0", + "http 1.4.1", "httparse", "log", ] @@ -15473,7 +15608,7 @@ dependencies = [ "futures-test-sink", "futures-util", "graphql-ws-client", - "http 1.1.0", + "http 1.4.1", "http-body-util", "hyper 1.8.1", "hyper-util", @@ -16701,7 +16836,7 @@ dependencies = [ "displaydoc", "flate2", "getrandom 0.3.4", - "hmac", + "hmac 0.12.1", "indexmap 2.12.0", "lzma-rs", "memchr", diff --git a/app/Cargo.toml b/app/Cargo.toml index 841378ab..f30b37b2 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -288,7 +288,7 @@ tokio-util.workspace = true aws-config = { version = "1.8.12", features = ["credentials-login"] } aws-credential-types = "1" aws-sdk-bedrock = "1" -aws-sdk-bedrockruntime = "1" +aws-sdk-bedrockruntime = "1.132" aws-sdk-sts = "1" aws-smithy-types = "1" aws-types = "1" diff --git a/app/src/ai/bedrock/convert.rs b/app/src/ai/bedrock/convert.rs index 9fe2e1a3..80ff8168 100644 --- a/app/src/ai/bedrock/convert.rs +++ b/app/src/ai/bedrock/convert.rs @@ -1,9 +1,10 @@ use std::collections::HashMap; use aws_sdk_bedrockruntime::types::{ - CachePointBlock, CachePointType, ContentBlock, ConversationRole, InferenceConfiguration, - Message as BedrockMessage, SystemContentBlock, Tool, ToolConfiguration, ToolInputSchema, - ToolResultBlock, ToolResultContentBlock, ToolResultStatus, ToolSpecification, ToolUseBlock, + CachePointBlock, CachePointType, CacheTtl, ContentBlock, ConversationRole, + InferenceConfiguration, Message as BedrockMessage, SystemContentBlock, Tool, + ToolConfiguration, ToolInputSchema, ToolResultBlock, ToolResultContentBlock, + ToolResultStatus, ToolSpecification, ToolUseBlock, }; use aws_smithy_types::Document; use serde_json::Value as JsonValue; @@ -244,11 +245,15 @@ fn convert_messages(messages: Vec, caching_config: &Caching let cache_idx = messages.len() - 2; let msg = messages.remove(cache_idx); let mut content = msg.content().to_vec(); + + let mut builder = CachePointBlock::builder().r#type(CachePointType::Default); + if caching_config.extended_ttl_requested { + builder = builder.ttl(CacheTtl::OneHour); + log::info!("[bedrock] Using 1-hour cache TTL (ENABLE_PROMPT_CACHING_1H=1)"); + } + content.push(ContentBlock::CachePoint( - CachePointBlock::builder() - .r#type(CachePointType::Default) - .build() - .expect("valid cache point"), + builder.build().expect("valid cache point"), )); let cached_msg = BedrockMessage::builder() .role(msg.role().clone()) @@ -256,11 +261,6 @@ fn convert_messages(messages: Vec, caching_config: &Caching .build() .expect("valid message with cache point"); messages.insert(cache_idx, cached_msg); - - if caching_config.extended_ttl_requested { - log::info!("[bedrock] Extended 1-hour caching requested (ENABLE_PROMPT_CACHING_1H=1)"); - // TODO: Use explicit TTL when AWS SDK supports it - } } messages @@ -302,11 +302,12 @@ fn convert_system_prompt(system_prompt: Option, caching_config: &Caching Some(prompt) if !prompt.is_empty() => { let mut blocks = vec![SystemContentBlock::Text(prompt)]; if caching_config.enabled { + let mut builder = CachePointBlock::builder().r#type(CachePointType::Default); + if caching_config.extended_ttl_requested { + builder = builder.ttl(CacheTtl::OneHour); + } blocks.push(SystemContentBlock::CachePoint( - CachePointBlock::builder() - .r#type(CachePointType::Default) - .build() - .expect("valid cache point"), + builder.build().expect("valid cache point"), )); } blocks @@ -357,11 +358,12 @@ fn build_tool_config(tools: Vec, caching_config: &CachingConfig) .collect(); if caching_config.enabled { + let mut builder = CachePointBlock::builder().r#type(CachePointType::Default); + if caching_config.extended_ttl_requested { + builder = builder.ttl(CacheTtl::OneHour); + } tool_specs.push(Tool::CachePoint( - CachePointBlock::builder() - .r#type(CachePointType::Default) - .build() - .expect("valid cache point"), + builder.build().expect("valid cache point"), )); } diff --git a/app/src/ai/bedrock/settings_view.rs b/app/src/ai/bedrock/settings_view.rs index fa01f031..675222e5 100644 --- a/app/src/ai/bedrock/settings_view.rs +++ b/app/src/ai/bedrock/settings_view.rs @@ -89,10 +89,20 @@ impl View for SettingsView { } // Model Configuration + column = column.with_child( + Text::new( + "\n━━━ Model Selection ━━━", + appearance.ui_font_family(), + font_size - 1.0, + ) + .with_color(label_color) + .finish(), + ); + if let Some(model) = &self.external_config.anthropic_model { column = column.with_child( Text::new( - format!("Primary Model: {}", model), + format!("Default Model (ANTHROPIC_MODEL): {}", model), appearance.ui_font_family(), font_size, ) @@ -100,6 +110,35 @@ impl View for SettingsView { .soft_wrap(true) .finish(), ); + column = column.with_child( + Text::new( + "Sets the initial model in dropdown. You can change it.", + appearance.ui_font_family(), + font_size - 1.0, + ) + .with_color(label_color) + .soft_wrap(true) + .finish(), + ); + } else { + column = column.with_child( + Text::new( + "Default Model: Not set", + appearance.ui_font_family(), + font_size, + ) + .with_color(label_color) + .finish(), + ); + column = column.with_child( + Text::new( + "Set ANTHROPIC_MODEL in settings.json to set initial model", + appearance.ui_font_family(), + font_size - 1.0, + ) + .with_color(label_color) + .finish(), + ); } if let Some(model) = &self.external_config.anthropic_small_fast_model { @@ -134,13 +173,33 @@ impl View for SettingsView { if self.caching_config.extended_ttl_requested { column = column.with_child( Text::new( - "Extended 1h TTL: ✓ Requested", + "Extended 1h Cache TTL: ✓ Active", appearance.ui_font_family(), font_size, ) .with_color(text_color) .finish(), ); + column = column.with_child( + Text::new( + "All cache points use 1-hour TTL. Model list filtered to compatible models only.", + appearance.ui_font_family(), + font_size - 1.0, + ) + .with_color(label_color) + .soft_wrap(true) + .finish(), + ); + column = column.with_child( + Text::new( + "Compatible: Claude Opus 4.5, Sonnet 4.5, Haiku 4.5", + appearance.ui_font_family(), + font_size - 1.0, + ) + .with_color(label_color) + .soft_wrap(true) + .finish(), + ); } // Additional Models @@ -173,7 +232,16 @@ impl View for SettingsView { // Help text column = column.with_child( Text::new( - "\nSettings loaded from ~/.claude/settings.json", + "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", + appearance.ui_font_family(), + font_size, + ) + .with_color(label_color) + .finish(), + ); + column = column.with_child( + Text::new( + "Settings loaded from ~/.claude/settings.json", appearance.ui_font_family(), font_size - 1.0, ) diff --git a/app/src/ai/bedrock/translator.rs b/app/src/ai/bedrock/translator.rs index f3b729fb..9bd5095b 100644 --- a/app/src/ai/bedrock/translator.rs +++ b/app/src/ai/bedrock/translator.rs @@ -40,8 +40,10 @@ pub async fn execute( .map(|tc| tc.tasks.is_empty()) .unwrap_or(true); + // Use the model from params (selected in UI or defaulted from ANTHROPIC_MODEL) let mut model_id = params.model_id; if model_id.is_empty() || model_id == "auto" { + // Fall back to default if nothing is set model_id = "us.anthropic.claude-opus-4-6".to_string(); } diff --git a/app/src/ai/llms.rs b/app/src/ai/llms.rs index cc716a94..ab16763b 100644 --- a/app/src/ai/llms.rs +++ b/app/src/ai/llms.rs @@ -646,7 +646,42 @@ impl LLMPreferences { let region = settings.bedrock_region.value().clone(); let cross_region = *settings.bedrock_cross_region_inference.value(); - let effective = get_effective_models(&user_models); + // Check if user wants only 1-hour cache models + use crate::ai::bedrock::external_config::ExternalBedrockConfig; + let external_config = ExternalBedrockConfig::load(); + let require_1h_cache = external_config.enable_prompt_caching_1h; + + let mut effective = get_effective_models(&user_models); + + // Filter out models that don't support 1-hour caching if required + if require_1h_cache { + effective.retain(|model| { + // 1-hour caching is supported by Claude 4.5+ models + // Opus 4.5+, Sonnet 4.5+, Haiku 4.5+ + let supports_1h = model.model_id.contains("4-5") + || model.model_id.contains("4.5") + || model.model_id.contains("-4-6") // Opus/Sonnet 4.6+ also support 1h + || model.model_id.contains("4.6") + || model.model_id.contains("-4-7") + || model.model_id.contains("4.7") + || model.model_id.contains("-4-8") + || model.model_id.contains("4.8"); + + if !supports_1h { + log::info!( + "[bedrock] Filtering out model {} - does not support 1-hour cache (ENABLE_PROMPT_CACHING_1H=1)", + model.model_id + ); + } + supports_1h + }); + + if effective.is_empty() { + log::warn!("[bedrock] No models left after filtering for 1-hour cache support!"); + } + } + + let effective = effective; for model in effective { let model_id = if cross_region && !region.is_empty() { super::bedrock::models::apply_cross_region_prefix(&model.model_id, ®ion) @@ -700,15 +735,43 @@ impl LLMPreferences { cli.choices.retain(|m| m.provider != LLMProvider::Unknown); } - // Default agent mode to Claude Opus 4.6, falling back to the first available model. - if let Some(id) = self - .models_by_feature - .agent_mode - .choices - .iter() - .find(|m| m.display_name.contains("Opus 4.6")) - .or_else(|| self.models_by_feature.agent_mode.choices.first()) - .map(|m| m.id.clone()) + // Default agent mode to ANTHROPIC_MODEL from external config if set, + // otherwise Claude Opus 4.6, falling back to the first available model. + // Note: external_config already loaded above for filtering + let external_config_for_default = ExternalBedrockConfig::load(); + + if let Some(id) = external_config_for_default + .anthropic_model + .as_ref() + .and_then(|model_id| { + // Match by model_id (with or without cross-region prefix) + self.models_by_feature + .agent_mode + .choices + .iter() + .find(|m| { + m.id.as_str() == model_id + || m.id.as_str().ends_with(model_id) + || model_id.ends_with(m.id.as_str()) + }) + .map(|m| { + log::info!( + "[bedrock] Setting default agent mode model from ANTHROPIC_MODEL: {} -> {}", + model_id, + m.display_name + ); + m.id.clone() + }) + }) + .or_else(|| { + self.models_by_feature + .agent_mode + .choices + .iter() + .find(|m| m.display_name.contains("Opus 4.6")) + .or_else(|| self.models_by_feature.agent_mode.choices.first()) + .map(|m| m.id.clone()) + }) { self.models_by_feature.agent_mode.default_id = id; }