krons 0.1.1__py3-none-any.whl → 0.2.1__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 (142) 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 +56 -74
  37. krons/core/base/__init__.py +121 -0
  38. krons/core/{broadcaster.py → base/broadcaster.py} +7 -3
  39. krons/core/{element.py → base/element.py} +13 -5
  40. krons/core/{event.py → base/event.py} +39 -6
  41. krons/core/{eventbus.py → base/eventbus.py} +3 -1
  42. krons/core/{flow.py → base/flow.py} +11 -4
  43. krons/core/{graph.py → base/graph.py} +24 -8
  44. krons/core/{node.py → base/node.py} +44 -19
  45. krons/core/{pile.py → base/pile.py} +22 -8
  46. krons/core/{processor.py → base/processor.py} +21 -7
  47. krons/core/{progression.py → base/progression.py} +3 -1
  48. krons/{specs → core/specs}/__init__.py +0 -5
  49. krons/{specs → core/specs}/adapters/dataclass_field.py +16 -8
  50. krons/{specs → core/specs}/adapters/pydantic_adapter.py +11 -5
  51. krons/{specs → core/specs}/adapters/sql_ddl.py +14 -8
  52. krons/{specs → core/specs}/catalog/__init__.py +2 -2
  53. krons/{specs → core/specs}/catalog/_audit.py +2 -2
  54. krons/{specs → core/specs}/catalog/_common.py +2 -2
  55. krons/{specs → core/specs}/catalog/_content.py +4 -4
  56. krons/{specs → core/specs}/catalog/_enforcement.py +3 -3
  57. krons/{specs → core/specs}/factory.py +5 -5
  58. krons/{specs → core/specs}/operable.py +8 -2
  59. krons/{specs → core/specs}/protocol.py +4 -2
  60. krons/{specs → core/specs}/spec.py +23 -11
  61. krons/{types → core/types}/base.py +4 -2
  62. krons/{types → core/types}/db_types.py +2 -2
  63. krons/errors.py +13 -13
  64. krons/protocols.py +9 -4
  65. krons/resource/__init__.py +89 -0
  66. krons/{services → resource}/backend.py +48 -22
  67. krons/{services → resource}/endpoint.py +28 -14
  68. krons/{services → resource}/hook.py +20 -7
  69. krons/{services → resource}/imodel.py +46 -28
  70. krons/{services → resource}/registry.py +26 -24
  71. krons/{services → resource}/utilities/rate_limited_executor.py +7 -3
  72. krons/{services → resource}/utilities/rate_limiter.py +3 -1
  73. krons/{services → resource}/utilities/resilience.py +15 -5
  74. krons/resource/utilities/token_calculator.py +185 -0
  75. krons/session/__init__.py +12 -17
  76. krons/session/constraints.py +70 -0
  77. krons/session/exchange.py +11 -3
  78. krons/session/message.py +3 -1
  79. krons/session/registry.py +35 -0
  80. krons/session/session.py +165 -174
  81. krons/utils/__init__.py +45 -0
  82. krons/utils/_function_arg_parser.py +99 -0
  83. krons/utils/_pythonic_function_call.py +249 -0
  84. krons/utils/_to_list.py +9 -3
  85. krons/utils/_utils.py +6 -2
  86. krons/utils/concurrency/_async_call.py +4 -2
  87. krons/utils/concurrency/_errors.py +3 -1
  88. krons/utils/concurrency/_patterns.py +3 -1
  89. krons/utils/concurrency/_resource_tracker.py +6 -2
  90. krons/utils/display.py +257 -0
  91. krons/utils/fuzzy/__init__.py +6 -1
  92. krons/utils/fuzzy/_fuzzy_match.py +14 -8
  93. krons/utils/fuzzy/_string_similarity.py +3 -1
  94. krons/utils/fuzzy/_to_dict.py +3 -1
  95. krons/utils/schemas/__init__.py +26 -0
  96. krons/utils/schemas/_breakdown_pydantic_annotation.py +131 -0
  97. krons/utils/schemas/_formatter.py +72 -0
  98. krons/utils/schemas/_minimal_yaml.py +151 -0
  99. krons/utils/schemas/_typescript.py +153 -0
  100. krons/utils/validators/__init__.py +3 -0
  101. krons/utils/validators/_validate_image_url.py +56 -0
  102. krons/work/__init__.py +115 -0
  103. krons/work/engine.py +333 -0
  104. krons/work/form.py +242 -0
  105. krons/{operations → work/operations}/__init__.py +7 -4
  106. krons/{operations → work/operations}/builder.py +1 -1
  107. krons/{enforcement → work/operations}/context.py +36 -5
  108. krons/{operations → work/operations}/flow.py +13 -5
  109. krons/{operations → work/operations}/node.py +45 -43
  110. krons/work/operations/registry.py +103 -0
  111. krons/work/report.py +268 -0
  112. krons/work/rules/__init__.py +47 -0
  113. krons/{enforcement → work/rules}/common/boolean.py +3 -1
  114. krons/{enforcement → work/rules}/common/choice.py +9 -3
  115. krons/{enforcement → work/rules}/common/number.py +3 -1
  116. krons/{enforcement → work/rules}/common/string.py +9 -3
  117. krons/{enforcement → work/rules}/rule.py +1 -1
  118. krons/{enforcement → work/rules}/validator.py +20 -5
  119. krons/work/worker.py +266 -0
  120. {krons-0.1.1.dist-info → krons-0.2.1.dist-info}/METADATA +15 -1
  121. krons-0.2.1.dist-info/RECORD +151 -0
  122. krons/enforcement/__init__.py +0 -57
  123. krons/enforcement/policy.py +0 -80
  124. krons/enforcement/service.py +0 -370
  125. krons/operations/registry.py +0 -92
  126. krons/services/__init__.py +0 -81
  127. krons/specs/phrase.py +0 -405
  128. krons-0.1.1.dist-info/RECORD +0 -101
  129. /krons/{specs → core/specs}/adapters/__init__.py +0 -0
  130. /krons/{specs → core/specs}/adapters/_utils.py +0 -0
  131. /krons/{specs → core/specs}/adapters/factory.py +0 -0
  132. /krons/{types → core/types}/__init__.py +0 -0
  133. /krons/{types → core/types}/_sentinel.py +0 -0
  134. /krons/{types → core/types}/identity.py +0 -0
  135. /krons/{services → resource}/utilities/__init__.py +0 -0
  136. /krons/{services → resource}/utilities/header_factory.py +0 -0
  137. /krons/{enforcement → work/rules}/common/__init__.py +0 -0
  138. /krons/{enforcement → work/rules}/common/mapping.py +0 -0
  139. /krons/{enforcement → work/rules}/common/model.py +0 -0
  140. /krons/{enforcement → work/rules}/registry.py +0 -0
  141. {krons-0.1.1.dist-info → krons-0.2.1.dist-info}/WHEEL +0 -0
  142. {krons-0.1.1.dist-info → krons-0.2.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,47 @@
1
+ # Copyright (c) 2025 - 2026, HaiyangLi <quantocean.li at gmail dot com>
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """Rules module: validation rules with auto-correction support.
5
+
6
+ Core exports:
7
+ - Rule, RuleParams, RuleQualifier: Base rule classes
8
+ - ValidationError: Validation failure exception
9
+ - Validator: Spec-aware validation orchestrator
10
+ - RuleRegistry: Type-to-rule mapping with inheritance
11
+ - Common rules: StringRule, NumberRule, BooleanRule, ChoiceRule, MappingRule, BaseModelRule
12
+ """
13
+
14
+ from krons.errors import ValidationError
15
+
16
+ from .common import (
17
+ BaseModelRule,
18
+ BooleanRule,
19
+ ChoiceRule,
20
+ MappingRule,
21
+ NumberRule,
22
+ StringRule,
23
+ )
24
+ from .registry import RuleRegistry, get_default_registry, reset_default_registry
25
+ from .rule import Rule, RuleParams, RuleQualifier
26
+ from .validator import Validator
27
+
28
+ __all__ = (
29
+ # Base classes
30
+ "Rule",
31
+ "RuleParams",
32
+ "RuleQualifier",
33
+ "ValidationError",
34
+ # Validator
35
+ "Validator",
36
+ # Registry
37
+ "RuleRegistry",
38
+ "get_default_registry",
39
+ "reset_default_registry",
40
+ # Common rules
41
+ "BaseModelRule",
42
+ "BooleanRule",
43
+ "ChoiceRule",
44
+ "MappingRule",
45
+ "NumberRule",
46
+ "StringRule",
47
+ )
@@ -50,7 +50,9 @@ class BooleanRule(Rule):
50
50
  ValueError: If not a boolean
51
51
  """
52
52
  if not isinstance(v, bool):
53
- raise ValueError(f"Invalid boolean value: expected bool, got {type(v).__name__}")
53
+ raise ValueError(
54
+ f"Invalid boolean value: expected bool, got {type(v).__name__}"
55
+ )
54
56
 
55
57
  async def perform_fix(self, v: Any, _t: type) -> Any:
56
58
  """Attempt to convert value to boolean.
@@ -58,7 +58,9 @@ class ChoiceRule(Rule):
58
58
  self.case_sensitive = case_sensitive
59
59
 
60
60
  if not case_sensitive:
61
- self._lower_map = {str(c).lower(): c for c in self.choices if isinstance(c, str)}
61
+ self._lower_map = {
62
+ str(c).lower(): c for c in self.choices if isinstance(c, str)
63
+ }
62
64
 
63
65
  async def validate(self, v: Any, t: type, **kw) -> None:
64
66
  """Validate that value is in allowed choices (exact match only).
@@ -72,7 +74,9 @@ class ChoiceRule(Rule):
72
74
  if v in self.choices:
73
75
  return
74
76
 
75
- raise ValueError(f"Invalid choice: {v} not in {sorted(str(c) for c in self.choices)}")
77
+ raise ValueError(
78
+ f"Invalid choice: {v} not in {sorted(str(c) for c in self.choices)}"
79
+ )
76
80
 
77
81
  async def perform_fix(self, v: Any, _t: type) -> Any:
78
82
  """Attempt to fix value to closest choice.
@@ -94,4 +98,6 @@ class ChoiceRule(Rule):
94
98
  if v_lower in self._lower_map:
95
99
  return self._lower_map[v_lower]
96
100
 
97
- raise ValueError(f"Cannot fix choice: {v} not in {sorted(str(c) for c in self.choices)}")
101
+ raise ValueError(
102
+ f"Cannot fix choice: {v} not in {sorted(str(c) for c in self.choices)}"
103
+ )
@@ -66,7 +66,9 @@ class NumberRule(Rule):
66
66
  ValueError: If not a number or constraints violated
67
67
  """
68
68
  if not isinstance(v, (int, float)):
69
- raise ValueError(f"Invalid number value: expected int or float, got {type(v).__name__}")
69
+ raise ValueError(
70
+ f"Invalid number value: expected int or float, got {type(v).__name__}"
71
+ )
70
72
 
71
73
  if self.ge is not None and v < self.ge:
72
74
  raise ValueError(f"Number too small: {v} < {self.ge}")
@@ -103,14 +103,18 @@ class StringRule(Rule):
103
103
  ValueError: If not a string or constraints violated
104
104
  """
105
105
  if not isinstance(v, str):
106
- raise ValueError(f"Invalid string value: expected str, got {type(v).__name__}")
106
+ raise ValueError(
107
+ f"Invalid string value: expected str, got {type(v).__name__}"
108
+ )
107
109
 
108
110
  if self.min_length is not None and len(v) < self.min_length:
109
111
  raise ValueError(
110
112
  f"String too short: got {len(v)} characters, minimum {self.min_length}"
111
113
  )
112
114
  if self.max_length is not None and len(v) > self.max_length:
113
- raise ValueError(f"String too long: got {len(v)} characters, maximum {self.max_length}")
115
+ raise ValueError(
116
+ f"String too long: got {len(v)} characters, maximum {self.max_length}"
117
+ )
114
118
 
115
119
  if self._compiled_pattern is not None:
116
120
  if len(v) > self.regex_max_input_length:
@@ -119,7 +123,9 @@ class StringRule(Rule):
119
123
  f"maximum {self.regex_max_input_length}"
120
124
  )
121
125
  if not self._compiled_pattern.match(v):
122
- raise ValueError(f"String does not match required pattern: {self.pattern}")
126
+ raise ValueError(
127
+ f"String does not match required pattern: {self.pattern}"
128
+ )
123
129
 
124
130
  async def perform_fix(self, v: Any, t: type) -> Any:
125
131
  """Attempt to convert value to string and re-validate.
@@ -8,8 +8,8 @@ from dataclasses import dataclass, field
8
8
  from enum import IntEnum, auto
9
9
  from typing import Any
10
10
 
11
+ from krons.core.types import Params
11
12
  from krons.errors import ValidationError
12
- from krons.types import Params
13
13
 
14
14
  __all__ = ("Rule", "RuleParams", "RuleQualifier", "ValidationError")
15
15
 
@@ -7,14 +7,16 @@ from collections import deque
7
7
  from datetime import datetime
8
8
  from typing import TYPE_CHECKING, Any, ClassVar
9
9
 
10
- from krons.types import is_sentinel
10
+ from krons.core.types import is_sentinel, not_sentinel
11
11
  from krons.utils.concurrency import is_coro_func
12
12
 
13
13
  from .registry import RuleRegistry, get_default_registry
14
14
  from .rule import Rule, ValidationError
15
15
 
16
16
  if TYPE_CHECKING:
17
- from krons.specs import Operable, Spec
17
+ from pydantic import BaseModel
18
+
19
+ from krons.core.specs import Operable, Spec
18
20
 
19
21
  __all__ = ("Validator",)
20
22
 
@@ -29,7 +31,9 @@ class Validator:
29
31
  ):
30
32
  self.registry = registry or get_default_registry()
31
33
  max_entries = (
32
- max_log_entries if max_log_entries is not None else self.DEFAULT_MAX_LOG_ENTRIES
34
+ max_log_entries
35
+ if max_log_entries is not None
36
+ else self.DEFAULT_MAX_LOG_ENTRIES
33
37
  )
34
38
  self.validation_log: deque[dict[str, Any]] = deque(
35
39
  maxlen=max_entries if max_entries > 0 else None
@@ -141,7 +145,9 @@ class Validator:
141
145
  raise ValidationError(error_msg)
142
146
  else:
143
147
  try:
144
- value = await rule.invoke(field_name, value, spec.base_type, auto_fix=auto_fix)
148
+ value = await rule.invoke(
149
+ field_name, value, spec.base_type, auto_fix=auto_fix
150
+ )
145
151
  except Exception as e:
146
152
  self.log_validation_error(field_name, value, str(e))
147
153
  raise
@@ -171,14 +177,20 @@ class Validator:
171
177
 
172
178
  return value
173
179
 
174
- async def validate_operable(
180
+ async def validate(
175
181
  self,
176
182
  data: dict[str, Any],
177
183
  operable: Operable,
178
184
  capabilities: set[str] | None = None,
179
185
  auto_fix: bool = True,
180
186
  strict: bool = True,
187
+ structure: type[BaseModel] | None = None,
181
188
  ) -> dict[str, Any]:
189
+ if not_sentinel(capabilities, {"none"}) and not capabilities.issubset(
190
+ operable.allowed()
191
+ ):
192
+ raise ValidationError("Capabilities exceed operable's allowed set")
193
+
182
194
  capabilities = capabilities or operable.allowed()
183
195
  validated: dict[str, Any] = {}
184
196
 
@@ -195,4 +207,7 @@ class Validator:
195
207
  spec, value, auto_fix=auto_fix, strict=strict
196
208
  )
197
209
 
210
+ if structure is not None:
211
+ validated = operable.validate_instance(structure, validated)
212
+
198
213
  return validated
krons/work/worker.py ADDED
@@ -0,0 +1,266 @@
1
+ # Copyright (c) 2025 - 2026, HaiyangLi <quantocean.li at gmail dot com>
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """Worker - Declarative workflow definition via decorated methods.
5
+
6
+ A Worker defines workflows through:
7
+ - @work: Typed operations with assignment DSL (inputs -> outputs)
8
+ - @worklink: Conditional edges between work methods
9
+
10
+ Example:
11
+ class FileCoder(Worker):
12
+ @work(assignment="instruction, context -> code", capacity=2)
13
+ async def write_code(self, form_id, **kwargs):
14
+ result = await llm.chat(**kwargs)
15
+ return form_id, result.code
16
+
17
+ @worklink(from_="write_code", to_="execute_code")
18
+ async def write_to_execute(self, from_result):
19
+ form_id, code = from_result
20
+ return {"form_id": form_id, "code": code}
21
+
22
+ engine = WorkerEngine(worker=FileCoder())
23
+ await engine.add_task(form=my_form, task_function="write_code")
24
+ await engine.execute()
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import functools
30
+ from dataclasses import dataclass
31
+ from typing import TYPE_CHECKING, Awaitable, Callable
32
+ from uuid import UUID
33
+
34
+ if TYPE_CHECKING:
35
+ from .form import Form
36
+
37
+ __all__ = (
38
+ "Worker",
39
+ "WorkConfig",
40
+ "WorkLink",
41
+ "work",
42
+ "worklink",
43
+ )
44
+
45
+
46
+ @dataclass
47
+ class WorkConfig:
48
+ """Configuration for a @work decorated method.
49
+
50
+ Attributes:
51
+ assignment: DSL string 'inputs -> outputs' for typed I/O
52
+ form_param_key: Parameter name that receives form ID
53
+ capacity: Max concurrent executions (rate limiting)
54
+ refresh_time: Seconds between capacity resets
55
+ timeout: Max execution time in seconds
56
+ """
57
+
58
+ assignment: str = ""
59
+ form_param_key: str = ""
60
+ capacity: int = 1
61
+ refresh_time: float = 0.1
62
+ timeout: float | None = None
63
+
64
+
65
+ @dataclass
66
+ class WorkLink:
67
+ """Edge definition between work methods.
68
+
69
+ Attributes:
70
+ from_: Source method name
71
+ to_: Target method name
72
+ handler_name: Name of the handler method on the Worker
73
+ """
74
+
75
+ from_: str
76
+ to_: str
77
+ handler_name: str
78
+
79
+
80
+ class Worker:
81
+ """Base class for declarative workflow definition.
82
+
83
+ Subclass and decorate methods with @work and @worklink to define workflows.
84
+ Worker maintains form storage and tracks work method metadata.
85
+
86
+ Attributes:
87
+ name: Worker name (default: class name)
88
+ forms: Dict mapping form IDs to Form instances
89
+ _work_methods: Registry of @work decorated methods
90
+ _work_links: Registry of @worklink edges
91
+ _stopped: Stop flag for execution
92
+
93
+ Example:
94
+ class MyCoder(Worker):
95
+ name = "coder"
96
+
97
+ @work(assignment="task -> plan", capacity=2)
98
+ async def plan(self, task_name, **kwargs):
99
+ ...
100
+ return task_name
101
+
102
+ @work(assignment="plan -> code", form_param_key="task_name")
103
+ async def implement(self, task_name, **kwargs):
104
+ ...
105
+
106
+ @worklink(from_="plan", to_="implement")
107
+ async def plan_to_implement(self, from_result):
108
+ return {"task_name": from_result}
109
+ """
110
+
111
+ name: str = "worker"
112
+
113
+ def __init__(self) -> None:
114
+ """Initialize worker state."""
115
+ # Form storage: form_id -> Form
116
+ self.forms: dict[str | UUID, Form] = {}
117
+
118
+ # Collect @work methods from class
119
+ self._work_methods: dict[str, tuple[Callable, WorkConfig]] = {}
120
+ self._work_links: list[WorkLink] = []
121
+ self._stopped = False
122
+
123
+ self._collect_work_metadata()
124
+
125
+ def _collect_work_metadata(self) -> None:
126
+ """Scan class for @work and @worklink decorated methods."""
127
+ for name in dir(self):
128
+ if name.startswith("_"):
129
+ continue
130
+
131
+ attr = getattr(self, name, None)
132
+ if attr is None:
133
+ continue
134
+
135
+ # Check for @work decorator
136
+ if hasattr(attr, "_work_config"):
137
+ config: WorkConfig = attr._work_config
138
+ self._work_methods[name] = (attr, config)
139
+
140
+ # Check for @worklink decorator
141
+ if hasattr(attr, "_worklink_from") and hasattr(attr, "_worklink_to"):
142
+ link = WorkLink(
143
+ from_=attr._worklink_from,
144
+ to_=attr._worklink_to,
145
+ handler_name=name,
146
+ )
147
+ self._work_links.append(link)
148
+
149
+ def get_links_from(self, method_name: str) -> list[WorkLink]:
150
+ """Get all outgoing links from a method."""
151
+ return [link for link in self._work_links if link.from_ == method_name]
152
+
153
+ def get_links_to(self, method_name: str) -> list[WorkLink]:
154
+ """Get all incoming links to a method."""
155
+ return [link for link in self._work_links if link.to_ == method_name]
156
+
157
+ async def stop(self) -> None:
158
+ """Signal worker to stop processing."""
159
+ self._stopped = True
160
+
161
+ async def start(self) -> None:
162
+ """Clear stop flag to allow processing."""
163
+ self._stopped = False
164
+
165
+ def is_stopped(self) -> bool:
166
+ """Check if worker is stopped."""
167
+ return self._stopped
168
+
169
+ def __repr__(self) -> str:
170
+ methods = list(self._work_methods.keys())
171
+ links = len(self._work_links)
172
+ forms = len(self.forms)
173
+ return f"{self.__class__.__name__}(methods={methods}, links={links}, forms={forms})"
174
+
175
+
176
+ def work(
177
+ assignment: str = "",
178
+ *,
179
+ form_param_key: str = "",
180
+ capacity: int = 1,
181
+ refresh_time: float = 0.1,
182
+ timeout: float | None = None,
183
+ ) -> Callable[[Callable[..., Awaitable]], Callable[..., Awaitable]]:
184
+ """Decorator for typed work methods.
185
+
186
+ Args:
187
+ assignment: DSL string 'inputs -> outputs' defining typed I/O.
188
+ Used for form field binding when form_param_key is set.
189
+ form_param_key: Parameter name that receives form ID.
190
+ If set, the engine will bind form fields to kwargs.
191
+ capacity: Max concurrent executions (for rate limiting).
192
+ refresh_time: Seconds between capacity resets.
193
+ timeout: Max execution time in seconds.
194
+
195
+ Returns:
196
+ Decorator that attaches WorkConfig to the method.
197
+
198
+ Example:
199
+ @work(assignment="context, instruction -> code", form_param_key="form_id")
200
+ async def write_code(self, form_id, **kwargs):
201
+ # kwargs contains context, instruction from form
202
+ result = await llm.chat(**kwargs)
203
+ return form_id, result.code
204
+ """
205
+
206
+ def decorator(func: Callable[..., Awaitable]) -> Callable[..., Awaitable]:
207
+ config = WorkConfig(
208
+ assignment=assignment,
209
+ form_param_key=form_param_key,
210
+ capacity=capacity,
211
+ refresh_time=refresh_time,
212
+ timeout=timeout,
213
+ )
214
+
215
+ @functools.wraps(func)
216
+ async def wrapper(*args, **kwargs):
217
+ return await func(*args, **kwargs)
218
+
219
+ wrapper._work_config = config # type: ignore[attr-defined]
220
+ return wrapper
221
+
222
+ return decorator
223
+
224
+
225
+ def worklink(
226
+ from_: str,
227
+ to_: str,
228
+ ) -> Callable[[Callable[..., Awaitable]], Callable[..., Awaitable]]:
229
+ """Decorator for conditional edges between work methods.
230
+
231
+ The decorated function receives the result from the 'from_' method
232
+ and returns kwargs dict for the 'to_' method. Return None to skip
233
+ the edge (conditional routing).
234
+
235
+ Args:
236
+ from_: Source method name
237
+ to_: Target method name
238
+
239
+ Returns:
240
+ Decorator that attaches WorkLink info to the method.
241
+
242
+ Example:
243
+ @worklink(from_="write_code", to_="execute_code")
244
+ async def write_to_execute(self, from_result):
245
+ form_id, code = from_result
246
+ return {"form_id": form_id, "code": code}
247
+
248
+ @worklink(from_="execute_code", to_="debug_code")
249
+ async def execute_to_debug(self, from_result):
250
+ form_id, error = from_result
251
+ if error is not None: # Conditional edge
252
+ return {"form_id": form_id, "error": error}
253
+ # Return None = edge not taken
254
+ """
255
+
256
+ def decorator(func: Callable[..., Awaitable]) -> Callable[..., Awaitable]:
257
+ @functools.wraps(func)
258
+ async def wrapper(*args, **kwargs):
259
+ return await func(*args, **kwargs)
260
+
261
+ # Store link info - handler_name will be set during _collect_work_metadata
262
+ wrapper._worklink_from = from_ # type: ignore[attr-defined]
263
+ wrapper._worklink_to = to_ # type: ignore[attr-defined]
264
+ return wrapper
265
+
266
+ return decorator
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: krons
3
- Version: 0.1.1
3
+ Version: 0.2.1
4
4
  Summary: Spec-based composable framework for building type-safe systems
5
5
  Project-URL: Homepage, https://github.com/khive-ai/krons
6
6
  Project-URL: Repository, https://github.com/khive-ai/krons
@@ -22,7 +22,21 @@ Requires-Dist: anyio>=4.10.0
22
22
  Requires-Dist: httpx>=0.26.0
23
23
  Requires-Dist: orjson>=3.10.0
24
24
  Requires-Dist: pydantic>=2.10.0
25
+ Requires-Dist: pyyaml>=6.0.3
25
26
  Requires-Dist: rapidfuzz>=3.10.0
27
+ Provides-Extra: agent
28
+ Requires-Dist: fastmcp>=2.14.4; extra == 'agent'
29
+ Requires-Dist: tiktoken>=0.11.0; extra == 'agent'
30
+ Provides-Extra: all
31
+ Requires-Dist: fastmcp>=2.14.4; extra == 'all'
32
+ Requires-Dist: rich>=13.0; extra == 'all'
33
+ Requires-Dist: tiktoken>=0.11.0; extra == 'all'
34
+ Provides-Extra: display
35
+ Requires-Dist: rich>=13.0; extra == 'display'
36
+ Provides-Extra: fastmcp
37
+ Requires-Dist: fastmcp>=2.14.4; extra == 'fastmcp'
38
+ Provides-Extra: tiktoken
39
+ Requires-Dist: tiktoken>=0.11.0; extra == 'tiktoken'
26
40
  Description-Content-Type: text/markdown
27
41
 
28
42
  # krons
@@ -0,0 +1,151 @@
1
+ krons/__init__.py,sha256=4q7g_NShAZ3SXLVTZ4H8vFSCXpps-3tjCs7qg6WkQPI,1359
2
+ krons/errors.py,sha256=nRWkWZmokOlp3hquC2dBe-gowdVIHllENiD0KxU3m8U,4051
3
+ krons/protocols.py,sha256=xYJUHdXbFHYtjy3rcEt8ttIngzigEve7tYa-8G35v7k,15333
4
+ krons/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ krons/agent/__init__.py,sha256=7AfP-1BHYosM5MiqcSOpoGF0y-bQ2OKN8oQePR2o98Y,4581
6
+ krons/agent/tool.py,sha256=JDlj_QCMw6ILKu2NkKLOoywiIlIIQwVfS-A_HT88OTg,8573
7
+ krons/agent/mcps/__init__.py,sha256=B3RYDI0XaDaECpgxqFp5bBIdCFRGPyzbCDpQeljY0P0,447
8
+ krons/agent/mcps/loader.py,sha256=SAw3ZLvBYHfT1bklLI7i15mnqwAA_aZtnJRR9WEfKl4,10554
9
+ krons/agent/mcps/wrapper.py,sha256=tW4aXzMMBqh4BtcuEaLxYqLxttGRJ06UQSjWmqdsGfI,28169
10
+ krons/agent/message/__init__.py,sha256=vumj0lHyraEoshw0tx8yWt5fyUUuAC3qVBiSLQuq-9A,499
11
+ krons/agent/message/action.py,sha256=ynONthCd78gWzl3dbQughJOGd-Iys6lbOzzYwuLXGWU,2047
12
+ krons/agent/message/assistant.py,sha256=V75490V9mQLKbBkH9cYAmnva2-Q3hwCAj_4flLUCvOE,1459
13
+ krons/agent/message/common.py,sha256=j9rpVoZhH6FV9JLVv6K-IgRJqPGnT5Vcw80UsEWv5ME,1282
14
+ krons/agent/message/instruction.py,sha256=jouxli_yDwRcLJhXs8f9cR0MdreW3pNRDIw2ha7K4uE,4793
15
+ krons/agent/message/prepare_msg.py,sha256=85XB-S2qxzXAUxn0cu46LNbWq0JL6w1IO1soJ3ci0Y4,7017
16
+ krons/agent/message/role.py,sha256=wKY5gIov4yHuvHiXEGFJ8YIaDR2YcmyH83JnDLgnFtI,1397
17
+ krons/agent/message/system.py,sha256=vCwFrYtM8K-5bVvl-mo0xW-Q_1AWqIAOgwDnNQ41DeA,1884
18
+ krons/agent/operations/__init__.py,sha256=UqKGE99xyYBGHnEHPVLq8Bf7dvs195gAnpMH3zpjNNA,2095
19
+ krons/agent/operations/act.py,sha256=WeUiVMcYOQN2EYLEvQU17HcqoQkcWzuqIgJjGP4AWXM,3606
20
+ krons/agent/operations/generate.py,sha256=oNXAwUKw3rcPlGkgq8zhggnNJPVcAzIIUhYIca23Ro0,5447
21
+ krons/agent/operations/llm_reparse.py,sha256=kF_KglZu_R4RVTpl-1lheeHGfvsK1bvAJidDdpW1iEA,2696
22
+ krons/agent/operations/operate.py,sha256=zOIPRoLYV0sXeSTmCvuD5mlwArimP456a7GEpxljjQ8,8966
23
+ krons/agent/operations/parse.py,sha256=HN7gnRm5_6gbabah3enaL280FVywQqIK5N6oOZG8NGQ,8261
24
+ krons/agent/operations/react.py,sha256=f5_5IVf8oi2TD8QXT53DefTxd1zO0nMmJ07SrL_28NQ,9468
25
+ krons/agent/operations/specs.py,sha256=UzH3A8VivQkUZdjDq78T_pmBQlY-BJafOVAKvpS0go8,7407
26
+ krons/agent/operations/structure.py,sha256=8HN5_dA4sYZFUbVPffEGmcamm9VUxFveAko59sgWydI,5250
27
+ krons/agent/operations/utils.py,sha256=goot2IAmrxTOoJa1kn2kfvv03i8mb02ifOKfBJMv_Qw,2335
28
+ krons/agent/providers/__init__.py,sha256=XJtszdh8f5sfAR0eQipKplBSLdKwtL0F7uQPmah8g6s,555
29
+ krons/agent/providers/anthropic_messages.py,sha256=x0oBKQa-T3zu8TyZ50Rm1fvgkY8ukzvAZJc1HVugBh0,4388
30
+ krons/agent/providers/claude_code.py,sha256=nTKJkK1gGUdCgXpzRXiGtC840a5qm0tKBeakqlPQ9Kw,9881
31
+ krons/agent/providers/gemini.py,sha256=TCJkk9iSnnkPdDuTlAB5yOphNYUPkPGKVYkrja9zcuI,9059
32
+ krons/agent/providers/match.py,sha256=3kiW-hyfnF6UgBYD7LG4KFhFKXbWpDdtDQ71-9t5wIQ,2251
33
+ krons/agent/providers/oai_chat.py,sha256=-97XBJ6WQT9OsjKSrV5s3or-WhBfUjDHomGiBYjCeVY,5463
34
+ krons/agent/third_party/__init__.py,sha256=gR454Rk5b1Bv3QibE-822UcoSWhpLCLtJae028QGtN4,110
35
+ krons/agent/third_party/anthropic_models.py,sha256=rd2Skh_Xx1S2G1JXi2YFb1j6SHm62e7brNBq9CZPAgI,3968
36
+ krons/agent/third_party/claude_code.py,sha256=NBYMsdPheb0PqGgTO2_aUAR2XoGDsPUwsB7Nd9PjipA,23758
37
+ krons/agent/third_party/gemini_models.py,sha256=2dag2WbSTKLBd8t0F7t_TXCcl7aqsTFgDI6XLkhsZwM,17416
38
+ krons/agent/third_party/openai_models.py,sha256=L-GkJ-bBW5zwPbgCWEYHMNqRiZfFTAUJE96AVZoDAkQ,8040
39
+ krons/core/__init__.py,sha256=HG0wBaNCbH3xGDRNX3657g-hZo4Isu2MKaXeUXmXqa0,3496
40
+ krons/core/base/__init__.py,sha256=Edl_HH2F1l0SMHMzuEPU_IFjkigRR1vtqVXcPm-mr1o,3614
41
+ krons/core/base/broadcaster.py,sha256=Es__WfL-j2h5NS6aU18fgaFkZsJIL-zDe86Qvlea-io,4013
42
+ krons/core/base/element.py,sha256=LqBbmPttXoDAFy56JzWhSJ4sBw_LW7hMGxZUog85N8g,7642
43
+ krons/core/base/event.py,sha256=iZQ65Xg3a8eaCEl4Hca-pxPNtckhrHkydEqYyppbuo0,11524
44
+ krons/core/base/eventbus.py,sha256=z45ORtGey5A1N9hdyEPOhlX1whaTTdqdk7g3XEDL-_8,3761
45
+ krons/core/base/flow.py,sha256=eqsYm8QO7bD5Y9VYnlpHFWpaUCY0rCeKGrDrFUyBVA4,12364
46
+ krons/core/base/graph.py,sha256=FiTMArxuz6At3oYPEoamES3FtBM-AM6QnA7EQ8G18mo,15972
47
+ krons/core/base/node.py,sha256=0_-R6uNe6-LF3adQRZowSIICdy6ep3cQIQRdF0oosoQ,34699
48
+ krons/core/base/pile.py,sha256=ZKvnX3BjMsrH989TLNZII1dDWv4yPhSRzLKXx2yzyKQ,21108
49
+ krons/core/base/processor.py,sha256=GrdNZa5YD0tXle847ttnxuWe4Ego1MhULzWHmSz5RqQ,18768
50
+ krons/core/base/progression.py,sha256=srcrmp_zALgg7II5TJsuMM9DdNJszgTxLxvES0WBOTE,9844
51
+ krons/core/specs/__init__.py,sha256=AKFEXqLmJpW__Y6xorJ1Vbp7AZ46kIs0157STVFGVWk,500
52
+ krons/core/specs/factory.py,sha256=TsRZIoWuz0sLYUGfQ_X5Gff2oBll-quk-kOnpX5_yB0,3362
53
+ krons/core/specs/operable.py,sha256=zO15i56_UzTTuM3yg_-EMtQY1Equ_WBi3rUCoCKrYck,11067
54
+ krons/core/specs/protocol.py,sha256=Se-Cow7de4hF4vkvw5qYj14c37JJ-4aJSDIvJHFyst8,4392
55
+ krons/core/specs/spec.py,sha256=QaVWN3ZrX8l1x15xXtfpNGzSIV01X0H3jy9Mjv9vT8Y,18706
56
+ krons/core/specs/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
+ krons/core/specs/adapters/_utils.py,sha256=ChWH-C2LRXaXiWM3n7P0DZ8Oci5dSDq59DIJtHx2h-g,1365
58
+ krons/core/specs/adapters/dataclass_field.py,sha256=thhgC3iQsrkfMkKzB4XmDbwh4fpLYocdL0KvY_zxbzo,8802
59
+ krons/core/specs/adapters/factory.py,sha256=_LoSMCtHNkK5kNL5LUDjHvJJ2BtDHAaW1gly5LpNPXQ,1572
60
+ krons/core/specs/adapters/pydantic_adapter.py,sha256=UxnOtJdeIyJEPBBjSACWE-JVc0yWxHSepz5i2SZhS6A,10410
61
+ krons/core/specs/adapters/sql_ddl.py,sha256=NTttvx16X926qeQNWxqdoLlToeiMwufpKEPWNdAXHg0,31377
62
+ krons/core/specs/catalog/__init__.py,sha256=TwS7hvGxa4-2Usu5rKGlhmUS0hhBlRzVNFNKDE7q8eU,1231
63
+ krons/core/specs/catalog/_audit.py,sha256=g-L4eSJw3H0hfejex5LwsnepaGyTUPrL8JbPU7lPycY,1260
64
+ krons/core/specs/catalog/_common.py,sha256=ItOPWV1fEeaz6ss5XNZYUetp204saIs7s5Bl0Jt8AOQ,1207
65
+ krons/core/specs/catalog/_content.py,sha256=pQAbgjoObZcAQLe8Wg-_sqQ8VYZUGsRQJnjuiCjH-p0,1972
66
+ krons/core/specs/catalog/_enforcement.py,sha256=JlKF_qGc-Xk7E_ebvq6lQs19iRBAWk2TOe1Qm7XMf1k,2126
67
+ krons/core/types/__init__.py,sha256=pJ6kuop7vSLZp5jMBs-CQ7CHRjvUuO8kHhpZ34KM85I,1049
68
+ krons/core/types/_sentinel.py,sha256=QJGyRAYNkJ2ylw6dhxPHJNgmxqBdorBd-xx7pNrUykw,8855
69
+ krons/core/types/base.py,sha256=Un3MrPGuyns9SgPczJ5vbQExemTO-9B2Eks87NDd--A,12330
70
+ krons/core/types/db_types.py,sha256=3ZaAXJe4dartmF-IyKr59NVlL2EbGpx7AtVqqbrIXIk,8136
71
+ krons/core/types/identity.py,sha256=Ccb5oJArC1PEzdzNPlLi_oK87uKx7BFevX9rvY9y-14,1949
72
+ krons/resource/__init__.py,sha256=h83vOSa6zXXMRdfxch7yg7kGy6Pe_6c0wWANByATbZ8,2783
73
+ krons/resource/backend.py,sha256=mW1kkAZrUwnBZeMOY_0I_egpTKpuNyIs5Mps4r2cOxk,10979
74
+ krons/resource/endpoint.py,sha256=X_Yt5KLXKN5Wwc75MX0c5gPyE_Dlv16rIfm8CKd1xYE,22253
75
+ krons/resource/hook.py,sha256=XQujjDwBZa2vCktP6Ey0QFfFFNEd6igc1qUR5-tfeQU,17115
76
+ krons/resource/imodel.py,sha256=_BOfbLk5JNoKAnjf3k67E6O2Qef4fsKa1hprw1C4L1Q,18595
77
+ krons/resource/registry.py,sha256=Vqe8uUtMMtxu4NgU5RUPFmpIxtKxdCfmVQQy1lhZMZo,3829
78
+ krons/resource/utilities/__init__.py,sha256=-ycdoYgtXrliJwbtQGMHBRvZL0qX2xLzooL3RNV99x4,1013
79
+ krons/resource/utilities/header_factory.py,sha256=WZCCZMh9EJVi8tnY7dJD07FcTOrrUMvecJmzy8P65Os,2992
80
+ krons/resource/utilities/rate_limited_executor.py,sha256=xtvxTeHowoghb4WpY_9WNgqbFKHPBZKtKbaGyaTpTsk,9710
81
+ krons/resource/utilities/rate_limiter.py,sha256=DdaqnxOnst43ZAxcC9kBnnFbUCAsR89KzA0XL5xGPtU,6003
82
+ krons/resource/utilities/resilience.py,sha256=eOPSZ6m89IH_RCgaGLrFPQGTZtMBZOrs8G0sjSpfv40,14389
83
+ krons/resource/utilities/token_calculator.py,sha256=gBQvkmqL4gnqFx5o-FtPeoLDPBFTdN0ZpFkaT71VVLk,6437
84
+ krons/session/__init__.py,sha256=aCdCYe2HCLV8ujlBZHSqFr0nF8gj_PypI0BXoIU7ThU,1017
85
+ krons/session/constraints.py,sha256=DvmLKyf5eYUX2nxCDcco1-uDipGjiIJwoUxWVtX2w84,2361
86
+ krons/session/exchange.py,sha256=QChEqK5NFZJpHWxPrXd8LkGBCFg6LNONd1ZEy0MufpI,9165
87
+ krons/session/message.py,sha256=mtZ-PYCCoyWLy7BSB5TL5a0hIt0aQX2zdckOCIl6NZ0,2007
88
+ krons/session/registry.py,sha256=13pPKmDZJaj9q27oUOdtH6-jrHP_HDFF8lCdcNF--EE,999
89
+ krons/session/session.py,sha256=bBK6rYPmYu5YcmU-oWoJL9pU8pdzr9Y4Z5wcIIiuirw,13221
90
+ krons/utils/__init__.py,sha256=V-jTKULdofjJXxcEHygefKV_v6XE4H3poCdxPLjvf_4,1718
91
+ krons/utils/_function_arg_parser.py,sha256=H5JVLBVY8W9ZNkZ4_YVtVVY1rFYvnIRhSrAhKEdj2Qc,3479
92
+ krons/utils/_hash.py,sha256=W2Ma9v8-INPaGkur7GTtbF8KwuXSJNSwk8DCNPRvx8Q,6859
93
+ krons/utils/_json_dump.py,sha256=rpBmr0NCmIKRdmpgn1nSWIHI3FTsGYsQjOa3YTxCi3M,12482
94
+ krons/utils/_lazy_init.py,sha256=bCx_W3dfzirB5KnAt6_jmbnwOk2xbU7-kgOhMQqzW70,1806
95
+ krons/utils/_pythonic_function_call.py,sha256=PAqnb-PNzVBvIrXh5-680mj7BlRiUziiHkjxELe0q78,8388
96
+ krons/utils/_to_list.py,sha256=RuiUWkoxHjD-2pqbbumar69AADCKYoGqSKL5FPu6LdM,6114
97
+ krons/utils/_to_num.py,sha256=NyyIL9Az1EaW6QFh9R7AeJdaUPoSRBNVBX9yj78wpok,3102
98
+ krons/utils/_utils.py,sha256=g1LAKX9-AwREb-XzXaAaHMCeSBlhtfWMu7GK1XDDAzg,11145
99
+ krons/utils/display.py,sha256=haAzWny-Ez1H6LphxNsEaYyFY3uctj2xbvgR8ZAUfJg,7312
100
+ krons/utils/concurrency/__init__.py,sha256=1HD-aY4Hevde0Hm0-VYa0eheT5OiwhlnDJtofIuV9wk,6754
101
+ krons/utils/concurrency/_async_call.py,sha256=cb7iXphip1zKPsim6whf-nCvdopq1spQNqx7sWxiejo,10233
102
+ krons/utils/concurrency/_cancel.py,sha256=b9f6yT8gObS6Y3EWKoGzNfYxqM1ZrN_8ctfyIjT1m1U,3326
103
+ krons/utils/concurrency/_errors.py,sha256=8bG72f71OxWYTdQ9AmUnI20UjEz0UYsWjQayter4CNI,2603
104
+ krons/utils/concurrency/_patterns.py,sha256=Xl31p63JkU370N9bA05uu60GDXrgwm1DHCjEq2U8-oo,11540
105
+ krons/utils/concurrency/_primitives.py,sha256=D1Rl1hujXXPdL4Mw6eW5EN8utM3bh8vmmFtXmuvsDw0,9024
106
+ krons/utils/concurrency/_priority_queue.py,sha256=wGi6ESS6AIZ0QNRBQR5UwcSQI5GhKjATBC_aEw6c0aQ,4342
107
+ krons/utils/concurrency/_resource_tracker.py,sha256=aBLTpSaUsUCtRLEHzIaBYu0NOuACFbIt3JGACo7K-0E,3179
108
+ krons/utils/concurrency/_run_async.py,sha256=A_vuPi9-wWRr3oXiAMnv3l7MV2B76jkBXoVztX_Vcgo,1940
109
+ krons/utils/concurrency/_task.py,sha256=4ivkiUUbutheoYZ-G8-mTfHX4YgWguGIy1v5hSU871w,2528
110
+ krons/utils/concurrency/_utils.py,sha256=CyH_z4prYFeCFgwk4Pc3q0m5NRzDB2TP-G2oxdM_-C0,2136
111
+ krons/utils/fuzzy/__init__.py,sha256=aztwfirLUDf8i-NukKhER1Pp0cZBcgtD92G1n_I4cq4,502
112
+ krons/utils/fuzzy/_extract_json.py,sha256=6Y-QEfXrTBdaNmlbMfVm6xJPBKI0RBbr_t0VYGE7nMo,2922
113
+ krons/utils/fuzzy/_fuzzy_json.py,sha256=hV6v9YkQpGxwOnhpLM6jkrG5fZKYluMTxgdqsk5sTew,9151
114
+ krons/utils/fuzzy/_fuzzy_match.py,sha256=6uDoKwqEnms_NMT2T4GB0LC9hhyeuEOOCAu39vO1COA,4953
115
+ krons/utils/fuzzy/_string_similarity.py,sha256=4G0lgBek8zgRcgKi6V0npwRKdkdmQ_wfayr3eCLZmEY,6155
116
+ krons/utils/fuzzy/_to_dict.py,sha256=cS-7NPKIePFlsGSRExCnBRPn7qHIHmcqR1tQBl5tRjE,12941
117
+ krons/utils/schemas/__init__.py,sha256=Si_UjyztaT0Shi85ISs6ESpTLohk9yLSYdy2x4-82Yo,701
118
+ krons/utils/schemas/_breakdown_pydantic_annotation.py,sha256=wfgrCnpPP74UpDyeOxox4uZgVEJZU7UBiBwZHL0ocT0,4075
119
+ krons/utils/schemas/_formatter.py,sha256=DSxYkJDNncno1T_FSEAa1B3BrONfR_swFbd1AOtrtcg,2682
120
+ krons/utils/schemas/_minimal_yaml.py,sha256=IhbPmInpLpuQpPcTtW1noLoVi4JLFBUL2aRaeLvoB2Q,4652
121
+ krons/utils/schemas/_typescript.py,sha256=XrbUQBbWxhzwuAXS6w54vwBno9Bg3dPpqZFRPPQIKBg,5240
122
+ krons/utils/sql/__init__.py,sha256=yNjm9Dr-ZjrZSD3Lext7fPR80qX5svGT2PnXqlb5qXs,264
123
+ krons/utils/sql/_sql_validation.py,sha256=smxNU3HjPhQuRK5ehG9Fl4DeljKK9tqUQIvRUopgvc8,4229
124
+ krons/utils/validators/__init__.py,sha256=qmCYoL6fQL1O6-GcfcUARii_fjXYNed6rKZWOp3skj8,87
125
+ krons/utils/validators/_validate_image_url.py,sha256=WAffNY3WpPFe_lqFLHumJS0_hm92NwIOmDOK1GR61ew,1858
126
+ krons/work/__init__.py,sha256=8C3ypK0FInl6YSos-F_MkuDhu_Vb_uN8HEegFq8pvwc,3541
127
+ krons/work/engine.py,sha256=ZHZzzRqedpLEkcX5eQ6zZQMXYfgmLMRN2JOwkLajND4,10924
128
+ krons/work/form.py,sha256=QwXYclRR-MxneNLF0CKbMvk_K-piZBmMXyCd_Y3XOeE,7264
129
+ krons/work/report.py,sha256=wxjMGnUc8b2Jn26l0t0k0cWetZFDyvyqBz0ldae4BD0,9027
130
+ krons/work/worker.py,sha256=hK7t5ztG_5wKPScwjtsctP4-ob6bHw9WKRJTw_gw_Fc,8494
131
+ krons/work/operations/__init__.py,sha256=vU-jy0Xy25W4vj98wV3sk3c8x6hKc5sf4nW3U_FQGdY,999
132
+ krons/work/operations/builder.py,sha256=_OGvooSl8s3Pmx8En15SX4XxQ12wF2ZszwVb5Hqzdpc,7779
133
+ krons/work/operations/context.py,sha256=rMcKR8kJkVk-7X1nW8LGoSjW4Eaj66LM46El8wwURmU,5323
134
+ krons/work/operations/flow.py,sha256=RL_GIIKlIURsbLp4-xRA7bJukeCVx0b_garodGd1NhI,14803
135
+ krons/work/operations/node.py,sha256=3NbM2o22oMpFXy6I7osGKIOIzJ69r5Bby99LHrNifr4,3374
136
+ krons/work/operations/registry.py,sha256=cVQA36_jgr9bxRiCsVM_plGPrTVr08vci1aio3olZyM,3483
137
+ krons/work/rules/__init__.py,sha256=oxBsYI3vxAp85G1pMIpQM_84HUYwHbB1lxa9t4uB6YY,1187
138
+ krons/work/rules/registry.py,sha256=JFdwcIVtysntj6wMY3bO7-hqwNU7QIoIbEQuhsy5O4U,4742
139
+ krons/work/rules/rule.py,sha256=uKjjiBwgzryiAOyTUI2YiqSyFg3QluvNRJ09EwtrbK8,9471
140
+ krons/work/rules/validator.py,sha256=CbbdueLI_6JvbbG0yvcljyhjO3HhpF4mlfBTzdhP4jA,7240
141
+ krons/work/rules/common/__init__.py,sha256=U0rEcHOxLftR4-xJNuuXMSDYFAvbs0KzwV3GhlZcUrY,1026
142
+ krons/work/rules/common/boolean.py,sha256=zX9i-oggejwFA0QYQSj1rDU1epqqpUe-ARFS4aol3qY,2644
143
+ krons/work/rules/common/choice.py,sha256=QT0qMDvoPHTGjYeybRkZX8oolIy8Y3iQIqehoV28cco,3264
144
+ krons/work/rules/common/mapping.py,sha256=Loq54MNEtwpnHN0aypTjFOqwoOKLEysddHh-JESedvs,3824
145
+ krons/work/rules/common/model.py,sha256=xmM6coEThf_fgIiqJiyDgvdfib_FpVeY6LgWPVcWSwU,3026
146
+ krons/work/rules/common/number.py,sha256=cCukgMSpQu5RdYK5rXAUyop9qXgDRfLCioMvE8kIzHg,3162
147
+ krons/work/rules/common/string.py,sha256=zHp_OLh0FL4PvmSlyDTEzb2I97-DBSEyI2zcMo10voA,5090
148
+ krons-0.2.1.dist-info/METADATA,sha256=O9pmDvpEfTq1HydsJRGUwTA6IPn_veB57Em2udfOO9g,2527
149
+ krons-0.2.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
150
+ krons-0.2.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
151
+ krons-0.2.1.dist-info/RECORD,,