diff --git a/assistant_v2/FEATURE_PROGRESS.md b/assistant_v2/FEATURE_PROGRESS.md index 8800889..4a63a9c 100644 --- a/assistant_v2/FEATURE_PROGRESS.md +++ b/assistant_v2/FEATURE_PROGRESS.md @@ -8,7 +8,7 @@ This document tracks which features from the original assistant have been implem | System volume adjustment (Windows only) | Pending | | Media playback commands | Done | | Launch applications from voice | Pending | -| Display log files | Done | +| Display log files | Done (includes live stream) | | Get system info | Done | | List and kill processes | Pending | | Run internet speed tests | Done (async) | diff --git a/assistant_v2/src/main.rs b/assistant_v2/src/main.rs index 3c0c447..1346187 100644 --- a/assistant_v2/src/main.rs +++ b/assistant_v2/src/main.rs @@ -16,7 +16,9 @@ use open; use enigo::{Enigo, KeyboardControllable}; use speakstream::ss::SpeakStream; use std::error::Error; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::fs; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::sync::LazyLock; @@ -246,6 +248,19 @@ async fn main() -> Result<(), Box> { strict: None, } .into(), + FunctionObject { + name: "show_live_log_stream".into(), + description: Some( + "Shows live updates of the log file via opening powershell and running 'Get-Content -Wait'.".into(), + ), + parameters: Some(serde_json::json!({ + "type": "object", + "properties": {}, + "required": [], + })), + strict: None, + } + .into(), FunctionObject { name: "get_system_info".into(), description: Some("Returns system information like CPU and memory usage.".into()), @@ -597,6 +612,32 @@ fn speedtest() -> Result { Ok(output) } +fn get_currently_active_log_file() -> Option { + let mut entries: Vec<_> = fs::read_dir(&*LOGS_DIR).ok()?.filter_map(Result::ok).collect(); + entries.sort_by_key(|entry| entry.file_name()); + entries.last().map(|last| last.path()) +} + +fn run_get_content_wait_on_file(file_path: &Path) -> Result { + let file_path_str = file_path + .to_str() + .ok_or_else(|| "Failed to convert file path to string".to_string())?; + Command::new("cmd") + .args([ + "/C", + "start", + "powershell", + "-NoExit", + "-Command", + "Get-Content", + &format!("\"{}\"", file_path_str), + "-Wait", + ]) + .spawn() + .map(|_| "Successfully opened file in powershell".to_string()) + .map_err(|err| format!("Failed to open file in powershell: {:?}", err)) +} + async fn handle_requires_action( client: Client, run_object: RunObject, @@ -745,6 +786,20 @@ async fn handle_requires_action( }); } + if tool.function.name == "show_live_log_stream" { + let msg = match get_currently_active_log_file() { + Some(log_file) => match run_get_content_wait_on_file(&log_file) { + Ok(m) => m, + Err(e) => e, + }, + None => "No log files found".to_string(), + }; + tool_outputs.push(ToolsOutputs { + tool_call_id: Some(tool.id.clone()), + output: Some(msg.into()), + }); + } + if tool.function.name == "speedtest" { tool_outputs.push(ToolsOutputs { tool_call_id: Some(tool.id.clone()), @@ -1019,6 +1074,35 @@ mod tests { })); } + #[test] + fn includes_show_live_log_stream_function() { + let req = CreateAssistantRequestArgs::default() + .instructions("test") + .model("gpt-4o") + .tools(vec![FunctionObject { + name: "show_live_log_stream".into(), + description: Some( + "Shows live updates of the log file via opening powershell and running 'Get-Content -Wait'.".into(), + ), + parameters: Some(serde_json::json!({ + "type": "object", + "properties": {}, + "required": [], + })), + strict: None, + } + .into()]) + .build() + .unwrap(); + + let tools = req.tools.unwrap(); + assert!(tools.iter().any(|t| match t { + async_openai::types::AssistantTools::Function(f) => + f.function.name == "show_live_log_stream", + _ => false, + })); + } + #[test] fn includes_speedtest_function() { let req = CreateAssistantRequestArgs::default()