vulcan-core 1.0.0__py3-none-any.whl → 1.1.0__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 +24 -5
- vulcan_core/conditions.py +25 -5
- vulcan_core/models.py +3 -0
- {vulcan_core-1.0.0.dist-info → vulcan_core-1.1.0.dist-info}/METADATA +1 -1
- vulcan_core-1.1.0.dist-info/RECORD +12 -0
- vulcan_core-1.0.0.dist-info/RECORD +0 -12
- {vulcan_core-1.0.0.dist-info → vulcan_core-1.1.0.dist-info}/LICENSE +0 -0
- {vulcan_core-1.0.0.dist-info → vulcan_core-1.1.0.dist-info}/NOTICE +0 -0
- {vulcan_core-1.0.0.dist-info → vulcan_core-1.1.0.dist-info}/WHEEL +0 -0
vulcan_core/ast_utils.py
CHANGED
|
@@ -152,16 +152,35 @@ class ASTProcessor[T: Callable]:
|
|
|
152
152
|
raise ASTProcessingError(msg)
|
|
153
153
|
|
|
154
154
|
# The source includes the entire line of code (e.g., assignment and condition() call)
|
|
155
|
-
# We need to
|
|
156
|
-
# nested parentheses in the lambda's body correctly
|
|
155
|
+
# We need to extract just the lambda expression, handling nested structures correctly
|
|
157
156
|
source = self.source[lambda_start:]
|
|
157
|
+
|
|
158
|
+
# Track depth of various brackets to ensure we don't split inside valid nested structures apart from trailing
|
|
159
|
+
# arguments within the condition() call
|
|
158
160
|
paren_level = 0
|
|
161
|
+
bracket_level = 0
|
|
162
|
+
brace_level = 0
|
|
163
|
+
|
|
159
164
|
for i, char in enumerate(source):
|
|
160
165
|
if char == "(":
|
|
161
166
|
paren_level += 1
|
|
162
|
-
elif char == ")"
|
|
163
|
-
paren_level
|
|
164
|
-
|
|
167
|
+
elif char == ")":
|
|
168
|
+
if paren_level > 0:
|
|
169
|
+
paren_level -= 1
|
|
170
|
+
elif paren_level == 0: # End of expression in a function call
|
|
171
|
+
return source[:i]
|
|
172
|
+
elif char == "[":
|
|
173
|
+
bracket_level += 1
|
|
174
|
+
elif char == "]":
|
|
175
|
+
if bracket_level > 0:
|
|
176
|
+
bracket_level -= 1
|
|
177
|
+
elif char == "{":
|
|
178
|
+
brace_level += 1
|
|
179
|
+
elif char == "}":
|
|
180
|
+
if brace_level > 0:
|
|
181
|
+
brace_level -= 1
|
|
182
|
+
# Only consider comma as a separator when not inside any brackets
|
|
183
|
+
elif char == "," and paren_level == 0 and bracket_level == 0 and brace_level == 0:
|
|
165
184
|
return source[:i]
|
|
166
185
|
|
|
167
186
|
return source
|
vulcan_core/conditions.py
CHANGED
|
@@ -8,11 +8,11 @@ import re
|
|
|
8
8
|
from abc import abstractmethod
|
|
9
9
|
from dataclasses import dataclass, field
|
|
10
10
|
from enum import Enum, auto
|
|
11
|
+
from functools import lru_cache
|
|
11
12
|
from string import Formatter
|
|
12
13
|
from typing import TYPE_CHECKING
|
|
13
14
|
|
|
14
15
|
from langchain.prompts import ChatPromptTemplate
|
|
15
|
-
from langchain_openai import ChatOpenAI
|
|
16
16
|
from pydantic import BaseModel, Field
|
|
17
17
|
|
|
18
18
|
from vulcan_core.actions import ASTProcessor
|
|
@@ -22,6 +22,11 @@ if TYPE_CHECKING: # pragma: no cover - not used at runtime
|
|
|
22
22
|
from langchain_core.language_models import BaseChatModel
|
|
23
23
|
from langchain_core.runnables import RunnableSerializable
|
|
24
24
|
|
|
25
|
+
import importlib.util
|
|
26
|
+
import logging
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
25
30
|
|
|
26
31
|
@dataclass(frozen=True, slots=True)
|
|
27
32
|
class Expression(DeclaresFacts):
|
|
@@ -182,6 +187,7 @@ class LiteralFormatter(Formatter):
|
|
|
182
187
|
@dataclass(frozen=True, slots=True)
|
|
183
188
|
class AICondition(Condition):
|
|
184
189
|
chain: RunnableSerializable
|
|
190
|
+
model: BaseChatModel
|
|
185
191
|
system_template: str
|
|
186
192
|
inquiry_template: str
|
|
187
193
|
func: None = field(init=False, default=None)
|
|
@@ -238,13 +244,23 @@ def ai_condition(model: BaseChatModel, inquiry: str) -> AICondition:
|
|
|
238
244
|
)
|
|
239
245
|
structured_model = model.with_structured_output(BooleanDecision)
|
|
240
246
|
chain = prompt_template | structured_model
|
|
241
|
-
return AICondition(chain=chain, system_template=system, inquiry_template=inquiry, facts=facts)
|
|
247
|
+
return AICondition(chain=chain, model=model, system_template=system, inquiry_template=inquiry, facts=facts)
|
|
242
248
|
|
|
243
249
|
|
|
244
|
-
|
|
250
|
+
@lru_cache(maxsize=1)
|
|
251
|
+
def _detect_default_model() -> BaseChatModel:
|
|
252
|
+
# TODO: Expand this to detect other providers
|
|
253
|
+
if importlib.util.find_spec("langchain_openai"):
|
|
254
|
+
from langchain_openai import ChatOpenAI
|
|
255
|
+
|
|
256
|
+
logger.debug("Using OpenAI as the default LLM model provider.")
|
|
257
|
+
return ChatOpenAI(model="gpt-4o-mini", temperature=0, max_tokens=100) # type: ignore[call-arg] - pyright can't see the args for some reason
|
|
258
|
+
else:
|
|
259
|
+
msg = "Unable to import a default LLM provider. Please install `vulcan_core` with the approriate extras package or specify your custom model explicitly."
|
|
260
|
+
raise ImportError(msg)
|
|
245
261
|
|
|
246
262
|
|
|
247
|
-
def condition(func: ConditionCallable | str) -> Condition:
|
|
263
|
+
def condition(func: ConditionCallable | str, model: BaseChatModel | None = None) -> Condition:
|
|
248
264
|
"""
|
|
249
265
|
Creates a Condition object from a lambda or function. It performs limited static analysis of the code to ensure
|
|
250
266
|
proper usage and discover the facts/attributes accessed by the condition. This allows the rule engine to track
|
|
@@ -284,10 +300,14 @@ def condition(func: ConditionCallable | str) -> Condition:
|
|
|
284
300
|
"""
|
|
285
301
|
|
|
286
302
|
if not isinstance(func, str):
|
|
303
|
+
# Logic condition assumed, ignore kwargs
|
|
287
304
|
processed = ASTProcessor[ConditionCallable](func, condition, bool)
|
|
288
305
|
return Condition(processed.facts, processed.func)
|
|
289
306
|
else:
|
|
290
|
-
|
|
307
|
+
# AI condition assumed
|
|
308
|
+
if not model:
|
|
309
|
+
model = _detect_default_model()
|
|
310
|
+
return ai_condition(model, func)
|
|
291
311
|
|
|
292
312
|
|
|
293
313
|
# TODO: Create a convenience function for creating OnFactChanged conditions
|
vulcan_core/models.py
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
vulcan_core/__init__.py,sha256=pjCnmbMjrsp672WXRQeOV0aSKUEoA_mj0o7q_ouMWs8,1057
|
|
2
|
+
vulcan_core/actions.py,sha256=RO5w5X-drxtDY_mVv0xR2njasWkGPt1AZo9RXsBi8X0,917
|
|
3
|
+
vulcan_core/ast_utils.py,sha256=dIJgiPaozUZ7ggY9_wksSWO0htG1rxaBJzGpWW3CmUU,12576
|
|
4
|
+
vulcan_core/conditions.py,sha256=znFG9eaIib_iA36kOQJFXySisjgexbNqGUYAw3419c0,13198
|
|
5
|
+
vulcan_core/engine.py,sha256=UtrfNkrokZazW_l3_zmwKyackJLON-mpGvnNmZyjBqM,9313
|
|
6
|
+
vulcan_core/models.py,sha256=gFc5SOE_weoNPZo_kV_R95OYdeOp7L57rKlIwSAwqh4,8584
|
|
7
|
+
vulcan_core/util.py,sha256=THlBzIO9zw7JiKGYB8PIan4oRjsGuNaZpx-lgCmUAVI,3419
|
|
8
|
+
vulcan_core-1.1.0.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
|
9
|
+
vulcan_core-1.1.0.dist-info/METADATA,sha256=Hw2a87OIUInw-2wpPdXgXLNtx0Ra3r_82r4qsFFrBaI,4349
|
|
10
|
+
vulcan_core-1.1.0.dist-info/NOTICE,sha256=UN1_Gd_Snmu8T62mxExNUdIF2o7DMdGu8bI8rKqhVnc,244
|
|
11
|
+
vulcan_core-1.1.0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
|
12
|
+
vulcan_core-1.1.0.dist-info/RECORD,,
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
vulcan_core/__init__.py,sha256=pjCnmbMjrsp672WXRQeOV0aSKUEoA_mj0o7q_ouMWs8,1057
|
|
2
|
-
vulcan_core/actions.py,sha256=RO5w5X-drxtDY_mVv0xR2njasWkGPt1AZo9RXsBi8X0,917
|
|
3
|
-
vulcan_core/ast_utils.py,sha256=eNFcgmUaAXfkMjNy8rF2LDHslqj-4l2bqiUns_80fXY,11818
|
|
4
|
-
vulcan_core/conditions.py,sha256=3TuXSd7UNlpwQnPy-5YJ-6zNGNZcW7b4iMGzo3fzBQE,12423
|
|
5
|
-
vulcan_core/engine.py,sha256=UtrfNkrokZazW_l3_zmwKyackJLON-mpGvnNmZyjBqM,9313
|
|
6
|
-
vulcan_core/models.py,sha256=j9uA4MjzemN4BCVvifqAdFCanUguMeafhulr6GiI49Q,8480
|
|
7
|
-
vulcan_core/util.py,sha256=THlBzIO9zw7JiKGYB8PIan4oRjsGuNaZpx-lgCmUAVI,3419
|
|
8
|
-
vulcan_core-1.0.0.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
|
9
|
-
vulcan_core-1.0.0.dist-info/METADATA,sha256=q4hqsr_6-G5pCkPFMeZcpgJYn7iq5vAKDYtax9rx4Dk,4349
|
|
10
|
-
vulcan_core-1.0.0.dist-info/NOTICE,sha256=UN1_Gd_Snmu8T62mxExNUdIF2o7DMdGu8bI8rKqhVnc,244
|
|
11
|
-
vulcan_core-1.0.0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
|
12
|
-
vulcan_core-1.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|