soprano-sdk 0.1.97__py3-none-any.whl → 0.1.99__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.
- soprano_sdk/agents/adaptor.py +0 -1
- soprano_sdk/core/engine.py +1 -1
- soprano_sdk/core/rollback_strategies.py +0 -1
- soprano_sdk/core/state.py +1 -1
- soprano_sdk/nodes/base.py +0 -1
- soprano_sdk/nodes/call_function.py +8 -4
- soprano_sdk/nodes/collect_input.py +32 -20
- soprano_sdk/tools.py +0 -1
- soprano_sdk/utils/template.py +5 -5
- soprano_sdk/utils/tracing.py +1 -3
- soprano_sdk/validation/schema.py +5 -10
- {soprano_sdk-0.1.97.dist-info → soprano_sdk-0.1.99.dist-info}/METADATA +2 -1
- {soprano_sdk-0.1.97.dist-info → soprano_sdk-0.1.99.dist-info}/RECORD +15 -15
- {soprano_sdk-0.1.97.dist-info → soprano_sdk-0.1.99.dist-info}/WHEEL +0 -0
- {soprano_sdk-0.1.97.dist-info → soprano_sdk-0.1.99.dist-info}/licenses/LICENSE +0 -0
soprano_sdk/agents/adaptor.py
CHANGED
soprano_sdk/core/engine.py
CHANGED
|
@@ -20,7 +20,7 @@ from ..validation import validate_workflow
|
|
|
20
20
|
class WorkflowEngine:
|
|
21
21
|
def __init__(self, yaml_path: str, configs: dict):
|
|
22
22
|
self.yaml_path = yaml_path
|
|
23
|
-
self.configs = configs
|
|
23
|
+
self.configs = configs or {}
|
|
24
24
|
logger.info(f"Loading workflow from: {yaml_path}")
|
|
25
25
|
|
|
26
26
|
try:
|
soprano_sdk/core/state.py
CHANGED
soprano_sdk/nodes/base.py
CHANGED
|
@@ -77,10 +77,14 @@ class CallFunctionStrategy(ActionStrategy):
|
|
|
77
77
|
) -> Dict[str, Any]:
|
|
78
78
|
for transition in self.transitions:
|
|
79
79
|
check_value = result
|
|
80
|
-
if '
|
|
81
|
-
check_value = get_nested_value(result, transition['
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
if 'ref' in transition:
|
|
81
|
+
check_value = get_nested_value(result, transition['ref'])
|
|
82
|
+
|
|
83
|
+
condition = transition['condition']
|
|
84
|
+
if isinstance(condition, list):
|
|
85
|
+
if check_value not in condition:
|
|
86
|
+
continue
|
|
87
|
+
elif check_value != condition:
|
|
84
88
|
continue
|
|
85
89
|
|
|
86
90
|
next_dest = transition['next']
|
|
@@ -230,18 +230,16 @@ class CollectInputStrategy(ActionStrategy):
|
|
|
230
230
|
def _validate_collected_input(self, state) -> Tuple[bool, Optional[str]]:
|
|
231
231
|
if not self.validator:
|
|
232
232
|
return True, None
|
|
233
|
-
|
|
234
|
-
if isinstance(result, tuple):
|
|
235
|
-
return result
|
|
236
|
-
return result, None
|
|
233
|
+
return self.validator(**state)
|
|
237
234
|
|
|
238
235
|
def _handle_pre_populated_field(self, state: Dict[str, Any], conversation: List) -> Dict[str, Any]:
|
|
239
236
|
logger.info(f"Field '{self.field}' is populated, skipping collection")
|
|
240
237
|
|
|
241
|
-
is_valid_input,
|
|
238
|
+
is_valid_input, validator_error_message = self._validate_collected_input(state)
|
|
242
239
|
if not is_valid_input:
|
|
243
240
|
self._set_status(state, "collecting")
|
|
244
|
-
|
|
241
|
+
conversation.append({"role": "user", "content": f"{state[self.field]}"})
|
|
242
|
+
return self._handle_validation_failure(state, conversation, message=validator_error_message)
|
|
245
243
|
|
|
246
244
|
if self.transitions:
|
|
247
245
|
first_transition = self.transitions[0]
|
|
@@ -351,16 +349,18 @@ class CollectInputStrategy(ActionStrategy):
|
|
|
351
349
|
conversation: List[Dict[str, str]],
|
|
352
350
|
state: Dict[str, Any]
|
|
353
351
|
) -> str:
|
|
354
|
-
|
|
355
|
-
if not (prompt := self.agent_config.get('initial_message')):
|
|
356
|
-
prompt = agent.invoke([{"role": "user", "content": ""}])
|
|
352
|
+
last_assistant_message = next((msg['content'] for msg in reversed(conversation) if msg['role'] == 'assistant'), None)
|
|
357
353
|
|
|
358
|
-
|
|
359
|
-
|
|
354
|
+
if last_assistant_message is not None:
|
|
355
|
+
return last_assistant_message
|
|
360
356
|
|
|
361
|
-
|
|
357
|
+
if not (prompt := self.agent_config.get('initial_message')):
|
|
358
|
+
prompt = agent.invoke([{"role": "user", "content": ""}])
|
|
362
359
|
|
|
363
|
-
|
|
360
|
+
prompt = self._render_template_string(prompt, state)
|
|
361
|
+
conversation.append({"role": "assistant", "content": prompt})
|
|
362
|
+
|
|
363
|
+
return prompt
|
|
364
364
|
|
|
365
365
|
def _update_conversation(self, state: Dict[str, Any], conversation: List[Dict[str, str]]):
|
|
366
366
|
state[WorkflowKeys.CONVERSATIONS][self._conversation_key] = conversation
|
|
@@ -421,8 +421,17 @@ class CollectInputStrategy(ActionStrategy):
|
|
|
421
421
|
self._set_status(state, 'collecting')
|
|
422
422
|
|
|
423
423
|
for transition in self.transitions:
|
|
424
|
-
|
|
425
|
-
if
|
|
424
|
+
patterns = transition['pattern']
|
|
425
|
+
if isinstance(patterns, str):
|
|
426
|
+
patterns = [patterns]
|
|
427
|
+
|
|
428
|
+
matched_pattern = None
|
|
429
|
+
for pattern in patterns:
|
|
430
|
+
if pattern in agent_response:
|
|
431
|
+
matched_pattern = pattern
|
|
432
|
+
break
|
|
433
|
+
|
|
434
|
+
if not matched_pattern:
|
|
426
435
|
continue
|
|
427
436
|
|
|
428
437
|
matched = True
|
|
@@ -430,12 +439,12 @@ class CollectInputStrategy(ActionStrategy):
|
|
|
430
439
|
|
|
431
440
|
logger.info(f"Matched transition: {transition}")
|
|
432
441
|
|
|
433
|
-
value = agent_response.split(
|
|
442
|
+
value = agent_response.split(matched_pattern)[1].strip()
|
|
434
443
|
if value:
|
|
435
444
|
self._store_field_value(state, value)
|
|
436
|
-
is_valid_input,
|
|
445
|
+
is_valid_input, validation_error_message = self._validate_collected_input(state)
|
|
437
446
|
if not is_valid_input:
|
|
438
|
-
return self._handle_validation_failure(state, conversation, message=
|
|
447
|
+
return self._handle_validation_failure(state, conversation, message=validation_error_message)
|
|
439
448
|
state[WorkflowKeys.MESSAGES] = [f"✓ {self._formatted_field_name} collected: {value}" ]
|
|
440
449
|
else:
|
|
441
450
|
state[WorkflowKeys.MESSAGES] = []
|
|
@@ -493,7 +502,10 @@ class CollectInputStrategy(ActionStrategy):
|
|
|
493
502
|
raise RuntimeError(f"Transition in step '{self.step_id}' missing required properties for structured output routing")
|
|
494
503
|
|
|
495
504
|
field_value = agent_response.get(ref_field)
|
|
496
|
-
if
|
|
505
|
+
if isinstance(match_value, list):
|
|
506
|
+
if field_value in match_value:
|
|
507
|
+
return next_node
|
|
508
|
+
elif field_value == match_value:
|
|
497
509
|
return next_node
|
|
498
510
|
|
|
499
511
|
return None
|
|
@@ -524,7 +536,7 @@ class CollectInputStrategy(ActionStrategy):
|
|
|
524
536
|
if field_def.get('type') == 'number':
|
|
525
537
|
try:
|
|
526
538
|
state[self.field] = int(value)
|
|
527
|
-
except ValueError:
|
|
539
|
+
except (ValueError, TypeError):
|
|
528
540
|
state[self.field] = value
|
|
529
541
|
else:
|
|
530
542
|
state[self.field] = value
|
soprano_sdk/tools.py
CHANGED
soprano_sdk/utils/template.py
CHANGED
|
@@ -7,14 +7,14 @@ def get_nested_value(data: Any, path: str) -> Any:
|
|
|
7
7
|
if not path:
|
|
8
8
|
return data
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
template_str = f'{{{{ {path} }}}}'
|
|
12
|
-
else:
|
|
13
|
-
template_str = path
|
|
10
|
+
template_str = f"{{{{ {path} }}}}"
|
|
14
11
|
|
|
15
12
|
try:
|
|
16
13
|
template = Template(template_str)
|
|
17
|
-
|
|
14
|
+
if isinstance(data, dict):
|
|
15
|
+
result = template.render(**data)
|
|
16
|
+
else:
|
|
17
|
+
result = template.render(data)
|
|
18
18
|
|
|
19
19
|
if not result or result == '':
|
|
20
20
|
return None
|
soprano_sdk/utils/tracing.py
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
from opentelemetry import trace
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Any
|
|
3
3
|
from contextlib import contextmanager
|
|
4
|
-
import logging
|
|
5
4
|
|
|
6
5
|
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
|
|
7
|
-
from opentelemetry.trace import Link
|
|
8
6
|
|
|
9
7
|
from ..utils.logger import logger
|
|
10
8
|
|
soprano_sdk/validation/schema.py
CHANGED
|
@@ -164,10 +164,6 @@ WORKFLOW_SCHEMA = {
|
|
|
164
164
|
"pattern": "^[a-zA-Z_][a-zA-Z0-9_.]*\\.[a-zA-Z_][a-zA-Z0-9_]*$",
|
|
165
165
|
"description": "Function path (for call_function, format: module.function)"
|
|
166
166
|
},
|
|
167
|
-
"inputs": {
|
|
168
|
-
"type": "object",
|
|
169
|
-
"description": "Input mapping for function"
|
|
170
|
-
},
|
|
171
167
|
"output": {
|
|
172
168
|
"type": "string",
|
|
173
169
|
"description": "Output field name"
|
|
@@ -183,8 +179,11 @@ WORKFLOW_SCHEMA = {
|
|
|
183
179
|
"type": "object",
|
|
184
180
|
"properties": {
|
|
185
181
|
"pattern": {
|
|
186
|
-
"
|
|
187
|
-
|
|
182
|
+
"anyOf": [
|
|
183
|
+
{"type": "string"},
|
|
184
|
+
{"type": "array", "items": {"type": "string"}}
|
|
185
|
+
],
|
|
186
|
+
"description": "Pattern(s) to match in response (for non-structured output)"
|
|
188
187
|
},
|
|
189
188
|
"match": {
|
|
190
189
|
"description": "Value to match against a field (for structured output)"
|
|
@@ -196,10 +195,6 @@ WORKFLOW_SCHEMA = {
|
|
|
196
195
|
"condition": {
|
|
197
196
|
"description": "Condition to evaluate (for call_function)"
|
|
198
197
|
},
|
|
199
|
-
"path": {
|
|
200
|
-
"type": "string",
|
|
201
|
-
"description": "Dot-notation path to the value for evaluation (optional)"
|
|
202
|
-
},
|
|
203
198
|
"next": {
|
|
204
199
|
"type": "string",
|
|
205
200
|
"description": "Next step or outcome ID"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: soprano-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.99
|
|
4
4
|
Summary: YAML-driven workflow engine with AI agent integration for building conversational SOPs
|
|
5
5
|
Author: Arvind Thangamani
|
|
6
6
|
License: MIT
|
|
@@ -24,6 +24,7 @@ Requires-Dist: langgraph==1.0.2
|
|
|
24
24
|
Requires-Dist: openai>=1.92.1
|
|
25
25
|
Requires-Dist: pydantic-ai>=1.22.0
|
|
26
26
|
Requires-Dist: pydantic>=2.0.0
|
|
27
|
+
Requires-Dist: pytest>=9.0.1
|
|
27
28
|
Requires-Dist: pyyaml>=6.0
|
|
28
29
|
Provides-Extra: dev
|
|
29
30
|
Requires-Dist: gradio>=5.46.0; extra == 'dev'
|
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
soprano_sdk/__init__.py,sha256=y3c4i7Q7SAPS2Tee7V0TzWdhgMxBWfDJJ98eqD1HxGI,188
|
|
2
2
|
soprano_sdk/engine.py,sha256=EFK91iTHjp72otLN6Kg-yeLye2J3CAKN0QH4FI2taL8,14838
|
|
3
|
-
soprano_sdk/tools.py,sha256=
|
|
3
|
+
soprano_sdk/tools.py,sha256=cYEto0cmQ5GZ4zsH_YPfbaaDbzl5aO3rAOm7KlzTFVk,7444
|
|
4
4
|
soprano_sdk/agents/__init__.py,sha256=Yzbtv6iP_ABRgZo0IUjy9vDofEvLFbOjuABw758176A,636
|
|
5
|
-
soprano_sdk/agents/adaptor.py,sha256=
|
|
5
|
+
soprano_sdk/agents/adaptor.py,sha256=Cm02YKFclrESu-Qq4CTknCgU7KaA7Z_2FspnQDkEVfU,3214
|
|
6
6
|
soprano_sdk/agents/factory.py,sha256=Aucfz4rZVKCXMAQtbGAqp1JR8aYwa66mokRmKkKGhYA,6699
|
|
7
7
|
soprano_sdk/agents/structured_output.py,sha256=7DSVzfMPsZAqBwI3v6XL15qG5Gh4jJ-qddcVPaa3gdc,3326
|
|
8
8
|
soprano_sdk/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
soprano_sdk/core/constants.py,sha256=pEwW_NeHhxs7aG457uiBCs65czAapozY6r9JAegc01Y,1451
|
|
10
|
-
soprano_sdk/core/engine.py,sha256=
|
|
11
|
-
soprano_sdk/core/rollback_strategies.py,sha256=
|
|
12
|
-
soprano_sdk/core/state.py,sha256=
|
|
10
|
+
soprano_sdk/core/engine.py,sha256=aN805CtG7TDBkBbIJSbAbLo_5tKqrfve394L_DzZq8s,8347
|
|
11
|
+
soprano_sdk/core/rollback_strategies.py,sha256=NjDTtBCZlqyDql5PSwI9SMDLK7_BNlTxbW_cq_5gV0g,7783
|
|
12
|
+
soprano_sdk/core/state.py,sha256=ICyFhio1VESQxzYNwKrS-gk3QDc0OrwJLQtVnZ9c9LU,2611
|
|
13
13
|
soprano_sdk/nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
soprano_sdk/nodes/base.py,sha256=
|
|
15
|
-
soprano_sdk/nodes/call_function.py,sha256=
|
|
16
|
-
soprano_sdk/nodes/collect_input.py,sha256=
|
|
14
|
+
soprano_sdk/nodes/base.py,sha256=bD8P_SLW65yQ1QaCQ5aN8eQCe50sdHh4wdcQzdY6wGA,2218
|
|
15
|
+
soprano_sdk/nodes/call_function.py,sha256=23Q9wUY1F9dLMW2qSAApA8Sw5SyvtS70RBCMhZxvRu0,4506
|
|
16
|
+
soprano_sdk/nodes/collect_input.py,sha256=PlcsD9EtNtMtfnihaJSVjW9RC7ztpaV2ETaF6ZbKNbY,22840
|
|
17
17
|
soprano_sdk/nodes/factory.py,sha256=l-Gysfgnao-o2dphhnbjjxcH3ojZanZNYN3CBH9dDbA,1624
|
|
18
18
|
soprano_sdk/routing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
19
|
soprano_sdk/routing/router.py,sha256=SrNciTIXXdC9bAbbO5bX7PN9mlRbITjr4RZdNm4jEVA,3450
|
|
20
20
|
soprano_sdk/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
21
|
soprano_sdk/utils/function.py,sha256=yqkY4MlHOenv-Q3NciiovK1lamyrGQljpy6Q41wviy8,1216
|
|
22
22
|
soprano_sdk/utils/logger.py,sha256=hMYaNHt5syGOXRkglTUKzkgfSbWerix_pHQntcYyep8,157
|
|
23
|
-
soprano_sdk/utils/template.py,sha256=
|
|
23
|
+
soprano_sdk/utils/template.py,sha256=MG_B9TMx1ShpnSGo7s7TO-VfQzuFByuRNhJTvZ668kM,685
|
|
24
24
|
soprano_sdk/utils/tool.py,sha256=hWN826HIKmLdswLCTURLH8hWlb2WU0MB8nIUErbpB-8,1877
|
|
25
|
-
soprano_sdk/utils/tracing.py,sha256=
|
|
25
|
+
soprano_sdk/utils/tracing.py,sha256=gSHeBDLe-MbAZ9rkzpCoGFveeMdR9KLaA6tteB0IWjk,1991
|
|
26
26
|
soprano_sdk/validation/__init__.py,sha256=ImChmO86jYHU90xzTttto2-LmOUOmvY_ibOQaLRz5BA,262
|
|
27
|
-
soprano_sdk/validation/schema.py,sha256=
|
|
27
|
+
soprano_sdk/validation/schema.py,sha256=HzqRp-5rl3GzX5KtFckAbBh8S7zklvQmRERIyfByS2w,13335
|
|
28
28
|
soprano_sdk/validation/validator.py,sha256=l2P24wiCWBNTZ9-dRbgWwK48BGaR1xIdnBxzSCu0RPM,6498
|
|
29
|
-
soprano_sdk-0.1.
|
|
30
|
-
soprano_sdk-0.1.
|
|
31
|
-
soprano_sdk-0.1.
|
|
32
|
-
soprano_sdk-0.1.
|
|
29
|
+
soprano_sdk-0.1.99.dist-info/METADATA,sha256=Jo1IR-exqqTG8MYbC-X-ymu4skcCPPgmJ6Z-SZwXFlE,11298
|
|
30
|
+
soprano_sdk-0.1.99.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
31
|
+
soprano_sdk-0.1.99.dist-info/licenses/LICENSE,sha256=A1aBauSjPNtVehOXJe3WuvdU2xvM9H8XmigFMm6665s,1073
|
|
32
|
+
soprano_sdk-0.1.99.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|