aboutsummaryrefslogtreecommitdiffstats
path: root/crates/asist-app/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/asist-app/src')
-rw-r--r--crates/asist-app/src/main.rs27
-rw-r--r--crates/asist-app/src/registry.rs67
2 files changed, 73 insertions, 21 deletions
diff --git a/crates/asist-app/src/main.rs b/crates/asist-app/src/main.rs
index 7b00b1f..cc08e62 100644
--- a/crates/asist-app/src/main.rs
+++ b/crates/asist-app/src/main.rs
@@ -109,6 +109,17 @@ fn run(config: Config, args: &Args) -> Result<()> {
// Las dos capacidades que tocan algo fuera del proceso se anuncian: una
// enciende la cámara y la otra ejecuta órdenes. Quien lo arranca debería
// saberlo sin tener que leerse la configuración.
+ if tools.get("mirar_la_pantalla").is_some() {
+ eprintln!(
+ "Captura de pantalla ACTIVA — a {} px{}",
+ config.screen.width,
+ if config.screen.save_dir.is_empty() {
+ String::new()
+ } else {
+ format!(", guardando en {}", config.screen.save_dir)
+ }
+ );
+ }
if tools.get("mirar_por_la_camara").is_some() {
eprintln!(
"Cámara ACTIVA — {} a {}x{}{}",
@@ -320,6 +331,22 @@ fn check(config: &Config) -> Result<()> {
}
}
+ if !config.screen.enabled {
+ println!(" - pantalla desactivada en la configuración");
+ } else {
+ match config.screen.command.first() {
+ Some(program) if !program.contains('/') || PathBuf::from(program).exists() => ok(
+ "pantalla",
+ format!("{} px · {}", config.screen.width, program),
+ ),
+ Some(program) => println!(" - pantalla no existe {program}"),
+ None => println!(" - pantalla screen.command está vacío"),
+ }
+ if std::env::var_os("WAYLAND_DISPLAY").is_none() && std::env::var_os("DISPLAY").is_none() {
+ println!(" - sesión gráfica no hay; la pantalla no se podrá capturar");
+ }
+ }
+
if !config.camera.enabled {
println!(" - cámara desactivada en la configuración");
} else if config.camera.device.exists() {
diff --git a/crates/asist-app/src/registry.rs b/crates/asist-app/src/registry.rs
index 13bbb75..a3a10ed 100644
--- a/crates/asist-app/src/registry.rs
+++ b/crates/asist-app/src/registry.rs
@@ -8,11 +8,12 @@
use std::sync::Arc;
use std::time::Duration;
-use asist_core::config::{CameraConfig, Config, SearchConfig};
+use asist_core::config::{self as cfg, Config, SearchConfig};
use asist_core::tools::ToolRegistry;
use asist_llm::LlmClient;
-use asist_tools::camera::CaptureConfig;
-use asist_tools::{Camera, SearchBackend, WebSearch};
+use asist_tools::{
+ Camera, CameraConfig, Screen, ScreenConfig, SearchBackend, VisionTool, WebSearch,
+};
/// Lo que no se pudo activar y por qué, para decírselo al usuario al arrancar
/// en vez de dejarlo en silencio.
@@ -38,13 +39,16 @@ pub fn build(config: &Config, llm: &Arc<LlmClient>) -> (ToolRegistry, Vec<Skippe
}),
}
- match camera_tool(&config.camera, llm) {
- Ok(Some(tool)) => registry.register(Arc::new(tool)),
- Ok(None) => {}
- Err(reason) => skipped.push(Skipped {
- tool: "mirar_por_la_camara",
- reason,
- }),
+ // Las dos que ven comparten forma, así que se registran igual.
+ for (result, tool) in [
+ (camera_tool(&config.camera, llm), "mirar_por_la_camara"),
+ (screen_tool(&config.screen, llm), "mirar_la_pantalla"),
+ ] {
+ match result {
+ Ok(Some(vision)) => registry.register(Arc::new(vision)),
+ Ok(None) => {}
+ Err(reason) => skipped.push(Skipped { tool, reason }),
+ }
}
(registry, skipped)
@@ -98,25 +102,46 @@ fn search_tool(config: &SearchConfig) -> Result<Option<WebSearch>, String> {
)))
}
-fn camera_tool(config: &CameraConfig, llm: &Arc<LlmClient>) -> Result<Option<Camera>, String> {
+fn camera_tool(
+ config: &cfg::CameraConfig,
+ llm: &Arc<LlmClient>,
+) -> Result<Option<VisionTool>, String> {
if !config.enabled {
return Ok(None);
}
- let capture = CaptureConfig {
+ let source = Camera::new(CameraConfig {
device: config.device.clone(),
width: config.width,
height: config.height,
warmup_frames: config.warmup_frames,
timeout: Duration::from_secs(config.timeout_secs),
save_dir: (!config.save_dir.is_empty()).then(|| config.save_dir.clone().into()),
- };
- // Registrar una herramienta que va a fallar siempre es peor que no tenerla:
- // el modelo la llamaría, se comería el error y gastaría un turno entero.
- if !Camera::available(&capture) {
- return Err(format!(
- "no existe {}; comprueba con «v4l2-ctl --list-devices»",
- capture.device.display()
- ));
+ });
+ finish(VisionTool::camera(Box::new(source), Arc::clone(llm)))
+}
+
+fn screen_tool(
+ config: &cfg::ScreenConfig,
+ llm: &Arc<LlmClient>,
+) -> Result<Option<VisionTool>, String> {
+ if !config.enabled {
+ return Ok(None);
+ }
+ let source = Screen::new(ScreenConfig {
+ command: config.command.clone(),
+ width: config.width,
+ output: config.output.clone(),
+ timeout: Duration::from_secs(config.timeout_secs),
+ save_dir: (!config.save_dir.is_empty()).then(|| config.save_dir.clone().into()),
+ });
+ finish(VisionTool::screen(Box::new(source), Arc::clone(llm)))
+}
+
+/// Registrar una herramienta que va a fallar siempre es peor que no tenerla:
+/// el modelo la llamaría, se comería el error y gastaría un turno entero.
+fn finish(tool: VisionTool) -> Result<Option<VisionTool>, String> {
+ match tool.available() {
+ Ok(()) => Ok(Some(tool)),
+ Err(err) => Err(err.to_string()),
}
- Ok(Some(Camera::new(capture, Arc::clone(llm))))
}