vulcan-core 1.1.2__py3-none-any.whl → 1.1.3__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 vulcan-core might be problematic. Click here for more details.

vulcan_core/ast_utils.py CHANGED
@@ -5,6 +5,7 @@ import ast
5
5
  import inspect
6
6
  import re
7
7
  import textwrap
8
+ import threading
8
9
  from ast import Attribute, Module, Name, NodeTransformer, NodeVisitor
9
10
  from collections.abc import Callable
10
11
  from dataclasses import dataclass, field
@@ -83,10 +84,11 @@ class AttributeTransformer(NodeTransformer):
83
84
  return node
84
85
 
85
86
 
86
- # Global index to cache and track lambda functions position in lambda source lines.
87
+ # Global index to cache and track lambda function positions within the same source lines.
87
88
  # Tuple format: (source code, last processed index)
88
- # TODO: Note in documentation that thread safety is not guaranteed when loading the same ruleset.
89
- # TODO: Consider updating to use a thread-safe structure like threading.local() if needed.
89
+ # TODO: Consider if a redesign is possible to have a single ASTProcessor handle the entire source line, perhaps eagerly
90
+ # processing all lambdas found in the line before the correspondign `condition` call.
91
+ _lambda_index_lock = threading.Lock()
90
92
  lambda_index: dict[Any, tuple[str, int | None]] = {}
91
93
 
92
94
 
@@ -111,21 +113,27 @@ class ASTProcessor[T: Callable]:
111
113
  try:
112
114
  if self.is_lambda:
113
115
  # As of Python 3.12, there is no way to determine to which lambda self.func refers in an
114
- # expression containing multiple lambdas. Therefore we use a global to track the index of each
116
+ # expression containing multiple lambdas. Therefore we use a global dict to track the index of each
115
117
  # lambda function encountered, as the order will correspond to the order of ASTProcessor
116
118
  # invocations for that line. An additional benefit is that we can also use this as a cache to
117
119
  # avoid re-reading the source code for lambda functions sharing the same line.
118
- key = self.func.__code__.co_filename + ":" + str(self.func.__code__.co_firstlineno)
119
-
120
- index = lambda_index.get(key)
121
- if index is None or index[1] is None:
122
- self.source = self._get_lambda_source()
123
- index = (self.source, 0)
124
- lambda_index[key] = index
125
- else:
126
- self.source = index[0]
127
- index = (self.source, index[1] + 1)
128
- lambda_index[key] = index
120
+ #
121
+ # The key for the index is a hash of the stack trace plus line number, which will be
122
+ # unique for each call of a list of lambdas on the same line.
123
+ frames = inspect.stack()[1:] # Exclude current frame
124
+ key = "".join(f"{f.filename}:{f.lineno}" for f in frames)
125
+
126
+ # Use a lock to ensure thread safety when accessing the global lambda index
127
+ with _lambda_index_lock:
128
+ index = lambda_index.get(key)
129
+ if index is None or index[1] is None:
130
+ self.source = self._get_lambda_source()
131
+ index = (self.source, 0)
132
+ lambda_index[key] = index
133
+ else:
134
+ self.source = index[0]
135
+ index = (self.source, index[1] + 1)
136
+ lambda_index[key] = index
129
137
 
130
138
  # Normalize the lambda source and extract the next lambda expression from the last index
131
139
  self.source = self._normalize_lambda_source(self.source, index[1])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: vulcan-core
3
- Version: 1.1.2
3
+ Version: 1.1.3
4
4
  Summary: AI-Hybrid Rules Engine for Logical Reasoning.
5
5
  License: Apache-2.0
6
6
  Keywords: rules,logic,reasoning,ai,artificial intelligence,RAG,LLM
@@ -65,7 +65,7 @@ engine.rule(
65
65
 
66
66
  # Use computed logic for operations that must be correct:
67
67
  engine.rule(
68
- when=condition(Apple.baking && lambda: Inventory.apples < 10),
68
+ when=condition(lambda: Apple.baking and Inventory.apples < 10),
69
69
  then=action(Order(apples=10)),
70
70
  )
71
71
 
@@ -1,12 +1,12 @@
1
1
  vulcan_core/__init__.py,sha256=pjCnmbMjrsp672WXRQeOV0aSKUEoA_mj0o7q_ouMWs8,1057
2
2
  vulcan_core/actions.py,sha256=RO5w5X-drxtDY_mVv0xR2njasWkGPt1AZo9RXsBi8X0,917
3
- vulcan_core/ast_utils.py,sha256=cx_TLfWCfO8eH70H-v3G-SsAo0iGe4WIKSh6Sq9Z78Y,17183
3
+ vulcan_core/ast_utils.py,sha256=PD3IKU4Cz5-BdIBoparIKh-YHJ4ik78FGBy3bXxZkmk,17700
4
4
  vulcan_core/conditions.py,sha256=ZK4plEO2dB7gq0esroEhL29naB_qAsoU4AVSv0rXClk,15670
5
5
  vulcan_core/engine.py,sha256=WjayTDEjKaIEVkkSZyDjdbu1Xy1XIvPewI83l6Sjo9g,9672
6
6
  vulcan_core/models.py,sha256=7um3u-rAy9gg2otTnZFGlKfHJKfsvGEosU3mGq_0jyg,8964
7
7
  vulcan_core/util.py,sha256=Uq5uWhrfWd8fNv6IeeTFZRGeLBAECPZUx63UjbbSMrA,3420
8
- vulcan_core-1.1.2.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
9
- vulcan_core-1.1.2.dist-info/METADATA,sha256=4dWg7LcAq_L3l_JBptWUwKg_GG8kz6QjHgsCJHc1Xq0,4424
10
- vulcan_core-1.1.2.dist-info/NOTICE,sha256=UN1_Gd_Snmu8T62mxExNUdIF2o7DMdGu8bI8rKqhVnc,244
11
- vulcan_core-1.1.2.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
12
- vulcan_core-1.1.2.dist-info/RECORD,,
8
+ vulcan_core-1.1.3.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
9
+ vulcan_core-1.1.3.dist-info/METADATA,sha256=VD7h3KcJn2THORJ0g-IEz3kZdekwq3cDaLqGJpGc_N4,4425
10
+ vulcan_core-1.1.3.dist-info/NOTICE,sha256=UN1_Gd_Snmu8T62mxExNUdIF2o7DMdGu8bI8rKqhVnc,244
11
+ vulcan_core-1.1.3.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
12
+ vulcan_core-1.1.3.dist-info/RECORD,,