add missing ttl setting and filter dropdown models when 1hr ttl is set in settings.json
This commit is contained in:
Generated
+316
-181
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -288,7 +288,7 @@ tokio-util.workspace = true
|
|||||||
aws-config = { version = "1.8.12", features = ["credentials-login"] }
|
aws-config = { version = "1.8.12", features = ["credentials-login"] }
|
||||||
aws-credential-types = "1"
|
aws-credential-types = "1"
|
||||||
aws-sdk-bedrock = "1"
|
aws-sdk-bedrock = "1"
|
||||||
aws-sdk-bedrockruntime = "1"
|
aws-sdk-bedrockruntime = "1.132"
|
||||||
aws-sdk-sts = "1"
|
aws-sdk-sts = "1"
|
||||||
aws-smithy-types = "1"
|
aws-smithy-types = "1"
|
||||||
aws-types = "1"
|
aws-types = "1"
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use aws_sdk_bedrockruntime::types::{
|
use aws_sdk_bedrockruntime::types::{
|
||||||
CachePointBlock, CachePointType, ContentBlock, ConversationRole, InferenceConfiguration,
|
CachePointBlock, CachePointType, CacheTtl, ContentBlock, ConversationRole,
|
||||||
Message as BedrockMessage, SystemContentBlock, Tool, ToolConfiguration, ToolInputSchema,
|
InferenceConfiguration, Message as BedrockMessage, SystemContentBlock, Tool,
|
||||||
ToolResultBlock, ToolResultContentBlock, ToolResultStatus, ToolSpecification, ToolUseBlock,
|
ToolConfiguration, ToolInputSchema, ToolResultBlock, ToolResultContentBlock,
|
||||||
|
ToolResultStatus, ToolSpecification, ToolUseBlock,
|
||||||
};
|
};
|
||||||
use aws_smithy_types::Document;
|
use aws_smithy_types::Document;
|
||||||
use serde_json::Value as JsonValue;
|
use serde_json::Value as JsonValue;
|
||||||
@@ -244,11 +245,15 @@ fn convert_messages(messages: Vec<ConversationMessage>, caching_config: &Caching
|
|||||||
let cache_idx = messages.len() - 2;
|
let cache_idx = messages.len() - 2;
|
||||||
let msg = messages.remove(cache_idx);
|
let msg = messages.remove(cache_idx);
|
||||||
let mut content = msg.content().to_vec();
|
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(
|
content.push(ContentBlock::CachePoint(
|
||||||
CachePointBlock::builder()
|
builder.build().expect("valid cache point"),
|
||||||
.r#type(CachePointType::Default)
|
|
||||||
.build()
|
|
||||||
.expect("valid cache point"),
|
|
||||||
));
|
));
|
||||||
let cached_msg = BedrockMessage::builder()
|
let cached_msg = BedrockMessage::builder()
|
||||||
.role(msg.role().clone())
|
.role(msg.role().clone())
|
||||||
@@ -256,11 +261,6 @@ fn convert_messages(messages: Vec<ConversationMessage>, caching_config: &Caching
|
|||||||
.build()
|
.build()
|
||||||
.expect("valid message with cache point");
|
.expect("valid message with cache point");
|
||||||
messages.insert(cache_idx, cached_msg);
|
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
|
messages
|
||||||
@@ -302,11 +302,12 @@ fn convert_system_prompt(system_prompt: Option<String>, caching_config: &Caching
|
|||||||
Some(prompt) if !prompt.is_empty() => {
|
Some(prompt) if !prompt.is_empty() => {
|
||||||
let mut blocks = vec![SystemContentBlock::Text(prompt)];
|
let mut blocks = vec![SystemContentBlock::Text(prompt)];
|
||||||
if caching_config.enabled {
|
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(
|
blocks.push(SystemContentBlock::CachePoint(
|
||||||
CachePointBlock::builder()
|
builder.build().expect("valid cache point"),
|
||||||
.r#type(CachePointType::Default)
|
|
||||||
.build()
|
|
||||||
.expect("valid cache point"),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
blocks
|
blocks
|
||||||
@@ -357,11 +358,12 @@ fn build_tool_config(tools: Vec<ToolDefinition>, caching_config: &CachingConfig)
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if caching_config.enabled {
|
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(
|
tool_specs.push(Tool::CachePoint(
|
||||||
CachePointBlock::builder()
|
builder.build().expect("valid cache point"),
|
||||||
.r#type(CachePointType::Default)
|
|
||||||
.build()
|
|
||||||
.expect("valid cache point"),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,10 +89,20 @@ impl View for SettingsView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Model Configuration
|
// 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 {
|
if let Some(model) = &self.external_config.anthropic_model {
|
||||||
column = column.with_child(
|
column = column.with_child(
|
||||||
Text::new(
|
Text::new(
|
||||||
format!("Primary Model: {}", model),
|
format!("Default Model (ANTHROPIC_MODEL): {}", model),
|
||||||
appearance.ui_font_family(),
|
appearance.ui_font_family(),
|
||||||
font_size,
|
font_size,
|
||||||
)
|
)
|
||||||
@@ -100,6 +110,35 @@ impl View for SettingsView {
|
|||||||
.soft_wrap(true)
|
.soft_wrap(true)
|
||||||
.finish(),
|
.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 {
|
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 {
|
if self.caching_config.extended_ttl_requested {
|
||||||
column = column.with_child(
|
column = column.with_child(
|
||||||
Text::new(
|
Text::new(
|
||||||
"Extended 1h TTL: ✓ Requested",
|
"Extended 1h Cache TTL: ✓ Active",
|
||||||
appearance.ui_font_family(),
|
appearance.ui_font_family(),
|
||||||
font_size,
|
font_size,
|
||||||
)
|
)
|
||||||
.with_color(text_color)
|
.with_color(text_color)
|
||||||
.finish(),
|
.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
|
// Additional Models
|
||||||
@@ -173,7 +232,16 @@ impl View for SettingsView {
|
|||||||
// Help text
|
// Help text
|
||||||
column = column.with_child(
|
column = column.with_child(
|
||||||
Text::new(
|
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(),
|
appearance.ui_font_family(),
|
||||||
font_size - 1.0,
|
font_size - 1.0,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -40,8 +40,10 @@ pub async fn execute(
|
|||||||
.map(|tc| tc.tasks.is_empty())
|
.map(|tc| tc.tasks.is_empty())
|
||||||
.unwrap_or(true);
|
.unwrap_or(true);
|
||||||
|
|
||||||
|
// Use the model from params (selected in UI or defaulted from ANTHROPIC_MODEL)
|
||||||
let mut model_id = params.model_id;
|
let mut model_id = params.model_id;
|
||||||
if model_id.is_empty() || model_id == "auto" {
|
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();
|
model_id = "us.anthropic.claude-opus-4-6".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+73
-10
@@ -646,7 +646,42 @@ impl LLMPreferences {
|
|||||||
let region = settings.bedrock_region.value().clone();
|
let region = settings.bedrock_region.value().clone();
|
||||||
let cross_region = *settings.bedrock_cross_region_inference.value();
|
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 {
|
for model in effective {
|
||||||
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)
|
||||||
@@ -700,15 +735,43 @@ impl LLMPreferences {
|
|||||||
cli.choices.retain(|m| m.provider != LLMProvider::Unknown);
|
cli.choices.retain(|m| m.provider != LLMProvider::Unknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default agent mode to Claude Opus 4.6, falling back to the first available model.
|
// Default agent mode to ANTHROPIC_MODEL from external config if set,
|
||||||
if let Some(id) = self
|
// otherwise Claude Opus 4.6, falling back to the first available model.
|
||||||
.models_by_feature
|
// Note: external_config already loaded above for filtering
|
||||||
.agent_mode
|
let external_config_for_default = ExternalBedrockConfig::load();
|
||||||
.choices
|
|
||||||
.iter()
|
if let Some(id) = external_config_for_default
|
||||||
.find(|m| m.display_name.contains("Opus 4.6"))
|
.anthropic_model
|
||||||
.or_else(|| self.models_by_feature.agent_mode.choices.first())
|
.as_ref()
|
||||||
.map(|m| m.id.clone())
|
.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;
|
self.models_by_feature.agent_mode.default_id = id;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user