lionagi 0.14.5__tar.gz → 0.14.6__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.
- {lionagi-0.14.5 → lionagi-0.14.6}/PKG-INFO +1 -1
- lionagi-0.14.6/docs/discussions/anyio-migration.md +178 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/fields/instruct.py +3 -17
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/concurrency/cancel.py +1 -1
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/builder.py +9 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/flow.py +163 -60
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/generic/pile.py +34 -15
- lionagi-0.14.6/lionagi/service/connections/providers/_claude_code/__init__.py +3 -0
- lionagi-0.14.6/lionagi/service/connections/providers/_claude_code/models.py +235 -0
- lionagi-0.14.5/lionagi/service/connections/providers/claude_code_cli.py → lionagi-0.14.6/lionagi/service/connections/providers/_claude_code/stream_cli.py +20 -80
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/connections/providers/claude_code_.py +13 -223
- lionagi-0.14.6/lionagi/service/connections/providers/claude_code_cli.py +105 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/session/branch.py +6 -46
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/session/session.py +26 -8
- lionagi-0.14.6/lionagi/version.py +1 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/pyproject.toml +1 -1
- {lionagi-0.14.5 → lionagi-0.14.6}/uv.lock +1 -1
- lionagi-0.14.5/lionagi/version.py +0 -1
- {lionagi-0.14.5 → lionagi-0.14.6}/.coveragerc +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/.env.example +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/.github/FUNDING.yml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/.github/dependabot.yml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/.github/workflows/ci.yml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/.github/workflows/codeql.yml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/.github/workflows/docs.yml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/.github/workflows/release.yml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/.gitignore +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/.pre-commit-config.yaml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/.python-version +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/CODE_OF_CONDUCT.md +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/CONTRIBUTING.md +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/LICENSE +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/README.md +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/assets/operation_builder.gif +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/cookbooks/001_branch_converse.ipynb +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/cookbooks/002_branch_interact.ipynb +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/cookbooks/003_branch_info.ipynb +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/cookbooks/004_conversation_patterns.ipynb +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/cookbooks/005_react_basics.ipynb +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/cookbooks/006_operation_graphs_claim_extraction.ipynb +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/cookbooks/data/002_comedian.json +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/cookbooks/data/002_critic.json +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/cookbooks/data/006_lion_proof_ch2.md +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/cookbooks/using_claude_code.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/Makefile +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/_static/custom.css +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/_templates/layout.html +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/conf.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/index.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/action.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/adapter.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/branch.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/branch_operations.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/concepts.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/element_id.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/event.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/form.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/graph.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/index.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/instruct.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/lib_file.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/lib_nested.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/lib_package.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/lib_schema.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/lib_validate.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/log.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/mail.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/message.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/models.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/operative_step.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/pile.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/processor.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/progression.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/service.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/session.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/modules/utils.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/tutorials/get_started.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/tutorials/get_started_pt2.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/tutorials/get_started_pt3.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/docs/tutorials/index.rst +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/_class_registry.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/_errors.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/_types.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/adapters/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/adapters/async_postgres_adapter.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/adapters/postgres_model_adapter.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/config.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/fields/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/fields/action.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/fields/base.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/fields/code.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/fields/file.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/fields/reason.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/fields/research.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/concurrency/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/concurrency/errors.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/concurrency/patterns.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/concurrency/primitives.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/concurrency/resource_tracker.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/concurrency/task.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/file/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/file/chunk.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/file/concat.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/file/concat_files.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/file/file_ops.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/file/params.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/file/process.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/file/save.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/nested/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/nested/flatten.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/nested/nfilter.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/nested/nget.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/nested/ninsert.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/nested/nmerge.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/nested/npop.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/nested/nset.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/nested/unflatten.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/nested/utils.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/package/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/package/imports.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/package/management.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/package/params.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/package/system.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/parse.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/schema/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/schema/as_readable.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/schema/extract_code_block.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/schema/extract_docstring.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/schema/function_to_schema.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/schema/json_schema.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/schema/load_pydantic_model_from_schema.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/base.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/llmlingua.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/perplexity.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/symbolic_compress_context.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/synthlang.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/synthlang_/base.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/synthlang_/resources/frameworks/abstract_algebra.toml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/synthlang_/resources/frameworks/category_theory.toml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/synthlang_/resources/frameworks/complex_analysis.toml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/synthlang_/resources/frameworks/framework_options.json +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/synthlang_/resources/frameworks/group_theory.toml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/synthlang_/resources/frameworks/math_logic.toml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/synthlang_/resources/frameworks/reflective_patterns.toml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/synthlang_/resources/frameworks/set_theory.toml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/synthlang_/resources/frameworks/topology_fundamentals.toml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/synthlang_/resources/mapping/lion_emoji_mapping.toml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/synthlang_/resources/mapping/python_math_mapping.toml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/synthlang_/resources/mapping/rust_chinese_mapping.toml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/synthlang_/resources/utility/base_synthlang_system_prompt.toml +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/synthlang_/translate_to_synthlang.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/token_transform/types.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/validate/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/validate/common_field_validators.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/validate/fuzzy_match_keys.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/validate/fuzzy_validate_mapping.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/validate/string_similarity.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/libs/validate/validate_boolean.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/models/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/models/field_model.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/models/hashable_model.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/models/model_params.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/models/note.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/models/operable_model.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/models/schema_model.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/ReAct/ReAct.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/ReAct/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/ReAct/utils.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/_act/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/_act/act.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/brainstorm/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/brainstorm/brainstorm.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/brainstorm/prompt.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/chat/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/chat/chat.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/communicate/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/communicate/communicate.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/instruct/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/instruct/instruct.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/interpret/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/interpret/interpret.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/manager.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/node.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/operate/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/operate/operate.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/parse/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/parse/parse.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/plan/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/plan/plan.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/plan/prompt.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/select/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/select/select.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/select/utils.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/translate/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/translate/translate.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/types.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/operations/utils.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/_concepts.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/action/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/action/function_calling.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/action/manager.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/action/tool.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/forms/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/forms/base.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/forms/flow.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/forms/form.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/forms/report.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/generic/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/generic/element.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/generic/event.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/generic/log.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/generic/processor.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/generic/progression.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/graph/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/graph/edge.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/graph/graph.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/graph/node.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/mail/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/mail/exchange.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/mail/mail.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/mail/mailbox.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/mail/manager.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/mail/package.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/messages/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/messages/action_request.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/messages/action_response.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/messages/assistant_response.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/messages/base.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/messages/instruction.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/messages/manager.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/messages/message.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/messages/system.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/messages/templates/README.md +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/messages/templates/action_request.jinja2 +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/messages/templates/action_response.jinja2 +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/messages/templates/assistant_response.jinja2 +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/messages/templates/instruction_message.jinja2 +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/messages/templates/system_message.jinja2 +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/messages/templates/tool_schemas.jinja2 +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/operatives/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/operatives/operative.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/operatives/step.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/protocols/types.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/py.typed +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/connections/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/connections/api_calling.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/connections/endpoint.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/connections/endpoint_config.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/connections/header_factory.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/connections/match_endpoint.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/connections/providers/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/connections/providers/anthropic_.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/connections/providers/exa_.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/connections/providers/oai_.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/connections/providers/ollama_.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/connections/providers/perplexity_.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/imodel.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/manager.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/rate_limited_processor.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/resilience.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/third_party/README.md +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/third_party/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/third_party/anthropic_models.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/third_party/exa_models.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/third_party/openai_models.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/third_party/pplx_models.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/token_calculator.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/service/types.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/session/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/session/prompts.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/settings.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/tools/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/tools/base.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/tools/file/__init__.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/tools/file/reader.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/tools/memory/tools.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/tools/types.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/lionagi/utils.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/main.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/scripts/README.md +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/scripts/concat.py +0 -0
- {lionagi-0.14.5 → lionagi-0.14.6}/scripts/config.py +0 -0
@@ -0,0 +1,178 @@
|
|
1
|
+
# Migration to AnyIO: A Technical Discussion
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
In our latest release (v0.14.5), we've made a strategic decision to migrate our asynchronous primitives from raw `asyncio` to `anyio`. This document discusses the rationale, implications, and future benefits of this architectural change.
|
6
|
+
|
7
|
+
## Why AnyIO?
|
8
|
+
|
9
|
+
### 1. Backend Agnosticism
|
10
|
+
|
11
|
+
AnyIO provides a unified interface that works with multiple async backends:
|
12
|
+
- **asyncio** (current Python standard)
|
13
|
+
- **trio** (structured concurrency pioneer)
|
14
|
+
- **curio** (high-performance alternative)
|
15
|
+
|
16
|
+
This abstraction layer means our codebase is no longer tightly coupled to asyncio's implementation details. As the Python async ecosystem evolves, we can adapt without major rewrites.
|
17
|
+
|
18
|
+
### 2. Structured Concurrency by Default
|
19
|
+
|
20
|
+
```python
|
21
|
+
# Before (asyncio)
|
22
|
+
tasks = []
|
23
|
+
for item in items:
|
24
|
+
task = asyncio.create_task(process(item))
|
25
|
+
tasks.append(task)
|
26
|
+
results = await asyncio.gather(*tasks) # What if a task fails?
|
27
|
+
|
28
|
+
# After (anyio)
|
29
|
+
async with create_task_group() as tg:
|
30
|
+
for item in items:
|
31
|
+
await tg.start_soon(process, item)
|
32
|
+
# All tasks are guaranteed to complete or cancel properly
|
33
|
+
```
|
34
|
+
|
35
|
+
Task groups enforce a fundamental principle: **no task outlives its parent scope**. This eliminates entire classes of bugs related to orphaned tasks and resource leaks.
|
36
|
+
|
37
|
+
### 3. Better Cancellation Semantics
|
38
|
+
|
39
|
+
AnyIO's cancellation model is more predictable:
|
40
|
+
- Cancellation is always delivered at checkpoint
|
41
|
+
- Cancel scopes provide fine-grained control
|
42
|
+
- No more mysterious "Task was destroyed but it is pending!" errors
|
43
|
+
|
44
|
+
```python
|
45
|
+
# Timeout with proper cancellation
|
46
|
+
with anyio.move_on_after(5.0) as cancel_scope:
|
47
|
+
result = await long_running_operation()
|
48
|
+
if cancel_scope.cancelled_caught:
|
49
|
+
print("Operation timed out cleanly")
|
50
|
+
```
|
51
|
+
|
52
|
+
## Implementation Highlights
|
53
|
+
|
54
|
+
### Thread-to-Async Bridge
|
55
|
+
|
56
|
+
One subtle but important change is how we handle sync functions in async contexts:
|
57
|
+
|
58
|
+
```python
|
59
|
+
# Before
|
60
|
+
result = await asyncio.to_thread(sync_func, arg)
|
61
|
+
|
62
|
+
# After
|
63
|
+
result = await anyio.to_thread.run_sync(sync_func, arg)
|
64
|
+
```
|
65
|
+
|
66
|
+
AnyIO's approach provides:
|
67
|
+
- Better thread pool management
|
68
|
+
- Proper cancellation propagation to threads
|
69
|
+
- Consistent behavior across different async backends
|
70
|
+
|
71
|
+
### Unified Lock Semantics
|
72
|
+
|
73
|
+
Our `ConcurrencyLock` now wraps AnyIO's primitives, providing:
|
74
|
+
- Same lock works in both sync and async contexts
|
75
|
+
- No more deadlocks from mixing lock types
|
76
|
+
- Better debugging with lock ownership tracking
|
77
|
+
|
78
|
+
### Sleep and Timing
|
79
|
+
|
80
|
+
```python
|
81
|
+
# Before
|
82
|
+
await asyncio.sleep(1.0)
|
83
|
+
|
84
|
+
# After
|
85
|
+
await anyio.sleep(1.0)
|
86
|
+
```
|
87
|
+
|
88
|
+
While seemingly trivial, AnyIO's sleep:
|
89
|
+
- Respects cancellation properly
|
90
|
+
- Works consistently across backends
|
91
|
+
- Integrates with structured concurrency
|
92
|
+
|
93
|
+
## Performance Implications
|
94
|
+
|
95
|
+
### The Good
|
96
|
+
1. **Better CPU utilization**: Task groups reduce overhead compared to gather/wait
|
97
|
+
2. **Memory efficiency**: Structured concurrency prevents task accumulation
|
98
|
+
3. **Predictable performance**: Consistent behavior reduces edge-case slowdowns
|
99
|
+
|
100
|
+
### The Trade-offs
|
101
|
+
1. **Slight overhead**: Abstraction layer adds minimal overhead (~5-10%)
|
102
|
+
2. **Learning curve**: Developers need to understand structured concurrency
|
103
|
+
3. **Ecosystem compatibility**: Some asyncio-specific libraries need adapters
|
104
|
+
|
105
|
+
## Future Benefits
|
106
|
+
|
107
|
+
### 1. Trio Compatibility
|
108
|
+
We can now experiment with trio as a backend for specific use cases:
|
109
|
+
```python
|
110
|
+
# Run lionagi with trio backend
|
111
|
+
import trio
|
112
|
+
import anyio
|
113
|
+
|
114
|
+
async def main():
|
115
|
+
async with anyio.from_thread.start_blocking_portal(
|
116
|
+
backend="trio", backend_options={"trio_token": trio.lowlevel.current_trio_token()}
|
117
|
+
) as portal:
|
118
|
+
await portal.call(run_lionagi_operation)
|
119
|
+
```
|
120
|
+
|
121
|
+
### 2. Better Testing
|
122
|
+
AnyIO provides excellent testing utilities:
|
123
|
+
```python
|
124
|
+
async def test_timeout_handling():
|
125
|
+
with anyio.move_on_after(0.1):
|
126
|
+
await anyio.sleep(1.0) # Will be cancelled
|
127
|
+
# Test continues normally
|
128
|
+
```
|
129
|
+
|
130
|
+
### 3. WebAssembly Ready
|
131
|
+
As Python moves toward WASM support, AnyIO's abstraction will help us adapt to environments where traditional threading doesn't exist.
|
132
|
+
|
133
|
+
## Migration Patterns
|
134
|
+
|
135
|
+
### For Library Users
|
136
|
+
Most changes are transparent, but be aware of:
|
137
|
+
- Different exception types (use `anyio.get_cancelled_exc_class()`)
|
138
|
+
- Import changes if you were using our internal async utilities
|
139
|
+
- Slightly different timeout behavior (more predictable)
|
140
|
+
|
141
|
+
### for Library Developers
|
142
|
+
Key patterns to adopt:
|
143
|
+
```python
|
144
|
+
# Always use task groups for concurrent operations
|
145
|
+
async with create_task_group() as tg:
|
146
|
+
await tg.start_soon(task1)
|
147
|
+
await tg.start_soon(task2)
|
148
|
+
|
149
|
+
# Use cancel scopes for timeouts
|
150
|
+
with anyio.move_on_after(timeout):
|
151
|
+
result = await operation()
|
152
|
+
|
153
|
+
# Handle backend-specific exceptions
|
154
|
+
try:
|
155
|
+
await operation()
|
156
|
+
except anyio.get_cancelled_exc_class():
|
157
|
+
# Properly handle cancellation
|
158
|
+
raise
|
159
|
+
```
|
160
|
+
|
161
|
+
## Philosophical Alignment
|
162
|
+
|
163
|
+
This migration aligns with our core principles:
|
164
|
+
|
165
|
+
1. **Reliability First**: Structured concurrency eliminates entire bug categories
|
166
|
+
2. **Future-Proof Design**: Backend agnosticism protects against ecosystem changes
|
167
|
+
3. **Developer Experience**: Clearer mental models reduce cognitive load
|
168
|
+
4. **Performance with Correctness**: We optimize without sacrificing safety
|
169
|
+
|
170
|
+
## Conclusion
|
171
|
+
|
172
|
+
The migration to AnyIO represents a maturation of our async architecture. While the immediate benefits include better resource management and fewer concurrency bugs, the long-term value lies in our ability to evolve with the Python async ecosystem.
|
173
|
+
|
174
|
+
This change positions lionagi to take advantage of future innovations in async Python while providing our users with a more reliable and predictable experience today.
|
175
|
+
|
176
|
+
---
|
177
|
+
|
178
|
+
*For specific migration examples and API changes, see our [v0.14.6 Release Notes](./RELEASE_NOTES_v0.14.5.md).*
|
@@ -35,7 +35,6 @@ class Instruct(HashableModel):
|
|
35
35
|
"reason",
|
36
36
|
"actions",
|
37
37
|
"action_strategy",
|
38
|
-
"batch_size",
|
39
38
|
"request_params",
|
40
39
|
"response_params",
|
41
40
|
]
|
@@ -97,16 +96,10 @@ class Instruct(HashableModel):
|
|
97
96
|
"None: Contextual execution."
|
98
97
|
),
|
99
98
|
)
|
100
|
-
action_strategy: Literal["
|
101
|
-
Field(
|
102
|
-
None,
|
103
|
-
description="Action strategy to use for executing actions. Default "
|
104
|
-
"is 'concurrent'. Only provide for if actions are enabled.",
|
105
|
-
)
|
106
|
-
)
|
107
|
-
batch_size: int | None = Field(
|
99
|
+
action_strategy: Literal["sequential", "concurrent"] | None = Field(
|
108
100
|
None,
|
109
|
-
description="
|
101
|
+
description="Action strategy to use for executing actions. Default "
|
102
|
+
"is 'concurrent'. Only provide for if actions are enabled.",
|
110
103
|
)
|
111
104
|
|
112
105
|
@field_validator("instruction", "guidance", "context", mode="before")
|
@@ -123,13 +116,6 @@ class Instruct(HashableModel):
|
|
123
116
|
return "concurrent"
|
124
117
|
return v
|
125
118
|
|
126
|
-
@field_validator("batch_size", mode="before")
|
127
|
-
def _validate_batch_size(cls, v):
|
128
|
-
try:
|
129
|
-
return to_num(v, num_type=int)
|
130
|
-
except Exception:
|
131
|
-
return None
|
132
|
-
|
133
119
|
|
134
120
|
class InstructResponse(HashableModel):
|
135
121
|
instruct: Instruct
|
@@ -14,6 +14,7 @@ from typing import Any
|
|
14
14
|
from lionagi.operations.node import BranchOperations, Operation
|
15
15
|
from lionagi.protocols.graph.edge import Edge
|
16
16
|
from lionagi.protocols.graph.graph import Graph
|
17
|
+
from lionagi.protocols.types import ID
|
17
18
|
|
18
19
|
__all__ = (
|
19
20
|
"OperationGraphBuilder",
|
@@ -76,6 +77,7 @@ class OperationGraphBuilder:
|
|
76
77
|
node_id: str | None = None,
|
77
78
|
depends_on: list[str] | None = None,
|
78
79
|
inherit_context: bool = False,
|
80
|
+
branch=None,
|
79
81
|
**parameters,
|
80
82
|
) -> str:
|
81
83
|
"""
|
@@ -108,6 +110,9 @@ class OperationGraphBuilder:
|
|
108
110
|
# Add as metadata for easy lookup
|
109
111
|
node.metadata["reference_id"] = node_id
|
110
112
|
|
113
|
+
if branch:
|
114
|
+
node.branch_id = ID.get_id(branch)
|
115
|
+
|
111
116
|
# Handle dependencies
|
112
117
|
if depends_on:
|
113
118
|
for dep_id in depends_on:
|
@@ -227,6 +232,7 @@ class OperationGraphBuilder:
|
|
227
232
|
source_node_ids: list[str] | None = None,
|
228
233
|
inherit_context: bool = False,
|
229
234
|
inherit_from_source: int = 0,
|
235
|
+
branch=None,
|
230
236
|
**parameters,
|
231
237
|
) -> str:
|
232
238
|
"""
|
@@ -264,6 +270,9 @@ class OperationGraphBuilder:
|
|
264
270
|
if node_id:
|
265
271
|
node.metadata["reference_id"] = node_id
|
266
272
|
|
273
|
+
if branch:
|
274
|
+
node.branch_id = ID.get_id(branch)
|
275
|
+
|
267
276
|
# Store context inheritance for aggregations
|
268
277
|
if inherit_context and sources:
|
269
278
|
node.metadata["inherit_context"] = True
|
@@ -60,15 +60,25 @@ class DependencyAwareExecutor:
|
|
60
60
|
self.operation_branches = {} # operation_id -> Branch
|
61
61
|
|
62
62
|
# Initialize completion events for all operations
|
63
|
+
# and check for already completed operations
|
63
64
|
for node in graph.internal_nodes.values():
|
64
65
|
if isinstance(node, Operation):
|
65
66
|
self.completion_events[node.id] = ConcurrencyEvent()
|
66
67
|
|
68
|
+
# If operation is already completed, mark it and store results
|
69
|
+
if node.execution.status == EventStatus.COMPLETED:
|
70
|
+
self.completion_events[node.id].set()
|
71
|
+
if hasattr(node, "response"):
|
72
|
+
self.results[node.id] = node.response
|
73
|
+
|
67
74
|
async def execute(self) -> dict[str, Any]:
|
68
75
|
"""Execute the operation graph."""
|
69
76
|
if not self.graph.is_acyclic():
|
70
77
|
raise ValueError("Graph must be acyclic for flow execution")
|
71
78
|
|
79
|
+
# Pre-allocate ALL branches upfront to avoid any locking during execution
|
80
|
+
await self._preallocate_all_branches()
|
81
|
+
|
72
82
|
# Create capacity limiter for concurrency control
|
73
83
|
# None means no limit, use the configured unlimited value
|
74
84
|
capacity = (
|
@@ -91,10 +101,97 @@ class DependencyAwareExecutor:
|
|
91
101
|
"final_context": self.context,
|
92
102
|
}
|
93
103
|
|
104
|
+
async def _preallocate_all_branches(self):
|
105
|
+
"""Pre-allocate ALL branches including for context inheritance to eliminate runtime locking."""
|
106
|
+
operations_needing_branches = []
|
107
|
+
|
108
|
+
# First pass: identify all operations that need branches
|
109
|
+
for node in self.graph.internal_nodes.values():
|
110
|
+
if not isinstance(node, Operation):
|
111
|
+
continue
|
112
|
+
|
113
|
+
# Skip if operation already has a branch_id
|
114
|
+
if node.branch_id:
|
115
|
+
try:
|
116
|
+
# Ensure the branch exists in our local map
|
117
|
+
branch = self.session.branches[node.branch_id]
|
118
|
+
self.operation_branches[node.id] = branch
|
119
|
+
except:
|
120
|
+
pass
|
121
|
+
continue
|
122
|
+
|
123
|
+
# Check if operation needs a new branch
|
124
|
+
predecessors = self.graph.get_predecessors(node)
|
125
|
+
if predecessors or node.metadata.get("inherit_context"):
|
126
|
+
operations_needing_branches.append(node)
|
127
|
+
|
128
|
+
if not operations_needing_branches:
|
129
|
+
return
|
130
|
+
|
131
|
+
# Create all branches in a single lock acquisition
|
132
|
+
async with self.session.branches.async_lock:
|
133
|
+
# For context inheritance, we need to create placeholder branches
|
134
|
+
# that will be updated once dependencies complete
|
135
|
+
for operation in operations_needing_branches:
|
136
|
+
# Create a fresh branch for now
|
137
|
+
branch_clone = self.session.default_branch.clone(
|
138
|
+
sender=self.session.id
|
139
|
+
)
|
140
|
+
|
141
|
+
# Store in our operation branches map
|
142
|
+
self.operation_branches[operation.id] = branch_clone
|
143
|
+
|
144
|
+
# Add to session branches collection directly
|
145
|
+
# Check if this is a real branch (not a mock)
|
146
|
+
try:
|
147
|
+
from lionagi.protocols.types import IDType
|
148
|
+
|
149
|
+
# Try to validate the ID
|
150
|
+
if hasattr(branch_clone, "id"):
|
151
|
+
branch_id = branch_clone.id
|
152
|
+
# Only add to collections if it's a valid ID
|
153
|
+
if isinstance(branch_id, (str, IDType)) or (
|
154
|
+
hasattr(branch_id, "__str__")
|
155
|
+
and not hasattr(branch_id, "_mock_name")
|
156
|
+
):
|
157
|
+
self.session.branches.collections[branch_id] = (
|
158
|
+
branch_clone
|
159
|
+
)
|
160
|
+
self.session.branches.progression.append(branch_id)
|
161
|
+
except:
|
162
|
+
# If validation fails, it's likely a mock - skip adding to collections
|
163
|
+
pass
|
164
|
+
|
165
|
+
# Mark branches that need context inheritance for later update
|
166
|
+
if operation.metadata.get("inherit_context"):
|
167
|
+
branch_clone.metadata = branch_clone.metadata or {}
|
168
|
+
branch_clone.metadata["pending_context_inheritance"] = True
|
169
|
+
branch_clone.metadata["inherit_from_operation"] = (
|
170
|
+
operation.metadata.get("primary_dependency")
|
171
|
+
)
|
172
|
+
|
173
|
+
if self.verbose:
|
174
|
+
print(f"Pre-allocated {len(operations_needing_branches)} branches")
|
175
|
+
|
94
176
|
async def _execute_operation(
|
95
177
|
self, operation: Operation, limiter: CapacityLimiter
|
96
178
|
):
|
97
179
|
"""Execute a single operation with dependency waiting."""
|
180
|
+
# Skip if operation is already completed
|
181
|
+
if operation.execution.status == EventStatus.COMPLETED:
|
182
|
+
if self.verbose:
|
183
|
+
print(
|
184
|
+
f"Skipping already completed operation: {str(operation.id)[:8]}"
|
185
|
+
)
|
186
|
+
# Ensure results are available for dependencies
|
187
|
+
if operation.id not in self.results and hasattr(
|
188
|
+
operation, "response"
|
189
|
+
):
|
190
|
+
self.results[operation.id] = operation.response
|
191
|
+
# Signal completion for any waiting operations
|
192
|
+
self.completion_events[operation.id].set()
|
193
|
+
return
|
194
|
+
|
98
195
|
try:
|
99
196
|
# Wait for dependencies
|
100
197
|
await self._wait_for_dependencies(operation)
|
@@ -102,7 +199,7 @@ class DependencyAwareExecutor:
|
|
102
199
|
# Acquire capacity to limit concurrency
|
103
200
|
async with limiter:
|
104
201
|
# Prepare operation context
|
105
|
-
|
202
|
+
self._prepare_operation(operation)
|
106
203
|
|
107
204
|
# Execute the operation
|
108
205
|
if self.verbose:
|
@@ -191,7 +288,7 @@ class DependencyAwareExecutor:
|
|
191
288
|
f"Edge condition not satisfied for {str(operation.id)[:8]}"
|
192
289
|
)
|
193
290
|
|
194
|
-
|
291
|
+
def _prepare_operation(self, operation: Operation):
|
195
292
|
"""Prepare operation with context and branch assignment."""
|
196
293
|
# Update operation context with predecessors
|
197
294
|
predecessors = self.graph.get_predecessors(operation)
|
@@ -209,77 +306,83 @@ class DependencyAwareExecutor:
|
|
209
306
|
if "context" not in operation.parameters:
|
210
307
|
operation.parameters["context"] = pred_context
|
211
308
|
else:
|
212
|
-
|
309
|
+
# Handle case where context might be a string
|
310
|
+
existing_context = operation.parameters["context"]
|
311
|
+
if isinstance(existing_context, dict):
|
312
|
+
existing_context.update(pred_context)
|
313
|
+
else:
|
314
|
+
# If it's a string or other type, create a new dict
|
315
|
+
operation.parameters["context"] = {
|
316
|
+
"original_context": existing_context,
|
317
|
+
**pred_context,
|
318
|
+
}
|
213
319
|
|
214
320
|
# Add execution context
|
215
321
|
if self.context:
|
216
322
|
if "context" not in operation.parameters:
|
217
323
|
operation.parameters["context"] = self.context.copy()
|
218
324
|
else:
|
219
|
-
|
325
|
+
# Handle case where context might be a string
|
326
|
+
existing_context = operation.parameters["context"]
|
327
|
+
if isinstance(existing_context, dict):
|
328
|
+
existing_context.update(self.context)
|
329
|
+
else:
|
330
|
+
# If it's a string or other type, create a new dict
|
331
|
+
operation.parameters["context"] = {
|
332
|
+
"original_context": existing_context,
|
333
|
+
**self.context,
|
334
|
+
}
|
220
335
|
|
221
336
|
# Determine and assign branch
|
222
|
-
branch =
|
337
|
+
branch = self._resolve_branch_for_operation(operation)
|
223
338
|
self.operation_branches[operation.id] = branch
|
224
339
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
340
|
+
def _resolve_branch_for_operation(self, operation: Operation) -> Branch:
|
341
|
+
"""Resolve which branch an operation should use - all branches are pre-allocated."""
|
342
|
+
# All branches should be pre-allocated
|
343
|
+
if operation.id in self.operation_branches:
|
344
|
+
branch = self.operation_branches[operation.id]
|
345
|
+
|
346
|
+
# Handle deferred context inheritance
|
347
|
+
if (
|
348
|
+
hasattr(branch, "metadata")
|
349
|
+
and branch.metadata
|
350
|
+
and branch.metadata.get("pending_context_inheritance")
|
351
|
+
):
|
352
|
+
|
353
|
+
primary_dep_id = branch.metadata.get("inherit_from_operation")
|
354
|
+
if primary_dep_id and primary_dep_id in self.results:
|
355
|
+
# Find the primary dependency's branch
|
356
|
+
primary_branch = self.operation_branches.get(
|
357
|
+
primary_dep_id, self.session.default_branch
|
358
|
+
)
|
238
359
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
for node in self.graph.internal_nodes.values():
|
245
|
-
if (
|
246
|
-
isinstance(node, Operation)
|
247
|
-
and node.id == primary_dep_id
|
248
|
-
and node.branch_id
|
360
|
+
# Copy the messages from primary branch to this branch
|
361
|
+
# This avoids creating a new branch and thus avoids locking
|
362
|
+
# Access messages through the MessageManager
|
363
|
+
if hasattr(branch, "_message_manager") and hasattr(
|
364
|
+
primary_branch, "_message_manager"
|
249
365
|
):
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
try:
|
270
|
-
async with self.session.branches:
|
271
|
-
fresh_branch = self.session.split(
|
272
|
-
self.session.default_branch
|
273
|
-
)
|
274
|
-
if self.verbose:
|
275
|
-
print(
|
276
|
-
f"Operation {str(operation.id)[:8]} starting with fresh context"
|
277
|
-
)
|
278
|
-
return fresh_branch
|
279
|
-
except:
|
280
|
-
pass
|
366
|
+
branch._message_manager.pile.clear()
|
367
|
+
for msg in primary_branch._message_manager.pile:
|
368
|
+
branch._message_manager.pile.append(msg.clone())
|
369
|
+
|
370
|
+
# Clear the pending flag
|
371
|
+
branch.metadata["pending_context_inheritance"] = False
|
372
|
+
|
373
|
+
if self.verbose:
|
374
|
+
print(
|
375
|
+
f"Operation {str(operation.id)[:8]} inherited context from {str(primary_dep_id)[:8]}"
|
376
|
+
)
|
377
|
+
|
378
|
+
return branch
|
379
|
+
|
380
|
+
# Fallback to default branch (should not happen with proper pre-allocation)
|
381
|
+
if self.verbose:
|
382
|
+
print(
|
383
|
+
f"Warning: Operation {str(operation.id)[:8]} using default branch (not pre-allocated)"
|
384
|
+
)
|
281
385
|
|
282
|
-
# Default to session's default branch or the provided branch
|
283
386
|
if hasattr(self, "_default_branch") and self._default_branch:
|
284
387
|
return self._default_branch
|
285
388
|
return self.session.default_branch
|