meshagent-computers 0.0.7__py3-none-any.whl → 0.0.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of meshagent-computers might be problematic. Click here for more details.

@@ -5,3 +5,4 @@ from .docker import DockerComputer
5
5
  from .scrapybara import ScrapybaraBrowser, ScrapybaraUbuntu
6
6
  from .operator import Operator
7
7
  from .agent import ComputerAgent
8
+ from .version import __version__
@@ -1,29 +1,182 @@
1
- from meshagent.openai import OpenAIResponsesAdapter
2
- from meshagent.agents import LLMAdapter, AgentChatContext
1
+ from meshagent.agents import LLMAdapter
3
2
  from meshagent.tools import Tool, Toolkit, ToolContext
4
- from meshagent.agents.prompt import PromptAgent
5
- from meshagent.computers import Computer, Operator
3
+ from meshagent.computers import Computer, Operator, BrowserbaseBrowser
6
4
  from meshagent.agents.chat import ChatBot, ChatThreadContext
7
- from meshagent.api import RemoteParticipant, FileResponse
5
+ from meshagent.api import RequiredToolkit, RemoteParticipant
8
6
  from meshagent.api.messaging import RawOutputs
7
+ from meshagent.tools.toolkit import register_toolkit_factory
9
8
 
10
- from typing import Optional
9
+ from typing import Optional, Type, Callable
11
10
  import base64
12
- import json
13
11
  import logging
14
12
 
15
13
  logging.basicConfig()
16
14
  logger = logging.getLogger("computer")
17
15
  logger.setLevel(logging.INFO)
18
16
 
19
- class ComputerAgent[ComputerType:Computer, OperatorType:Operator](ChatBot):
17
+ def make_computer_toolkit(*, operator_cls: Type[Operator], computer_cls: Type[Computer], render_screen: Callable[[bytes],None]):
18
+
19
+ operator = operator_cls()
20
+ computer = computer_cls()
21
+ started = False
22
+
23
+ class ComputerTool(Tool):
24
+ def __init__(self, *, operator: Operator, computer: Computer, title = "computer_call", description = "handle computer calls from computer use preview", rules = [], thumbnail_url = None, defs = None):
25
+ super().__init__(
26
+ name="computer_call",
27
+ # TODO: give a correct schema
28
+ input_schema={
29
+ "additionalProperties" : False,
30
+ "type" : "object",
31
+ "required" : [],
32
+ "properties" : {}
33
+ },
34
+ title=title,
35
+ description=description,
36
+ rules=rules,
37
+ thumbnail_url=thumbnail_url,
38
+ defs=defs,
39
+
40
+ )
41
+ self.computer = computer
42
+
43
+
44
+ @property
45
+ def options(self):
46
+ return {
47
+ "type": "computer-preview",
48
+ "display_width": self.computer.dimensions[0],
49
+ "display_height": self.computer.dimensions[1],
50
+ "environment": self.computer.environment,
51
+ }
52
+
53
+ async def execute(self, context: ToolContext, *, arguments):
54
+
55
+ nonlocal started
56
+ if started == False:
57
+ await self.computer.__aenter__()
58
+ started = True
59
+
60
+
61
+ outputs = await operator.play(computer=self.computer, item=arguments)
62
+ for output in outputs:
63
+ if output["type"] == "computer_call_output":
64
+ if output["output"] != None:
65
+ if output["output"]["type"] == "input_image":
66
+
67
+ b64 : str = output["output"]["image_url"]
68
+ image_data_b64 = b64.split(",", 1)
69
+
70
+ image_bytes = base64.b64decode(image_data_b64[1])
71
+ render_screen(image_bytes)
72
+
73
+
74
+ nonlocal computer_toolkit
75
+ if len(computer_toolkit.tools) == 1:
76
+ # HACK: after looking at the page, add the other tools,
77
+ # if we add these first then the computer-use-preview mode fails if it calls them before using the computer
78
+ computer_toolkit.tools.extend([
79
+ ScreenshotTool(computer=computer),
80
+ GotoURL(computer=computer),
81
+ ])
82
+ return RawOutputs(outputs=outputs)
83
+
84
+ class ScreenshotTool(Tool):
85
+ def __init__(self, computer: Computer):
86
+ self.computer = computer
87
+
88
+ super().__init__(
89
+ name="screenshot",
90
+ # TODO: give a correct schema
91
+ input_schema={
92
+ "additionalProperties" : False,
93
+ "type" : "object",
94
+ "required" : ["full_page","save_path"],
95
+ "properties" : {
96
+ "full_page" : {
97
+ "type" : "boolean"
98
+ },
99
+ "save_path" : {
100
+ "type" : "string",
101
+ "description" : "a file path to save the screenshot to (should end with .png)"
102
+ }
103
+ }
104
+ },
105
+ description="take a screenshot of the current page",
106
+ )
107
+
108
+
109
+ async def execute(self, context: ToolContext, save_path: str, full_page: bool):
110
+ nonlocal started
111
+ if started == False:
112
+ await self.computer.__aenter__()
113
+ started = True
114
+
115
+ screenshot_bytes = await self.computer.screenshot_bytes(full_page=full_page)
116
+ handle = await context.room.storage.open(path=save_path, overwrite=True)
117
+ await context.room.storage.write(handle=handle, data=screenshot_bytes)
118
+ await context.room.storage.close(handle=handle)
119
+
120
+ return f"saved screenshot to {save_path}"
121
+
122
+ class GotoURL(Tool):
123
+ def __init__(self, computer: Computer):
124
+ self.computer = computer
125
+
126
+ super().__init__(
127
+ name="goto",
128
+ description="goes to a specific URL. Make sure it starts with http:// or https://",
129
+ # TODO: give a correct schema
130
+ input_schema={
131
+ "additionalProperties" : False,
132
+ "type" : "object",
133
+ "required" : ["url"],
134
+ "properties" : {
135
+ "url" : {
136
+ "type" : "string",
137
+ "description": "Fully qualified URL to navigate to.",
138
+ }
139
+ }
140
+ },
141
+ )
142
+
143
+
144
+ async def execute(self, context: ToolContext, url: str):
145
+ nonlocal started
146
+ if started == False:
147
+ await self.computer.__aenter__()
148
+ started = True
149
+
150
+ if url.startswith("https://") == False and url.startswith("http://") == False:
151
+ url = "https://"+url
152
+
153
+ await self.computer.goto(url)
154
+
155
+ render_screen(await self.computer.screenshot_bytes(full_page=False))
156
+
157
+
158
+ computer_tool = ComputerTool(computer=computer, operator=operator)
159
+
160
+ computer_toolkit = Toolkit(name="meshagent.openai.computer", tools=[
161
+ computer_tool
162
+ ])
163
+
164
+ return computer_toolkit
165
+
166
+ async def make_browserbase_toolkit(context: ToolContext, requirement: RequiredToolkit):
167
+ return make_computer_toolkit(operator_cls=Operator(), computer_cls=BrowserbaseBrowser())
168
+
169
+ register_toolkit_factory("browserbase", make_browserbase_toolkit)
170
+
171
+
172
+ class ComputerAgent(ChatBot):
20
173
  def __init__(self, *, name,
21
174
  title=None,
22
175
  description=None,
23
176
  requires=None,
24
177
  labels = None,
25
- computer_cls: ComputerType,
26
- operator_cls: OperatorType,
178
+ computer_cls: Type[Computer],
179
+ operator_cls: Type[Operator],
27
180
  rules: Optional[list[str]] = None,
28
181
  llm_adapter: Optional[LLMAdapter] = None,
29
182
  toolkits: list[Toolkit] = None
@@ -48,178 +201,28 @@ class ComputerAgent[ComputerType:Computer, OperatorType:Operator](ChatBot):
48
201
  self.operator_cls = operator_cls
49
202
 
50
203
 
51
- async def init_thread_context(self, *, thread_context: ChatThreadContext):
204
+ async def get_thread_toolkits(self, *, thread_context: ChatThreadContext, participant: RemoteParticipant):
52
205
 
53
- operator : Operator = self.operator_cls()
54
- computer : Computer = self.computer_cls()
55
- started = False
56
-
57
- class ComputerTool(Tool):
58
- def __init__(self, *, operator: Operator, computer: Computer, title = "computer_call", description = "handle computer calls from computer use preview", rules = [], thumbnail_url = None, defs = None):
59
- super().__init__(
60
- name="computer_call",
61
- # TODO: give a correct schema
62
- input_schema={
63
- "additionalProperties" : False,
64
- "type" : "object",
65
- "required" : [],
66
- "properties" : {}
67
- },
68
- title=title,
69
- description=description,
70
- rules=rules,
71
- thumbnail_url=thumbnail_url,
72
- defs=defs,
73
-
74
- )
75
- self.computer = computer
76
-
77
-
78
- @property
79
- def options(self):
80
- return {
81
- "type": "computer-preview",
82
- "display_width": self.computer.dimensions[0],
83
- "display_height": self.computer.dimensions[1],
84
- "environment": self.computer.environment,
85
- }
86
-
87
- async def execute(self, context: ToolContext, *, arguments):
88
-
89
- nonlocal started
90
- if started == False:
91
- await self.computer.__aenter__()
92
- started = True
93
-
94
- for participant in thread_context.participants:
95
- await context.room.messaging.send_message(
96
- to=participant,
97
- type="computer_use",
98
- message={
99
- "arguments" : arguments
100
- }
101
- )
102
-
103
- outputs = await operator.play(computer=self.computer, item=arguments)
104
- for output in outputs:
105
- if output["type"] == "computer_call_output":
106
- if output["output"] != None:
107
- if output["output"]["type"] == "input_image":
108
-
109
- b64 : str = output["output"]["image_url"]
110
- image_data_b64 = b64.split(",", 1)
111
-
112
- image_bytes = base64.b64decode(image_data_b64[1])
113
-
114
- for participant in thread_context.participants:
115
- context.room.messaging.send_message_nowait(
116
- to=participant,
117
- type="computer_screen",
118
- message={
119
- },
120
- attachment=image_bytes
121
- )
122
-
123
- nonlocal computer_toolkit
124
- if len(computer_toolkit.tools) == 1:
125
- # HACK: after looking at the page, add the other tools,
126
- # if we add these first then the computer-use-preview mode fails if it calls them before using the computer
127
- computer_toolkit.tools.extend([
128
- ScreenshotTool(computer=computer),
129
- GotoURL(computer=computer),
130
- ])
131
- return RawOutputs(outputs=outputs)
132
-
133
- class ScreenshotTool(Tool):
134
- def __init__(self, computer: Computer):
135
- self.computer = computer
136
-
137
- super().__init__(
138
- name="screenshot",
139
- # TODO: give a correct schema
140
- input_schema={
141
- "additionalProperties" : False,
142
- "type" : "object",
143
- "required" : ["full_page","save_path"],
144
- "properties" : {
145
- "full_page" : {
146
- "type" : "boolean"
147
- },
148
- "save_path" : {
149
- "type" : "string",
150
- "description" : "a file path to save the screenshot to (should end with .png)"
151
- }
152
- }
206
+ toolkits = await super().get_thread_toolkits(thread_context=thread_context, participant=participant)
207
+
208
+ def render_screen(image_bytes: bytes):
209
+ for participant in thread_context.participants:
210
+ self.room.messaging.send_message_nowait(
211
+ to=participant,
212
+ type="computer_screen",
213
+ message={
153
214
  },
154
- description="take a screenshot of the current page",
215
+ attachment=image_bytes
155
216
  )
156
-
157
217
 
158
- async def execute(self, context: ToolContext, save_path: str, full_page: bool):
159
- nonlocal started
160
- if started == False:
161
- await self.computer.__aenter__()
162
- started = True
163
-
164
- screenshot_bytes = await self.computer.screenshot_bytes(full_page=full_page)
165
- handle = await context.room.storage.open(path=save_path, overwrite=True)
166
- await context.room.storage.write(handle=handle, data=screenshot_bytes)
167
- await context.room.storage.close(handle=handle)
168
-
169
- return f"saved screenshot to {save_path}"
170
-
171
- class GotoURL(Tool):
172
- def __init__(self, computer: Computer):
173
- self.computer = computer
174
-
175
- super().__init__(
176
- name="goto",
177
- description="goes to a specific URL. Make sure it starts with http:// or https://",
178
- # TODO: give a correct schema
179
- input_schema={
180
- "additionalProperties" : False,
181
- "type" : "object",
182
- "required" : ["url"],
183
- "properties" : {
184
- "url" : {
185
- "type" : "string",
186
- "description": "Fully qualified URL to navigate to.",
187
- }
188
- }
189
- },
190
- )
191
-
192
-
193
- async def execute(self, context: ToolContext, url: str):
194
- nonlocal started
195
- if started == False:
196
- await self.computer.__aenter__()
197
- started = True
198
-
199
- if url.startswith("https://") == False and url.startswith("http://") == False:
200
- url = "https://"+url
201
-
202
- await self.computer.goto(url)
203
-
204
- # send an updated screen out
205
- for participant in thread_context.participants:
206
- context.room.messaging.send_message_nowait(
207
- to=participant,
208
- type="computer_screen",
209
- message={
210
- },
211
- attachment = await self.computer.screenshot_bytes(full_page=False)
212
- )
213
-
214
- computer_tool = ComputerTool(computer=computer, operator=operator)
215
-
216
- computer_toolkit = Toolkit(name="meshagent.openai.computer", tools=[
217
- computer_tool
218
- ])
219
-
220
- thread_context.toolkits = [
218
+ computer_toolkit = make_computer_toolkit(
219
+ operator_cls=self.operator_cls,
220
+ computer_cls=self.computer_cls,
221
+ render_screen=render_screen
222
+ )
223
+ return [
221
224
  computer_toolkit,
222
- *thread_context.toolkits
225
+ *toolkits
223
226
  ]
224
227
 
225
228
 
@@ -47,7 +47,7 @@ def sanitize_message(msg: dict) -> dict:
47
47
  return msg
48
48
 
49
49
 
50
- def create_response(**kwargs):
50
+ def track_response(**kwargs):
51
51
  url = "https://api.openai.com/v1/responses"
52
52
  headers = {
53
53
  "Authorization": f"Bearer {os.getenv('OPENAI_API_KEY')}",
@@ -0,0 +1 @@
1
+ __version__ = "0.0.8"
@@ -1,9 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshagent-computers
3
- Version: 0.0.7
3
+ Version: 0.0.8
4
4
  Summary: Computer Building Blocks for Meshagent
5
- Home-page:
6
- License: Apache License 2.0
5
+ License-Expression: Apache-2.0
7
6
  Project-URL: Documentation, https://meshagent.com
8
7
  Project-URL: Website, https://meshagent.com
9
8
  Project-URL: Source, https://github.com/meshagent
@@ -13,16 +12,10 @@ License-File: LICENSE
13
12
  Requires-Dist: pytest>=8.3.4
14
13
  Requires-Dist: pytest-asyncio>=0.24.0
15
14
  Requires-Dist: openai>=1.66.2
16
- Requires-Dist: meshagent-api>=0.0.7
17
- Requires-Dist: meshagent-agents>=0.0.7
18
- Requires-Dist: meshagent-tools>=0.0.7
15
+ Requires-Dist: meshagent-api>=0.0.8
16
+ Requires-Dist: meshagent-agents>=0.0.8
17
+ Requires-Dist: meshagent-tools>=0.0.8
19
18
  Requires-Dist: playwright~=1.50.0
20
19
  Requires-Dist: browserbase~=1.2.0
21
20
  Requires-Dist: scrapybara~=2.4.1
22
- Dynamic: description-content-type
23
- Dynamic: license
24
21
  Dynamic: license-file
25
- Dynamic: project-url
26
- Dynamic: requires-dist
27
- Dynamic: requires-python
28
- Dynamic: summary
@@ -1,5 +1,5 @@
1
- meshagent/computers/__init__.py,sha256=4aVM46U1kVLeN8rurNulc6_24a-rHSMIY0v0rc1ydOI,288
2
- meshagent/computers/agent.py,sha256=w9Yg9I2cfx-fNo9Y0TOcndxcoyOTz2wxRoijxGt9dB4,9089
1
+ meshagent/computers/__init__.py,sha256=PEzgkWE_sB6TrBA8crwWM7QaNPYWJisCKT81tk6pepQ,321
2
+ meshagent/computers/agent.py,sha256=AR1MDxuz7DiLul8H5aluU5z61Yt7lGqmDo6l1QKWRwA,8459
3
3
  meshagent/computers/base_playwright.py,sha256=NhQoOzWmekBmobQ2kvN7w4I3hZeyMIuMKI2eHbG1_mk,6017
4
4
  meshagent/computers/browserbase.py,sha256=ZT-ZDndkBD-MHD3UjxXpwpx7-_VCkkanjW1rIJr7-98,8161
5
5
  meshagent/computers/computer.py,sha256=465GKDV3cJgzp1SB5B0xzLoftMKU0AxAuBVrZ9drBBc,1072
@@ -7,9 +7,10 @@ meshagent/computers/docker.py,sha256=BGkrlspIYlg3WViaqctyRUye-fONfF40AvLXOQuXPQ8
7
7
  meshagent/computers/local_playwright.py,sha256=W7VhxIYEXn2BO3wZZVsc-hH-CU7NTssxFGO7pwz_n1I,919
8
8
  meshagent/computers/operator.py,sha256=JTyGvoPnKOqUDaZv58p_2n5Q6m6dm8V214VMIhVEu08,2913
9
9
  meshagent/computers/scrapybara.py,sha256=IoeV02QcBDkpW05A1NH-lc4jnBEmZ4SPsRo3nnXC3UE,7502
10
- meshagent/computers/utils.py,sha256=t9G9dzfJ5a0DSyrW1KNdzlhLJW_S6eGUcS39wnZyHU8,2081
11
- meshagent_computers-0.0.7.dist-info/licenses/LICENSE,sha256=eTt0SPW-sVNdkZe9PS_S8WfCIyLjRXRl7sUBWdlteFg,10254
12
- meshagent_computers-0.0.7.dist-info/METADATA,sha256=eYIzYXp2Ia_WFN1mjBrF7u9iPk08P-MZLifVB56PqRM,852
13
- meshagent_computers-0.0.7.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
14
- meshagent_computers-0.0.7.dist-info/top_level.txt,sha256=GlcXnHtRP6m7zlG3Df04M35OsHtNXy_DY09oFwWrH74,10
15
- meshagent_computers-0.0.7.dist-info/RECORD,,
10
+ meshagent/computers/utils.py,sha256=N7Ltjx3HfhJ-CYectNodGsBtddk6mSTHkCH0meqHT70,2080
11
+ meshagent/computers/version.py,sha256=ICOxGt7ta-t2F9uXLMXVo-4VDYGM0IEvKPStCf0y8Rk,21
12
+ meshagent_computers-0.0.8.dist-info/licenses/LICENSE,sha256=eTt0SPW-sVNdkZe9PS_S8WfCIyLjRXRl7sUBWdlteFg,10254
13
+ meshagent_computers-0.0.8.dist-info/METADATA,sha256=24jshdqcrgglfb-LQfoFjt2wLgqEmiOKwvcPJW8jEG8,706
14
+ meshagent_computers-0.0.8.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
15
+ meshagent_computers-0.0.8.dist-info/top_level.txt,sha256=GlcXnHtRP6m7zlG3Df04M35OsHtNXy_DY09oFwWrH74,10
16
+ meshagent_computers-0.0.8.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (77.0.3)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5