simplex 1.0.0__py3-none-any.whl → 1.0.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 simplex might be problematic. Click here for more details.
- simplex/__init__.py +34 -3
- simplex/client.py +355 -0
- simplex/errors.py +160 -0
- simplex/http_client.py +403 -0
- simplex/resources/__init__.py +10 -0
- simplex/resources/workflow.py +502 -0
- simplex/resources/workflow_session.py +333 -0
- simplex/types.py +276 -0
- simplex-1.0.6.dist-info/METADATA +423 -0
- simplex-1.0.6.dist-info/RECORD +13 -0
- {simplex-1.0.0.dist-info → simplex-1.0.6.dist-info}/WHEEL +1 -1
- {simplex-1.0.0.dist-info → simplex-1.0.6.dist-info/licenses}/LICENSE +2 -2
- simplex/constants.py +0 -1
- simplex/simplex.py +0 -187
- simplex/utils.py +0 -12
- simplex-1.0.0.dist-info/METADATA +0 -29
- simplex-1.0.0.dist-info/RECORD +0 -10
- simplex-1.0.0.dist-info/entry_points.txt +0 -2
- {simplex-1.0.0.dist-info → simplex-1.0.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Workflow resource for the Simplex SDK.
|
|
3
|
+
|
|
4
|
+
This module provides the Workflow class which handles all workflow-related
|
|
5
|
+
operations including execution, session management, and agent tasks.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
from simplex.errors import SimplexError, WorkflowError
|
|
12
|
+
from simplex.http_client import HttpClient
|
|
13
|
+
from simplex.types import (
|
|
14
|
+
AgenticResponse,
|
|
15
|
+
CreateWorkflowSessionResponse,
|
|
16
|
+
RunAgentResponse,
|
|
17
|
+
RunWorkflowResponse,
|
|
18
|
+
WorkflowStatusResponse,
|
|
19
|
+
WorkflowVariables,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Workflow:
|
|
24
|
+
"""
|
|
25
|
+
Resource class for workflow operations.
|
|
26
|
+
|
|
27
|
+
This class provides methods for:
|
|
28
|
+
- Running workflows with variables
|
|
29
|
+
- Creating and managing workflow sessions
|
|
30
|
+
- Executing agent tasks
|
|
31
|
+
- Managing workflow segments
|
|
32
|
+
- Controlling capture mode
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
_http_client: HTTP client for making API requests
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self, http_client: HttpClient):
|
|
39
|
+
"""
|
|
40
|
+
Initialize the Workflow resource.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
http_client: HTTP client instance for API communication
|
|
44
|
+
"""
|
|
45
|
+
self._http_client = http_client
|
|
46
|
+
|
|
47
|
+
def run(
|
|
48
|
+
self,
|
|
49
|
+
workflow_id: str,
|
|
50
|
+
variables: Optional[WorkflowVariables] = None,
|
|
51
|
+
metadata: Optional[str] = None,
|
|
52
|
+
webhook_url: Optional[str] = None
|
|
53
|
+
) -> RunWorkflowResponse:
|
|
54
|
+
"""
|
|
55
|
+
Execute a workflow by ID.
|
|
56
|
+
|
|
57
|
+
This method starts a workflow execution with the provided parameters.
|
|
58
|
+
The workflow will run asynchronously and you can check its status
|
|
59
|
+
using the get_status() method with the returned session_id.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
workflow_id: Unique identifier of the workflow to run
|
|
63
|
+
variables: Dictionary of variables to pass to the workflow (optional)
|
|
64
|
+
metadata: Optional metadata string to attach to the run
|
|
65
|
+
webhook_url: Optional webhook URL for status updates
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Response containing session_id and execution details
|
|
69
|
+
|
|
70
|
+
Raises:
|
|
71
|
+
WorkflowError: If the workflow execution fails to start
|
|
72
|
+
|
|
73
|
+
Example:
|
|
74
|
+
>>> result = client.workflows.run(
|
|
75
|
+
... "workflow-123",
|
|
76
|
+
... variables={"username": "user@example.com", "product_id": "456"}
|
|
77
|
+
... )
|
|
78
|
+
>>> print(f"Started workflow with session: {result['session_id']}")
|
|
79
|
+
"""
|
|
80
|
+
request_data = {
|
|
81
|
+
'workflow_id': workflow_id,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if variables:
|
|
85
|
+
request_data['variables'] = variables
|
|
86
|
+
|
|
87
|
+
if metadata:
|
|
88
|
+
request_data['metadata'] = metadata
|
|
89
|
+
|
|
90
|
+
if webhook_url:
|
|
91
|
+
request_data['webhook_url'] = webhook_url
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
response = self._http_client.post('/run_workflow', data=request_data)
|
|
95
|
+
|
|
96
|
+
if not response.get('succeeded'):
|
|
97
|
+
raise WorkflowError(
|
|
98
|
+
response.get('message') or 'Workflow execution failed',
|
|
99
|
+
workflow_id=workflow_id,
|
|
100
|
+
session_id=response.get('session_id')
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return response
|
|
104
|
+
except WorkflowError:
|
|
105
|
+
raise
|
|
106
|
+
except Exception as e:
|
|
107
|
+
raise WorkflowError(
|
|
108
|
+
f"Failed to run workflow: {str(e)}",
|
|
109
|
+
workflow_id=workflow_id
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def get_status(self, session_id: str) -> WorkflowStatusResponse:
|
|
113
|
+
"""
|
|
114
|
+
Get the status of a workflow execution.
|
|
115
|
+
|
|
116
|
+
This method retrieves the current status of a workflow, including
|
|
117
|
+
whether it has completed and what actions have been taken.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
session_id: Session ID returned from run()
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Status response with completion info and action history
|
|
124
|
+
|
|
125
|
+
Raises:
|
|
126
|
+
WorkflowError: If status check fails
|
|
127
|
+
|
|
128
|
+
Example:
|
|
129
|
+
>>> status = client.workflows.get_status("session-123")
|
|
130
|
+
>>> if status['completed']:
|
|
131
|
+
... print(f"Workflow completed with {status['total_actions']} actions")
|
|
132
|
+
"""
|
|
133
|
+
try:
|
|
134
|
+
response = self._http_client.get(
|
|
135
|
+
f'/run_workflow_status?session_id={session_id}'
|
|
136
|
+
)
|
|
137
|
+
return response
|
|
138
|
+
except Exception as e:
|
|
139
|
+
raise WorkflowError(
|
|
140
|
+
f"Failed to get workflow status: {str(e)}",
|
|
141
|
+
session_id=session_id
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
def create_workflow_session(
|
|
145
|
+
self,
|
|
146
|
+
workflow_name: str,
|
|
147
|
+
url: str,
|
|
148
|
+
proxies: bool = False,
|
|
149
|
+
session_data: Optional[Any] = None
|
|
150
|
+
) -> CreateWorkflowSessionResponse:
|
|
151
|
+
"""
|
|
152
|
+
Create a new workflow session.
|
|
153
|
+
|
|
154
|
+
This creates a new browser session that can be controlled programmatically.
|
|
155
|
+
Unlike run(), this gives you direct control over the session through
|
|
156
|
+
agent tasks and actions.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
workflow_name: Name for this workflow session
|
|
160
|
+
url: Starting URL for the browser session
|
|
161
|
+
proxies: Whether to use proxies (default: False)
|
|
162
|
+
session_data: Optional data to associate with the session
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Response containing session details and access URLs
|
|
166
|
+
|
|
167
|
+
Raises:
|
|
168
|
+
WorkflowError: If session creation fails
|
|
169
|
+
|
|
170
|
+
Example:
|
|
171
|
+
>>> session = client.workflows.create_workflow_session(
|
|
172
|
+
... "test-session",
|
|
173
|
+
... "https://example.com",
|
|
174
|
+
... proxies=False
|
|
175
|
+
... )
|
|
176
|
+
>>> print(f"Session ID: {session['session_id']}")
|
|
177
|
+
>>> print(f"Livestream: {session['livestream_url']}")
|
|
178
|
+
"""
|
|
179
|
+
# Ensure URL has protocol
|
|
180
|
+
if not url.startswith('http://') and not url.startswith('https://'):
|
|
181
|
+
url = 'https://' + url
|
|
182
|
+
|
|
183
|
+
request_data = {
|
|
184
|
+
'workflow_name': workflow_name,
|
|
185
|
+
'url': url,
|
|
186
|
+
'proxies': proxies
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if session_data is not None:
|
|
190
|
+
request_data['session_data'] = json.dumps(session_data)
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
response = self._http_client.post(
|
|
194
|
+
'/create_workflow_session',
|
|
195
|
+
data=request_data
|
|
196
|
+
)
|
|
197
|
+
return response
|
|
198
|
+
except Exception as e:
|
|
199
|
+
raise WorkflowError(
|
|
200
|
+
f"Failed to create workflow session: {str(e)}"
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
def start_segment(
|
|
204
|
+
self,
|
|
205
|
+
workflow_id: str,
|
|
206
|
+
segment_name: str
|
|
207
|
+
) -> Dict[str, Any]:
|
|
208
|
+
"""
|
|
209
|
+
Start a new segment within a workflow.
|
|
210
|
+
|
|
211
|
+
Segments allow you to organize workflow actions into logical groups.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
workflow_id: ID of the workflow
|
|
215
|
+
segment_name: Name for the segment
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
Response containing segment_id if successful
|
|
219
|
+
|
|
220
|
+
Raises:
|
|
221
|
+
WorkflowError: If starting the segment fails
|
|
222
|
+
|
|
223
|
+
Example:
|
|
224
|
+
>>> result = client.workflows.start_segment("workflow-123", "login-phase")
|
|
225
|
+
>>> segment_id = result['segment_id']
|
|
226
|
+
"""
|
|
227
|
+
request_data = {
|
|
228
|
+
'workflow_id': workflow_id,
|
|
229
|
+
'segment_name': segment_name
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
response = self._http_client.post('/start_segment', data=request_data)
|
|
234
|
+
return response
|
|
235
|
+
except Exception as e:
|
|
236
|
+
raise WorkflowError(
|
|
237
|
+
f"Failed to start segment: {str(e)}",
|
|
238
|
+
workflow_id=workflow_id
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
def finish_segment(self, workflow_id: str) -> Dict[str, Any]:
|
|
242
|
+
"""
|
|
243
|
+
Finish the current segment within a workflow.
|
|
244
|
+
|
|
245
|
+
This completes the current segment and returns any recorded actions.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
workflow_id: ID of the workflow
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
Response containing segment actions if successful
|
|
252
|
+
|
|
253
|
+
Raises:
|
|
254
|
+
WorkflowError: If finishing the segment fails
|
|
255
|
+
|
|
256
|
+
Example:
|
|
257
|
+
>>> result = client.workflows.finish_segment("workflow-123")
|
|
258
|
+
>>> actions = result.get('segment_actions', [])
|
|
259
|
+
"""
|
|
260
|
+
request_data = {
|
|
261
|
+
'workflow_id': workflow_id
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
try:
|
|
265
|
+
response = self._http_client.post('/finish_segment', data=request_data)
|
|
266
|
+
return response
|
|
267
|
+
except Exception as e:
|
|
268
|
+
raise WorkflowError(
|
|
269
|
+
f"Failed to finish segment: {str(e)}",
|
|
270
|
+
workflow_id=workflow_id
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
def start_capture(self, session_id: str) -> Dict[str, bool]:
|
|
274
|
+
"""
|
|
275
|
+
Start capture mode for a session.
|
|
276
|
+
|
|
277
|
+
Capture mode records all browser actions for later playback or analysis.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
session_id: ID of the session
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
Response indicating success
|
|
284
|
+
|
|
285
|
+
Raises:
|
|
286
|
+
WorkflowError: If starting capture mode fails
|
|
287
|
+
|
|
288
|
+
Example:
|
|
289
|
+
>>> result = client.workflows.start_capture("session-123")
|
|
290
|
+
>>> if result['succeeded']:
|
|
291
|
+
... print("Capture mode started")
|
|
292
|
+
"""
|
|
293
|
+
request_data = {
|
|
294
|
+
'session_id': session_id
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
try:
|
|
298
|
+
response = self._http_client.post('/start_capture_mode', data=request_data)
|
|
299
|
+
return response
|
|
300
|
+
except Exception as e:
|
|
301
|
+
raise WorkflowError(
|
|
302
|
+
f"Failed to start capture mode: {str(e)}",
|
|
303
|
+
session_id=session_id
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
def stop_capture(self, session_id: str) -> Dict[str, bool]:
|
|
307
|
+
"""
|
|
308
|
+
Stop capture mode for a session.
|
|
309
|
+
|
|
310
|
+
This stops recording browser actions.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
session_id: ID of the session
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
Response indicating success
|
|
317
|
+
|
|
318
|
+
Raises:
|
|
319
|
+
WorkflowError: If stopping capture mode fails
|
|
320
|
+
|
|
321
|
+
Example:
|
|
322
|
+
>>> result = client.workflows.stop_capture("session-123")
|
|
323
|
+
>>> if result['succeeded']:
|
|
324
|
+
... print("Capture mode stopped")
|
|
325
|
+
"""
|
|
326
|
+
request_data = {
|
|
327
|
+
'session_id': session_id
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
try:
|
|
331
|
+
response = self._http_client.post('/stop_capture_mode', data=request_data)
|
|
332
|
+
return response
|
|
333
|
+
except Exception as e:
|
|
334
|
+
raise WorkflowError(
|
|
335
|
+
f"Failed to stop capture mode: {str(e)}",
|
|
336
|
+
session_id=session_id
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
def close_workflow_session(self, session_id: str) -> Dict[str, Any]:
|
|
340
|
+
"""
|
|
341
|
+
Close a workflow session.
|
|
342
|
+
|
|
343
|
+
This releases all resources associated with the session.
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
session_id: ID of the session to close
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
Response indicating success
|
|
350
|
+
|
|
351
|
+
Raises:
|
|
352
|
+
WorkflowError: If closing the session fails
|
|
353
|
+
|
|
354
|
+
Example:
|
|
355
|
+
>>> result = client.workflows.close_workflow_session("session-123")
|
|
356
|
+
>>> print(result.get('message'))
|
|
357
|
+
"""
|
|
358
|
+
request_data = {
|
|
359
|
+
'session_id': session_id
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
try:
|
|
363
|
+
response = self._http_client.post(
|
|
364
|
+
'/close_workflow_session',
|
|
365
|
+
data=request_data
|
|
366
|
+
)
|
|
367
|
+
return response
|
|
368
|
+
except Exception as e:
|
|
369
|
+
raise WorkflowError(
|
|
370
|
+
f"Failed to close workflow session: {str(e)}",
|
|
371
|
+
session_id=session_id
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
def agentic(
|
|
375
|
+
self,
|
|
376
|
+
task: str,
|
|
377
|
+
session_id: str,
|
|
378
|
+
max_steps: Optional[int] = None,
|
|
379
|
+
actions_to_exclude: Optional[List[str]] = None,
|
|
380
|
+
variables: Optional[Dict[str, Any]] = None
|
|
381
|
+
) -> AgenticResponse:
|
|
382
|
+
"""
|
|
383
|
+
Execute an agentic task within a session.
|
|
384
|
+
|
|
385
|
+
This method allows you to give natural language instructions to an AI agent
|
|
386
|
+
that will execute them within a browser session.
|
|
387
|
+
|
|
388
|
+
Args:
|
|
389
|
+
task: Natural language description of what to do
|
|
390
|
+
session_id: Session where the task should be executed
|
|
391
|
+
max_steps: Maximum number of steps the agent can take (optional)
|
|
392
|
+
actions_to_exclude: List of action types to exclude (optional)
|
|
393
|
+
variables: Dictionary of variables to use in the task (optional)
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
Response containing task results
|
|
397
|
+
|
|
398
|
+
Raises:
|
|
399
|
+
SimplexError: If the task execution fails
|
|
400
|
+
|
|
401
|
+
Example:
|
|
402
|
+
>>> result = client.workflows.agentic(
|
|
403
|
+
... "Find the login button and click it",
|
|
404
|
+
... "session-123",
|
|
405
|
+
... max_steps=10
|
|
406
|
+
... )
|
|
407
|
+
>>> if result['succeeded']:
|
|
408
|
+
... print("Task completed:", result['result'])
|
|
409
|
+
"""
|
|
410
|
+
request_data = {
|
|
411
|
+
'task': task,
|
|
412
|
+
'session_id': session_id
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if max_steps is not None:
|
|
416
|
+
request_data['max_steps'] = max_steps
|
|
417
|
+
|
|
418
|
+
if actions_to_exclude:
|
|
419
|
+
request_data['actions_to_exclude'] = actions_to_exclude
|
|
420
|
+
|
|
421
|
+
if variables:
|
|
422
|
+
request_data['variables'] = json.dumps(variables)
|
|
423
|
+
|
|
424
|
+
try:
|
|
425
|
+
response = self._http_client.post('/agentic', data=request_data)
|
|
426
|
+
|
|
427
|
+
if not response.get('succeeded') and response.get('error'):
|
|
428
|
+
raise SimplexError(
|
|
429
|
+
f"Agent task failed: {response['error']}",
|
|
430
|
+
status_code=500,
|
|
431
|
+
data={'session_id': session_id, 'task': task}
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
return response
|
|
435
|
+
except SimplexError:
|
|
436
|
+
raise
|
|
437
|
+
except Exception as e:
|
|
438
|
+
raise SimplexError(
|
|
439
|
+
f"Failed to run agent task: {str(e)}",
|
|
440
|
+
status_code=500,
|
|
441
|
+
data={'session_id': session_id, 'task': task}
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
def run_agent(
|
|
445
|
+
self,
|
|
446
|
+
agent_name: str,
|
|
447
|
+
session_id: str,
|
|
448
|
+
variables: Optional[Dict[str, Any]] = None
|
|
449
|
+
) -> RunAgentResponse:
|
|
450
|
+
"""
|
|
451
|
+
Run a named agent within a session.
|
|
452
|
+
|
|
453
|
+
Named agents are pre-configured agents that can be executed by name.
|
|
454
|
+
They encapsulate common workflows or tasks.
|
|
455
|
+
|
|
456
|
+
Args:
|
|
457
|
+
agent_name: Name of the agent to run
|
|
458
|
+
session_id: Session where the agent should run
|
|
459
|
+
variables: Dictionary of variables to pass to the agent (optional)
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
Response containing agent execution results
|
|
463
|
+
|
|
464
|
+
Raises:
|
|
465
|
+
SimplexError: If the agent execution fails
|
|
466
|
+
|
|
467
|
+
Example:
|
|
468
|
+
>>> result = client.workflows.run_agent(
|
|
469
|
+
... "Login Agent",
|
|
470
|
+
... "session-123",
|
|
471
|
+
... variables={"username": "user@example.com", "password": "secret"}
|
|
472
|
+
... )
|
|
473
|
+
>>> if result['succeeded']:
|
|
474
|
+
... print("Agent completed:", result['result'])
|
|
475
|
+
"""
|
|
476
|
+
request_data = {
|
|
477
|
+
'agent_name': agent_name,
|
|
478
|
+
'session_id': session_id
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if variables:
|
|
482
|
+
request_data['variables'] = variables
|
|
483
|
+
|
|
484
|
+
try:
|
|
485
|
+
response = self._http_client.post('/run_agent', data=request_data)
|
|
486
|
+
|
|
487
|
+
if not response.get('succeeded'):
|
|
488
|
+
raise SimplexError(
|
|
489
|
+
f"Agent run failed for {agent_name}",
|
|
490
|
+
status_code=500,
|
|
491
|
+
data={'agent_name': agent_name, 'session_id': session_id}
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
return response
|
|
495
|
+
except SimplexError:
|
|
496
|
+
raise
|
|
497
|
+
except Exception as e:
|
|
498
|
+
raise SimplexError(
|
|
499
|
+
f"Failed to run agent: {str(e)}",
|
|
500
|
+
status_code=500,
|
|
501
|
+
data={'agent_name': agent_name, 'session_id': session_id}
|
|
502
|
+
)
|