by-framework 0.2.2.dev9__tar.gz → 0.2.2.dev10__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.
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/PKG-INFO +1 -1
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/pyproject.toml +1 -1
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/__init__.py +2 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/client/client.py +124 -3
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/common/emitter.py +16 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/__init__.py +2 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/__init__.py +2 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/content_type.py +1 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/responses.py +11 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/registry.py +2 -1
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/worker/runner.py +10 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/client/test_client.py +345 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/common/test_config.py +38 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/common/test_constants.py +6 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/test_registry.py +32 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/worker/test_emitter.py +41 -1
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/worker/test_runner.py +76 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/uv.lock +1 -1
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/.dockerignore +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/.github/RELEASING.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/.github/workflows/ci.yml +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/.github/workflows/publish.yml +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/.github/workflows/stale.yml +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/.gitignore +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/.pre-commit-config.yaml +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/AGENTS.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/CHANGELOG.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/CLAUDE.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/CODE_OF_CONDUCT.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/CONTRIBUTING.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/LICENSE +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/Makefile +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/README.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/README_zh.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/SECURITY.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/assets/img/architecture_en.png +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/assets/img/architecture_zh.png +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/autoformat.sh +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/docs/README.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/docs/by-framework-trace-change-usage.html +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/docs/by-framework-trace-complete-guide.html +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/docs/plans/2026-05-15-worker-task-state-stats.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/docs/plans/2026-06-10-trace-metrics-correlation.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/docs/worker_management.html +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-adk/README.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-adk/pyproject.toml +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-adk/src/by_framework_adk/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-adk/src/by_framework_adk/_utils.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-adk/src/by_framework_adk/adapter.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-adk/src/by_framework_adk/worker.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-adk/tests/test_worker.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/Dockerfile +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/README.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/frontend/index.html +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/frontend/package-lock.json +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/frontend/package.json +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/frontend/src/main.jsx +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/frontend/src/styles.css +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/frontend/vite.config.js +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/pyproject.toml +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/src/by_framework_dashboard/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/src/by_framework_dashboard/adapters.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/src/by_framework_dashboard/dashboard.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/src/by_framework_dashboard/static/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/src/by_framework_dashboard/static/app.js +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/src/by_framework_dashboard/static/index.html +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/src/by_framework_dashboard/static/styles.css +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-dashboard/tests/test_dashboard_server.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-history-byclaw/README.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-history-byclaw/pyproject.toml +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-history-byclaw/src/by_framework_history_byclaw/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-history-byclaw/src/by_framework_history_byclaw/byclaw_history.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-history-byclaw/tests/test_byclaw_history.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-history-postgres/README.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-history-postgres/pyproject.toml +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-history-postgres/src/by_framework_history_postgres/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-history-postgres/src/by_framework_history_postgres/postgres.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-history-postgres/tests/test_postgres_history_storage.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/README.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/pyproject.toml +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/src/by_framework_langgraph/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/src/by_framework_langgraph/_utils.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/src/by_framework_langgraph/adapter.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/src/by_framework_langgraph/tools.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/src/by_framework_langgraph/worker.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/tests/test_adapter.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/tests/test_tools.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-langgraph/tests/test_utils.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-langfuse/README.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-langfuse/pyproject.toml +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-langfuse/src/by_framework_trace_langfuse/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-langfuse/src/by_framework_trace_langfuse/langfuse.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-langfuse/tests/test_langfuse_import.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-phoenix/README.md +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-phoenix/pyproject.toml +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-phoenix/src/by_framework_trace_phoenix/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-phoenix/src/by_framework_trace_phoenix/phoenix.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-phoenix/tests/test_phoenix_import.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-phoenix/tests/test_phoenix_plugin.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-query/pyproject.toml +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-query/src/by_framework_trace_query/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-query/src/by_framework_trace_query/client.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-query/src/by_framework_trace_query/merger.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-query/src/by_framework_trace_query/models.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-query/src/by_framework_trace_query/redis_source.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/libs/by-framework-trace-query/tests/test_trace_query_sdk.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/pylintrc +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/scripts/python_quality.sh +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/__main__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/admin/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/admin/cli.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/admin/worker_manager.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/client/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/client/byai_client.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/common/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/common/config.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/common/constants.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/common/exceptions.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/common/logger.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/common/redis_client.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/availability.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/delivery_gate.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/discovery.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/extensions/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/extensions/agent_config.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/extensions/agent_config_audit.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/extensions/plugin.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/extensions/registry.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/extensions/trace_provider.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/action_type.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/agent_state.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/byai_codec.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/byai_command.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/byai_types.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/commands.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/content_codec.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/data_message.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/data_shapes.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/event_type.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/events.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/message.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/message_header.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/results.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/agent_config_manager.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/agent_runtime_state.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/file_manager.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/file_paths.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/file_permissions.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/filestore/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/filestore/base.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/filestore/local.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/history/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/history/base.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/history/history_manager.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/history/in_memory.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/runtime/session_manager.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/wakeup_controller.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/workspace.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/errors/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/errors/base.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/errors/common.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/errors/execution.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/errors/http.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/errors/protocol.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/errors/registry.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/metrics/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/metrics/catalog.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/metrics/collector.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/metrics/read_client.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/metrics/snapshot.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/trace/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/trace/external_trace.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/trace/span_recorder.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/trace/trace_schema.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/trace/trace_writer.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/util/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/util/discovery_http_client.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/util/generate_message_id.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/util/http_client.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/worker/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/worker/_control_handling.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/worker/_execution_tracking.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/worker/_message_processing.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/worker/app.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/worker/byai_context.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/worker/byai_worker.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/worker/context.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/worker/heartbeat.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/worker/processor.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/worker/sandbox/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/worker/sandbox/hook_sandbox.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/worker/worker.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/admin/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/admin/test_cli.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/client/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/common/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/common/test_exceptions.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/common/test_logger.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/common/test_redis_client.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/conftest.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/protocol/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/protocol/test_byai_codec.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/protocol/test_command_wire.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/protocol/test_protocol.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/protocol/test_results.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/runtime/history/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/runtime/history/test_history_persistence.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/runtime/test_file_access_context.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/runtime/test_file_manager_default_storage.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/runtime/test_file_paths.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/runtime/test_file_permissions.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/runtime/test_filestore_local_collection.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/runtime/test_filestore_local_mutation.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/runtime/test_filestore_local_read.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/runtime/test_filestore_local_search.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/test_agent_config_audit.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/test_availability.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/test_discovery.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/core/test_worker_manager.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/integration/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/integration/test_ask_user_flow.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/integration/test_callback_flow.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/integration/test_logger_integration.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/integration/test_scatter_gather.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/metrics/test_catalog.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/metrics/test_collector.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/metrics/test_metrics.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/metrics/test_read_client.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/metrics/test_snapshot.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/plugin/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/plugin/test_langfuse_plugin.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/plugin/test_plugin_discovery.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/plugin/test_plugin_improvements.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/plugin/test_plugin_registry.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/plugin/test_plugin_system.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/trace/test_external_trace.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/trace/test_logger_correlation.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/trace/test_otel_exporter.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/trace/test_span_recorder.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/trace/test_trace_writer.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/util/test_discovery_http_client.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/util/test_discovery_http_client_download.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/util/test_discovery_http_client_upload.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/util/test_http_client.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/worker/__init__.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/worker/test_admin_control_handling.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/worker/test_app.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/worker/test_byai_worker.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/worker/test_context.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/worker/test_control_handling.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/worker/test_fetch_messages.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/worker/test_gateway_worker.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/worker/test_heartbeat.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/worker/test_message_processing.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/worker/test_processor.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/worker/test_sandbox.py +0 -0
- {by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/tests/worker/test_workspace.py +0 -0
|
@@ -8,6 +8,7 @@ from `GatewayWorker` and running `run_worker`.
|
|
|
8
8
|
from .admin import WorkerManager
|
|
9
9
|
from .client.byai_client import ByaiGatewayClient
|
|
10
10
|
from .client.client import (
|
|
11
|
+
CancelSessionResponse,
|
|
11
12
|
CancelTaskResponse,
|
|
12
13
|
DataStreamEntry,
|
|
13
14
|
GatewayClient,
|
|
@@ -148,6 +149,7 @@ __all__ = [
|
|
|
148
149
|
"DataStreamEntry",
|
|
149
150
|
"SendMessageResponse",
|
|
150
151
|
"CancelTaskResponse",
|
|
152
|
+
"CancelSessionResponse",
|
|
151
153
|
"run_worker",
|
|
152
154
|
"logger",
|
|
153
155
|
"setup_logging",
|
|
@@ -37,6 +37,7 @@ from by_framework.core.protocol.commands import (
|
|
|
37
37
|
from by_framework.core.protocol.data_message import DataMessage
|
|
38
38
|
from by_framework.core.protocol.message_header import MessageHeader
|
|
39
39
|
from by_framework.core.protocol.responses import (
|
|
40
|
+
CancelSessionResponse,
|
|
40
41
|
CancelTaskResponse,
|
|
41
42
|
ExecutionStatus,
|
|
42
43
|
SendMessageResponse,
|
|
@@ -581,6 +582,99 @@ class GatewayClient:
|
|
|
581
582
|
cancelled_count=len(to_cancel),
|
|
582
583
|
)
|
|
583
584
|
|
|
585
|
+
async def cancel_session(
|
|
586
|
+
self,
|
|
587
|
+
session_id: str,
|
|
588
|
+
reason: str = "",
|
|
589
|
+
requested_by: str = "client",
|
|
590
|
+
cancel_mode: str = CancelMode.GRACEFUL,
|
|
591
|
+
) -> CancelSessionResponse:
|
|
592
|
+
"""Cancel every active execution registered under a session.
|
|
593
|
+
|
|
594
|
+
Unlike cancel_task, this does not require a starting message_id:
|
|
595
|
+
it cancels the whole session's active executions in one call.
|
|
596
|
+
"""
|
|
597
|
+
if self.registry is None:
|
|
598
|
+
raise ValueError("GatewayClient requires a WorkerRegistry to cancel tasks")
|
|
599
|
+
|
|
600
|
+
all_executions = await self.registry.get_all_session_executions(session_id)
|
|
601
|
+
|
|
602
|
+
if not all_executions:
|
|
603
|
+
return CancelSessionResponse(
|
|
604
|
+
success=False,
|
|
605
|
+
session_id=session_id,
|
|
606
|
+
status=ExecutionStatus.NOT_FOUND,
|
|
607
|
+
timestamp=int(time.time() * 1000),
|
|
608
|
+
cancelled_count=0,
|
|
609
|
+
already_finished_count=0,
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
terminal_states = {"COMPLETED", "FAILED", "CANCELLED"}
|
|
613
|
+
to_cancel = [
|
|
614
|
+
node
|
|
615
|
+
for node in all_executions
|
|
616
|
+
if node.get("status", "") not in terminal_states
|
|
617
|
+
]
|
|
618
|
+
terminal_nodes = [
|
|
619
|
+
node for node in all_executions if node.get("status", "") in terminal_states
|
|
620
|
+
]
|
|
621
|
+
|
|
622
|
+
# Flag terminal executions so a still-running descendant can't wake
|
|
623
|
+
# them back up via callback, without changing their status.
|
|
624
|
+
for node in terminal_nodes:
|
|
625
|
+
await self.registry.mark_cancel_requested(
|
|
626
|
+
node.get("execution_id", ""), session_id, reason
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
if not to_cancel:
|
|
630
|
+
return CancelSessionResponse(
|
|
631
|
+
success=False,
|
|
632
|
+
session_id=session_id,
|
|
633
|
+
status=ExecutionStatus.ALREADY_FINISHED,
|
|
634
|
+
timestamp=int(time.time() * 1000),
|
|
635
|
+
cancelled_count=0,
|
|
636
|
+
already_finished_count=len(terminal_nodes),
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
for node in to_cancel:
|
|
640
|
+
node_execution_id = node.get("execution_id", "")
|
|
641
|
+
node_worker_id = node.get("worker_id", "")
|
|
642
|
+
node_message_id = node.get("message_id", "")
|
|
643
|
+
|
|
644
|
+
await self.registry.mark_execution_cancelling(
|
|
645
|
+
node_execution_id, session_id, reason
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
if node_worker_id:
|
|
649
|
+
cancel_command = CancelTaskCommand(
|
|
650
|
+
header=MessageHeader(
|
|
651
|
+
message_id=f"{CANCEL_MESSAGE_ID_PREFIX}{uuid.uuid4().hex[:8]}",
|
|
652
|
+
session_id=session_id,
|
|
653
|
+
trace_id=node.get("trace_id") or uuid.uuid4().hex,
|
|
654
|
+
target_agent_type=node.get("target_agent_type", ""),
|
|
655
|
+
parent_message_id=node_message_id,
|
|
656
|
+
),
|
|
657
|
+
target_message_id=node_message_id,
|
|
658
|
+
target_execution_id=node_execution_id,
|
|
659
|
+
target_worker_id=node_worker_id,
|
|
660
|
+
reason=reason,
|
|
661
|
+
requested_by=requested_by,
|
|
662
|
+
cancel_mode=cancel_mode,
|
|
663
|
+
)
|
|
664
|
+
await self.redis.xadd(
|
|
665
|
+
RedisKeys.worker_ctrl_stream(node_worker_id),
|
|
666
|
+
cancel_command.to_redis_payload(),
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
return CancelSessionResponse(
|
|
670
|
+
success=True,
|
|
671
|
+
session_id=session_id,
|
|
672
|
+
status=ExecutionStatus.CANCEL_REQUESTED,
|
|
673
|
+
timestamp=int(time.time() * 1000),
|
|
674
|
+
cancelled_count=len(to_cancel),
|
|
675
|
+
already_finished_count=len(terminal_nodes),
|
|
676
|
+
)
|
|
677
|
+
|
|
584
678
|
async def send_message(
|
|
585
679
|
self,
|
|
586
680
|
target_agent_type: str,
|
|
@@ -691,7 +785,28 @@ class GatewayClient:
|
|
|
691
785
|
content=params["content"],
|
|
692
786
|
extra_payload=params["extra_payload"],
|
|
693
787
|
)
|
|
694
|
-
|
|
788
|
+
|
|
789
|
+
# A RESUME reuses the message_id of the original AskAgentCommand so the
|
|
790
|
+
# worker can look the suspended execution back up. Reuse its
|
|
791
|
+
# execution_id too, and skip re-initializing the registry record for
|
|
792
|
+
# it below -- initialize_execution() would otherwise overwrite the
|
|
793
|
+
# message_id -> execution_id mapping and detach this resume from the
|
|
794
|
+
# execution it's meant to continue.
|
|
795
|
+
resumed_execution = None
|
|
796
|
+
if (
|
|
797
|
+
params["action_type"] == ActionType.RESUME.value
|
|
798
|
+
and self.registry
|
|
799
|
+
and hasattr(self.registry, "get_execution_by_message_id")
|
|
800
|
+
):
|
|
801
|
+
resumed_execution = await self.registry.get_execution_by_message_id(
|
|
802
|
+
message_id, session_id=params["session_id"]
|
|
803
|
+
)
|
|
804
|
+
|
|
805
|
+
execution_id = (
|
|
806
|
+
resumed_execution["execution_id"]
|
|
807
|
+
if resumed_execution
|
|
808
|
+
else f"{EXECUTION_ID_PREFIX}{uuid.uuid4().hex[:8]}"
|
|
809
|
+
)
|
|
695
810
|
|
|
696
811
|
# 3. Resolve route and optionally probe agent type/liveness
|
|
697
812
|
should_dispatch_control = True
|
|
@@ -837,8 +952,14 @@ class GatewayClient:
|
|
|
837
952
|
error_code=ExecutionStatus.ERR_AGENT_TYPE_UNAVAILABLE,
|
|
838
953
|
)
|
|
839
954
|
|
|
840
|
-
# Initialize execution tracking
|
|
841
|
-
|
|
955
|
+
# Initialize execution tracking. Skipped for a RESUME that reattached
|
|
956
|
+
# to an existing execution -- that execution is already tracked, and
|
|
957
|
+
# re-initializing it here would overwrite its message_id mapping.
|
|
958
|
+
if (
|
|
959
|
+
not resumed_execution
|
|
960
|
+
and self.registry
|
|
961
|
+
and hasattr(self.registry, "initialize_execution")
|
|
962
|
+
):
|
|
842
963
|
try:
|
|
843
964
|
await self.registry.initialize_execution(
|
|
844
965
|
{
|
|
@@ -107,6 +107,22 @@ class DefaultSseLayoutBuilder(DataLayoutBuilder):
|
|
|
107
107
|
parent_order_id: Optional[str] = None,
|
|
108
108
|
) -> Dict[str, Any]:
|
|
109
109
|
"""Build default BaiYing-compatible user input form payload."""
|
|
110
|
+
try:
|
|
111
|
+
parsed_prompt = json.loads(prompt)
|
|
112
|
+
except (json.JSONDecodeError, TypeError):
|
|
113
|
+
parsed_prompt = None
|
|
114
|
+
|
|
115
|
+
# Structured question prompts are already in the format expected by clients.
|
|
116
|
+
if isinstance(parsed_prompt, dict) and "questions" in parsed_prompt:
|
|
117
|
+
return self.build(
|
|
118
|
+
content=prompt,
|
|
119
|
+
role="assistant",
|
|
120
|
+
content_type=SseReasonMessageType.ask_user_question.value,
|
|
121
|
+
source_agent_type=source_agent_type,
|
|
122
|
+
order_id=order_id,
|
|
123
|
+
parent_order_id=parent_order_id,
|
|
124
|
+
)
|
|
125
|
+
|
|
110
126
|
input_form = {
|
|
111
127
|
"formStatus": 0,
|
|
112
128
|
"pluginMachineFields": [
|
|
@@ -26,6 +26,7 @@ from .protocol import (
|
|
|
26
26
|
BaiYingMessage,
|
|
27
27
|
BaiYingMessageRole,
|
|
28
28
|
BaseCommand,
|
|
29
|
+
CancelSessionResponse,
|
|
29
30
|
CancelTaskCommand,
|
|
30
31
|
CancelTaskResponse,
|
|
31
32
|
DataMessage,
|
|
@@ -53,6 +54,7 @@ from .workspace import WorkspaceManager
|
|
|
53
54
|
__all__ = [
|
|
54
55
|
"SendMessageResponse",
|
|
55
56
|
"CancelTaskResponse",
|
|
57
|
+
"CancelSessionResponse",
|
|
56
58
|
"BaiYingMessage",
|
|
57
59
|
"BaiYingMessageRole",
|
|
58
60
|
"MessageContent",
|
{by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/__init__.py
RENAMED
|
@@ -65,6 +65,7 @@ from .message import (
|
|
|
65
65
|
)
|
|
66
66
|
from .message_header import MessageHeader
|
|
67
67
|
from .responses import (
|
|
68
|
+
CancelSessionResponse,
|
|
68
69
|
CancelTaskResponse,
|
|
69
70
|
CancelTaskResponseDict,
|
|
70
71
|
SendMessageResponse,
|
|
@@ -81,6 +82,7 @@ from .results import (
|
|
|
81
82
|
__all__ = [
|
|
82
83
|
"SendMessageResponse",
|
|
83
84
|
"CancelTaskResponse",
|
|
85
|
+
"CancelSessionResponse",
|
|
84
86
|
"SendMessageResponseDict",
|
|
85
87
|
"CancelTaskResponseDict",
|
|
86
88
|
"AgentTaskResult",
|
{by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/content_type.py
RENAMED
|
@@ -33,6 +33,7 @@ class SseReasonMessageType(str, Enum):
|
|
|
33
33
|
think_code_result = "3007" # thinking process code execution result
|
|
34
34
|
task_finished = "3009" # task finished
|
|
35
35
|
task_user_input = "3013" # user input
|
|
36
|
+
ask_user_question = "3014" # ask user question
|
|
36
37
|
task_create_file = "3010" # create file
|
|
37
38
|
task_title = "3011" # task title
|
|
38
39
|
agent_card = "2015" # agent card
|
{by_framework-0.2.2.dev9 → by_framework-0.2.2.dev10}/src/by_framework/core/protocol/responses.py
RENAMED
|
@@ -92,3 +92,14 @@ class CancelTaskResponse:
|
|
|
92
92
|
timestamp: int
|
|
93
93
|
error: str = ""
|
|
94
94
|
cancelled_count: int = 0
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass(frozen=True)
|
|
98
|
+
class CancelSessionResponse:
|
|
99
|
+
success: bool
|
|
100
|
+
session_id: str
|
|
101
|
+
status: str
|
|
102
|
+
timestamp: int
|
|
103
|
+
cancelled_count: int = 0
|
|
104
|
+
already_finished_count: int = 0
|
|
105
|
+
error: str = ""
|
|
@@ -1011,7 +1011,8 @@ class WorkerRegistry:
|
|
|
1011
1011
|
old_execution = dict(current)
|
|
1012
1012
|
current["status"] = status
|
|
1013
1013
|
now = int(time.time() * 1000)
|
|
1014
|
-
|
|
1014
|
+
if is_terminal_state(status):
|
|
1015
|
+
current["finished_at"] = now
|
|
1015
1016
|
current["updated_at"] = now
|
|
1016
1017
|
if completion:
|
|
1017
1018
|
for key, value in completion.items():
|
|
@@ -505,6 +505,16 @@ class WorkerRunner:
|
|
|
505
505
|
header.message_id, session_id=header.session_id
|
|
506
506
|
)
|
|
507
507
|
|
|
508
|
+
if isinstance(command, ResumeCommand) and existing_execution is None:
|
|
509
|
+
logger.warning(
|
|
510
|
+
"[%s] ResumeCommand did not resolve to an existing execution "
|
|
511
|
+
"(message_id=%s, session_id=%s); starting a new, disconnected "
|
|
512
|
+
"execution instead of continuing the suspended one.",
|
|
513
|
+
self.worker.worker_id,
|
|
514
|
+
header.message_id,
|
|
515
|
+
header.session_id,
|
|
516
|
+
)
|
|
517
|
+
|
|
508
518
|
# Skip terminal state replays
|
|
509
519
|
if (
|
|
510
520
|
existing_execution
|
|
@@ -1022,3 +1022,348 @@ async def test_client_cascading_cancel_root_completed_children_running():
|
|
|
1022
1022
|
registry.mark_cancel_requested.assert_called_once_with(
|
|
1023
1023
|
"exec-a", "sess-1", "user abort"
|
|
1024
1024
|
)
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
@pytest.mark.asyncio
|
|
1028
|
+
async def test_client_cancel_session_cancels_all_non_terminal_executions():
|
|
1029
|
+
"""Test that cancel_session cancels every non-terminal execution in a
|
|
1030
|
+
session in one call, without needing a starting message_id."""
|
|
1031
|
+
exec_a = {
|
|
1032
|
+
"execution_id": "exec-a",
|
|
1033
|
+
"message_id": "msg-a",
|
|
1034
|
+
"session_id": "sess-1",
|
|
1035
|
+
"worker_id": "worker-1",
|
|
1036
|
+
"target_agent_type": "agent_a",
|
|
1037
|
+
"status": "RUNNING",
|
|
1038
|
+
"parent_message_id": "",
|
|
1039
|
+
}
|
|
1040
|
+
exec_b = {
|
|
1041
|
+
"execution_id": "exec-b",
|
|
1042
|
+
"message_id": "msg-b",
|
|
1043
|
+
"session_id": "sess-1",
|
|
1044
|
+
"worker_id": "worker-2",
|
|
1045
|
+
"target_agent_type": "agent_b",
|
|
1046
|
+
"status": "QUEUED",
|
|
1047
|
+
"parent_message_id": "msg-a",
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
registry = AsyncMock()
|
|
1051
|
+
registry.get_all_session_executions.return_value = [exec_a, exec_b]
|
|
1052
|
+
redis = AsyncMock()
|
|
1053
|
+
client = GatewayClient(registry=registry, redis_client=redis)
|
|
1054
|
+
|
|
1055
|
+
result = await client.cancel_session(session_id="sess-1", reason="user abort")
|
|
1056
|
+
|
|
1057
|
+
assert result.success is True
|
|
1058
|
+
assert result.status == "CANCEL_REQUESTED"
|
|
1059
|
+
assert result.cancelled_count == 2
|
|
1060
|
+
assert result.already_finished_count == 0
|
|
1061
|
+
|
|
1062
|
+
assert registry.mark_execution_cancelling.call_count == 2
|
|
1063
|
+
cancel_ids = [
|
|
1064
|
+
call.args[0] for call in registry.mark_execution_cancelling.call_args_list
|
|
1065
|
+
]
|
|
1066
|
+
assert "exec-a" in cancel_ids
|
|
1067
|
+
assert "exec-b" in cancel_ids
|
|
1068
|
+
|
|
1069
|
+
assert redis.xadd.call_count == 2
|
|
1070
|
+
target_streams = [call.args[0] for call in redis.xadd.call_args_list]
|
|
1071
|
+
assert any("worker-1" in s for s in target_streams)
|
|
1072
|
+
assert any("worker-2" in s for s in target_streams)
|
|
1073
|
+
|
|
1074
|
+
|
|
1075
|
+
@pytest.mark.asyncio
|
|
1076
|
+
async def test_client_cancel_session_flags_terminal_executions_without_cancelling():
|
|
1077
|
+
"""Terminal executions in the session should only be flagged with
|
|
1078
|
+
cancel_requested (not re-marked cancelling, no CancelTaskCommand sent),
|
|
1079
|
+
so a still-running descendant can't wake them back up via callback."""
|
|
1080
|
+
exec_a = {
|
|
1081
|
+
"execution_id": "exec-a",
|
|
1082
|
+
"message_id": "msg-a",
|
|
1083
|
+
"session_id": "sess-1",
|
|
1084
|
+
"worker_id": "worker-1",
|
|
1085
|
+
"target_agent_type": "agent_a",
|
|
1086
|
+
"status": "COMPLETED",
|
|
1087
|
+
"parent_message_id": "",
|
|
1088
|
+
}
|
|
1089
|
+
exec_b = {
|
|
1090
|
+
"execution_id": "exec-b",
|
|
1091
|
+
"message_id": "msg-b",
|
|
1092
|
+
"session_id": "sess-1",
|
|
1093
|
+
"worker_id": "worker-2",
|
|
1094
|
+
"target_agent_type": "agent_b",
|
|
1095
|
+
"status": "RUNNING",
|
|
1096
|
+
"parent_message_id": "msg-a",
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
registry = AsyncMock()
|
|
1100
|
+
registry.get_all_session_executions.return_value = [exec_a, exec_b]
|
|
1101
|
+
redis = AsyncMock()
|
|
1102
|
+
client = GatewayClient(registry=registry, redis_client=redis)
|
|
1103
|
+
|
|
1104
|
+
result = await client.cancel_session(session_id="sess-1", reason="user abort")
|
|
1105
|
+
|
|
1106
|
+
assert result.success is True
|
|
1107
|
+
assert result.status == "CANCEL_REQUESTED"
|
|
1108
|
+
assert result.cancelled_count == 1
|
|
1109
|
+
assert result.already_finished_count == 1
|
|
1110
|
+
|
|
1111
|
+
# Only the non-terminal execution gets mark_execution_cancelling
|
|
1112
|
+
registry.mark_execution_cancelling.assert_called_once_with(
|
|
1113
|
+
"exec-b", "sess-1", "user abort"
|
|
1114
|
+
)
|
|
1115
|
+
# The terminal execution only gets flagged, not re-marked cancelling
|
|
1116
|
+
registry.mark_cancel_requested.assert_called_once_with(
|
|
1117
|
+
"exec-a", "sess-1", "user abort"
|
|
1118
|
+
)
|
|
1119
|
+
|
|
1120
|
+
# Only one CancelTaskCommand sent, to the non-terminal execution's worker
|
|
1121
|
+
assert redis.xadd.call_count == 1
|
|
1122
|
+
target_streams = [call.args[0] for call in redis.xadd.call_args_list]
|
|
1123
|
+
assert any("worker-2" in s for s in target_streams)
|
|
1124
|
+
assert not any("worker-1" in s for s in target_streams)
|
|
1125
|
+
|
|
1126
|
+
|
|
1127
|
+
@pytest.mark.asyncio
|
|
1128
|
+
async def test_client_cancel_session_all_terminal_returns_already_finished():
|
|
1129
|
+
"""If every execution in the session is already terminal, cancel_session
|
|
1130
|
+
should report failure with ALREADY_FINISHED and not dispatch anything."""
|
|
1131
|
+
exec_a = {
|
|
1132
|
+
"execution_id": "exec-a",
|
|
1133
|
+
"message_id": "msg-a",
|
|
1134
|
+
"session_id": "sess-1",
|
|
1135
|
+
"worker_id": "worker-1",
|
|
1136
|
+
"target_agent_type": "agent_a",
|
|
1137
|
+
"status": "COMPLETED",
|
|
1138
|
+
"parent_message_id": "",
|
|
1139
|
+
}
|
|
1140
|
+
exec_b = {
|
|
1141
|
+
"execution_id": "exec-b",
|
|
1142
|
+
"message_id": "msg-b",
|
|
1143
|
+
"session_id": "sess-1",
|
|
1144
|
+
"worker_id": "worker-2",
|
|
1145
|
+
"target_agent_type": "agent_b",
|
|
1146
|
+
"status": "FAILED",
|
|
1147
|
+
"parent_message_id": "msg-a",
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
registry = AsyncMock()
|
|
1151
|
+
registry.get_all_session_executions.return_value = [exec_a, exec_b]
|
|
1152
|
+
redis = AsyncMock()
|
|
1153
|
+
client = GatewayClient(registry=registry, redis_client=redis)
|
|
1154
|
+
|
|
1155
|
+
result = await client.cancel_session(session_id="sess-1", reason="user abort")
|
|
1156
|
+
|
|
1157
|
+
assert result.success is False
|
|
1158
|
+
assert result.status == "ALREADY_FINISHED"
|
|
1159
|
+
assert result.cancelled_count == 0
|
|
1160
|
+
assert result.already_finished_count == 2
|
|
1161
|
+
|
|
1162
|
+
registry.mark_execution_cancelling.assert_not_called()
|
|
1163
|
+
assert registry.mark_cancel_requested.call_count == 2
|
|
1164
|
+
redis.xadd.assert_not_called()
|
|
1165
|
+
|
|
1166
|
+
|
|
1167
|
+
@pytest.mark.asyncio
|
|
1168
|
+
async def test_client_cancel_session_empty_session_returns_not_found():
|
|
1169
|
+
"""A session with no executions at all should return NOT_FOUND, not be
|
|
1170
|
+
confused with a session whose executions are all already finished."""
|
|
1171
|
+
registry = AsyncMock()
|
|
1172
|
+
registry.get_all_session_executions.return_value = []
|
|
1173
|
+
redis = AsyncMock()
|
|
1174
|
+
client = GatewayClient(registry=registry, redis_client=redis)
|
|
1175
|
+
|
|
1176
|
+
result = await client.cancel_session(session_id="sess-unknown", reason="")
|
|
1177
|
+
|
|
1178
|
+
assert result.success is False
|
|
1179
|
+
assert result.status == "NOT_FOUND"
|
|
1180
|
+
assert result.cancelled_count == 0
|
|
1181
|
+
assert result.already_finished_count == 0
|
|
1182
|
+
|
|
1183
|
+
registry.mark_execution_cancelling.assert_not_called()
|
|
1184
|
+
registry.mark_cancel_requested.assert_not_called()
|
|
1185
|
+
redis.xadd.assert_not_called()
|
|
1186
|
+
|
|
1187
|
+
|
|
1188
|
+
@pytest.mark.asyncio
|
|
1189
|
+
async def test_client_cancel_session_marks_registry_when_worker_not_assigned():
|
|
1190
|
+
"""A non-terminal execution that hasn't been claimed by a worker yet
|
|
1191
|
+
(no worker_id) should still be marked cancelling, but no
|
|
1192
|
+
CancelTaskCommand can be routed anywhere for it yet."""
|
|
1193
|
+
exec_a = {
|
|
1194
|
+
"execution_id": "exec-a",
|
|
1195
|
+
"message_id": "msg-a",
|
|
1196
|
+
"session_id": "sess-1",
|
|
1197
|
+
"worker_id": "",
|
|
1198
|
+
"target_agent_type": "agent_a",
|
|
1199
|
+
"status": "QUEUED",
|
|
1200
|
+
"parent_message_id": "",
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
registry = AsyncMock()
|
|
1204
|
+
registry.get_all_session_executions.return_value = [exec_a]
|
|
1205
|
+
redis = AsyncMock()
|
|
1206
|
+
client = GatewayClient(registry=registry, redis_client=redis)
|
|
1207
|
+
|
|
1208
|
+
result = await client.cancel_session(session_id="sess-1", reason="user abort")
|
|
1209
|
+
|
|
1210
|
+
assert result.success is True
|
|
1211
|
+
assert result.status == "CANCEL_REQUESTED"
|
|
1212
|
+
assert result.cancelled_count == 1
|
|
1213
|
+
|
|
1214
|
+
registry.mark_execution_cancelling.assert_called_once_with(
|
|
1215
|
+
"exec-a", "sess-1", "user abort"
|
|
1216
|
+
)
|
|
1217
|
+
redis.xadd.assert_not_called()
|
|
1218
|
+
|
|
1219
|
+
|
|
1220
|
+
@pytest.mark.asyncio
|
|
1221
|
+
async def test_client_cancel_session_without_registry_raises():
|
|
1222
|
+
"""cancel_session should fail the same way cancel_task does when the
|
|
1223
|
+
client was constructed without a WorkerRegistry."""
|
|
1224
|
+
redis = AsyncMock()
|
|
1225
|
+
client = GatewayClient(registry=None, redis_client=redis)
|
|
1226
|
+
|
|
1227
|
+
with pytest.raises(ValueError):
|
|
1228
|
+
await client.cancel_session(session_id="sess-1")
|
|
1229
|
+
|
|
1230
|
+
|
|
1231
|
+
@pytest.mark.asyncio
|
|
1232
|
+
async def test_byai_client_cancel_session_inherited_unmodified():
|
|
1233
|
+
"""ByaiGatewayClient only overrides send_message; cancel_session should
|
|
1234
|
+
be inherited from GatewayClient and behave identically."""
|
|
1235
|
+
exec_a = {
|
|
1236
|
+
"execution_id": "exec-a",
|
|
1237
|
+
"message_id": "msg-a",
|
|
1238
|
+
"session_id": "sess-1",
|
|
1239
|
+
"worker_id": "worker-1",
|
|
1240
|
+
"target_agent_type": "agent_a",
|
|
1241
|
+
"status": "RUNNING",
|
|
1242
|
+
"parent_message_id": "",
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
registry = AsyncMock()
|
|
1246
|
+
registry.get_all_session_executions.return_value = [exec_a]
|
|
1247
|
+
redis = AsyncMock()
|
|
1248
|
+
client = ByaiGatewayClient(registry=registry, redis_client=redis)
|
|
1249
|
+
|
|
1250
|
+
result = await client.cancel_session(session_id="sess-1", reason="user abort")
|
|
1251
|
+
|
|
1252
|
+
assert result.success is True
|
|
1253
|
+
assert result.status == "CANCEL_REQUESTED"
|
|
1254
|
+
assert result.cancelled_count == 1
|
|
1255
|
+
registry.mark_execution_cancelling.assert_called_once_with(
|
|
1256
|
+
"exec-a", "sess-1", "user abort"
|
|
1257
|
+
)
|
|
1258
|
+
assert redis.xadd.call_count == 1
|
|
1259
|
+
|
|
1260
|
+
|
|
1261
|
+
@pytest.mark.asyncio
|
|
1262
|
+
async def test_client_send_message_resume_reuses_existing_execution_id():
|
|
1263
|
+
"""Resuming a suspended execution must reuse its execution_id and must
|
|
1264
|
+
not re-initialize the registry record for it. initialize_execution()
|
|
1265
|
+
unconditionally overwrites the message_id -> execution_id mapping, which
|
|
1266
|
+
would silently detach the ResumeCommand from the original (still
|
|
1267
|
+
WAITING_USER) execution and orphan it."""
|
|
1268
|
+
mock_redis = AsyncMock()
|
|
1269
|
+
mock_registry = AsyncMock()
|
|
1270
|
+
mock_registry.get_execution_by_message_id.return_value = {
|
|
1271
|
+
"execution_id": "exec-original",
|
|
1272
|
+
"message_id": "msg-1",
|
|
1273
|
+
"session_id": "s1",
|
|
1274
|
+
"status": "WAITING_USER",
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
client = GatewayClient(redis_client=mock_redis, registry=mock_registry)
|
|
1278
|
+
await client.send_message(
|
|
1279
|
+
target_agent_type="langgraph_agent",
|
|
1280
|
+
session_id="s1",
|
|
1281
|
+
content="user reply",
|
|
1282
|
+
action_type="RESUME",
|
|
1283
|
+
message_id="msg-1",
|
|
1284
|
+
target_worker_id="worker-42",
|
|
1285
|
+
)
|
|
1286
|
+
|
|
1287
|
+
mock_registry.get_execution_by_message_id.assert_awaited_once_with(
|
|
1288
|
+
"msg-1", session_id="s1"
|
|
1289
|
+
)
|
|
1290
|
+
mock_registry.initialize_execution.assert_not_called()
|
|
1291
|
+
|
|
1292
|
+
|
|
1293
|
+
@pytest.mark.asyncio
|
|
1294
|
+
async def test_client_send_message_resume_without_match_falls_back_to_new_execution():
|
|
1295
|
+
"""A RESUME whose message_id doesn't resolve to an existing execution
|
|
1296
|
+
(e.g. a genuinely new resume-shaped message) falls back to today's
|
|
1297
|
+
behavior: mint a fresh execution_id and initialize it in the registry."""
|
|
1298
|
+
mock_redis = AsyncMock()
|
|
1299
|
+
mock_registry = AsyncMock()
|
|
1300
|
+
mock_registry.get_execution_by_message_id.return_value = None
|
|
1301
|
+
|
|
1302
|
+
client = GatewayClient(redis_client=mock_redis, registry=mock_registry)
|
|
1303
|
+
await client.send_message(
|
|
1304
|
+
target_agent_type="langgraph_agent",
|
|
1305
|
+
session_id="s1",
|
|
1306
|
+
content="user reply",
|
|
1307
|
+
action_type="RESUME",
|
|
1308
|
+
message_id="msg-unmatched",
|
|
1309
|
+
target_worker_id="worker-42",
|
|
1310
|
+
)
|
|
1311
|
+
|
|
1312
|
+
mock_registry.get_execution_by_message_id.assert_awaited_once_with(
|
|
1313
|
+
"msg-unmatched", session_id="s1"
|
|
1314
|
+
)
|
|
1315
|
+
mock_registry.initialize_execution.assert_awaited_once()
|
|
1316
|
+
initialized = mock_registry.initialize_execution.await_args.args[0]
|
|
1317
|
+
assert initialized["message_id"] == "msg-unmatched"
|
|
1318
|
+
assert initialized["execution_id"]
|
|
1319
|
+
|
|
1320
|
+
|
|
1321
|
+
@pytest.mark.asyncio
|
|
1322
|
+
async def test_client_send_message_ask_agent_does_not_query_registry_for_resume_match():
|
|
1323
|
+
"""A fresh ASK_AGENT dispatch always gets a new message_id, so there is
|
|
1324
|
+
nothing to reattach to -- it should skip the resume lookup entirely and
|
|
1325
|
+
keep minting+initializing a new execution as before."""
|
|
1326
|
+
mock_redis = AsyncMock()
|
|
1327
|
+
mock_registry = AsyncMock()
|
|
1328
|
+
|
|
1329
|
+
client = GatewayClient(redis_client=mock_redis, registry=mock_registry)
|
|
1330
|
+
await client.send_message(
|
|
1331
|
+
target_agent_type="langgraph_agent",
|
|
1332
|
+
session_id="s1",
|
|
1333
|
+
content="hello",
|
|
1334
|
+
action_type="ASK_AGENT",
|
|
1335
|
+
message_id="msg-new",
|
|
1336
|
+
target_worker_id="worker-42",
|
|
1337
|
+
)
|
|
1338
|
+
|
|
1339
|
+
mock_registry.get_execution_by_message_id.assert_not_called()
|
|
1340
|
+
mock_registry.initialize_execution.assert_awaited_once()
|
|
1341
|
+
|
|
1342
|
+
|
|
1343
|
+
@pytest.mark.asyncio
|
|
1344
|
+
async def test_client_send_message_resume_falls_back_when_registry_lacks_lookup():
|
|
1345
|
+
"""A registry double/implementation without get_execution_by_message_id
|
|
1346
|
+
must not blow up a RESUME send -- mirror the defensive hasattr() guard
|
|
1347
|
+
already used for initialize_execution() and fall back to minting a new
|
|
1348
|
+
execution_id."""
|
|
1349
|
+
mock_redis = AsyncMock()
|
|
1350
|
+
|
|
1351
|
+
class RegistryWithoutLookup:
|
|
1352
|
+
async def initialize_execution(self, execution):
|
|
1353
|
+
return None
|
|
1354
|
+
|
|
1355
|
+
async def is_worker_online(self, worker_id):
|
|
1356
|
+
return True
|
|
1357
|
+
|
|
1358
|
+
client = GatewayClient(redis_client=mock_redis, registry=RegistryWithoutLookup())
|
|
1359
|
+
|
|
1360
|
+
response = await client.send_message(
|
|
1361
|
+
target_agent_type="langgraph_agent",
|
|
1362
|
+
session_id="s1",
|
|
1363
|
+
content="user reply",
|
|
1364
|
+
action_type="RESUME",
|
|
1365
|
+
message_id="msg-1",
|
|
1366
|
+
target_worker_id="worker-42",
|
|
1367
|
+
)
|
|
1368
|
+
|
|
1369
|
+
assert response.success is True
|