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
erdo/invoke/client.py ADDED
@@ -0,0 +1,234 @@
1
+ """API client for invoking bots via the backend orchestrator."""
2
+
3
+ import json
4
+ from typing import Any, Dict, Generator, List, Optional, Union
5
+
6
+ import requests
7
+
8
+ from ..config import get_config
9
+
10
+
11
+ class SSEClient:
12
+ """Client for Server-Sent Events streaming."""
13
+
14
+ def __init__(self, response: requests.Response):
15
+ """Initialize SSE client with a response object."""
16
+ self.response = response
17
+ self.response.encoding = "utf-8"
18
+
19
+ def events(self) -> Generator[Dict[str, Any], None, None]:
20
+ """Yield events from the SSE stream."""
21
+ for line in self.response.iter_lines():
22
+ if line:
23
+ line = line.decode("utf-8") if isinstance(line, bytes) else line
24
+ if line.startswith("data: "):
25
+ data = line[6:] # Remove 'data: ' prefix
26
+ if data.strip():
27
+ try:
28
+ yield json.loads(data)
29
+ except json.JSONDecodeError:
30
+ # Some events might not be JSON
31
+ yield {"raw": data}
32
+
33
+
34
+ class InvokeClient:
35
+ """Client for invoking bots via the Erdo backend."""
36
+
37
+ def __init__(
38
+ self, endpoint: Optional[str] = None, auth_token: Optional[str] = None
39
+ ):
40
+ """Initialize the invoke client.
41
+
42
+ Args:
43
+ endpoint: API endpoint URL. If not provided, uses config.
44
+ auth_token: Authentication token. If not provided, uses config.
45
+ """
46
+ config = get_config()
47
+ self.endpoint = endpoint or config.endpoint
48
+ self.auth_token = auth_token or config.auth_token
49
+
50
+ def invoke_bot(
51
+ self,
52
+ bot_identifier: str,
53
+ messages: Optional[List[Dict[str, str]]] = None,
54
+ parameters: Optional[Dict[str, Any]] = None,
55
+ dataset_slugs: Optional[list] = None,
56
+ mode: Optional[Union[str, Dict[str, Any]]] = None,
57
+ manual_mocks: Optional[Dict[str, Dict[str, Any]]] = None,
58
+ stream: bool = False,
59
+ ) -> Union[SSEClient, Dict[str, Any]]:
60
+ """Invoke a bot via the backend orchestrator.
61
+
62
+ Args:
63
+ bot_identifier: Bot ID or key (e.g., "erdo.data-analyzer")
64
+ messages: Messages in format [{"role": "user", "content": "..."}]
65
+ parameters: Parameters to pass to the bot
66
+ dataset_slugs: Optional dataset slugs to include
67
+ mode: Invocation mode - string: "live|replay|manual" OR dict: {"mode": "replay", "refresh": true}
68
+ manual_mocks: Manual mock responses for mode="manual" (step_path -> mock response)
69
+ stream: Whether to return SSE client for streaming (default: False)
70
+
71
+ Returns:
72
+ SSEClient for streaming or final result dict for non-streaming
73
+
74
+ Raises:
75
+ requests.RequestException: If the API request fails.
76
+ """
77
+ url = f"{self.endpoint}/bots/{bot_identifier}/invoke"
78
+ headers = {
79
+ "Authorization": f"Bearer {self.auth_token}",
80
+ "Content-Type": "application/json",
81
+ "Accept": "text/event-stream", # Endpoint always returns SSE
82
+ }
83
+
84
+ # Build invoke parameters
85
+ invoke_params: Dict[str, Any] = {}
86
+
87
+ # Add messages if provided
88
+ if messages:
89
+ invoke_params["messages"] = messages
90
+
91
+ # Add parameters if provided
92
+ if parameters:
93
+ invoke_params["parameters"] = parameters
94
+
95
+ # Add dataset slugs if provided
96
+ if dataset_slugs:
97
+ invoke_params["dataset_slugs"] = dataset_slugs
98
+
99
+ # Add mode if provided (live/replay/manual)
100
+ if mode:
101
+ invoke_params["mode"] = mode
102
+
103
+ # Add manual mocks if provided (for manual mode)
104
+ if manual_mocks:
105
+ invoke_params["manual_mocks"] = manual_mocks
106
+
107
+ # Make the request - always stream to handle SSE
108
+ response = requests.post(url, json=invoke_params, headers=headers, stream=True)
109
+
110
+ if response.status_code != 200:
111
+ error_msg = f"API request failed with status {response.status_code}"
112
+ try:
113
+ error_details = response.text
114
+ error_msg = f"{error_msg}: {error_details}"
115
+ except Exception:
116
+ pass
117
+ raise requests.RequestException(error_msg)
118
+
119
+ sse_client = SSEClient(response)
120
+
121
+ if stream:
122
+ # Return SSE client for streaming
123
+ return sse_client
124
+ else:
125
+ # Consume all events and return final result
126
+ events: list[Dict[str, Any]] = []
127
+ final_result: Dict[str, Any] = {}
128
+
129
+ for event in sse_client.events():
130
+ events.append(event)
131
+
132
+ # Look for completion or result events
133
+ if "payload" in event:
134
+ payload = event["payload"]
135
+ if isinstance(payload, dict):
136
+ if "result" in payload:
137
+ final_result = payload["result"]
138
+ elif "invocation_id" in payload:
139
+ final_result["invocation_id"] = payload["invocation_id"]
140
+ elif "bot_name" in payload:
141
+ final_result["bot_name"] = payload["bot_name"]
142
+
143
+ # Check for invocation completed
144
+ if event.get("type") == "invocation_completed":
145
+ if "result" in event:
146
+ final_result = event["result"]
147
+ break
148
+
149
+ # Return collected data
150
+ return {
151
+ "events": events,
152
+ "result": final_result,
153
+ "event_count": len(events),
154
+ }
155
+
156
+ def invoke_bot_from_thread(
157
+ self,
158
+ bot_identifier: str,
159
+ thread_id: str,
160
+ message: str,
161
+ parameters: Optional[Dict[str, Any]] = None,
162
+ stream: bool = False,
163
+ ) -> Union[SSEClient, Dict[str, Any]]:
164
+ """Invoke a bot from within a thread context.
165
+
166
+ Args:
167
+ bot_identifier: Bot ID or key
168
+ thread_id: Thread ID to invoke from
169
+ message: User message
170
+ parameters: Additional parameters
171
+ stream: Whether to return SSE client for streaming (default: False)
172
+
173
+ Returns:
174
+ SSEClient for streaming or final result dict
175
+ """
176
+ url = f"{self.endpoint}/bots/{bot_identifier}/invoke-from-thread"
177
+ headers = {
178
+ "Authorization": f"Bearer {self.auth_token}",
179
+ "Content-Type": "application/json",
180
+ "Accept": "text/event-stream", # Endpoint always returns SSE
181
+ }
182
+
183
+ invoke_params = {
184
+ "thread_id": thread_id,
185
+ "message": message,
186
+ "parameters": parameters or {},
187
+ }
188
+
189
+ # Make the request - always stream to handle SSE
190
+ response = requests.post(url, json=invoke_params, headers=headers, stream=True)
191
+
192
+ if response.status_code != 200:
193
+ error_msg = f"API request failed with status {response.status_code}"
194
+ try:
195
+ error_details = response.text
196
+ error_msg = f"{error_msg}: {error_details}"
197
+ except Exception:
198
+ pass
199
+ raise requests.RequestException(error_msg)
200
+
201
+ sse_client = SSEClient(response)
202
+
203
+ if stream:
204
+ # Return SSE client for streaming
205
+ return sse_client
206
+ else:
207
+ # Consume all events and return final result
208
+ events: list[Dict[str, Any]] = []
209
+ final_result: Dict[str, Any] = {}
210
+
211
+ for event in sse_client.events():
212
+ events.append(event)
213
+
214
+ # Look for completion or result events
215
+ if "payload" in event:
216
+ payload = event["payload"]
217
+ if isinstance(payload, dict):
218
+ if "result" in payload:
219
+ final_result = payload["result"]
220
+ elif "invocation_id" in payload:
221
+ final_result["invocation_id"] = payload["invocation_id"]
222
+
223
+ # Check for invocation completed
224
+ if event.get("type") == "invocation_completed":
225
+ if "result" in event:
226
+ final_result = event["result"]
227
+ break
228
+
229
+ # Return collected data
230
+ return {
231
+ "events": events,
232
+ "result": final_result,
233
+ "event_count": len(events),
234
+ }