flatmachines 1.0.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.
Files changed (41) hide show
  1. flatmachines/__init__.py +136 -0
  2. flatmachines/actions.py +408 -0
  3. flatmachines/adapters/__init__.py +38 -0
  4. flatmachines/adapters/flatagent.py +86 -0
  5. flatmachines/adapters/pi_agent_bridge.py +127 -0
  6. flatmachines/adapters/pi_agent_runner.mjs +99 -0
  7. flatmachines/adapters/smolagents.py +125 -0
  8. flatmachines/agents.py +144 -0
  9. flatmachines/assets/MACHINES.md +141 -0
  10. flatmachines/assets/README.md +11 -0
  11. flatmachines/assets/__init__.py +0 -0
  12. flatmachines/assets/flatagent.d.ts +219 -0
  13. flatmachines/assets/flatagent.schema.json +271 -0
  14. flatmachines/assets/flatagent.slim.d.ts +58 -0
  15. flatmachines/assets/flatagents-runtime.d.ts +523 -0
  16. flatmachines/assets/flatagents-runtime.schema.json +281 -0
  17. flatmachines/assets/flatagents-runtime.slim.d.ts +187 -0
  18. flatmachines/assets/flatmachine.d.ts +403 -0
  19. flatmachines/assets/flatmachine.schema.json +620 -0
  20. flatmachines/assets/flatmachine.slim.d.ts +106 -0
  21. flatmachines/assets/profiles.d.ts +140 -0
  22. flatmachines/assets/profiles.schema.json +93 -0
  23. flatmachines/assets/profiles.slim.d.ts +26 -0
  24. flatmachines/backends.py +222 -0
  25. flatmachines/distributed.py +835 -0
  26. flatmachines/distributed_hooks.py +351 -0
  27. flatmachines/execution.py +638 -0
  28. flatmachines/expressions/__init__.py +60 -0
  29. flatmachines/expressions/cel.py +101 -0
  30. flatmachines/expressions/simple.py +166 -0
  31. flatmachines/flatmachine.py +1263 -0
  32. flatmachines/hooks.py +381 -0
  33. flatmachines/locking.py +69 -0
  34. flatmachines/monitoring.py +505 -0
  35. flatmachines/persistence.py +213 -0
  36. flatmachines/run.py +117 -0
  37. flatmachines/utils.py +166 -0
  38. flatmachines/validation.py +79 -0
  39. flatmachines-1.0.0.dist-info/METADATA +390 -0
  40. flatmachines-1.0.0.dist-info/RECORD +41 -0
  41. flatmachines-1.0.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,101 @@
1
+ """
2
+ CEL expression engine for flatmachines.
3
+
4
+ Wraps cel-python to provide full CEL support including:
5
+ - List macros (all, exists, filter, map)
6
+ - String methods (startsWith, contains, endsWith)
7
+ - Timestamps and durations
8
+ - Type coercion
9
+
10
+ Requires: pip install flatmachines[cel]
11
+ """
12
+
13
+ from typing import Any, Dict
14
+
15
+ try:
16
+ import celpy
17
+ from celpy import celtypes
18
+ CEL_AVAILABLE = True
19
+ except ImportError:
20
+ CEL_AVAILABLE = False
21
+
22
+
23
+ class CELExpressionEngine:
24
+ """
25
+ CEL expression engine using cel-python.
26
+
27
+ Provides full CEL support for advanced expressions.
28
+ """
29
+
30
+ def __init__(self):
31
+ if not CEL_AVAILABLE:
32
+ raise ImportError(
33
+ "CEL expression engine requires cel-python. "
34
+ "Install with: pip install flatmachines[cel]"
35
+ )
36
+ self._env = celpy.Environment()
37
+
38
+ def evaluate(self, expression: str, variables: Dict[str, Any]) -> Any:
39
+ """
40
+ Evaluate a CEL expression with the given variables.
41
+
42
+ Args:
43
+ expression: The CEL expression string to evaluate
44
+ variables: Dictionary of variable names to values
45
+
46
+ Returns:
47
+ The result of evaluating the expression
48
+
49
+ Raises:
50
+ ValueError: If expression syntax is invalid
51
+ """
52
+ if not expression or not expression.strip():
53
+ return True # Empty expression is always true
54
+
55
+ try:
56
+ # Parse the expression
57
+ ast = self._env.compile(expression)
58
+
59
+ # Create the program
60
+ prog = self._env.program(ast)
61
+
62
+ # Convert Python values to CEL types
63
+ cel_vars = self._to_cel_types(variables)
64
+
65
+ # Evaluate
66
+ result = prog.evaluate(cel_vars)
67
+
68
+ # Convert result back to Python
69
+ return self._from_cel_type(result)
70
+
71
+ except Exception as e:
72
+ raise ValueError(f"CEL expression error: {expression} - {e}") from e
73
+
74
+ def _to_cel_types(self, obj: Any) -> Any:
75
+ """Convert Python types to CEL types."""
76
+ if isinstance(obj, dict):
77
+ return {k: self._to_cel_types(v) for k, v in obj.items()}
78
+ if isinstance(obj, list):
79
+ return [self._to_cel_types(v) for v in obj]
80
+ # Primitives pass through
81
+ return obj
82
+
83
+ def _from_cel_type(self, obj: Any) -> Any:
84
+ """Convert CEL types back to Python types."""
85
+ if CEL_AVAILABLE:
86
+ if isinstance(obj, celtypes.BoolType):
87
+ return bool(obj)
88
+ if isinstance(obj, celtypes.IntType):
89
+ return int(obj)
90
+ if isinstance(obj, celtypes.DoubleType):
91
+ return float(obj)
92
+ if isinstance(obj, celtypes.StringType):
93
+ return str(obj)
94
+ if isinstance(obj, celtypes.ListType):
95
+ return [self._from_cel_type(v) for v in obj]
96
+ if isinstance(obj, celtypes.MapType):
97
+ return {k: self._from_cel_type(v) for k, v in obj.items()}
98
+ return obj
99
+
100
+
101
+ __all__ = ["CELExpressionEngine", "CEL_AVAILABLE"]
@@ -0,0 +1,166 @@
1
+ """
2
+ Simple expression engine for flatmachines.
3
+
4
+ Supports:
5
+ - Comparisons: ==, !=, <, <=, >, >=
6
+ - Boolean operators: and, or, not
7
+ - Field access: context.field, input.field, output.field
8
+ - Literals: strings, numbers, booleans, null
9
+
10
+ Examples:
11
+ context.score >= 8
12
+ context.current == context.target
13
+ context.score >= 8 and context.round < 4
14
+ not context.failed
15
+ """
16
+
17
+ import ast
18
+ import operator
19
+ import re
20
+ from typing import Any, Dict, List, Optional, Tuple
21
+
22
+
23
+ class SimpleExpressionEngine:
24
+ """
25
+ Simple expression parser and evaluator.
26
+
27
+ Uses Python's ast module for safe parsing, then evaluates
28
+ with a restricted set of operations.
29
+ """
30
+
31
+ # Supported comparison operators
32
+ COMPARISON_OPS = {
33
+ ast.Eq: operator.eq,
34
+ ast.NotEq: operator.ne,
35
+ ast.Lt: operator.lt,
36
+ ast.LtE: operator.le,
37
+ ast.Gt: operator.gt,
38
+ ast.GtE: operator.ge,
39
+ }
40
+
41
+ # Supported boolean operators
42
+ BOOL_OPS = {
43
+ ast.And: lambda values: all(values),
44
+ ast.Or: lambda values: any(values),
45
+ }
46
+
47
+ def __init__(self):
48
+ pass
49
+
50
+ def evaluate(self, expression: str, variables: Dict[str, Any]) -> Any:
51
+ """
52
+ Evaluate an expression with the given variables.
53
+
54
+ Args:
55
+ expression: The expression string to evaluate
56
+ variables: Dictionary of variable names to values
57
+
58
+ Returns:
59
+ The result of evaluating the expression
60
+
61
+ Raises:
62
+ ValueError: If expression syntax is invalid
63
+ KeyError: If referenced variable doesn't exist
64
+ """
65
+ if not expression or not expression.strip():
66
+ return True # Empty expression is always true (default transition)
67
+
68
+ try:
69
+ tree = ast.parse(expression, mode='eval')
70
+ except SyntaxError as e:
71
+ raise ValueError(f"Invalid expression syntax: {expression}") from e
72
+
73
+ return self._eval_node(tree.body, variables)
74
+
75
+ def _eval_node(self, node: ast.AST, variables: Dict[str, Any]) -> Any:
76
+ """Recursively evaluate an AST node."""
77
+
78
+ # Literals
79
+ if isinstance(node, ast.Constant):
80
+ return node.value
81
+
82
+ # Name (variable reference like 'context', 'input', 'output')
83
+ if isinstance(node, ast.Name):
84
+ name = node.id
85
+ # Handle boolean literals
86
+ if name == 'true' or name == 'True':
87
+ return True
88
+ if name == 'false' or name == 'False':
89
+ return False
90
+ if name == 'null' or name == 'None':
91
+ return None
92
+ if name not in variables:
93
+ raise KeyError(f"Unknown variable: {name}")
94
+ return variables[name]
95
+
96
+ # Attribute access (e.g., context.score, context.nested.field)
97
+ if isinstance(node, ast.Attribute):
98
+ value = self._eval_node(node.value, variables)
99
+ attr = node.attr
100
+ if isinstance(value, dict):
101
+ if attr not in value:
102
+ return None # Missing fields return None
103
+ return value[attr]
104
+ return getattr(value, attr, None)
105
+
106
+ # Comparison (e.g., context.score >= 8)
107
+ if isinstance(node, ast.Compare):
108
+ left = self._eval_node(node.left, variables)
109
+ for op, comparator in zip(node.ops, node.comparators):
110
+ right = self._eval_node(comparator, variables)
111
+ op_func = self.COMPARISON_OPS.get(type(op))
112
+ if op_func is None:
113
+ raise ValueError(f"Unsupported comparison operator: {type(op).__name__}")
114
+ if not op_func(left, right):
115
+ return False
116
+ left = right
117
+ return True
118
+
119
+ # Boolean operators (and, or)
120
+ if isinstance(node, ast.BoolOp):
121
+ op_func = self.BOOL_OPS.get(type(node.op))
122
+ if op_func is None:
123
+ raise ValueError(f"Unsupported boolean operator: {type(node.op).__name__}")
124
+ # Short-circuit evaluation
125
+ if isinstance(node.op, ast.And):
126
+ for value_node in node.values:
127
+ if not self._eval_node(value_node, variables):
128
+ return False
129
+ return True
130
+ else: # Or
131
+ for value_node in node.values:
132
+ if self._eval_node(value_node, variables):
133
+ return True
134
+ return False
135
+
136
+ # Unary operators (not)
137
+ if isinstance(node, ast.UnaryOp):
138
+ operand = self._eval_node(node.operand, variables)
139
+ if isinstance(node.op, ast.Not):
140
+ return not operand
141
+ raise ValueError(f"Unsupported unary operator: {type(node.op).__name__}")
142
+
143
+ # Binary operators (for arithmetic in expressions like context.round + 1)
144
+ if isinstance(node, ast.BinOp):
145
+ left = self._eval_node(node.left, variables)
146
+ right = self._eval_node(node.right, variables)
147
+ if isinstance(node.op, ast.Add):
148
+ return left + right
149
+ if isinstance(node.op, ast.Sub):
150
+ return left - right
151
+ if isinstance(node.op, ast.Mult):
152
+ return left * right
153
+ if isinstance(node.op, ast.Div):
154
+ return left / right
155
+ raise ValueError(f"Unsupported binary operator: {type(node.op).__name__}")
156
+
157
+ # Subscript (e.g., context["key"])
158
+ if isinstance(node, ast.Subscript):
159
+ value = self._eval_node(node.value, variables)
160
+ index = self._eval_node(node.slice, variables)
161
+ return value[index]
162
+
163
+ raise ValueError(f"Unsupported expression type: {type(node).__name__}")
164
+
165
+
166
+ __all__ = ["SimpleExpressionEngine"]