kailash 0.9.6__py3-none-any.whl → 0.9.8__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.
kailash/__init__.py CHANGED
@@ -50,7 +50,7 @@ except ImportError:
50
50
  # For backward compatibility
51
51
  WorkflowGraph = Workflow
52
52
 
53
- __version__ = "0.9.6"
53
+ __version__ = "0.9.8"
54
54
 
55
55
  __all__ = [
56
56
  # Core workflow components
@@ -727,26 +727,38 @@ class IterativeLLMAgentNode(LLMAgentNode):
727
727
  elif isinstance(tool, dict):
728
728
  plan["selected_tools"].append(tool.get("name", "unknown"))
729
729
 
730
- # Create execution steps
730
+ # Create execution steps based on query and available tools
731
731
  if "analyze" in user_query.lower():
732
+ # For analysis queries, create multi-step plan
732
733
  plan["execution_steps"] = [
733
734
  {
734
735
  "step": 1,
735
736
  "action": "gather_data",
736
- "tools": plan["selected_tools"][:1],
737
+ "tools": (
738
+ plan["selected_tools"][:1] if plan["selected_tools"] else []
739
+ ),
737
740
  },
738
741
  {
739
742
  "step": 2,
740
743
  "action": "perform_analysis",
741
- "tools": plan["selected_tools"][1:2],
744
+ "tools": (
745
+ plan["selected_tools"][1:2]
746
+ if len(plan["selected_tools"]) > 1
747
+ else []
748
+ ),
742
749
  },
743
750
  {
744
751
  "step": 3,
745
752
  "action": "generate_insights",
746
- "tools": plan["selected_tools"][2:3],
753
+ "tools": (
754
+ plan["selected_tools"][2:3]
755
+ if len(plan["selected_tools"]) > 2
756
+ else []
757
+ ),
747
758
  },
748
759
  ]
749
760
  else:
761
+ # For other queries, single step execution
750
762
  plan["execution_steps"] = [
751
763
  {"step": 1, "action": "execute_query", "tools": plan["selected_tools"]}
752
764
  ]
@@ -959,11 +971,60 @@ class IterativeLLMAgentNode(LLMAgentNode):
959
971
  self.logger.error(f"Tool execution failed for {tool_name}: {e}")
960
972
 
961
973
  # Combine all tool outputs
962
- step_result["output"] = (
963
- "\n".join(tool_results)
964
- if tool_results
965
- else f"No tools executed for action: {action}"
966
- )
974
+ if tool_results:
975
+ step_result["output"] = "\n".join(tool_results)
976
+ else:
977
+ # No tools were executed - fall back to LLM for this action
978
+ self.logger.info(
979
+ f"No MCP tools available for action: {action}, using LLM fallback"
980
+ )
981
+
982
+ # Extract user query from kwargs
983
+ messages = kwargs.get("messages", [])
984
+ user_query = ""
985
+ for msg in reversed(messages):
986
+ if msg.get("role") == "user":
987
+ user_query = msg.get("content", "")
988
+ break
989
+
990
+ # Create a prompt for the LLM to handle this action
991
+ action_prompt = f"Please {action} for the following request: {user_query}"
992
+ llm_messages = [
993
+ {
994
+ "role": "system",
995
+ "content": kwargs.get(
996
+ "system_prompt", "You are a helpful AI assistant."
997
+ ),
998
+ },
999
+ {"role": "user", "content": action_prompt},
1000
+ ]
1001
+
1002
+ # Use parent's LLM capabilities
1003
+ try:
1004
+ llm_kwargs = {
1005
+ "provider": kwargs.get("provider", "openai"),
1006
+ "model": kwargs.get("model", "gpt-4"),
1007
+ "messages": llm_messages,
1008
+ "temperature": kwargs.get("temperature", 0.7),
1009
+ "max_tokens": kwargs.get("max_tokens", 1000),
1010
+ }
1011
+
1012
+ llm_response = super().run(**llm_kwargs)
1013
+
1014
+ if llm_response.get("success") and llm_response.get("response"):
1015
+ content = llm_response["response"].get("content", "")
1016
+ step_result["output"] = f"LLM Response for {action}: {content}"
1017
+ step_result["success"] = True
1018
+ else:
1019
+ step_result["output"] = (
1020
+ f"Failed to execute {action}: {llm_response.get('error', 'Unknown error')}"
1021
+ )
1022
+ step_result["success"] = False
1023
+ except Exception as e:
1024
+ self.logger.error(f"LLM fallback failed for action {action}: {e}")
1025
+ step_result["output"] = f"Error executing {action}: {str(e)}"
1026
+ step_result["success"] = False
1027
+
967
1028
  step_result["duration"] = time.time() - start_time
968
1029
 
969
1030
  # Mark as failed if no tools executed successfully
kailash/nodes/base.py CHANGED
@@ -216,17 +216,58 @@ class Node(ABC):
216
216
  )
217
217
  self.logger = logging.getLogger(f"kailash.nodes.{self.id}")
218
218
 
219
- # Filter out internal fields from config
220
- internal_fields = {
219
+ # Filter out internal fields from config with comprehensive parameter handling
220
+ # Get parameter definitions once and cache for both filtering and validation
221
+ try:
222
+ if not hasattr(self, "_temp_param_definitions"):
223
+ self._temp_param_definitions = self.get_parameters()
224
+ defined_params = set(self._temp_param_definitions.keys())
225
+ except Exception as e:
226
+ # If get_parameters() fails, log but continue with safe defaults
227
+ self.logger.debug(
228
+ f"Could not get parameter definitions during init: {e}"
229
+ )
230
+ defined_params = set()
231
+ self._temp_param_definitions = {}
232
+
233
+ # Comprehensive parameter filtering: handle ALL potential conflicts
234
+ # Fields that are always internal (never user parameters)
235
+ always_internal = {"metadata"}
236
+
237
+ # Fields that can be either internal or user parameters
238
+ potentially_user_params = {
221
239
  "id",
222
240
  "name",
223
241
  "description",
224
242
  "version",
225
243
  "author",
226
244
  "tags",
227
- "metadata",
228
245
  }
229
- self.config = {k: v for k, v in kwargs.items() if k not in internal_fields}
246
+
247
+ # Build dynamic filter list based on user-defined parameters
248
+ internal_fields = always_internal.copy()
249
+ for field in potentially_user_params:
250
+ if field not in defined_params:
251
+ # Field is not user-defined, so treat as internal field
252
+ internal_fields.add(field)
253
+ # If field IS user-defined, don't add to internal_fields (preserve it)
254
+
255
+ # Also filter any field that starts with metadata prefix or other internal patterns
256
+ # This handles cases like 'metadata_name', 'metadata_*', etc.
257
+ def is_internal_field(field_name: str) -> bool:
258
+ # Check if it's in our explicit internal fields list
259
+ if field_name in internal_fields:
260
+ return True
261
+ # Check for metadata-related field patterns
262
+ if field_name.startswith("metadata_"):
263
+ return True
264
+ # Check for other internal patterns
265
+ if field_name.startswith("_"): # Private fields
266
+ return True
267
+ return False
268
+
269
+ # Apply comprehensive filtering
270
+ self.config = {k: v for k, v in kwargs.items() if not is_internal_field(k)}
230
271
 
231
272
  # Parameter resolution cache - initialize before validation
232
273
  cache_size = int(
@@ -554,11 +595,23 @@ class Node(ABC):
554
595
  return bool(re.match(r"^\$\{[^}]+\}$", value))
555
596
 
556
597
  def _get_cached_parameters(self) -> dict[str, NodeParameter]:
557
- """Get cached parameter definitions.
598
+ """Get cached parameter definitions with optimal performance.
599
+
600
+ Uses parameters cached during initialization to avoid duplicate get_parameters() calls.
558
601
 
559
602
  Returns:
560
603
  Dictionary of parameter definitions, cached for performance
561
604
  """
605
+ # First check if we have parameters cached from initialization
606
+ if hasattr(self, "_temp_param_definitions") and self._temp_param_definitions:
607
+ # Use cached parameters from init and clean up temporary cache
608
+ if self._cached_params is None:
609
+ self._cached_params = self._temp_param_definitions
610
+ # Clean up temporary cache to free memory
611
+ delattr(self, "_temp_param_definitions")
612
+ return self._cached_params
613
+
614
+ # Fallback to original behavior if no cached parameters from init
562
615
  if self._cached_params is None:
563
616
  try:
564
617
  self._cached_params = self.get_parameters()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kailash
3
- Version: 0.9.6
3
+ Version: 0.9.8
4
4
  Summary: Python SDK for the Kailash container-node architecture
5
5
  Home-page: https://github.com/integrum/kailash-python-sdk
6
6
  Author: Integrum
@@ -1,4 +1,4 @@
1
- kailash/__init__.py,sha256=4FAW32FbWRGxkLCykiHb-3-1ri0JkYDO_5s0fkjshX0,2771
1
+ kailash/__init__.py,sha256=X2FdWjwIF5LEYMDpF8b6b_KmtkP63DBf3-RNmu7ytnw,2771
2
2
  kailash/__main__.py,sha256=vr7TVE5o16V6LsTmRFKG6RDKUXHpIWYdZ6Dok2HkHnI,198
3
3
  kailash/access_control.py,sha256=MjKtkoQ2sg1Mgfe7ovGxVwhAbpJKvaepPWr8dxOueMA,26058
4
4
  kailash/access_control_abac.py,sha256=FPfa_8PuDP3AxTjdWfiH3ntwWO8NodA0py9W8SE5dno,30263
@@ -140,7 +140,7 @@ kailash/monitoring/__init__.py,sha256=C5WmkNpk_mmAScqMWiCfkUbjhM5W16dsnRnc3Ial-U
140
140
  kailash/monitoring/alerts.py,sha256=eKX4ooPw1EicumPuswlR_nU18UgRETWvFg8FzCW5pVU,21416
141
141
  kailash/monitoring/metrics.py,sha256=SiAnL3o6K0QaJHgfAuWBa-0pTkW5zymhuPEsj4bgOgM,22022
142
142
  kailash/nodes/__init__.py,sha256=p2KSo0dyUBCLClU123qpQ0tyv5S_36PTxosNyW58nyY,1031
143
- kailash/nodes/base.py,sha256=3KPCp2PDLCPGm4VHSHt8QSONLTX9y3UhQ-3ldQf4oUg,82623
143
+ kailash/nodes/base.py,sha256=GR2E1fWf8j1yMvJic7m2NAih7kjY1NtoDi47hHwoZ40,85437
144
144
  kailash/nodes/base_async.py,sha256=whxepCiVplrltfzEQuabmnGCpEV5WgfqwgxbLdCyiDk,8864
145
145
  kailash/nodes/base_cycle_aware.py,sha256=Xpze9xZzLepWeLpi9Y3tMn1dm2LVv-omr5TSQuGTtWo,13377
146
146
  kailash/nodes/base_with_acl.py,sha256=ZfrkLPgrEBcNbG0LKvtq6glDxyOYOMRw3VXX4vWX6bI,11852
@@ -166,7 +166,7 @@ kailash/nodes/ai/ai_providers.py,sha256=egfiOZzPmZ10d3wBCJ6ST4tRFrrtq0kt1VyCqxVp
166
166
  kailash/nodes/ai/embedding_generator.py,sha256=akGCzz7zLRSziqEQCiPwL2qWhRWxuM_1RQh-YtVEddw,31879
167
167
  kailash/nodes/ai/hybrid_search.py,sha256=k26uDDP_bwrIpv7Yl7PBCPvWSyQEmTlBjI1IpbgDsO4,35446
168
168
  kailash/nodes/ai/intelligent_agent_orchestrator.py,sha256=LvBqMKc64zSxFWVCjbLKKel2QwEzoTeJAEgna7rZw00,83097
169
- kailash/nodes/ai/iterative_llm_agent.py,sha256=Ed5hOuIrUev7hR5XzAfpfNHRjcZkJYSO_aOZsvvQDkI,98619
169
+ kailash/nodes/ai/iterative_llm_agent.py,sha256=G6pQnvSJcMBxloBvLBletFdiIRZGntNaMaVx2no0igY,101273
170
170
  kailash/nodes/ai/llm_agent.py,sha256=NeNJZbV_VOUbULug2LASwyzLyoUO5wi58Bc9sXTubuc,90181
171
171
  kailash/nodes/ai/models.py,sha256=wsEeUTuegy87mnLtKgSTg7ggCXvC1n3MsL-iZ4qujHs,16393
172
172
  kailash/nodes/ai/self_organizing.py,sha256=B7NwKaBW8OHQBf5b0F9bSs8Wm-5BDJ9IjIkxS9h00mg,62885
@@ -403,9 +403,9 @@ kailash/workflow/templates.py,sha256=XQMAKZXC2dlxgMMQhSEOWAF3hIbe9JJt9j_THchhAm8
403
403
  kailash/workflow/type_inference.py,sha256=i1F7Yd_Z3elTXrthsLpqGbOnQBIVVVEjhRpI0HrIjd0,24492
404
404
  kailash/workflow/validation.py,sha256=r2zApGiiG8UEn7p5Ji842l8OR1_KftzDkWc7gg0cac0,44675
405
405
  kailash/workflow/visualization.py,sha256=nHBW-Ai8QBMZtn2Nf3EE1_aiMGi9S6Ui_BfpA5KbJPU,23187
406
- kailash-0.9.6.dist-info/licenses/LICENSE,sha256=Axe6g7bTrJkToK9h9j2SpRUKKNaDZDCo2lQ2zPxCE6s,1065
407
- kailash-0.9.6.dist-info/METADATA,sha256=ITS5v3xASITLatpJV0zFwF24A7gv4afVqPU00beunMQ,22298
408
- kailash-0.9.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
409
- kailash-0.9.6.dist-info/entry_points.txt,sha256=M_q3b8PG5W4XbhSgESzIJjh3_4OBKtZFYFsOdkr2vO4,45
410
- kailash-0.9.6.dist-info/top_level.txt,sha256=z7GzH2mxl66498pVf5HKwo5wwfPtt9Aq95uZUpH6JV0,8
411
- kailash-0.9.6.dist-info/RECORD,,
406
+ kailash-0.9.8.dist-info/licenses/LICENSE,sha256=Axe6g7bTrJkToK9h9j2SpRUKKNaDZDCo2lQ2zPxCE6s,1065
407
+ kailash-0.9.8.dist-info/METADATA,sha256=u5ctxqIA1Wv7Vkj-25IgjKvU33wmvjImTjYku2hyFaY,22298
408
+ kailash-0.9.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
409
+ kailash-0.9.8.dist-info/entry_points.txt,sha256=M_q3b8PG5W4XbhSgESzIJjh3_4OBKtZFYFsOdkr2vO4,45
410
+ kailash-0.9.8.dist-info/top_level.txt,sha256=z7GzH2mxl66498pVf5HKwo5wwfPtt9Aq95uZUpH6JV0,8
411
+ kailash-0.9.8.dist-info/RECORD,,