soprano-sdk 0.2.11__py3-none-any.whl → 0.2.12__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.
@@ -20,7 +20,7 @@ class LangGraphAgentAdapter(AgentAdapter):
20
20
  self.agent = agent
21
21
 
22
22
  def invoke(self, messages: List[Dict[str, str]]) -> Dict[str, Any]:
23
- logger.info("Invoking LangGraph agent with messages")
23
+ logger.info("Invoking LangGraphAgentAdapter agent with messages")
24
24
  response = self.agent.invoke({"messages": messages})
25
25
 
26
26
  if structured_response := response.get('structured_response'):
@@ -44,7 +44,7 @@ class CrewAIAgentAdapter(AgentAdapter):
44
44
 
45
45
  def invoke(self, messages: List[Dict[str, str]]) -> Any:
46
46
  try:
47
- logger.info("Invoking LangGraph agent with messages")
47
+ logger.info("Invoking CrewAIAgentAdapter agent with messages")
48
48
  result = self.agent.kickoff(messages, response_format=self.output_schema)
49
49
 
50
50
  if structured_response := getattr(result, 'pydantic', None) :
@@ -65,7 +65,7 @@ class AgnoAgentAdapter(AgentAdapter):
65
65
 
66
66
  def invoke(self, messages: List[Dict[str, str]]) -> Dict[str, Any]:
67
67
  try:
68
- logger.info("Invoking LangGraph agent with messages")
68
+ logger.info("Invoking AgnoAgentAdapter agent with messages")
69
69
  response = self.agent.run(messages)
70
70
  agent_response = response.content if hasattr(response, 'content') else str(response)
71
71
 
@@ -195,6 +195,18 @@ class CollectInputStrategy(ActionStrategy):
195
195
  context_value = self.engine_context.get_context_value(self.field)
196
196
  if context_value is None:
197
197
  return
198
+
199
+ # Check if this node has already executed - if so, don't overwrite the collected value
200
+ execution_order = state.get(WorkflowKeys.NODE_EXECUTION_ORDER, [])
201
+ if self.step_id in execution_order:
202
+ logger.info(f"Skipping context value for '{self.field}' - node '{self.step_id}' already executed")
203
+ span.add_event("context.value_skipped", {
204
+ "field": self.field,
205
+ "reason": "node_already_executed",
206
+ "existing_value": str(state.get(self.field))
207
+ })
208
+ return
209
+
198
210
  logger.info(f"Using context value for '{self.field}': {context_value}")
199
211
  state[self.field] = context_value
200
212
  span.add_event("context.value_used", {"field": self.field, "value": str(context_value)})
@@ -375,7 +387,9 @@ class CollectInputStrategy(ActionStrategy):
375
387
  if self.is_structured_output:
376
388
  try:
377
389
  response_dict = json.loads(agent_response) if isinstance(agent_response, str) else agent_response
378
- prompt = response_dict.get("bot_response", "")
390
+ bot_response = response_dict.get("bot_response", None)
391
+ # Treat empty or whitespace-only bot_response as None
392
+ prompt = bot_response if (bot_response and bot_response.strip()) else None
379
393
  except (json.JSONDecodeError, TypeError, ValueError) as e:
380
394
  logger.error(f"Error When Converting Structured Output {agent_response} to JSON {e}")
381
395
  prompt = agent_response
soprano_sdk/tools.py CHANGED
@@ -83,11 +83,15 @@ class WorkflowTool:
83
83
  callback_handler = CallbackHandler()
84
84
  config = {"configurable": {"thread_id": thread_id}, "callbacks": [callback_handler]}
85
85
 
86
- self.engine.update_context(initial_context)
87
86
  span.add_event("context.updated", {"fields": list(initial_context.keys())})
88
87
 
89
88
  state = self.graph.get_state(config)
90
89
 
90
+ # Update engine context on both resume and fresh start
91
+ # Note: collect_input nodes will check NODE_EXECUTION_ORDER to avoid
92
+ # overwriting already-collected values
93
+ self.engine.update_context(initial_context)
94
+
91
95
  if state.next:
92
96
  span.set_attribute("workflow.resumed", True)
93
97
  logger.info(f"[WorkflowTool] Resuming interrupted workflow {self.name} (thread: {thread_id})")
@@ -95,6 +99,7 @@ class WorkflowTool:
95
99
  Command(resume=user_message or "", update=initial_context),
96
100
  config=config
97
101
  )
102
+
98
103
  else:
99
104
  span.set_attribute("workflow.resumed", False)
100
105
  logger.info(f"[WorkflowTool] Starting fresh workflow {self.name} (thread: {thread_id})")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: soprano-sdk
3
- Version: 0.2.11
3
+ Version: 0.2.12
4
4
  Summary: YAML-driven workflow engine with AI agent integration for building conversational SOPs
5
5
  Author: Arvind Thangamani
6
6
  License: MIT
@@ -29,6 +29,7 @@ Requires-Dist: pyyaml>=6.0
29
29
  Provides-Extra: dev
30
30
  Requires-Dist: gradio>=5.46.0; extra == 'dev'
31
31
  Requires-Dist: pytest>=7.0.0; extra == 'dev'
32
+ Requires-Dist: ruff==0.14.13; extra == 'dev'
32
33
  Provides-Extra: persistence
33
34
  Requires-Dist: langgraph-checkpoint-mongodb>=0.2.0; extra == 'persistence'
34
35
  Requires-Dist: pymongo>=4.0.0; extra == 'persistence'
@@ -1,8 +1,8 @@
1
1
  soprano_sdk/__init__.py,sha256=YZVl_SwQ0C-E_5_f1AwUe_hPcbgCt8k7k4_WAHM8vjE,243
2
2
  soprano_sdk/engine.py,sha256=EFK91iTHjp72otLN6Kg-yeLye2J3CAKN0QH4FI2taL8,14838
3
- soprano_sdk/tools.py,sha256=xsJbY1hZzhXbNRTAVj9M_2saU0oa7J8O9DaRGGuPf30,8832
3
+ soprano_sdk/tools.py,sha256=0zIMCQ0voPGXFHgQCN4jKaMPBXCbnia4Ly5hssPI3oU,9033
4
4
  soprano_sdk/agents/__init__.py,sha256=Yzbtv6iP_ABRgZo0IUjy9vDofEvLFbOjuABw758176A,636
5
- soprano_sdk/agents/adaptor.py,sha256=Cm02YKFclrESu-Qq4CTknCgU7KaA7Z_2FspnQDkEVfU,3214
5
+ soprano_sdk/agents/adaptor.py,sha256=dRk4pU1UgUhsKRotOzILjeXk0Zzoj7GzB-SKyBgKbNs,3242
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/authenticators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -16,7 +16,7 @@ soprano_sdk/nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
16
16
  soprano_sdk/nodes/async_function.py,sha256=v6WujLKm8NXX2iAkJ7Gz_QIVCtWFrpC6nnPyyfuCxXs,9354
17
17
  soprano_sdk/nodes/base.py,sha256=idFyOGGPnjsASYnrOF_NIh7eFcSuJqw61EoVN_WCTaU,2360
18
18
  soprano_sdk/nodes/call_function.py,sha256=afYBmj5Aditbkvb_7gD3CsXBEEUohcsC1_cdHfcOunE,5847
19
- soprano_sdk/nodes/collect_input.py,sha256=Hxf9asv4-5Su5FLewzEajUjtYqzadwETnktAVaTKwZc,24384
19
+ soprano_sdk/nodes/collect_input.py,sha256=IHDzhfh97-AxtaWfRbSFXgaaxnw4BLgumfPt7rbqA0s,25125
20
20
  soprano_sdk/nodes/factory.py,sha256=IbBzT4FKBnYw5PuSo7uDONV3HSFtoyqjBQQtXtUY2IY,1756
21
21
  soprano_sdk/routing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  soprano_sdk/routing/router.py,sha256=Z218r4BMbmlL9282ombutAoKsIs1WHZ2d5YHnbCeet8,3698
@@ -29,7 +29,7 @@ soprano_sdk/utils/tracing.py,sha256=gSHeBDLe-MbAZ9rkzpCoGFveeMdR9KLaA6tteB0IWjk,
29
29
  soprano_sdk/validation/__init__.py,sha256=ImChmO86jYHU90xzTttto2-LmOUOmvY_ibOQaLRz5BA,262
30
30
  soprano_sdk/validation/schema.py,sha256=SlC4sq-ueEg0p_8Uox_cgPj9S-0AEEiOOlA1Vsu0DsE,15443
31
31
  soprano_sdk/validation/validator.py,sha256=GaCvHvjwVe88Z8yatQsueiPnqtq1oo5uN75gogzpQT0,8940
32
- soprano_sdk-0.2.11.dist-info/METADATA,sha256=5YwqTD9lTaDhDwqNWEfH4UXJOPdpXEPZYI12QjC6NHs,11298
33
- soprano_sdk-0.2.11.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
34
- soprano_sdk-0.2.11.dist-info/licenses/LICENSE,sha256=A1aBauSjPNtVehOXJe3WuvdU2xvM9H8XmigFMm6665s,1073
35
- soprano_sdk-0.2.11.dist-info/RECORD,,
32
+ soprano_sdk-0.2.12.dist-info/METADATA,sha256=t7Kq8Jrr6wYLa17TuLFyCBS6ACpX6NL7CXqPf2-QAH8,11343
33
+ soprano_sdk-0.2.12.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
34
+ soprano_sdk-0.2.12.dist-info/licenses/LICENSE,sha256=A1aBauSjPNtVehOXJe3WuvdU2xvM9H8XmigFMm6665s,1073
35
+ soprano_sdk-0.2.12.dist-info/RECORD,,