asteroid-odyssey 0.1.22__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.
Files changed (96) hide show
  1. asteroid_odyssey/__init__.py +20 -34
  2. asteroid_odyssey/client.py +387 -337
  3. asteroid_odyssey/openapi_client/__init__.py +73 -0
  4. asteroid_odyssey/openapi_client/api/__init__.py +7 -0
  5. asteroid_odyssey/openapi_client/api/api_api.py +516 -0
  6. asteroid_odyssey/openapi_client/api/execution_api.py +335 -0
  7. asteroid_odyssey/openapi_client/api/sdk_api.py +1434 -0
  8. asteroid_odyssey/openapi_client/api_client.py +801 -0
  9. asteroid_odyssey/openapi_client/api_response.py +21 -0
  10. asteroid_odyssey/openapi_client/configuration.py +606 -0
  11. asteroid_odyssey/openapi_client/exceptions.py +216 -0
  12. asteroid_odyssey/openapi_client/models/__init__.py +27 -0
  13. asteroid_odyssey/openapi_client/models/browser_session_recording_response.py +87 -0
  14. asteroid_odyssey/openapi_client/models/error_response.py +87 -0
  15. asteroid_odyssey/openapi_client/models/execution_response.py +87 -0
  16. asteroid_odyssey/openapi_client/models/execution_result.py +101 -0
  17. asteroid_odyssey/openapi_client/models/execution_result_response.py +100 -0
  18. asteroid_odyssey/openapi_client/models/execution_status_response.py +95 -0
  19. asteroid_odyssey/openapi_client/models/health_check200_response.py +87 -0
  20. asteroid_odyssey/openapi_client/models/health_check500_response.py +87 -0
  21. asteroid_odyssey/openapi_client/models/status.py +43 -0
  22. asteroid_odyssey/openapi_client/models/structured_agent_execution_request.py +89 -0
  23. asteroid_odyssey/openapi_client/models/upload_execution_files200_response.py +89 -0
  24. asteroid_odyssey/openapi_client/rest.py +258 -0
  25. asteroid_odyssey-1.0.0.dist-info/METADATA +208 -0
  26. asteroid_odyssey-1.0.0.dist-info/RECORD +29 -0
  27. {asteroid_odyssey-0.1.22.dist-info → asteroid_odyssey-1.0.0.dist-info}/WHEEL +1 -1
  28. asteroid_odyssey/api/generated/asteroid_agents_api_client/__init__.py +0 -8
  29. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/__init__.py +0 -1
  30. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/agent/get_agents.py +0 -127
  31. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/api/__init__.py +0 -0
  32. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/api/get_open_api.py +0 -79
  33. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/api/health_check.py +0 -131
  34. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/credentials/__init__.py +0 -0
  35. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/credentials/get_credentials_public_key.py +0 -127
  36. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/execution/__init__.py +0 -0
  37. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/execution/delete_execution.py +0 -156
  38. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/execution/get_browser_session.py +0 -154
  39. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/execution/get_execution.py +0 -154
  40. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/execution/get_execution_files.py +0 -159
  41. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/execution/get_execution_progress.py +0 -159
  42. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/execution/get_executions_for_workflow.py +0 -156
  43. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/execution/update_execution_status.py +0 -115
  44. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/notifications/__init__.py +0 -0
  45. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/notifications/set_slack_channel.py +0 -107
  46. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/optimiser/__init__.py +0 -0
  47. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/optimiser/queue_optimisation_job.py +0 -103
  48. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/__init__.py +0 -0
  49. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/add_workflow_credential.py +0 -111
  50. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/create_workflow.py +0 -174
  51. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/delete_workflow.py +0 -156
  52. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/delete_workflow_credentials.py +0 -96
  53. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/execute_workflow.py +0 -179
  54. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/get_agent_workflow_executions.py +0 -155
  55. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/get_workflow.py +0 -154
  56. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/get_workflow_credentials.py +0 -154
  57. asteroid_odyssey/api/generated/asteroid_agents_api_client/api/workflow/get_workflow_versions.py +0 -159
  58. asteroid_odyssey/api/generated/asteroid_agents_api_client/client.py +0 -268
  59. asteroid_odyssey/api/generated/asteroid_agents_api_client/errors.py +0 -16
  60. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/__init__.py +0 -61
  61. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/agent.py +0 -90
  62. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/browser_session.py +0 -136
  63. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/create_workflow_request.py +0 -126
  64. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/create_workflow_request_fields.py +0 -48
  65. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/create_workflow_request_provider.py +0 -9
  66. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/credential.py +0 -66
  67. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/credentials_request.py +0 -72
  68. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/credentials_response.py +0 -80
  69. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/delete_execution_response_200.py +0 -58
  70. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/delete_execution_response_404.py +0 -58
  71. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/delete_workflow_response_200.py +0 -58
  72. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/delete_workflow_response_404.py +0 -58
  73. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/execution.py +0 -147
  74. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/execution_dynamic_data.py +0 -48
  75. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/execution_result.py +0 -43
  76. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/execution_status.py +0 -89
  77. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/file.py +0 -127
  78. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/health_check_response_200.py +0 -58
  79. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/health_check_response_500.py +0 -58
  80. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/optimisation_request.py +0 -59
  81. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/progress_update.py +0 -77
  82. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/result_schema.py +0 -51
  83. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/slack_channel_request.py +0 -58
  84. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/status.py +0 -13
  85. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/workflow.py +0 -152
  86. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/workflow_execution.py +0 -82
  87. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/workflow_execution_request.py +0 -48
  88. asteroid_odyssey/api/generated/asteroid_agents_api_client/models/workflow_fields.py +0 -48
  89. asteroid_odyssey/api/generated/asteroid_agents_api_client/py.typed +0 -1
  90. asteroid_odyssey/api/generated/asteroid_agents_api_client/types.py +0 -45
  91. asteroid_odyssey/exceptions.py +0 -15
  92. asteroid_odyssey-0.1.22.dist-info/METADATA +0 -31
  93. asteroid_odyssey-0.1.22.dist-info/RECORD +0 -72
  94. asteroid_odyssey-0.1.22.dist-info/entry_points.txt +0 -2
  95. /asteroid_odyssey/{api/generated/asteroid_agents_api_client/api/agent/__init__.py → openapi_client/py.typed} +0 -0
  96. {asteroid_odyssey-0.1.22.dist-info → asteroid_odyssey-1.0.0.dist-info}/top_level.txt +0 -0
@@ -1,394 +1,444 @@
1
- from typing import Optional, Dict, Any, List, Union, Callable, Tuple
2
- from uuid import UUID
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
- from .api.generated.asteroid_agents_api_client.models import (
9
- CreateWorkflowRequest,
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.workflow.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
- class ExecutionTerminalState(Enum):
28
- """Terminal states for an execution"""
29
- COMPLETED = "completed"
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
- agent_name = "iris"
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
- A high-level client for interacting with the Asteroid API.
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
- def __init__(
51
- self,
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
- Initialize the Asteroid client.
58
-
39
+ Create an API client with the provided API key.
40
+
59
41
  Args:
60
- api_key: API key for authentication. If not provided, will look for ASTEROID_API_KEY env var
61
- base_url: Base URL for the API. Defaults to production URL if not specified
62
- verify_ssl: Whether to verify SSL certificates. Defaults to True
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
- self.api_key = api_key or os.getenv("ASTEROID_API_KEY")
65
- if not self.api_key:
66
- raise ValueError(
67
- "API key is required. Either pass it directly or set ASTEROID_API_KEY environment variable"
68
- )
69
-
70
- self.base_url = base_url or "https://odyssey.asteroid.ai/api/v1"
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
- Returns:
83
- List of agent details
84
- """
85
- try:
86
- result = asteroid_get_agents(client=self.client)
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
- Create a new workflow for an agent.
105
-
63
+ Execute an agent with the provided parameters.
64
+
106
65
  Args:
107
- workflow_name: Name of the workflow
108
- start_url: Starting URL for the workflow
109
- prompt: Prompt for the workflow
110
- result_schema: Optional custom result schema. Currently not fully supported.
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
- Workflow ID
114
-
115
- Warning:
116
- Custom result schemas are not fully supported yet and will be added in a future update.
117
- Currently, only the default schema will be used regardless of input.
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
- if result_schema is not None:
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
- fields = CreateWorkflowRequestFields()
147
- fields.additional_properties = {
148
- "workflow_name": workflow_name,
149
- "start_url": start_url
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
- Execute an existing workflow.
179
-
88
+ Get the current status for an execution.
89
+
180
90
  Args:
181
- workflow_id: ID of workflow to execute
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
- List of workflow executions
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
- result = get_agent_workflow_executions(
212
- agent_name=agent_name,
213
- client=self.client
214
- ).parsed
215
- if not isinstance(result, List):
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 full execution details.
225
-
110
+ Get the final result of an execution.
111
+
226
112
  Args:
227
- execution_id: ID of the execution to retrieve
228
-
113
+ execution_id: The execution identifier
114
+
229
115
  Returns:
230
- Execution object with full details
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
- result = get_execution(id=UUID(execution_id), client=self.client).parsed
234
- if not isinstance(result, Execution):
235
- raise ValueError("The result is not of type Execution")
236
- return result
237
- except Exception as e:
238
- logger.error(f"Failed to get execution: {str(e)}")
239
- raise
240
-
241
- def get_execution_status(self, execution_id: str) -> ExecutionStatus:
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
- Get the current status of an execution.
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: ID of the execution to check
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
- Current execution status
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
- execution = self.get_execution(execution_id)
252
- if not isinstance(execution.status, ExecutionStatus):
253
- raise ValueError("The execution status is not of type ExecutionStatus")
254
- return execution.status
255
-
256
- def get_execution_result(self, execution_id: str) -> ExecutionResult:
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
- Get the result of an execution.
259
-
187
+ Upload files to an execution.
188
+
260
189
  Args:
261
- execution_id: ID of the execution to get results for
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
- ExecutionResult object containing status, result, and other metadata
265
-
198
+ The upload response containing message and file IDs
199
+
266
200
  Raises:
267
- ValueError: If execution doesn't exist or hasn't completed
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
- execution = self.get_execution(execution_id)
270
- return ExecutionResult(execution)
271
-
272
- def wait_for_execution(
273
- self,
274
- execution_id: str,
275
- polling_interval: float = 1.0,
276
- timeout: Optional[float] = None,
277
- status_callback: Optional[Callable[[ExecutionStatus], None]] = None
278
- ) -> ExecutionStatus:
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
- Wait for an execution to reach a terminal state.
281
-
256
+ Get the browser session recording URL for a completed execution.
257
+
282
258
  Args:
283
- execution_id: ID of the execution to wait for
284
- polling_interval: Time in seconds between status checks
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
- Final execution status
290
-
262
+ The URL of the browser session recording
263
+
291
264
  Raises:
292
- TimeoutError: If timeout is reached before execution reaches terminal state
293
- ValueError: If execution_id is invalid
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
- start_time = time.time()
296
- last_status = None
297
-
298
- while True:
299
- # Check if we've exceeded timeout
300
- if timeout and (time.time() - start_time) > timeout:
301
- raise TimeoutError(f"Execution {execution_id} did not complete within {timeout} seconds")
302
-
303
- # Get current status
304
- current_status = self.get_execution_status(execution_id)
305
-
306
- # Call status callback if status has changed
307
- if status_callback and current_status != last_status:
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
- def wait_for_execution_result(
319
- self,
320
- execution_id: str,
321
- polling_interval: float = 1.0,
322
- timeout: Optional[float] = None,
323
- status_callback: Optional[Callable[[ExecutionStatus], None]] = None
324
- ) -> ExecutionResult:
325
- """
326
- Wait for an execution to complete and get its result.
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
- Args:
329
- execution_id: ID of the execution to wait for
330
- polling_interval: Time in seconds between status checks
331
- timeout: Maximum time in seconds to wait. None means wait indefinitely
332
- status_callback: Optional callback function that will be called with each status update
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
- # Get the final result
350
- result = self.get_execution_result(execution_id)
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
- return result
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
- Args:
370
- workflow_id: ID of workflow to execute
371
- execution_params: Parameters for workflow execution
372
- polling_interval: Time in seconds between status checks
373
- timeout: Maximum time in seconds to wait. None means wait indefinitely
374
- status_callback: Optional callback function that will be called with each status update
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
- # Wait for result
383
- return self.wait_for_execution_result(
384
- execution_id=execution_id,
385
- polling_interval=polling_interval,
386
- timeout=timeout,
387
- status_callback=status_callback
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
- def __exit__(self, exc_type, exc_val, exc_tb):
394
- pass
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
+ ]