krons 0.2.0__tar.gz → 0.2.2__tar.gz

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 (274) hide show
  1. {krons-0.2.0 → krons-0.2.2}/PKG-INFO +1 -1
  2. {krons-0.2.0 → krons-0.2.2}/pyproject.toml +1 -1
  3. {krons-0.2.0 → krons-0.2.2}/src/krons/core/__init__.py +3 -0
  4. {krons-0.2.0 → krons-0.2.2}/src/krons/core/base/__init__.py +4 -0
  5. krons-0.2.2/src/krons/core/base/log.py +32 -0
  6. {krons-0.2.0 → krons-0.2.2}/src/krons/session/session.py +114 -1
  7. {krons-0.2.0 → krons-0.2.2}/src/krons/work/__init__.py +0 -11
  8. {krons-0.2.0 → krons-0.2.2}/src/krons/work/form.py +4 -67
  9. krons-0.2.2/tests/core/test_log.py +44 -0
  10. krons-0.2.2/tests/session/test_session_logging.py +337 -0
  11. {krons-0.2.0 → krons-0.2.2}/tests/work/test_form.py +0 -167
  12. {krons-0.2.0 → krons-0.2.2}/uv.lock +1 -1
  13. krons-0.2.0/src/krons/work/phrase.py +0 -522
  14. krons-0.2.0/src/krons/work/policy.py +0 -80
  15. krons-0.2.0/src/krons/work/service.py +0 -379
  16. krons-0.2.0/tests/enforcement/test_service.py +0 -591
  17. krons-0.2.0/tests/specs/test_phrase.py +0 -633
  18. krons-0.2.0/tests/utils/concurrency/__init__.py +0 -2
  19. krons-0.2.0/tests/work/test_phrase.py +0 -1141
  20. {krons-0.2.0 → krons-0.2.2}/.github/workflows/release.yml +0 -0
  21. {krons-0.2.0 → krons-0.2.2}/.gitignore +0 -0
  22. {krons-0.2.0 → krons-0.2.2}/.python-version +0 -0
  23. {krons-0.2.0 → krons-0.2.2}/CLAUDE.md +0 -0
  24. {krons-0.2.0 → krons-0.2.2}/LICENSE +0 -0
  25. {krons-0.2.0 → krons-0.2.2}/README.md +0 -0
  26. {krons-0.2.0 → krons-0.2.2}/cookbooks/007_fan_out_in.py +0 -0
  27. {krons-0.2.0 → krons-0.2.2}/cookbooks/test_conduct.py +0 -0
  28. {krons-0.2.0 → krons-0.2.2}/src/krons/__init__.py +0 -0
  29. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/__init__.py +0 -0
  30. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/mcps/__init__.py +0 -0
  31. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/mcps/loader.py +0 -0
  32. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/mcps/wrapper.py +0 -0
  33. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/message/__init__.py +0 -0
  34. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/message/action.py +0 -0
  35. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/message/assistant.py +0 -0
  36. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/message/common.py +0 -0
  37. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/message/instruction.py +0 -0
  38. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/message/prepare_msg.py +0 -0
  39. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/message/role.py +0 -0
  40. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/message/system.py +0 -0
  41. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/operations/__init__.py +0 -0
  42. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/operations/act.py +0 -0
  43. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/operations/generate.py +0 -0
  44. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/operations/llm_reparse.py +0 -0
  45. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/operations/operate.py +0 -0
  46. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/operations/parse.py +0 -0
  47. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/operations/react.py +0 -0
  48. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/operations/specs.py +0 -0
  49. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/operations/structure.py +0 -0
  50. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/operations/utils.py +0 -0
  51. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/providers/__init__.py +0 -0
  52. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/providers/anthropic_messages.py +0 -0
  53. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/providers/claude_code.py +0 -0
  54. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/providers/gemini.py +0 -0
  55. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/providers/match.py +0 -0
  56. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/providers/oai_chat.py +0 -0
  57. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/third_party/__init__.py +0 -0
  58. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/third_party/anthropic_models.py +0 -0
  59. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/third_party/claude_code.py +0 -0
  60. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/third_party/gemini_models.py +0 -0
  61. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/third_party/openai_models.py +0 -0
  62. {krons-0.2.0 → krons-0.2.2}/src/krons/agent/tool.py +0 -0
  63. {krons-0.2.0 → krons-0.2.2}/src/krons/core/base/broadcaster.py +0 -0
  64. {krons-0.2.0 → krons-0.2.2}/src/krons/core/base/element.py +0 -0
  65. {krons-0.2.0 → krons-0.2.2}/src/krons/core/base/event.py +0 -0
  66. {krons-0.2.0 → krons-0.2.2}/src/krons/core/base/eventbus.py +0 -0
  67. {krons-0.2.0 → krons-0.2.2}/src/krons/core/base/flow.py +0 -0
  68. {krons-0.2.0 → krons-0.2.2}/src/krons/core/base/graph.py +0 -0
  69. {krons-0.2.0 → krons-0.2.2}/src/krons/core/base/node.py +0 -0
  70. {krons-0.2.0 → krons-0.2.2}/src/krons/core/base/pile.py +0 -0
  71. {krons-0.2.0 → krons-0.2.2}/src/krons/core/base/processor.py +0 -0
  72. {krons-0.2.0 → krons-0.2.2}/src/krons/core/base/progression.py +0 -0
  73. {krons-0.2.0 → krons-0.2.2}/src/krons/core/specs/__init__.py +0 -0
  74. {krons-0.2.0 → krons-0.2.2}/src/krons/core/specs/adapters/__init__.py +0 -0
  75. {krons-0.2.0 → krons-0.2.2}/src/krons/core/specs/adapters/_utils.py +0 -0
  76. {krons-0.2.0 → krons-0.2.2}/src/krons/core/specs/adapters/dataclass_field.py +0 -0
  77. {krons-0.2.0 → krons-0.2.2}/src/krons/core/specs/adapters/factory.py +0 -0
  78. {krons-0.2.0 → krons-0.2.2}/src/krons/core/specs/adapters/pydantic_adapter.py +0 -0
  79. {krons-0.2.0 → krons-0.2.2}/src/krons/core/specs/adapters/sql_ddl.py +0 -0
  80. {krons-0.2.0 → krons-0.2.2}/src/krons/core/specs/catalog/__init__.py +0 -0
  81. {krons-0.2.0 → krons-0.2.2}/src/krons/core/specs/catalog/_audit.py +0 -0
  82. {krons-0.2.0 → krons-0.2.2}/src/krons/core/specs/catalog/_common.py +0 -0
  83. {krons-0.2.0 → krons-0.2.2}/src/krons/core/specs/catalog/_content.py +0 -0
  84. {krons-0.2.0 → krons-0.2.2}/src/krons/core/specs/catalog/_enforcement.py +0 -0
  85. {krons-0.2.0 → krons-0.2.2}/src/krons/core/specs/factory.py +0 -0
  86. {krons-0.2.0 → krons-0.2.2}/src/krons/core/specs/operable.py +0 -0
  87. {krons-0.2.0 → krons-0.2.2}/src/krons/core/specs/protocol.py +0 -0
  88. {krons-0.2.0 → krons-0.2.2}/src/krons/core/specs/spec.py +0 -0
  89. {krons-0.2.0 → krons-0.2.2}/src/krons/core/types/__init__.py +0 -0
  90. {krons-0.2.0 → krons-0.2.2}/src/krons/core/types/_sentinel.py +0 -0
  91. {krons-0.2.0 → krons-0.2.2}/src/krons/core/types/base.py +0 -0
  92. {krons-0.2.0 → krons-0.2.2}/src/krons/core/types/db_types.py +0 -0
  93. {krons-0.2.0 → krons-0.2.2}/src/krons/core/types/identity.py +0 -0
  94. {krons-0.2.0 → krons-0.2.2}/src/krons/errors.py +0 -0
  95. {krons-0.2.0 → krons-0.2.2}/src/krons/protocols.py +0 -0
  96. {krons-0.2.0 → krons-0.2.2}/src/krons/py.typed +0 -0
  97. {krons-0.2.0 → krons-0.2.2}/src/krons/resource/__init__.py +0 -0
  98. {krons-0.2.0 → krons-0.2.2}/src/krons/resource/backend.py +0 -0
  99. {krons-0.2.0 → krons-0.2.2}/src/krons/resource/endpoint.py +0 -0
  100. {krons-0.2.0 → krons-0.2.2}/src/krons/resource/hook.py +0 -0
  101. {krons-0.2.0 → krons-0.2.2}/src/krons/resource/imodel.py +0 -0
  102. {krons-0.2.0 → krons-0.2.2}/src/krons/resource/registry.py +0 -0
  103. {krons-0.2.0 → krons-0.2.2}/src/krons/resource/utilities/__init__.py +0 -0
  104. {krons-0.2.0 → krons-0.2.2}/src/krons/resource/utilities/header_factory.py +0 -0
  105. {krons-0.2.0 → krons-0.2.2}/src/krons/resource/utilities/rate_limited_executor.py +0 -0
  106. {krons-0.2.0 → krons-0.2.2}/src/krons/resource/utilities/rate_limiter.py +0 -0
  107. {krons-0.2.0 → krons-0.2.2}/src/krons/resource/utilities/resilience.py +0 -0
  108. {krons-0.2.0 → krons-0.2.2}/src/krons/resource/utilities/token_calculator.py +0 -0
  109. {krons-0.2.0 → krons-0.2.2}/src/krons/session/__init__.py +0 -0
  110. {krons-0.2.0 → krons-0.2.2}/src/krons/session/constraints.py +0 -0
  111. {krons-0.2.0 → krons-0.2.2}/src/krons/session/exchange.py +0 -0
  112. {krons-0.2.0 → krons-0.2.2}/src/krons/session/message.py +0 -0
  113. {krons-0.2.0 → krons-0.2.2}/src/krons/session/registry.py +0 -0
  114. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/__init__.py +0 -0
  115. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/_function_arg_parser.py +0 -0
  116. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/_hash.py +0 -0
  117. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/_json_dump.py +0 -0
  118. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/_lazy_init.py +0 -0
  119. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/_pythonic_function_call.py +0 -0
  120. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/_to_list.py +0 -0
  121. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/_to_num.py +0 -0
  122. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/_utils.py +0 -0
  123. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/concurrency/__init__.py +0 -0
  124. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/concurrency/_async_call.py +0 -0
  125. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/concurrency/_cancel.py +0 -0
  126. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/concurrency/_errors.py +0 -0
  127. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/concurrency/_patterns.py +0 -0
  128. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/concurrency/_primitives.py +0 -0
  129. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/concurrency/_priority_queue.py +0 -0
  130. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/concurrency/_resource_tracker.py +0 -0
  131. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/concurrency/_run_async.py +0 -0
  132. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/concurrency/_task.py +0 -0
  133. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/concurrency/_utils.py +0 -0
  134. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/display.py +0 -0
  135. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/fuzzy/__init__.py +0 -0
  136. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/fuzzy/_extract_json.py +0 -0
  137. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/fuzzy/_fuzzy_json.py +0 -0
  138. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/fuzzy/_fuzzy_match.py +0 -0
  139. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/fuzzy/_string_similarity.py +0 -0
  140. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/fuzzy/_to_dict.py +0 -0
  141. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/schemas/__init__.py +0 -0
  142. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/schemas/_breakdown_pydantic_annotation.py +0 -0
  143. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/schemas/_formatter.py +0 -0
  144. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/schemas/_minimal_yaml.py +0 -0
  145. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/schemas/_typescript.py +0 -0
  146. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/sql/__init__.py +0 -0
  147. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/sql/_sql_validation.py +0 -0
  148. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/validators/__init__.py +0 -0
  149. {krons-0.2.0 → krons-0.2.2}/src/krons/utils/validators/_validate_image_url.py +0 -0
  150. {krons-0.2.0 → krons-0.2.2}/src/krons/work/engine.py +0 -0
  151. {krons-0.2.0 → krons-0.2.2}/src/krons/work/operations/__init__.py +0 -0
  152. {krons-0.2.0 → krons-0.2.2}/src/krons/work/operations/builder.py +0 -0
  153. {krons-0.2.0 → krons-0.2.2}/src/krons/work/operations/context.py +0 -0
  154. {krons-0.2.0 → krons-0.2.2}/src/krons/work/operations/flow.py +0 -0
  155. {krons-0.2.0 → krons-0.2.2}/src/krons/work/operations/node.py +0 -0
  156. {krons-0.2.0 → krons-0.2.2}/src/krons/work/operations/registry.py +0 -0
  157. {krons-0.2.0 → krons-0.2.2}/src/krons/work/report.py +0 -0
  158. {krons-0.2.0 → krons-0.2.2}/src/krons/work/rules/__init__.py +0 -0
  159. {krons-0.2.0 → krons-0.2.2}/src/krons/work/rules/common/__init__.py +0 -0
  160. {krons-0.2.0 → krons-0.2.2}/src/krons/work/rules/common/boolean.py +0 -0
  161. {krons-0.2.0 → krons-0.2.2}/src/krons/work/rules/common/choice.py +0 -0
  162. {krons-0.2.0 → krons-0.2.2}/src/krons/work/rules/common/mapping.py +0 -0
  163. {krons-0.2.0 → krons-0.2.2}/src/krons/work/rules/common/model.py +0 -0
  164. {krons-0.2.0 → krons-0.2.2}/src/krons/work/rules/common/number.py +0 -0
  165. {krons-0.2.0 → krons-0.2.2}/src/krons/work/rules/common/string.py +0 -0
  166. {krons-0.2.0 → krons-0.2.2}/src/krons/work/rules/registry.py +0 -0
  167. {krons-0.2.0 → krons-0.2.2}/src/krons/work/rules/rule.py +0 -0
  168. {krons-0.2.0 → krons-0.2.2}/src/krons/work/rules/validator.py +0 -0
  169. {krons-0.2.0 → krons-0.2.2}/src/krons/work/worker.py +0 -0
  170. {krons-0.2.0 → krons-0.2.2}/tests/__init__.py +0 -0
  171. {krons-0.2.0 → krons-0.2.2}/tests/agent/__init__.py +0 -0
  172. {krons-0.2.0 → krons-0.2.2}/tests/agent/test_mcps_unit.py +0 -0
  173. {krons-0.2.0 → krons-0.2.2}/tests/agent/test_message.py +0 -0
  174. {krons-0.2.0 → krons-0.2.2}/tests/agent/test_providers_e2e.py +0 -0
  175. {krons-0.2.0 → krons-0.2.2}/tests/agent/test_providers_unit.py +0 -0
  176. {krons-0.2.0 → krons-0.2.2}/tests/agent/test_tool_unit.py +0 -0
  177. {krons-0.2.0 → krons-0.2.2}/tests/conftest.py +0 -0
  178. {krons-0.2.0 → krons-0.2.2}/tests/core/__init__.py +0 -0
  179. {krons-0.2.0 → krons-0.2.2}/tests/core/test_broadcaster.py +0 -0
  180. {krons-0.2.0 → krons-0.2.2}/tests/core/test_element.py +0 -0
  181. {krons-0.2.0 → krons-0.2.2}/tests/core/test_error_paths.py +0 -0
  182. {krons-0.2.0 → krons-0.2.2}/tests/core/test_event.py +0 -0
  183. {krons-0.2.0 → krons-0.2.2}/tests/core/test_event_status_race.py +0 -0
  184. {krons-0.2.0 → krons-0.2.2}/tests/core/test_eventbus.py +0 -0
  185. {krons-0.2.0 → krons-0.2.2}/tests/core/test_flow.py +0 -0
  186. {krons-0.2.0 → krons-0.2.2}/tests/core/test_flow_edge_cases.py +0 -0
  187. {krons-0.2.0 → krons-0.2.2}/tests/core/test_graph.py +0 -0
  188. {krons-0.2.0 → krons-0.2.2}/tests/core/test_graph_event_loop.py +0 -0
  189. {krons-0.2.0 → krons-0.2.2}/tests/core/test_node.py +0 -0
  190. {krons-0.2.0 → krons-0.2.2}/tests/core/test_pile.py +0 -0
  191. {krons-0.2.0 → krons-0.2.2}/tests/core/test_pile_edge_cases.py +0 -0
  192. {krons-0.2.0 → krons-0.2.2}/tests/core/test_processor.py +0 -0
  193. {krons-0.2.0 → krons-0.2.2}/tests/core/test_processor_security.py +0 -0
  194. {krons-0.2.0 → krons-0.2.2}/tests/core/test_progression.py +0 -0
  195. {krons-0.2.0 → krons-0.2.2}/tests/core/test_progression_edge_cases.py +0 -0
  196. {krons-0.2.0 → krons-0.2.2}/tests/core/test_serialization_roundtrip.py +0 -0
  197. {krons-0.2.0 → krons-0.2.2}/tests/core/test_thread_safety_stress.py +0 -0
  198. {krons-0.2.0/tests/enforcement → krons-0.2.2/tests/operations}/__init__.py +0 -0
  199. {krons-0.2.0 → krons-0.2.2}/tests/operations/test_builder.py +0 -0
  200. {krons-0.2.0 → krons-0.2.2}/tests/operations/test_flow.py +0 -0
  201. {krons-0.2.0 → krons-0.2.2}/tests/operations/test_node.py +0 -0
  202. {krons-0.2.0 → krons-0.2.2}/tests/operations/test_op_flow.py +0 -0
  203. {krons-0.2.0 → krons-0.2.2}/tests/operations/test_op_node.py +0 -0
  204. {krons-0.2.0 → krons-0.2.2}/tests/operations/test_registry.py +0 -0
  205. {krons-0.2.0/tests/operations → krons-0.2.2/tests/resource}/__init__.py +0 -0
  206. {krons-0.2.0 → krons-0.2.2}/tests/resource/test_backend.py +0 -0
  207. {krons-0.2.0 → krons-0.2.2}/tests/resource/test_endpoint.py +0 -0
  208. {krons-0.2.0 → krons-0.2.2}/tests/resource/test_hook.py +0 -0
  209. {krons-0.2.0 → krons-0.2.2}/tests/resource/test_imodel.py +0 -0
  210. {krons-0.2.0 → krons-0.2.2}/tests/resource/test_registry.py +0 -0
  211. {krons-0.2.0/tests/resource → krons-0.2.2/tests/resource/utilities}/__init__.py +0 -0
  212. {krons-0.2.0 → krons-0.2.2}/tests/resource/utilities/test_header_factory.py +0 -0
  213. {krons-0.2.0 → krons-0.2.2}/tests/resource/utilities/test_rate_limiter.py +0 -0
  214. {krons-0.2.0 → krons-0.2.2}/tests/resource/utilities/test_resilience.py +0 -0
  215. {krons-0.2.0 → krons-0.2.2}/tests/resource/utilities/test_resilience_retry.py +0 -0
  216. {krons-0.2.0/tests/resource/utilities → krons-0.2.2/tests/rules}/__init__.py +0 -0
  217. {krons-0.2.0 → krons-0.2.2}/tests/rules/test_base.py +0 -0
  218. {krons-0.2.0 → krons-0.2.2}/tests/rules/test_builtin.py +0 -0
  219. {krons-0.2.0 → krons-0.2.2}/tests/rules/test_registry.py +0 -0
  220. {krons-0.2.0 → krons-0.2.2}/tests/rules/test_rule_params_edge_cases.py +0 -0
  221. {krons-0.2.0 → krons-0.2.2}/tests/rules/test_rules_comprehensive.py +0 -0
  222. {krons-0.2.0 → krons-0.2.2}/tests/rules/test_validator.py +0 -0
  223. {krons-0.2.0/tests/rules → krons-0.2.2/tests/session}/__init__.py +0 -0
  224. {krons-0.2.0 → krons-0.2.2}/tests/session/test_exchange.py +0 -0
  225. {krons-0.2.0 → krons-0.2.2}/tests/session/test_message.py +0 -0
  226. {krons-0.2.0 → krons-0.2.2}/tests/session/test_message_edge_cases.py +0 -0
  227. {krons-0.2.0 → krons-0.2.2}/tests/session/test_session.py +0 -0
  228. {krons-0.2.0 → krons-0.2.2}/tests/session/test_session_edge_cases.py +0 -0
  229. {krons-0.2.0 → krons-0.2.2}/tests/specs/__init__.py +0 -0
  230. {krons-0.2.0 → krons-0.2.2}/tests/specs/test_catalog.py +0 -0
  231. {krons-0.2.0 → krons-0.2.2}/tests/specs/test_factory.py +0 -0
  232. {krons-0.2.0 → krons-0.2.2}/tests/test_protocols.py +0 -0
  233. {krons-0.2.0/tests/session → krons-0.2.2/tests/types}/__init__.py +0 -0
  234. {krons-0.2.0 → krons-0.2.2}/tests/types/conftest.py +0 -0
  235. {krons-0.2.0/tests/types → krons-0.2.2/tests/types/spec_adapters}/__init__.py +0 -0
  236. {krons-0.2.0 → krons-0.2.2}/tests/types/spec_adapters/test_adapters_py311.py +0 -0
  237. {krons-0.2.0 → krons-0.2.2}/tests/types/spec_adapters/test_protocol.py +0 -0
  238. {krons-0.2.0 → krons-0.2.2}/tests/types/spec_adapters/test_pydantic_field.py +0 -0
  239. {krons-0.2.0 → krons-0.2.2}/tests/types/spec_adapters/test_sql_ddl_specs.py +0 -0
  240. {krons-0.2.0 → krons-0.2.2}/tests/types/test_db_types.py +0 -0
  241. {krons-0.2.0 → krons-0.2.2}/tests/types/test_identity.py +0 -0
  242. {krons-0.2.0 → krons-0.2.2}/tests/types/test_model.py +0 -0
  243. {krons-0.2.0 → krons-0.2.2}/tests/types/test_operable.py +0 -0
  244. {krons-0.2.0 → krons-0.2.2}/tests/types/test_sentinel.py +0 -0
  245. {krons-0.2.0 → krons-0.2.2}/tests/types/test_spec.py +0 -0
  246. {krons-0.2.0 → krons-0.2.2}/tests/types/test_types.py +0 -0
  247. {krons-0.2.0 → krons-0.2.2}/tests/types/test_types_py311.py +0 -0
  248. {krons-0.2.0/tests/types/spec_adapters → krons-0.2.2/tests/utils}/__init__.py +0 -0
  249. {krons-0.2.0/tests/utils → krons-0.2.2/tests/utils/concurrency}/__init__.py +0 -0
  250. {krons-0.2.0 → krons-0.2.2}/tests/utils/concurrency/test_async_call.py +0 -0
  251. {krons-0.2.0 → krons-0.2.2}/tests/utils/concurrency/test_cancel.py +0 -0
  252. {krons-0.2.0 → krons-0.2.2}/tests/utils/concurrency/test_errors.py +0 -0
  253. {krons-0.2.0 → krons-0.2.2}/tests/utils/concurrency/test_patterns.py +0 -0
  254. {krons-0.2.0 → krons-0.2.2}/tests/utils/concurrency/test_primitives.py +0 -0
  255. {krons-0.2.0 → krons-0.2.2}/tests/utils/concurrency/test_priority_queue.py +0 -0
  256. {krons-0.2.0 → krons-0.2.2}/tests/utils/concurrency/test_run_async.py +0 -0
  257. {krons-0.2.0 → krons-0.2.2}/tests/utils/concurrency/test_task.py +0 -0
  258. {krons-0.2.0 → krons-0.2.2}/tests/utils/concurrency/test_utils.py +0 -0
  259. {krons-0.2.0 → krons-0.2.2}/tests/utils/test_extract_json.py +0 -0
  260. {krons-0.2.0 → krons-0.2.2}/tests/utils/test_fuzzy_json.py +0 -0
  261. {krons-0.2.0 → krons-0.2.2}/tests/utils/test_fuzzy_match.py +0 -0
  262. {krons-0.2.0 → krons-0.2.2}/tests/utils/test_hash.py +0 -0
  263. {krons-0.2.0 → krons-0.2.2}/tests/utils/test_json_dump.py +0 -0
  264. {krons-0.2.0 → krons-0.2.2}/tests/utils/test_lazy_init.py +0 -0
  265. {krons-0.2.0 → krons-0.2.2}/tests/utils/test_sql_validation.py +0 -0
  266. {krons-0.2.0 → krons-0.2.2}/tests/utils/test_string_similarity.py +0 -0
  267. {krons-0.2.0 → krons-0.2.2}/tests/utils/test_to_dict.py +0 -0
  268. {krons-0.2.0 → krons-0.2.2}/tests/utils/test_to_list.py +0 -0
  269. {krons-0.2.0 → krons-0.2.2}/tests/utils/test_to_num.py +0 -0
  270. {krons-0.2.0 → krons-0.2.2}/tests/utils/test_utils.py +0 -0
  271. {krons-0.2.0 → krons-0.2.2}/tests/work/__init__.py +0 -0
  272. {krons-0.2.0 → krons-0.2.2}/tests/work/test_engine.py +0 -0
  273. {krons-0.2.0 → krons-0.2.2}/tests/work/test_report.py +0 -0
  274. {krons-0.2.0 → krons-0.2.2}/tests/work/test_worker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: krons
3
- Version: 0.2.0
3
+ Version: 0.2.2
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "krons"
3
- version = "0.2.0"
3
+ version = "0.2.2"
4
4
  description = "Spec-based composable framework for building type-safe systems"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -21,6 +21,7 @@ _LAZY_IMPORTS: dict[str, tuple[str, str]] = {
21
21
  "PERSISTABLE_NODE_REGISTRY": ("krons.core.base", "PERSISTABLE_NODE_REGISTRY"),
22
22
  # Classes
23
23
  "Broadcaster": ("krons.core.base", "Broadcaster"),
24
+ "DataLoggerConfig": ("krons.core.base", "DataLoggerConfig"),
24
25
  "Edge": ("krons.core.base", "Edge"),
25
26
  "EdgeCondition": ("krons.core.base", "EdgeCondition"),
26
27
  "Element": ("krons.core.base", "Element"),
@@ -75,6 +76,7 @@ if TYPE_CHECKING:
75
76
  NODE_REGISTRY,
76
77
  PERSISTABLE_NODE_REGISTRY,
77
78
  Broadcaster,
79
+ DataLoggerConfig,
78
80
  Edge,
79
81
  EdgeCondition,
80
82
  Element,
@@ -103,6 +105,7 @@ __all__ = [
103
105
  "PERSISTABLE_NODE_REGISTRY",
104
106
  # classes
105
107
  "Broadcaster",
108
+ "DataLoggerConfig",
106
109
  "Edge",
107
110
  "EdgeCondition",
108
111
  "Element",
@@ -26,6 +26,8 @@ _LAZY_IMPORTS: dict[str, tuple[str, str]] = {
26
26
  "Edge": ("krons.core.base.graph", "Edge"),
27
27
  "EdgeCondition": ("krons.core.base.graph", "EdgeCondition"),
28
28
  "Graph": ("krons.core.base.graph", "Graph"),
29
+ # log
30
+ "DataLoggerConfig": ("krons.core.base.log", "DataLoggerConfig"),
29
31
  # node
30
32
  "NODE_REGISTRY": ("krons.core.base.node", "NODE_REGISTRY"),
31
33
  "PERSISTABLE_NODE_REGISTRY": ("krons.core.base.node", "PERSISTABLE_NODE_REGISTRY"),
@@ -77,6 +79,7 @@ if TYPE_CHECKING:
77
79
  from .eventbus import EventBus, Handler
78
80
  from .flow import Flow
79
81
  from .graph import Edge, EdgeCondition, Graph
82
+ from .log import DataLoggerConfig
80
83
  from .node import (
81
84
  NODE_REGISTRY,
82
85
  PERSISTABLE_NODE_REGISTRY,
@@ -97,6 +100,7 @@ __all__ = [
97
100
  "PERSISTABLE_NODE_REGISTRY",
98
101
  # classes
99
102
  "Broadcaster",
103
+ "DataLoggerConfig",
100
104
  "Edge",
101
105
  "EdgeCondition",
102
106
  "Element",
@@ -0,0 +1,32 @@
1
+ # Copyright (c) 2025 - 2026, HaiyangLi <quantocean.li at gmail dot com>
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """Logging configuration for Session message persistence.
5
+
6
+ Provides DataLoggerConfig for configuring automatic message dumps.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from pathlib import Path
12
+ from typing import Literal
13
+
14
+ from pydantic import Field
15
+
16
+ from krons.core.types import HashableModel
17
+
18
+ __all__ = ("DataLoggerConfig",)
19
+
20
+
21
+ class DataLoggerConfig(HashableModel):
22
+ """Configuration for Session message persistence.
23
+
24
+ Attributes:
25
+ persist_dir: Directory for dump files.
26
+ extension: Output format (.json array or .jsonl newline-delimited).
27
+ auto_save_on_exit: Register atexit handler on Session creation.
28
+ """
29
+
30
+ persist_dir: str | Path = Field(default="./logs")
31
+ extension: Literal[".json", ".jsonl"] = Field(default=".jsonl")
32
+ auto_save_on_exit: bool = Field(default=True)
@@ -9,12 +9,16 @@ Branch is a named message progression with capability/resource access control.
9
9
 
10
10
  from __future__ import annotations
11
11
 
12
+ import atexit
12
13
  import contextlib
13
14
  from collections.abc import AsyncGenerator, Iterable
15
+ from pathlib import Path
14
16
  from typing import Any, Literal
17
+
18
+ from krons.core.base.log import DataLoggerConfig
15
19
  from uuid import UUID
16
20
 
17
- from pydantic import Field, model_validator
21
+ from pydantic import Field, PrivateAttr, model_validator
18
22
 
19
23
  from krons.core import Element, Flow, Pile, Progression
20
24
  from krons.core.types import HashableModel, Unset, UnsetType, not_sentinel
@@ -59,6 +63,10 @@ class SessionConfig(HashableModel):
59
63
  default_gen_model: str | None = None
60
64
  default_parse_model: str | None = None
61
65
  auto_create_default_branch: bool = True
66
+ log_config: DataLoggerConfig | None = Field(
67
+ default=None,
68
+ description="DataLoggerConfig for auto-logging (None = disabled)",
69
+ )
62
70
 
63
71
 
64
72
  class Session(Element):
@@ -71,6 +79,9 @@ class Session(Element):
71
79
  config: SessionConfig = Field(default_factory=SessionConfig)
72
80
  default_branch_id: UUID | None = None
73
81
 
82
+ _registered_atexit: bool = PrivateAttr(default=False)
83
+ _dump_count: int = PrivateAttr(default=0)
84
+
74
85
  @model_validator(mode="after")
75
86
  def _validate_default_branch(self) -> Session:
76
87
  """Auto-create default branch and register built-in operations."""
@@ -83,6 +94,15 @@ class Session(Element):
83
94
  )
84
95
  self.set_default_branch(default_branch_name)
85
96
 
97
+ # Register atexit handler if configured
98
+ if (
99
+ self.config.log_config is not None
100
+ and self.config.log_config.auto_save_on_exit
101
+ and not self._registered_atexit
102
+ ):
103
+ atexit.register(self._save_at_exit)
104
+ self._registered_atexit = True
105
+
86
106
  # Register built-in operations (lazy import avoids circular)
87
107
  from krons.agent.operations import (
88
108
  generate,
@@ -386,6 +406,99 @@ class Session(Element):
386
406
  async for result in handler(params, ctx):
387
407
  yield result
388
408
 
409
+ def dump_messages(self, clear: bool = False) -> Path | None:
410
+ """Sync dump all session messages to file.
411
+
412
+ Args:
413
+ clear: Clear messages after dump (default False).
414
+
415
+ Returns:
416
+ Path to written file, or None if no log_config or no messages.
417
+ """
418
+ from krons.utils import create_path, json_dumpb, json_lines_iter
419
+ from krons.utils.concurrency import run_async
420
+
421
+ if self.config.log_config is None or len(self.messages) == 0:
422
+ return None
423
+
424
+ cfg = self.config.log_config
425
+ self._dump_count += 1
426
+
427
+ filepath = run_async(
428
+ create_path(
429
+ directory=cfg.persist_dir,
430
+ filename=f"session_{str(self.id)[:8]}_{self._dump_count}",
431
+ extension=cfg.extension,
432
+ timestamp=True,
433
+ file_exist_ok=True,
434
+ )
435
+ )
436
+
437
+ items = [msg.to_dict(mode="json") for msg in self.messages]
438
+
439
+ std_path = Path(filepath)
440
+ if cfg.extension == ".jsonl":
441
+ with std_path.open("wb") as f:
442
+ for chunk in json_lines_iter(items, safe_fallback=True):
443
+ f.write(chunk)
444
+ else:
445
+ data = json_dumpb(items, safe_fallback=True)
446
+ std_path.write_bytes(data)
447
+
448
+ if clear:
449
+ self.communications.items.clear()
450
+
451
+ return std_path
452
+
453
+ async def adump_messages(self, clear: bool = False) -> Path | None:
454
+ """Async dump all session messages to file with lock protection.
455
+
456
+ Args:
457
+ clear: Clear messages after dump (default False).
458
+
459
+ Returns:
460
+ Path to written file, or None if no log_config or no messages.
461
+ """
462
+ from krons.utils import create_path, json_dumpb, json_lines_iter
463
+
464
+ if self.config.log_config is None or len(self.messages) == 0:
465
+ return None
466
+
467
+ cfg = self.config.log_config
468
+
469
+ async with self.messages:
470
+ self._dump_count += 1
471
+
472
+ filepath = await create_path(
473
+ directory=cfg.persist_dir,
474
+ filename=f"session_{str(self.id)[:8]}_{self._dump_count}",
475
+ extension=cfg.extension,
476
+ timestamp=True,
477
+ file_exist_ok=True,
478
+ )
479
+
480
+ items = [msg.to_dict(mode="json") for msg in self.messages]
481
+
482
+ if cfg.extension == ".jsonl":
483
+ content = b"".join(json_lines_iter(items, safe_fallback=True))
484
+ await filepath.write_bytes(content)
485
+ else:
486
+ data = json_dumpb(items, safe_fallback=True)
487
+ await filepath.write_bytes(data)
488
+
489
+ if clear:
490
+ self.communications.items.clear()
491
+
492
+ return Path(filepath)
493
+
494
+ def _save_at_exit(self) -> None:
495
+ """atexit callback. Dumps messages synchronously. Errors are suppressed."""
496
+ if len(self.messages) > 0:
497
+ try:
498
+ self.dump_messages(clear=False)
499
+ except Exception:
500
+ pass # Silent failure during interpreter shutdown
501
+
389
502
  def _resolve_branch(self, branch: Branch | UUID | str | None) -> Branch:
390
503
  """Resolve to Branch, falling back to default. Raises if neither available."""
391
504
  if branch is not None:
@@ -32,7 +32,6 @@ Two complementary patterns at different abstraction levels:
32
32
  return await self.llm.chat(**kwargs)
33
33
 
34
34
  Core concepts:
35
- - Phrase: Typed operation signature (inputs -> outputs)
36
35
  - Form: Data binding + scheduling (stateful artifact)
37
36
  - Report: Multi-step workflow declaration (stateful artifact)
38
37
  - Worker: Execution capability (stateless station)
@@ -53,11 +52,6 @@ _LAZY_IMPORTS: dict[str, tuple[str, str]] = {
53
52
  "ParsedAssignment": ("krons.work.form", "ParsedAssignment"),
54
53
  "parse_assignment": ("krons.work.form", "parse_assignment"),
55
54
  "parse_full_assignment": ("krons.work.form", "parse_full_assignment"),
56
- # phrase
57
- "CrudOperation": ("krons.work.phrase", "CrudOperation"),
58
- "CrudPattern": ("krons.work.phrase", "CrudPattern"),
59
- "Phrase": ("krons.work.phrase", "Phrase"),
60
- "phrase": ("krons.work.phrase", "phrase"),
61
55
  # report
62
56
  "Report": ("krons.work.report", "Report"),
63
57
  # worker
@@ -102,16 +96,12 @@ if TYPE_CHECKING:
102
96
  parse_assignment,
103
97
  parse_full_assignment,
104
98
  )
105
- from krons.work.phrase import CrudOperation, CrudPattern, Phrase, phrase
106
99
  from krons.work.report import Report
107
100
  from krons.work.worker import WorkConfig, Worker, WorkLink, work, worklink
108
101
 
109
102
  __all__ = (
110
- "CrudOperation",
111
- "CrudPattern",
112
103
  "Form",
113
104
  "ParsedAssignment",
114
- "Phrase",
115
105
  "Report",
116
106
  "WorkConfig",
117
107
  "WorkLink",
@@ -120,7 +110,6 @@ __all__ = (
120
110
  "WorkerTask",
121
111
  "parse_assignment",
122
112
  "parse_full_assignment",
123
- "phrase",
124
113
  "work",
125
114
  "worklink",
126
115
  )
@@ -6,23 +6,19 @@
6
6
  A Form represents an instantiated work unit with:
7
7
  - Data binding (input values)
8
8
  - Execution state tracking (filled, workable)
9
- - Optional Phrase reference for typed I/O
10
9
 
11
- Forms are the stateful layer between Phrase (definition) and Operation (execution).
10
+ Forms are the stateful scheduling layer for Operations.
12
11
  """
13
12
 
14
13
  from __future__ import annotations
15
14
 
16
15
  from dataclasses import dataclass
17
- from typing import TYPE_CHECKING, Any
16
+ from typing import Any
18
17
 
19
18
  from pydantic import Field
20
19
 
21
20
  from krons.core import Element
22
21
 
23
- if TYPE_CHECKING:
24
- from .phrase import Phrase
25
-
26
22
  __all__ = ("Form", "ParsedAssignment", "parse_assignment", "parse_full_assignment")
27
23
 
28
24
 
@@ -123,9 +119,7 @@ def parse_full_assignment(assignment: str) -> ParsedAssignment:
123
119
  class Form(Element):
124
120
  """Data binding container for work units.
125
121
 
126
- A Form binds input data and tracks execution state. It can be created:
127
- 1. From a Phrase (typed I/O)
128
- 2. From an assignment string (dynamic fields)
122
+ A Form binds input data and tracks execution state.
129
123
 
130
124
  Assignment DSL supports full format:
131
125
  "branch: inputs -> outputs | resource"
@@ -144,7 +138,6 @@ class Form(Element):
144
138
  available_data: Current data values
145
139
  output: Execution result
146
140
  filled: Whether form has been executed
147
- phrase: Optional Phrase reference for typed execution
148
141
  """
149
142
 
150
143
  assignment: str = Field(
@@ -165,9 +158,6 @@ class Form(Element):
165
158
  output: Any = Field(default=None)
166
159
  filled: bool = Field(default=False)
167
160
 
168
- # Optional phrase reference (set via from_phrase())
169
- _phrase: "Phrase | None" = None
170
-
171
161
  def model_post_init(self, _: Any) -> None:
172
162
  """Parse assignment to derive fields if not already set."""
173
163
  if self.assignment and not self.input_fields and not self.output_fields:
@@ -179,35 +169,6 @@ class Form(Element):
179
169
  if parsed.resource and self.resource is None:
180
170
  self.resource = parsed.resource
181
171
 
182
- @classmethod
183
- def from_phrase(
184
- cls,
185
- phrase: "Phrase",
186
- **initial_data: Any,
187
- ) -> "Form":
188
- """Create Form from a Phrase with optional initial data.
189
-
190
- Args:
191
- phrase: Phrase defining typed I/O
192
- **initial_data: Initial input values
193
-
194
- Returns:
195
- Form bound to the phrase
196
- """
197
- form = cls(
198
- assignment=f"{', '.join(phrase.inputs)} -> {', '.join(phrase.outputs)}",
199
- input_fields=list(phrase.inputs),
200
- output_fields=list(phrase.outputs),
201
- available_data=dict(initial_data),
202
- )
203
- form._phrase = phrase
204
- return form
205
-
206
- @property
207
- def phrase(self) -> "Phrase | None":
208
- """Get bound phrase if any."""
209
- return self._phrase
210
-
211
172
  def is_workable(self) -> bool:
212
173
  """Check if form is ready for execution.
213
174
 
@@ -274,32 +235,8 @@ class Form(Element):
274
235
  result[field] = self.available_data[field]
275
236
  return result
276
237
 
277
- async def execute(self, ctx: Any = None) -> Any:
278
- """Execute the form if it has a bound phrase.
279
-
280
- Args:
281
- ctx: Execution context
282
-
283
- Returns:
284
- Execution result
285
-
286
- Raises:
287
- RuntimeError: If no phrase bound or form not workable
288
- """
289
- if self._phrase is None:
290
- raise RuntimeError("Form has no bound phrase - cannot execute")
291
-
292
- if not self.is_workable():
293
- missing = [f for f in self.input_fields if f not in self.available_data]
294
- raise RuntimeError(f"Form not workable - missing inputs: {missing}")
295
-
296
- result = await self._phrase(self.get_inputs(), ctx)
297
- self.set_output(result)
298
- return result
299
-
300
238
  def __repr__(self) -> str:
301
239
  status = (
302
240
  "filled" if self.filled else ("ready" if self.is_workable() else "pending")
303
241
  )
304
- phrase_info = f", phrase={self._phrase.name}" if self._phrase else ""
305
- return f"Form('{self.assignment}', {status}{phrase_info})"
242
+ return f"Form('{self.assignment}', {status})"
@@ -0,0 +1,44 @@
1
+ # Copyright (c) 2025 - 2026, HaiyangLi <quantocean.li at gmail dot com>
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """Tests for krons.core.base.log - DataLoggerConfig."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from pathlib import Path
9
+
10
+ import pytest
11
+
12
+ from krons.core.base.log import DataLoggerConfig
13
+
14
+
15
+ class TestDataLoggerConfig:
16
+ """Tests for DataLoggerConfig."""
17
+
18
+ def test_defaults(self):
19
+ """Config should have sensible defaults."""
20
+ config = DataLoggerConfig()
21
+ assert config.persist_dir == "./logs"
22
+ assert config.extension == ".jsonl"
23
+ assert config.auto_save_on_exit is True
24
+
25
+ def test_custom_values(self):
26
+ """Config should accept custom values."""
27
+ config = DataLoggerConfig(
28
+ persist_dir="/tmp/custom_logs",
29
+ extension=".json",
30
+ auto_save_on_exit=False,
31
+ )
32
+ assert config.persist_dir == "/tmp/custom_logs"
33
+ assert config.extension == ".json"
34
+ assert config.auto_save_on_exit is False
35
+
36
+ def test_extension_validation(self):
37
+ """Config should only accept .json or .jsonl."""
38
+ with pytest.raises(Exception):
39
+ DataLoggerConfig(extension=".csv")
40
+
41
+ def test_persist_dir_as_path(self):
42
+ """Config should accept Path object for persist_dir."""
43
+ config = DataLoggerConfig(persist_dir=Path("/tmp/logs"))
44
+ assert config.persist_dir == Path("/tmp/logs")