krons 0.1.0__py3-none-any.whl → 0.2.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 (162) hide show
  1. krons/__init__.py +49 -0
  2. krons/agent/__init__.py +144 -0
  3. krons/agent/mcps/__init__.py +14 -0
  4. krons/agent/mcps/loader.py +287 -0
  5. krons/agent/mcps/wrapper.py +799 -0
  6. krons/agent/message/__init__.py +20 -0
  7. krons/agent/message/action.py +69 -0
  8. krons/agent/message/assistant.py +52 -0
  9. krons/agent/message/common.py +49 -0
  10. krons/agent/message/instruction.py +130 -0
  11. krons/agent/message/prepare_msg.py +187 -0
  12. krons/agent/message/role.py +53 -0
  13. krons/agent/message/system.py +53 -0
  14. krons/agent/operations/__init__.py +82 -0
  15. krons/agent/operations/act.py +100 -0
  16. krons/agent/operations/generate.py +145 -0
  17. krons/agent/operations/llm_reparse.py +89 -0
  18. krons/agent/operations/operate.py +247 -0
  19. krons/agent/operations/parse.py +243 -0
  20. krons/agent/operations/react.py +286 -0
  21. krons/agent/operations/specs.py +235 -0
  22. krons/agent/operations/structure.py +151 -0
  23. krons/agent/operations/utils.py +79 -0
  24. krons/agent/providers/__init__.py +17 -0
  25. krons/agent/providers/anthropic_messages.py +146 -0
  26. krons/agent/providers/claude_code.py +276 -0
  27. krons/agent/providers/gemini.py +268 -0
  28. krons/agent/providers/match.py +75 -0
  29. krons/agent/providers/oai_chat.py +174 -0
  30. krons/agent/third_party/__init__.py +2 -0
  31. krons/agent/third_party/anthropic_models.py +154 -0
  32. krons/agent/third_party/claude_code.py +682 -0
  33. krons/agent/third_party/gemini_models.py +508 -0
  34. krons/agent/third_party/openai_models.py +295 -0
  35. krons/agent/tool.py +291 -0
  36. krons/core/__init__.py +127 -0
  37. krons/core/base/__init__.py +121 -0
  38. {kronos/core → krons/core/base}/broadcaster.py +7 -3
  39. {kronos/core → krons/core/base}/element.py +15 -7
  40. {kronos/core → krons/core/base}/event.py +41 -8
  41. {kronos/core → krons/core/base}/eventbus.py +4 -2
  42. {kronos/core → krons/core/base}/flow.py +14 -7
  43. {kronos/core → krons/core/base}/graph.py +27 -11
  44. {kronos/core → krons/core/base}/node.py +47 -22
  45. {kronos/core → krons/core/base}/pile.py +26 -12
  46. {kronos/core → krons/core/base}/processor.py +23 -9
  47. {kronos/core → krons/core/base}/progression.py +5 -3
  48. {kronos → krons/core}/specs/__init__.py +0 -5
  49. {kronos → krons/core}/specs/adapters/dataclass_field.py +16 -8
  50. {kronos → krons/core}/specs/adapters/pydantic_adapter.py +11 -5
  51. {kronos → krons/core}/specs/adapters/sql_ddl.py +16 -10
  52. {kronos → krons/core}/specs/catalog/__init__.py +2 -2
  53. {kronos → krons/core}/specs/catalog/_audit.py +3 -3
  54. {kronos → krons/core}/specs/catalog/_common.py +2 -2
  55. {kronos → krons/core}/specs/catalog/_content.py +5 -5
  56. {kronos → krons/core}/specs/catalog/_enforcement.py +4 -4
  57. {kronos → krons/core}/specs/factory.py +7 -7
  58. {kronos → krons/core}/specs/operable.py +9 -3
  59. {kronos → krons/core}/specs/protocol.py +4 -2
  60. {kronos → krons/core}/specs/spec.py +25 -13
  61. {kronos → krons/core}/types/base.py +7 -5
  62. {kronos → krons/core}/types/db_types.py +2 -2
  63. {kronos → krons/core}/types/identity.py +1 -1
  64. {kronos → krons}/errors.py +13 -13
  65. {kronos → krons}/protocols.py +9 -4
  66. krons/resource/__init__.py +89 -0
  67. {kronos/services → krons/resource}/backend.py +50 -24
  68. {kronos/services → krons/resource}/endpoint.py +28 -14
  69. {kronos/services → krons/resource}/hook.py +22 -9
  70. {kronos/services → krons/resource}/imodel.py +50 -32
  71. {kronos/services → krons/resource}/registry.py +27 -25
  72. {kronos/services → krons/resource}/utilities/rate_limited_executor.py +10 -6
  73. {kronos/services → krons/resource}/utilities/rate_limiter.py +4 -2
  74. {kronos/services → krons/resource}/utilities/resilience.py +17 -7
  75. krons/resource/utilities/token_calculator.py +185 -0
  76. {kronos → krons}/session/__init__.py +12 -17
  77. krons/session/constraints.py +70 -0
  78. {kronos → krons}/session/exchange.py +14 -6
  79. {kronos → krons}/session/message.py +4 -2
  80. krons/session/registry.py +35 -0
  81. {kronos → krons}/session/session.py +165 -174
  82. krons/utils/__init__.py +85 -0
  83. krons/utils/_function_arg_parser.py +99 -0
  84. krons/utils/_pythonic_function_call.py +249 -0
  85. {kronos → krons}/utils/_to_list.py +9 -3
  86. {kronos → krons}/utils/_utils.py +9 -5
  87. {kronos → krons}/utils/concurrency/__init__.py +38 -38
  88. {kronos → krons}/utils/concurrency/_async_call.py +6 -4
  89. {kronos → krons}/utils/concurrency/_errors.py +3 -1
  90. {kronos → krons}/utils/concurrency/_patterns.py +3 -1
  91. {kronos → krons}/utils/concurrency/_resource_tracker.py +6 -2
  92. krons/utils/display.py +257 -0
  93. {kronos → krons}/utils/fuzzy/__init__.py +6 -1
  94. {kronos → krons}/utils/fuzzy/_fuzzy_match.py +14 -8
  95. {kronos → krons}/utils/fuzzy/_string_similarity.py +3 -1
  96. {kronos → krons}/utils/fuzzy/_to_dict.py +3 -1
  97. krons/utils/schemas/__init__.py +26 -0
  98. krons/utils/schemas/_breakdown_pydantic_annotation.py +131 -0
  99. krons/utils/schemas/_formatter.py +72 -0
  100. krons/utils/schemas/_minimal_yaml.py +151 -0
  101. krons/utils/schemas/_typescript.py +153 -0
  102. {kronos → krons}/utils/sql/_sql_validation.py +1 -1
  103. krons/utils/validators/__init__.py +3 -0
  104. krons/utils/validators/_validate_image_url.py +56 -0
  105. krons/work/__init__.py +126 -0
  106. krons/work/engine.py +333 -0
  107. krons/work/form.py +305 -0
  108. {kronos → krons/work}/operations/__init__.py +7 -4
  109. {kronos → krons/work}/operations/builder.py +4 -4
  110. {kronos/enforcement → krons/work/operations}/context.py +37 -6
  111. {kronos → krons/work}/operations/flow.py +17 -9
  112. krons/work/operations/node.py +103 -0
  113. krons/work/operations/registry.py +103 -0
  114. {kronos/specs → krons/work}/phrase.py +131 -14
  115. {kronos/enforcement → krons/work}/policy.py +3 -3
  116. krons/work/report.py +268 -0
  117. krons/work/rules/__init__.py +47 -0
  118. {kronos/enforcement → krons/work/rules}/common/boolean.py +3 -1
  119. {kronos/enforcement → krons/work/rules}/common/choice.py +9 -3
  120. {kronos/enforcement → krons/work/rules}/common/number.py +3 -1
  121. {kronos/enforcement → krons/work/rules}/common/string.py +9 -3
  122. {kronos/enforcement → krons/work/rules}/rule.py +2 -2
  123. {kronos/enforcement → krons/work/rules}/validator.py +21 -6
  124. {kronos/enforcement → krons/work}/service.py +16 -7
  125. krons/work/worker.py +266 -0
  126. {krons-0.1.0.dist-info → krons-0.2.0.dist-info}/METADATA +19 -5
  127. krons-0.2.0.dist-info/RECORD +154 -0
  128. kronos/core/__init__.py +0 -145
  129. kronos/enforcement/__init__.py +0 -57
  130. kronos/operations/node.py +0 -101
  131. kronos/operations/registry.py +0 -92
  132. kronos/services/__init__.py +0 -81
  133. kronos/specs/adapters/__init__.py +0 -0
  134. kronos/utils/__init__.py +0 -40
  135. krons-0.1.0.dist-info/RECORD +0 -101
  136. {kronos → krons/core/specs/adapters}/__init__.py +0 -0
  137. {kronos → krons/core}/specs/adapters/_utils.py +0 -0
  138. {kronos → krons/core}/specs/adapters/factory.py +0 -0
  139. {kronos → krons/core}/types/__init__.py +0 -0
  140. {kronos → krons/core}/types/_sentinel.py +0 -0
  141. {kronos → krons}/py.typed +0 -0
  142. {kronos/services → krons/resource}/utilities/__init__.py +0 -0
  143. {kronos/services → krons/resource}/utilities/header_factory.py +0 -0
  144. {kronos → krons}/utils/_hash.py +0 -0
  145. {kronos → krons}/utils/_json_dump.py +0 -0
  146. {kronos → krons}/utils/_lazy_init.py +0 -0
  147. {kronos → krons}/utils/_to_num.py +0 -0
  148. {kronos → krons}/utils/concurrency/_cancel.py +0 -0
  149. {kronos → krons}/utils/concurrency/_primitives.py +0 -0
  150. {kronos → krons}/utils/concurrency/_priority_queue.py +0 -0
  151. {kronos → krons}/utils/concurrency/_run_async.py +0 -0
  152. {kronos → krons}/utils/concurrency/_task.py +0 -0
  153. {kronos → krons}/utils/concurrency/_utils.py +0 -0
  154. {kronos → krons}/utils/fuzzy/_extract_json.py +0 -0
  155. {kronos → krons}/utils/fuzzy/_fuzzy_json.py +0 -0
  156. {kronos → krons}/utils/sql/__init__.py +0 -0
  157. {kronos/enforcement → krons/work/rules}/common/__init__.py +0 -0
  158. {kronos/enforcement → krons/work/rules}/common/mapping.py +0 -0
  159. {kronos/enforcement → krons/work/rules}/common/model.py +0 -0
  160. {kronos/enforcement → krons/work/rules}/registry.py +0 -0
  161. {krons-0.1.0.dist-info → krons-0.2.0.dist-info}/WHEEL +0 -0
  162. {krons-0.1.0.dist-info → krons-0.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,121 @@
1
+ # Copyright (c) 2025 - 2026, HaiyangLi <quantocean.li at gmail dot com>
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """Core primitives with lazy loading for fast import."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import TYPE_CHECKING
9
+
10
+ # Lazy import mapping - all modules are in krons.core.base.*
11
+ _LAZY_IMPORTS: dict[str, tuple[str, str]] = {
12
+ # broadcaster
13
+ "Broadcaster": ("krons.core.base.broadcaster", "Broadcaster"),
14
+ # element
15
+ "Element": ("krons.core.base.element", "Element"),
16
+ # event
17
+ "Event": ("krons.core.base.event", "Event"),
18
+ "EventStatus": ("krons.core.base.event", "EventStatus"),
19
+ "Execution": ("krons.core.base.event", "Execution"),
20
+ # eventbus
21
+ "EventBus": ("krons.core.base.eventbus", "EventBus"),
22
+ "Handler": ("krons.core.base.eventbus", "Handler"),
23
+ # flow
24
+ "Flow": ("krons.core.base.flow", "Flow"),
25
+ # graph
26
+ "Edge": ("krons.core.base.graph", "Edge"),
27
+ "EdgeCondition": ("krons.core.base.graph", "EdgeCondition"),
28
+ "Graph": ("krons.core.base.graph", "Graph"),
29
+ # node
30
+ "NODE_REGISTRY": ("krons.core.base.node", "NODE_REGISTRY"),
31
+ "PERSISTABLE_NODE_REGISTRY": ("krons.core.base.node", "PERSISTABLE_NODE_REGISTRY"),
32
+ "Node": ("krons.core.base.node", "Node"),
33
+ "NodeConfig": ("krons.core.base.node", "NodeConfig"),
34
+ "create_node": ("krons.core.base.node", "create_node"),
35
+ "generate_ddl": ("krons.core.base.node", "generate_ddl"),
36
+ "generate_all_ddl": ("krons.core.base.node", "generate_all_ddl"),
37
+ "get_fk_dependencies": ("krons.core.base.node", "get_fk_dependencies"),
38
+ # pile
39
+ "Pile": ("krons.core.base.pile", "Pile"),
40
+ # processor
41
+ "Executor": ("krons.core.base.processor", "Executor"),
42
+ "Processor": ("krons.core.base.processor", "Processor"),
43
+ # progression
44
+ "Progression": ("krons.core.base.progression", "Progression"),
45
+ }
46
+
47
+ _LOADED: dict[str, object] = {}
48
+
49
+
50
+ def __getattr__(name: str) -> object:
51
+ """Lazy import attributes on first access."""
52
+ if name in _LOADED:
53
+ return _LOADED[name]
54
+
55
+ if name in _LAZY_IMPORTS:
56
+ from importlib import import_module
57
+
58
+ module_name, attr_name = _LAZY_IMPORTS[name]
59
+ module = import_module(module_name)
60
+ value = getattr(module, attr_name)
61
+ _LOADED[name] = value
62
+ return value
63
+
64
+ raise AttributeError(f"module 'krons.core' has no attribute {name!r}")
65
+
66
+
67
+ def __dir__() -> list[str]:
68
+ """Return all available attributes for autocomplete."""
69
+ return list(__all__)
70
+
71
+
72
+ # TYPE_CHECKING block for static analysis
73
+ if TYPE_CHECKING:
74
+ from .broadcaster import Broadcaster
75
+ from .element import Element
76
+ from .event import Event, EventStatus, Execution
77
+ from .eventbus import EventBus, Handler
78
+ from .flow import Flow
79
+ from .graph import Edge, EdgeCondition, Graph
80
+ from .node import (
81
+ NODE_REGISTRY,
82
+ PERSISTABLE_NODE_REGISTRY,
83
+ Node,
84
+ NodeConfig,
85
+ create_node,
86
+ generate_all_ddl,
87
+ generate_ddl,
88
+ get_fk_dependencies,
89
+ )
90
+ from .pile import Pile
91
+ from .processor import Executor, Processor
92
+ from .progression import Progression
93
+
94
+ __all__ = [
95
+ # constants/registries
96
+ "NODE_REGISTRY",
97
+ "PERSISTABLE_NODE_REGISTRY",
98
+ # classes
99
+ "Broadcaster",
100
+ "Edge",
101
+ "EdgeCondition",
102
+ "Element",
103
+ "Event",
104
+ "EventBus",
105
+ "EventStatus",
106
+ "Execution",
107
+ "Executor",
108
+ "Flow",
109
+ "Graph",
110
+ "Handler",
111
+ "Node",
112
+ "NodeConfig",
113
+ "Pile",
114
+ "Processor",
115
+ "Progression",
116
+ # functions
117
+ "create_node",
118
+ "generate_all_ddl",
119
+ "generate_ddl",
120
+ "get_fk_dependencies",
121
+ ]
@@ -8,7 +8,7 @@ import weakref
8
8
  from collections.abc import Awaitable, Callable
9
9
  from typing import Any, ClassVar
10
10
 
11
- from kronos.utils import is_coro_func
11
+ from krons.utils.concurrency import is_coro_func
12
12
 
13
13
  logger = logging.getLogger(__name__)
14
14
 
@@ -41,7 +41,9 @@ class Broadcaster:
41
41
  return cls._instance
42
42
 
43
43
  @classmethod
44
- def subscribe(cls, callback: Callable[[Any], None] | Callable[[Any], Awaitable[None]]) -> None:
44
+ def subscribe(
45
+ cls, callback: Callable[[Any], None] | Callable[[Any], Awaitable[None]]
46
+ ) -> None:
45
47
  """Add subscriber callback (idempotent, stored as weakref).
46
48
 
47
49
  Args:
@@ -51,7 +53,9 @@ class Broadcaster:
51
53
  for weak_ref in cls._subscribers:
52
54
  if weak_ref() is callback:
53
55
  return
54
- weak_callback: weakref.ref[Callable[[Any], None] | Callable[[Any], Awaitable[None]]]
56
+ weak_callback: weakref.ref[
57
+ Callable[[Any], None] | Callable[[Any], Awaitable[None]]
58
+ ]
55
59
  if hasattr(callback, "__self__"):
56
60
  weak_callback = weakref.WeakMethod(callback) # type: ignore[assignment]
57
61
  else:
@@ -11,15 +11,15 @@ from uuid import UUID, uuid4
11
11
  import orjson
12
12
  from pydantic import BaseModel, ConfigDict, Field, field_validator
13
13
 
14
- from kronos.protocols import (
14
+ from krons.core.types import MaybeSentinel, Unset, UnsetType, is_sentinel, is_unset
15
+ from krons.protocols import (
15
16
  Deserializable,
16
17
  Hashable,
17
18
  Observable,
18
19
  Serializable,
19
20
  implements,
20
21
  )
21
- from kronos.types import MaybeSentinel, Unset, UnsetType, is_sentinel, is_unset
22
- from kronos.utils import (
22
+ from krons.utils import (
23
23
  coerce_created_at,
24
24
  json_dump,
25
25
  load_type_from_string,
@@ -68,7 +68,9 @@ class Element(BaseModel):
68
68
 
69
69
  @field_validator("metadata", mode="before")
70
70
  @classmethod
71
- def _validate_meta_integrity(cls, val: dict[str, Any] | MaybeSentinel) -> dict[str, Any]:
71
+ def _validate_meta_integrity(
72
+ cls, val: dict[str, Any] | MaybeSentinel
73
+ ) -> dict[str, Any]:
72
74
  """Validate and coerce metadata to dict. Raises ValueError if conversion fails."""
73
75
  if is_sentinel(val, {"none"}):
74
76
  return {}
@@ -108,7 +110,9 @@ class Element(BaseModel):
108
110
  def to_dict(
109
111
  self,
110
112
  mode: Literal["python", "json", "db"] = "python",
111
- created_at_format: (Literal["datetime", "isoformat", "timestamp"] | UnsetType) = Unset,
113
+ created_at_format: (
114
+ Literal["datetime", "isoformat", "timestamp"] | UnsetType
115
+ ) = Unset,
112
116
  meta_key: str | UnsetType = Unset,
113
117
  **kwargs: Any,
114
118
  ) -> dict[str, Any]:
@@ -187,7 +191,9 @@ class Element(BaseModel):
187
191
  try:
188
192
  target_cls = load_type_from_string(kron_class)
189
193
  except ValueError as e:
190
- raise ValueError(f"Failed to deserialize class '{kron_class}': {e}") from e
194
+ raise ValueError(
195
+ f"Failed to deserialize class '{kron_class}': {e}"
196
+ ) from e
191
197
 
192
198
  if not issubclass(target_cls, Element):
193
199
  raise ValueError(
@@ -195,7 +201,9 @@ class Element(BaseModel):
195
201
  f"Cannot deserialize into {cls.__name__}"
196
202
  )
197
203
 
198
- target_func = getattr(target_cls.from_dict, "__func__", target_cls.from_dict)
204
+ target_func = getattr(
205
+ target_cls.from_dict, "__func__", target_cls.from_dict
206
+ )
199
207
  cls_func = getattr(cls.from_dict, "__func__", cls.from_dict)
200
208
  if target_func is cls_func:
201
209
  return target_cls.model_validate(data, **kwargs)
@@ -11,10 +11,17 @@ from typing import Any, final
11
11
  import orjson
12
12
  from pydantic import Field, field_serializer, field_validator
13
13
 
14
- from kronos.errors import KronError, KronTimeoutError
15
- from kronos.protocols import Invocable, Serializable, implements
16
- from kronos.types import Enum, MaybeSentinel, MaybeUnset, Unset, is_sentinel, is_unset
17
- from kronos.utils import async_synchronized, concurrency, json_dumpb
14
+ from krons.core.types import (
15
+ Enum,
16
+ MaybeSentinel,
17
+ MaybeUnset,
18
+ Unset,
19
+ is_sentinel,
20
+ is_unset,
21
+ )
22
+ from krons.errors import KronsError, KronTimeoutError, ValidationError
23
+ from krons.protocols import Invocable, Serializable, implements
24
+ from krons.utils import async_synchronized, concurrency, json_dumpb
18
25
 
19
26
  from .element import LN_ELEMENT_FIELDS, Element
20
27
 
@@ -142,7 +149,9 @@ class Execution:
142
149
  if isinstance(exc, Serializable):
143
150
  exceptions.append(exc.to_dict())
144
151
  elif isinstance(exc, ExceptionGroup):
145
- exceptions.append(self._serialize_exception_group(exc, depth + 1, _seen))
152
+ exceptions.append(
153
+ self._serialize_exception_group(exc, depth + 1, _seen)
154
+ )
146
155
  else:
147
156
  exceptions.append(
148
157
  {
@@ -262,11 +271,14 @@ class Event(Element):
262
271
  except Exception as e:
263
272
  if isinstance(e, ExceptionGroup):
264
273
  retryable = all(
265
- not isinstance(exc, KronError) or exc.retryable for exc in e.exceptions
274
+ not isinstance(exc, KronsError) or exc.retryable
275
+ for exc in e.exceptions
266
276
  )
267
277
  self.execution.retryable = retryable
268
278
  else:
269
- self.execution.retryable = e.retryable if isinstance(e, KronError) else True
279
+ self.execution.retryable = (
280
+ e.retryable if isinstance(e, KronsError) else True
281
+ )
270
282
 
271
283
  self.execution.response = Unset
272
284
  self.execution.error = e
@@ -286,7 +298,9 @@ class Event(Element):
286
298
 
287
299
  async def stream(self) -> Any:
288
300
  """Stream execution results. Override if streaming=True."""
289
- raise NotImplementedError("Subclasses must implement stream() if streaming=True")
301
+ raise NotImplementedError(
302
+ "Subclasses must implement stream() if streaming=True"
303
+ )
290
304
 
291
305
  def as_fresh_event(self, copy_meta: bool = False) -> Event:
292
306
  """Clone with reset execution state (new ID, PENDING status).
@@ -314,3 +328,22 @@ class Event(Element):
314
328
  "created_at": self.created_at,
315
329
  }
316
330
  return fresh
331
+
332
+ def assert_completed(self, *, retryable: MaybeUnset[bool] = Unset):
333
+ if self.execution.status != EventStatus.COMPLETED:
334
+ retryable_value = (
335
+ self.execution.retryable if is_unset(retryable) else retryable
336
+ )
337
+ retryable_value = True if retryable_value is True else False
338
+ exec_dict = self.execution.to_dict()
339
+ exec_dict.pop("response", None)
340
+ exec_dict.pop("retryable", None)
341
+
342
+ raise ValidationError(
343
+ "Event did not complete successfully.",
344
+ details={
345
+ "event_id": str(self.id),
346
+ **exec_dict,
347
+ },
348
+ retryable=retryable_value,
349
+ )
@@ -8,7 +8,7 @@ from collections import defaultdict
8
8
  from collections.abc import Awaitable, Callable
9
9
  from typing import Any
10
10
 
11
- from kronos.utils.concurrency import gather
11
+ from krons.utils.concurrency import gather
12
12
 
13
13
  __all__ = ("EventBus", "Handler")
14
14
 
@@ -85,7 +85,9 @@ class EventBus:
85
85
  if topic not in self._subs:
86
86
  return
87
87
  if handlers := self._cleanup_dead_refs(topic):
88
- await gather(*(h(*args, **kwargs) for h in handlers), return_exceptions=True)
88
+ await gather(
89
+ *(h(*args, **kwargs) for h in handlers), return_exceptions=True
90
+ )
89
91
 
90
92
  def clear(self, topic: str | None = None) -> None:
91
93
  """Clear subscriptions.
@@ -9,10 +9,10 @@ from uuid import UUID
9
9
 
10
10
  from pydantic import Field, PrivateAttr, field_validator, model_validator
11
11
 
12
- from kronos.errors import ExistsError, NotFoundError
13
- from kronos.protocols import Serializable, implements
14
- from kronos.types import Unset, UnsetType
15
- from kronos.utils import extract_types, synchronized
12
+ from krons.core.types import Unset, UnsetType
13
+ from krons.errors import ExistsError, NotFoundError
14
+ from krons.protocols import Serializable, implements
15
+ from krons.utils import extract_types, synchronized
16
16
 
17
17
  from .element import Element
18
18
  from .pile import Pile
@@ -99,7 +99,9 @@ class Flow(Element, Generic[E, P]):
99
99
 
100
100
  # Create Pile with items and type validation (item_type/strict_type are frozen)
101
101
  # Even if items=None, create Pile if item_type/strict_type specified
102
- data["items"] = Pile(items=items, item_type=item_type, strict_type=strict_type)
102
+ data["items"] = Pile(
103
+ items=items, item_type=item_type, strict_type=strict_type
104
+ )
103
105
 
104
106
  # Handle progressions - let field validator convert dict/list to Pile
105
107
  if progressions is not None:
@@ -217,7 +219,10 @@ class Flow(Element, Generic[E, P]):
217
219
  NotFoundError: If progression not found.
218
220
  """
219
221
  name_to_delete: str | None
220
- if isinstance(progression_id, str) and progression_id in self._progression_names:
222
+ if (
223
+ isinstance(progression_id, str)
224
+ and progression_id in self._progression_names
225
+ ):
221
226
  uid = self._progression_names[progression_id]
222
227
  name_to_delete = progression_id
223
228
  else:
@@ -323,7 +328,9 @@ class Flow(Element, Generic[E, P]):
323
328
  def to_dict(
324
329
  self,
325
330
  mode: Literal["python", "json", "db"] = "python",
326
- created_at_format: (Literal["datetime", "isoformat", "timestamp"] | UnsetType) = Unset,
331
+ created_at_format: (
332
+ Literal["datetime", "isoformat", "timestamp"] | UnsetType
333
+ ) = Unset,
327
334
  meta_key: str | UnsetType = Unset,
328
335
  **kwargs: Any,
329
336
  ) -> dict[str, Any]:
@@ -11,10 +11,10 @@ from uuid import UUID
11
11
  from pydantic import Field, PrivateAttr, field_validator, model_validator
12
12
  from typing_extensions import override
13
13
 
14
- from kronos.errors import NotFoundError
15
- from kronos.protocols import Containable, Deserializable, Serializable, implements
16
- from kronos.types import Unset, UnsetType, is_unset
17
- from kronos.utils import synchronized
14
+ from krons.core.types import Unset, UnsetType, is_unset
15
+ from krons.errors import NotFoundError
16
+ from krons.protocols import Containable, Deserializable, Serializable, implements
17
+ from krons.utils import synchronized
18
18
 
19
19
  from .element import Element
20
20
  from .node import Node
@@ -61,7 +61,9 @@ class Edge(Element):
61
61
  condition: EdgeCondition | None = Field(
62
62
  default=None, exclude=True, description="Runtime traversal predicate"
63
63
  )
64
- properties: dict[str, Any] = Field(default_factory=dict, description="Custom edge attributes")
64
+ properties: dict[str, Any] = Field(
65
+ default_factory=dict, description="Custom edge attributes"
66
+ )
65
67
 
66
68
  @field_validator("head", "tail", mode="before")
67
69
  @classmethod
@@ -212,12 +214,16 @@ class Graph(Element):
212
214
  def get_predecessors(self, node_id: UUID | Node) -> list[Node]:
213
215
  """Get nodes with edges pointing to this node (in-neighbors)."""
214
216
  nid = self._coerce_id(node_id)
215
- return [self.nodes[self.edges[eid].head] for eid in self._in_edges.get(nid, set())]
217
+ return [
218
+ self.nodes[self.edges[eid].head] for eid in self._in_edges.get(nid, set())
219
+ ]
216
220
 
217
221
  def get_successors(self, node_id: UUID | Node) -> list[Node]:
218
222
  """Get nodes this node points to (out-neighbors)."""
219
223
  nid = self._coerce_id(node_id)
220
- return [self.nodes[self.edges[eid].tail] for eid in self._out_edges.get(nid, set())]
224
+ return [
225
+ self.nodes[self.edges[eid].tail] for eid in self._out_edges.get(nid, set())
226
+ ]
221
227
 
222
228
  def get_node_edges(
223
229
  self,
@@ -240,11 +246,17 @@ class Graph(Element):
240
246
 
241
247
  def get_heads(self) -> list[Node]:
242
248
  """Get source nodes (no incoming edges)."""
243
- return [self.nodes[nid] for nid, in_edges in self._in_edges.items() if not in_edges]
249
+ return [
250
+ self.nodes[nid] for nid, in_edges in self._in_edges.items() if not in_edges
251
+ ]
244
252
 
245
253
  def get_tails(self) -> list[Node]:
246
254
  """Get sink nodes (no outgoing edges)."""
247
- return [self.nodes[nid] for nid, out_edges in self._out_edges.items() if not out_edges]
255
+ return [
256
+ self.nodes[nid]
257
+ for nid, out_edges in self._out_edges.items()
258
+ if not out_edges
259
+ ]
248
260
 
249
261
  # ==================== Graph Algorithms ====================
250
262
 
@@ -361,10 +373,14 @@ class Graph(Element):
361
373
  def to_dict(
362
374
  self,
363
375
  mode: Literal["python", "json", "db"] = "python",
364
- created_at_format: (Literal["datetime", "isoformat", "timestamp"] | UnsetType) = Unset,
376
+ created_at_format: (
377
+ Literal["datetime", "isoformat", "timestamp"] | UnsetType
378
+ ) = Unset,
365
379
  meta_key: str | UnsetType = Unset,
366
380
  item_meta_key: str | UnsetType = Unset,
367
- item_created_at_format: (Literal["datetime", "isoformat", "timestamp"] | UnsetType) = Unset,
381
+ item_created_at_format: (
382
+ Literal["datetime", "isoformat", "timestamp"] | UnsetType
383
+ ) = Unset,
368
384
  **kwargs: Any,
369
385
  ) -> dict[str, Any]:
370
386
  """Serialize graph with nodes and edges as nested Pile dicts.
@@ -15,8 +15,7 @@ from uuid import UUID
15
15
 
16
16
  from pydantic import BaseModel, field_serializer, field_validator
17
17
 
18
- from kronos.protocols import Deserializable, Serializable, implements
19
- from kronos.types import (
18
+ from krons.core.types import (
20
19
  ModelConfig,
21
20
  Params,
22
21
  Unset,
@@ -25,8 +24,9 @@ from kronos.types import (
25
24
  is_unset,
26
25
  not_sentinel,
27
26
  )
28
- from kronos.types.db_types import VectorMeta, extract_kron_db_meta
29
- from kronos.utils import compute_hash, json_dump, now_utc
27
+ from krons.core.types.db_types import VectorMeta, extract_kron_db_meta
28
+ from krons.protocols import Deserializable, Serializable, implements
29
+ from krons.utils import compute_hash, json_dump, now_utc
30
30
 
31
31
  from .element import Element
32
32
 
@@ -56,9 +56,13 @@ def _enable_embedding_requires_dim(config: NodeConfig) -> None:
56
56
  """Validate: embedding_enabled requires positive embedding_dim."""
57
57
  if config.embedding_enabled:
58
58
  if config.is_sentinel_field("embedding_dim"):
59
- raise ValueError("embedding_dim must be specified when embedding is enabled")
59
+ raise ValueError(
60
+ "embedding_dim must be specified when embedding is enabled"
61
+ )
60
62
  if config.embedding_dim <= 0:
61
- raise ValueError(f"embedding_dim must be positive, got {config.embedding_dim}")
63
+ raise ValueError(
64
+ f"embedding_dim must be positive, got {config.embedding_dim}"
65
+ )
62
66
 
63
67
 
64
68
  def _only_typed_content_can_flatten(config: NodeConfig) -> None:
@@ -231,7 +235,9 @@ class Node(Element):
231
235
  cls._resolved_content_type = None
232
236
  else:
233
237
  cls._resolved_content_type = (
234
- None if config.is_sentinel_field("content_type") else config.content_type
238
+ None
239
+ if config.is_sentinel_field("content_type")
240
+ else config.content_type
235
241
  )
236
242
 
237
243
  @field_serializer("content")
@@ -258,12 +264,15 @@ class Node(Element):
258
264
  f"or Element.metadata for simple key-value pairs."
259
265
  )
260
266
 
261
- # Polymorphic: restore type from kronos_class in metadata
267
+ # Polymorphic: restore type from krons_class in metadata
262
268
  if isinstance(value, dict) and "metadata" in value:
263
269
  metadata = value.get("metadata", {})
264
270
  kron_class = metadata.get("kron_class")
265
271
  if kron_class:
266
- if kron_class in NODE_REGISTRY or kron_class.split(".")[-1] in NODE_REGISTRY:
272
+ if (
273
+ kron_class in NODE_REGISTRY
274
+ or kron_class.split(".")[-1] in NODE_REGISTRY
275
+ ):
267
276
  return Node.from_dict(value)
268
277
  return Element.from_dict(value)
269
278
  return value
@@ -271,7 +280,9 @@ class Node(Element):
271
280
  def to_dict(
272
281
  self,
273
282
  mode: Literal["python", "json", "db"] = "python",
274
- created_at_format: (Literal["datetime", "isoformat", "timestamp"] | UnsetType) = Unset,
283
+ created_at_format: (
284
+ Literal["datetime", "isoformat", "timestamp"] | UnsetType
285
+ ) = Unset,
275
286
  meta_key: str | UnsetType = Unset,
276
287
  content_serializer: Callable[[Any], Any] | None = None,
277
288
  **kwargs: Any,
@@ -421,14 +432,18 @@ class Node(Element):
421
432
  and issubclass(content_type, BaseModel)
422
433
  ):
423
434
  content_field_names = set(content_type.model_fields.keys())
424
- content_data = {k: v for k, v in data.items() if k in content_field_names}
435
+ content_data = {
436
+ k: v for k, v in data.items() if k in content_field_names
437
+ }
425
438
  for k in content_field_names:
426
439
  data.pop(k, None)
427
440
  data["content"] = content_type(**content_data)
428
441
 
429
442
  # Handle meta_key for DB rows
430
443
  effective_meta_key = (
431
- meta_key if not is_unset(meta_key) else (config.meta_key if from_row else Unset)
444
+ meta_key
445
+ if not is_unset(meta_key)
446
+ else (config.meta_key if from_row else Unset)
432
447
  )
433
448
 
434
449
  if content_deserializer is not None:
@@ -503,7 +518,7 @@ class Node(Element):
503
518
  Computed integrity_hash, or None if integrity_hashing disabled
504
519
 
505
520
  """
506
- from kronos.utils import compute_chain_hash
521
+ from krons.utils import compute_chain_hash
507
522
 
508
523
  config = self.get_config()
509
524
  if not config.integrity_hashing:
@@ -704,8 +719,8 @@ def create_node(
704
719
  >>> Job = create_node("Job", embedding_enabled=True, embedding_dim=1536)
705
720
 
706
721
  """
707
- from kronos.specs.catalog import AuditSpecs, ContentSpecs
708
- from kronos.specs.operable import Operable
722
+ from krons.core.specs.catalog import AuditSpecs, ContentSpecs
723
+ from krons.core.specs.operable import Operable
709
724
 
710
725
  # Resolve embedding dimension
711
726
  resolved_embedding_dim: int | UnsetType = Unset
@@ -723,7 +738,9 @@ def create_node(
723
738
  )
724
739
  elif embedding_enabled:
725
740
  if embedding_dim is None or embedding_dim <= 0:
726
- raise ValueError("embedding_dim must be positive when embedding_enabled=True")
741
+ raise ValueError(
742
+ "embedding_dim must be positive when embedding_enabled=True"
743
+ )
727
744
  resolved_embedding_dim = embedding_dim
728
745
  has_embedding = True
729
746
 
@@ -742,7 +759,11 @@ def create_node(
742
759
  include.append("embedding")
743
760
 
744
761
  needs_update_tracking = (
745
- track_updated_at or content_hashing or integrity_hashing or soft_delete or versioning
762
+ track_updated_at
763
+ or content_hashing
764
+ or integrity_hashing
765
+ or soft_delete
766
+ or versioning
746
767
  )
747
768
  if needs_update_tracking:
748
769
  include.append("updated_at")
@@ -805,7 +826,9 @@ def _extract_base_type(annotation: Any) -> Any:
805
826
  if annotation is None:
806
827
  return None
807
828
 
808
- if isinstance(annotation, types.UnionType) or get_origin(annotation) is type(int | str):
829
+ if isinstance(annotation, types.UnionType) or get_origin(annotation) is type(
830
+ int | str
831
+ ):
809
832
  args = get_args(annotation)
810
833
  non_none_args = [a for a in args if a is not type(None)]
811
834
  if non_none_args:
@@ -835,12 +858,14 @@ def generate_ddl(
835
858
  ValueError: If node_cls has no table_name configured
836
859
 
837
860
  """
838
- from kronos.specs.catalog import AuditSpecs, ContentSpecs
839
- from kronos.specs.operable import Operable
861
+ from krons.core.specs.catalog import AuditSpecs, ContentSpecs
862
+ from krons.core.specs.operable import Operable
840
863
 
841
864
  config = node_cls.get_config()
842
865
  if not config.is_persisted:
843
- raise ValueError(f"{node_cls.__name__} is not persistable (no table_name configured)")
866
+ raise ValueError(
867
+ f"{node_cls.__name__} is not persistable (no table_name configured)"
868
+ )
844
869
 
845
870
  # 1. Build all possible specs for this node
846
871
  content_type = (
@@ -855,7 +880,7 @@ def generate_ddl(
855
880
 
856
881
  # Flatten content: extract fields from BaseModel instead of generic JSONB
857
882
  if config.flatten_content and content_type is not None:
858
- from kronos.specs.adapters.pydantic_adapter import PydanticSpecAdapter
883
+ from krons.core.specs.adapters.pydantic_adapter import PydanticSpecAdapter
859
884
 
860
885
  if isinstance(content_type, type) and issubclass(content_type, BaseModel):
861
886
  all_specs.extend(PydanticSpecAdapter.extract_specs(content_type))