orgo 0.0.12__tar.gz → 0.0.14__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orgo
3
- Version: 0.0.12
3
+ Version: 0.0.14
4
4
  Summary: Computers for AI agents
5
5
  Author: Orgo Team
6
6
  License: MIT
@@ -35,6 +35,9 @@ computer.type("Hello world")
35
35
  computer.key("Enter")
36
36
  computer.screenshot() # Returns PIL Image
37
37
 
38
+ # Execute Python code
39
+ computer.exec("import pyautogui; pyautogui.click(512, 384)")
40
+
38
41
  # Cleanup
39
42
  computer.shutdown()
40
43
  ```
@@ -22,6 +22,9 @@ computer.type("Hello world")
22
22
  computer.key("Enter")
23
23
  computer.screenshot() # Returns PIL Image
24
24
 
25
+ # Execute Python code
26
+ computer.exec("import pyautogui; pyautogui.click(512, 384)")
27
+
25
28
  # Cleanup
26
29
  computer.shutdown()
27
30
  ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "orgo"
7
- version = "0.0.12"
7
+ version = "0.0.14"
8
8
  description = "Computers for AI agents"
9
9
  authors = [{name = "Orgo Team"}]
10
10
  license = {text = "MIT"}
@@ -1,3 +1,4 @@
1
+ # src/orgo/__init__.py
1
2
  """Orgo SDK: Desktop infrastructure for AI agents"""
2
3
 
3
4
  from .computer import Computer
@@ -1,3 +1,4 @@
1
+ # src/orgo/api/__init__.py
1
2
  """API package for Orgo SDK"""
2
3
 
3
4
  from .client import ApiClient
@@ -1,5 +1,5 @@
1
- """API client for Orgo service"""
2
1
  # src/orgo/api/client.py
2
+ """API client for Orgo service"""
3
3
 
4
4
  import requests
5
5
  from typing import Dict, Any, Optional
@@ -96,6 +96,13 @@ class ApiClient:
96
96
  "command": command
97
97
  })
98
98
 
99
+ def execute_python(self, project_id: str, code: str, timeout: int = 10) -> Dict[str, Any]:
100
+ """Execute Python code on the computer"""
101
+ return self._request("POST", f"computers/{project_id}/exec", {
102
+ "code": code,
103
+ "timeout": timeout
104
+ })
105
+
99
106
  def wait(self, project_id: str, seconds: float) -> Dict[str, Any]:
100
107
  return self._request("POST", f"computers/{project_id}/wait", {
101
108
  "seconds": seconds
@@ -1,15 +1,18 @@
1
- """Computer class for interacting with Orgo virtual environments"""
2
1
  # src/orgo/computer.py
3
-
2
+ """Computer class for interacting with Orgo virtual environments"""
4
3
  import os
5
4
  import io
6
5
  import base64
6
+ import logging
7
7
  from typing import Dict, List, Any, Optional, Callable, Union
8
8
  from PIL import Image
9
+ from requests.exceptions import RequestException
9
10
 
10
11
  from .api.client import ApiClient
11
12
  from .prompt import get_provider
13
+ from .project import ProjectManager
12
14
 
15
+ logger = logging.getLogger(__name__)
13
16
 
14
17
  class Computer:
15
18
  def __init__(self, project_id=None, api_key=None, config=None, base_api_url=None):
@@ -26,16 +29,31 @@ class Computer:
26
29
  self.base_api_url = base_api_url
27
30
  self.api = ApiClient(self.api_key, self.base_api_url)
28
31
 
32
+ # Look for a saved project ID if none was provided
33
+ if project_id is None:
34
+ project_id = ProjectManager.load_project_id()
35
+
29
36
  if project_id:
30
- self.project_id = project_id
31
- self._info = self.api.connect_computer(project_id)
37
+ try:
38
+ self.project_id = project_id
39
+ self._info = self.api.connect_computer(project_id)
40
+ except (RequestException, ValueError) as e:
41
+ logger.warning(f"Could not connect to saved project {project_id}: {e}")
42
+ self._create_new_computer(config)
32
43
  else:
33
- response = self.api.create_computer(config)
34
- self.project_id = response.get("name")
35
- self._info = response
44
+ self._create_new_computer(config)
36
45
 
46
+ def _create_new_computer(self, config=None):
47
+ """Create a new computer instance and save its ID"""
48
+ response = self.api.create_computer(config)
49
+ self.project_id = response.get("name")
50
+ self._info = response
51
+
37
52
  if not self.project_id:
38
53
  raise ValueError("Failed to initialize computer: No project ID returned")
54
+
55
+ # Save the project ID for future use
56
+ ProjectManager.save_project_id(self.project_id)
39
57
 
40
58
  def status(self) -> Dict[str, Any]:
41
59
  """Get current computer status"""
@@ -93,6 +111,37 @@ class Computer:
93
111
  response = self.api.execute_bash(self.project_id, command)
94
112
  return response.get("output", "")
95
113
 
114
+ def exec(self, code: str, timeout: int = 10) -> Dict[str, Any]:
115
+ """
116
+ Execute Python code on the remote computer.
117
+
118
+ Args:
119
+ code: Python code to execute
120
+ timeout: Maximum execution time in seconds (default: 10, max: 300)
121
+
122
+ Returns:
123
+ Dict with keys:
124
+ - success: bool indicating if execution completed without errors
125
+ - output: str containing stdout output
126
+ - error: str containing error message if any
127
+ - error_type: str with exception type name if error occurred
128
+ - timeout: bool indicating if execution timed out
129
+
130
+ Example:
131
+ result = computer.exec('''
132
+ import os
133
+ print(f"Current directory: {os.getcwd()}")
134
+ print(f"Files: {os.listdir('.')}")
135
+ ''')
136
+
137
+ if result['success']:
138
+ print(result['output'])
139
+ else:
140
+ print(f"Error: {result['error']}")
141
+ """
142
+ response = self.api.execute_python(self.project_id, code, timeout)
143
+ return response
144
+
96
145
  def wait(self, seconds: float) -> Dict[str, Any]:
97
146
  """Wait for specified number of seconds"""
98
147
  return self.api.wait(self.project_id, seconds)
@@ -130,28 +179,6 @@ class Computer:
130
179
 
131
180
  Returns:
132
181
  List of messages from the conversation
133
-
134
- Examples:
135
- # Simple usage with environment variables
136
- computer.prompt("Open Firefox and search for Python tutorials")
137
-
138
- # With explicit API key
139
- computer.prompt("Open Terminal and list files", api_key="your-anthropic-key")
140
-
141
- # With callback for progress updates
142
- computer.prompt("Create a new text file", callback=my_callback_function)
143
-
144
- # With thinking enabled (Claude 3.7 Sonnet)
145
- computer.prompt(
146
- "Analyze a complex webpage",
147
- thinking_enabled=True
148
- )
149
-
150
- # With custom screenshot management
151
- computer.prompt(
152
- "Perform a complex multi-step task",
153
- max_saved_screenshots=10 # Keep more screenshots for complex tasks
154
- )
155
182
  """
156
183
  # Get the provider instance
157
184
  provider_instance = get_provider(provider)
@@ -0,0 +1,51 @@
1
+ # src/orgo/project.py
2
+ """Project management for Orgo virtual environments"""
3
+ import os
4
+ import json
5
+ import logging
6
+ from typing import Optional
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ class ProjectManager:
11
+ """Manages project persistence for Orgo computers"""
12
+
13
+ @staticmethod
14
+ def load_project_id() -> Optional[str]:
15
+ """Load project ID from local config file"""
16
+ config_path = ProjectManager._get_config_path()
17
+
18
+ if not os.path.exists(config_path):
19
+ return None
20
+
21
+ try:
22
+ with open(config_path, 'r') as f:
23
+ data = json.load(f)
24
+ return data.get('project_id')
25
+ except (json.JSONDecodeError, IOError, OSError) as e:
26
+ logger.warning(f"Error loading project config: {str(e)}")
27
+ return None
28
+
29
+ @staticmethod
30
+ def save_project_id(project_id: str) -> None:
31
+ """Save project ID to local config file"""
32
+ config_dir = ProjectManager._get_project_dir()
33
+ config_path = ProjectManager._get_config_path()
34
+
35
+ try:
36
+ os.makedirs(config_dir, exist_ok=True)
37
+ with open(config_path, 'w') as f:
38
+ json.dump({'project_id': project_id}, f, indent=2)
39
+ except (IOError, OSError) as e:
40
+ logger.error(f"Failed to save project ID: {str(e)}")
41
+ raise RuntimeError(f"Failed to save project configuration: {str(e)}") from e
42
+
43
+ @staticmethod
44
+ def _get_project_dir() -> str:
45
+ """Get the project directory path"""
46
+ return os.path.join(os.getcwd(), ".orgo")
47
+
48
+ @staticmethod
49
+ def _get_config_path() -> str:
50
+ """Get the full path to the config file"""
51
+ return os.path.join(ProjectManager._get_project_dir(), "project.json")
@@ -1,3 +1,4 @@
1
+ # src/orgo/prompt.py
1
2
  """
2
3
  Prompt module for interacting with virtual computers using AI models.
3
4
  """
@@ -1,3 +1,4 @@
1
+ # src/orgo/utils/__init__.py
1
2
  """Utility functions for Orgo SDK"""
2
3
 
3
4
  from .auth import get_api_key
@@ -1,3 +1,4 @@
1
+ # src/orgo/utils/auth.py
1
2
  """Authentication utilities for Orgo SDK"""
2
3
 
3
4
  import os
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orgo
3
- Version: 0.0.12
3
+ Version: 0.0.14
4
4
  Summary: Computers for AI agents
5
5
  Author: Orgo Team
6
6
  License: MIT
@@ -35,6 +35,9 @@ computer.type("Hello world")
35
35
  computer.key("Enter")
36
36
  computer.screenshot() # Returns PIL Image
37
37
 
38
+ # Execute Python code
39
+ computer.exec("import pyautogui; pyautogui.click(512, 384)")
40
+
38
41
  # Cleanup
39
42
  computer.shutdown()
40
43
  ```
@@ -2,16 +2,13 @@ README.md
2
2
  pyproject.toml
3
3
  src/orgo/__init__.py
4
4
  src/orgo/computer.py
5
+ src/orgo/project.py
5
6
  src/orgo/prompt.py
6
7
  src/orgo.egg-info/PKG-INFO
7
8
  src/orgo.egg-info/SOURCES.txt
8
9
  src/orgo.egg-info/dependency_links.txt
9
10
  src/orgo.egg-info/requires.txt
10
11
  src/orgo.egg-info/top_level.txt
11
- src/orgo/adapters/__init__.py
12
- src/orgo/adapters/anthropic.py
13
- src/orgo/adapters/base.py
14
- src/orgo/adapters/openai.py
15
12
  src/orgo/api/__init__.py
16
13
  src/orgo/api/client.py
17
14
  src/orgo/utils/__init__.py
@@ -1,7 +0,0 @@
1
- """Adapters for integrating Orgo with AI models"""
2
-
3
- from .base import BaseAdapter
4
- from .anthropic import AnthropicAdapter
5
- from .openai import OpenAIAdapter
6
-
7
- __all__ = ["BaseAdapter", "AnthropicAdapter", "OpenAIAdapter"]
@@ -1,64 +0,0 @@
1
- """Adapter for Anthropic's Claude"""
2
-
3
- from typing import Dict, Any, Optional
4
- from .base import BaseAdapter
5
-
6
- class AnthropicAdapter(BaseAdapter):
7
- def get_tool_definition(self) -> Dict[str, Any]:
8
- return {
9
- "name": "computer",
10
- "description": "Controls a virtual computer to automate tasks",
11
- "type": "function",
12
- "parameters": {
13
- "type": "object",
14
- "properties": {
15
- "action": {
16
- "type": "string",
17
- "enum": ["left_click", "right_click", "double_click", "type", "key", "scroll", "screenshot"],
18
- "description": "The action to perform on the computer"
19
- },
20
- "coordinate": {
21
- "type": "array",
22
- "items": {"type": "number"},
23
- "description": "The x,y coordinates for click actions",
24
- "minItems": 2,
25
- "maxItems": 2
26
- },
27
- "text": {
28
- "type": "string",
29
- "description": "The text to type or key to press"
30
- },
31
- "direction": {
32
- "type": "string",
33
- "enum": ["up", "down", "left", "right"],
34
- "description": "The direction to scroll"
35
- },
36
- "amount": {
37
- "type": "number",
38
- "description": "The amount to scroll"
39
- }
40
- },
41
- "required": ["action"],
42
- "additionalProperties": False
43
- }
44
- }
45
-
46
- def format_result(self, tool_id: str, output: Optional[str] = None, error: Optional[str] = None) -> Dict[str, Any]:
47
- screenshot = self.computer.get_base64()
48
- result = {
49
- "type": "tool_result",
50
- "id": tool_id,
51
- "content": {
52
- "type": "image",
53
- "source": {
54
- "type": "base64",
55
- "media_type": "image/png",
56
- "data": screenshot
57
- }
58
- }
59
- }
60
-
61
- if error:
62
- result["error"] = error
63
-
64
- return result
@@ -1,14 +0,0 @@
1
- """Base adapter for AI model integration"""
2
-
3
- from typing import Dict, Any, Optional
4
- from ..computer import Computer
5
-
6
- class BaseAdapter:
7
- def __init__(self, computer: Computer):
8
- self.computer = computer
9
-
10
- def get_tool_definition(self) -> Dict[str, Any]:
11
- raise NotImplementedError("Subclasses must implement get_tool_definition()")
12
-
13
- def format_result(self, tool_id: str, output: Optional[str] = None, error: Optional[str] = None) -> Dict[str, Any]:
14
- raise NotImplementedError("Subclasses must implement format_result()")
@@ -1,69 +0,0 @@
1
- """Adapter for OpenAI models"""
2
-
3
- from typing import Dict, Any, Optional
4
- from .base import BaseAdapter
5
-
6
- class OpenAIAdapter(BaseAdapter):
7
- def get_tool_definition(self) -> Dict[str, Any]:
8
- return {
9
- "type": "function",
10
- "function": {
11
- "name": "computer",
12
- "description": "Controls a virtual computer to automate tasks",
13
- "parameters": {
14
- "type": "object",
15
- "properties": {
16
- "action": {
17
- "type": "string",
18
- "enum": ["click", "right_click", "double_click", "type", "key", "scroll", "screenshot"],
19
- "description": "The action to perform on the computer"
20
- },
21
- "x": {
22
- "type": "number",
23
- "description": "The x coordinate for click actions"
24
- },
25
- "y": {
26
- "type": "number",
27
- "description": "The y coordinate for click actions"
28
- },
29
- "text": {
30
- "type": "string",
31
- "description": "The text to type or key to press"
32
- },
33
- "direction": {
34
- "type": "string",
35
- "enum": ["up", "down", "left", "right"],
36
- "description": "The direction to scroll"
37
- },
38
- "amount": {
39
- "type": "number",
40
- "description": "The amount to scroll"
41
- }
42
- },
43
- "required": ["action"],
44
- "additionalProperties": False
45
- }
46
- }
47
- }
48
-
49
- def format_result(self, tool_id: str, output: Optional[str] = None, error: Optional[str] = None) -> Dict[str, Any]:
50
- screenshot = self.computer.get_base64()
51
- result = {
52
- "tool_call_id": tool_id,
53
- "content": [
54
- {
55
- "type": "image_url",
56
- "image_url": {
57
- "url": f"data:image/png;base64,{screenshot}"
58
- }
59
- }
60
- ]
61
- }
62
-
63
- if error:
64
- result["content"].insert(0, {
65
- "type": "text",
66
- "text": f"Error: {error}"
67
- })
68
-
69
- return result
File without changes