soprano-sdk 0.1.93__py3-none-any.whl → 0.1.95__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.
@@ -0,0 +1,302 @@
1
+ WORKFLOW_SCHEMA = {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "required": ["name", "description", "version", "data", "steps", "outcomes"],
5
+ "properties": {
6
+ "name": {
7
+ "type": "string",
8
+ "description": "Workflow name"
9
+ },
10
+ "description": {
11
+ "type": "string",
12
+ "description": "Workflow description"
13
+ },
14
+ "version": {
15
+ "type": "string",
16
+ "pattern": "^\\d+\\.\\d+(\\.\\d+)?$",
17
+ "description": "Semantic version (e.g., 1.0.0)"
18
+ },
19
+ "agent_framework": {
20
+ "type": "string",
21
+ "enum": ["langgraph", "crewai", "agno", "pydantic-ai"],
22
+ "default": "langgraph",
23
+ "description": "Agent framework to use for all agents in this workflow (default: langgraph)"
24
+ },
25
+ "data": {
26
+ "type": "array",
27
+ "description": "Data fields used in the workflow",
28
+ "items": {
29
+ "type": "object",
30
+ "required": ["name", "type", "description"],
31
+ "properties": {
32
+ "name": {
33
+ "type": "string",
34
+ "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$",
35
+ "description": "Field name (must be valid Python identifier)"
36
+ },
37
+ "type": {
38
+ "type": "string",
39
+ "enum": ["text", "number", "boolean", "list", "dict", "any", "double"],
40
+ "description": "Field data type"
41
+ },
42
+ "description": {
43
+ "type": "string",
44
+ "description": "Field description"
45
+ },
46
+ "default": {
47
+ "description": "Default value for the field"
48
+ }
49
+ }
50
+ }
51
+ },
52
+ "inputs": {
53
+ "type": "array",
54
+ "description": "Input fields required to start the workflow (list of data field names)",
55
+ "items": {
56
+ "type": "string",
57
+ "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$",
58
+ "description": "Name of a field from the data array"
59
+ }
60
+ },
61
+ "steps": {
62
+ "type": "array",
63
+ "description": "Workflow steps",
64
+ "minItems": 1,
65
+ "items": {
66
+ "type": "object",
67
+ "required": ["id", "action"],
68
+ "properties": {
69
+ "id": {
70
+ "type": "string",
71
+ "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$",
72
+ "description": "Step ID (must be unique and valid Python identifier)"
73
+ },
74
+ "action": {
75
+ "type": "string",
76
+ "enum": ["collect_input_with_agent", "call_function"],
77
+ "description": "Action type"
78
+ },
79
+ "field": {
80
+ "type": "string",
81
+ "description": "Field to collect (for collect_input_with_agent)"
82
+ },
83
+ "validator": {
84
+ "type": "string",
85
+ "description": "Optional data validator"
86
+ },
87
+ "max_attempts": {
88
+ "type": "integer",
89
+ "minimum": 1,
90
+ "maximum": 20,
91
+ "description": "Maximum attempts (for collect_input_with_agent)"
92
+ },
93
+ "agent": {
94
+ "type": "object",
95
+ "description": "Agent configuration (for collect_input_with_agent)",
96
+ "required": ["name", "instructions"],
97
+ "properties": {
98
+ "name": {
99
+ "type": "string"
100
+ },
101
+ "model": {
102
+ "type": "string"
103
+ },
104
+ "description": {
105
+ "type": "string"
106
+ },
107
+ "instructions": {
108
+ "type": "string"
109
+ },
110
+ "tools": {
111
+ "type": "array"
112
+ },
113
+ "base_url": {
114
+ "type": "string",
115
+ "format": "uri"
116
+ },
117
+ "api_key": {
118
+ "type": "string"
119
+ },
120
+ "structured_output": {
121
+ "type": "object",
122
+ "description": "Structured output configuration",
123
+ "required": ["enabled"],
124
+ "properties": {
125
+ "enabled": {
126
+ "type": "boolean",
127
+ "description": "Whether to enable structured output"
128
+ },
129
+ "fields": {
130
+ "type": "array",
131
+ "description": "Field definitions for structured output",
132
+ "items": {
133
+ "type": "object",
134
+ "required": ["name", "type", "description"],
135
+ "properties": {
136
+ "name": {
137
+ "type": "string",
138
+ "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$",
139
+ "description": "Field name (must be valid Python identifier)"
140
+ },
141
+ "type": {
142
+ "type": "string",
143
+ "enum": ["text", "number", "boolean", "list", "dict", "double"],
144
+ "description": "Field data type"
145
+ },
146
+ "description": {
147
+ "type": "string",
148
+ "description": "Field description"
149
+ },
150
+ "required": {
151
+ "type": "boolean",
152
+ "description": "Whether the field is required",
153
+ "default": True
154
+ }
155
+ }
156
+ }
157
+ }
158
+ }
159
+ }
160
+ }
161
+ },
162
+ "function": {
163
+ "type": "string",
164
+ "pattern": "^[a-zA-Z_][a-zA-Z0-9_.]*\\.[a-zA-Z_][a-zA-Z0-9_]*$",
165
+ "description": "Function path (for call_function, format: module.function)"
166
+ },
167
+ "inputs": {
168
+ "type": "object",
169
+ "description": "Input mapping for function"
170
+ },
171
+ "output": {
172
+ "type": "string",
173
+ "description": "Output field name"
174
+ },
175
+ "next": {
176
+ "type": "string",
177
+ "description": "Next step ID (for simple routing)"
178
+ },
179
+ "transitions": {
180
+ "type": "array",
181
+ "description": "Conditional transitions",
182
+ "items": {
183
+ "type": "object",
184
+ "properties": {
185
+ "pattern": {
186
+ "type": "string",
187
+ "description": "Pattern to match in response (for non-structured output)"
188
+ },
189
+ "match": {
190
+ "description": "Value to match against a field (for structured output)"
191
+ },
192
+ "ref": {
193
+ "type": "string",
194
+ "description": "Field name to check when using 'match' (for structured output)"
195
+ },
196
+ "condition": {
197
+ "description": "Condition to evaluate (for call_function)"
198
+ },
199
+ "path": {
200
+ "type": "string",
201
+ "description": "Dot-notation path to the value for evaluation (optional)"
202
+ },
203
+ "next": {
204
+ "type": "string",
205
+ "description": "Next step or outcome ID"
206
+ }
207
+ },
208
+ "oneOf": [
209
+ {"required": ["pattern", "next"]},
210
+ {"required": ["match", "next", "ref"]},
211
+ {"required": ["condition", "next"]}
212
+ ]
213
+ }
214
+ },
215
+ "url": {
216
+ "type": "string",
217
+ "format": "uri",
218
+ "description": "Webhook URL (for webhook action)"
219
+ },
220
+ "method": {
221
+ "type": "string",
222
+ "enum": ["GET", "POST", "PUT", "PATCH", "DELETE"],
223
+ "description": "HTTP method (for webhook action)"
224
+ },
225
+ "headers": {
226
+ "type": "object",
227
+ "description": "HTTP headers (for webhook action)"
228
+ },
229
+ "timeout": {
230
+ "type": "integer",
231
+ "minimum": 1,
232
+ "maximum": 600,
233
+ "description": "Timeout in seconds"
234
+ }
235
+ }
236
+ }
237
+ },
238
+ "outcomes": {
239
+ "type": "array",
240
+ "description": "Workflow outcomes",
241
+ "minItems": 1,
242
+ "items": {
243
+ "type": "object",
244
+ "required": ["id", "type", "message"],
245
+ "properties": {
246
+ "id": {
247
+ "type": "string",
248
+ "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$",
249
+ "description": "Outcome ID (must be unique)"
250
+ },
251
+ "type": {
252
+ "type": "string",
253
+ "enum": ["success", "failure"],
254
+ "description": "Outcome type"
255
+ },
256
+ "message": {
257
+ "type": "string",
258
+ "description": "Outcome message (supports {field} placeholders)"
259
+ }
260
+ }
261
+ }
262
+ },
263
+ "metadata": {
264
+ "type": "object",
265
+ "description": "Additional workflow metadata",
266
+ "properties": {
267
+ "author": {"type": "string"},
268
+ "tags": {"type": "array", "items": {"type": "string"}},
269
+ "documentation": {"type": "string"}
270
+ }
271
+ },
272
+ "tool_config": {
273
+ "type": "object",
274
+ "description": "Tool configuration for the workflow",
275
+ "properties": {
276
+ "tools": {
277
+ "type": "array",
278
+ "description": "List of available tools",
279
+ "items": {
280
+ "type": "object",
281
+ "required": ["name", "description", "callable"],
282
+ "properties": {
283
+ "name": {
284
+ "type": "string",
285
+ "description": "Tool name"
286
+ },
287
+ "description": {
288
+ "type": "string",
289
+ "description": "Tool description"
290
+ },
291
+ "callable": {
292
+ "type": "string",
293
+ "pattern": "^[a-zA-Z_][a-zA-Z0-9_.]*\\.[a-zA-Z_][a-zA-Z0-9_]*$",
294
+ "description": "Callable path (format: module.function)"
295
+ }
296
+ }
297
+ }
298
+ }
299
+ }
300
+ }
301
+ }
302
+ }
@@ -0,0 +1,173 @@
1
+ from typing import List, Set
2
+
3
+ import jsonschema
4
+
5
+ from .schema import WORKFLOW_SCHEMA
6
+
7
+
8
+ class ValidationResult:
9
+ def __init__(self, is_valid: bool, errors: List[str] = None):
10
+ self.is_valid = is_valid
11
+ self.errors = errors or []
12
+
13
+ def __bool__(self):
14
+ return self.is_valid
15
+
16
+ def __str__(self):
17
+ if self.is_valid:
18
+ return "Validation passed"
19
+ return f"Validation failed with {len(self.errors)} error(s):\n" + "\n".join(f" - {e}" for e in self.errors)
20
+
21
+
22
+ class WorkflowValidator:
23
+ def __init__(self, config: dict):
24
+ self.config = config
25
+ self.errors: List[str] = []
26
+
27
+ def validate(self) -> ValidationResult:
28
+ self.errors = []
29
+ self._validate_schema()
30
+ if not self.errors:
31
+ self._validate_step_ids()
32
+ self._validate_outcome_ids()
33
+ self._validate_transitions()
34
+ self._validate_data_fields()
35
+ self._validate_input_fields()
36
+ self._validate_function_references()
37
+
38
+ return ValidationResult(is_valid=len(self.errors) == 0, errors=self.errors)
39
+
40
+ def _validate_schema(self):
41
+ try:
42
+ jsonschema.validate(instance=self.config, schema=WORKFLOW_SCHEMA)
43
+ except jsonschema.ValidationError as e:
44
+ path = " -> ".join(str(p) for p in e.path) if e.path else "root"
45
+ self.errors.append(f"Schema validation error at '{path}': {e.message}")
46
+ except jsonschema.SchemaError as e:
47
+ self.errors.append(f"Invalid schema definition: {e.message}")
48
+
49
+ def _validate_input_fields(self):
50
+ """Validate that all input field names reference valid data fields."""
51
+ input_fields = self.config.get('inputs', [])
52
+ data_fields = set([field.get('name') for field in self.config.get('data', [])])
53
+
54
+ for field_name in input_fields:
55
+ if field_name not in data_fields:
56
+ self.errors.append(f"Input field '{field_name}' is not defined in data fields")
57
+
58
+ def _validate_step_ids(self):
59
+ steps = self.config.get('steps', [])
60
+ step_ids: Set[str] = set()
61
+
62
+ for i, step in enumerate(steps):
63
+ step_id = step.get('id')
64
+ if not step_id:
65
+ self.errors.append(f"Step at index {i} is missing 'id' field")
66
+ continue
67
+
68
+ if step_id in step_ids:
69
+ self.errors.append(f"Duplicate step ID: '{step_id}'")
70
+ step_ids.add(step_id)
71
+
72
+ def _validate_outcome_ids(self):
73
+ outcomes = self.config.get('outcomes', [])
74
+ outcome_ids: Set[str] = set()
75
+
76
+ for i, outcome in enumerate(outcomes):
77
+ outcome_id = outcome.get('id')
78
+ if not outcome_id:
79
+ self.errors.append(f"Outcome at index {i} is missing 'id' field")
80
+ continue
81
+
82
+ if outcome_id in outcome_ids:
83
+ self.errors.append(f"Duplicate outcome ID: '{outcome_id}'")
84
+ outcome_ids.add(outcome_id)
85
+
86
+ def _validate_transitions(self):
87
+ steps = self.config.get('steps', [])
88
+ outcomes = self.config.get('outcomes', [])
89
+
90
+ step_ids = {step.get('id') for step in steps if step.get('id')}
91
+ outcome_ids = {outcome.get('id') for outcome in outcomes if outcome.get('id')}
92
+ valid_targets = step_ids | outcome_ids
93
+
94
+ for step in steps:
95
+ step_id = step.get('id', 'unknown')
96
+
97
+ next_step = step.get('next')
98
+ if next_step and next_step not in valid_targets:
99
+ self.errors.append(
100
+ f"Step '{step_id}' references unknown target in 'next': '{next_step}'"
101
+ )
102
+
103
+ transitions = step.get('transitions', [])
104
+ for i, transition in enumerate(transitions):
105
+ next_target = transition.get('next')
106
+ if next_target and next_target not in valid_targets:
107
+ self.errors.append(
108
+ f"Step '{step_id}' transition {i} references unknown target: '{next_target}'"
109
+ )
110
+
111
+ def _validate_data_fields(self):
112
+ data_fields = {field.get('name') for field in self.config.get('data', [])}
113
+ steps = self.config.get('steps', [])
114
+
115
+ for step in steps:
116
+ step_id = step.get('id', 'unknown')
117
+ action = step.get('action')
118
+
119
+ if action == 'collect_input_with_agent':
120
+ field = step.get('field')
121
+ if not field:
122
+ self.errors.append(f"Step '{step_id}' is missing 'field' property")
123
+ elif field not in data_fields:
124
+ self.errors.append(
125
+ f"Step '{step_id}' references unknown field: '{field}'"
126
+ )
127
+
128
+ agent = step.get('agent')
129
+ if not agent:
130
+ self.errors.append(f"Step '{step_id}' is missing 'agent' configuration")
131
+ elif not agent.get('instructions'):
132
+ self.errors.append(f"Step '{step_id}' agent is missing 'instructions'")
133
+
134
+ elif action == 'call_function':
135
+ output = step.get('output')
136
+ function = step.get('function')
137
+
138
+ if not function:
139
+ self.errors.append(f"Step '{step_id}' is missing 'function' property")
140
+
141
+ if not output:
142
+ self.errors.append(f"Step '{step_id}' is missing 'output' property")
143
+ elif output not in data_fields:
144
+ self.errors.append(
145
+ f"Step '{step_id}' output field '{output}' not defined in data fields"
146
+ )
147
+
148
+ def _validate_function_references(self):
149
+ steps = self.config.get('steps', [])
150
+
151
+ for step in steps:
152
+ if step.get('action') == 'call_function':
153
+ step_id = step.get('id', 'unknown')
154
+ function_path = step.get('function', '')
155
+
156
+ if not function_path:
157
+ continue # Already caught by _validate_data_fields
158
+
159
+ if '.' not in function_path:
160
+ self.errors.append(
161
+ f"Step '{step_id}' function path '{function_path}' is invalid. "
162
+ f"Expected format: 'module.function'"
163
+ )
164
+
165
+
166
+ def validate_workflow(config: dict) -> ValidationResult:
167
+ validator = WorkflowValidator(config)
168
+ result = validator.validate()
169
+
170
+ if not result.is_valid:
171
+ raise RuntimeError(str(result))
172
+
173
+ return result
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: soprano-sdk
3
- Version: 0.1.93
3
+ Version: 0.1.95
4
4
  Summary: YAML-driven workflow engine with AI agent integration for building conversational SOPs
5
5
  Author: Arvind Thangamani
6
6
  License: MIT
@@ -0,0 +1,33 @@
1
+ soprano_sdk/__init__.py,sha256=y3c4i7Q7SAPS2Tee7V0TzWdhgMxBWfDJJ98eqD1HxGI,188
2
+ soprano_sdk/engine.py,sha256=EFK91iTHjp72otLN6Kg-yeLye2J3CAKN0QH4FI2taL8,14838
3
+ soprano_sdk/tools.py,sha256=A0qFEwn208GCYc-_7ZbZKR7N3HG5-6TA3Ma1RPo6YnM,8029
4
+ soprano_sdk/agents/__init__.py,sha256=Yzbtv6iP_ABRgZo0IUjy9vDofEvLFbOjuABw758176A,636
5
+ soprano_sdk/agents/adaptor.py,sha256=IMMgo9_KLI82i1eenOaojw7UE0jjx9vjm8mjfsodKSM,3226
6
+ soprano_sdk/agents/factory.py,sha256=Aucfz4rZVKCXMAQtbGAqp1JR8aYwa66mokRmKkKGhYA,6699
7
+ soprano_sdk/agents/structured_output.py,sha256=LDBWCMJFclOvcFB3OJpu37tO0Ct_M-L2PIH5MCYSjLI,3262
8
+ soprano_sdk/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ soprano_sdk/core/constants.py,sha256=pEwW_NeHhxs7aG457uiBCs65czAapozY6r9JAegc01Y,1451
10
+ soprano_sdk/core/engine.py,sha256=WEyqGaBasGnSqlBAkFSQIZXJb7T6OEsN-DWznIBAzNs,8341
11
+ soprano_sdk/core/rollback_strategies.py,sha256=UZyDBRmbi4kS7D9cGJT_mzSZzfqgbUoeTnOGcW1WOc0,7798
12
+ soprano_sdk/core/state.py,sha256=h0Uo4uCwBAGTWrmzpDbcTwH6lI97-fXU9ek0tc3p2bM,2617
13
+ soprano_sdk/nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ soprano_sdk/nodes/base.py,sha256=H6wvvN3kUeXluZAt5Hf3iocO9NjTElAH4fTb2-1JKr0,2233
15
+ soprano_sdk/nodes/call_function.py,sha256=6M9QywYpitwDsbIbOSGw0YN_lH4kRFWa5UMxIIf393M,4346
16
+ soprano_sdk/nodes/collect_input.py,sha256=LhDsqtlBKK-uIz90QIODDoR5PZbnv4rWyj-hvTR64do,21987
17
+ soprano_sdk/nodes/factory.py,sha256=l-Gysfgnao-o2dphhnbjjxcH3ojZanZNYN3CBH9dDbA,1624
18
+ soprano_sdk/routing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ soprano_sdk/routing/router.py,sha256=SrNciTIXXdC9bAbbO5bX7PN9mlRbITjr4RZdNm4jEVA,3450
20
+ soprano_sdk/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ soprano_sdk/utils/data.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
22
+ soprano_sdk/utils/function.py,sha256=yqkY4MlHOenv-Q3NciiovK1lamyrGQljpy6Q41wviy8,1216
23
+ soprano_sdk/utils/logger.py,sha256=hMYaNHt5syGOXRkglTUKzkgfSbWerix_pHQntcYyep8,157
24
+ soprano_sdk/utils/template.py,sha256=EssXvRDWdyITVWp52weYNIwnqj9ilJaoQotWFIg4rGQ,678
25
+ soprano_sdk/utils/tool.py,sha256=hWN826HIKmLdswLCTURLH8hWlb2WU0MB8nIUErbpB-8,1877
26
+ soprano_sdk/utils/tracing.py,sha256=iSJlTAaiGzgBvZhLISCGAd9_7F2HRzhcIUNHuaFv_Zc,2059
27
+ soprano_sdk/validation/__init__.py,sha256=ImChmO86jYHU90xzTttto2-LmOUOmvY_ibOQaLRz5BA,262
28
+ soprano_sdk/validation/schema.py,sha256=uJJZRDgwzWT2W8amd_W8mUAULvDnHJhiMEl-5so1ZK0,13559
29
+ soprano_sdk/validation/validator.py,sha256=l2P24wiCWBNTZ9-dRbgWwK48BGaR1xIdnBxzSCu0RPM,6498
30
+ soprano_sdk-0.1.95.dist-info/METADATA,sha256=c8u9LFyCMo-p4U3A_2V0zh16dpK4BoRYBcVB0nckXIw,11269
31
+ soprano_sdk-0.1.95.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
32
+ soprano_sdk-0.1.95.dist-info/licenses/LICENSE,sha256=A1aBauSjPNtVehOXJe3WuvdU2xvM9H8XmigFMm6665s,1073
33
+ soprano_sdk-0.1.95.dist-info/RECORD,,
@@ -1,4 +0,0 @@
1
- soprano_sdk-0.1.93.dist-info/METADATA,sha256=TmMcHnMXPkO1WEYt75D3N8I3ev5TtXZJKqDTWj8zRiY,11269
2
- soprano_sdk-0.1.93.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
3
- soprano_sdk-0.1.93.dist-info/licenses/LICENSE,sha256=A1aBauSjPNtVehOXJe3WuvdU2xvM9H8XmigFMm6665s,1073
4
- soprano_sdk-0.1.93.dist-info/RECORD,,