lc_tools/extended/computer/
screen.rs1use std::time::Duration;
9
10use async_trait::async_trait;
11use serde_json::Value;
12
13use lc_core::tools::ToolError;
14use lc_core::BaseTool;
15
16use super::actions::{ComputerMode, ComputerUseInput, ComputerUseOutput};
17
18pub struct ComputerUseTool {
25 pub(super) mode: ComputerMode,
26 pub(super) api_key: String,
27 pub(super) base_url: String,
28 pub(super) display_width: u32,
29 pub(super) display_height: u32,
30 pub(super) client: reqwest::Client,
31}
32
33impl ComputerUseTool {
38 pub fn new_anthropic(
40 api_key: impl Into<String>,
41 display_width: u32,
42 display_height: u32,
43 ) -> Self {
44 Self {
45 mode: ComputerMode::AnthropicApi,
46 api_key: api_key.into(),
47 base_url: "https://api.anthropic.com".to_string(),
48 display_width,
49 display_height,
50 client: reqwest::Client::builder()
51 .timeout(Duration::from_secs(60))
52 .build()
53 .unwrap_or_else(|_| reqwest::Client::new()),
54 }
55 }
56
57 pub fn with_base_url(mut self, url: impl Into<String>) -> Self {
59 self.base_url = url.into();
60 self
61 }
62
63 pub fn with_timeout(mut self, timeout: Duration) -> Self {
65 self.client = reqwest::Client::builder()
66 .timeout(timeout)
67 .build()
68 .unwrap_or_else(|_| reqwest::Client::new());
69 self
70 }
71
72 #[cfg(feature = "native-computer")]
74 pub fn new_native(display_width: u32, display_height: u32) -> Self {
75 Self {
76 mode: ComputerMode::Native,
77 api_key: String::new(),
78 base_url: String::new(),
79 display_width,
80 display_height,
81 client: reqwest::Client::new(),
82 }
83 }
84
85 pub fn mode(&self) -> &ComputerMode {
87 &self.mode
88 }
89}
90
91impl Default for ComputerUseTool {
92 fn default() -> Self {
93 Self::new_anthropic(String::new(), 1024, 768)
94 }
95}
96
97#[async_trait]
102impl BaseTool for ComputerUseTool {
103 fn name(&self) -> &str {
104 "computer_use"
105 }
106
107 fn description(&self) -> &str {
108 "Computer use tool for screen interaction. \
109 Input JSON: {\"action\": \"screenshot|click|type|scroll|key_press|wait\", \
110 \"coordinate\": [x, y], \"text\": \"...\", \"keys\": [\"...\"], \
111 \"direction\": \"up|down|left|right\", \"amount\": N, \"duration_ms\": N}. \
112 - screenshot: capture the current screen. \
113 - click: click at (x, y). \
114 - type: type text string. \
115 - scroll: scroll at (x, y) in direction by amount. \
116 - key_press: press key combination. \
117 - wait: wait for duration_ms milliseconds."
118 }
119
120 async fn run(&self, input: String) -> Result<String, ToolError> {
121 let parsed: ComputerUseInput =
122 serde_json::from_str(&input).map_err(|e| ToolError::InvalidInput(e.to_string()))?;
123
124 let output = self.dispatch(&parsed).await?;
125
126 serde_json::to_string(&output).map_err(|e| ToolError::ExecutionFailed(e.to_string()))
127 }
128
129 fn args_schema(&self) -> Option<Value> {
130 use schemars::schema_for;
131 serde_json::to_value(schema_for!(ComputerUseInput)).ok()
132 }
133}
134
135impl ComputerUseTool {
140 pub(super) async fn dispatch(
142 &self,
143 input: &ComputerUseInput,
144 ) -> Result<ComputerUseOutput, ToolError> {
145 match self.mode {
146 ComputerMode::AnthropicApi => self.execute_anthropic(input).await,
147 #[cfg(feature = "native-computer")]
148 ComputerMode::Native => self.execute_native(input).await,
149 }
150 }
151}