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.

@@ -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
+ )