krons 0.1.1__tar.gz → 0.2.0__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.
- {krons-0.1.1 → krons-0.2.0}/.gitignore +2 -0
- {krons-0.1.1 → krons-0.2.0}/CLAUDE.md +120 -6
- {krons-0.1.1 → krons-0.2.0}/PKG-INFO +15 -1
- krons-0.2.0/cookbooks/007_fan_out_in.py +227 -0
- krons-0.2.0/cookbooks/test_conduct.py +72 -0
- {krons-0.1.1 → krons-0.2.0}/pyproject.toml +16 -1
- krons-0.2.0/src/krons/__init__.py +49 -0
- krons-0.2.0/src/krons/agent/__init__.py +144 -0
- krons-0.2.0/src/krons/agent/mcps/__init__.py +14 -0
- krons-0.2.0/src/krons/agent/mcps/loader.py +287 -0
- krons-0.2.0/src/krons/agent/mcps/wrapper.py +799 -0
- krons-0.2.0/src/krons/agent/message/__init__.py +20 -0
- krons-0.2.0/src/krons/agent/message/action.py +69 -0
- krons-0.2.0/src/krons/agent/message/assistant.py +52 -0
- krons-0.2.0/src/krons/agent/message/common.py +49 -0
- krons-0.2.0/src/krons/agent/message/instruction.py +130 -0
- krons-0.2.0/src/krons/agent/message/prepare_msg.py +187 -0
- krons-0.2.0/src/krons/agent/message/role.py +53 -0
- krons-0.2.0/src/krons/agent/message/system.py +53 -0
- krons-0.2.0/src/krons/agent/operations/__init__.py +82 -0
- krons-0.2.0/src/krons/agent/operations/act.py +100 -0
- krons-0.2.0/src/krons/agent/operations/generate.py +145 -0
- krons-0.2.0/src/krons/agent/operations/llm_reparse.py +89 -0
- krons-0.2.0/src/krons/agent/operations/operate.py +247 -0
- krons-0.2.0/src/krons/agent/operations/parse.py +243 -0
- krons-0.2.0/src/krons/agent/operations/react.py +286 -0
- krons-0.2.0/src/krons/agent/operations/specs.py +235 -0
- krons-0.2.0/src/krons/agent/operations/structure.py +151 -0
- krons-0.2.0/src/krons/agent/operations/utils.py +79 -0
- krons-0.2.0/src/krons/agent/providers/__init__.py +17 -0
- krons-0.2.0/src/krons/agent/providers/anthropic_messages.py +146 -0
- krons-0.2.0/src/krons/agent/providers/claude_code.py +276 -0
- krons-0.2.0/src/krons/agent/providers/gemini.py +268 -0
- krons-0.2.0/src/krons/agent/providers/match.py +75 -0
- krons-0.2.0/src/krons/agent/providers/oai_chat.py +174 -0
- krons-0.2.0/src/krons/agent/third_party/anthropic_models.py +154 -0
- krons-0.2.0/src/krons/agent/third_party/claude_code.py +682 -0
- krons-0.2.0/src/krons/agent/third_party/gemini_models.py +508 -0
- krons-0.2.0/src/krons/agent/third_party/openai_models.py +295 -0
- krons-0.2.0/src/krons/agent/tool.py +291 -0
- krons-0.2.0/src/krons/core/__init__.py +127 -0
- krons-0.2.0/src/krons/core/base/__init__.py +121 -0
- {krons-0.1.1/src/krons/core → krons-0.2.0/src/krons/core/base}/broadcaster.py +7 -3
- {krons-0.1.1/src/krons/core → krons-0.2.0/src/krons/core/base}/element.py +13 -5
- {krons-0.1.1/src/krons/core → krons-0.2.0/src/krons/core/base}/event.py +39 -6
- {krons-0.1.1/src/krons/core → krons-0.2.0/src/krons/core/base}/eventbus.py +3 -1
- {krons-0.1.1/src/krons/core → krons-0.2.0/src/krons/core/base}/flow.py +11 -4
- {krons-0.1.1/src/krons/core → krons-0.2.0/src/krons/core/base}/graph.py +24 -8
- {krons-0.1.1/src/krons/core → krons-0.2.0/src/krons/core/base}/node.py +44 -19
- {krons-0.1.1/src/krons/core → krons-0.2.0/src/krons/core/base}/pile.py +22 -8
- {krons-0.1.1/src/krons/core → krons-0.2.0/src/krons/core/base}/processor.py +21 -7
- {krons-0.1.1/src/krons/core → krons-0.2.0/src/krons/core/base}/progression.py +3 -1
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/specs/__init__.py +0 -5
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/specs/adapters/dataclass_field.py +16 -8
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/specs/adapters/pydantic_adapter.py +11 -5
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/specs/adapters/sql_ddl.py +14 -8
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/specs/catalog/__init__.py +2 -2
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/specs/catalog/_audit.py +2 -2
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/specs/catalog/_common.py +2 -2
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/specs/catalog/_content.py +4 -4
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/specs/catalog/_enforcement.py +3 -3
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/specs/factory.py +5 -5
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/specs/operable.py +8 -2
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/specs/protocol.py +4 -2
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/specs/spec.py +23 -11
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/types/base.py +4 -2
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/types/db_types.py +2 -2
- {krons-0.1.1 → krons-0.2.0}/src/krons/errors.py +13 -13
- {krons-0.1.1 → krons-0.2.0}/src/krons/protocols.py +9 -4
- krons-0.2.0/src/krons/resource/__init__.py +89 -0
- {krons-0.1.1/src/krons/services → krons-0.2.0/src/krons/resource}/backend.py +48 -22
- {krons-0.1.1/src/krons/services → krons-0.2.0/src/krons/resource}/endpoint.py +28 -14
- {krons-0.1.1/src/krons/services → krons-0.2.0/src/krons/resource}/hook.py +20 -7
- {krons-0.1.1/src/krons/services → krons-0.2.0/src/krons/resource}/imodel.py +46 -28
- {krons-0.1.1/src/krons/services → krons-0.2.0/src/krons/resource}/registry.py +26 -24
- {krons-0.1.1/src/krons/services → krons-0.2.0/src/krons/resource}/utilities/rate_limited_executor.py +7 -3
- {krons-0.1.1/src/krons/services → krons-0.2.0/src/krons/resource}/utilities/rate_limiter.py +3 -1
- {krons-0.1.1/src/krons/services → krons-0.2.0/src/krons/resource}/utilities/resilience.py +15 -5
- krons-0.2.0/src/krons/resource/utilities/token_calculator.py +185 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/session/__init__.py +12 -17
- krons-0.2.0/src/krons/session/constraints.py +70 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/session/exchange.py +11 -3
- {krons-0.1.1 → krons-0.2.0}/src/krons/session/message.py +3 -1
- krons-0.2.0/src/krons/session/registry.py +35 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/session/session.py +165 -174
- krons-0.2.0/src/krons/utils/__init__.py +85 -0
- krons-0.2.0/src/krons/utils/_function_arg_parser.py +99 -0
- krons-0.2.0/src/krons/utils/_pythonic_function_call.py +249 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/_to_list.py +9 -3
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/_utils.py +6 -2
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/concurrency/_async_call.py +4 -2
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/concurrency/_errors.py +3 -1
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/concurrency/_patterns.py +3 -1
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/concurrency/_resource_tracker.py +6 -2
- krons-0.2.0/src/krons/utils/display.py +257 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/fuzzy/__init__.py +6 -1
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/fuzzy/_fuzzy_match.py +14 -8
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/fuzzy/_string_similarity.py +3 -1
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/fuzzy/_to_dict.py +3 -1
- krons-0.2.0/src/krons/utils/schemas/__init__.py +26 -0
- krons-0.2.0/src/krons/utils/schemas/_breakdown_pydantic_annotation.py +131 -0
- krons-0.2.0/src/krons/utils/schemas/_formatter.py +72 -0
- krons-0.2.0/src/krons/utils/schemas/_minimal_yaml.py +151 -0
- krons-0.2.0/src/krons/utils/schemas/_typescript.py +153 -0
- krons-0.2.0/src/krons/utils/validators/__init__.py +3 -0
- krons-0.2.0/src/krons/utils/validators/_validate_image_url.py +56 -0
- krons-0.2.0/src/krons/work/__init__.py +126 -0
- krons-0.2.0/src/krons/work/engine.py +333 -0
- krons-0.2.0/src/krons/work/form.py +305 -0
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/work}/operations/__init__.py +7 -4
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/work}/operations/builder.py +1 -1
- {krons-0.1.1/src/krons/enforcement → krons-0.2.0/src/krons/work/operations}/context.py +36 -5
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/work}/operations/flow.py +13 -5
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/work}/operations/node.py +45 -43
- krons-0.2.0/src/krons/work/operations/registry.py +103 -0
- {krons-0.1.1/src/krons/specs → krons-0.2.0/src/krons/work}/phrase.py +130 -13
- {krons-0.1.1/src/krons/enforcement → krons-0.2.0/src/krons/work}/policy.py +3 -3
- krons-0.2.0/src/krons/work/report.py +268 -0
- krons-0.2.0/src/krons/work/rules/__init__.py +47 -0
- {krons-0.1.1/src/krons/enforcement → krons-0.2.0/src/krons/work/rules}/common/boolean.py +3 -1
- {krons-0.1.1/src/krons/enforcement → krons-0.2.0/src/krons/work/rules}/common/choice.py +9 -3
- {krons-0.1.1/src/krons/enforcement → krons-0.2.0/src/krons/work/rules}/common/number.py +3 -1
- {krons-0.1.1/src/krons/enforcement → krons-0.2.0/src/krons/work/rules}/common/string.py +9 -3
- {krons-0.1.1/src/krons/enforcement → krons-0.2.0/src/krons/work/rules}/rule.py +1 -1
- {krons-0.1.1/src/krons/enforcement → krons-0.2.0/src/krons/work/rules}/validator.py +20 -5
- {krons-0.1.1/src/krons/enforcement → krons-0.2.0/src/krons/work}/service.py +16 -7
- krons-0.2.0/src/krons/work/worker.py +266 -0
- krons-0.2.0/tests/agent/test_mcps_unit.py +216 -0
- krons-0.2.0/tests/agent/test_message.py +220 -0
- krons-0.2.0/tests/agent/test_providers_e2e.py +339 -0
- krons-0.2.0/tests/agent/test_providers_unit.py +276 -0
- krons-0.2.0/tests/agent/test_tool_unit.py +293 -0
- {krons-0.1.1 → krons-0.2.0}/tests/conftest.py +7 -3
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_broadcaster.py +1 -2
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_error_paths.py +13 -13
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_event.py +2 -3
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_event_status_race.py +2 -2
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_flow_edge_cases.py +9 -3
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_node.py +12 -11
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_processor.py +1 -2
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_processor_security.py +49 -19
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_serialization_roundtrip.py +59 -18
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_thread_safety_stress.py +4 -2
- {krons-0.1.1 → krons-0.2.0}/tests/enforcement/test_service.py +34 -38
- {krons-0.1.1 → krons-0.2.0}/tests/operations/test_builder.py +23 -7
- {krons-0.1.1 → krons-0.2.0}/tests/operations/test_flow.py +22 -17
- {krons-0.1.1 → krons-0.2.0}/tests/operations/test_node.py +7 -45
- {krons-0.1.1 → krons-0.2.0}/tests/operations/test_op_flow.py +103 -79
- {krons-0.1.1 → krons-0.2.0}/tests/operations/test_op_node.py +6 -12
- {krons-0.1.1 → krons-0.2.0}/tests/operations/test_registry.py +28 -37
- {krons-0.1.1/tests/services → krons-0.2.0/tests/resource}/test_backend.py +74 -59
- {krons-0.1.1/tests/services → krons-0.2.0/tests/resource}/test_endpoint.py +8 -4
- {krons-0.1.1/tests/services → krons-0.2.0/tests/resource}/test_hook.py +38 -18
- {krons-0.1.1/tests/services → krons-0.2.0/tests/resource}/test_imodel.py +11 -9
- {krons-0.1.1/tests/services → krons-0.2.0/tests/resource}/test_registry.py +21 -21
- {krons-0.1.1/tests/services → krons-0.2.0/tests/resource}/utilities/test_header_factory.py +4 -2
- {krons-0.1.1/tests/services → krons-0.2.0/tests/resource}/utilities/test_rate_limiter.py +4 -4
- {krons-0.1.1/tests/services → krons-0.2.0/tests/resource}/utilities/test_resilience.py +22 -8
- {krons-0.1.1/tests/services → krons-0.2.0/tests/resource}/utilities/test_resilience_retry.py +23 -13
- {krons-0.1.1 → krons-0.2.0}/tests/rules/test_base.py +5 -2
- {krons-0.1.1 → krons-0.2.0}/tests/rules/test_builtin.py +1 -1
- {krons-0.1.1 → krons-0.2.0}/tests/rules/test_registry.py +5 -3
- {krons-0.1.1 → krons-0.2.0}/tests/rules/test_rule_params_edge_cases.py +1 -1
- {krons-0.1.1 → krons-0.2.0}/tests/rules/test_rules_comprehensive.py +18 -8
- {krons-0.1.1 → krons-0.2.0}/tests/rules/test_validator.py +12 -6
- {krons-0.1.1 → krons-0.2.0}/tests/session/test_exchange.py +3 -1
- {krons-0.1.1 → krons-0.2.0}/tests/session/test_message.py +21 -7
- {krons-0.1.1 → krons-0.2.0}/tests/session/test_message_edge_cases.py +6 -2
- {krons-0.1.1 → krons-0.2.0}/tests/session/test_session.py +34 -27
- {krons-0.1.1 → krons-0.2.0}/tests/session/test_session_edge_cases.py +17 -20
- {krons-0.1.1 → krons-0.2.0}/tests/specs/test_catalog.py +2 -2
- {krons-0.1.1 → krons-0.2.0}/tests/specs/test_factory.py +8 -8
- {krons-0.1.1 → krons-0.2.0}/tests/specs/test_phrase.py +3 -3
- {krons-0.1.1 → krons-0.2.0}/tests/test_protocols.py +4 -1
- {krons-0.1.1 → krons-0.2.0}/tests/types/conftest.py +5 -3
- {krons-0.1.1 → krons-0.2.0}/tests/types/spec_adapters/test_adapters_py311.py +12 -6
- {krons-0.1.1 → krons-0.2.0}/tests/types/spec_adapters/test_protocol.py +1 -1
- {krons-0.1.1 → krons-0.2.0}/tests/types/spec_adapters/test_pydantic_field.py +14 -6
- {krons-0.1.1 → krons-0.2.0}/tests/types/spec_adapters/test_sql_ddl_specs.py +13 -7
- {krons-0.1.1 → krons-0.2.0}/tests/types/test_db_types.py +1 -1
- {krons-0.1.1 → krons-0.2.0}/tests/types/test_identity.py +1 -1
- {krons-0.1.1 → krons-0.2.0}/tests/types/test_model.py +1 -1
- {krons-0.1.1 → krons-0.2.0}/tests/types/test_operable.py +9 -5
- {krons-0.1.1 → krons-0.2.0}/tests/types/test_sentinel.py +1 -1
- {krons-0.1.1 → krons-0.2.0}/tests/types/test_spec.py +15 -13
- {krons-0.1.1 → krons-0.2.0}/tests/types/test_types.py +7 -3
- {krons-0.1.1 → krons-0.2.0}/tests/types/test_types_py311.py +9 -2
- krons-0.2.0/tests/utils/concurrency/__init__.py +2 -0
- {krons-0.1.1 → krons-0.2.0}/tests/utils/concurrency/test_async_call.py +3 -1
- {krons-0.1.1 → krons-0.2.0}/tests/utils/concurrency/test_cancel.py +3 -1
- {krons-0.1.1 → krons-0.2.0}/tests/utils/concurrency/test_patterns.py +16 -5
- {krons-0.1.1 → krons-0.2.0}/tests/utils/concurrency/test_run_async.py +3 -1
- {krons-0.1.1 → krons-0.2.0}/tests/utils/test_fuzzy_json.py +3 -1
- {krons-0.1.1 → krons-0.2.0}/tests/utils/test_fuzzy_match.py +9 -3
- {krons-0.1.1 → krons-0.2.0}/tests/utils/test_hash.py +3 -1
- {krons-0.1.1 → krons-0.2.0}/tests/utils/test_json_dump.py +3 -1
- {krons-0.1.1 → krons-0.2.0}/tests/utils/test_string_similarity.py +12 -4
- {krons-0.1.1 → krons-0.2.0}/tests/utils/test_to_dict.py +3 -1
- krons-0.2.0/tests/work/__init__.py +4 -0
- krons-0.2.0/tests/work/test_engine.py +817 -0
- krons-0.2.0/tests/work/test_form.py +640 -0
- krons-0.2.0/tests/work/test_phrase.py +1141 -0
- krons-0.2.0/tests/work/test_report.py +550 -0
- krons-0.2.0/tests/work/test_worker.py +665 -0
- krons-0.2.0/uv.lock +2628 -0
- krons-0.1.1/src/krons/core/__init__.py +0 -145
- krons-0.1.1/src/krons/enforcement/__init__.py +0 -57
- krons-0.1.1/src/krons/operations/registry.py +0 -92
- krons-0.1.1/src/krons/services/__init__.py +0 -81
- krons-0.1.1/src/krons/utils/__init__.py +0 -40
- krons-0.1.1/uv.lock +0 -641
- {krons-0.1.1 → krons-0.2.0}/.github/workflows/release.yml +0 -0
- {krons-0.1.1 → krons-0.2.0}/.python-version +0 -0
- {krons-0.1.1 → krons-0.2.0}/LICENSE +0 -0
- {krons-0.1.1 → krons-0.2.0}/README.md +0 -0
- {krons-0.1.1/tests → krons-0.2.0/src/krons/agent/third_party}/__init__.py +0 -0
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core/specs/adapters}/__init__.py +0 -0
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/specs/adapters/_utils.py +0 -0
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/specs/adapters/factory.py +0 -0
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/types/__init__.py +0 -0
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/types/_sentinel.py +0 -0
- {krons-0.1.1/src/krons → krons-0.2.0/src/krons/core}/types/identity.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/py.typed +0 -0
- {krons-0.1.1/src/krons/services → krons-0.2.0/src/krons/resource}/utilities/__init__.py +0 -0
- {krons-0.1.1/src/krons/services → krons-0.2.0/src/krons/resource}/utilities/header_factory.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/_hash.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/_json_dump.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/_lazy_init.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/_to_num.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/concurrency/__init__.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/concurrency/_cancel.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/concurrency/_primitives.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/concurrency/_priority_queue.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/concurrency/_run_async.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/concurrency/_task.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/concurrency/_utils.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/fuzzy/_extract_json.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/fuzzy/_fuzzy_json.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/sql/__init__.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/src/krons/utils/sql/_sql_validation.py +0 -0
- {krons-0.1.1/src/krons/enforcement → krons-0.2.0/src/krons/work/rules}/common/__init__.py +0 -0
- {krons-0.1.1/src/krons/enforcement → krons-0.2.0/src/krons/work/rules}/common/mapping.py +0 -0
- {krons-0.1.1/src/krons/enforcement → krons-0.2.0/src/krons/work/rules}/common/model.py +0 -0
- {krons-0.1.1/src/krons/enforcement → krons-0.2.0/src/krons/work/rules}/registry.py +0 -0
- {krons-0.1.1/tests/core → krons-0.2.0/tests}/__init__.py +0 -0
- {krons-0.1.1/src/krons/specs/adapters → krons-0.2.0/tests/agent}/__init__.py +0 -0
- {krons-0.1.1/tests/enforcement → krons-0.2.0/tests/core}/__init__.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_element.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_eventbus.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_flow.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_graph.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_graph_event_loop.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_pile.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_pile_edge_cases.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_progression.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/core/test_progression_edge_cases.py +0 -0
- {krons-0.1.1/tests/operations → krons-0.2.0/tests/enforcement}/__init__.py +0 -0
- {krons-0.1.1/tests/rules → krons-0.2.0/tests/operations}/__init__.py +0 -0
- {krons-0.1.1/tests/services → krons-0.2.0/tests/resource}/__init__.py +0 -0
- {krons-0.1.1/tests/services → krons-0.2.0/tests/resource}/utilities/__init__.py +0 -0
- {krons-0.1.1/tests/session → krons-0.2.0/tests/rules}/__init__.py +0 -0
- {krons-0.1.1/tests/types → krons-0.2.0/tests/session}/__init__.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/specs/__init__.py +0 -0
- {krons-0.1.1/tests/types/spec_adapters → krons-0.2.0/tests/types}/__init__.py +0 -0
- {krons-0.1.1/tests/utils → krons-0.2.0/tests/types/spec_adapters}/__init__.py +0 -0
- {krons-0.1.1/tests/utils/concurrency → krons-0.2.0/tests/utils}/__init__.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/utils/concurrency/test_errors.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/utils/concurrency/test_primitives.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/utils/concurrency/test_priority_queue.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/utils/concurrency/test_task.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/utils/concurrency/test_utils.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/utils/test_extract_json.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/utils/test_lazy_init.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/utils/test_sql_validation.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/utils/test_to_list.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/utils/test_to_num.py +0 -0
- {krons-0.1.1 → krons-0.2.0}/tests/utils/test_utils.py +0 -0
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
- **Spec/Operable**: Type-safe field definitions with validation, defaults, and DB metadata
|
|
8
8
|
- **Node**: Polymorphic content containers with DB serialization
|
|
9
9
|
- **Services**: Unified service interfaces (iModel, KronService) with hooks and rate limiting
|
|
10
|
-
- **
|
|
10
|
+
- **Work**: Declarative workflow orchestration with Report/Worker pattern
|
|
11
|
+
- **Rules**: Validation rules and enforcement
|
|
11
12
|
|
|
12
13
|
## Architecture
|
|
13
14
|
|
|
@@ -17,10 +18,11 @@ krons/
|
|
|
17
18
|
├── specs/ # Spec definitions, Operable composition, adapters
|
|
18
19
|
│ ├── catalog/ # Pre-built specs (Content, Audit, Common, Enforcement)
|
|
19
20
|
│ └── adapters/ # Pydantic, SQL DDL, Dataclass adapters
|
|
21
|
+
├── work/ # Workflow orchestration (Report, Worker, Form, Phrase)
|
|
20
22
|
├── services/ # Service backends, iModel, hooks, rate limiting
|
|
21
|
-
├──
|
|
23
|
+
├── operations/ # Operation builders, context, registry
|
|
24
|
+
├── rules/ # Validation rules, registry, common validators
|
|
22
25
|
├── types/ # Base types, sentinels, DB types (FK, Vector)
|
|
23
|
-
├── operations/ # Operation builders and registry
|
|
24
26
|
├── protocols.py # Runtime-checkable protocols with @implements
|
|
25
27
|
└── utils/ # Fuzzy matching, SQL utilities, helpers
|
|
26
28
|
```
|
|
@@ -192,6 +194,109 @@ fk_meta = extract_kron_db_meta(field_info, metas="FK")
|
|
|
192
194
|
vec_meta = extract_kron_db_meta(field_info, metas="Vector")
|
|
193
195
|
```
|
|
194
196
|
|
|
197
|
+
### 7. Work (Workflow Orchestration)
|
|
198
|
+
|
|
199
|
+
Two complementary patterns at different abstraction levels:
|
|
200
|
+
|
|
201
|
+
**Report** (artifact state) - Declarative workflow definition:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from krons.work import Report
|
|
205
|
+
|
|
206
|
+
class HiringBriefReport(Report):
|
|
207
|
+
"""Multi-step workflow with typed outputs as class attributes."""
|
|
208
|
+
|
|
209
|
+
# Typed output fields
|
|
210
|
+
role_classification: RoleClassification | None = None
|
|
211
|
+
strategic_context: StrategicContext | None = None
|
|
212
|
+
executive_summary: ExecutiveSummary | None = None
|
|
213
|
+
|
|
214
|
+
# Overall contract
|
|
215
|
+
assignment: str = "job_input, market_context -> executive_summary"
|
|
216
|
+
|
|
217
|
+
# Form assignments with branch/resource hints
|
|
218
|
+
# DSL: "branch: inputs -> outputs | resource"
|
|
219
|
+
form_assignments: list[str] = [
|
|
220
|
+
# Same branch = sequential execution
|
|
221
|
+
"classifier: job_input -> role_classification | api:fast",
|
|
222
|
+
"classifier: role_classification -> extracted_skills | api:fast",
|
|
223
|
+
|
|
224
|
+
# Different branches = parallel execution
|
|
225
|
+
"strategist: job_input, role_classification -> strategic_context | api:synthesis",
|
|
226
|
+
"writer: strategic_context -> executive_summary | api:reasoning",
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
# Execute workflow
|
|
230
|
+
report = HiringBriefReport()
|
|
231
|
+
report.initialize(job_input="...", market_context="...")
|
|
232
|
+
|
|
233
|
+
while not report.is_complete():
|
|
234
|
+
for form in report.next_forms(): # Data-driven scheduling
|
|
235
|
+
result = await execute_form(form) # Route to Worker via form.resource
|
|
236
|
+
form.set_output(result)
|
|
237
|
+
report.complete_form(form)
|
|
238
|
+
|
|
239
|
+
output = report.get_deliverable()
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Worker** (execution capability) - Functional station with @work methods:
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
from krons.work import Worker, WorkerEngine, work, worklink
|
|
246
|
+
|
|
247
|
+
class ClassifierWorker(Worker):
|
|
248
|
+
"""Execution capability with internal DAG for retries."""
|
|
249
|
+
|
|
250
|
+
name = "classifier"
|
|
251
|
+
|
|
252
|
+
@work(assignment="job_input -> role_classification", capacity=2)
|
|
253
|
+
async def classify_role(self, job_input, **kwargs):
|
|
254
|
+
result = await self.llm.chat(**kwargs)
|
|
255
|
+
return result.role_classification
|
|
256
|
+
|
|
257
|
+
@work(assignment="code -> execution_result")
|
|
258
|
+
async def execute_code(self, code):
|
|
259
|
+
error = run_code(code)
|
|
260
|
+
return code, error
|
|
261
|
+
|
|
262
|
+
# Conditional edge for retry loops
|
|
263
|
+
@worklink(from_="execute_code", to_="debug_code")
|
|
264
|
+
async def maybe_debug(self, result):
|
|
265
|
+
code, error = result
|
|
266
|
+
if error: # Only follow if error
|
|
267
|
+
return {"code": code, "error": error}
|
|
268
|
+
return None # Skip edge
|
|
269
|
+
|
|
270
|
+
# Execute with engine
|
|
271
|
+
engine = WorkerEngine(worker=ClassifierWorker())
|
|
272
|
+
task = await engine.add_task(task_function="classify_role", job_input="...")
|
|
273
|
+
await engine.execute()
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Key concepts:**
|
|
277
|
+
|
|
278
|
+
| Component | Role | State |
|
|
279
|
+
|-----------|------|-------|
|
|
280
|
+
| **Report** | Work order / artifact | Stateful (tracks one job) |
|
|
281
|
+
| **Worker** | Station / capability | Stateless (handles many jobs) |
|
|
282
|
+
| **Form** | Unit of work | Stateful (one assignment) |
|
|
283
|
+
| **Phrase** | Typed I/O signature | Definition only |
|
|
284
|
+
|
|
285
|
+
**Form assignment DSL:**
|
|
286
|
+
|
|
287
|
+
```
|
|
288
|
+
"branch: inputs -> outputs | resource"
|
|
289
|
+
|
|
290
|
+
Examples:
|
|
291
|
+
"a, b -> c" # Simple (no branch, no resource)
|
|
292
|
+
"classifier: job -> role | api:fast" # Full (branch + resource)
|
|
293
|
+
"writer: context -> summary" # Branch only
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
- **branch**: Groups forms for sequential execution (same branch = sequential)
|
|
297
|
+
- **resource**: Hint for routing to Worker capabilities (e.g., `api:fast`, `api:reasoning`)
|
|
298
|
+
- Forms without branch execute in parallel based on data availability
|
|
299
|
+
|
|
195
300
|
## Testing Patterns
|
|
196
301
|
|
|
197
302
|
### Test Structure
|
|
@@ -200,8 +305,10 @@ vec_meta = extract_kron_db_meta(field_info, metas="Vector")
|
|
|
200
305
|
tests/
|
|
201
306
|
├── core/ # Node, Element, Event tests
|
|
202
307
|
├── specs/ # Spec, Operable, Catalog tests
|
|
308
|
+
├── work/ # Report, Worker, Form, Phrase, Engine tests
|
|
203
309
|
├── services/ # iModel, hook tests
|
|
204
|
-
├──
|
|
310
|
+
├── operations/ # Operation, context tests
|
|
311
|
+
├── rules/ # Validation rule tests
|
|
205
312
|
└── utils/ # Utility function tests
|
|
206
313
|
```
|
|
207
314
|
|
|
@@ -251,11 +358,17 @@ class MockPolicyEngine:
|
|
|
251
358
|
5. **compose_structure frozen param**: Currently broken in PydanticSpecAdapter (doesn't accept
|
|
252
359
|
`frozen` kwarg).
|
|
253
360
|
|
|
361
|
+
6. **Report vs Worker**: They operate at different levels:
|
|
362
|
+
- Report = declarative workflow (WHAT to do) - subclass and define `form_assignments`
|
|
363
|
+
- Worker = execution capability (HOW to do it) - has `@work` methods
|
|
364
|
+
- Forms without explicit branch run in parallel; same branch = sequential
|
|
365
|
+
|
|
366
|
+
7. **Form assignment DSL**: The full format is `"branch: inputs -> outputs | resource"`.
|
|
367
|
+
All parts except `inputs -> outputs` are optional.
|
|
368
|
+
|
|
254
369
|
## Running Tests
|
|
255
370
|
|
|
256
371
|
```bash
|
|
257
|
-
cd libs/krons
|
|
258
|
-
|
|
259
372
|
# All tests
|
|
260
373
|
uv run pytest tests/ -q
|
|
261
374
|
|
|
@@ -263,6 +376,7 @@ uv run pytest tests/ -q
|
|
|
263
376
|
uv run pytest tests/ --cov=krons --cov-report=term-missing
|
|
264
377
|
|
|
265
378
|
# Specific module
|
|
379
|
+
uv run pytest tests/work/test_report.py -v
|
|
266
380
|
uv run pytest tests/specs/test_catalog.py -v
|
|
267
381
|
|
|
268
382
|
# Single test
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: krons
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
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,227 @@
|
|
|
1
|
+
# Copyright (c) 2025 - 2026, HaiyangLi <quantocean.li at gmail dot com>
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""Fan-out / fan-in pattern with Claude Code.
|
|
5
|
+
|
|
6
|
+
Pattern:
|
|
7
|
+
1. Register resources and operations with session
|
|
8
|
+
2. session.conduct() -> orchestrator produces structured Instruct tasks
|
|
9
|
+
3. Tasks fan out to parallel Claude Code instances via session.conduct()
|
|
10
|
+
4. Results fan in and are synthesized by orchestrator via session.conduct()
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
uv run python cookbooks/007_fan_out_in.py
|
|
14
|
+
uv run python cookbooks/007_fan_out_in.py --simple # Quick smoke test
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import sys
|
|
20
|
+
|
|
21
|
+
import anyio
|
|
22
|
+
from pydantic import BaseModel, Field
|
|
23
|
+
|
|
24
|
+
from krons.agent.operations import GenerateParams, Instruct, ReturnAs
|
|
25
|
+
from krons.agent.providers.claude_code import (
|
|
26
|
+
ClaudeCodeEndpoint,
|
|
27
|
+
create_claude_code_config,
|
|
28
|
+
)
|
|
29
|
+
from krons.resource import iModel
|
|
30
|
+
from krons.session import Session, SessionConfig
|
|
31
|
+
from krons.utils.display import Timer, as_readable, display, phase, status
|
|
32
|
+
from krons.utils.fuzzy import extract_json, fuzzy_validate_mapping
|
|
33
|
+
|
|
34
|
+
CC_WORKSPACE = ".khive/workspace"
|
|
35
|
+
VERBOSE = True
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
# Claude Code factory
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def create_cc(name: str, subdir: str, **kwargs) -> iModel:
|
|
44
|
+
"""Create a Claude Code iModel for a workspace subdirectory."""
|
|
45
|
+
config = create_claude_code_config(name=name)
|
|
46
|
+
config.update({"ws": f"{CC_WORKSPACE}/{subdir}", **kwargs})
|
|
47
|
+
endpoint = ClaudeCodeEndpoint(config=config)
|
|
48
|
+
return iModel(backend=endpoint)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
# Structured output models
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class InvestigationPlan(BaseModel):
|
|
57
|
+
"""Orchestrator's structured plan: analysis + parallel Instruct tasks."""
|
|
58
|
+
|
|
59
|
+
analysis: str = Field(description="Initial analysis of the codebase")
|
|
60
|
+
research_tasks: list[Instruct] = Field(
|
|
61
|
+
description="Three parallel research instructions for deeper investigation",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# ---------------------------------------------------------------------------
|
|
66
|
+
# Prompts
|
|
67
|
+
# ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
PLAN_PROMPT = (
|
|
70
|
+
"Investigate the codebase in the specified directory. "
|
|
71
|
+
"Glance over the key components, pay attention to architecture, "
|
|
72
|
+
"design patterns, and notable features. "
|
|
73
|
+
"Produce three parallel research instructions for deeper investigation."
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
PLAN_PROMPT_SIMPLE = (
|
|
77
|
+
"List the top 3 things to investigate in a Python project. "
|
|
78
|
+
"For each, provide a short research instruction."
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
SYNTHESIS_PROMPT = """\
|
|
82
|
+
Synthesize the information from the researcher branches into a cohesive overview:
|
|
83
|
+
1. Key components and their roles
|
|
84
|
+
2. Architectural patterns used
|
|
85
|
+
3. Design patterns and notable features
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
SYNTHESIS_PROMPT_SIMPLE = """\
|
|
89
|
+
Combine the researcher findings into a brief 2-3 sentence summary.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# ---------------------------------------------------------------------------
|
|
94
|
+
# Helpers
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def parse_plan(text: str) -> InvestigationPlan:
|
|
99
|
+
"""Extract InvestigationPlan from LLM text output."""
|
|
100
|
+
target_keys = list(InvestigationPlan.model_fields.keys())
|
|
101
|
+
extracted = extract_json(text, fuzzy_parse=True)
|
|
102
|
+
if not extracted:
|
|
103
|
+
raise ValueError("Failed to extract JSON from orchestrator response")
|
|
104
|
+
|
|
105
|
+
block = extracted[0] if isinstance(extracted, list) else extracted
|
|
106
|
+
validated = fuzzy_validate_mapping(block, target_keys)
|
|
107
|
+
return InvestigationPlan.model_validate(validated)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
# Main
|
|
112
|
+
# ---------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
async def main(simple: bool = False):
|
|
116
|
+
plan_prompt = PLAN_PROMPT_SIMPLE if simple else PLAN_PROMPT
|
|
117
|
+
synth_prompt = SYNTHESIS_PROMPT_SIMPLE if simple else SYNTHESIS_PROMPT
|
|
118
|
+
context = ["lionagi"] if not simple else None
|
|
119
|
+
|
|
120
|
+
# --- 1. Create resources ---
|
|
121
|
+
orc_model = create_cc("orchestrator", "orchestrator")
|
|
122
|
+
|
|
123
|
+
# --- 2. Create session with resources and operations ---
|
|
124
|
+
session = Session(
|
|
125
|
+
config=SessionConfig(
|
|
126
|
+
default_branch_name="orchestrator",
|
|
127
|
+
default_gen_model="orchestrator",
|
|
128
|
+
shared_resources={"orchestrator"},
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
session.resources.register(orc_model)
|
|
132
|
+
|
|
133
|
+
orc_branch = session.default_branch
|
|
134
|
+
status(
|
|
135
|
+
f"Session ready: {len(session.resources)} resources, branch={orc_branch.name}"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# --- 3. Phase 1: Plan (structured output from orchestrator) ---
|
|
139
|
+
phase("Phase 1: Planning")
|
|
140
|
+
|
|
141
|
+
plan_op = await session.conduct(
|
|
142
|
+
"generate",
|
|
143
|
+
orc_branch,
|
|
144
|
+
GenerateParams(
|
|
145
|
+
primary=plan_prompt,
|
|
146
|
+
context=context,
|
|
147
|
+
request_model=InvestigationPlan,
|
|
148
|
+
imodel="orchestrator",
|
|
149
|
+
return_as=ReturnAs.TEXT,
|
|
150
|
+
),
|
|
151
|
+
verbose=VERBOSE,
|
|
152
|
+
)
|
|
153
|
+
plan = parse_plan(plan_op.execution.response)
|
|
154
|
+
|
|
155
|
+
status(f"Analysis: {plan.analysis[:120]}...")
|
|
156
|
+
status(f"Research tasks: {len(plan.research_tasks)}")
|
|
157
|
+
for i, task in enumerate(plan.research_tasks):
|
|
158
|
+
status(f" [{i + 1}] {(task.instruction or '')[:80]}...")
|
|
159
|
+
|
|
160
|
+
if VERBOSE:
|
|
161
|
+
display(
|
|
162
|
+
as_readable(plan, format_curly=True),
|
|
163
|
+
title="Investigation Plan",
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# --- 4. Phase 2: Fan-out (parallel research) ---
|
|
167
|
+
phase("Phase 2: Parallel Research")
|
|
168
|
+
results: list[str | None] = [None] * len(plan.research_tasks)
|
|
169
|
+
|
|
170
|
+
async def run_research(idx: int, task: Instruct) -> None:
|
|
171
|
+
name = f"researcher_{idx}"
|
|
172
|
+
researcher = create_cc(name, name)
|
|
173
|
+
session.resources.register(researcher, update=True)
|
|
174
|
+
branch = session.create_branch(name=name, resources={name})
|
|
175
|
+
|
|
176
|
+
instruction = task.instruction or ""
|
|
177
|
+
if task.guidance:
|
|
178
|
+
instruction += f"\n\nGuidance: {task.guidance}"
|
|
179
|
+
|
|
180
|
+
op = await session.conduct(
|
|
181
|
+
"generate",
|
|
182
|
+
branch,
|
|
183
|
+
GenerateParams(
|
|
184
|
+
primary=instruction,
|
|
185
|
+
context=task.context,
|
|
186
|
+
imodel=name,
|
|
187
|
+
return_as=ReturnAs.TEXT,
|
|
188
|
+
),
|
|
189
|
+
verbose=VERBOSE,
|
|
190
|
+
)
|
|
191
|
+
results[idx] = op.execution.response
|
|
192
|
+
status(
|
|
193
|
+
f"Researcher {idx + 1} done ({len(results[idx] or '')} chars)",
|
|
194
|
+
style="success",
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
async with anyio.create_task_group() as tg:
|
|
198
|
+
for i, task in enumerate(plan.research_tasks):
|
|
199
|
+
tg.start_soon(run_research, i, task)
|
|
200
|
+
|
|
201
|
+
# --- 5. Phase 3: Fan-in (synthesis) ---
|
|
202
|
+
phase("Phase 3: Synthesis")
|
|
203
|
+
research_context = [
|
|
204
|
+
f"--- Researcher {i + 1} ---\n{r}"
|
|
205
|
+
for i, r in enumerate(results)
|
|
206
|
+
if r is not None
|
|
207
|
+
]
|
|
208
|
+
|
|
209
|
+
synth_op = await session.conduct(
|
|
210
|
+
"generate",
|
|
211
|
+
orc_branch,
|
|
212
|
+
GenerateParams(
|
|
213
|
+
primary=synth_prompt,
|
|
214
|
+
context=research_context,
|
|
215
|
+
imodel="orchestrator",
|
|
216
|
+
return_as=ReturnAs.TEXT,
|
|
217
|
+
),
|
|
218
|
+
verbose=VERBOSE,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
phase("Final Synthesis")
|
|
222
|
+
print(synth_op.execution.response)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
if __name__ == "__main__":
|
|
226
|
+
simple = "--simple" in sys.argv
|
|
227
|
+
anyio.run(main, simple)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Copyright (c) 2025 - 2026, HaiyangLi <quantocean.li at gmail dot com>
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""Minimal test: one structured output via session.conduct("structure").
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
uv run python cookbooks/test_conduct.py
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import anyio
|
|
13
|
+
from pydantic import BaseModel, Field
|
|
14
|
+
|
|
15
|
+
from krons.agent.operations import GenerateParams, Instruct, StructureParams
|
|
16
|
+
from krons.agent.providers.claude_code import (
|
|
17
|
+
ClaudeCodeEndpoint,
|
|
18
|
+
create_claude_code_config,
|
|
19
|
+
)
|
|
20
|
+
from krons.core.specs import Operable
|
|
21
|
+
from krons.resource import iModel
|
|
22
|
+
from krons.session import Session, SessionConfig
|
|
23
|
+
from krons.utils.display import as_readable, display, phase, status
|
|
24
|
+
from krons.work.rules.validator import Validator
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Plan(BaseModel):
|
|
28
|
+
analysis: str = Field(description="One sentence analysis")
|
|
29
|
+
tasks: list[Instruct] = Field(description="Two short research tasks")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def main():
|
|
33
|
+
# Setup
|
|
34
|
+
config = create_claude_code_config(name="test")
|
|
35
|
+
endpoint = ClaudeCodeEndpoint(config=config)
|
|
36
|
+
model = iModel(backend=endpoint)
|
|
37
|
+
|
|
38
|
+
session = Session(
|
|
39
|
+
config=SessionConfig(
|
|
40
|
+
default_branch_name="main",
|
|
41
|
+
default_gen_model="test",
|
|
42
|
+
shared_resources={"test"},
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
session.resources.register(model)
|
|
46
|
+
|
|
47
|
+
phase("Structured Output Test (structure operation)")
|
|
48
|
+
|
|
49
|
+
operable = Operable.from_structure(Plan)
|
|
50
|
+
|
|
51
|
+
op = await session.conduct(
|
|
52
|
+
"structure",
|
|
53
|
+
params=StructureParams(
|
|
54
|
+
generate_params=GenerateParams(
|
|
55
|
+
primary="Name two colors. For each, give a one-sentence research task.",
|
|
56
|
+
request_model=Plan,
|
|
57
|
+
imodel="test",
|
|
58
|
+
),
|
|
59
|
+
validator=Validator(),
|
|
60
|
+
operable=operable,
|
|
61
|
+
strict=False,
|
|
62
|
+
),
|
|
63
|
+
verbose=True,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
result = op.execution.response
|
|
67
|
+
status(f"Result type: {type(result).__name__}")
|
|
68
|
+
display(as_readable(result, format_curly=True), title="Structured Output")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if __name__ == "__main__":
|
|
72
|
+
anyio.run(main)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "krons"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "Spec-based composable framework for building type-safe systems"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -25,6 +25,7 @@ dependencies = [
|
|
|
25
25
|
"httpx>=0.26.0",
|
|
26
26
|
"orjson>=3.10.0",
|
|
27
27
|
"pydantic>=2.10.0",
|
|
28
|
+
"pyyaml>=6.0.3",
|
|
28
29
|
"rapidfuzz>=3.10.0",
|
|
29
30
|
]
|
|
30
31
|
|
|
@@ -41,6 +42,8 @@ dev = [
|
|
|
41
42
|
"pytest>=8.0.0",
|
|
42
43
|
"pytest-asyncio>=0.24.0",
|
|
43
44
|
"hypothesis>=6.150.2",
|
|
45
|
+
"ipykernel>=7.1.0",
|
|
46
|
+
"ipywidgets>=8.1.8",
|
|
44
47
|
]
|
|
45
48
|
|
|
46
49
|
[build-system]
|
|
@@ -58,4 +61,16 @@ markers = [
|
|
|
58
61
|
"unit: marks tests as unit tests",
|
|
59
62
|
"integration: marks tests as integration tests",
|
|
60
63
|
"slow: marks tests as slow",
|
|
64
|
+
"property: marks tests as property-based tests (hypothesis)",
|
|
61
65
|
]
|
|
66
|
+
|
|
67
|
+
[project.optional-dependencies]
|
|
68
|
+
tiktoken = ["tiktoken>=0.11.0",]
|
|
69
|
+
fastmcp = ["fastmcp>=2.14.4",]
|
|
70
|
+
|
|
71
|
+
display = ["rich>=13.0"]
|
|
72
|
+
|
|
73
|
+
agent = [
|
|
74
|
+
"krons[tiktoken, fastmcp]",
|
|
75
|
+
]
|
|
76
|
+
all = ["krons[agent, display]"]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Copyright (c) 2025 - 2026, HaiyangLi <quantocean.li at gmail dot com>
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
# Lazy module re-exports via __getattr__
|
|
9
|
+
_MODULE_ALIASES: dict[str, str] = {
|
|
10
|
+
"types": "krons.core.types",
|
|
11
|
+
"specs": "krons.core.specs",
|
|
12
|
+
"session": "krons.session",
|
|
13
|
+
"operations": "krons.work.operations",
|
|
14
|
+
"agent": "krons.agent",
|
|
15
|
+
"resource": "krons.resource",
|
|
16
|
+
"work": "krons.work",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
_LOADED_MODULES: dict[str, object] = {}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def __getattr__(name: str) -> object:
|
|
23
|
+
"""Lazy load aliased modules."""
|
|
24
|
+
if name in _LOADED_MODULES:
|
|
25
|
+
return _LOADED_MODULES[name]
|
|
26
|
+
|
|
27
|
+
if name in _MODULE_ALIASES:
|
|
28
|
+
from importlib import import_module
|
|
29
|
+
|
|
30
|
+
module = import_module(_MODULE_ALIASES[name])
|
|
31
|
+
_LOADED_MODULES[name] = module
|
|
32
|
+
return module
|
|
33
|
+
|
|
34
|
+
raise AttributeError(f"module 'krons' has no attribute {name!r}")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def __dir__() -> list[str]:
|
|
38
|
+
"""List available attributes."""
|
|
39
|
+
return list(_MODULE_ALIASES.keys())
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
if TYPE_CHECKING:
|
|
43
|
+
from krons import agent as agent
|
|
44
|
+
from krons import resource as resource
|
|
45
|
+
from krons import session as session
|
|
46
|
+
from krons import work as work
|
|
47
|
+
from krons.core import specs as specs
|
|
48
|
+
from krons.core import types as types
|
|
49
|
+
from krons.work import operations as operations
|