conductor-python 1.3.9__tar.gz → 1.3.11__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.
- {conductor_python-1.3.9 → conductor_python-1.3.11}/PKG-INFO +27 -2
- {conductor_python-1.3.9 → conductor_python-1.3.11}/README.md +26 -1
- {conductor_python-1.3.9 → conductor_python-1.3.11}/pyproject.toml +1 -1
- conductor_python-1.3.11/src/conductor/client/__init__.py +29 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/automator/async_task_runner.py +72 -5
- conductor_python-1.3.11/src/conductor/client/automator/lease_tracker.py +224 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/automator/task_handler.py +42 -5
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/automator/task_runner.py +189 -23
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/configuration/configuration.py +12 -1
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/workflow_resource_api.py +90 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api_client.py +2 -4
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/async_api_client.py +2 -4
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/async_rest.py +33 -32
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/__init__.py +2 -1
- conductor_python-1.3.11/src/conductor/client/http/models/workflow_message.py +41 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/rest.py +152 -43
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/orkes_workflow_client.py +14 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/worker/worker.py +3 -1
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/worker/worker_config.py +10 -2
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/worker/worker_task.py +14 -4
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/executor/workflow_executor.py +19 -0
- conductor_python-1.3.11/src/conductor/client/workflow/task/pull_workflow_messages_task.py +30 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/task_type.py +1 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow_client.py +16 -1
- conductor_python-1.3.9/src/conductor/client/workflow/task/llm_tasks/utils/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/__init__.py +0 -0
- {conductor_python-1.3.9/src/conductor/client → conductor_python-1.3.11/src/conductor/client/ai}/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/ai/configuration.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/ai/integrations.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/ai/orchestrator.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/authorization_client.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/ai → conductor_python-1.3.11/src/conductor/client/automator}/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/automator/json_schema_generator.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/automator/utils.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/automator → conductor_python-1.3.11/src/conductor/client/configuration}/__init__.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/configuration → conductor_python-1.3.11/src/conductor/client/configuration/settings}/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/configuration/settings/authentication_settings.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/configuration/settings/metrics_settings.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/context/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/context/task_context.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/event/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/event/conductor_event.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/event/event_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/event/event_dispatcher.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/event/listener_register.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/event/listeners.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/configuration/settings → conductor_python-1.3.11/src/conductor/client/event/queue}/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/event/queue/kafka_queue_configuration.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/event/queue/queue_configuration.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/event/queue/queue_worker_configuration.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/event/sync_event_dispatcher.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/event/sync_listener_register.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/event/task_events.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/event/task_runner_events.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/event/workflow_events.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/event/queue → conductor_python-1.3.11/src/conductor/client/exceptions}/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/exceptions/api_error.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/exceptions/api_exception_handler.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/exceptions → conductor_python-1.3.11/src/conductor/client/helpers}/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/helpers/helper.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/helpers → conductor_python-1.3.11/src/conductor/client/http}/__init__.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/http → conductor_python-1.3.11/src/conductor/client/http/api}/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/application_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/async_task_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/authorization_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/event_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/gateway_auth_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/group_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/integration_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/metadata_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/prompt_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/role_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/scheduler_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/schema_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/secret_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/service_registry_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/task_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/token_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/user_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/api/workflow_bulk_resource_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/action.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/auditable.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/authentication_config.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/authorization_request.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/bulk_response.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/circuit_breaker_transition_response.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/conductor_application.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/conductor_user.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/correlation_ids_search_request.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/create_or_update_application_request.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/create_or_update_role_request.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/event_handler.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/external_storage_location.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/generate_token_request.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/group.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/health.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/health_check_status.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/integration.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/integration_api.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/integration_api_update.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/integration_def.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/integration_update.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/permission.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/poll_data.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/prompt_template.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/prompt_test_request.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/proto_registry_entry.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/rate_limit.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/request_param.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/rerun_workflow_request.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/response.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/role.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/save_schedule_request.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/schema_def.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/scrollable_search_result_workflow_summary.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/search_result_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/search_result_task_summary.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/search_result_workflow.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/search_result_workflow_schedule_execution_model.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/search_result_workflow_summary.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/service_method.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/service_registry.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/signal_response.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/skip_task_request.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/start_workflow.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/start_workflow_request.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/state_change_event.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/sub_workflow_params.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/subject_ref.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/tag_object.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/tag_string.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/target_ref.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/task_def.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/task_details.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/task_exec_log.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/task_result.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/task_result_status.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/task_summary.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/terminate_workflow.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/token.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/update_workflow_variables.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/upsert_group_request.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/upsert_user_request.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/workflow.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/workflow_def.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/workflow_run.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/workflow_schedule.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/workflow_schedule_execution_model.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/workflow_state_update.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/workflow_status.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/workflow_summary.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/workflow_tag.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/workflow_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/models/workflow_test_request.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/http/thread.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/integration_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/metadata_client.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/http/api → conductor_python-1.3.11/src/conductor/client/orkes}/__init__.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/orkes → conductor_python-1.3.11/src/conductor/client/orkes/api}/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/api/tags_api.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/orkes/api → conductor_python-1.3.11/src/conductor/client/orkes/models}/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/models/access_key.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/models/access_key_status.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/models/access_type.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/models/created_access_key.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/models/granted_permission.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/models/metadata_tag.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/models/ratelimit_tag.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/orkes_authorization_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/orkes_base_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/orkes_integration_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/orkes_metadata_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/orkes_prompt_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/orkes_scheduler_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/orkes_schema_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/orkes_secret_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/orkes_service_registry_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes/orkes_task_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/orkes_clients.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/prompt_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/scheduler_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/schema_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/secret_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/service_registry_client.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/task_client.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/orkes/models → conductor_python-1.3.11/src/conductor/client/telemetry}/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/telemetry/metrics_collector.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/telemetry → conductor_python-1.3.11/src/conductor/client/telemetry/model}/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/telemetry/model/metric_documentation.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/telemetry/model/metric_label.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/telemetry/model/metric_name.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/telemetry/model → conductor_python-1.3.11/src/conductor/client/worker}/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/worker/exception.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/worker/worker_interface.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/worker/worker_loader.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/worker → conductor_python-1.3.11/src/conductor/client/workflow}/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/conductor_workflow.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/workflow → conductor_python-1.3.11/src/conductor/client/workflow/executor}/__init__.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/workflow/executor → conductor_python-1.3.11/src/conductor/client/workflow/task}/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/do_while_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/dynamic_fork_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/dynamic_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/event_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/fork_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/get_document.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/http_poll_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/http_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/human_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/inline.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/javascript_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/join_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/json_jq_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/kafka_publish.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/kafka_publish_input.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/call_mcp_tool.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/chat_message.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/generate_audio.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/generate_image.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/list_mcp_tools.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/llm_chat_complete.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/llm_generate_embeddings.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/llm_index_documents.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/llm_index_text.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/llm_query_embeddings.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/llm_search_embeddings.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/llm_search_index.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/llm_store_embeddings.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/llm_text_complete.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/tool_call.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/tool_spec.py +0 -0
- {conductor_python-1.3.9/src/conductor/client/workflow/task → conductor_python-1.3.11/src/conductor/client/workflow/task/llm_tasks/utils}/__init__.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/utils/embedding_model.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/llm_tasks/utils/prompt.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/set_variable_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/simple_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/start_workflow_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/sub_workflow_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/switch_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/terminate_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/timeout_policy.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/wait_for_webhook_task.py +0 -0
- {conductor_python-1.3.9 → conductor_python-1.3.11}/src/conductor/client/workflow/task/wait_task.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: conductor-python
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.11
|
|
4
4
|
Summary: Python SDK for working with https://github.com/conductor-oss/conductor
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Author: Orkes
|
|
@@ -54,6 +54,7 @@ If you find [Conductor](https://github.com/conductor-oss/conductor) useful, plea
|
|
|
54
54
|
* [Workers: Sync and Async](#workers-sync-and-async)
|
|
55
55
|
* [Workflows with HTTP Calls and Waits](#workflows-with-http-calls-and-waits)
|
|
56
56
|
* [Long-Running Tasks with TaskContext](#long-running-tasks-with-taskcontext)
|
|
57
|
+
* [Lease Extension for Long-Running Tasks](#lease-extension-for-long-running-tasks)
|
|
57
58
|
* [Monitoring with Metrics](#monitoring-with-metrics)
|
|
58
59
|
* [Managing Workflow Executions](#managing-workflow-executions)
|
|
59
60
|
* [AI & LLM Workflows](#ai--llm-workflows)
|
|
@@ -140,7 +141,7 @@ Create a `quickstart.py` with the following:
|
|
|
140
141
|
```python
|
|
141
142
|
from conductor.client.automator.task_handler import TaskHandler
|
|
142
143
|
from conductor.client.configuration.configuration import Configuration
|
|
143
|
-
from conductor.client.orkes_clients import OrkesClients
|
|
144
|
+
from conductor.client.orkes_clients import OrkesClients # works with OSS Conductor and Orkes Conductor
|
|
144
145
|
from conductor.client.workflow.conductor_workflow import ConductorWorkflow
|
|
145
146
|
from conductor.client.worker.worker_task import worker_task
|
|
146
147
|
|
|
@@ -166,6 +167,8 @@ def main():
|
|
|
166
167
|
workflow.register(overwrite=True)
|
|
167
168
|
|
|
168
169
|
# Start polling for tasks (one worker subprocess per worker function).
|
|
170
|
+
# Note: scan_for_annotated_workers=True only discovers @worker_task functions that have
|
|
171
|
+
# already been imported. If workers are in a separate module, import it first.
|
|
169
172
|
with TaskHandler(configuration=config, scan_for_annotated_workers=True) as task_handler:
|
|
170
173
|
task_handler.start_processes()
|
|
171
174
|
|
|
@@ -310,6 +313,26 @@ def batch_job(batch_id: str) -> Union[dict, TaskInProgress]:
|
|
|
310
313
|
|
|
311
314
|
See [examples/task_context_example.py](examples/task_context_example.py) for all patterns (polling, retry-aware logic, async context, input access).
|
|
312
315
|
|
|
316
|
+
### Lease Extension for Long-Running Tasks
|
|
317
|
+
|
|
318
|
+
For tasks that run longer than `responseTimeoutSeconds` (e.g., LLM inference, data pipelines, batch jobs), enable automatic lease extension. The SDK sends heartbeats at 80% of `responseTimeoutSeconds`, resetting the server's timeout timer so the task stays alive:
|
|
319
|
+
|
|
320
|
+
```python
|
|
321
|
+
from conductor.client.worker.worker_task import worker_task
|
|
322
|
+
|
|
323
|
+
@worker_task(
|
|
324
|
+
task_definition_name='train_model',
|
|
325
|
+
lease_extend_enabled=True, # Automatic heartbeat — keeps task alive
|
|
326
|
+
)
|
|
327
|
+
def train_model(dataset_id: str) -> dict:
|
|
328
|
+
"""Runs for 10 minutes, but responseTimeoutSeconds is only 60s.
|
|
329
|
+
Heartbeats at 48s intervals keep the lease alive."""
|
|
330
|
+
model = train(dataset_id)
|
|
331
|
+
return {'model_id': model.id, 'accuracy': model.accuracy}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Disabled by default. Enable per-worker via decorator, constructor, or environment variable (`conductor_worker_<task>_lease_extend_enabled=true`). See [LEASE_EXTENSION.md](LEASE_EXTENSION.md) for the full guide.
|
|
335
|
+
|
|
313
336
|
### Monitoring with Metrics
|
|
314
337
|
|
|
315
338
|
Enable Prometheus metrics with a single setting — the SDK exposes poll counts, execution times, error rates, and HTTP latency:
|
|
@@ -442,6 +465,7 @@ See the [Examples Guide](examples/README.md) for the full catalog. Key examples:
|
|
|
442
465
|
| [kitchensink.py](examples/kitchensink.py) | All task types (HTTP, JS, JQ, Switch) | `python examples/kitchensink.py` |
|
|
443
466
|
| [workflow_ops.py](examples/workflow_ops.py) | Pause, resume, terminate, retry, restart, rerun, signal | `python examples/workflow_ops.py` |
|
|
444
467
|
| [task_context_example.py](examples/task_context_example.py) | Long-running tasks with TaskInProgress | `python examples/task_context_example.py` |
|
|
468
|
+
| [lease_extension_example.py](examples/lease_extension_example.py) | Automatic heartbeat for long-running tasks | `python examples/lease_extension_example.py` |
|
|
445
469
|
| [metrics_example.py](examples/metrics_example.py) | Prometheus metrics collection | `python examples/metrics_example.py` |
|
|
446
470
|
| [fastapi_worker_service.py](examples/fastapi_worker_service.py) | FastAPI: expose a workflow as an API (+ workers) | `uvicorn examples.fastapi_worker_service:app --port 8081 --workers 1` |
|
|
447
471
|
| [helloworld.py](examples/helloworld/helloworld.py) | Minimal hello world | `python examples/helloworld/helloworld.py` |
|
|
@@ -466,6 +490,7 @@ End-to-end examples covering all APIs for each domain:
|
|
|
466
490
|
| [Worker Design](docs/design/WORKER_DESIGN.md) | Architecture: AsyncTaskRunner vs TaskRunner, discovery, lifecycle |
|
|
467
491
|
| [Worker Guide](docs/WORKER.md) | All worker patterns (function, class, annotation, async) |
|
|
468
492
|
| [Worker Configuration](WORKER_CONFIGURATION.md) | Hierarchical environment variable configuration |
|
|
493
|
+
| [Lease Extension](LEASE_EXTENSION.md) | Automatic heartbeat for long-running tasks |
|
|
469
494
|
| [Workflow Management](docs/WORKFLOW.md) | Start, pause, resume, terminate, retry, search |
|
|
470
495
|
| [Workflow Testing](docs/WORKFLOW_TESTING.md) | Unit testing with mock outputs |
|
|
471
496
|
| [Task Management](docs/TASK_MANAGEMENT.md) | Task operations |
|
|
@@ -19,6 +19,7 @@ If you find [Conductor](https://github.com/conductor-oss/conductor) useful, plea
|
|
|
19
19
|
* [Workers: Sync and Async](#workers-sync-and-async)
|
|
20
20
|
* [Workflows with HTTP Calls and Waits](#workflows-with-http-calls-and-waits)
|
|
21
21
|
* [Long-Running Tasks with TaskContext](#long-running-tasks-with-taskcontext)
|
|
22
|
+
* [Lease Extension for Long-Running Tasks](#lease-extension-for-long-running-tasks)
|
|
22
23
|
* [Monitoring with Metrics](#monitoring-with-metrics)
|
|
23
24
|
* [Managing Workflow Executions](#managing-workflow-executions)
|
|
24
25
|
* [AI & LLM Workflows](#ai--llm-workflows)
|
|
@@ -105,7 +106,7 @@ Create a `quickstart.py` with the following:
|
|
|
105
106
|
```python
|
|
106
107
|
from conductor.client.automator.task_handler import TaskHandler
|
|
107
108
|
from conductor.client.configuration.configuration import Configuration
|
|
108
|
-
from conductor.client.orkes_clients import OrkesClients
|
|
109
|
+
from conductor.client.orkes_clients import OrkesClients # works with OSS Conductor and Orkes Conductor
|
|
109
110
|
from conductor.client.workflow.conductor_workflow import ConductorWorkflow
|
|
110
111
|
from conductor.client.worker.worker_task import worker_task
|
|
111
112
|
|
|
@@ -131,6 +132,8 @@ def main():
|
|
|
131
132
|
workflow.register(overwrite=True)
|
|
132
133
|
|
|
133
134
|
# Start polling for tasks (one worker subprocess per worker function).
|
|
135
|
+
# Note: scan_for_annotated_workers=True only discovers @worker_task functions that have
|
|
136
|
+
# already been imported. If workers are in a separate module, import it first.
|
|
134
137
|
with TaskHandler(configuration=config, scan_for_annotated_workers=True) as task_handler:
|
|
135
138
|
task_handler.start_processes()
|
|
136
139
|
|
|
@@ -275,6 +278,26 @@ def batch_job(batch_id: str) -> Union[dict, TaskInProgress]:
|
|
|
275
278
|
|
|
276
279
|
See [examples/task_context_example.py](examples/task_context_example.py) for all patterns (polling, retry-aware logic, async context, input access).
|
|
277
280
|
|
|
281
|
+
### Lease Extension for Long-Running Tasks
|
|
282
|
+
|
|
283
|
+
For tasks that run longer than `responseTimeoutSeconds` (e.g., LLM inference, data pipelines, batch jobs), enable automatic lease extension. The SDK sends heartbeats at 80% of `responseTimeoutSeconds`, resetting the server's timeout timer so the task stays alive:
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
from conductor.client.worker.worker_task import worker_task
|
|
287
|
+
|
|
288
|
+
@worker_task(
|
|
289
|
+
task_definition_name='train_model',
|
|
290
|
+
lease_extend_enabled=True, # Automatic heartbeat — keeps task alive
|
|
291
|
+
)
|
|
292
|
+
def train_model(dataset_id: str) -> dict:
|
|
293
|
+
"""Runs for 10 minutes, but responseTimeoutSeconds is only 60s.
|
|
294
|
+
Heartbeats at 48s intervals keep the lease alive."""
|
|
295
|
+
model = train(dataset_id)
|
|
296
|
+
return {'model_id': model.id, 'accuracy': model.accuracy}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Disabled by default. Enable per-worker via decorator, constructor, or environment variable (`conductor_worker_<task>_lease_extend_enabled=true`). See [LEASE_EXTENSION.md](LEASE_EXTENSION.md) for the full guide.
|
|
300
|
+
|
|
278
301
|
### Monitoring with Metrics
|
|
279
302
|
|
|
280
303
|
Enable Prometheus metrics with a single setting — the SDK exposes poll counts, execution times, error rates, and HTTP latency:
|
|
@@ -407,6 +430,7 @@ See the [Examples Guide](examples/README.md) for the full catalog. Key examples:
|
|
|
407
430
|
| [kitchensink.py](examples/kitchensink.py) | All task types (HTTP, JS, JQ, Switch) | `python examples/kitchensink.py` |
|
|
408
431
|
| [workflow_ops.py](examples/workflow_ops.py) | Pause, resume, terminate, retry, restart, rerun, signal | `python examples/workflow_ops.py` |
|
|
409
432
|
| [task_context_example.py](examples/task_context_example.py) | Long-running tasks with TaskInProgress | `python examples/task_context_example.py` |
|
|
433
|
+
| [lease_extension_example.py](examples/lease_extension_example.py) | Automatic heartbeat for long-running tasks | `python examples/lease_extension_example.py` |
|
|
410
434
|
| [metrics_example.py](examples/metrics_example.py) | Prometheus metrics collection | `python examples/metrics_example.py` |
|
|
411
435
|
| [fastapi_worker_service.py](examples/fastapi_worker_service.py) | FastAPI: expose a workflow as an API (+ workers) | `uvicorn examples.fastapi_worker_service:app --port 8081 --workers 1` |
|
|
412
436
|
| [helloworld.py](examples/helloworld/helloworld.py) | Minimal hello world | `python examples/helloworld/helloworld.py` |
|
|
@@ -431,6 +455,7 @@ End-to-end examples covering all APIs for each domain:
|
|
|
431
455
|
| [Worker Design](docs/design/WORKER_DESIGN.md) | Architecture: AsyncTaskRunner vs TaskRunner, discovery, lifecycle |
|
|
432
456
|
| [Worker Guide](docs/WORKER.md) | All worker patterns (function, class, annotation, async) |
|
|
433
457
|
| [Worker Configuration](WORKER_CONFIGURATION.md) | Hierarchical environment variable configuration |
|
|
458
|
+
| [Lease Extension](LEASE_EXTENSION.md) | Automatic heartbeat for long-running tasks |
|
|
434
459
|
| [Workflow Management](docs/WORKFLOW.md) | Start, pause, resume, terminate, retry, search |
|
|
435
460
|
| [Workflow Testing](docs/WORKFLOW_TESTING.md) | Unit testing with mock outputs |
|
|
436
461
|
| [Task Management](docs/TASK_MANAGEMENT.md) | Task operations |
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "conductor-python"
|
|
7
|
-
version = "1.3.
|
|
7
|
+
version = "1.3.11" # Do not change! Placeholder. Real version injected during build (edited)
|
|
8
8
|
description = "Python SDK for working with https://github.com/conductor-oss/conductor"
|
|
9
9
|
authors = ["Orkes <developers@orkes.io>"]
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Convenience re-exports for common symbols
|
|
2
|
+
# Allows: from conductor.client import Configuration, TaskHandler, ...
|
|
3
|
+
from conductor.client.configuration.configuration import Configuration
|
|
4
|
+
from conductor.client.automator.task_handler import TaskHandler
|
|
5
|
+
from conductor.client.automator.task_runner import TaskRunner
|
|
6
|
+
from conductor.client.orkes_clients import OrkesClients
|
|
7
|
+
from conductor.client.workflow.conductor_workflow import ConductorWorkflow
|
|
8
|
+
from conductor.client.workflow.executor.workflow_executor import WorkflowExecutor
|
|
9
|
+
from conductor.client.worker.worker_task import worker_task
|
|
10
|
+
from conductor.client.worker.worker_interface import WorkerInterface
|
|
11
|
+
from conductor.client.http.models.task import Task
|
|
12
|
+
from conductor.client.http.models.task_result import TaskResult
|
|
13
|
+
from conductor.client.http.models.task_result_status import TaskResultStatus
|
|
14
|
+
from conductor.client.http.models.start_workflow_request import StartWorkflowRequest
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"Configuration",
|
|
18
|
+
"TaskHandler",
|
|
19
|
+
"TaskRunner",
|
|
20
|
+
"OrkesClients",
|
|
21
|
+
"ConductorWorkflow",
|
|
22
|
+
"WorkflowExecutor",
|
|
23
|
+
"worker_task",
|
|
24
|
+
"WorkerInterface",
|
|
25
|
+
"Task",
|
|
26
|
+
"TaskResult",
|
|
27
|
+
"TaskResultStatus",
|
|
28
|
+
"StartWorkflowRequest",
|
|
29
|
+
]
|
|
@@ -32,6 +32,7 @@ from conductor.client.worker.worker_interface import WorkerInterface
|
|
|
32
32
|
from conductor.client.worker.worker_config import resolve_worker_config, get_worker_config_oneline
|
|
33
33
|
from conductor.client.worker.exception import NonRetryableException
|
|
34
34
|
from conductor.client.automator.json_schema_generator import generate_json_schema_from_function
|
|
35
|
+
from conductor.client.automator.lease_tracker import LeaseManager
|
|
35
36
|
|
|
36
37
|
logger = logging.getLogger(
|
|
37
38
|
Configuration.get_logging_formatted_name(
|
|
@@ -112,6 +113,9 @@ class AsyncTaskRunner:
|
|
|
112
113
|
self._semaphore = None
|
|
113
114
|
self._shutdown = False # Flag to indicate graceful shutdown
|
|
114
115
|
self._use_update_v2 = True # Will be set to False if server doesn't support v2 endpoint
|
|
116
|
+
self._lease_manager = LeaseManager.get_instance()
|
|
117
|
+
self._tracked_task_ids = set() # Local set for cleanup on shutdown
|
|
118
|
+
self._sync_task_client = None # Created after fork for LeaseManager heartbeats
|
|
115
119
|
|
|
116
120
|
async def run(self) -> None:
|
|
117
121
|
"""Main async loop - runs continuously in single event loop."""
|
|
@@ -131,6 +135,17 @@ class AsyncTaskRunner:
|
|
|
131
135
|
api_client=self.async_api_client
|
|
132
136
|
)
|
|
133
137
|
|
|
138
|
+
# Create a sync TaskResourceApi for LeaseManager heartbeats
|
|
139
|
+
# (LeaseManager sends heartbeats from its own ThreadPoolExecutor)
|
|
140
|
+
from conductor.client.http.api.task_resource_api import TaskResourceApi
|
|
141
|
+
from conductor.client.http.api_client import ApiClient
|
|
142
|
+
self._sync_task_client = TaskResourceApi(
|
|
143
|
+
ApiClient(
|
|
144
|
+
configuration=self.configuration,
|
|
145
|
+
metrics_collector=self.metrics_collector
|
|
146
|
+
)
|
|
147
|
+
)
|
|
148
|
+
|
|
134
149
|
# Create semaphore in the event loop (must be created within the loop)
|
|
135
150
|
self._semaphore = asyncio.Semaphore(self._max_workers)
|
|
136
151
|
|
|
@@ -166,6 +181,11 @@ class AsyncTaskRunner:
|
|
|
166
181
|
"""Clean up async resources."""
|
|
167
182
|
logger.debug("Cleaning up AsyncTaskRunner resources...")
|
|
168
183
|
|
|
184
|
+
# Untrack all tasks this runner was tracking from the shared LeaseManager
|
|
185
|
+
for task_id in list(self._tracked_task_ids):
|
|
186
|
+
self._lease_manager.untrack(task_id)
|
|
187
|
+
self._tracked_task_ids.clear()
|
|
188
|
+
|
|
169
189
|
# Cancel any running tasks (EAFP style)
|
|
170
190
|
try:
|
|
171
191
|
for task in list(self._running_tasks):
|
|
@@ -182,6 +202,13 @@ class AsyncTaskRunner:
|
|
|
182
202
|
except (IOError, OSError) as e:
|
|
183
203
|
logger.warning(f"Error closing async client: {e}")
|
|
184
204
|
|
|
205
|
+
# Close sync HTTP client used for lease heartbeats
|
|
206
|
+
if self._sync_task_client:
|
|
207
|
+
try:
|
|
208
|
+
self._sync_task_client.api_client.rest_client.connection.close()
|
|
209
|
+
except Exception:
|
|
210
|
+
pass
|
|
211
|
+
|
|
185
212
|
# Clear event listeners
|
|
186
213
|
self.event_dispatcher = None
|
|
187
214
|
|
|
@@ -227,7 +254,18 @@ class AsyncTaskRunner:
|
|
|
227
254
|
output_schema_name = None
|
|
228
255
|
schema_registry_available = True
|
|
229
256
|
|
|
230
|
-
if
|
|
257
|
+
# Check if schema registration is enabled for this worker
|
|
258
|
+
register_schema = getattr(self.worker, 'register_schema', False)
|
|
259
|
+
# Also check global Configuration default
|
|
260
|
+
if hasattr(self.configuration, 'register_schema') and self.configuration.register_schema is not None:
|
|
261
|
+
# Worker-level setting takes precedence if explicitly set (not default)
|
|
262
|
+
if not hasattr(self.worker, 'register_schema'):
|
|
263
|
+
register_schema = self.configuration.register_schema
|
|
264
|
+
|
|
265
|
+
if not register_schema:
|
|
266
|
+
logger.debug(f"Schema registration disabled for {task_name} (register_schema=False)")
|
|
267
|
+
|
|
268
|
+
if register_schema and hasattr(self.worker, 'execute_function'):
|
|
231
269
|
logger.debug(f"Generating JSON schemas from function signature...")
|
|
232
270
|
# Pass strict_schema flag to control additionalProperties
|
|
233
271
|
strict_mode = getattr(self.worker, 'strict_schema', False)
|
|
@@ -309,6 +347,8 @@ class AsyncTaskRunner:
|
|
|
309
347
|
logger.debug(f"Could not register schemas for {task_name}: {e}")
|
|
310
348
|
else:
|
|
311
349
|
logger.debug(f" ⚠ No schemas generated (unable to analyze function signature)")
|
|
350
|
+
elif not register_schema:
|
|
351
|
+
pass # Already logged above
|
|
312
352
|
else:
|
|
313
353
|
logger.debug(f" ⚠ Class-based worker (no execute_function) - registering task without schemas")
|
|
314
354
|
|
|
@@ -573,15 +613,13 @@ class AsyncTaskRunner:
|
|
|
573
613
|
# Acquire semaphore for entire task lifecycle (execution + update)
|
|
574
614
|
# This ensures we never exceed thread_count tasks in any stage of processing
|
|
575
615
|
async with self._semaphore:
|
|
616
|
+
self._track_lease(task)
|
|
576
617
|
try:
|
|
577
618
|
while task is not None and not self._shutdown:
|
|
578
619
|
task_result = await self.__async_execute_task(task)
|
|
579
|
-
# If task returned TaskInProgress, don't update yet
|
|
580
|
-
if isinstance(task_result, TaskInProgress):
|
|
581
|
-
logger.debug("Task %s is in progress, will update when complete", task.task_id)
|
|
582
|
-
return
|
|
583
620
|
if task_result is None:
|
|
584
621
|
return
|
|
622
|
+
self._untrack_lease(task.task_id)
|
|
585
623
|
# Update task and get next task from v2 response
|
|
586
624
|
task = await self.__async_update_task(task_result)
|
|
587
625
|
# v2 returns the next task; if v1 was used (returns None), immediately
|
|
@@ -589,12 +627,17 @@ class AsyncTaskRunner:
|
|
|
589
627
|
if task is None and not self._use_update_v2 and not self._shutdown:
|
|
590
628
|
tasks = await self.__async_batch_poll(1)
|
|
591
629
|
task = tasks[0] if tasks else None
|
|
630
|
+
if task is not None:
|
|
631
|
+
self._track_lease(task)
|
|
592
632
|
except Exception as e:
|
|
593
633
|
logger.error(
|
|
594
634
|
"Error executing/updating task %s: %s",
|
|
595
635
|
task.task_id if task else "unknown",
|
|
596
636
|
traceback.format_exc()
|
|
597
637
|
)
|
|
638
|
+
finally:
|
|
639
|
+
if task is not None:
|
|
640
|
+
self._untrack_lease(task.task_id)
|
|
598
641
|
|
|
599
642
|
async def __async_execute_task(self, task: Task) -> TaskResult:
|
|
600
643
|
"""Execute async worker function directly (no threads, no BackgroundEventLoop)."""
|
|
@@ -908,6 +951,30 @@ class AsyncTaskRunner:
|
|
|
908
951
|
|
|
909
952
|
return None
|
|
910
953
|
|
|
954
|
+
# -- Lease extension (heartbeat) delegation to LeaseManager ----------------
|
|
955
|
+
|
|
956
|
+
def _track_lease(self, task) -> None:
|
|
957
|
+
"""Start tracking a task for lease extension via the shared LeaseManager."""
|
|
958
|
+
if not getattr(self.worker, 'lease_extend_enabled', False):
|
|
959
|
+
return
|
|
960
|
+
timeout = getattr(task, 'response_timeout_seconds', None) or 0
|
|
961
|
+
if timeout <= 0:
|
|
962
|
+
return
|
|
963
|
+
self._lease_manager.track(
|
|
964
|
+
task_id=task.task_id,
|
|
965
|
+
workflow_instance_id=task.workflow_instance_id,
|
|
966
|
+
response_timeout_seconds=timeout,
|
|
967
|
+
task_client=self._sync_task_client,
|
|
968
|
+
)
|
|
969
|
+
self._tracked_task_ids.add(task.task_id)
|
|
970
|
+
|
|
971
|
+
def _untrack_lease(self, task_id: str) -> None:
|
|
972
|
+
"""Stop tracking a task for lease extension."""
|
|
973
|
+
self._lease_manager.untrack(task_id)
|
|
974
|
+
self._tracked_task_ids.discard(task_id)
|
|
975
|
+
|
|
976
|
+
# --------------------------------------------------------------------------
|
|
977
|
+
|
|
911
978
|
def __set_worker_properties(self) -> None:
|
|
912
979
|
"""
|
|
913
980
|
Resolve worker configuration using hierarchical override (same as TaskRunner).
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""Centralized lease extension (heartbeat) management for Conductor task runners.
|
|
2
|
+
|
|
3
|
+
Architecture:
|
|
4
|
+
LeaseManager runs a single background daemon thread that periodically checks
|
|
5
|
+
for tasks needing lease extension heartbeats. Due heartbeats are dispatched
|
|
6
|
+
to a small fixed ThreadPoolExecutor for parallel, non-blocking API calls.
|
|
7
|
+
|
|
8
|
+
This decouples heartbeat work entirely from worker poll loops, preventing
|
|
9
|
+
heartbeat API calls (and their retries) from blocking task polling.
|
|
10
|
+
|
|
11
|
+
Thread-safe: track() and untrack() can be called from any thread or event loop.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
import threading
|
|
17
|
+
import time
|
|
18
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from typing import Any, Dict, Optional
|
|
21
|
+
|
|
22
|
+
from conductor.client.http.models.task_result import TaskResult
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
# Lease extension constants (matches Java SDK)
|
|
27
|
+
LEASE_EXTEND_RETRY_COUNT = 3
|
|
28
|
+
LEASE_EXTEND_DURATION_FACTOR = 0.8
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class LeaseInfo:
|
|
33
|
+
"""Tracks when a heartbeat is next due for an in-flight task."""
|
|
34
|
+
task_id: str
|
|
35
|
+
workflow_instance_id: str
|
|
36
|
+
response_timeout_seconds: float
|
|
37
|
+
last_heartbeat_time: float # time.monotonic() of last heartbeat (or task start)
|
|
38
|
+
interval_seconds: float # 80% of responseTimeoutSeconds
|
|
39
|
+
task_client: Any = None # Sync TaskResourceApi for sending heartbeats
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class LeaseManager:
|
|
43
|
+
"""Centralized lease extension manager for all workers in a process.
|
|
44
|
+
|
|
45
|
+
One background daemon thread checks for due heartbeats at a fixed interval.
|
|
46
|
+
A small ThreadPoolExecutor sends heartbeat API calls in parallel.
|
|
47
|
+
Poll loops are never blocked by heartbeat work.
|
|
48
|
+
|
|
49
|
+
Usage:
|
|
50
|
+
manager = LeaseManager.get_instance()
|
|
51
|
+
manager.track(task_id, workflow_id, timeout, task_client)
|
|
52
|
+
# ... task completes ...
|
|
53
|
+
manager.untrack(task_id)
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
_instance: Optional['LeaseManager'] = None
|
|
57
|
+
_instance_lock = threading.Lock()
|
|
58
|
+
_instance_pid: Optional[int] = None
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def get_instance(cls, check_interval: float = 1.0,
|
|
62
|
+
max_heartbeat_workers: int = 4) -> 'LeaseManager':
|
|
63
|
+
"""Get or create the process-wide LeaseManager singleton.
|
|
64
|
+
|
|
65
|
+
Fork-safe: a new instance is created after fork (threads don't survive fork).
|
|
66
|
+
"""
|
|
67
|
+
current_pid = os.getpid()
|
|
68
|
+
if cls._instance is None or cls._instance_pid != current_pid:
|
|
69
|
+
with cls._instance_lock:
|
|
70
|
+
if cls._instance is None or cls._instance_pid != current_pid:
|
|
71
|
+
cls._instance = cls(
|
|
72
|
+
check_interval=check_interval,
|
|
73
|
+
max_heartbeat_workers=max_heartbeat_workers,
|
|
74
|
+
)
|
|
75
|
+
cls._instance_pid = current_pid
|
|
76
|
+
return cls._instance
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def _reset_instance(cls):
|
|
80
|
+
"""Reset the singleton. For testing only."""
|
|
81
|
+
with cls._instance_lock:
|
|
82
|
+
if cls._instance is not None:
|
|
83
|
+
cls._instance.shutdown()
|
|
84
|
+
cls._instance = None
|
|
85
|
+
cls._instance_pid = None
|
|
86
|
+
|
|
87
|
+
def __init__(self, check_interval: float = 1.0, max_heartbeat_workers: int = 4):
|
|
88
|
+
self._tracked: Dict[str, LeaseInfo] = {}
|
|
89
|
+
self._lock = threading.Lock()
|
|
90
|
+
self._executor = ThreadPoolExecutor(
|
|
91
|
+
max_workers=max_heartbeat_workers,
|
|
92
|
+
thread_name_prefix="lease-heartbeat",
|
|
93
|
+
)
|
|
94
|
+
self._stop_event = threading.Event()
|
|
95
|
+
self._check_interval = check_interval
|
|
96
|
+
self._thread: Optional[threading.Thread] = None
|
|
97
|
+
self._started = False
|
|
98
|
+
self._start_lock = threading.Lock()
|
|
99
|
+
|
|
100
|
+
def _ensure_started(self) -> None:
|
|
101
|
+
"""Lazily start the background thread on first track() call."""
|
|
102
|
+
if self._started:
|
|
103
|
+
return
|
|
104
|
+
with self._start_lock:
|
|
105
|
+
if not self._started:
|
|
106
|
+
self._thread = threading.Thread(
|
|
107
|
+
target=self._run, daemon=True, name="lease-manager",
|
|
108
|
+
)
|
|
109
|
+
self._thread.start()
|
|
110
|
+
self._started = True
|
|
111
|
+
logger.debug(
|
|
112
|
+
"LeaseManager started (check_interval=%.1fs)", self._check_interval,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def track(self, task_id: str, workflow_instance_id: str,
|
|
116
|
+
response_timeout_seconds: float, task_client: Any) -> None:
|
|
117
|
+
"""Start tracking a task for lease extension heartbeats.
|
|
118
|
+
|
|
119
|
+
Thread-safe. Can be called from any worker thread or event loop.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
task_id: Conductor task ID.
|
|
123
|
+
workflow_instance_id: Workflow instance this task belongs to.
|
|
124
|
+
response_timeout_seconds: The task's server-side response timeout.
|
|
125
|
+
task_client: A **sync** TaskResourceApi for sending heartbeat API calls.
|
|
126
|
+
"""
|
|
127
|
+
interval = response_timeout_seconds * LEASE_EXTEND_DURATION_FACTOR
|
|
128
|
+
if interval < 1:
|
|
129
|
+
logger.debug(
|
|
130
|
+
"Skipping lease tracking for task %s (interval %.1fs too short)",
|
|
131
|
+
task_id, interval,
|
|
132
|
+
)
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
info = LeaseInfo(
|
|
136
|
+
task_id=task_id,
|
|
137
|
+
workflow_instance_id=workflow_instance_id,
|
|
138
|
+
response_timeout_seconds=response_timeout_seconds,
|
|
139
|
+
last_heartbeat_time=time.monotonic(),
|
|
140
|
+
interval_seconds=interval,
|
|
141
|
+
task_client=task_client,
|
|
142
|
+
)
|
|
143
|
+
with self._lock:
|
|
144
|
+
self._tracked[task_id] = info
|
|
145
|
+
self._ensure_started()
|
|
146
|
+
logger.debug(
|
|
147
|
+
"Tracking lease for task %s (timeout=%ss, heartbeat every %ss)",
|
|
148
|
+
task_id, response_timeout_seconds, interval,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
def untrack(self, task_id: str) -> None:
|
|
152
|
+
"""Stop tracking a task. Thread-safe."""
|
|
153
|
+
with self._lock:
|
|
154
|
+
removed = self._tracked.pop(task_id, None)
|
|
155
|
+
if removed is not None:
|
|
156
|
+
logger.debug("Untracked lease for task %s", task_id)
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def tracked_count(self) -> int:
|
|
160
|
+
"""Number of currently tracked tasks."""
|
|
161
|
+
with self._lock:
|
|
162
|
+
return len(self._tracked)
|
|
163
|
+
|
|
164
|
+
# -- Background thread -----------------------------------------------------
|
|
165
|
+
|
|
166
|
+
def _run(self) -> None:
|
|
167
|
+
"""Background loop — checks for due heartbeats at fixed intervals."""
|
|
168
|
+
while not self._stop_event.is_set():
|
|
169
|
+
try:
|
|
170
|
+
self._check_and_send()
|
|
171
|
+
except Exception as e:
|
|
172
|
+
logger.error("LeaseManager error: %s", e)
|
|
173
|
+
self._stop_event.wait(self._check_interval)
|
|
174
|
+
|
|
175
|
+
def _check_and_send(self) -> None:
|
|
176
|
+
"""Find tasks with due heartbeats and dispatch to the thread pool."""
|
|
177
|
+
now = time.monotonic()
|
|
178
|
+
with self._lock:
|
|
179
|
+
due = [
|
|
180
|
+
info for info in self._tracked.values()
|
|
181
|
+
if now - info.last_heartbeat_time >= info.interval_seconds
|
|
182
|
+
]
|
|
183
|
+
for info in due:
|
|
184
|
+
# Update timestamp immediately to prevent double-dispatch on next tick
|
|
185
|
+
info.last_heartbeat_time = time.monotonic()
|
|
186
|
+
self._executor.submit(self._send_heartbeat, info)
|
|
187
|
+
|
|
188
|
+
@staticmethod
|
|
189
|
+
def _send_heartbeat(info: LeaseInfo) -> None:
|
|
190
|
+
"""Send a single lease extension heartbeat with retry.
|
|
191
|
+
|
|
192
|
+
Runs in a pool thread — blocking retries only block the pool thread,
|
|
193
|
+
never a poll loop.
|
|
194
|
+
"""
|
|
195
|
+
result = TaskResult(
|
|
196
|
+
task_id=info.task_id,
|
|
197
|
+
workflow_instance_id=info.workflow_instance_id,
|
|
198
|
+
extend_lease=True,
|
|
199
|
+
)
|
|
200
|
+
for attempt in range(LEASE_EXTEND_RETRY_COUNT):
|
|
201
|
+
try:
|
|
202
|
+
info.task_client.update_task(body=result)
|
|
203
|
+
logger.debug("Extended lease for task %s", info.task_id)
|
|
204
|
+
return
|
|
205
|
+
except Exception as e:
|
|
206
|
+
if attempt < LEASE_EXTEND_RETRY_COUNT - 1:
|
|
207
|
+
time.sleep(0.5 * (attempt + 2))
|
|
208
|
+
else:
|
|
209
|
+
logger.error(
|
|
210
|
+
"Failed to extend lease for task %s after %d attempts: %s",
|
|
211
|
+
info.task_id, LEASE_EXTEND_RETRY_COUNT, e,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# -- Lifecycle -------------------------------------------------------------
|
|
215
|
+
|
|
216
|
+
def shutdown(self) -> None:
|
|
217
|
+
"""Stop the background thread and thread pool."""
|
|
218
|
+
self._stop_event.set()
|
|
219
|
+
if self._started and self._thread is not None:
|
|
220
|
+
self._thread.join(timeout=5)
|
|
221
|
+
self._executor.shutdown(wait=False)
|
|
222
|
+
with self._lock:
|
|
223
|
+
self._tracked.clear()
|
|
224
|
+
logger.debug("LeaseManager shut down")
|