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