erdo 0.1.4__py3-none-any.whl → 0.1.6__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 erdo might be problematic. Click here for more details.
- erdo/__init__.py +13 -13
- erdo/_generated/actions/__init__.py +3 -1
- erdo/_generated/actions/analysis.py +116 -4
- erdo/_generated/actions/bot.py +61 -5
- erdo/_generated/actions/codeexec.py +53 -28
- erdo/_generated/actions/llm.py +44 -5
- erdo/_generated/actions/memory.py +252 -57
- erdo/_generated/actions/pdfextractor.py +97 -0
- erdo/_generated/actions/resource_definitions.py +114 -12
- erdo/_generated/actions/sqlexec.py +86 -0
- erdo/_generated/actions/utils.py +178 -56
- erdo/_generated/actions/webparser.py +15 -5
- erdo/_generated/actions/websearch.py +15 -5
- erdo/_generated/condition/__init__.py +137 -127
- erdo/_generated/internal_actions.py +14 -2
- erdo/_generated/types.py +92 -48
- erdo/actions/__init__.py +8 -8
- erdo/bot_permissions.py +266 -0
- erdo/config/__init__.py +5 -0
- erdo/config/config.py +140 -0
- erdo/invoke/__init__.py +10 -0
- erdo/invoke/client.py +213 -0
- erdo/invoke/invoke.py +244 -0
- erdo/sync/__init__.py +17 -0
- erdo/sync/client.py +95 -0
- erdo/sync/extractor.py +482 -0
- erdo/sync/sync.py +327 -0
- erdo/types.py +516 -18
- {erdo-0.1.4.dist-info → erdo-0.1.6.dist-info}/METADATA +4 -1
- erdo-0.1.6.dist-info/RECORD +45 -0
- erdo-0.1.4.dist-info/RECORD +0 -33
- {erdo-0.1.4.dist-info → erdo-0.1.6.dist-info}/WHEEL +0 -0
- {erdo-0.1.4.dist-info → erdo-0.1.6.dist-info}/entry_points.txt +0 -0
- {erdo-0.1.4.dist-info → erdo-0.1.6.dist-info}/licenses/LICENSE +0 -0
erdo/invoke/invoke.py
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"""Main invoke functionality for running agents via the orchestrator."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import Any, Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
from .client import InvokeClient
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class InvokeResult:
|
|
12
|
+
"""Result from a bot invocation."""
|
|
13
|
+
|
|
14
|
+
success: bool
|
|
15
|
+
bot_id: Optional[str] = None
|
|
16
|
+
invocation_id: Optional[str] = None
|
|
17
|
+
result: Optional[Any] = None
|
|
18
|
+
error: Optional[str] = None
|
|
19
|
+
events: List[Dict[str, Any]] = field(default_factory=list)
|
|
20
|
+
|
|
21
|
+
def __str__(self) -> str:
|
|
22
|
+
if self.success:
|
|
23
|
+
return f"✅ Invocation successful (ID: {self.invocation_id})"
|
|
24
|
+
else:
|
|
25
|
+
return f"❌ Invocation failed: {self.error}"
|
|
26
|
+
|
|
27
|
+
def get_final_result(self) -> Optional[Any]:
|
|
28
|
+
"""Get the final result from the invocation."""
|
|
29
|
+
return self.result
|
|
30
|
+
|
|
31
|
+
def get_all_events(self) -> List[Dict[str, Any]]:
|
|
32
|
+
"""Get all events from the invocation."""
|
|
33
|
+
return self.events
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Invoke:
|
|
37
|
+
"""Main class for invoking agents."""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
agent: Optional[Any] = None,
|
|
42
|
+
parameters: Optional[Dict[str, Any]] = None,
|
|
43
|
+
dataset_ids: Optional[list] = None,
|
|
44
|
+
endpoint: Optional[str] = None,
|
|
45
|
+
auth_token: Optional[str] = None,
|
|
46
|
+
stream: bool = False,
|
|
47
|
+
print_events: bool = False,
|
|
48
|
+
):
|
|
49
|
+
"""Initialize and optionally invoke an agent immediately.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
agent: Optional Agent instance to invoke immediately
|
|
53
|
+
parameters: Parameters to pass to the agent
|
|
54
|
+
dataset_ids: Dataset IDs to include
|
|
55
|
+
endpoint: API endpoint URL
|
|
56
|
+
auth_token: Authentication token
|
|
57
|
+
stream: Whether to stream events
|
|
58
|
+
print_events: Whether to print events as they arrive
|
|
59
|
+
"""
|
|
60
|
+
self.client = InvokeClient(endpoint=endpoint, auth_token=auth_token)
|
|
61
|
+
self.print_events = print_events
|
|
62
|
+
self.result = None
|
|
63
|
+
|
|
64
|
+
# If an agent is provided, invoke it immediately
|
|
65
|
+
if agent:
|
|
66
|
+
bot_key = getattr(agent, "key", None)
|
|
67
|
+
if not bot_key:
|
|
68
|
+
raise ValueError("Agent must have a 'key' attribute for invocation")
|
|
69
|
+
|
|
70
|
+
self.result = self.invoke_by_key(
|
|
71
|
+
bot_key, parameters=parameters, dataset_ids=dataset_ids, stream=stream
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def invoke_agent(
|
|
75
|
+
self,
|
|
76
|
+
agent: Any,
|
|
77
|
+
parameters: Optional[Dict[str, Any]] = None,
|
|
78
|
+
dataset_ids: Optional[list] = None,
|
|
79
|
+
stream: bool = False,
|
|
80
|
+
) -> InvokeResult:
|
|
81
|
+
"""Invoke an agent instance.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
agent: Agent instance with a 'key' attribute
|
|
85
|
+
parameters: Parameters to pass to the agent
|
|
86
|
+
dataset_ids: Dataset IDs to include
|
|
87
|
+
stream: Whether to stream events
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
InvokeResult with the outcome
|
|
91
|
+
"""
|
|
92
|
+
bot_key = getattr(agent, "key", None)
|
|
93
|
+
if not bot_key:
|
|
94
|
+
raise ValueError("Agent must have a 'key' attribute for invocation")
|
|
95
|
+
|
|
96
|
+
return self.invoke_by_key(bot_key, parameters, dataset_ids, stream)
|
|
97
|
+
|
|
98
|
+
def invoke_by_key(
|
|
99
|
+
self,
|
|
100
|
+
bot_key: str,
|
|
101
|
+
parameters: Optional[Dict[str, Any]] = None,
|
|
102
|
+
dataset_ids: Optional[list] = None,
|
|
103
|
+
stream: bool = False,
|
|
104
|
+
) -> InvokeResult:
|
|
105
|
+
"""Invoke a bot by its key.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
bot_key: Bot key (e.g., "erdo.data-analyzer")
|
|
109
|
+
parameters: Parameters to pass to the bot
|
|
110
|
+
dataset_ids: Dataset IDs to include
|
|
111
|
+
stream: Whether to stream events
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
InvokeResult with the outcome
|
|
115
|
+
"""
|
|
116
|
+
try:
|
|
117
|
+
response = self.client.invoke_bot(
|
|
118
|
+
bot_key, parameters=parameters, dataset_ids=dataset_ids, stream=stream
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if stream:
|
|
122
|
+
# Process SSE events
|
|
123
|
+
events = []
|
|
124
|
+
final_result = None
|
|
125
|
+
invocation_id = None
|
|
126
|
+
|
|
127
|
+
# Type guard: response should be SSEClient when stream=True
|
|
128
|
+
if not isinstance(response, dict):
|
|
129
|
+
for event in response.events():
|
|
130
|
+
events.append(event)
|
|
131
|
+
|
|
132
|
+
if self.print_events:
|
|
133
|
+
self._print_event(event)
|
|
134
|
+
|
|
135
|
+
# Extract invocation ID from events
|
|
136
|
+
if "invocation_id" in event:
|
|
137
|
+
invocation_id = event["invocation_id"]
|
|
138
|
+
|
|
139
|
+
# Check for final result
|
|
140
|
+
if event.get("type") == "invocation_completed":
|
|
141
|
+
final_result = event.get("result")
|
|
142
|
+
elif event.get("type") == "result":
|
|
143
|
+
final_result = event.get("data")
|
|
144
|
+
|
|
145
|
+
return InvokeResult(
|
|
146
|
+
success=True,
|
|
147
|
+
bot_id=bot_key,
|
|
148
|
+
invocation_id=invocation_id,
|
|
149
|
+
result=final_result,
|
|
150
|
+
events=events,
|
|
151
|
+
)
|
|
152
|
+
else:
|
|
153
|
+
# Non-streaming response - response is a dict
|
|
154
|
+
response_dict = response if isinstance(response, dict) else {}
|
|
155
|
+
return InvokeResult(
|
|
156
|
+
success=True,
|
|
157
|
+
bot_id=bot_key,
|
|
158
|
+
result=response_dict,
|
|
159
|
+
events=[response_dict],
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
except Exception as e:
|
|
163
|
+
return InvokeResult(success=False, bot_id=bot_key, error=str(e))
|
|
164
|
+
|
|
165
|
+
def _print_event(self, event: Dict[str, Any]):
|
|
166
|
+
"""Print an event in a readable format."""
|
|
167
|
+
event_type = event.get("type", "unknown")
|
|
168
|
+
|
|
169
|
+
if event_type == "step_started":
|
|
170
|
+
step_name = event.get("step_name", "Unknown step")
|
|
171
|
+
print(f"🔄 Step started: {step_name}")
|
|
172
|
+
elif event_type == "step_completed":
|
|
173
|
+
step_name = event.get("step_name", "Unknown step")
|
|
174
|
+
print(f"✅ Step completed: {step_name}")
|
|
175
|
+
elif event_type == "llm_chunk":
|
|
176
|
+
content = event.get("content", "")
|
|
177
|
+
print(content, end="", flush=True)
|
|
178
|
+
elif event_type == "invocation_completed":
|
|
179
|
+
print("\n✨ Invocation completed")
|
|
180
|
+
elif event_type == "error":
|
|
181
|
+
error = event.get("error", "Unknown error")
|
|
182
|
+
print(f"❌ Error: {error}")
|
|
183
|
+
else:
|
|
184
|
+
# Generic event printing
|
|
185
|
+
print(f"📡 {event_type}: {json.dumps(event, indent=2)}")
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# Convenience functions
|
|
189
|
+
def invoke_agent(
|
|
190
|
+
agent: Any,
|
|
191
|
+
parameters: Optional[Dict[str, Any]] = None,
|
|
192
|
+
dataset_ids: Optional[list] = None,
|
|
193
|
+
stream: bool = False,
|
|
194
|
+
print_events: bool = False,
|
|
195
|
+
**kwargs,
|
|
196
|
+
) -> InvokeResult:
|
|
197
|
+
"""Invoke an agent instance.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
agent: Agent instance with a 'key' attribute
|
|
201
|
+
parameters: Parameters to pass to the agent
|
|
202
|
+
dataset_ids: Dataset IDs to include
|
|
203
|
+
stream: Whether to stream events
|
|
204
|
+
print_events: Whether to print events
|
|
205
|
+
**kwargs: Additional arguments (endpoint, auth_token)
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
InvokeResult with the outcome
|
|
209
|
+
"""
|
|
210
|
+
invoke = Invoke(
|
|
211
|
+
endpoint=kwargs.get("endpoint"),
|
|
212
|
+
auth_token=kwargs.get("auth_token"),
|
|
213
|
+
print_events=print_events,
|
|
214
|
+
)
|
|
215
|
+
return invoke.invoke_agent(agent, parameters, dataset_ids, stream)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def invoke_by_key(
|
|
219
|
+
bot_key: str,
|
|
220
|
+
parameters: Optional[Dict[str, Any]] = None,
|
|
221
|
+
dataset_ids: Optional[list] = None,
|
|
222
|
+
stream: bool = False,
|
|
223
|
+
print_events: bool = False,
|
|
224
|
+
**kwargs,
|
|
225
|
+
) -> InvokeResult:
|
|
226
|
+
"""Invoke a bot by its key.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
bot_key: Bot key (e.g., "erdo.data-analyzer")
|
|
230
|
+
parameters: Parameters to pass to the bot
|
|
231
|
+
dataset_ids: Dataset IDs to include
|
|
232
|
+
stream: Whether to stream events
|
|
233
|
+
print_events: Whether to print events
|
|
234
|
+
**kwargs: Additional arguments (endpoint, auth_token)
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
InvokeResult with the outcome
|
|
238
|
+
"""
|
|
239
|
+
invoke = Invoke(
|
|
240
|
+
endpoint=kwargs.get("endpoint"),
|
|
241
|
+
auth_token=kwargs.get("auth_token"),
|
|
242
|
+
print_events=print_events,
|
|
243
|
+
)
|
|
244
|
+
return invoke.invoke_by_key(bot_key, parameters, dataset_ids, stream)
|
erdo/sync/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Sync module for Erdo SDK - sync agents to the backend."""
|
|
2
|
+
|
|
3
|
+
from .sync import (
|
|
4
|
+
Sync,
|
|
5
|
+
SyncResult,
|
|
6
|
+
sync_agent,
|
|
7
|
+
sync_agents_from_directory,
|
|
8
|
+
sync_agents_from_file,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"Sync",
|
|
13
|
+
"SyncResult",
|
|
14
|
+
"sync_agent",
|
|
15
|
+
"sync_agents_from_file",
|
|
16
|
+
"sync_agents_from_directory",
|
|
17
|
+
]
|
erdo/sync/client.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""API client for syncing agents to the backend."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any, Dict, Optional
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
from ..config import get_config
|
|
9
|
+
from .extractor import TemplateStringEncoder
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SyncClient:
|
|
13
|
+
"""Client for syncing agents to the Erdo backend."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self, endpoint: Optional[str] = None, auth_token: Optional[str] = None
|
|
17
|
+
):
|
|
18
|
+
"""Initialize the sync client.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
endpoint: API endpoint URL. If not provided, uses config.
|
|
22
|
+
auth_token: Authentication token. If not provided, uses config.
|
|
23
|
+
"""
|
|
24
|
+
config = get_config()
|
|
25
|
+
self.endpoint = endpoint or config.endpoint
|
|
26
|
+
self.auth_token = auth_token or config.auth_token
|
|
27
|
+
|
|
28
|
+
def upsert_bot(self, bot_request: Dict[str, Any]) -> str:
|
|
29
|
+
"""Upsert a bot to the backend.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
bot_request: The bot request data containing bot info, steps, etc.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
The bot ID of the upserted bot.
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
requests.RequestException: If the API request fails.
|
|
39
|
+
ValueError: If the response is invalid.
|
|
40
|
+
"""
|
|
41
|
+
url = f"{self.endpoint}/bot/upsert"
|
|
42
|
+
headers = {
|
|
43
|
+
"Content-Type": "application/json",
|
|
44
|
+
"Authorization": f"Bearer {self.auth_token}",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# Use custom encoder to handle special types
|
|
48
|
+
json_data = json.dumps(bot_request, cls=TemplateStringEncoder)
|
|
49
|
+
response = requests.post(url, data=json_data, headers=headers)
|
|
50
|
+
|
|
51
|
+
if response.status_code != 200:
|
|
52
|
+
error_msg = f"API request failed with status {response.status_code}"
|
|
53
|
+
try:
|
|
54
|
+
error_details = response.text
|
|
55
|
+
error_msg = f"{error_msg}: {error_details}"
|
|
56
|
+
except Exception:
|
|
57
|
+
pass
|
|
58
|
+
raise requests.RequestException(error_msg)
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
result = response.json()
|
|
62
|
+
return result.get("bot_id", "")
|
|
63
|
+
except json.JSONDecodeError as e:
|
|
64
|
+
raise ValueError(f"Failed to decode response: {e}")
|
|
65
|
+
|
|
66
|
+
def sync_test(self, test_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
67
|
+
"""Sync a test to the backend.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
test_data: The test data to sync.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
The response from the API.
|
|
74
|
+
|
|
75
|
+
Raises:
|
|
76
|
+
requests.RequestException: If the API request fails.
|
|
77
|
+
"""
|
|
78
|
+
url = f"{self.endpoint}/test/sync"
|
|
79
|
+
headers = {
|
|
80
|
+
"Content-Type": "application/json",
|
|
81
|
+
"Authorization": f"Bearer {self.auth_token}",
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
response = requests.post(url, json=test_data, headers=headers)
|
|
85
|
+
|
|
86
|
+
if response.status_code != 200:
|
|
87
|
+
error_msg = f"API request failed with status {response.status_code}"
|
|
88
|
+
try:
|
|
89
|
+
error_details = response.text
|
|
90
|
+
error_msg = f"{error_msg}: {error_details}"
|
|
91
|
+
except Exception:
|
|
92
|
+
pass
|
|
93
|
+
raise requests.RequestException(error_msg)
|
|
94
|
+
|
|
95
|
+
return response.json()
|