krons 0.2.1__py3-none-any.whl → 0.2.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.
- krons/core/__init__.py +3 -0
- krons/core/base/flow.py +7 -0
- krons/session/session.py +121 -3
- {krons-0.2.1.dist-info → krons-0.2.3.dist-info}/METADATA +1 -1
- {krons-0.2.1.dist-info → krons-0.2.3.dist-info}/RECORD +7 -7
- {krons-0.2.1.dist-info → krons-0.2.3.dist-info}/WHEEL +0 -0
- {krons-0.2.1.dist-info → krons-0.2.3.dist-info}/licenses/LICENSE +0 -0
krons/core/__init__.py
CHANGED
|
@@ -21,6 +21,7 @@ _LAZY_IMPORTS: dict[str, tuple[str, str]] = {
|
|
|
21
21
|
"PERSISTABLE_NODE_REGISTRY": ("krons.core.base", "PERSISTABLE_NODE_REGISTRY"),
|
|
22
22
|
# Classes
|
|
23
23
|
"Broadcaster": ("krons.core.base", "Broadcaster"),
|
|
24
|
+
"DataLoggerConfig": ("krons.core.base", "DataLoggerConfig"),
|
|
24
25
|
"Edge": ("krons.core.base", "Edge"),
|
|
25
26
|
"EdgeCondition": ("krons.core.base", "EdgeCondition"),
|
|
26
27
|
"Element": ("krons.core.base", "Element"),
|
|
@@ -75,6 +76,7 @@ if TYPE_CHECKING:
|
|
|
75
76
|
NODE_REGISTRY,
|
|
76
77
|
PERSISTABLE_NODE_REGISTRY,
|
|
77
78
|
Broadcaster,
|
|
79
|
+
DataLoggerConfig,
|
|
78
80
|
Edge,
|
|
79
81
|
EdgeCondition,
|
|
80
82
|
Element,
|
|
@@ -103,6 +105,7 @@ __all__ = [
|
|
|
103
105
|
"PERSISTABLE_NODE_REGISTRY",
|
|
104
106
|
# classes
|
|
105
107
|
"Broadcaster",
|
|
108
|
+
"DataLoggerConfig",
|
|
106
109
|
"Edge",
|
|
107
110
|
"EdgeCondition",
|
|
108
111
|
"Element",
|
krons/core/base/flow.py
CHANGED
|
@@ -321,6 +321,13 @@ class Flow(Element, Generic[E, P]):
|
|
|
321
321
|
|
|
322
322
|
return self.items.remove(uid)
|
|
323
323
|
|
|
324
|
+
@synchronized
|
|
325
|
+
def clear(self) -> None:
|
|
326
|
+
"""Clear all items and progressions."""
|
|
327
|
+
self.items.clear()
|
|
328
|
+
self.progressions.clear()
|
|
329
|
+
self._progression_names.clear()
|
|
330
|
+
|
|
324
331
|
def __repr__(self) -> str:
|
|
325
332
|
name_str = f", name='{self.name}'" if self.name else ""
|
|
326
333
|
return f"Flow(items={len(self.items)}, progressions={len(self.progressions)}{name_str})"
|
krons/session/session.py
CHANGED
|
@@ -9,12 +9,14 @@ Branch is a named message progression with capability/resource access control.
|
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
+
import atexit
|
|
12
13
|
import contextlib
|
|
13
14
|
from collections.abc import AsyncGenerator, Iterable
|
|
15
|
+
from pathlib import Path
|
|
14
16
|
from typing import Any, Literal
|
|
15
17
|
from uuid import UUID
|
|
16
18
|
|
|
17
|
-
from pydantic import Field, model_validator
|
|
19
|
+
from pydantic import Field, PrivateAttr, field_serializer, model_validator
|
|
18
20
|
|
|
19
21
|
from krons.core import Element, Flow, Pile, Progression
|
|
20
22
|
from krons.core.types import HashableModel, Unset, UnsetType, not_sentinel
|
|
@@ -60,17 +62,40 @@ class SessionConfig(HashableModel):
|
|
|
60
62
|
default_parse_model: str | None = None
|
|
61
63
|
auto_create_default_branch: bool = True
|
|
62
64
|
|
|
65
|
+
# Logging configuration
|
|
66
|
+
log_persist_dir: str | Path | None = Field(
|
|
67
|
+
default=None,
|
|
68
|
+
description="Directory for session dumps. None disables logging.",
|
|
69
|
+
)
|
|
70
|
+
log_auto_save_on_exit: bool = Field(
|
|
71
|
+
default=True,
|
|
72
|
+
description="Register atexit handler on Session creation.",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def logging_enabled(self) -> bool:
|
|
77
|
+
"""True if logging is configured (log_persist_dir is set)."""
|
|
78
|
+
return self.log_persist_dir is not None
|
|
79
|
+
|
|
63
80
|
|
|
64
81
|
class Session(Element):
|
|
65
82
|
user: str | None = None
|
|
66
83
|
communications: Flow[Message, Branch] = Field(
|
|
67
84
|
default_factory=lambda: Flow(item_type=Message)
|
|
68
85
|
)
|
|
69
|
-
resources: ResourceRegistry = Field(default_factory=ResourceRegistry)
|
|
70
|
-
operations: OperationRegistry = Field(default_factory=OperationRegistry)
|
|
86
|
+
resources: ResourceRegistry = Field(default_factory=ResourceRegistry, exclude=True)
|
|
87
|
+
operations: OperationRegistry = Field(default_factory=OperationRegistry, exclude=True)
|
|
71
88
|
config: SessionConfig = Field(default_factory=SessionConfig)
|
|
72
89
|
default_branch_id: UUID | None = None
|
|
73
90
|
|
|
91
|
+
_registered_atexit: bool = PrivateAttr(default=False)
|
|
92
|
+
_dump_count: int = PrivateAttr(default=0)
|
|
93
|
+
|
|
94
|
+
@field_serializer("communications")
|
|
95
|
+
def _serialize_communications(self, flow: Flow) -> dict:
|
|
96
|
+
"""Use Flow's custom to_dict for proper nested serialization."""
|
|
97
|
+
return flow.to_dict(mode="json")
|
|
98
|
+
|
|
74
99
|
@model_validator(mode="after")
|
|
75
100
|
def _validate_default_branch(self) -> Session:
|
|
76
101
|
"""Auto-create default branch and register built-in operations."""
|
|
@@ -83,6 +108,15 @@ class Session(Element):
|
|
|
83
108
|
)
|
|
84
109
|
self.set_default_branch(default_branch_name)
|
|
85
110
|
|
|
111
|
+
# Register atexit handler if configured
|
|
112
|
+
if (
|
|
113
|
+
self.config.logging_enabled
|
|
114
|
+
and self.config.log_auto_save_on_exit
|
|
115
|
+
and not self._registered_atexit
|
|
116
|
+
):
|
|
117
|
+
atexit.register(self._save_at_exit)
|
|
118
|
+
self._registered_atexit = True
|
|
119
|
+
|
|
86
120
|
# Register built-in operations (lazy import avoids circular)
|
|
87
121
|
from krons.agent.operations import (
|
|
88
122
|
generate,
|
|
@@ -386,6 +420,90 @@ class Session(Element):
|
|
|
386
420
|
async for result in handler(params, ctx):
|
|
387
421
|
yield result
|
|
388
422
|
|
|
423
|
+
def dump(self, clear: bool = False) -> Path | None:
|
|
424
|
+
"""Sync dump entire session state for replay.
|
|
425
|
+
|
|
426
|
+
Serializes session (messages, branches, config) to JSON.
|
|
427
|
+
Resources and operations are excluded (re-register on restore).
|
|
428
|
+
To restore: Session.from_dict(data), then re-register resources.
|
|
429
|
+
|
|
430
|
+
Args:
|
|
431
|
+
clear: Clear communications after dump (default False).
|
|
432
|
+
|
|
433
|
+
Returns:
|
|
434
|
+
Path to session file, or None if logging disabled or empty.
|
|
435
|
+
"""
|
|
436
|
+
from krons.utils import create_path, json_dumpb
|
|
437
|
+
from krons.utils.concurrency import run_async
|
|
438
|
+
|
|
439
|
+
if not self.config.logging_enabled or len(self.messages) == 0:
|
|
440
|
+
return None
|
|
441
|
+
|
|
442
|
+
self._dump_count += 1
|
|
443
|
+
|
|
444
|
+
filepath = run_async(
|
|
445
|
+
create_path(
|
|
446
|
+
directory=self.config.log_persist_dir,
|
|
447
|
+
filename=f"session_{str(self.id)[:8]}_{self._dump_count}",
|
|
448
|
+
extension=".json",
|
|
449
|
+
timestamp=True,
|
|
450
|
+
file_exist_ok=True,
|
|
451
|
+
)
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
data = json_dumpb(self.to_dict(mode="json"), safe_fallback=True)
|
|
455
|
+
std_path = Path(filepath)
|
|
456
|
+
std_path.write_bytes(data)
|
|
457
|
+
|
|
458
|
+
if clear:
|
|
459
|
+
self.communications.clear()
|
|
460
|
+
|
|
461
|
+
return std_path
|
|
462
|
+
|
|
463
|
+
async def adump(self, clear: bool = False) -> Path | None:
|
|
464
|
+
"""Async dump entire session state for replay.
|
|
465
|
+
|
|
466
|
+
Serializes the full session (messages, branches, config) to JSON.
|
|
467
|
+
To restore: Session.from_dict(data), then re-register resources.
|
|
468
|
+
|
|
469
|
+
Args:
|
|
470
|
+
clear: Clear communications after dump (default False).
|
|
471
|
+
|
|
472
|
+
Returns:
|
|
473
|
+
Path to session file, or None if logging disabled or empty.
|
|
474
|
+
"""
|
|
475
|
+
from krons.utils import create_path, json_dumpb
|
|
476
|
+
|
|
477
|
+
if not self.config.logging_enabled or len(self.messages) == 0:
|
|
478
|
+
return None
|
|
479
|
+
|
|
480
|
+
async with self.messages:
|
|
481
|
+
self._dump_count += 1
|
|
482
|
+
|
|
483
|
+
filepath = await create_path(
|
|
484
|
+
directory=self.config.log_persist_dir,
|
|
485
|
+
filename=f"session_{str(self.id)[:8]}_{self._dump_count}",
|
|
486
|
+
extension=".json",
|
|
487
|
+
timestamp=True,
|
|
488
|
+
file_exist_ok=True,
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
data = json_dumpb(self.to_dict(mode="json"), safe_fallback=True)
|
|
492
|
+
await filepath.write_bytes(data)
|
|
493
|
+
|
|
494
|
+
if clear:
|
|
495
|
+
self.communications.clear()
|
|
496
|
+
|
|
497
|
+
return Path(filepath)
|
|
498
|
+
|
|
499
|
+
def _save_at_exit(self) -> None:
|
|
500
|
+
"""atexit callback. Dumps session synchronously. Errors are suppressed."""
|
|
501
|
+
if len(self.messages) > 0:
|
|
502
|
+
try:
|
|
503
|
+
self.dump(clear=False)
|
|
504
|
+
except Exception:
|
|
505
|
+
pass # Silent failure during interpreter shutdown
|
|
506
|
+
|
|
389
507
|
def _resolve_branch(self, branch: Branch | UUID | str | None) -> Branch:
|
|
390
508
|
"""Resolve to Branch, falling back to default. Raises if neither available."""
|
|
391
509
|
if branch is not None:
|
|
@@ -36,13 +36,13 @@ krons/agent/third_party/anthropic_models.py,sha256=rd2Skh_Xx1S2G1JXi2YFb1j6SHm62
|
|
|
36
36
|
krons/agent/third_party/claude_code.py,sha256=NBYMsdPheb0PqGgTO2_aUAR2XoGDsPUwsB7Nd9PjipA,23758
|
|
37
37
|
krons/agent/third_party/gemini_models.py,sha256=2dag2WbSTKLBd8t0F7t_TXCcl7aqsTFgDI6XLkhsZwM,17416
|
|
38
38
|
krons/agent/third_party/openai_models.py,sha256=L-GkJ-bBW5zwPbgCWEYHMNqRiZfFTAUJE96AVZoDAkQ,8040
|
|
39
|
-
krons/core/__init__.py,sha256=
|
|
39
|
+
krons/core/__init__.py,sha256=TKi3toVRYybyjUwUJlng--pfWzWihUWQBCNh0UTfTSQ,3611
|
|
40
40
|
krons/core/base/__init__.py,sha256=Edl_HH2F1l0SMHMzuEPU_IFjkigRR1vtqVXcPm-mr1o,3614
|
|
41
41
|
krons/core/base/broadcaster.py,sha256=Es__WfL-j2h5NS6aU18fgaFkZsJIL-zDe86Qvlea-io,4013
|
|
42
42
|
krons/core/base/element.py,sha256=LqBbmPttXoDAFy56JzWhSJ4sBw_LW7hMGxZUog85N8g,7642
|
|
43
43
|
krons/core/base/event.py,sha256=iZQ65Xg3a8eaCEl4Hca-pxPNtckhrHkydEqYyppbuo0,11524
|
|
44
44
|
krons/core/base/eventbus.py,sha256=z45ORtGey5A1N9hdyEPOhlX1whaTTdqdk7g3XEDL-_8,3761
|
|
45
|
-
krons/core/base/flow.py,sha256=
|
|
45
|
+
krons/core/base/flow.py,sha256=bFG5x7PEbWApsD24GTgpD5VsedLO6364scaAxvYw6LM,12561
|
|
46
46
|
krons/core/base/graph.py,sha256=FiTMArxuz6At3oYPEoamES3FtBM-AM6QnA7EQ8G18mo,15972
|
|
47
47
|
krons/core/base/node.py,sha256=0_-R6uNe6-LF3adQRZowSIICdy6ep3cQIQRdF0oosoQ,34699
|
|
48
48
|
krons/core/base/pile.py,sha256=ZKvnX3BjMsrH989TLNZII1dDWv4yPhSRzLKXx2yzyKQ,21108
|
|
@@ -86,7 +86,7 @@ krons/session/constraints.py,sha256=DvmLKyf5eYUX2nxCDcco1-uDipGjiIJwoUxWVtX2w84,
|
|
|
86
86
|
krons/session/exchange.py,sha256=QChEqK5NFZJpHWxPrXd8LkGBCFg6LNONd1ZEy0MufpI,9165
|
|
87
87
|
krons/session/message.py,sha256=mtZ-PYCCoyWLy7BSB5TL5a0hIt0aQX2zdckOCIl6NZ0,2007
|
|
88
88
|
krons/session/registry.py,sha256=13pPKmDZJaj9q27oUOdtH6-jrHP_HDFF8lCdcNF--EE,999
|
|
89
|
-
krons/session/session.py,sha256=
|
|
89
|
+
krons/session/session.py,sha256=2dykFkdK0DUu3kdv1v8RIevtuD3P50w_0vNIE3qs3D0,17204
|
|
90
90
|
krons/utils/__init__.py,sha256=V-jTKULdofjJXxcEHygefKV_v6XE4H3poCdxPLjvf_4,1718
|
|
91
91
|
krons/utils/_function_arg_parser.py,sha256=H5JVLBVY8W9ZNkZ4_YVtVVY1rFYvnIRhSrAhKEdj2Qc,3479
|
|
92
92
|
krons/utils/_hash.py,sha256=W2Ma9v8-INPaGkur7GTtbF8KwuXSJNSwk8DCNPRvx8Q,6859
|
|
@@ -145,7 +145,7 @@ krons/work/rules/common/mapping.py,sha256=Loq54MNEtwpnHN0aypTjFOqwoOKLEysddHh-JE
|
|
|
145
145
|
krons/work/rules/common/model.py,sha256=xmM6coEThf_fgIiqJiyDgvdfib_FpVeY6LgWPVcWSwU,3026
|
|
146
146
|
krons/work/rules/common/number.py,sha256=cCukgMSpQu5RdYK5rXAUyop9qXgDRfLCioMvE8kIzHg,3162
|
|
147
147
|
krons/work/rules/common/string.py,sha256=zHp_OLh0FL4PvmSlyDTEzb2I97-DBSEyI2zcMo10voA,5090
|
|
148
|
-
krons-0.2.
|
|
149
|
-
krons-0.2.
|
|
150
|
-
krons-0.2.
|
|
151
|
-
krons-0.2.
|
|
148
|
+
krons-0.2.3.dist-info/METADATA,sha256=5VYw8_rlpQ5Q9W8EGwy8XZ19F--f_URzcmVAiG9WIqY,2527
|
|
149
|
+
krons-0.2.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
150
|
+
krons-0.2.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
151
|
+
krons-0.2.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|