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.
- erdo/__init__.py +35 -0
- erdo/_generated/__init__.py +18 -0
- erdo/_generated/actions/__init__.py +34 -0
- erdo/_generated/actions/analysis.py +179 -0
- erdo/_generated/actions/bot.py +186 -0
- erdo/_generated/actions/codeexec.py +199 -0
- erdo/_generated/actions/llm.py +148 -0
- erdo/_generated/actions/memory.py +463 -0
- erdo/_generated/actions/pdfextractor.py +97 -0
- erdo/_generated/actions/resource_definitions.py +296 -0
- erdo/_generated/actions/sqlexec.py +90 -0
- erdo/_generated/actions/utils.py +475 -0
- erdo/_generated/actions/webparser.py +119 -0
- erdo/_generated/actions/websearch.py +85 -0
- erdo/_generated/condition/__init__.py +556 -0
- erdo/_generated/internal.py +51 -0
- erdo/_generated/internal_actions.py +91 -0
- erdo/_generated/parameters.py +17 -0
- erdo/_generated/secrets.py +17 -0
- erdo/_generated/template_functions.py +55 -0
- erdo/_generated/types.py +3907 -0
- erdo/actions/__init__.py +40 -0
- erdo/bot_permissions.py +266 -0
- erdo/cli_entry.py +73 -0
- erdo/conditions/__init__.py +11 -0
- erdo/config/__init__.py +5 -0
- erdo/config/config.py +140 -0
- erdo/formatting.py +279 -0
- erdo/install_cli.py +140 -0
- erdo/integrations.py +131 -0
- erdo/invoke/__init__.py +11 -0
- erdo/invoke/client.py +234 -0
- erdo/invoke/invoke.py +555 -0
- erdo/state.py +376 -0
- erdo/sync/__init__.py +17 -0
- erdo/sync/client.py +95 -0
- erdo/sync/extractor.py +492 -0
- erdo/sync/sync.py +327 -0
- erdo/template.py +136 -0
- erdo/test/__init__.py +41 -0
- erdo/test/evaluate.py +272 -0
- erdo/test/runner.py +263 -0
- erdo/types.py +1431 -0
- erdo-0.1.31.dist-info/METADATA +471 -0
- erdo-0.1.31.dist-info/RECORD +48 -0
- erdo-0.1.31.dist-info/WHEEL +4 -0
- erdo-0.1.31.dist-info/entry_points.txt +2 -0
- 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
|
+
}
|