Significant progress. Performing cleanup now

This commit is contained in:
Ryan Ward
2026-05-11 14:31:02 -05:00
parent fe105e0369
commit 140da74f99
37 changed files with 843 additions and 1220 deletions
+227
View File
@@ -0,0 +1,227 @@
use std::time::Duration;
use galaxyui::r#async::Timer;
use galaxyui::ViewHandle;
use galaxyui::WindowId;
use crate::ai::agent::{AIAgentOutputStatus, AIAgentTextSection, FinishedAIAgentOutput};
use crate::pane_group::PaneGroup;
use crate::terminal::TerminalView;
use crate::workspace::Workspace;
use crate::BlocklistAIHistoryModel;
const TARGET_DIR: &str = "~/GIT/stitcher/stitcher";
const AGENT_QUERY: &str = r#"/agent Analyze this project and respond with EXACTLY this structured format at the end of your response:
Answer: <a 2-3 sentence summary of how this project handles video uploads and media conversions>
ProjectDescription: <a 1 sentence description of what this project is>
You MUST include both "Answer:" and "ProjectDescription:" fields in your final response. Use tools to explore the codebase first, then provide your structured answer."#;
const INITIAL_DELAY: Duration = Duration::from_secs(5);
const CD_SETTLE_DELAY: Duration = Duration::from_secs(3);
const POLL_INTERVAL: Duration = Duration::from_millis(2000);
const MAX_WAIT: Duration = Duration::from_secs(300);
pub fn schedule(ctx: &mut galaxyui::ViewContext<Workspace>) {
log::info!("[smoke-test] Bedrock smoke test scheduled — starting in {:?}", INITIAL_DELAY);
ctx.spawn(
async move { Timer::after(INITIAL_DELAY).await },
|_ws: &mut Workspace, _, ctx| {
let Some(window_id) = ctx.window_ids().next() else {
log::error!("[smoke-test] No window, aborting");
std::process::exit(1);
};
run_cd(ctx, window_id);
},
);
}
fn run_cd(ctx: &mut galaxyui::ViewContext<Workspace>, window_id: WindowId) {
log::info!("[smoke-test] cd {TARGET_DIR}");
let terminal_view = get_terminal_view(ctx, window_id);
terminal_view.update(ctx, |view, ctx| {
view.write_to_pty(format!("cd {TARGET_DIR}\n").into_bytes(), ctx);
});
ctx.spawn(
async move { Timer::after(CD_SETTLE_DELAY).await },
move |_ws: &mut Workspace, _, ctx| {
submit_query(ctx, window_id);
},
);
}
fn submit_query(ctx: &mut galaxyui::ViewContext<Workspace>, window_id: WindowId) {
log::info!("[smoke-test] Submitting query...");
let terminal_view = get_terminal_view(ctx, window_id);
terminal_view.update(ctx, |view, ctx| {
let input = view.input().clone();
input.update(ctx, |input, ctx| {
input.submit_queued_prompt(AGENT_QUERY.to_string(), ctx);
});
});
log::info!("[smoke-test] Polling for completion (max {:?})", MAX_WAIT);
poll(ctx, window_id, std::time::Instant::now());
}
fn poll(
ctx: &mut galaxyui::ViewContext<Workspace>,
window_id: WindowId,
start: std::time::Instant,
) {
ctx.spawn(
async move { Timer::after(POLL_INTERVAL).await },
move |_ws: &mut Workspace, _, ctx| {
let elapsed = start.elapsed();
if elapsed > MAX_WAIT {
log::error!("[smoke-test] TIMEOUT after {:?}", elapsed);
std::process::exit(1);
}
let terminal_view = get_terminal_view(ctx, window_id);
if let Some(full_text) = get_finished_text(ctx, &terminal_view) {
log::info!("[smoke-test] === CONVERSATION COMPLETE ({:.1}s) ===", elapsed.as_secs_f64());
log::info!("[smoke-test] Full response length: {} chars", full_text.len());
let answer = extract_field(&full_text, "Answer:");
let project_desc = extract_field(&full_text, "ProjectDescription:");
match (&answer, &project_desc) {
(Some(a), Some(p)) => {
log::info!("[smoke-test] ========================================");
log::info!("[smoke-test] Answer: {}", a);
log::info!("[smoke-test] ProjectDescription: {}", p);
log::info!("[smoke-test] ========================================");
log::info!("[smoke-test] === TEST PASSED ===");
std::process::exit(0);
}
_ => {
log::warn!("[smoke-test] ========================================");
if let Some(a) = &answer {
log::info!("[smoke-test] Answer: {}", a);
} else {
log::warn!("[smoke-test] MISSING: Answer field not found in response");
}
if let Some(p) = &project_desc {
log::info!("[smoke-test] ProjectDescription: {}", p);
} else {
log::warn!("[smoke-test] MISSING: ProjectDescription field not found in response");
}
log::warn!("[smoke-test] ========================================");
log::warn!("[smoke-test] Full text dump:");
for line in full_text.lines() {
log::warn!("[smoke-test] {}", line);
}
log::warn!("[smoke-test] === TEST FAILED (missing structured fields) ===");
std::process::exit(1);
}
}
}
poll(ctx, window_id, start);
},
);
}
fn extract_field<'a>(text: &'a str, prefix: &str) -> Option<&'a str> {
for line in text.lines() {
let trimmed = line.trim();
if trimmed.starts_with(prefix) {
let value = trimmed[prefix.len()..].trim();
if !value.is_empty() {
return Some(value);
}
}
}
None
}
fn get_finished_text(
ctx: &galaxyui::ViewContext<Workspace>,
terminal_view: &ViewHandle<TerminalView>,
) -> Option<String> {
use galaxyui::SingletonEntity;
let view_id = terminal_view.id();
BlocklistAIHistoryModel::handle(ctx).read(ctx, |history, _| {
let conv = history.active_conversation(view_id)?;
if conv.status().is_in_progress() {
log::info!("[smoke-test] Still running...");
return None;
}
let mut all_text = String::new();
for exchange in conv.exchanges_reversed() {
match &exchange.output_status {
AIAgentOutputStatus::Finished {
finished_output: FinishedAIAgentOutput::Success { output },
} => {
let output = output.get();
for text_section in output.text_from_agent_output() {
for section in &text_section.sections {
match section {
AIAgentTextSection::PlainText { text } => {
all_text.push_str(text.text());
all_text.push('\n');
}
AIAgentTextSection::Code { code, .. } => {
all_text.push_str(code);
all_text.push('\n');
}
_ => {}
}
}
}
}
AIAgentOutputStatus::Finished {
finished_output: FinishedAIAgentOutput::Error { error, .. },
} => {
log::error!("[smoke-test] Conversation finished with error: {error}");
return None;
}
AIAgentOutputStatus::Finished {
finished_output: FinishedAIAgentOutput::Cancelled { .. },
} => {
log::error!("[smoke-test] Conversation was cancelled");
return None;
}
_ => {
log::info!("[smoke-test] Exchange still in progress...");
return None;
}
}
}
if all_text.is_empty() {
log::warn!("[smoke-test] Conversation finished but no text output found");
return None;
}
Some(all_text)
})
}
fn get_terminal_view(
ctx: &galaxyui::ViewContext<Workspace>,
window_id: WindowId,
) -> ViewHandle<TerminalView> {
let pane_group: ViewHandle<PaneGroup> = ctx
.views_of_type(window_id)
.expect("[smoke-test] views for window")
.first()
.expect("[smoke-test] pane group")
.clone();
pane_group.read(ctx, |pg, ctx| {
pg.terminal_views(ctx)
.into_iter()
.next()
.expect("[smoke-test] should have at least one terminal view")
})
}