asteroid-odyssey 0.1.21__py3-none-any.whl → 1.0.0__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.
- asteroid_odyssey/__init__.py +20 -34
- asteroid_odyssey/client.py +387 -337
- asteroid_odyssey/openapi_client/__init__.py +73 -0
- asteroid_odyssey/openapi_client/api/__init__.py +7 -0
- asteroid_odyssey/openapi_client/api/api_api.py +516 -0
- asteroid_odyssey/openapi_client/api/execution_api.py +335 -0
- asteroid_odyssey/openapi_client/api/sdk_api.py +1434 -0
- asteroid_odyssey/openapi_client/api_client.py +801 -0
- asteroid_odyssey/openapi_client/api_response.py +21 -0
- asteroid_odyssey/openapi_client/configuration.py +606 -0
- asteroid_odyssey/openapi_client/exceptions.py +216 -0
- asteroid_odyssey/openapi_client/models/__init__.py +27 -0
- asteroid_odyssey/openapi_client/models/browser_session_recording_response.py +87 -0
- asteroid_odyssey/openapi_client/models/error_response.py +87 -0
- asteroid_odyssey/openapi_client/models/execution_response.py +87 -0
- asteroid_odyssey/openapi_client/models/execution_result.py +101 -0
- asteroid_odyssey/openapi_client/models/execution_result_response.py +100 -0
- asteroid_odyssey/openapi_client/models/execution_status_response.py +95 -0
- asteroid_odyssey/openapi_client/models/health_check200_response.py +87 -0
- asteroid_odyssey/openapi_client/models/health_check500_response.py +87 -0
- asteroid_odyssey/openapi_client/models/status.py +43 -0
- asteroid_odyssey/openapi_client/models/structured_agent_execution_request.py +89 -0
- asteroid_odyssey/openapi_client/models/upload_execution_files200_response.py +89 -0
- asteroid_odyssey/openapi_client/rest.py +258 -0
- asteroid_odyssey-1.0.0.dist-info/METADATA +208 -0
- asteroid_odyssey-1.0.0.dist-info/RECORD +29 -0
- {asteroid_odyssey-0.1.21.dist-info → asteroid_odyssey-1.0.0.dist-info}/WHEEL +1 -1
- asteroid_odyssey/api/generated/asteroid_agents_api_client/__init__.py +0 -8
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/__init__.py +0 -1
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/agent/get_agents.py +0 -127
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/api/__init__.py +0 -0
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/api/get_open_api.py +0 -79
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/api/health_check.py +0 -131
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/credentials/__init__.py +0 -0
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/credentials/get_credentials_public_key.py +0 -127
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/execution/__init__.py +0 -0
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/execution/delete_execution.py +0 -156
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/execution/get_browser_session.py +0 -154
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/execution/get_execution.py +0 -154
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/execution/get_execution_files.py +0 -159
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/execution/get_execution_progress.py +0 -159
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/execution/get_executions_for_workflow.py +0 -156
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/execution/update_execution_status.py +0 -115
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/notifications/__init__.py +0 -0
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/notifications/set_slack_channel.py +0 -107
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/optimiser/__init__.py +0 -0
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/optimiser/queue_optimisation_job.py +0 -103
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/__init__.py +0 -0
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/add_workflow_credential.py +0 -111
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/create_workflow.py +0 -174
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/delete_workflow.py +0 -156
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/delete_workflow_credentials.py +0 -96
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/execute_workflow.py +0 -179
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/get_agent_workflow_executions.py +0 -155
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/get_workflow.py +0 -154
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/get_workflow_credentials.py +0 -154
- asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/get_workflow_versions.py +0 -159
- asteroid_odyssey/api/generated/asteroid_agents_api_client/client.py +0 -268
- asteroid_odyssey/api/generated/asteroid_agents_api_client/errors.py +0 -16
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/__init__.py +0 -61
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/agent.py +0 -90
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/browser_session.py +0 -136
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/create_workflow_request.py +0 -126
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/create_workflow_request_fields.py +0 -48
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/create_workflow_request_provider.py +0 -9
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/credential.py +0 -66
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/credentials_request.py +0 -72
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/credentials_response.py +0 -80
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/delete_execution_response_200.py +0 -58
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/delete_execution_response_404.py +0 -58
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/delete_workflow_response_200.py +0 -58
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/delete_workflow_response_404.py +0 -58
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/execution.py +0 -147
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/execution_dynamic_data.py +0 -48
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/execution_result.py +0 -43
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/execution_status.py +0 -89
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/file.py +0 -127
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/health_check_response_200.py +0 -58
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/health_check_response_500.py +0 -58
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/optimisation_request.py +0 -59
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/progress_update.py +0 -77
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/result_schema.py +0 -51
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/slack_channel_request.py +0 -58
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/status.py +0 -13
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/workflow.py +0 -152
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/workflow_execution.py +0 -82
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/workflow_execution_request.py +0 -48
- asteroid_odyssey/api/generated/asteroid_agents_api_client/models/workflow_fields.py +0 -48
- asteroid_odyssey/api/generated/asteroid_agents_api_client/py.typed +0 -1
- asteroid_odyssey/api/generated/asteroid_agents_api_client/types.py +0 -45
- asteroid_odyssey/exceptions.py +0 -15
- asteroid_odyssey-0.1.21.dist-info/METADATA +0 -31
- asteroid_odyssey-0.1.21.dist-info/RECORD +0 -72
- asteroid_odyssey-0.1.21.dist-info/entry_points.txt +0 -2
- /asteroid_odyssey/{api/generated/asteroid_agents_api_client/api/agent/__init__.py → openapi_client/py.typed} +0 -0
- {asteroid_odyssey-0.1.21.dist-info → asteroid_odyssey-1.0.0.dist-info}/top_level.txt +0 -0
asteroid_odyssey/client.py
CHANGED
|
@@ -1,394 +1,444 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
import time
|
|
5
|
-
from enum import Enum
|
|
6
|
-
import os
|
|
1
|
+
"""
|
|
2
|
+
Asteroid Agents Python SDK - High-Level Client Interface
|
|
7
3
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
WorkflowExecution,
|
|
11
|
-
ExecutionStatus,
|
|
12
|
-
Execution,
|
|
13
|
-
WorkflowExecutionRequest,
|
|
14
|
-
Agent,
|
|
15
|
-
ResultSchema,
|
|
16
|
-
CreateWorkflowRequestFields,
|
|
17
|
-
CreateWorkflowRequestProvider
|
|
18
|
-
)
|
|
19
|
-
from .api.generated.asteroid_agents_api_client.client import Client as ApiClient
|
|
20
|
-
from .api.generated.asteroid_agents_api_client.api.execution.get_execution import sync_detailed as get_execution
|
|
21
|
-
from .api.generated.asteroid_agents_api_client.api.agent.get_agents import sync_detailed as asteroid_get_agents
|
|
22
|
-
from .api.generated.asteroid_agents_api_client.api.default.create_workflow import sync_detailed as create_workflow
|
|
23
|
-
from .api.generated.asteroid_agents_api_client.api.workflow.get_agent_workflow_executions import sync_detailed as get_agent_workflow_executions
|
|
24
|
-
from .api.generated.asteroid_agents_api_client.api.workflow.execute_workflow import sync_detailed as execute_workflow
|
|
25
|
-
logger = logging.getLogger(__name__)
|
|
4
|
+
Provides a clean, easy-to-use interface for interacting with the Asteroid Agents API,
|
|
5
|
+
similar to the TypeScript SDK.
|
|
26
6
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
FAILED = "failed"
|
|
31
|
-
CANCELLED = "cancelled"
|
|
32
|
-
ERROR = "error"
|
|
7
|
+
This module provides a high-level client that wraps the generated OpenAPI client
|
|
8
|
+
without modifying any generated files.
|
|
9
|
+
"""
|
|
33
10
|
|
|
34
|
-
|
|
11
|
+
import time
|
|
12
|
+
import os
|
|
13
|
+
from typing import Dict, Any, Optional, List, Union, Tuple
|
|
14
|
+
from openapi_client import (
|
|
15
|
+
Configuration,
|
|
16
|
+
ApiClient,
|
|
17
|
+
SDKApi,
|
|
18
|
+
ExecutionApi,
|
|
19
|
+
ExecutionStatusResponse,
|
|
20
|
+
ExecutionResultResponse,
|
|
21
|
+
BrowserSessionRecordingResponse,
|
|
22
|
+
UploadExecutionFiles200Response,
|
|
23
|
+
Status,
|
|
24
|
+
StructuredAgentExecutionRequest
|
|
25
|
+
)
|
|
26
|
+
from openapi_client.exceptions import ApiException
|
|
35
27
|
|
|
36
|
-
class ExecutionResult:
|
|
37
|
-
"""Wrapper class for execution results"""
|
|
38
|
-
def __init__(self, execution: Execution):
|
|
39
|
-
self.execution_id = execution.id
|
|
40
|
-
self.status = execution.status
|
|
41
|
-
self.result = execution.result
|
|
42
|
-
self.error = execution.error if hasattr(execution, 'error') else None
|
|
43
|
-
self.created_at = execution.created_at
|
|
44
|
-
self.completed_at = execution.completed_at if hasattr(execution, 'completed_at') else None
|
|
45
28
|
|
|
46
29
|
class AsteroidClient:
|
|
47
30
|
"""
|
|
48
|
-
|
|
31
|
+
High-level client for the Asteroid Agents API.
|
|
32
|
+
|
|
33
|
+
This class provides a convenient interface for executing agents and managing
|
|
34
|
+
their execution lifecycle, similar to the TypeScript SDK.
|
|
49
35
|
"""
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
api_key: Optional[str] = None,
|
|
53
|
-
base_url: Optional[str] = None,
|
|
54
|
-
verify_ssl: bool = True
|
|
55
|
-
):
|
|
36
|
+
|
|
37
|
+
def __init__(self, api_key: str, base_url: Optional[str] = None):
|
|
56
38
|
"""
|
|
57
|
-
|
|
58
|
-
|
|
39
|
+
Create an API client with the provided API key.
|
|
40
|
+
|
|
59
41
|
Args:
|
|
60
|
-
api_key: API key for authentication
|
|
61
|
-
base_url:
|
|
62
|
-
|
|
42
|
+
api_key: Your API key for authentication
|
|
43
|
+
base_url: Optional base URL (defaults to https://odyssey.asteroid.ai/api/v1)
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
client = AsteroidClient('your-api-key')
|
|
63
47
|
"""
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
# Initialize API client
|
|
72
|
-
self.client = ApiClient(
|
|
73
|
-
base_url=self.base_url,
|
|
74
|
-
verify_ssl=verify_ssl,
|
|
75
|
-
headers={"X-Asteroid-Agents-Api-Key": f"{self.api_key}"}
|
|
48
|
+
if api_key is None:
|
|
49
|
+
raise TypeError("API key cannot be None")
|
|
50
|
+
|
|
51
|
+
# Configure the API client
|
|
52
|
+
config = Configuration(
|
|
53
|
+
host=base_url or "https://odyssey.asteroid.ai/api/v1",
|
|
54
|
+
api_key={'ApiKeyAuth': api_key}
|
|
76
55
|
)
|
|
77
|
-
|
|
78
|
-
def get_agents(self) -> List["Agent"]:
|
|
79
|
-
"""
|
|
80
|
-
Get list of available agents.
|
|
81
56
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if result.parsed is None:
|
|
88
|
-
raise ValueError("No agents were returned from the API")
|
|
89
|
-
if not isinstance(result.parsed, list):
|
|
90
|
-
raise ValueError(f"The result is not of type list, it is of type: {type(result.parsed)}")
|
|
91
|
-
return result.parsed
|
|
92
|
-
except Exception as e:
|
|
93
|
-
logger.error(f"Failed to get agents: {str(e)}")
|
|
94
|
-
raise
|
|
95
|
-
|
|
96
|
-
def create_workflow(
|
|
97
|
-
self,
|
|
98
|
-
workflow_name: str,
|
|
99
|
-
start_url: str,
|
|
100
|
-
prompt: str,
|
|
101
|
-
result_schema: Optional[ResultSchema] = None
|
|
102
|
-
) -> str:
|
|
57
|
+
self.api_client = ApiClient(config)
|
|
58
|
+
self.sdk_api = SDKApi(self.api_client)
|
|
59
|
+
self.execution_api = ExecutionApi(self.api_client)
|
|
60
|
+
|
|
61
|
+
def execute_agent(self, agent_id: str, agent_profile_id: str, execution_data: Dict[str, Any]) -> str:
|
|
103
62
|
"""
|
|
104
|
-
|
|
105
|
-
|
|
63
|
+
Execute an agent with the provided parameters.
|
|
64
|
+
|
|
106
65
|
Args:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
66
|
+
agent_id: The ID of the agent to execute
|
|
67
|
+
agent_profile_id: The ID of the agent profile
|
|
68
|
+
execution_data: The execution parameters
|
|
69
|
+
|
|
112
70
|
Returns:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
71
|
+
The execution ID
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
Exception: If the execution request fails
|
|
75
|
+
|
|
76
|
+
Example:
|
|
77
|
+
execution_id = client.execute_structured_agent('my-agent-id', 'agent-profile-id', {'input': 'some dynamic value'})
|
|
118
78
|
"""
|
|
119
|
-
|
|
120
|
-
logger.warning("Custom result schemas are not fully supported yet and will be ignored. Using default schema.")
|
|
121
|
-
|
|
122
|
-
# Default result schema
|
|
123
|
-
default_schema = ResultSchema()
|
|
124
|
-
default_schema.additional_properties = {
|
|
125
|
-
"properties": {
|
|
126
|
-
"explanation": {
|
|
127
|
-
"description": "Detailed explanation of the result",
|
|
128
|
-
"type": "string"
|
|
129
|
-
},
|
|
130
|
-
"success": {
|
|
131
|
-
"description": "Whether the operation was successful",
|
|
132
|
-
"type": "boolean"
|
|
133
|
-
}
|
|
134
|
-
},
|
|
135
|
-
"required": [
|
|
136
|
-
"explanation",
|
|
137
|
-
"success"
|
|
138
|
-
],
|
|
139
|
-
"type": "object"
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if result_schema is None:
|
|
143
|
-
result_schema = default_schema
|
|
144
|
-
|
|
79
|
+
req = StructuredAgentExecutionRequest(agent_profile_id=agent_profile_id, dynamic_data=execution_data)
|
|
145
80
|
try:
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
request = CreateWorkflowRequest(
|
|
153
|
-
name=workflow_name,
|
|
154
|
-
result_schema=result_schema,
|
|
155
|
-
fields=fields,
|
|
156
|
-
prompts=[prompt],
|
|
157
|
-
provider=CreateWorkflowRequestProvider.OPENAI
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
result = create_workflow(
|
|
161
|
-
agent_name=agent_name,
|
|
162
|
-
body=request,
|
|
163
|
-
client=self.client
|
|
164
|
-
).parsed
|
|
165
|
-
if not isinstance(result, str):
|
|
166
|
-
raise ValueError("The result is not of type str")
|
|
167
|
-
return result
|
|
168
|
-
except Exception as e:
|
|
169
|
-
logger.error(f"Failed to create workflow: {str(e)}")
|
|
170
|
-
raise
|
|
171
|
-
|
|
172
|
-
def execute_workflow(
|
|
173
|
-
self,
|
|
174
|
-
workflow_id: UUID,
|
|
175
|
-
execution_params: Dict[str, Any]
|
|
176
|
-
) -> str:
|
|
81
|
+
response = self.sdk_api.execute_agent_structured(agent_id, req)
|
|
82
|
+
return response.execution_id
|
|
83
|
+
except ApiException as e:
|
|
84
|
+
raise Exception(f"Failed to execute agent: {e}")
|
|
85
|
+
|
|
86
|
+
def get_execution_status(self, execution_id: str) -> ExecutionStatusResponse:
|
|
177
87
|
"""
|
|
178
|
-
|
|
179
|
-
|
|
88
|
+
Get the current status for an execution.
|
|
89
|
+
|
|
180
90
|
Args:
|
|
181
|
-
|
|
182
|
-
execution_params: Parameters for workflow execution
|
|
183
|
-
|
|
184
|
-
Returns:
|
|
185
|
-
Execution ID
|
|
186
|
-
"""
|
|
187
|
-
try:
|
|
188
|
-
# Convert execution_params to WorkflowExecutionRequest using from_dict
|
|
189
|
-
request_body = WorkflowExecutionRequest.from_dict(execution_params)
|
|
91
|
+
execution_id: The execution identifier
|
|
190
92
|
|
|
191
|
-
result = execute_workflow(
|
|
192
|
-
workflow_id=workflow_id,
|
|
193
|
-
body=request_body,
|
|
194
|
-
client=self.client
|
|
195
|
-
).parsed
|
|
196
|
-
if not isinstance(result, str):
|
|
197
|
-
raise ValueError("The result is not of type str")
|
|
198
|
-
return result
|
|
199
|
-
except Exception as e:
|
|
200
|
-
logger.error(f"Failed to execute workflow: {str(e)}")
|
|
201
|
-
raise
|
|
202
|
-
|
|
203
|
-
def get_workflow_executions(self) -> List[WorkflowExecution]:
|
|
204
|
-
"""
|
|
205
|
-
Get list of workflow executions.
|
|
206
|
-
|
|
207
93
|
Returns:
|
|
208
|
-
|
|
94
|
+
The execution status details
|
|
95
|
+
|
|
96
|
+
Raises:
|
|
97
|
+
Exception: If the status request fails
|
|
98
|
+
|
|
99
|
+
Example:
|
|
100
|
+
status = client.get_execution_status(execution_id)
|
|
101
|
+
print(status.status)
|
|
209
102
|
"""
|
|
210
103
|
try:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
raise ValueError("The result is not of type List")
|
|
217
|
-
return result
|
|
218
|
-
except Exception as e:
|
|
219
|
-
logger.error(f"Failed to get workflow executions: {str(e)}")
|
|
220
|
-
raise
|
|
221
|
-
|
|
222
|
-
def get_execution(self, execution_id: str) -> Execution:
|
|
104
|
+
return self.sdk_api.get_execution_status(execution_id)
|
|
105
|
+
except ApiException as e:
|
|
106
|
+
raise Exception(f"Failed to get execution status: {e}")
|
|
107
|
+
|
|
108
|
+
def get_execution_result(self, execution_id: str) -> Dict[str, Any]:
|
|
223
109
|
"""
|
|
224
|
-
Get the
|
|
225
|
-
|
|
110
|
+
Get the final result of an execution.
|
|
111
|
+
|
|
226
112
|
Args:
|
|
227
|
-
execution_id:
|
|
228
|
-
|
|
113
|
+
execution_id: The execution identifier
|
|
114
|
+
|
|
229
115
|
Returns:
|
|
230
|
-
|
|
116
|
+
The result object of the execution
|
|
117
|
+
|
|
118
|
+
Raises:
|
|
119
|
+
Exception: If the result request fails or execution failed
|
|
120
|
+
|
|
121
|
+
Example:
|
|
122
|
+
result = client.get_execution_result(execution_id)
|
|
123
|
+
print(result)
|
|
231
124
|
"""
|
|
232
125
|
try:
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
126
|
+
response = self.sdk_api.get_execution_result(execution_id)
|
|
127
|
+
|
|
128
|
+
if response.error:
|
|
129
|
+
raise Exception(response.error)
|
|
130
|
+
|
|
131
|
+
return response.result or {}
|
|
132
|
+
except ApiException as e:
|
|
133
|
+
raise Exception(f"Failed to get execution result: {e}")
|
|
134
|
+
|
|
135
|
+
def wait_for_execution_result(
|
|
136
|
+
self,
|
|
137
|
+
execution_id: str,
|
|
138
|
+
interval: float = 1.0,
|
|
139
|
+
timeout: float = 3600.0
|
|
140
|
+
) -> Dict[str, Any]:
|
|
242
141
|
"""
|
|
243
|
-
|
|
244
|
-
|
|
142
|
+
Wait for an execution to reach a terminal state and return the result.
|
|
143
|
+
|
|
144
|
+
Continuously polls the execution status until it's either "completed",
|
|
145
|
+
"cancelled", or "failed".
|
|
146
|
+
|
|
245
147
|
Args:
|
|
246
|
-
execution_id:
|
|
247
|
-
|
|
148
|
+
execution_id: The execution identifier
|
|
149
|
+
interval: Polling interval in seconds (default is 1.0)
|
|
150
|
+
timeout: Maximum wait time in seconds (default is 3600 - 1 hour)
|
|
151
|
+
|
|
248
152
|
Returns:
|
|
249
|
-
|
|
153
|
+
The execution result if completed
|
|
154
|
+
|
|
155
|
+
Raises:
|
|
156
|
+
Exception: If the execution ends as "cancelled" or "failed", or times out
|
|
157
|
+
|
|
158
|
+
Example:
|
|
159
|
+
result = client.wait_for_execution_result(execution_id, interval=2.0)
|
|
250
160
|
"""
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
161
|
+
start_time = time.time()
|
|
162
|
+
|
|
163
|
+
while True:
|
|
164
|
+
elapsed_time = time.time() - start_time
|
|
165
|
+
if elapsed_time >= timeout:
|
|
166
|
+
raise Exception(f"Execution {execution_id} timed out after {timeout}s")
|
|
167
|
+
|
|
168
|
+
status_response = self.get_execution_status(execution_id)
|
|
169
|
+
current_status = status_response.status
|
|
170
|
+
|
|
171
|
+
if current_status == Status.COMPLETED:
|
|
172
|
+
return self.get_execution_result(execution_id)
|
|
173
|
+
elif current_status in [Status.FAILED, Status.CANCELLED]:
|
|
174
|
+
reason = f" - {status_response.reason}" if status_response.reason else ""
|
|
175
|
+
raise Exception(f"Execution {execution_id} ended with status: {current_status.value}{reason}")
|
|
176
|
+
|
|
177
|
+
# Wait for the specified interval before polling again
|
|
178
|
+
time.sleep(interval)
|
|
179
|
+
|
|
180
|
+
def upload_execution_files(
|
|
181
|
+
self,
|
|
182
|
+
execution_id: str,
|
|
183
|
+
files: List[Union[bytes, str, Tuple[str, bytes]]],
|
|
184
|
+
default_filename: str = "file.txt"
|
|
185
|
+
) -> UploadExecutionFiles200Response:
|
|
257
186
|
"""
|
|
258
|
-
|
|
259
|
-
|
|
187
|
+
Upload files to an execution.
|
|
188
|
+
|
|
260
189
|
Args:
|
|
261
|
-
execution_id:
|
|
262
|
-
|
|
190
|
+
execution_id: The execution identifier
|
|
191
|
+
files: List of files to upload. Each file can be:
|
|
192
|
+
- bytes: Raw file content (will use default_filename)
|
|
193
|
+
- str: File path as string (will read file and use filename)
|
|
194
|
+
- Tuple[str, bytes]: (filename, file_content) tuple
|
|
195
|
+
default_filename: Default filename to use when file is provided as bytes
|
|
196
|
+
|
|
263
197
|
Returns:
|
|
264
|
-
|
|
265
|
-
|
|
198
|
+
The upload response containing message and file IDs
|
|
199
|
+
|
|
266
200
|
Raises:
|
|
267
|
-
|
|
201
|
+
Exception: If the upload request fails
|
|
202
|
+
|
|
203
|
+
Example:
|
|
204
|
+
# Upload with file content (file should be in your current working directory)
|
|
205
|
+
with open('hello.txt', 'r') as f:
|
|
206
|
+
file_content = f.read()
|
|
207
|
+
|
|
208
|
+
response = client.upload_execution_files(execution_id, [file_content.encode()])
|
|
209
|
+
print(f"Uploaded files: {response.file_ids}")
|
|
210
|
+
|
|
211
|
+
# Upload with filename and content
|
|
212
|
+
files = [('hello.txt', file_content.encode())]
|
|
213
|
+
response = client.upload_execution_files(execution_id, files)
|
|
214
|
+
|
|
215
|
+
# Or create content directly
|
|
216
|
+
hello_content = "Hello World!".encode()
|
|
217
|
+
response = client.upload_execution_files(execution_id, [hello_content])
|
|
268
218
|
"""
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
219
|
+
try:
|
|
220
|
+
# Process files to ensure proper format
|
|
221
|
+
processed_files = []
|
|
222
|
+
for file_item in files:
|
|
223
|
+
if isinstance(file_item, tuple):
|
|
224
|
+
# Already in (filename, content) format
|
|
225
|
+
filename, content = file_item
|
|
226
|
+
if isinstance(content, str):
|
|
227
|
+
content = content.encode()
|
|
228
|
+
processed_files.append((filename, content))
|
|
229
|
+
elif isinstance(file_item, str):
|
|
230
|
+
# Check if string is a file path that exists, otherwise treat as content
|
|
231
|
+
if os.path.isfile(file_item):
|
|
232
|
+
# File path - read the file
|
|
233
|
+
filename = os.path.basename(file_item)
|
|
234
|
+
with open(file_item, 'rb') as f:
|
|
235
|
+
content = f.read()
|
|
236
|
+
processed_files.append((filename, content))
|
|
237
|
+
else:
|
|
238
|
+
# String content - encode and use default filename
|
|
239
|
+
content = file_item.encode()
|
|
240
|
+
processed_files.append((default_filename, content))
|
|
241
|
+
elif isinstance(file_item, bytes):
|
|
242
|
+
# Raw bytes - use default filename
|
|
243
|
+
processed_files.append((default_filename, file_item))
|
|
244
|
+
else:
|
|
245
|
+
# Other types - convert to string content and encode
|
|
246
|
+
content = str(file_item).encode()
|
|
247
|
+
processed_files.append((default_filename, content))
|
|
248
|
+
|
|
249
|
+
response = self.execution_api.upload_execution_files(execution_id, files=processed_files)
|
|
250
|
+
return response
|
|
251
|
+
except ApiException as e:
|
|
252
|
+
raise Exception(f"Failed to upload execution files: {e}")
|
|
253
|
+
|
|
254
|
+
def get_browser_session_recording(self, execution_id: str) -> str:
|
|
279
255
|
"""
|
|
280
|
-
|
|
281
|
-
|
|
256
|
+
Get the browser session recording URL for a completed execution.
|
|
257
|
+
|
|
282
258
|
Args:
|
|
283
|
-
execution_id:
|
|
284
|
-
|
|
285
|
-
timeout: Maximum time in seconds to wait. None means wait indefinitely
|
|
286
|
-
status_callback: Optional callback function that will be called with each status update
|
|
287
|
-
|
|
259
|
+
execution_id: The execution identifier
|
|
260
|
+
|
|
288
261
|
Returns:
|
|
289
|
-
|
|
290
|
-
|
|
262
|
+
The URL of the browser session recording
|
|
263
|
+
|
|
291
264
|
Raises:
|
|
292
|
-
|
|
293
|
-
|
|
265
|
+
Exception: If the recording request fails
|
|
266
|
+
|
|
267
|
+
Example:
|
|
268
|
+
recording_url = client.get_browser_session_recording(execution_id)
|
|
269
|
+
print(f"Recording available at: {recording_url}")
|
|
294
270
|
"""
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
status_callback(current_status)
|
|
309
|
-
last_status = current_status
|
|
310
|
-
|
|
311
|
-
# Check if we've reached a terminal state
|
|
312
|
-
if current_status.status.value in [state.value for state in ExecutionTerminalState]:
|
|
313
|
-
return current_status
|
|
271
|
+
try:
|
|
272
|
+
response = self.sdk_api.get_browser_session_recording(execution_id)
|
|
273
|
+
return response.recording_url
|
|
274
|
+
except ApiException as e:
|
|
275
|
+
raise Exception(f"Failed to get browser session recording: {e}")
|
|
276
|
+
|
|
277
|
+
def __enter__(self):
|
|
278
|
+
"""Context manager entry."""
|
|
279
|
+
return self
|
|
280
|
+
|
|
281
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
282
|
+
"""Context manager exit."""
|
|
283
|
+
pass
|
|
314
284
|
|
|
315
|
-
# Wait before next check
|
|
316
|
-
time.sleep(polling_interval)
|
|
317
285
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
286
|
+
# Convenience functions that mirror the TypeScript SDK pattern
|
|
287
|
+
def create_client(api_key: str, base_url: Optional[str] = None) -> AsteroidClient:
|
|
288
|
+
"""
|
|
289
|
+
Create an API client with a provided API key.
|
|
290
|
+
|
|
291
|
+
This is a convenience function that creates an AsteroidClient instance.
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
api_key: Your API key
|
|
295
|
+
base_url: Optional base URL
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
A configured AsteroidClient instance
|
|
299
|
+
|
|
300
|
+
Example:
|
|
301
|
+
client = create_client('your-api-key')
|
|
302
|
+
"""
|
|
303
|
+
return AsteroidClient(api_key, base_url)
|
|
327
304
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
305
|
+
def execute_agent(client: AsteroidClient, agent_id: str, agent_profile_id: str, execution_data: Dict[str, Any]) -> str:
|
|
306
|
+
"""
|
|
307
|
+
Execute an agent with the provided parameters.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
client: The AsteroidClient instance
|
|
311
|
+
agent_id: The ID of the agent to execute
|
|
312
|
+
agent_profile_id: The ID of the agent profile
|
|
313
|
+
execution_data: The execution parameters
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
The execution ID
|
|
317
|
+
|
|
318
|
+
Example:
|
|
319
|
+
execution_id = execute_agent(client, 'my-agent-id', {'input': 'some dynamic value'})
|
|
320
|
+
"""
|
|
321
|
+
return client.execute_agent(agent_id, agent_profile_id, execution_data)
|
|
333
322
|
|
|
334
|
-
Returns:
|
|
335
|
-
ExecutionResult object containing final status, result, and other metadata
|
|
336
323
|
|
|
337
|
-
Raises:
|
|
338
|
-
TimeoutError: If timeout is reached before execution completes
|
|
339
|
-
ValueError: If execution_id is invalid
|
|
340
|
-
"""
|
|
341
|
-
# Wait for execution to reach terminal state
|
|
342
|
-
final_status = self.wait_for_execution(
|
|
343
|
-
execution_id=execution_id,
|
|
344
|
-
polling_interval=polling_interval,
|
|
345
|
-
timeout=timeout,
|
|
346
|
-
status_callback=status_callback
|
|
347
|
-
)
|
|
348
324
|
|
|
349
|
-
|
|
350
|
-
|
|
325
|
+
def get_execution_status(client: AsteroidClient, execution_id: str) -> ExecutionStatusResponse:
|
|
326
|
+
"""
|
|
327
|
+
Get the current status for an execution.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
client: The AsteroidClient instance
|
|
331
|
+
execution_id: The execution identifier
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
The execution status details
|
|
335
|
+
|
|
336
|
+
Example:
|
|
337
|
+
status = get_execution_status(client, execution_id)
|
|
338
|
+
print(status.status)
|
|
339
|
+
"""
|
|
340
|
+
return client.get_execution_status(execution_id)
|
|
351
341
|
|
|
352
|
-
# If execution failed, include error information in logs
|
|
353
|
-
if final_status in [ExecutionTerminalState.FAILED, ExecutionTerminalState.ERROR]:
|
|
354
|
-
logger.error(f"Execution {execution_id} failed with error: {result.error}")
|
|
355
342
|
|
|
356
|
-
|
|
343
|
+
def get_execution_result(client: AsteroidClient, execution_id: str) -> Dict[str, Any]:
|
|
344
|
+
"""
|
|
345
|
+
Get the final result of an execution.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
client: The AsteroidClient instance
|
|
349
|
+
execution_id: The execution identifier
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
The result object of the execution
|
|
353
|
+
|
|
354
|
+
Example:
|
|
355
|
+
result = get_execution_result(client, execution_id)
|
|
356
|
+
print(result)
|
|
357
|
+
"""
|
|
358
|
+
return client.get_execution_result(execution_id)
|
|
357
359
|
|
|
358
|
-
def execute_workflow_and_get_result(
|
|
359
|
-
self,
|
|
360
|
-
workflow_id: UUID,
|
|
361
|
-
execution_params: Dict[str, Any],
|
|
362
|
-
polling_interval: float = 1.0,
|
|
363
|
-
timeout: Optional[float] = None,
|
|
364
|
-
status_callback: Optional[Callable[[ExecutionStatus], None]] = None
|
|
365
|
-
) -> ExecutionResult:
|
|
366
|
-
"""
|
|
367
|
-
Execute a workflow and wait for its result.
|
|
368
360
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
361
|
+
def wait_for_execution_result(
|
|
362
|
+
client: AsteroidClient,
|
|
363
|
+
execution_id: str,
|
|
364
|
+
interval: float = 1.0,
|
|
365
|
+
timeout: float = 3600.0
|
|
366
|
+
) -> Dict[str, Any]:
|
|
367
|
+
"""
|
|
368
|
+
Wait for an execution to reach a terminal state and return the result.
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
client: The AsteroidClient instance
|
|
372
|
+
execution_id: The execution identifier
|
|
373
|
+
interval: Polling interval in seconds (default is 1.0)
|
|
374
|
+
timeout: Maximum wait time in seconds (default is 3600 - 1 hour)
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
The execution result if completed
|
|
378
|
+
|
|
379
|
+
Example:
|
|
380
|
+
result = wait_for_execution_result(client, execution_id, interval=2.0)
|
|
381
|
+
"""
|
|
382
|
+
return client.wait_for_execution_result(execution_id, interval, timeout)
|
|
375
383
|
|
|
376
|
-
Returns:
|
|
377
|
-
ExecutionResult object containing final status, result, and other metadata
|
|
378
|
-
"""
|
|
379
|
-
# Start execution
|
|
380
|
-
execution_id = self.execute_workflow(workflow_id, execution_params)
|
|
381
384
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
385
|
+
def upload_execution_files(
|
|
386
|
+
client: AsteroidClient,
|
|
387
|
+
execution_id: str,
|
|
388
|
+
files: List[Union[bytes, str, Tuple[str, bytes]]],
|
|
389
|
+
default_filename: str = "file.txt"
|
|
390
|
+
) -> UploadExecutionFiles200Response:
|
|
391
|
+
"""
|
|
392
|
+
Upload files to an execution.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
client: The AsteroidClient instance
|
|
396
|
+
execution_id: The execution identifier
|
|
397
|
+
files: List of files to upload
|
|
398
|
+
default_filename: Default filename to use when file is provided as bytes
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
The upload response containing message and file IDs
|
|
402
|
+
|
|
403
|
+
Example:
|
|
404
|
+
# Create a simple text file with "Hello World!" content
|
|
405
|
+
hello_content = "Hello World!".encode()
|
|
406
|
+
response = upload_execution_files(client, execution_id, [hello_content])
|
|
407
|
+
print(f"Uploaded files: {response.file_ids}")
|
|
408
|
+
|
|
409
|
+
# Or specify filename with content
|
|
410
|
+
files = [('hello.txt', "Hello World!".encode())]
|
|
411
|
+
response = upload_execution_files(client, execution_id, files)
|
|
412
|
+
"""
|
|
413
|
+
return client.upload_execution_files(execution_id, files, default_filename)
|
|
389
414
|
|
|
390
|
-
def __enter__(self):
|
|
391
|
-
return self
|
|
392
415
|
|
|
393
|
-
|
|
394
|
-
|
|
416
|
+
def get_browser_session_recording(client: AsteroidClient, execution_id: str) -> str:
|
|
417
|
+
"""
|
|
418
|
+
Get the browser session recording URL for a completed execution.
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
client: The AsteroidClient instance
|
|
422
|
+
execution_id: The execution identifier
|
|
423
|
+
|
|
424
|
+
Returns:
|
|
425
|
+
The URL of the browser session recording
|
|
426
|
+
|
|
427
|
+
Example:
|
|
428
|
+
recording_url = get_browser_session_recording(client, execution_id)
|
|
429
|
+
print(f"Recording available at: {recording_url}")
|
|
430
|
+
"""
|
|
431
|
+
return client.get_browser_session_recording(execution_id)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
# Re-export common types for convenience
|
|
435
|
+
__all__ = [
|
|
436
|
+
'AsteroidClient',
|
|
437
|
+
'create_client',
|
|
438
|
+
'execute_agent',
|
|
439
|
+
'get_execution_status',
|
|
440
|
+
'get_execution_result',
|
|
441
|
+
'wait_for_execution_result',
|
|
442
|
+
'upload_execution_files',
|
|
443
|
+
'get_browser_session_recording'
|
|
444
|
+
]
|