krons 0.1.1__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.
- krons/__init__.py +49 -0
- krons/agent/__init__.py +144 -0
- krons/agent/mcps/__init__.py +14 -0
- krons/agent/mcps/loader.py +287 -0
- krons/agent/mcps/wrapper.py +799 -0
- krons/agent/message/__init__.py +20 -0
- krons/agent/message/action.py +69 -0
- krons/agent/message/assistant.py +52 -0
- krons/agent/message/common.py +49 -0
- krons/agent/message/instruction.py +130 -0
- krons/agent/message/prepare_msg.py +187 -0
- krons/agent/message/role.py +53 -0
- krons/agent/message/system.py +53 -0
- krons/agent/operations/__init__.py +82 -0
- krons/agent/operations/act.py +100 -0
- krons/agent/operations/generate.py +145 -0
- krons/agent/operations/llm_reparse.py +89 -0
- krons/agent/operations/operate.py +247 -0
- krons/agent/operations/parse.py +243 -0
- krons/agent/operations/react.py +286 -0
- krons/agent/operations/specs.py +235 -0
- krons/agent/operations/structure.py +151 -0
- krons/agent/operations/utils.py +79 -0
- krons/agent/providers/__init__.py +17 -0
- krons/agent/providers/anthropic_messages.py +146 -0
- krons/agent/providers/claude_code.py +276 -0
- krons/agent/providers/gemini.py +268 -0
- krons/agent/providers/match.py +75 -0
- krons/agent/providers/oai_chat.py +174 -0
- krons/agent/third_party/__init__.py +2 -0
- krons/agent/third_party/anthropic_models.py +154 -0
- krons/agent/third_party/claude_code.py +682 -0
- krons/agent/third_party/gemini_models.py +508 -0
- krons/agent/third_party/openai_models.py +295 -0
- krons/agent/tool.py +291 -0
- krons/core/__init__.py +56 -74
- krons/core/base/__init__.py +121 -0
- krons/core/{broadcaster.py → base/broadcaster.py} +7 -3
- krons/core/{element.py → base/element.py} +13 -5
- krons/core/{event.py → base/event.py} +39 -6
- krons/core/{eventbus.py → base/eventbus.py} +3 -1
- krons/core/{flow.py → base/flow.py} +11 -4
- krons/core/{graph.py → base/graph.py} +24 -8
- krons/core/{node.py → base/node.py} +44 -19
- krons/core/{pile.py → base/pile.py} +22 -8
- krons/core/{processor.py → base/processor.py} +21 -7
- krons/core/{progression.py → base/progression.py} +3 -1
- krons/{specs → core/specs}/__init__.py +0 -5
- krons/{specs → core/specs}/adapters/dataclass_field.py +16 -8
- krons/{specs → core/specs}/adapters/pydantic_adapter.py +11 -5
- krons/{specs → core/specs}/adapters/sql_ddl.py +14 -8
- krons/{specs → core/specs}/catalog/__init__.py +2 -2
- krons/{specs → core/specs}/catalog/_audit.py +2 -2
- krons/{specs → core/specs}/catalog/_common.py +2 -2
- krons/{specs → core/specs}/catalog/_content.py +4 -4
- krons/{specs → core/specs}/catalog/_enforcement.py +3 -3
- krons/{specs → core/specs}/factory.py +5 -5
- krons/{specs → core/specs}/operable.py +8 -2
- krons/{specs → core/specs}/protocol.py +4 -2
- krons/{specs → core/specs}/spec.py +23 -11
- krons/{types → core/types}/base.py +4 -2
- krons/{types → core/types}/db_types.py +2 -2
- krons/errors.py +13 -13
- krons/protocols.py +9 -4
- krons/resource/__init__.py +89 -0
- krons/{services → resource}/backend.py +48 -22
- krons/{services → resource}/endpoint.py +28 -14
- krons/{services → resource}/hook.py +20 -7
- krons/{services → resource}/imodel.py +46 -28
- krons/{services → resource}/registry.py +26 -24
- krons/{services → resource}/utilities/rate_limited_executor.py +7 -3
- krons/{services → resource}/utilities/rate_limiter.py +3 -1
- krons/{services → resource}/utilities/resilience.py +15 -5
- krons/resource/utilities/token_calculator.py +185 -0
- krons/session/__init__.py +12 -17
- krons/session/constraints.py +70 -0
- krons/session/exchange.py +11 -3
- krons/session/message.py +3 -1
- krons/session/registry.py +35 -0
- krons/session/session.py +165 -174
- krons/utils/__init__.py +45 -0
- krons/utils/_function_arg_parser.py +99 -0
- krons/utils/_pythonic_function_call.py +249 -0
- krons/utils/_to_list.py +9 -3
- krons/utils/_utils.py +6 -2
- krons/utils/concurrency/_async_call.py +4 -2
- krons/utils/concurrency/_errors.py +3 -1
- krons/utils/concurrency/_patterns.py +3 -1
- krons/utils/concurrency/_resource_tracker.py +6 -2
- krons/utils/display.py +257 -0
- krons/utils/fuzzy/__init__.py +6 -1
- krons/utils/fuzzy/_fuzzy_match.py +14 -8
- krons/utils/fuzzy/_string_similarity.py +3 -1
- krons/utils/fuzzy/_to_dict.py +3 -1
- krons/utils/schemas/__init__.py +26 -0
- krons/utils/schemas/_breakdown_pydantic_annotation.py +131 -0
- krons/utils/schemas/_formatter.py +72 -0
- krons/utils/schemas/_minimal_yaml.py +151 -0
- krons/utils/schemas/_typescript.py +153 -0
- krons/utils/validators/__init__.py +3 -0
- krons/utils/validators/_validate_image_url.py +56 -0
- krons/work/__init__.py +126 -0
- krons/work/engine.py +333 -0
- krons/work/form.py +305 -0
- krons/{operations → work/operations}/__init__.py +7 -4
- krons/{operations → work/operations}/builder.py +1 -1
- krons/{enforcement → work/operations}/context.py +36 -5
- krons/{operations → work/operations}/flow.py +13 -5
- krons/{operations → work/operations}/node.py +45 -43
- krons/work/operations/registry.py +103 -0
- krons/{specs → work}/phrase.py +130 -13
- krons/{enforcement → work}/policy.py +3 -3
- krons/work/report.py +268 -0
- krons/work/rules/__init__.py +47 -0
- krons/{enforcement → work/rules}/common/boolean.py +3 -1
- krons/{enforcement → work/rules}/common/choice.py +9 -3
- krons/{enforcement → work/rules}/common/number.py +3 -1
- krons/{enforcement → work/rules}/common/string.py +9 -3
- krons/{enforcement → work/rules}/rule.py +1 -1
- krons/{enforcement → work/rules}/validator.py +20 -5
- krons/{enforcement → work}/service.py +16 -7
- krons/work/worker.py +266 -0
- {krons-0.1.1.dist-info → krons-0.2.0.dist-info}/METADATA +15 -1
- krons-0.2.0.dist-info/RECORD +154 -0
- krons/enforcement/__init__.py +0 -57
- krons/operations/registry.py +0 -92
- krons/services/__init__.py +0 -81
- krons-0.1.1.dist-info/RECORD +0 -101
- /krons/{specs → core/specs}/adapters/__init__.py +0 -0
- /krons/{specs → core/specs}/adapters/_utils.py +0 -0
- /krons/{specs → core/specs}/adapters/factory.py +0 -0
- /krons/{types → core/types}/__init__.py +0 -0
- /krons/{types → core/types}/_sentinel.py +0 -0
- /krons/{types → core/types}/identity.py +0 -0
- /krons/{services → resource}/utilities/__init__.py +0 -0
- /krons/{services → resource}/utilities/header_factory.py +0 -0
- /krons/{enforcement → work/rules}/common/__init__.py +0 -0
- /krons/{enforcement → work/rules}/common/mapping.py +0 -0
- /krons/{enforcement → work/rules}/common/model.py +0 -0
- /krons/{enforcement → work/rules}/registry.py +0 -0
- {krons-0.1.1.dist-info → krons-0.2.0.dist-info}/WHEEL +0 -0
- {krons-0.1.1.dist-info → krons-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -15,8 +15,7 @@ from uuid import UUID
|
|
|
15
15
|
|
|
16
16
|
from pydantic import BaseModel, field_serializer, field_validator
|
|
17
17
|
|
|
18
|
-
from krons.
|
|
19
|
-
from krons.types import (
|
|
18
|
+
from krons.core.types import (
|
|
20
19
|
ModelConfig,
|
|
21
20
|
Params,
|
|
22
21
|
Unset,
|
|
@@ -25,7 +24,8 @@ from krons.types import (
|
|
|
25
24
|
is_unset,
|
|
26
25
|
not_sentinel,
|
|
27
26
|
)
|
|
28
|
-
from krons.types.db_types import VectorMeta, extract_kron_db_meta
|
|
27
|
+
from krons.core.types.db_types import VectorMeta, extract_kron_db_meta
|
|
28
|
+
from krons.protocols import Deserializable, Serializable, implements
|
|
29
29
|
from krons.utils import compute_hash, json_dump, now_utc
|
|
30
30
|
|
|
31
31
|
from .element import Element
|
|
@@ -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(
|
|
59
|
+
raise ValueError(
|
|
60
|
+
"embedding_dim must be specified when embedding is enabled"
|
|
61
|
+
)
|
|
60
62
|
if config.embedding_dim <= 0:
|
|
61
|
-
raise ValueError(
|
|
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
|
|
238
|
+
None
|
|
239
|
+
if config.is_sentinel_field("content_type")
|
|
240
|
+
else config.content_type
|
|
235
241
|
)
|
|
236
242
|
|
|
237
243
|
@field_serializer("content")
|
|
@@ -263,7 +269,10 @@ class Node(Element):
|
|
|
263
269
|
metadata = value.get("metadata", {})
|
|
264
270
|
kron_class = metadata.get("kron_class")
|
|
265
271
|
if kron_class:
|
|
266
|
-
if
|
|
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: (
|
|
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 = {
|
|
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
|
|
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:
|
|
@@ -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 krons.specs.catalog import AuditSpecs, ContentSpecs
|
|
708
|
-
from krons.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(
|
|
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
|
|
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(
|
|
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 krons.specs.catalog import AuditSpecs, ContentSpecs
|
|
839
|
-
from krons.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(
|
|
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 krons.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))
|
|
@@ -12,9 +12,9 @@ from uuid import UUID
|
|
|
12
12
|
from pydantic import Field, PrivateAttr, field_serializer, field_validator
|
|
13
13
|
from typing_extensions import override
|
|
14
14
|
|
|
15
|
+
from krons.core.types import Unset, UnsetType, is_unset
|
|
15
16
|
from krons.errors import ExistsError, NotFoundError
|
|
16
17
|
from krons.protocols import Containable, Deserializable, Serializable, implements
|
|
17
|
-
from krons.types import Unset, UnsetType, is_unset
|
|
18
18
|
from krons.utils import extract_types, load_type_from_string, synchronized
|
|
19
19
|
from krons.utils.concurrency import Lock as AsyncLock
|
|
20
20
|
|
|
@@ -59,7 +59,9 @@ class Pile(Element, Generic[T]):
|
|
|
59
59
|
@property
|
|
60
60
|
def progression(self) -> Progression:
|
|
61
61
|
"""Read-only copy of progression order."""
|
|
62
|
-
return Progression(
|
|
62
|
+
return Progression(
|
|
63
|
+
order=list(self._progression.order), name=self._progression.name
|
|
64
|
+
)
|
|
63
65
|
|
|
64
66
|
item_type: set[type] | None = Field(
|
|
65
67
|
default=None,
|
|
@@ -104,7 +106,9 @@ class Pile(Element, Generic[T]):
|
|
|
104
106
|
NotFoundError: If order contains UUID not in items
|
|
105
107
|
TypeError: If item type validation fails
|
|
106
108
|
"""
|
|
107
|
-
super().__init__(
|
|
109
|
+
super().__init__(
|
|
110
|
+
**{"item_type": item_type, "strict_type": strict_type, **kwargs}
|
|
111
|
+
)
|
|
108
112
|
|
|
109
113
|
if items:
|
|
110
114
|
for item in items:
|
|
@@ -128,10 +132,14 @@ class Pile(Element, Generic[T]):
|
|
|
128
132
|
def to_dict(
|
|
129
133
|
self,
|
|
130
134
|
mode: Literal["python", "json", "db"] = "python",
|
|
131
|
-
created_at_format: (
|
|
135
|
+
created_at_format: (
|
|
136
|
+
Literal["datetime", "isoformat", "timestamp"] | UnsetType
|
|
137
|
+
) = Unset,
|
|
132
138
|
meta_key: str | UnsetType = Unset,
|
|
133
139
|
item_meta_key: str | UnsetType = Unset,
|
|
134
|
-
item_created_at_format: (
|
|
140
|
+
item_created_at_format: (
|
|
141
|
+
Literal["datetime", "isoformat", "timestamp"] | UnsetType
|
|
142
|
+
) = Unset,
|
|
135
143
|
**kwargs: Any,
|
|
136
144
|
) -> dict[str, Any]:
|
|
137
145
|
"""Serialize pile with items in progression order.
|
|
@@ -350,7 +358,9 @@ class Pile(Element, Generic[T]):
|
|
|
350
358
|
raise TypeError("Cannot mix int and UUID in list/tuple indexing")
|
|
351
359
|
items = [self.get(uid) for uid in keys]
|
|
352
360
|
else:
|
|
353
|
-
raise TypeError(
|
|
361
|
+
raise TypeError(
|
|
362
|
+
f"list/tuple must contain only int or UUID, got {type(first)}"
|
|
363
|
+
)
|
|
354
364
|
|
|
355
365
|
return Pile(
|
|
356
366
|
items=items,
|
|
@@ -530,7 +540,9 @@ class Pile(Element, Generic[T]):
|
|
|
530
540
|
and item_type_data
|
|
531
541
|
and isinstance(item_type_data[0], str)
|
|
532
542
|
):
|
|
533
|
-
allowed_types = {
|
|
543
|
+
allowed_types = {
|
|
544
|
+
load_type_from_string(type_str) for type_str in item_type_data
|
|
545
|
+
}
|
|
534
546
|
else:
|
|
535
547
|
allowed_types = extract_types(item_type_data)
|
|
536
548
|
|
|
@@ -549,7 +561,9 @@ class Pile(Element, Generic[T]):
|
|
|
549
561
|
"(strict_type=True)"
|
|
550
562
|
)
|
|
551
563
|
else:
|
|
552
|
-
if not any(
|
|
564
|
+
if not any(
|
|
565
|
+
issubclass(item_type_actual, t) for t in allowed_types
|
|
566
|
+
):
|
|
553
567
|
raise TypeError(
|
|
554
568
|
f"Item type {kron_class} is not a subclass of any allowed type {allowed_types}"
|
|
555
569
|
)
|
|
@@ -77,13 +77,19 @@ class Processor:
|
|
|
77
77
|
if queue_capacity < 1:
|
|
78
78
|
raise ValueError("Queue capacity must be greater than 0.")
|
|
79
79
|
if queue_capacity > 10000:
|
|
80
|
-
raise ValueError(
|
|
80
|
+
raise ValueError(
|
|
81
|
+
"Queue capacity must be <= 10000 (prevent unbounded batches)."
|
|
82
|
+
)
|
|
81
83
|
|
|
82
84
|
# Validate capacity_refresh_time (prevent hot loop or starvation)
|
|
83
85
|
if capacity_refresh_time < 0.01:
|
|
84
|
-
raise ValueError(
|
|
86
|
+
raise ValueError(
|
|
87
|
+
"Capacity refresh time must be >= 0.01s (prevent CPU hot loop)."
|
|
88
|
+
)
|
|
85
89
|
if capacity_refresh_time > 3600:
|
|
86
|
-
raise ValueError(
|
|
90
|
+
raise ValueError(
|
|
91
|
+
"Capacity refresh time must be <= 3600s (prevent starvation)."
|
|
92
|
+
)
|
|
87
93
|
|
|
88
94
|
# Validate concurrency_limit
|
|
89
95
|
if concurrency_limit < 1:
|
|
@@ -105,7 +111,9 @@ class Processor:
|
|
|
105
111
|
self.concurrency_limit = concurrency_limit
|
|
106
112
|
|
|
107
113
|
# Priority queue: (priority, event_uuid) tuples, min-heap ordering
|
|
108
|
-
self.queue: concurrency.PriorityQueue[tuple[float, UUID]] =
|
|
114
|
+
self.queue: concurrency.PriorityQueue[tuple[float, UUID]] = (
|
|
115
|
+
concurrency.PriorityQueue()
|
|
116
|
+
)
|
|
109
117
|
|
|
110
118
|
self._available_capacity = queue_capacity
|
|
111
119
|
self._execution_mode = False
|
|
@@ -231,7 +239,9 @@ class Processor:
|
|
|
231
239
|
self._denial_counts.pop(event_id, None)
|
|
232
240
|
|
|
233
241
|
if self.executor:
|
|
234
|
-
await self.executor._update_progression(
|
|
242
|
+
await self.executor._update_progression(
|
|
243
|
+
next_event, EventStatus.PROCESSING
|
|
244
|
+
)
|
|
235
245
|
|
|
236
246
|
if next_event.streaming:
|
|
237
247
|
|
|
@@ -255,7 +265,9 @@ class Processor:
|
|
|
255
265
|
if self.executor:
|
|
256
266
|
await self.executor._update_progression(event)
|
|
257
267
|
|
|
258
|
-
tg.start_soon(
|
|
268
|
+
tg.start_soon(
|
|
269
|
+
self._with_semaphore, invoke_and_update(next_event)
|
|
270
|
+
)
|
|
259
271
|
|
|
260
272
|
events_processed += 1
|
|
261
273
|
self._available_capacity -= 1
|
|
@@ -270,7 +282,9 @@ class Processor:
|
|
|
270
282
|
|
|
271
283
|
if denial_count >= 3:
|
|
272
284
|
if self.executor:
|
|
273
|
-
await self.executor._update_progression(
|
|
285
|
+
await self.executor._update_progression(
|
|
286
|
+
next_event, EventStatus.ABORTED
|
|
287
|
+
)
|
|
274
288
|
self._denial_counts.pop(event_id, None)
|
|
275
289
|
else:
|
|
276
290
|
backoff = denial_count * 1.0
|
|
@@ -177,7 +177,9 @@ class Progression(Element):
|
|
|
177
177
|
"""Set item(s) at index. Slice assignment requires list value."""
|
|
178
178
|
if isinstance(index, slice):
|
|
179
179
|
if not isinstance(value, list):
|
|
180
|
-
raise TypeError(
|
|
180
|
+
raise TypeError(
|
|
181
|
+
f"Cannot assign {type(value).__name__} to slice, expected list"
|
|
182
|
+
)
|
|
181
183
|
new_uids = [self._coerce_id(v) for v in value]
|
|
182
184
|
self.order[index] = new_uids
|
|
183
185
|
self._rebuild_members()
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
from .adapters.factory import AdapterType, get_adapter
|
|
5
5
|
from .catalog import AuditSpecs, CommonSpecs, ContentSpecs
|
|
6
6
|
from .operable import Operable
|
|
7
|
-
from .phrase import CrudOperation, CrudPattern, Phrase, phrase
|
|
8
7
|
from .protocol import SpecAdapter
|
|
9
8
|
from .spec import CommonMeta, Spec
|
|
10
9
|
|
|
@@ -14,12 +13,8 @@ __all__ = (
|
|
|
14
13
|
"CommonMeta",
|
|
15
14
|
"CommonSpecs",
|
|
16
15
|
"ContentSpecs",
|
|
17
|
-
"CrudOperation",
|
|
18
|
-
"CrudPattern",
|
|
19
16
|
"Operable",
|
|
20
|
-
"Phrase",
|
|
21
17
|
"Spec",
|
|
22
18
|
"SpecAdapter",
|
|
23
19
|
"get_adapter",
|
|
24
|
-
"phrase",
|
|
25
20
|
)
|
|
@@ -17,14 +17,14 @@ from dataclasses import field as dc_field
|
|
|
17
17
|
from dataclasses import fields
|
|
18
18
|
from typing import TYPE_CHECKING, Any
|
|
19
19
|
|
|
20
|
-
from krons.types._sentinel import Unset, UnsetType, is_sentinel
|
|
20
|
+
from krons.core.types._sentinel import Unset, UnsetType, is_sentinel
|
|
21
21
|
|
|
22
22
|
from ..protocol import SpecAdapter
|
|
23
23
|
from ..spec import Spec
|
|
24
24
|
from ._utils import resolve_annotation_to_base_types
|
|
25
25
|
|
|
26
26
|
if TYPE_CHECKING:
|
|
27
|
-
from krons.types.base import DataClass, ModelConfig, Params
|
|
27
|
+
from krons.core.types.base import DataClass, ModelConfig, Params
|
|
28
28
|
|
|
29
29
|
from ..operable import Operable
|
|
30
30
|
|
|
@@ -71,7 +71,9 @@ def _make_validator_method(validators: dict[str, list[Any]], is_frozen: bool) ->
|
|
|
71
71
|
errors.append(ValueError(f"Validation failed for '{fname}': {e}"))
|
|
72
72
|
|
|
73
73
|
if errors:
|
|
74
|
-
raise ExceptionGroup(
|
|
74
|
+
raise ExceptionGroup(
|
|
75
|
+
f"Field validation failed for {type(self).__name__}", errors
|
|
76
|
+
)
|
|
75
77
|
|
|
76
78
|
return _validate_with_field_validators
|
|
77
79
|
|
|
@@ -149,7 +151,7 @@ class DataClassSpecAdapter(SpecAdapter[dict[str, Any]]):
|
|
|
149
151
|
Returns:
|
|
150
152
|
Dynamically created dataclass with validators wired in
|
|
151
153
|
"""
|
|
152
|
-
from krons.types.base import DataClass, Params
|
|
154
|
+
from krons.core.types.base import DataClass, Params
|
|
153
155
|
|
|
154
156
|
use_specs = op.get_specs(include=include, exclude=exclude)
|
|
155
157
|
|
|
@@ -176,7 +178,9 @@ class DataClassSpecAdapter(SpecAdapter[dict[str, Any]]):
|
|
|
176
178
|
annotations[field_name] = spec.annotation
|
|
177
179
|
|
|
178
180
|
if "default_factory" in field_kwargs:
|
|
179
|
-
class_attrs[field_name] = dc_field(
|
|
181
|
+
class_attrs[field_name] = dc_field(
|
|
182
|
+
default_factory=field_kwargs["default_factory"]
|
|
183
|
+
)
|
|
180
184
|
elif "default" in field_kwargs:
|
|
181
185
|
class_attrs[field_name] = field_kwargs["default"]
|
|
182
186
|
|
|
@@ -221,15 +225,19 @@ class DataClassSpecAdapter(SpecAdapter[dict[str, Any]]):
|
|
|
221
225
|
return instance.to_dict()
|
|
222
226
|
|
|
223
227
|
@classmethod
|
|
224
|
-
def extract_specs(
|
|
228
|
+
def extract_specs(
|
|
229
|
+
cls, structure: type[Params] | type[DataClass]
|
|
230
|
+
) -> tuple[Spec, ...]:
|
|
225
231
|
"""Extract Specs from DataClass/Params, preserving defaults and type modifiers.
|
|
226
232
|
|
|
227
233
|
Raises:
|
|
228
234
|
TypeError: If structure is not a DataClass or Params subclass
|
|
229
235
|
"""
|
|
230
|
-
from krons.types.base import DataClass, Params
|
|
236
|
+
from krons.core.types.base import DataClass, Params
|
|
231
237
|
|
|
232
|
-
if not isinstance(structure, type) or not issubclass(
|
|
238
|
+
if not isinstance(structure, type) or not issubclass(
|
|
239
|
+
structure, (DataClass, Params)
|
|
240
|
+
):
|
|
233
241
|
raise TypeError(
|
|
234
242
|
f"structure must be a DataClass or Params subclass, got {type(structure)}"
|
|
235
243
|
)
|
|
@@ -20,15 +20,21 @@ from pydantic.fields import FieldInfo
|
|
|
20
20
|
from pydantic_core import PydanticUndefined
|
|
21
21
|
from pydantic_core._pydantic_core import PydanticUndefinedType
|
|
22
22
|
|
|
23
|
-
from krons.specs.protocol import SpecAdapter
|
|
24
|
-
from krons.specs.spec import Spec
|
|
25
|
-
from krons.types._sentinel import
|
|
26
|
-
|
|
23
|
+
from krons.core.specs.protocol import SpecAdapter
|
|
24
|
+
from krons.core.specs.spec import Spec
|
|
25
|
+
from krons.core.types._sentinel import (
|
|
26
|
+
Unset,
|
|
27
|
+
UnsetType,
|
|
28
|
+
is_sentinel,
|
|
29
|
+
is_unset,
|
|
30
|
+
not_sentinel,
|
|
31
|
+
)
|
|
32
|
+
from krons.core.types.db_types import FKMeta, VectorMeta
|
|
27
33
|
|
|
28
34
|
from ._utils import resolve_annotation_to_base_types
|
|
29
35
|
|
|
30
36
|
if TYPE_CHECKING:
|
|
31
|
-
from krons.specs.operable import Operable
|
|
37
|
+
from krons.core.specs.operable import Operable
|
|
32
38
|
|
|
33
39
|
__all__ = ("PydanticSpecAdapter",)
|
|
34
40
|
|
|
@@ -27,16 +27,22 @@ from enum import StrEnum
|
|
|
27
27
|
from typing import TYPE_CHECKING, Annotated, Any, get_args, get_origin
|
|
28
28
|
from uuid import UUID
|
|
29
29
|
|
|
30
|
-
from krons.types._sentinel import Unset, UnsetType, is_sentinel
|
|
31
|
-
from krons.types.db_types import
|
|
30
|
+
from krons.core.types._sentinel import Unset, UnsetType, is_sentinel
|
|
31
|
+
from krons.core.types.db_types import (
|
|
32
|
+
FK,
|
|
33
|
+
FKMeta,
|
|
34
|
+
Vector,
|
|
35
|
+
VectorMeta,
|
|
36
|
+
extract_kron_db_meta,
|
|
37
|
+
)
|
|
32
38
|
from krons.utils.sql import validate_identifier
|
|
33
39
|
|
|
34
40
|
from ..protocol import SpecAdapter
|
|
35
41
|
from ._utils import resolve_annotation_to_base_types
|
|
36
42
|
|
|
37
43
|
if TYPE_CHECKING:
|
|
38
|
-
from krons.specs.operable import Operable
|
|
39
|
-
from krons.specs.spec import Spec
|
|
44
|
+
from krons.core.specs.operable import Operable
|
|
45
|
+
from krons.core.specs.spec import Spec
|
|
40
46
|
|
|
41
47
|
__all__ = (
|
|
42
48
|
# Enums
|
|
@@ -414,9 +420,7 @@ class UniqueConstraintSpec:
|
|
|
414
420
|
for col in self.columns:
|
|
415
421
|
validate_identifier(col, "column name")
|
|
416
422
|
cols = ", ".join(f'"{c}"' for c in self.columns)
|
|
417
|
-
return (
|
|
418
|
-
f'ALTER TABLE "{schema}"."{table_name}" ADD CONSTRAINT "{self.name}" UNIQUE ({cols});'
|
|
419
|
-
)
|
|
423
|
+
return f'ALTER TABLE "{schema}"."{table_name}" ADD CONSTRAINT "{self.name}" UNIQUE ({cols});'
|
|
420
424
|
|
|
421
425
|
|
|
422
426
|
@dataclass(frozen=True, slots=True)
|
|
@@ -468,7 +472,9 @@ class TableSpec:
|
|
|
468
472
|
col_lines = col_separator.join(col_defs)
|
|
469
473
|
|
|
470
474
|
exists_clause = "IF NOT EXISTS " if if_not_exists else ""
|
|
471
|
-
return
|
|
475
|
+
return (
|
|
476
|
+
f"CREATE TABLE {exists_clause}{self.qualified_name} (\n {col_lines}\n);"
|
|
477
|
+
)
|
|
472
478
|
|
|
473
479
|
def to_full_ddl(self) -> list[str]:
|
|
474
480
|
"""Generate all DDL statements for this table.
|
|
@@ -9,14 +9,14 @@ Pre-defined Specs for common database patterns:
|
|
|
9
9
|
- **CommonSpecs**: name, slug, status, email, phone, tenant_id, settings
|
|
10
10
|
|
|
11
11
|
Usage:
|
|
12
|
-
from krons.specs.catalog import ContentSpecs, AuditSpecs
|
|
12
|
+
from krons.core.specs.catalog import ContentSpecs, AuditSpecs
|
|
13
13
|
|
|
14
14
|
content_specs = ContentSpecs.get_specs(dim=1536)
|
|
15
15
|
audit_specs = AuditSpecs.get_specs(use_uuid=True)
|
|
16
16
|
all_specs = content_specs + audit_specs
|
|
17
17
|
|
|
18
18
|
For custom Specs, use the factories directly:
|
|
19
|
-
from krons.specs.factory import create_embedding_spec, create_content_spec
|
|
19
|
+
from krons.core.specs.factory import create_embedding_spec, create_content_spec
|
|
20
20
|
|
|
21
21
|
my_embedding = create_embedding_spec("embedding", dim=1536)
|
|
22
22
|
my_content = create_content_spec("payload", content_type=MyModel)
|
|
@@ -10,8 +10,8 @@ from uuid import UUID
|
|
|
10
10
|
|
|
11
11
|
from pydantic import BaseModel, Field
|
|
12
12
|
|
|
13
|
-
from krons.specs.operable import Operable
|
|
14
|
-
from krons.specs.spec import Spec
|
|
13
|
+
from krons.core.specs.operable import Operable
|
|
14
|
+
from krons.core.specs.spec import Spec
|
|
15
15
|
from krons.utils import now_utc
|
|
16
16
|
|
|
17
17
|
|
|
@@ -10,8 +10,8 @@ from uuid import UUID
|
|
|
10
10
|
|
|
11
11
|
from pydantic import BaseModel
|
|
12
12
|
|
|
13
|
-
from krons.specs.operable import Operable
|
|
14
|
-
from krons.specs.spec import Spec
|
|
13
|
+
from krons.core.specs.operable import Operable
|
|
14
|
+
from krons.core.specs.spec import Spec
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class CommonSpecs(BaseModel):
|
|
@@ -11,10 +11,10 @@ from uuid import UUID, uuid4
|
|
|
11
11
|
|
|
12
12
|
from pydantic import BaseModel, Field
|
|
13
13
|
|
|
14
|
-
from krons.specs.operable import Operable
|
|
15
|
-
from krons.specs.spec import Spec
|
|
16
|
-
from krons.types._sentinel import Unset, UnsetType
|
|
17
|
-
from krons.types.db_types import VectorMeta
|
|
14
|
+
from krons.core.specs.operable import Operable
|
|
15
|
+
from krons.core.specs.spec import Spec
|
|
16
|
+
from krons.core.types._sentinel import Unset, UnsetType
|
|
17
|
+
from krons.core.types.db_types import VectorMeta
|
|
18
18
|
from krons.utils import now_utc
|
|
19
19
|
|
|
20
20
|
|
|
@@ -10,9 +10,9 @@ from typing import Any
|
|
|
10
10
|
|
|
11
11
|
from pydantic import BaseModel, Field, field_validator
|
|
12
12
|
|
|
13
|
-
from krons.specs.operable import Operable
|
|
14
|
-
from krons.specs.spec import Spec
|
|
15
|
-
from krons.types.base import Enum
|
|
13
|
+
from krons.core.specs.operable import Operable
|
|
14
|
+
from krons.core.specs.spec import Spec
|
|
15
|
+
from krons.core.types.base import Enum
|
|
16
16
|
from krons.utils import now_utc
|
|
17
17
|
|
|
18
18
|
__all__ = (
|
|
@@ -8,10 +8,10 @@ from __future__ import annotations
|
|
|
8
8
|
from datetime import datetime
|
|
9
9
|
from uuid import UUID, uuid4
|
|
10
10
|
|
|
11
|
-
from krons.specs.spec import Spec, not_sentinel
|
|
12
|
-
from krons.types import UnsetType
|
|
13
|
-
from krons.types._sentinel import Unset
|
|
14
|
-
from krons.types.base import is_sentinel
|
|
11
|
+
from krons.core.specs.spec import Spec, not_sentinel
|
|
12
|
+
from krons.core.types import UnsetType
|
|
13
|
+
from krons.core.types._sentinel import Unset
|
|
14
|
+
from krons.core.types.base import is_sentinel
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def create_datetime_spec(name: str, *, use_default: bool) -> Spec:
|
|
@@ -74,7 +74,7 @@ def create_embedding_spec(
|
|
|
74
74
|
return Spec(list[float], name=name, default_factory=list)
|
|
75
75
|
return Spec(list[float], name=name)
|
|
76
76
|
|
|
77
|
-
from krons.specs.adapters.sql_ddl import Vector
|
|
77
|
+
from krons.core.specs.adapters.sql_ddl import Vector
|
|
78
78
|
|
|
79
79
|
return Spec(Vector[dim], name=name)
|
|
80
80
|
|
|
@@ -6,8 +6,14 @@ from __future__ import annotations
|
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from typing import TYPE_CHECKING, Any, Self
|
|
8
8
|
|
|
9
|
+
from krons.core.types._sentinel import (
|
|
10
|
+
MaybeUnset,
|
|
11
|
+
Unset,
|
|
12
|
+
UnsetType,
|
|
13
|
+
is_unset,
|
|
14
|
+
not_sentinel,
|
|
15
|
+
)
|
|
9
16
|
from krons.protocols import Allowable, Hashable, implements
|
|
10
|
-
from krons.types._sentinel import MaybeUnset, Unset, UnsetType, is_unset, not_sentinel
|
|
11
17
|
|
|
12
18
|
from .adapters.factory import AdapterType, get_adapter
|
|
13
19
|
from .protocol import SpecAdapter
|
|
@@ -311,4 +317,4 @@ class Operable:
|
|
|
311
317
|
Returns:
|
|
312
318
|
Dict representation of the instance
|
|
313
319
|
"""
|
|
314
|
-
return self.adapter.dump_instance(instance
|
|
320
|
+
return self.adapter.dump_instance(instance)
|
|
@@ -24,7 +24,7 @@ from __future__ import annotations
|
|
|
24
24
|
from abc import ABC, abstractmethod
|
|
25
25
|
from typing import TYPE_CHECKING, Any, Generic, TypeVar
|
|
26
26
|
|
|
27
|
-
from krons.types._sentinel import Unset, UnsetType
|
|
27
|
+
from krons.core.types._sentinel import Unset, UnsetType
|
|
28
28
|
|
|
29
29
|
if TYPE_CHECKING:
|
|
30
30
|
from .operable import Operable
|
|
@@ -126,7 +126,9 @@ class SpecAdapter(ABC, Generic[F]):
|
|
|
126
126
|
Raises:
|
|
127
127
|
NotImplementedError: If adapter doesn't support instance creation
|
|
128
128
|
"""
|
|
129
|
-
raise NotImplementedError(
|
|
129
|
+
raise NotImplementedError(
|
|
130
|
+
f"{cls.__name__} does not support instance validation"
|
|
131
|
+
)
|
|
130
132
|
|
|
131
133
|
@classmethod
|
|
132
134
|
def dump_instance(cls, instance: Any, **kwargs) -> dict[str, Any]:
|