erdo 0.1.31__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.
Files changed (48) hide show
  1. erdo/__init__.py +35 -0
  2. erdo/_generated/__init__.py +18 -0
  3. erdo/_generated/actions/__init__.py +34 -0
  4. erdo/_generated/actions/analysis.py +179 -0
  5. erdo/_generated/actions/bot.py +186 -0
  6. erdo/_generated/actions/codeexec.py +199 -0
  7. erdo/_generated/actions/llm.py +148 -0
  8. erdo/_generated/actions/memory.py +463 -0
  9. erdo/_generated/actions/pdfextractor.py +97 -0
  10. erdo/_generated/actions/resource_definitions.py +296 -0
  11. erdo/_generated/actions/sqlexec.py +90 -0
  12. erdo/_generated/actions/utils.py +475 -0
  13. erdo/_generated/actions/webparser.py +119 -0
  14. erdo/_generated/actions/websearch.py +85 -0
  15. erdo/_generated/condition/__init__.py +556 -0
  16. erdo/_generated/internal.py +51 -0
  17. erdo/_generated/internal_actions.py +91 -0
  18. erdo/_generated/parameters.py +17 -0
  19. erdo/_generated/secrets.py +17 -0
  20. erdo/_generated/template_functions.py +55 -0
  21. erdo/_generated/types.py +3907 -0
  22. erdo/actions/__init__.py +40 -0
  23. erdo/bot_permissions.py +266 -0
  24. erdo/cli_entry.py +73 -0
  25. erdo/conditions/__init__.py +11 -0
  26. erdo/config/__init__.py +5 -0
  27. erdo/config/config.py +140 -0
  28. erdo/formatting.py +279 -0
  29. erdo/install_cli.py +140 -0
  30. erdo/integrations.py +131 -0
  31. erdo/invoke/__init__.py +11 -0
  32. erdo/invoke/client.py +234 -0
  33. erdo/invoke/invoke.py +555 -0
  34. erdo/state.py +376 -0
  35. erdo/sync/__init__.py +17 -0
  36. erdo/sync/client.py +95 -0
  37. erdo/sync/extractor.py +492 -0
  38. erdo/sync/sync.py +327 -0
  39. erdo/template.py +136 -0
  40. erdo/test/__init__.py +41 -0
  41. erdo/test/evaluate.py +272 -0
  42. erdo/test/runner.py +263 -0
  43. erdo/types.py +1431 -0
  44. erdo-0.1.31.dist-info/METADATA +471 -0
  45. erdo-0.1.31.dist-info/RECORD +48 -0
  46. erdo-0.1.31.dist-info/WHEEL +4 -0
  47. erdo-0.1.31.dist-info/entry_points.txt +2 -0
  48. erdo-0.1.31.dist-info/licenses/LICENSE +22 -0
@@ -0,0 +1,40 @@
1
+ # DO NOT EDIT THIS FILE MANUALLY - it will be overwritten.
2
+ # Generated by: erdo gen-client
3
+ """
4
+ Actions module re-exports.
5
+
6
+ This module provides clean access to all action services from the top level.
7
+ Users can import like: from erdo.actions import llm, bot, memory
8
+ """
9
+
10
+ # Import all services as modules
11
+ from .._generated.actions import analysis # noqa: F401
12
+ from .._generated.actions import bot # noqa: F401
13
+ from .._generated.actions import codeexec # noqa: F401
14
+ from .._generated.actions import llm # noqa: F401
15
+ from .._generated.actions import memory # noqa: F401
16
+ from .._generated.actions import resource_definitions # noqa: F401
17
+ from .._generated.actions import sqlexec # noqa: F401
18
+ from .._generated.actions import utils # noqa: F401
19
+ from .._generated.actions import webparser # noqa: F401
20
+ from .._generated.actions import websearch # noqa: F401
21
+
22
+ # Import internal actions explicitly
23
+ from .._generated.internal_actions import checkpoint_attempt # noqa: F401
24
+ from .._generated.internal_actions import refresh_resource # noqa: F401
25
+
26
+ # Make all services available for import
27
+ __all__ = [
28
+ "analysis",
29
+ "bot",
30
+ "checkpoint_attempt",
31
+ "codeexec",
32
+ "llm",
33
+ "memory",
34
+ "refresh_resource",
35
+ "resource_definitions",
36
+ "sqlexec",
37
+ "utils",
38
+ "webparser",
39
+ "websearch",
40
+ ]
@@ -0,0 +1,266 @@
1
+ """Bot permission management for the Erdo SDK."""
2
+
3
+ import os
4
+ from typing import Any, Dict, Optional
5
+
6
+ import requests
7
+
8
+
9
+ class BotPermissions:
10
+ """Manage RBAC permissions for bots."""
11
+
12
+ def __init__(self, base_url: Optional[str] = None):
13
+ """Initialize bot permissions manager.
14
+
15
+ Args:
16
+ base_url: Erdo server base URL. Defaults to ERDO_SERVER_URL env var or localhost:4000
17
+ """
18
+ self.base_url = base_url or os.getenv(
19
+ "ERDO_SERVER_URL", "http://localhost:4000"
20
+ )
21
+ self.session = requests.Session()
22
+
23
+ # Set auth token if available
24
+ token = os.getenv("ERDO_AUTH_TOKEN")
25
+ if token:
26
+ self.session.headers.update({"Authorization": f"Bearer {token}"})
27
+
28
+ def set_public_access(
29
+ self, bot_id: str, is_public: bool = True, permission_level: str = "view"
30
+ ) -> bool:
31
+ """Set public access for a bot.
32
+
33
+ Args:
34
+ bot_id: Bot ID
35
+ is_public: Whether to make the bot public
36
+ permission_level: Permission level for public access (view, comment, edit, admin)
37
+
38
+ Returns:
39
+ True if successful, False otherwise
40
+ """
41
+ try:
42
+ url = f"{self.base_url}/rbac/bot/{bot_id}/public"
43
+ data = {
44
+ "isPublic": is_public,
45
+ "level": permission_level if is_public else None,
46
+ }
47
+ response = self.session.put(url, json=data)
48
+ return response.status_code == 200
49
+ except Exception as e:
50
+ print(f"Error setting public access: {e}")
51
+ return False
52
+
53
+ def set_user_permission(
54
+ self, bot_id: str, user_id: str, permission_level: str
55
+ ) -> bool:
56
+ """Set permissions for a specific user.
57
+
58
+ Args:
59
+ bot_id: Bot ID
60
+ user_id: User ID
61
+ permission_level: Permission level (view, comment, edit, admin, owner)
62
+
63
+ Returns:
64
+ True if successful, False otherwise
65
+ """
66
+ try:
67
+ url = f"{self.base_url}/rbac/bot/{bot_id}/user/{user_id}"
68
+ data = {"level": permission_level}
69
+ response = self.session.put(url, json=data)
70
+ return response.status_code == 200
71
+ except Exception as e:
72
+ print(f"Error setting user permission: {e}")
73
+ return False
74
+
75
+ def set_org_permission(
76
+ self, bot_id: str, org_id: str, permission_level: str
77
+ ) -> bool:
78
+ """Set permissions for an organization.
79
+
80
+ Args:
81
+ bot_id: Bot ID
82
+ org_id: Organization ID
83
+ permission_level: Permission level (view, comment, edit, admin, owner)
84
+
85
+ Returns:
86
+ True if successful, False otherwise
87
+ """
88
+ try:
89
+ url = f"{self.base_url}/rbac/bot/{bot_id}/org/{org_id}"
90
+ data = {"level": permission_level}
91
+ response = self.session.put(url, json=data)
92
+ return response.status_code == 200
93
+ except Exception as e:
94
+ print(f"Error setting org permission: {e}")
95
+ return False
96
+
97
+ def remove_user_permission(self, bot_id: str, user_id: str) -> bool:
98
+ """Remove permissions for a specific user.
99
+
100
+ Args:
101
+ bot_id: Bot ID
102
+ user_id: User ID
103
+
104
+ Returns:
105
+ True if successful, False otherwise
106
+ """
107
+ try:
108
+ url = f"{self.base_url}/rbac/bot/{bot_id}/user/{user_id}"
109
+ response = self.session.delete(url)
110
+ return response.status_code == 200
111
+ except Exception as e:
112
+ print(f"Error removing user permission: {e}")
113
+ return False
114
+
115
+ def remove_org_permission(self, bot_id: str, org_id: str) -> bool:
116
+ """Remove permissions for an organization.
117
+
118
+ Args:
119
+ bot_id: Bot ID
120
+ org_id: Organization ID
121
+
122
+ Returns:
123
+ True if successful, False otherwise
124
+ """
125
+ try:
126
+ url = f"{self.base_url}/rbac/bot/{bot_id}/org/{org_id}"
127
+ response = self.session.delete(url)
128
+ return response.status_code == 200
129
+ except Exception as e:
130
+ print(f"Error removing org permission: {e}")
131
+ return False
132
+
133
+ def get_permissions(self, bot_id: str) -> Optional[Dict[str, Any]]:
134
+ """Get all permissions for a bot.
135
+
136
+ Args:
137
+ bot_id: Bot ID
138
+
139
+ Returns:
140
+ Dictionary with permission details or None if error
141
+ """
142
+ try:
143
+ url = f"{self.base_url}/rbac/permissions/bot/{bot_id}"
144
+ response = self.session.get(url)
145
+ if response.status_code == 200:
146
+ return response.json()
147
+ return None
148
+ except Exception as e:
149
+ print(f"Error getting permissions: {e}")
150
+ return None
151
+
152
+ def check_access(self, bot_id: str, permission_level: str = "view") -> bool:
153
+ """Check if current user has access to a bot.
154
+
155
+ Args:
156
+ bot_id: Bot ID
157
+ permission_level: Required permission level
158
+
159
+ Returns:
160
+ True if user has access, False otherwise
161
+ """
162
+ try:
163
+ url = f"{self.base_url}/rbac/access/bot/{bot_id}/{permission_level}"
164
+ response = self.session.get(url)
165
+ if response.status_code == 200:
166
+ data = response.json()
167
+ return data.get("hasAccess", False)
168
+ return False
169
+ except Exception as e:
170
+ print(f"Error checking access: {e}")
171
+ return False
172
+
173
+ def invite_user(self, bot_id: str, email: str, permission_level: str) -> bool:
174
+ """Invite a user to access a bot.
175
+
176
+ Args:
177
+ bot_id: Bot ID
178
+ email: User's email address
179
+ permission_level: Permission level to grant
180
+
181
+ Returns:
182
+ True if successful, False otherwise
183
+ """
184
+ try:
185
+ url = f"{self.base_url}/rbac/resource/bot/{bot_id}/invite"
186
+ data = {"email": email, "permissionLevel": permission_level}
187
+ response = self.session.post(url, json=data)
188
+ return response.status_code == 200
189
+ except Exception as e:
190
+ print(f"Error inviting user: {e}")
191
+ return False
192
+
193
+
194
+ # Convenience functions for direct use
195
+ def set_bot_public(
196
+ bot_id: str, is_public: bool = True, permission_level: str = "view"
197
+ ) -> bool:
198
+ """Set public access for a bot.
199
+
200
+ Args:
201
+ bot_id: Bot ID
202
+ is_public: Whether to make the bot public
203
+ permission_level: Permission level for public access
204
+
205
+ Returns:
206
+ True if successful, False otherwise
207
+ """
208
+ permissions = BotPermissions()
209
+ return permissions.set_public_access(bot_id, is_public, permission_level)
210
+
211
+
212
+ def set_bot_user_permission(bot_id: str, user_id: str, permission_level: str) -> bool:
213
+ """Set permissions for a user on a bot.
214
+
215
+ Args:
216
+ bot_id: Bot ID
217
+ user_id: User ID
218
+ permission_level: Permission level
219
+
220
+ Returns:
221
+ True if successful, False otherwise
222
+ """
223
+ permissions = BotPermissions()
224
+ return permissions.set_user_permission(bot_id, user_id, permission_level)
225
+
226
+
227
+ def set_bot_org_permission(bot_id: str, org_id: str, permission_level: str) -> bool:
228
+ """Set permissions for an organization on a bot.
229
+
230
+ Args:
231
+ bot_id: Bot ID
232
+ org_id: Organization ID
233
+ permission_level: Permission level
234
+
235
+ Returns:
236
+ True if successful, False otherwise
237
+ """
238
+ permissions = BotPermissions()
239
+ return permissions.set_org_permission(bot_id, org_id, permission_level)
240
+
241
+
242
+ def get_bot_permissions(bot_id: str) -> Optional[Dict[str, Any]]:
243
+ """Get all permissions for a bot.
244
+
245
+ Args:
246
+ bot_id: Bot ID
247
+
248
+ Returns:
249
+ Dictionary with permission details or None if error
250
+ """
251
+ permissions = BotPermissions()
252
+ return permissions.get_permissions(bot_id)
253
+
254
+
255
+ def check_bot_access(bot_id: str, permission_level: str = "view") -> bool:
256
+ """Check if current user has access to a bot.
257
+
258
+ Args:
259
+ bot_id: Bot ID
260
+ permission_level: Required permission level
261
+
262
+ Returns:
263
+ True if user has access, False otherwise
264
+ """
265
+ permissions = BotPermissions()
266
+ return permissions.check_access(bot_id, permission_level)
erdo/cli_entry.py ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env python3
2
+ """CLI entry point that executes the downloaded Erdo CLI binary."""
3
+
4
+ import platform
5
+ import shutil
6
+ import subprocess
7
+ import sys
8
+ from pathlib import Path
9
+ from typing import Optional
10
+
11
+
12
+ def get_binary_path() -> Path:
13
+ """Get the path to the CLI binary."""
14
+ package_dir = Path(__file__).parent
15
+ bin_dir = package_dir / "bin"
16
+
17
+ # Determine binary name based on platform
18
+ if platform.system().lower() == "windows":
19
+ binary_name = "erdo.exe"
20
+ else:
21
+ binary_name = "erdo"
22
+
23
+ return bin_dir / binary_name
24
+
25
+
26
+ def find_system_cli() -> Optional[str]:
27
+ """Find erdo CLI in system PATH, excluding pyenv shims."""
28
+ erdo_path = shutil.which("erdo")
29
+ if erdo_path and "pyenv/shims" not in erdo_path:
30
+ return erdo_path
31
+ return None
32
+
33
+
34
+ def main() -> None:
35
+ """Main CLI entry point."""
36
+ # First, check if erdo is available in system PATH
37
+ system_cli = find_system_cli()
38
+ if system_cli:
39
+ try:
40
+ result = subprocess.run([system_cli] + sys.argv[1:])
41
+ sys.exit(result.returncode)
42
+ except Exception as e:
43
+ print(f"Error executing system CLI: {e}")
44
+ # Fall through to try local binary
45
+
46
+ # Check for local binary
47
+ binary_path = get_binary_path()
48
+ if not binary_path.exists():
49
+ try:
50
+ from .install_cli import download_and_install_cli
51
+
52
+ download_and_install_cli()
53
+ except Exception as e:
54
+ print(f"Failed to download CLI: {e}")
55
+ print(
56
+ "Please manually download from: https://github.com/erdoai/homebrew-tap/releases"
57
+ )
58
+ sys.exit(1)
59
+
60
+ # Execute the local binary with all arguments
61
+ try:
62
+ result = subprocess.run([str(binary_path)] + sys.argv[1:])
63
+ sys.exit(result.returncode)
64
+ except FileNotFoundError:
65
+ print(f"Error: CLI binary not found at {binary_path}")
66
+ sys.exit(1)
67
+ except Exception as e:
68
+ print(f"Error executing CLI: {e}")
69
+ sys.exit(1)
70
+
71
+
72
+ if __name__ == "__main__":
73
+ main()
@@ -0,0 +1,11 @@
1
+ # DO NOT EDIT THIS FILE MANUALLY - it will be overwritten.
2
+ # Generated by: erdo gen-client
3
+ """
4
+ Conditions module for clean imports.
5
+
6
+ This module re-exports all condition classes from the generated condition module
7
+ so users can import like: from erdo.conditions import IsSuccess, And
8
+ """
9
+
10
+ # Re-export all condition classes from _generated
11
+ from .._generated.condition import * # noqa: F403,F401
@@ -0,0 +1,5 @@
1
+ """Configuration management for Erdo SDK."""
2
+
3
+ from .config import Config, get_config, set_config
4
+
5
+ __all__ = ["Config", "get_config", "set_config"]
erdo/config/config.py ADDED
@@ -0,0 +1,140 @@
1
+ """Configuration management for Erdo SDK."""
2
+
3
+ import json
4
+ import os
5
+ from pathlib import Path
6
+ from typing import Optional
7
+
8
+
9
+ class Config:
10
+ """Configuration manager for Erdo SDK."""
11
+
12
+ def __init__(
13
+ self, endpoint: Optional[str] = None, auth_token: Optional[str] = None
14
+ ):
15
+ """Initialize configuration.
16
+
17
+ Args:
18
+ endpoint: API endpoint URL. If not provided, will try environment variable or config file.
19
+ auth_token: Authentication token. If not provided, will try environment variable or config file.
20
+ """
21
+ self._endpoint = endpoint
22
+ self._auth_token = auth_token
23
+ self._config_yaml = Path.home() / ".erdo" / "config.yaml"
24
+ self._config_json = Path.home() / ".erdo" / "config.json"
25
+
26
+ @property
27
+ def endpoint(self) -> str:
28
+ """Get the API endpoint."""
29
+ if self._endpoint:
30
+ return self._endpoint
31
+
32
+ # Try environment variable
33
+ env_endpoint = os.environ.get("ERDO_ENDPOINT")
34
+ if env_endpoint:
35
+ return env_endpoint
36
+
37
+ # Try config file
38
+ config = self._load_config_file()
39
+ if config and "endpoint" in config:
40
+ return config["endpoint"]
41
+
42
+ raise ValueError(
43
+ "No endpoint configured. Set ERDO_ENDPOINT environment variable or run 'erdo configure'"
44
+ )
45
+
46
+ @property
47
+ def auth_token(self) -> str:
48
+ """Get the authentication token."""
49
+ if self._auth_token:
50
+ return self._auth_token
51
+
52
+ # Try environment variable
53
+ env_token = os.environ.get("ERDO_AUTH_TOKEN")
54
+ if env_token:
55
+ return env_token
56
+
57
+ # Try config file
58
+ config = self._load_config_file()
59
+ if config and "auth_token" in config:
60
+ return config["auth_token"]
61
+
62
+ raise ValueError(
63
+ "No auth token configured. Set ERDO_AUTH_TOKEN environment variable or run 'erdo login'"
64
+ )
65
+
66
+ def _load_config_file(self) -> Optional[dict]:
67
+ """Load configuration from file (supports both YAML and JSON)."""
68
+ # Try YAML first (CLI default format)
69
+ if self._config_yaml.exists():
70
+ try:
71
+ import yaml
72
+
73
+ with open(self._config_yaml, "r") as f:
74
+ return yaml.safe_load(f)
75
+ except Exception:
76
+ try:
77
+ # Fallback to simple parsing if yaml not available
78
+ with open(self._config_yaml, "r") as f:
79
+ config = {}
80
+ for line in f:
81
+ if ": " in line:
82
+ key, value = line.strip().split(": ", 1)
83
+ config[key] = value
84
+ return config
85
+ except Exception:
86
+ pass
87
+
88
+ # Try JSON as fallback
89
+ if self._config_json.exists():
90
+ try:
91
+ with open(self._config_json, "r") as f:
92
+ return json.load(f)
93
+ except (json.JSONDecodeError, IOError):
94
+ pass
95
+
96
+ return None
97
+
98
+ def save(self):
99
+ """Save configuration to file (as JSON)."""
100
+ self._config_json.parent.mkdir(parents=True, exist_ok=True)
101
+
102
+ config = {}
103
+ if self._endpoint:
104
+ config["endpoint"] = self._endpoint
105
+ if self._auth_token:
106
+ config["auth_token"] = self._auth_token
107
+
108
+ with open(self._config_json, "w") as f:
109
+ json.dump(config, f, indent=2)
110
+
111
+ def set_endpoint(self, endpoint: str):
112
+ """Set the API endpoint."""
113
+ self._endpoint = endpoint
114
+
115
+ def set_auth_token(self, auth_token: str):
116
+ """Set the authentication token."""
117
+ self._auth_token = auth_token
118
+
119
+
120
+ # Global config instance
121
+ _config: Optional[Config] = None
122
+
123
+
124
+ def get_config() -> Config:
125
+ """Get the global configuration instance."""
126
+ global _config
127
+ if _config is None:
128
+ _config = Config()
129
+ return _config
130
+
131
+
132
+ def set_config(endpoint: Optional[str] = None, auth_token: Optional[str] = None):
133
+ """Set the global configuration.
134
+
135
+ Args:
136
+ endpoint: API endpoint URL
137
+ auth_token: Authentication token
138
+ """
139
+ global _config
140
+ _config = Config(endpoint=endpoint, auth_token=auth_token)