mycorrhizal 0.1.0__py3-none-any.whl → 0.1.2__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.
- mycorrhizal/_version.py +1 -0
- mycorrhizal/hypha/core/builder.py +45 -7
- mycorrhizal/hypha/core/specs.py +19 -3
- mycorrhizal/rhizomorph/core.py +62 -4
- mycorrhizal/spores/__init__.py +69 -16
- mycorrhizal/spores/core.py +786 -75
- mycorrhizal/spores/models.py +22 -0
- mycorrhizal/spores/transport/__init__.py +9 -2
- mycorrhizal/spores/transport/base.py +36 -17
- mycorrhizal/spores/transport/file.py +126 -0
- {mycorrhizal-0.1.0.dist-info → mycorrhizal-0.1.2.dist-info}/METADATA +1 -1
- {mycorrhizal-0.1.0.dist-info → mycorrhizal-0.1.2.dist-info}/RECORD +13 -11
- {mycorrhizal-0.1.0.dist-info → mycorrhizal-0.1.2.dist-info}/WHEEL +0 -0
mycorrhizal/_version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
version = "0.1.2"
|
|
@@ -6,7 +6,7 @@ NetBuilder provides the API for declaratively constructing Petri net specificati
|
|
|
6
6
|
Used within @pn.net decorated functions.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
from typing import Optional, Callable, List, Dict, Type, Any
|
|
9
|
+
from typing import Optional, Callable, List, Dict, Type, Any, Union
|
|
10
10
|
from .specs import (
|
|
11
11
|
NetSpec,
|
|
12
12
|
PlaceSpec,
|
|
@@ -54,14 +54,52 @@ class NetBuilder:
|
|
|
54
54
|
|
|
55
55
|
def place(
|
|
56
56
|
self,
|
|
57
|
-
|
|
57
|
+
name_or_func: Union[str, Callable, None] = None,
|
|
58
58
|
type: PlaceType = PlaceType.BAG,
|
|
59
59
|
state_factory: Optional[Callable] = None,
|
|
60
|
-
) -> PlaceRef:
|
|
61
|
-
"""Declare a regular place
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
) -> Union[PlaceRef, Callable]:
|
|
61
|
+
"""Declare a regular place.
|
|
62
|
+
|
|
63
|
+
Can be used in three ways:
|
|
64
|
+
|
|
65
|
+
1. As a method call (returns PlaceRef for use in arcs):
|
|
66
|
+
queue = builder.place("queue", type=PlaceType.QUEUE)
|
|
67
|
+
|
|
68
|
+
2. As a decorator without parens (infers name from function):
|
|
69
|
+
@builder.place
|
|
70
|
+
def queue(bb):
|
|
71
|
+
return bb.tokens
|
|
72
|
+
|
|
73
|
+
3. As a decorator with custom name:
|
|
74
|
+
@builder.place("custom_name")
|
|
75
|
+
def my_func(bb):
|
|
76
|
+
return bb.tokens
|
|
77
|
+
"""
|
|
78
|
+
# Case 1: Used as decorator without parens - first arg is the function
|
|
79
|
+
if callable(name_or_func):
|
|
80
|
+
func = name_or_func
|
|
81
|
+
place_name = func.__name__
|
|
82
|
+
place_spec = PlaceSpec(place_name, type, handler=func, state_factory=state_factory)
|
|
83
|
+
self.spec.places[place_name] = place_spec
|
|
84
|
+
return PlaceRef(place_name, self.spec)
|
|
85
|
+
|
|
86
|
+
# Case 2 & 3: Used with explicit name (as method call or decorator with args)
|
|
87
|
+
# or called without any args (need to return decorator)
|
|
88
|
+
if isinstance(name_or_func, str):
|
|
89
|
+
name = name_or_func
|
|
90
|
+
place_spec = PlaceSpec(name, type, state_factory=state_factory)
|
|
91
|
+
self.spec.places[name] = place_spec
|
|
92
|
+
# Return PlaceRef - it's callable via __call__ for decorator use
|
|
93
|
+
return PlaceRef(name, self.spec)
|
|
94
|
+
|
|
95
|
+
# Case 4: Called without any args - shouldn't happen but handle gracefully
|
|
96
|
+
# This would be like @builder.place() with empty parens
|
|
97
|
+
def decorator(func: Callable) -> PlaceRef:
|
|
98
|
+
place_name = func.__name__
|
|
99
|
+
place_spec = PlaceSpec(place_name, type, handler=func, state_factory=state_factory)
|
|
100
|
+
self.spec.places[place_name] = place_spec
|
|
101
|
+
return PlaceRef(place_name, self.spec)
|
|
102
|
+
return decorator
|
|
65
103
|
|
|
66
104
|
def io_input_place(self):
|
|
67
105
|
"""Decorator for IOInputPlace with async generator"""
|
mycorrhizal/hypha/core/specs.py
CHANGED
|
@@ -147,11 +147,11 @@ class PlaceRef:
|
|
|
147
147
|
def __init__(self, local_name: str, parent_spec: NetSpec):
|
|
148
148
|
self.local_name = local_name
|
|
149
149
|
self.parent_spec = parent_spec
|
|
150
|
-
|
|
150
|
+
|
|
151
151
|
def get_fqn(self) -> str:
|
|
152
152
|
"""Compute FQN dynamically"""
|
|
153
153
|
return '.'.join(self.get_parts())
|
|
154
|
-
|
|
154
|
+
|
|
155
155
|
# For backward compatibility
|
|
156
156
|
@property
|
|
157
157
|
def fqn(self) -> str:
|
|
@@ -164,7 +164,23 @@ class PlaceRef:
|
|
|
164
164
|
@property
|
|
165
165
|
def parts(self) -> List[str]:
|
|
166
166
|
return self.get_parts()
|
|
167
|
-
|
|
167
|
+
|
|
168
|
+
def __call__(self, func: Callable) -> "PlaceRef":
|
|
169
|
+
"""Allow PlaceRef to be used as a decorator to register a handler.
|
|
170
|
+
|
|
171
|
+
Example:
|
|
172
|
+
@builder.place("queue")
|
|
173
|
+
def queue_handler(bb):
|
|
174
|
+
return bb.tokens
|
|
175
|
+
|
|
176
|
+
The PlaceRef is returned for use in arcs, and the handler is registered.
|
|
177
|
+
"""
|
|
178
|
+
# Get the place spec and register the handler
|
|
179
|
+
place_spec = self.parent_spec.places.get(self.local_name)
|
|
180
|
+
if place_spec is not None:
|
|
181
|
+
place_spec.handler = func
|
|
182
|
+
return self
|
|
183
|
+
|
|
168
184
|
def __repr__(self):
|
|
169
185
|
return f"PlaceRef({self.get_fqn()})"
|
|
170
186
|
|
mycorrhizal/rhizomorph/core.py
CHANGED
|
@@ -1309,9 +1309,36 @@ class _BT:
|
|
|
1309
1309
|
self._tracking_stack[-1].append((fn.__name__, fn))
|
|
1310
1310
|
return fn
|
|
1311
1311
|
|
|
1312
|
-
def sequence(self, *, memory: bool = True):
|
|
1313
|
-
"""Decorator to mark a generator function as a sequence composite.
|
|
1312
|
+
def sequence(self, func_or_none=None, *, memory: bool = True):
|
|
1313
|
+
"""Decorator to mark a generator function as a sequence composite.
|
|
1314
1314
|
|
|
1315
|
+
Can be used with or without parentheses:
|
|
1316
|
+
@bt.sequence
|
|
1317
|
+
def root():
|
|
1318
|
+
...
|
|
1319
|
+
|
|
1320
|
+
@bt.sequence(memory=False)
|
|
1321
|
+
def root():
|
|
1322
|
+
...
|
|
1323
|
+
"""
|
|
1324
|
+
# Case 1: Used as @bt.sequence (no parens)
|
|
1325
|
+
if callable(func_or_none):
|
|
1326
|
+
# Apply decorator directly with defaults
|
|
1327
|
+
spec = NodeSpec(
|
|
1328
|
+
kind=NodeSpecKind.SEQUENCE,
|
|
1329
|
+
name=_name_of(func_or_none),
|
|
1330
|
+
payload={"factory": func_or_none, "memory": memory},
|
|
1331
|
+
)
|
|
1332
|
+
func_or_none.node_spec = spec # type: ignore
|
|
1333
|
+
if self._tracking_stack:
|
|
1334
|
+
self._tracking_stack[-1].append((func_or_none.__name__, func_or_none))
|
|
1335
|
+
return func_or_none
|
|
1336
|
+
|
|
1337
|
+
# Case 2: Used as @bt.sequence() or @bt.sequence(memory=False)
|
|
1338
|
+
return self._sequence_impl(memory=memory)
|
|
1339
|
+
|
|
1340
|
+
def _sequence_impl(self, memory: bool):
|
|
1341
|
+
"""Implementation of sequence decorator."""
|
|
1315
1342
|
def deco(
|
|
1316
1343
|
factory: Callable[..., Generator[Any, None, None]],
|
|
1317
1344
|
) -> Callable[..., Generator[Any, None, None]]:
|
|
@@ -1327,9 +1354,40 @@ class _BT:
|
|
|
1327
1354
|
|
|
1328
1355
|
return deco
|
|
1329
1356
|
|
|
1330
|
-
def selector(self, *, memory: bool = True, reactive: bool = False):
|
|
1331
|
-
"""Decorator to mark a generator function as a selector composite.
|
|
1357
|
+
def selector(self, func_or_none=None, *, memory: bool = True, reactive: bool = False):
|
|
1358
|
+
"""Decorator to mark a generator function as a selector composite.
|
|
1359
|
+
|
|
1360
|
+
Can be used with or without parentheses:
|
|
1361
|
+
@bt.selector
|
|
1362
|
+
def root():
|
|
1363
|
+
...
|
|
1364
|
+
|
|
1365
|
+
@bt.selector(memory=False)
|
|
1366
|
+
def root():
|
|
1367
|
+
...
|
|
1368
|
+
|
|
1369
|
+
@bt.selector(reactive=True)
|
|
1370
|
+
def root():
|
|
1371
|
+
...
|
|
1372
|
+
"""
|
|
1373
|
+
# Case 1: Used as @bt.selector (no parens)
|
|
1374
|
+
if callable(func_or_none):
|
|
1375
|
+
# Apply decorator directly with defaults
|
|
1376
|
+
spec = NodeSpec(
|
|
1377
|
+
kind=NodeSpecKind.SELECTOR,
|
|
1378
|
+
name=_name_of(func_or_none),
|
|
1379
|
+
payload={"factory": func_or_none, "memory": memory, "reactive": reactive},
|
|
1380
|
+
)
|
|
1381
|
+
func_or_none.node_spec = spec # type: ignore
|
|
1382
|
+
if self._tracking_stack:
|
|
1383
|
+
self._tracking_stack[-1].append((func_or_none.__name__, func_or_none))
|
|
1384
|
+
return func_or_none
|
|
1385
|
+
|
|
1386
|
+
# Case 2: Used as @bt.selector() or @bt.selector(memory=False)
|
|
1387
|
+
return self._selector_impl(memory=memory, reactive=reactive)
|
|
1332
1388
|
|
|
1389
|
+
def _selector_impl(self, memory: bool, reactive: bool):
|
|
1390
|
+
"""Implementation of selector decorator."""
|
|
1333
1391
|
def deco(
|
|
1334
1392
|
factory: Callable[..., Generator[Any, None, None]],
|
|
1335
1393
|
) -> Callable[..., Generator[Any, None, None]]:
|
mycorrhizal/spores/__init__.py
CHANGED
|
@@ -6,22 +6,53 @@ OCEL (Object-Centric Event Log) compatible logging system for tracking events
|
|
|
6
6
|
and objects in Mycorrhizal systems. Provides automatic extraction of events
|
|
7
7
|
and objects from DSL execution.
|
|
8
8
|
|
|
9
|
-
Usage:
|
|
10
|
-
from mycorrhizal.spores import configure,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
Usage (Standalone Event and Object Logging):
|
|
10
|
+
from mycorrhizal.spores import configure, get_spore_sync, get_spore_async
|
|
11
|
+
from mycorrhizal.spores.transport import SyncFileTransport, AsyncFileTransport
|
|
12
|
+
from mycorrhizal.spores.models import SporesAttr
|
|
13
|
+
from pydantic import BaseModel
|
|
14
|
+
from typing import Annotated
|
|
15
|
+
|
|
16
|
+
# Mark domain model fields for object attribute logging
|
|
17
|
+
class Order(BaseModel):
|
|
18
|
+
id: str
|
|
19
|
+
status: Annotated[str, SporesAttr]
|
|
20
|
+
total: Annotated[float, SporesAttr]
|
|
21
|
+
|
|
22
|
+
# Sync code
|
|
23
|
+
configure(transport=SyncFileTransport("logs/ocel.jsonl"))
|
|
24
|
+
spore = get_spore_sync(__name__)
|
|
25
|
+
|
|
26
|
+
@spore.log_event(
|
|
27
|
+
event_type="OrderCreated",
|
|
28
|
+
relationships={"order": ("return", "Order")},
|
|
29
|
+
)
|
|
30
|
+
def create_order(customer: Customer, items: list) -> Order:
|
|
31
|
+
return Order(...)
|
|
32
|
+
|
|
33
|
+
# Async code
|
|
34
|
+
configure(transport=AsyncFileTransport("logs/ocel.jsonl"))
|
|
35
|
+
aspore = get_spore_async(__name__)
|
|
36
|
+
|
|
37
|
+
@aspore.log_event(
|
|
38
|
+
event_type="OrderCreated",
|
|
39
|
+
relationships={"order": ("return", "Order")},
|
|
17
40
|
)
|
|
41
|
+
async def create_order_async(customer: Customer, items: list) -> Order:
|
|
42
|
+
return Order(...)
|
|
43
|
+
|
|
44
|
+
Usage (DSL Adapters):
|
|
45
|
+
from mycorrhizal.spores import configure, spore
|
|
46
|
+
from mycorrhizal.spores.models import EventAttr, ObjectRef, ObjectScope
|
|
47
|
+
from pydantic import BaseModel
|
|
48
|
+
from typing import Annotated
|
|
18
49
|
|
|
19
|
-
# Mark
|
|
50
|
+
# Mark blackboard fields for event attribute extraction
|
|
20
51
|
class MissionContext(BaseModel):
|
|
21
52
|
mission_id: Annotated[str, EventAttr]
|
|
22
53
|
robot: Annotated[Robot, ObjectRef(qualifier="actor", scope=ObjectScope.GLOBAL)]
|
|
23
54
|
|
|
24
|
-
# Mark object types
|
|
55
|
+
# Mark object types for logging
|
|
25
56
|
@spore.object(object_type="Robot")
|
|
26
57
|
class Robot(BaseModel):
|
|
27
58
|
id: str
|
|
@@ -32,12 +63,15 @@ DSL Adapters:
|
|
|
32
63
|
RhizomorphAdapter - Log behavior tree node execution with status
|
|
33
64
|
EnokiAdapter - Log state machine execution and lifecycle events
|
|
34
65
|
|
|
66
|
+
Annotation Types:
|
|
67
|
+
EventAttr - Mark blackboard fields for automatic event attribute extraction (DSL adapters)
|
|
68
|
+
SporesAttr - Mark domain model fields for automatic object attribute logging (log_event)
|
|
69
|
+
ObjectRef - Mark blackboard fields as object references with scope
|
|
70
|
+
|
|
35
71
|
Key Concepts:
|
|
36
72
|
Event - Something that happens at a point in time with attributes
|
|
37
73
|
Object - An entity with a type and attributes
|
|
38
74
|
Relationship - Link between events and objects (e.g., "actor", "target")
|
|
39
|
-
EventAttr - Mark blackboard fields for automatic event attribute extraction
|
|
40
|
-
ObjectRef - Mark blackboard fields as object references with scope
|
|
41
75
|
|
|
42
76
|
Object Scopes:
|
|
43
77
|
EVENT - Object exists only for this event (default)
|
|
@@ -48,8 +82,13 @@ from .core import (
|
|
|
48
82
|
configure,
|
|
49
83
|
get_config,
|
|
50
84
|
get_object_cache,
|
|
85
|
+
get_spore_sync,
|
|
86
|
+
get_spore_async,
|
|
51
87
|
spore,
|
|
52
88
|
SporesConfig,
|
|
89
|
+
EventLogger,
|
|
90
|
+
AsyncEventLogger,
|
|
91
|
+
SyncEventLogger,
|
|
53
92
|
)
|
|
54
93
|
|
|
55
94
|
from .models import (
|
|
@@ -62,6 +101,7 @@ from .models import (
|
|
|
62
101
|
ObjectRef,
|
|
63
102
|
ObjectScope,
|
|
64
103
|
EventAttr,
|
|
104
|
+
SporesAttr,
|
|
65
105
|
generate_event_id,
|
|
66
106
|
generate_object_id,
|
|
67
107
|
attribute_value_from_python,
|
|
@@ -71,7 +111,7 @@ from .models import (
|
|
|
71
111
|
from .cache import ObjectLRUCache
|
|
72
112
|
|
|
73
113
|
from .encoder import Encoder, JSONEncoder
|
|
74
|
-
from .transport import Transport
|
|
114
|
+
from .transport import SyncTransport, AsyncTransport, Transport, SyncFileTransport, AsyncFileTransport
|
|
75
115
|
|
|
76
116
|
# DSL adapters
|
|
77
117
|
from .dsl import (
|
|
@@ -81,7 +121,10 @@ from .dsl import (
|
|
|
81
121
|
)
|
|
82
122
|
|
|
83
123
|
|
|
84
|
-
|
|
124
|
+
try:
|
|
125
|
+
from mycorrhizal._version import version as __version__
|
|
126
|
+
except ImportError:
|
|
127
|
+
__version__ = "0.0.0+dev"
|
|
85
128
|
|
|
86
129
|
|
|
87
130
|
__all__ = [
|
|
@@ -89,7 +132,12 @@ __all__ = [
|
|
|
89
132
|
'configure',
|
|
90
133
|
'get_config',
|
|
91
134
|
'get_object_cache',
|
|
92
|
-
'
|
|
135
|
+
'get_spore_sync',
|
|
136
|
+
'get_spore_async',
|
|
137
|
+
'EventLogger',
|
|
138
|
+
'AsyncEventLogger',
|
|
139
|
+
'SyncEventLogger',
|
|
140
|
+
'spore', # For DSL adapters and @spore.object() decorator
|
|
93
141
|
'SporesConfig',
|
|
94
142
|
|
|
95
143
|
# Models
|
|
@@ -102,6 +150,7 @@ __all__ = [
|
|
|
102
150
|
'ObjectRef',
|
|
103
151
|
'ObjectScope',
|
|
104
152
|
'EventAttr',
|
|
153
|
+
'SporesAttr',
|
|
105
154
|
'generate_event_id',
|
|
106
155
|
'generate_object_id',
|
|
107
156
|
'attribute_value_from_python',
|
|
@@ -115,7 +164,11 @@ __all__ = [
|
|
|
115
164
|
'JSONEncoder',
|
|
116
165
|
|
|
117
166
|
# Transport
|
|
118
|
-
'
|
|
167
|
+
'SyncTransport',
|
|
168
|
+
'AsyncTransport',
|
|
169
|
+
'Transport', # Alias for AsyncTransport (backward compatibility)
|
|
170
|
+
'SyncFileTransport',
|
|
171
|
+
'AsyncFileTransport',
|
|
119
172
|
|
|
120
173
|
# DSL Adapters
|
|
121
174
|
'HyphaAdapter',
|