Fix Bedrock not being used: enable by default, remove model_id gating, set fallback default model

- bedrock_enabled default: false -> true (was never routing to Bedrock)
- Remove is_bedrock_model check from bedrock_config_if_applicable (model_id
  could be 'auto' from server-populated prefs, causing Bedrock to be skipped)
- Default to claude-sonnet-4 when model_id is empty or 'auto'
- fallback_to_warp default: true -> false (no server exists)
- Smoke test: exit immediately on no-text with diagnostic info instead of
  polling forever
This commit is contained in:
Ryan Ward
2026-05-12 10:10:00 -05:00
parent 0009f1366a
commit e13ed355f6
5 changed files with 62 additions and 35 deletions
+5 -1
View File
@@ -167,13 +167,17 @@ pub async fn generate_multi_agent_output(
log::info!("[bedrock] needs_create_task={needs_create_task}"); log::info!("[bedrock] needs_create_task={needs_create_task}");
let model_id = request let mut model_id = request
.settings .settings
.as_ref() .as_ref()
.and_then(|s| s.model_config.as_ref()) .and_then(|s| s.model_config.as_ref())
.map(|mc| mc.base.clone()) .map(|mc| mc.base.clone())
.unwrap_or_default(); .unwrap_or_default();
if model_id.is_empty() || model_id == "auto" {
model_id = "us.anthropic.claude-sonnet-4-20250514-v1:0".to_string();
}
log::info!("[bedrock] Model: {model_id}"); log::info!("[bedrock] Model: {model_id}");
let diagnostic_logger = let diagnostic_logger =
+9 -3
View File
@@ -999,6 +999,13 @@ impl AIAgentExchange {
.iter() .iter()
.position(|m| m.id.0 == task_message.id); .position(|m| m.id.0 == task_message.id);
let proto_text = task_message.message.as_ref().map(|m| match m {
api::message::Message::AgentOutput(o) => format!("AgentOutput(text_len={})", o.text.len()),
api::message::Message::ToolCall(t) => format!("ToolCall(id={})", t.tool_call_id),
other => format!("{:?}", std::mem::discriminant(other)),
}).unwrap_or_else(|| "None".to_string());
log::info!("[bedrock-debug] upsert_output_for_message: id={}, proto_type={}", task_message.id, proto_text);
match task_message match task_message
.clone() .clone()
.to_client_output_message(ConversionParams { .to_client_output_message(ConversionParams {
@@ -1007,9 +1014,8 @@ impl AIAgentExchange {
task_id, task_id,
})? { })? {
MaybeAIAgentOutputMessage::Message(m) => { MaybeAIAgentOutputMessage::Message(m) => {
// Extract citations from the message and add to the output citations log::info!("[bedrock-debug] upsert_output_for_message: client_message_type={:?}", std::mem::discriminant(&m.message));
output.extend_citations(m.citations.clone()); output.extend_citations(m.citations.clone());
// Upsert behavior: update the message if it exists, otherwise add it to the end of the list.
if let Some(message_idx) = message_idx { if let Some(message_idx) = message_idx {
output.messages[message_idx] = m; output.messages[message_idx] = m;
} else { } else {
@@ -1018,7 +1024,7 @@ impl AIAgentExchange {
} }
MaybeAIAgentOutputMessage::NoClientRepresentation => { MaybeAIAgentOutputMessage::NoClientRepresentation => {
log::warn!( log::warn!(
"Tried to update output for message which no longer has a client representation" "[bedrock-debug] upsert_output_for_message: NoClientRepresentation for msg_id={}", task_message.id
); );
} }
} }
@@ -14,7 +14,7 @@ use crate::{
conversation::AIConversationId, conversation::AIConversationId,
AIIdentifiers, CancellationReason, AIIdentifiers, CancellationReason,
}, },
bedrock::{client::BedrockClientConfig, models::is_bedrock_model}, bedrock::client::BedrockClientConfig,
}, },
network::NetworkStatus, network::NetworkStatus,
report_error, send_telemetry_from_ctx, report_error, send_telemetry_from_ctx,
@@ -85,17 +85,13 @@ pub struct ResponseStream {
impl ResponseStream { impl ResponseStream {
fn bedrock_config_if_applicable( fn bedrock_config_if_applicable(
model_id: &str, _model_id: &str,
ctx: &ModelContext<Self>, ctx: &ModelContext<Self>,
) -> Option<BedrockClientConfig> { ) -> Option<BedrockClientConfig> {
let settings = AISettings::as_ref(ctx); let settings = AISettings::as_ref(ctx);
if !*settings.bedrock_enabled.value() { if !*settings.bedrock_enabled.value() {
return None; return None;
} }
let configured_models = settings.bedrock_models.value().clone();
if !is_bedrock_model(model_id, &configured_models) {
return None;
}
let auth_method = *settings.bedrock_auth_method.value(); let auth_method = *settings.bedrock_auth_method.value();
Some(BedrockClientConfig { Some(BedrockClientConfig {
auth_method, auth_method,
+44 -23
View File
@@ -12,17 +12,19 @@ use crate::BlocklistAIHistoryModel;
const TARGET_DIR: &str = "~/GIT/stitcher/stitcher"; const TARGET_DIR: &str = "~/GIT/stitcher/stitcher";
const FILE_VISIBILITY_QUERY: &str = r#"/agent Okay, let's try again. Can you see files now? List the files in the current directory using your tools. Then respond with EXACTLY this structured format: const FILE_VISIBILITY_QUERY: &str = r#"/agent Please review the hls stream server code and tell me how we inject ads? Use your tools to explore the codebase first. Then respond with EXACTLY this structured format at the end of your response:
results: yes Answered: YES
file_count: <number of files/directories you found> Results: <your summary of how ads are injected into HLS streams>
Files evaluated: <number of files you read>
If you cannot see any files or your tools fail, respond with: If you cannot find the answer or cannot see files, respond with:
results: no Answered: NO
file_count: 0 Results: <explanation of what went wrong>
Files evaluated: 0
You MUST use the file_glob or run_shell_command tool to check the directory contents first, then provide the structured response."#; You MUST use tools (file_glob, read_files, grep, run_shell_command) to explore the codebase before answering."#;
#[allow(dead_code)] #[allow(dead_code)]
const AGENT_QUERY: &str = r#"/agent Analyze this project and respond with EXACTLY this structured format at the end of your response: const AGENT_QUERY: &str = r#"/agent Analyze this project and respond with EXACTLY this structured format at the end of your response:
@@ -101,27 +103,28 @@ fn poll_file_visibility(
if let Some(full_text) = get_finished_text(ctx, &terminal_view) { if let Some(full_text) = get_finished_text(ctx, &terminal_view) {
log::info!("[smoke-test] === FILE VISIBILITY RESULT ({:.1}s) ===", elapsed.as_secs_f64()); log::info!("[smoke-test] === FILE VISIBILITY RESULT ({:.1}s) ===", elapsed.as_secs_f64());
let results = extract_field(&full_text, "results:"); let answered = extract_field(&full_text, "Answered:");
let file_count = extract_field(&full_text, "file_count:"); let results = extract_field(&full_text, "Results:");
let files_evaluated = extract_field(&full_text, "Files evaluated:");
match (&results, &file_count) { match (&answered, &results, &files_evaluated) {
(Some(r), Some(c)) => { (Some(a), Some(r), Some(f)) => {
log::info!("[smoke-test] results: {}", r); log::info!("[smoke-test] Answered: {}", a);
log::info!("[smoke-test] file_count: {}", c); log::info!("[smoke-test] Results: {}", r);
log::info!("[smoke-test] Files evaluated: {}", f);
if *r == "yes" { if a.to_uppercase().contains("YES") {
let count: u32 = c.parse().unwrap_or(0); let count: u32 = f.trim().parse().unwrap_or(0);
if count > 0 { if count > 0 {
log::info!("[smoke-test] === FILE VISIBILITY TEST PASSED (found {} files) ===", count); log::info!("[smoke-test] === TEST PASSED (evaluated {} files) ===", count);
log::info!("[smoke-test] LLM can see files. Proceeding to main test...");
std::process::exit(0); std::process::exit(0);
} else { } else {
log::error!("[smoke-test] === FILE VISIBILITY TEST FAILED (results=yes but file_count=0) ==="); log::error!("[smoke-test] === TEST FAILED (Answered=YES but Files evaluated=0) ===");
std::process::exit(1); std::process::exit(1);
} }
} else { } else {
log::error!("[smoke-test] === FILE VISIBILITY TEST FAILED (results=no) ==="); log::error!("[smoke-test] === TEST FAILED (Answered=NO) ===");
log::error!("[smoke-test] The LLM cannot see files in {}", TARGET_DIR); log::error!("[smoke-test] The LLM could not answer the question about {}", TARGET_DIR);
log::error!("[smoke-test] Full response:"); log::error!("[smoke-test] Full response:");
for line in full_text.lines() { for line in full_text.lines() {
log::error!("[smoke-test] {}", line); log::error!("[smoke-test] {}", line);
@@ -130,7 +133,7 @@ fn poll_file_visibility(
} }
} }
_ => { _ => {
log::error!("[smoke-test] === FILE VISIBILITY TEST FAILED (missing structured fields) ==="); log::error!("[smoke-test] === TEST FAILED (missing structured fields) ===");
log::error!("[smoke-test] Full response:"); log::error!("[smoke-test] Full response:");
for line in full_text.lines() { for line in full_text.lines() {
log::error!("[smoke-test] {}", line); log::error!("[smoke-test] {}", line);
@@ -245,7 +248,6 @@ fn get_finished_text(
let conv = history.active_conversation(view_id)?; let conv = history.active_conversation(view_id)?;
if conv.status().is_in_progress() { if conv.status().is_in_progress() {
log::info!("[smoke-test] Still running...");
return None; return None;
} }
@@ -293,7 +295,26 @@ fn get_finished_text(
if all_text.is_empty() { if all_text.is_empty() {
log::warn!("[smoke-test] Conversation finished but no text output found"); log::warn!("[smoke-test] Conversation finished but no text output found");
return None; log::warn!("[smoke-test] Conversation status: {:?}", conv.status());
log::warn!("[smoke-test] Number of exchanges: {}", conv.exchanges_reversed().count());
for (i, exchange) in conv.exchanges_reversed().enumerate() {
match &exchange.output_status {
AIAgentOutputStatus::Finished {
finished_output: FinishedAIAgentOutput::Success { output },
} => {
let o = output.get();
log::warn!("[smoke-test] Exchange {}: Finished/Success, messages={}", i, o.messages.len());
for (j, msg) in o.messages.iter().enumerate() {
log::warn!("[smoke-test] msg[{}]: type={:?}", j, std::mem::discriminant(&msg.message));
}
}
other => {
log::warn!("[smoke-test] Exchange {}: {:?}", i, std::mem::discriminant(other));
}
}
}
log::error!("[smoke-test] === TEST FAILED (no text in finished conversation) ===");
std::process::exit(1);
} }
Some(all_text) Some(all_text)
+2 -2
View File
@@ -1071,7 +1071,7 @@ define_settings_group!(AISettings, settings: [
// Whether direct Bedrock integration is enabled (client calls Bedrock API directly). // Whether direct Bedrock integration is enabled (client calls Bedrock API directly).
bedrock_enabled: BedrockEnabled { bedrock_enabled: BedrockEnabled {
type: bool, type: bool,
default: false, default: true,
supported_platforms: SupportedPlatforms::DESKTOP, supported_platforms: SupportedPlatforms::DESKTOP,
sync_to_cloud: SyncToCloud::Globally(RespectUserSyncSetting::Yes), sync_to_cloud: SyncToCloud::Globally(RespectUserSyncSetting::Yes),
private: false, private: false,
@@ -1113,7 +1113,7 @@ define_settings_group!(AISettings, settings: [
// Whether to fall back to routing through the Warp server when Bedrock credentials fail. // Whether to fall back to routing through the Warp server when Bedrock credentials fail.
bedrock_fallback_to_warp: BedrockFallbackToWarp { bedrock_fallback_to_warp: BedrockFallbackToWarp {
type: bool, type: bool,
default: true, default: false,
supported_platforms: SupportedPlatforms::DESKTOP, supported_platforms: SupportedPlatforms::DESKTOP,
sync_to_cloud: SyncToCloud::Globally(RespectUserSyncSetting::Yes), sync_to_cloud: SyncToCloud::Globally(RespectUserSyncSetting::Yes),
private: false, private: false,