diff --git a/app/src/ai/agent/api/impl.rs b/app/src/ai/agent/api/impl.rs index 6bc2b1a3..d1f45c53 100644 --- a/app/src/ai/agent/api/impl.rs +++ b/app/src/ai/agent/api/impl.rs @@ -167,13 +167,17 @@ pub async fn generate_multi_agent_output( log::info!("[bedrock] needs_create_task={needs_create_task}"); - let model_id = request + let mut model_id = request .settings .as_ref() .and_then(|s| s.model_config.as_ref()) .map(|mc| mc.base.clone()) .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}"); let diagnostic_logger = diff --git a/app/src/ai/agent/task.rs b/app/src/ai/agent/task.rs index 75787fbe..8762bb40 100644 --- a/app/src/ai/agent/task.rs +++ b/app/src/ai/agent/task.rs @@ -999,6 +999,13 @@ impl AIAgentExchange { .iter() .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 .clone() .to_client_output_message(ConversionParams { @@ -1007,9 +1014,8 @@ impl AIAgentExchange { task_id, })? { 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()); - // Upsert behavior: update the message if it exists, otherwise add it to the end of the list. if let Some(message_idx) = message_idx { output.messages[message_idx] = m; } else { @@ -1018,7 +1024,7 @@ impl AIAgentExchange { } MaybeAIAgentOutputMessage::NoClientRepresentation => { 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 ); } } diff --git a/app/src/ai/blocklist/controller/response_stream.rs b/app/src/ai/blocklist/controller/response_stream.rs index 0e3e94ed..31d098bd 100644 --- a/app/src/ai/blocklist/controller/response_stream.rs +++ b/app/src/ai/blocklist/controller/response_stream.rs @@ -14,7 +14,7 @@ use crate::{ conversation::AIConversationId, AIIdentifiers, CancellationReason, }, - bedrock::{client::BedrockClientConfig, models::is_bedrock_model}, + bedrock::client::BedrockClientConfig, }, network::NetworkStatus, report_error, send_telemetry_from_ctx, @@ -85,17 +85,13 @@ pub struct ResponseStream { impl ResponseStream { fn bedrock_config_if_applicable( - model_id: &str, + _model_id: &str, ctx: &ModelContext, ) -> Option { let settings = AISettings::as_ref(ctx); if !*settings.bedrock_enabled.value() { 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(); Some(BedrockClientConfig { auth_method, diff --git a/app/src/bedrock_smoke_test.rs b/app/src/bedrock_smoke_test.rs index 04065687..90d06a86 100644 --- a/app/src/bedrock_smoke_test.rs +++ b/app/src/bedrock_smoke_test.rs @@ -12,17 +12,19 @@ use crate::BlocklistAIHistoryModel; 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 -file_count: +Answered: YES +Results: +Files evaluated: -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 -file_count: 0 +Answered: NO +Results: +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)] 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) { log::info!("[smoke-test] === FILE VISIBILITY RESULT ({:.1}s) ===", elapsed.as_secs_f64()); - let results = extract_field(&full_text, "results:"); - let file_count = extract_field(&full_text, "file_count:"); + let answered = extract_field(&full_text, "Answered:"); + let results = extract_field(&full_text, "Results:"); + let files_evaluated = extract_field(&full_text, "Files evaluated:"); - match (&results, &file_count) { - (Some(r), Some(c)) => { - log::info!("[smoke-test] results: {}", r); - log::info!("[smoke-test] file_count: {}", c); + match (&answered, &results, &files_evaluated) { + (Some(a), Some(r), Some(f)) => { + log::info!("[smoke-test] Answered: {}", a); + log::info!("[smoke-test] Results: {}", r); + log::info!("[smoke-test] Files evaluated: {}", f); - if *r == "yes" { - let count: u32 = c.parse().unwrap_or(0); + if a.to_uppercase().contains("YES") { + let count: u32 = f.trim().parse().unwrap_or(0); if count > 0 { - log::info!("[smoke-test] === FILE VISIBILITY TEST PASSED (found {} files) ===", count); - log::info!("[smoke-test] LLM can see files. Proceeding to main test..."); + log::info!("[smoke-test] === TEST PASSED (evaluated {} files) ===", count); std::process::exit(0); } 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); } } else { - log::error!("[smoke-test] === FILE VISIBILITY TEST FAILED (results=no) ==="); - log::error!("[smoke-test] The LLM cannot see files in {}", TARGET_DIR); + log::error!("[smoke-test] === TEST FAILED (Answered=NO) ==="); + log::error!("[smoke-test] The LLM could not answer the question about {}", TARGET_DIR); log::error!("[smoke-test] Full response:"); for line in full_text.lines() { 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:"); for line in full_text.lines() { log::error!("[smoke-test] {}", line); @@ -245,7 +248,6 @@ fn get_finished_text( let conv = history.active_conversation(view_id)?; if conv.status().is_in_progress() { - log::info!("[smoke-test] Still running..."); return None; } @@ -293,7 +295,26 @@ fn get_finished_text( if all_text.is_empty() { 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) diff --git a/app/src/settings/ai.rs b/app/src/settings/ai.rs index cd9f099c..6e739c2a 100644 --- a/app/src/settings/ai.rs +++ b/app/src/settings/ai.rs @@ -1071,7 +1071,7 @@ define_settings_group!(AISettings, settings: [ // Whether direct Bedrock integration is enabled (client calls Bedrock API directly). bedrock_enabled: BedrockEnabled { type: bool, - default: false, + default: true, supported_platforms: SupportedPlatforms::DESKTOP, sync_to_cloud: SyncToCloud::Globally(RespectUserSyncSetting::Yes), 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. bedrock_fallback_to_warp: BedrockFallbackToWarp { type: bool, - default: true, + default: false, supported_platforms: SupportedPlatforms::DESKTOP, sync_to_cloud: SyncToCloud::Globally(RespectUserSyncSetting::Yes), private: false,