openhands-sdk 1.55.1__tar.gz → 1.57.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/PKG-INFO +2 -2
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/agent.py +7 -11
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/base.py +5 -24
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/condenser/base.py +4 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/condenser/llm_summarizing_condenser.py +4 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/base.py +29 -1
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/impl/local_conversation.py +38 -10
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/impl/remote_conversation.py +127 -44
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/request.py +147 -2
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/state.py +8 -8
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/title_utils.py +29 -12
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/llm_convertible/action.py +30 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/resume_transcript.py +1 -1
- openhands_sdk-1.57.0/openhands/sdk/git/cached_repo.py +1181 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/git/git_changes.py +9 -3
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/git/utils.py +2 -2
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/llm_profile_store.py +7 -1
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/meta_profile_store.py +4 -5
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/unverified_models.py +16 -3
- openhands_sdk-1.57.0/openhands/sdk/logger/_rich_handler.py +62 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/logger/logger.py +3 -2
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/marketplace/registry.py +44 -1
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/marketplace/types.py +60 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/profiles/agent_profile.py +17 -8
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/profiles/profile_refs.py +7 -2
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/settings/metadata.py +2 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/settings/model.py +170 -68
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/installed.py +6 -1
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/skill.py +6 -8
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/utils.py +10 -11
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/defaults.py +0 -18
- openhands_sdk-1.57.0/openhands/sdk/utils/cipher.py +131 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/pydantic_secrets.py +49 -5
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/redact.py +3 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands_sdk.egg-info/PKG-INFO +2 -2
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands_sdk.egg-info/SOURCES.txt +2 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands_sdk.egg-info/requires.txt +1 -1
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/pyproject.toml +2 -2
- openhands_sdk-1.55.1/openhands/sdk/git/cached_repo.py +0 -517
- openhands_sdk-1.55.1/openhands/sdk/utils/cipher.py +0 -81
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/acp_agent.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/acp_contracts.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/acp_file_credentials.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/acp_model_discovery.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/acp_models.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/acp_tracing.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/critic_mixin.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/parallel_executor.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/response_dispatch.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/stream_context.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/utils.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/automation/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/banner.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/agent_context.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/condenser/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/condenser/no_op_condenser.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/condenser/pipeline_condenser.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/condenser/prompts/summarizing_events.j2 +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/condenser/prompts/summarizing_system.j2 +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/condenser/utils.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/memory.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/presets.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/prompt.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/registry.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/section.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/sections/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/sections/dynamic.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/sections/planning.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/sections/static.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/templates/ask_agent_template.j2 +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/manipulation_indices.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/properties/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/properties/base.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/properties/batch_atomicity.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/properties/observation_uniqueness.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/properties/tool_call_matching.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/properties/tool_loop_atomicity.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/view.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/cancellation.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/conversation.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/conversation_stats.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/event_store.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/events_list_base.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/exceptions.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/fifo_lock.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/goal/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/goal/controller.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/goal/judge.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/goal/prompts.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/goal/runner.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/impl/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/message_request.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/persistence_const.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/resource_lock_manager.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/response_utils.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/secret_registry.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/stuck_detector.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/types.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/visualizer/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/visualizer/base.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/visualizer/default.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/credential.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/base.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/agent_finished.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/api/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/api/chat_template.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/api/client.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/api/critic.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/api/taxonomy.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/empty_patch.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/pass_critic.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/result.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/acp_tool_call.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/base.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/condenser.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/conversation_error.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/conversation_state.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/error_classification.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/hook_execution.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/llm_completion_log.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/llm_convertible/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/llm_convertible/message.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/llm_convertible/observation.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/llm_convertible/reasoning_utils.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/llm_convertible/system.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/security.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/streaming_delta.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/token.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/types.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/user_action.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/extensions/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/extensions/fetch.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/extensions/installation/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/extensions/installation/info.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/extensions/installation/interface.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/extensions/installation/manager.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/extensions/installation/metadata.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/extensions/installation/utils.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/git/exceptions.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/git/git_commits.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/git/git_diff.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/git/models.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/hooks/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/hooks/config.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/hooks/conversation_hooks.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/hooks/executor.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/hooks/manager.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/hooks/types.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/io/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/io/base.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/io/cache.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/io/local.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/io/memory.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/launch/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/launch/errors.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/launch/finalize.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/launch/preview.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/launch/resolve.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/_message_normalization.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/_response_stream.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/_tokenizer.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/auth/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/auth/credentials.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/auth/openai.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/call_context.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/cleanup_profile.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/exceptions/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/exceptions/classifier.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/exceptions/mapping.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/exceptions/types.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/fallback_strategy.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/llm.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/llm_registry.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/llm_response.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/message.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/mixins/fn_call_converter.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/mixins/fn_call_examples.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/mixins/non_native_fc.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/options/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/options/chat_options.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/options/common.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/options/responses_options.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/provider_connection_store.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/router/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/router/base.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/router/impl/multimodal.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/router/impl/random.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/streaming.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/image_inline.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/image_resize.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/litellm_provider.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/metrics.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/model_features.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/model_info.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/model_prompt_spec.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/openhands_provider.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/providers/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/providers/openrouter.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/responses_serialization.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/retry_mixin.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/runtime_metadata.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/telemetry.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/verified_models.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/vertex_preflight.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/logger/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/logger/rolling.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/marketplace/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/marketplace/registration.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/mcp/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/mcp/client.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/mcp/config.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/mcp/definition.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/mcp/exceptions.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/mcp/oauth.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/mcp/tool.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/mcp/utils.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/observability/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/observability/laminar.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/observability/utils.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/discovery.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/fetch.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/format/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/format/agent_plugins.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/format/agent_plugins_mcp.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/format/base.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/format/claude_code.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/format/schemas/mcp-1.0.0.schema.json +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/format/schemas/plugin-1.0.0.schema.json +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/installed.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/loader.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/paths.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/plugin.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/source.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/strict_fetch.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/types.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/profiles/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/profiles/agent_profile_store.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/profiles/resolver.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/profiles/seed.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/py.typed +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/secret/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/secret/secrets.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/_shell_ast.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/analyzer.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/confirmation_policy.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/defense_in_depth/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/defense_in_depth/pattern.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/defense_in_depth/policy_rails.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/defense_in_depth/shell_semantics.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/defense_in_depth/utils.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/ensemble.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/grayswan/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/grayswan/analyzer.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/grayswan/utils.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/llm_analyzer.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/risk.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/shell_parser.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/toolshield_helpers.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/toolshield_llm_analyzer.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/settings/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/settings/acp_install_catalog.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/settings/acp_providers.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/settings/api_models.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/exceptions.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/execute.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/fetch.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/trigger.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/types.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/subagent/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/subagent/load.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/subagent/registry.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/subagent/schema.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/subagent/scope.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/testing/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/testing/test_llm.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/builtins/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/builtins/classify_and_switch_llm.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/builtins/finish.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/builtins/invoke_skill.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/builtins/switch_llm.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/builtins/think.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/builtins/vision_inspect.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/client_tool.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/registry.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/schema.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/spec.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/tool.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/async_executor.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/async_utils.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/command.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/datetime.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/deprecation.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/files.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/github.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/json.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/masking.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/models.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/paging.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/path.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/truncate.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/visualize.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/base.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/local.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/models.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/remote/__init__.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/remote/async_remote_workspace.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/remote/base.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/remote/remote_workspace_mixin.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/repo.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/workspace.py +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands_sdk.egg-info/dependency_links.txt +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands_sdk.egg-info/top_level.txt +0 -0
- {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.57.0
|
|
4
4
|
Summary: OpenHands SDK - Core functionality for building AI agents
|
|
5
5
|
Project-URL: Source, https://github.com/OpenHands/software-agent-sdk
|
|
6
6
|
Project-URL: Homepage, https://github.com/OpenHands/software-agent-sdk
|
|
@@ -15,7 +15,7 @@ Requires-Dist: filelock>=3.20.1
|
|
|
15
15
|
Requires-Dist: httpx[socks]>=0.28.1
|
|
16
16
|
Requires-Dist: jsonschema>=4.23.0
|
|
17
17
|
Requires-Dist: joserfc>=1.7.5
|
|
18
|
-
Requires-Dist: litellm
|
|
18
|
+
Requires-Dist: litellm<1.106,>=1.93.0
|
|
19
19
|
Requires-Dist: pillow>=12.3.0
|
|
20
20
|
Requires-Dist: pydantic>=2.13.5
|
|
21
21
|
Requires-Dist: python-frontmatter>=1.3.0
|
|
@@ -625,6 +625,10 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
625
625
|
preset = self._prompt_preset or PromptPreset.DEFAULT
|
|
626
626
|
return create_registry(preset).build(ctx).dynamic
|
|
627
627
|
|
|
628
|
+
@staticmethod
|
|
629
|
+
def _mark_conversation_finished(state: ConversationState) -> None:
|
|
630
|
+
state.execution_status = ConversationExecutionStatus.FINISHED
|
|
631
|
+
|
|
628
632
|
def _execute_actions(
|
|
629
633
|
self,
|
|
630
634
|
conversation: LocalConversation,
|
|
@@ -648,11 +652,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
648
652
|
check_iterative_refinement=lambda ae: (
|
|
649
653
|
self._check_iterative_refinement(conversation, ae)
|
|
650
654
|
),
|
|
651
|
-
mark_finished=lambda:
|
|
652
|
-
state,
|
|
653
|
-
"execution_status",
|
|
654
|
-
ConversationExecutionStatus.FINISHED,
|
|
655
|
-
),
|
|
655
|
+
mark_finished=lambda: self._mark_conversation_finished(state),
|
|
656
656
|
)
|
|
657
657
|
|
|
658
658
|
async def _aexecute_actions(
|
|
@@ -683,11 +683,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
683
683
|
check_iterative_refinement=lambda ae: (
|
|
684
684
|
self._check_iterative_refinement(conversation, ae)
|
|
685
685
|
),
|
|
686
|
-
mark_finished=lambda:
|
|
687
|
-
state,
|
|
688
|
-
"execution_status",
|
|
689
|
-
ConversationExecutionStatus.FINISHED,
|
|
690
|
-
),
|
|
686
|
+
mark_finished=lambda: self._mark_conversation_finished(state),
|
|
691
687
|
)
|
|
692
688
|
|
|
693
689
|
@observe(name="agent.step", ignore_inputs=["state", "on_event"])
|
|
@@ -1594,7 +1590,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
1594
1590
|
condenser_config = self.condenser.model_dump(
|
|
1595
1591
|
exclude={"llm"}, exclude_none=True
|
|
1596
1592
|
)
|
|
1597
|
-
condenser_llm_obj =
|
|
1593
|
+
condenser_llm_obj = self.condenser.get_llm()
|
|
1598
1594
|
condenser_llm = (
|
|
1599
1595
|
condenser_llm_obj.model if condenser_llm_obj is not None else "N/A"
|
|
1600
1596
|
)
|
|
@@ -33,7 +33,6 @@ from openhands.sdk.mcp.client import MCPClient
|
|
|
33
33
|
from openhands.sdk.mcp.config import MCPServer
|
|
34
34
|
from openhands.sdk.mcp.tool import MCPToolDefinition, MCPToolExecutor
|
|
35
35
|
from openhands.sdk.tool import (
|
|
36
|
-
BROWSER_TOOL_NAME,
|
|
37
36
|
BUILT_IN_TOOL_CLASSES,
|
|
38
37
|
BUILT_IN_TOOLS,
|
|
39
38
|
Tool,
|
|
@@ -45,7 +44,6 @@ from openhands.sdk.tool.builtins.vision_inspect import (
|
|
|
45
44
|
VisionInspectTool,
|
|
46
45
|
has_vision_profile_available,
|
|
47
46
|
)
|
|
48
|
-
from openhands.sdk.utils.deprecation import warn_deprecated
|
|
49
47
|
from openhands.sdk.utils.models import DiscriminatedUnionMixin
|
|
50
48
|
from openhands.sdk.utils.path import get_user_persistence_dir
|
|
51
49
|
|
|
@@ -268,21 +266,11 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
268
266
|
|
|
269
267
|
@field_validator("system_prompt_kwargs")
|
|
270
268
|
@classmethod
|
|
271
|
-
def
|
|
269
|
+
def _drop_retired_enable_browser(
|
|
272
270
|
cls, value: dict[str, object]
|
|
273
271
|
) -> dict[str, object]:
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
"system_prompt_kwargs['enable_browser']",
|
|
277
|
-
deprecated_in="1.51.0",
|
|
278
|
-
removed_in="1.56.0",
|
|
279
|
-
details=(
|
|
280
|
-
"Browser guidance now comes from the loaded browser tools; "
|
|
281
|
-
"only custom Jinja templates still read this kwarg."
|
|
282
|
-
),
|
|
283
|
-
stacklevel=5,
|
|
284
|
-
)
|
|
285
|
-
return value
|
|
272
|
+
"""Old saved conversations may still carry this retired prompt flag."""
|
|
273
|
+
return {key: item for key, item in value.items() if key != "enable_browser"}
|
|
286
274
|
|
|
287
275
|
condenser: CondenserBase | None = Field(
|
|
288
276
|
default=None,
|
|
@@ -378,15 +366,10 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
378
366
|
# own Jinja template; everything else (incl. custom policies) uses the registry.
|
|
379
367
|
preset = self._prompt_preset
|
|
380
368
|
if preset is None:
|
|
381
|
-
# Deprecated kwarg, kept until 1.56.0 for templates that branch on it.
|
|
382
|
-
template_kwargs = {
|
|
383
|
-
"enable_browser": any(t.name == BROWSER_TOOL_NAME for t in self.tools),
|
|
384
|
-
**self._resolved_template_kwargs(),
|
|
385
|
-
}
|
|
386
369
|
return render_template(
|
|
387
370
|
prompt_dir=self.prompt_dir,
|
|
388
371
|
template_name=self.system_prompt_filename,
|
|
389
|
-
**
|
|
372
|
+
**self._resolved_template_kwargs(),
|
|
390
373
|
)
|
|
391
374
|
|
|
392
375
|
return create_registry(preset).build(self._build_prompt_context()).static
|
|
@@ -421,9 +404,7 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
421
404
|
"model_family" not in template_kwargs
|
|
422
405
|
or "model_variant" not in template_kwargs
|
|
423
406
|
):
|
|
424
|
-
spec = get_model_prompt_spec(
|
|
425
|
-
self.llm.model, getattr(self.llm, "model_canonical_name", None)
|
|
426
|
-
)
|
|
407
|
+
spec = get_model_prompt_spec(self.llm.model, self.llm.model_canonical_name)
|
|
427
408
|
if "model_family" not in template_kwargs and spec.family:
|
|
428
409
|
template_kwargs["model_family"] = spec.family
|
|
429
410
|
if "model_variant" not in template_kwargs and spec.variant:
|
|
@@ -29,6 +29,10 @@ class CondenserBase(DiscriminatedUnionMixin, ABC):
|
|
|
29
29
|
step the condenser will use that condensation event to produce a new `View`.
|
|
30
30
|
"""
|
|
31
31
|
|
|
32
|
+
def get_llm(self) -> LLM | None:
|
|
33
|
+
"""The LLM used by the condenser, if any."""
|
|
34
|
+
return None
|
|
35
|
+
|
|
32
36
|
@abstractmethod
|
|
33
37
|
def condense(self, view: View, agent_llm: LLM | None = None) -> View | Condensation:
|
|
34
38
|
"""Condense a sequence of events into a potentially smaller list.
|
|
@@ -112,6 +112,10 @@ class LLMSummarizingCondenser(RollingCondenser):
|
|
|
112
112
|
def handles_condensation_requests(self) -> bool:
|
|
113
113
|
return True
|
|
114
114
|
|
|
115
|
+
def get_llm(self) -> LLM | None:
|
|
116
|
+
"""The LLM used by the condenser, if any."""
|
|
117
|
+
return self.llm
|
|
118
|
+
|
|
115
119
|
def _effective_max_tokens(self, agent_llm: LLM | None) -> int | None:
|
|
116
120
|
"""Return the effective token cap that triggers token-based condensation.
|
|
117
121
|
|
|
@@ -2,7 +2,7 @@ import contextlib
|
|
|
2
2
|
from abc import ABC, abstractmethod
|
|
3
3
|
from collections.abc import AsyncIterator, Iterable, Mapping
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import TYPE_CHECKING, Protocol, TypeVar, cast
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Protocol, TypeVar, cast
|
|
6
6
|
|
|
7
7
|
from openhands.sdk.conversation.conversation_stats import ConversationStats
|
|
8
8
|
from openhands.sdk.conversation.events_list_base import EventsListBase
|
|
@@ -45,6 +45,7 @@ def _conversation_tag_attributes(
|
|
|
45
45
|
|
|
46
46
|
if TYPE_CHECKING:
|
|
47
47
|
from openhands.sdk.agent.base import AgentBase
|
|
48
|
+
from openhands.sdk.conversation.secret_registry import SecretRegistry
|
|
48
49
|
from openhands.sdk.conversation.state import ConversationExecutionStatus
|
|
49
50
|
from openhands.sdk.hooks import HookConfig
|
|
50
51
|
|
|
@@ -122,6 +123,11 @@ class ConversationStateProtocol(Protocol):
|
|
|
122
123
|
"""The hook configuration for this conversation."""
|
|
123
124
|
...
|
|
124
125
|
|
|
126
|
+
@property
|
|
127
|
+
def secret_registry(self) -> "SecretRegistry":
|
|
128
|
+
"""The secret registry for this conversation."""
|
|
129
|
+
...
|
|
130
|
+
|
|
125
131
|
|
|
126
132
|
class BaseConversation(ABC):
|
|
127
133
|
"""Abstract base class for conversation implementations.
|
|
@@ -131,9 +137,16 @@ class BaseConversation(ABC):
|
|
|
131
137
|
exchange, execution control, and state management.
|
|
132
138
|
"""
|
|
133
139
|
|
|
140
|
+
_cleanup_initiated: bool = False
|
|
141
|
+
_cleanup_complete: bool = False
|
|
142
|
+
_hook_processor: Any = None
|
|
143
|
+
|
|
134
144
|
def __init__(self) -> None:
|
|
135
145
|
"""Initialize the base conversation with span tracking."""
|
|
136
146
|
self._span_ended = False
|
|
147
|
+
self._cleanup_initiated = False
|
|
148
|
+
self._cleanup_complete = False
|
|
149
|
+
self._hook_processor = None
|
|
137
150
|
# Owned root span. The ``observe`` decorator looks up this attribute
|
|
138
151
|
# (by name ``_observability_root_span``) on ``self`` at every entry
|
|
139
152
|
# point and re-attaches it via ``Laminar.use_span`` so that nested
|
|
@@ -142,6 +155,21 @@ class BaseConversation(ABC):
|
|
|
142
155
|
# that constructed the conversation.
|
|
143
156
|
self._observability_root_span: RootSpan | None = None
|
|
144
157
|
|
|
158
|
+
@property
|
|
159
|
+
def cleanup_initiated(self) -> bool:
|
|
160
|
+
"""Whether conversation cleanup has started."""
|
|
161
|
+
return self._cleanup_initiated
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def cleanup_complete(self) -> bool:
|
|
165
|
+
"""Whether conversation cleanup has finished."""
|
|
166
|
+
return self._cleanup_complete
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def hook_processor(self) -> Any:
|
|
170
|
+
"""The active hook processor for the conversation, if configured."""
|
|
171
|
+
return self._hook_processor
|
|
172
|
+
|
|
145
173
|
def _start_observability_span(
|
|
146
174
|
self,
|
|
147
175
|
session_id: str,
|
{openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/impl/local_conversation.py
RENAMED
|
@@ -177,6 +177,16 @@ def _copy_event_for_fork(event: Event) -> Event:
|
|
|
177
177
|
return Event.model_validate_json(event.model_dump_json(exclude_none=True))
|
|
178
178
|
|
|
179
179
|
|
|
180
|
+
def _same_llm_config(a: LLM, b: LLM) -> bool:
|
|
181
|
+
"""Return whether two LLMs serialize to the same config, secrets included."""
|
|
182
|
+
if a is b:
|
|
183
|
+
return True
|
|
184
|
+
context = {"expose_secrets": True}
|
|
185
|
+
return a.model_dump(mode="json", context=context) == b.model_dump(
|
|
186
|
+
mode="json", context=context
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
180
190
|
class LocalConversation(BaseConversation):
|
|
181
191
|
agent: AgentBase
|
|
182
192
|
workspace: LocalWorkspace
|
|
@@ -528,8 +538,8 @@ class LocalConversation(BaseConversation):
|
|
|
528
538
|
# Idempotent with the create_request() lift; lower priority than
|
|
529
539
|
# request.secrets (below). On resume, fill-if-absent so a persisted
|
|
530
540
|
# value is never downgraded (or lost to redacted/no-cipher serialization).
|
|
531
|
-
if
|
|
532
|
-
ctx_secrets =
|
|
541
|
+
if self.agent is not None and self.agent.agent_context is not None:
|
|
542
|
+
ctx_secrets = self.agent.agent_context.secrets
|
|
533
543
|
if ctx_secrets:
|
|
534
544
|
existing_sources = self._state.secret_registry.secret_sources
|
|
535
545
|
fill_secrets: dict[str, SecretValue] = {}
|
|
@@ -1664,18 +1674,31 @@ class LocalConversation(BaseConversation):
|
|
|
1664
1674
|
"""Swap the agent's LLM to the given object.
|
|
1665
1675
|
|
|
1666
1676
|
The caller owns ``llm.usage_id``; it is the registry key. If an
|
|
1667
|
-
entry with that key already exists
|
|
1668
|
-
|
|
1669
|
-
|
|
1677
|
+
entry with that key already exists and has the same config, the
|
|
1678
|
+
cached LLM is reused. If its config differs (e.g. ``default``, which
|
|
1679
|
+
agent initialization registers, posted with another model), the
|
|
1680
|
+
passed ``llm`` replaces the entry and takes over the metrics already
|
|
1681
|
+
recorded under that ``usage_id``.
|
|
1670
1682
|
|
|
1671
1683
|
Args:
|
|
1672
1684
|
llm: LLM to install on the agent.
|
|
1673
1685
|
"""
|
|
1686
|
+
usage_id = llm.usage_id
|
|
1674
1687
|
try:
|
|
1675
|
-
|
|
1688
|
+
registered = self.llm_registry.get(usage_id)
|
|
1676
1689
|
except KeyError:
|
|
1690
|
+
registered = None
|
|
1691
|
+
if registered is not None and _same_llm_config(registered, llm):
|
|
1692
|
+
new_llm = registered
|
|
1693
|
+
else:
|
|
1677
1694
|
new_llm = create_subscription_llm_from_config(llm)
|
|
1695
|
+
if registered is not None:
|
|
1696
|
+
self.llm_registry.remove(usage_id)
|
|
1678
1697
|
self.llm_registry.add(new_llm)
|
|
1698
|
+
if registered is not None:
|
|
1699
|
+
# Keep one metrics object per usage_id: ConversationStats
|
|
1700
|
+
# tracks the registered LLM's, so accrued usage is retained.
|
|
1701
|
+
new_llm.restore_metrics(registered.metrics)
|
|
1679
1702
|
# A switch_llm tool runs on a worker thread while run()/arun() holds the
|
|
1680
1703
|
# state lock across the agent step on another thread, blocked awaiting
|
|
1681
1704
|
# this very tool. Re-acquiring _state here would deadlock, so skip it:
|
|
@@ -1760,7 +1783,8 @@ class LocalConversation(BaseConversation):
|
|
|
1760
1783
|
|
|
1761
1784
|
Raises:
|
|
1762
1785
|
ValueError: If the conversation's agent is not an :class:`ACPAgent`,
|
|
1763
|
-
|
|
1786
|
+
if ``model`` is empty or whitespace-only (live or not), or
|
|
1787
|
+
(for a live switch) the provider does not support runtime
|
|
1764
1788
|
model switching, or the ACP server rejects the switch.
|
|
1765
1789
|
TimeoutError: If a live switch's ``session/set_model`` round-trip
|
|
1766
1790
|
exceeds ``acp_prompt_timeout`` seconds.
|
|
@@ -1769,6 +1793,10 @@ class LocalConversation(BaseConversation):
|
|
|
1769
1793
|
raise ValueError(
|
|
1770
1794
|
"switch_acp_model is only supported for ACP conversations."
|
|
1771
1795
|
)
|
|
1796
|
+
# Check here, not only in set_acp_model: the pre-run path below skips
|
|
1797
|
+
# that call and would persist a blank model unchecked.
|
|
1798
|
+
if not model or not model.strip():
|
|
1799
|
+
raise ValueError("model must be a non-empty string")
|
|
1772
1800
|
with self._state:
|
|
1773
1801
|
# With a live session, perform the protocol switch first; if it
|
|
1774
1802
|
# fails we leave the persisted state untouched. Without one (pre
|
|
@@ -2818,9 +2846,9 @@ class LocalConversation(BaseConversation):
|
|
|
2818
2846
|
|
|
2819
2847
|
def close(self) -> None:
|
|
2820
2848
|
"""Close the conversation and clean up all tool executors."""
|
|
2821
|
-
if
|
|
2849
|
+
if self._cleanup_complete:
|
|
2822
2850
|
return
|
|
2823
|
-
first_attempt = not
|
|
2851
|
+
first_attempt = not self._cleanup_initiated
|
|
2824
2852
|
if first_attempt:
|
|
2825
2853
|
self._cleanup_initiated = True
|
|
2826
2854
|
|
|
@@ -2835,7 +2863,7 @@ class LocalConversation(BaseConversation):
|
|
|
2835
2863
|
logger.debug(f"Could not register accumulated cost: {e}")
|
|
2836
2864
|
|
|
2837
2865
|
logger.debug("Closing conversation and cleaning up tool executors")
|
|
2838
|
-
hook_processor =
|
|
2866
|
+
hook_processor = self._hook_processor
|
|
2839
2867
|
if hook_processor is not None:
|
|
2840
2868
|
hook_processor.run_session_end()
|
|
2841
2869
|
try:
|
{openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/impl/remote_conversation.py
RENAMED
|
@@ -13,6 +13,7 @@ from urllib.parse import urlparse
|
|
|
13
13
|
|
|
14
14
|
import httpx
|
|
15
15
|
import websockets
|
|
16
|
+
from litellm.types.utils import Delta, ModelResponseStream, StreamingChoices
|
|
16
17
|
from websockets.exceptions import ConnectionClosed
|
|
17
18
|
|
|
18
19
|
from openhands.sdk.agent.base import AgentBase
|
|
@@ -28,12 +29,16 @@ from openhands.sdk.conversation.exceptions import (
|
|
|
28
29
|
ConversationRunError,
|
|
29
30
|
WebSocketConnectionError,
|
|
30
31
|
)
|
|
31
|
-
from openhands.sdk.conversation.secret_registry import
|
|
32
|
+
from openhands.sdk.conversation.secret_registry import (
|
|
33
|
+
SecretRegistry,
|
|
34
|
+
SecretValue,
|
|
35
|
+
)
|
|
32
36
|
from openhands.sdk.conversation.state import ConversationExecutionStatus
|
|
33
37
|
from openhands.sdk.conversation.title_utils import generate_conversation_title
|
|
34
38
|
from openhands.sdk.conversation.types import (
|
|
35
39
|
ConversationCallbackType,
|
|
36
40
|
ConversationID,
|
|
41
|
+
ConversationTokenCallbackType,
|
|
37
42
|
StuckDetectionThresholds,
|
|
38
43
|
TraceMetadataValue,
|
|
39
44
|
)
|
|
@@ -49,6 +54,7 @@ from openhands.sdk.event.conversation_state import (
|
|
|
49
54
|
ConversationStateUpdateEvent,
|
|
50
55
|
)
|
|
51
56
|
from openhands.sdk.event.llm_completion_log import LLMCompletionLogEvent
|
|
57
|
+
from openhands.sdk.event.streaming_delta import StreamingDeltaEvent
|
|
52
58
|
from openhands.sdk.event.types import EventID
|
|
53
59
|
from openhands.sdk.hooks import HookConfig
|
|
54
60
|
from openhands.sdk.llm import LLM, Message, TextContent
|
|
@@ -92,6 +98,26 @@ def _validate_remote_agent(agent_data: dict) -> AgentBase:
|
|
|
92
98
|
return AgentBase.model_validate(agent_data)
|
|
93
99
|
|
|
94
100
|
|
|
101
|
+
def _token_delta_callback(
|
|
102
|
+
token_callbacks: list[ConversationTokenCallbackType],
|
|
103
|
+
) -> ConversationCallbackType:
|
|
104
|
+
"""Hand each streamed delta from the events socket to the token callbacks."""
|
|
105
|
+
on_token = BaseConversation.compose_callbacks(token_callbacks)
|
|
106
|
+
|
|
107
|
+
def callback(event: Event) -> None:
|
|
108
|
+
if isinstance(event, StreamingDeltaEvent):
|
|
109
|
+
delta = Delta(
|
|
110
|
+
content=event.content, reasoning_content=event.reasoning_content
|
|
111
|
+
)
|
|
112
|
+
on_token(
|
|
113
|
+
ModelResponseStream(
|
|
114
|
+
id=event.id, choices=[StreamingChoices(index=0, delta=delta)]
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
return callback
|
|
119
|
+
|
|
120
|
+
|
|
95
121
|
def _restore_tool_registrations(tool_module_qualnames: Mapping[str, str]) -> None:
|
|
96
122
|
"""Import the tool modules persisted with a remote conversation."""
|
|
97
123
|
for tool_name, module_qualname in tool_module_qualnames.items():
|
|
@@ -493,9 +519,16 @@ class RemoteEventsList(EventsListBase):
|
|
|
493
519
|
return len(self._cached_events) - 1
|
|
494
520
|
|
|
495
521
|
def create_default_callback(self) -> ConversationCallbackType:
|
|
496
|
-
"""Create a default callback that adds events to this list.
|
|
522
|
+
"""Create a default callback that adds events to this list.
|
|
523
|
+
|
|
524
|
+
Streamed ``StreamingDeltaEvent`` frames are transient: the server never
|
|
525
|
+
stores them, so they reach the other callbacks but stay out of this
|
|
526
|
+
list, which keeps matching the server's event log.
|
|
527
|
+
"""
|
|
497
528
|
|
|
498
529
|
def callback(event: Event) -> None:
|
|
530
|
+
if isinstance(event, StreamingDeltaEvent):
|
|
531
|
+
return
|
|
499
532
|
self.add_event(event)
|
|
500
533
|
|
|
501
534
|
return callback
|
|
@@ -689,6 +722,15 @@ class RemoteState(ConversationStateProtocol):
|
|
|
689
722
|
return HookConfig.model_validate(hook_config_data)
|
|
690
723
|
return None
|
|
691
724
|
|
|
725
|
+
@property
|
|
726
|
+
def secret_registry(self) -> SecretRegistry:
|
|
727
|
+
"""The secret registry for this conversation."""
|
|
728
|
+
info = self._get_conversation_info()
|
|
729
|
+
secret_registry_data = info.get("secret_registry")
|
|
730
|
+
if secret_registry_data is not None:
|
|
731
|
+
return SecretRegistry.model_validate(secret_registry_data)
|
|
732
|
+
return SecretRegistry()
|
|
733
|
+
|
|
692
734
|
def model_dump(self, **_kwargs):
|
|
693
735
|
"""Get a dictionary representation of the remote state."""
|
|
694
736
|
info = self._get_conversation_info()
|
|
@@ -746,6 +788,7 @@ class RemoteConversation(BaseConversation):
|
|
|
746
788
|
observability_tags: list[str] | None = None,
|
|
747
789
|
observability_span_name: str = "conversation",
|
|
748
790
|
observability_parent_span_context: str | None = None,
|
|
791
|
+
token_callbacks: list[ConversationTokenCallbackType] | None = None,
|
|
749
792
|
**_: object,
|
|
750
793
|
) -> None:
|
|
751
794
|
"""Remote conversation proxy that talks to an agent server.
|
|
@@ -783,7 +826,10 @@ class RemoteConversation(BaseConversation):
|
|
|
783
826
|
observability_tags: Optional root span tags for observability backends.
|
|
784
827
|
observability_span_name: Optional child span name for observability
|
|
785
828
|
backends. The root span remains named "conversation".
|
|
829
|
+
token_callbacks: Optional callbacks invoked with each streamed token
|
|
830
|
+
delta the server sends (agent LLM with ``stream=True``).
|
|
786
831
|
"""
|
|
832
|
+
super().__init__()
|
|
787
833
|
observability_metadata = merge_observability_metadata(observability_metadata)
|
|
788
834
|
observability_parent_span_context = (
|
|
789
835
|
observability_parent_span_context
|
|
@@ -920,6 +966,7 @@ class RemoteConversation(BaseConversation):
|
|
|
920
966
|
workspace=workspace,
|
|
921
967
|
conversation_id=conversation_id,
|
|
922
968
|
callbacks=callbacks,
|
|
969
|
+
token_callbacks=token_callbacks,
|
|
923
970
|
max_iteration_per_run=max_iteration_per_run,
|
|
924
971
|
client_tools=[*(client_tools or []), *attached_client_tools],
|
|
925
972
|
visualizer=visualizer,
|
|
@@ -1060,10 +1107,13 @@ class RemoteConversation(BaseConversation):
|
|
|
1060
1107
|
visualizer: (
|
|
1061
1108
|
type[ConversationVisualizerBase] | ConversationVisualizerBase | None
|
|
1062
1109
|
),
|
|
1110
|
+
token_callbacks: list[ConversationTokenCallbackType] | None = None,
|
|
1063
1111
|
) -> None:
|
|
1064
1112
|
super().__init__() # Initialize base class with span tracking
|
|
1065
1113
|
self.agent = agent
|
|
1066
1114
|
self._callbacks = callbacks or []
|
|
1115
|
+
if token_callbacks:
|
|
1116
|
+
self._callbacks.append(_token_delta_callback(token_callbacks))
|
|
1067
1117
|
self.max_iteration_per_run = max_iteration_per_run
|
|
1068
1118
|
self.workspace = workspace
|
|
1069
1119
|
self._client = workspace.client
|
|
@@ -1166,13 +1216,40 @@ class RemoteConversation(BaseConversation):
|
|
|
1166
1216
|
all_callbacks = self._callbacks + [run_complete_callback]
|
|
1167
1217
|
composed_callback = BaseConversation.compose_callbacks(all_callbacks)
|
|
1168
1218
|
|
|
1219
|
+
# Track which events the callbacks have seen, so that events the
|
|
1220
|
+
# server recorded while the socket was reconnecting (and that only
|
|
1221
|
+
# reached the cache through REST reconciliation) can be replayed to
|
|
1222
|
+
# them once, without the live socket delivering them a second time.
|
|
1223
|
+
delivered_ids: set[str] = set()
|
|
1224
|
+
replayed_ids: set[str] = set()
|
|
1225
|
+
|
|
1226
|
+
def socket_callback(event: Event) -> None:
|
|
1227
|
+
if event.id in replayed_ids:
|
|
1228
|
+
replayed_ids.discard(event.id)
|
|
1229
|
+
return
|
|
1230
|
+
delivered_ids.add(event.id)
|
|
1231
|
+
composed_callback(event)
|
|
1232
|
+
|
|
1233
|
+
def replay_missed_events() -> None:
|
|
1234
|
+
self._state.events.reconcile()
|
|
1235
|
+
replayed_ids.clear()
|
|
1236
|
+
for event in self._state.events[:]:
|
|
1237
|
+
if event.id in delivered_ids:
|
|
1238
|
+
continue
|
|
1239
|
+
delivered_ids.add(event.id)
|
|
1240
|
+
replayed_ids.add(event.id)
|
|
1241
|
+
try:
|
|
1242
|
+
composed_callback(event)
|
|
1243
|
+
except Exception:
|
|
1244
|
+
logger.exception("ws_replay_callback_error", stack_info=True)
|
|
1245
|
+
|
|
1169
1246
|
# Initialize WebSocket client for callbacks
|
|
1170
1247
|
self._ws_client = WebSocketCallbackClient(
|
|
1171
1248
|
host=self.workspace.host,
|
|
1172
1249
|
conversation_id=str(self._id),
|
|
1173
|
-
callback=
|
|
1250
|
+
callback=socket_callback,
|
|
1174
1251
|
api_key=self.workspace.api_key,
|
|
1175
|
-
on_reconnect=
|
|
1252
|
+
on_reconnect=replay_missed_events,
|
|
1176
1253
|
)
|
|
1177
1254
|
self._ws_client.start()
|
|
1178
1255
|
|
|
@@ -1210,6 +1287,9 @@ class RemoteConversation(BaseConversation):
|
|
|
1210
1287
|
# were emitted between the initial REST sync and WebSocket subscription.
|
|
1211
1288
|
# This is the "reconciliation" part of the subscription handshake.
|
|
1212
1289
|
self._state.events.reconcile()
|
|
1290
|
+
# Callbacks are not replayed history: only events recorded after this
|
|
1291
|
+
# point and missed by the socket are replayed on reconnect.
|
|
1292
|
+
delivered_ids.update(event.id for event in self._state.events[:])
|
|
1213
1293
|
|
|
1214
1294
|
def _create_llm_completion_log_callback(self) -> ConversationCallbackType:
|
|
1215
1295
|
"""Create a callback that writes LLM completion logs to client filesystem."""
|
|
@@ -1850,52 +1930,55 @@ class RemoteConversation(BaseConversation):
|
|
|
1850
1930
|
The workspace owns the client and will close it during its own cleanup.
|
|
1851
1931
|
Closing it here would prevent the workspace from making cleanup API calls.
|
|
1852
1932
|
"""
|
|
1853
|
-
if self.
|
|
1933
|
+
if self._cleanup_complete:
|
|
1854
1934
|
return
|
|
1855
|
-
self._cleanup_initiated
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
cost
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
try:
|
|
1879
|
-
# Stop WebSocket client if it exists
|
|
1880
|
-
if self._ws_client:
|
|
1881
|
-
self._ws_client.stop()
|
|
1882
|
-
self._ws_client = None
|
|
1883
|
-
except Exception:
|
|
1884
|
-
pass
|
|
1935
|
+
first_attempt = not self._cleanup_initiated
|
|
1936
|
+
if first_attempt:
|
|
1937
|
+
self._cleanup_initiated = True
|
|
1938
|
+
|
|
1939
|
+
# Best-effort: hand the accumulated LLM cost to the workspace so it can
|
|
1940
|
+
# be included in the automation completion callback. Only read cached
|
|
1941
|
+
# state — close() also runs on the failure path, where a live fetch
|
|
1942
|
+
# could block until timeout against an agent server that is already gone.
|
|
1943
|
+
# The cache tracks this run: the agent server streams a "stats" update
|
|
1944
|
+
# after every LLM response (EventService._setup_stats_streaming), so it
|
|
1945
|
+
# holds the run's spend even on the failure path, which wakes run() from
|
|
1946
|
+
# a per-field ERROR/STUCK update before the post-run full-state snapshot.
|
|
1947
|
+
try:
|
|
1948
|
+
cached = self._state._cached_state
|
|
1949
|
+
# Require an actual "stats" entry: a cache built only from partial
|
|
1950
|
+
# field updates — e.g. the subscribe-time push for a service with no
|
|
1951
|
+
# live conversation — would otherwise yield 0.0 and record a run as
|
|
1952
|
+
# free when its cost is really just unknown.
|
|
1953
|
+
if cached is not None and "stats" in cached:
|
|
1954
|
+
cost = self._state.stats.get_combined_metrics().accumulated_cost
|
|
1955
|
+
self.workspace.register_cost(cost)
|
|
1956
|
+
except Exception as e:
|
|
1957
|
+
logger.debug(f"Could not register accumulated cost: {e}")
|
|
1885
1958
|
|
|
1886
|
-
|
|
1887
|
-
if self.delete_on_close:
|
|
1959
|
+
# SessionEnd hooks are executed server-side (via hook_config in payload).
|
|
1888
1960
|
try:
|
|
1889
|
-
#
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
self.
|
|
1893
|
-
"DELETE",
|
|
1894
|
-
f"{CONVERSATIONS_PATH}/{self.id}",
|
|
1895
|
-
)
|
|
1961
|
+
# Stop WebSocket client if it exists
|
|
1962
|
+
if self._ws_client:
|
|
1963
|
+
self._ws_client.stop()
|
|
1964
|
+
self._ws_client = None
|
|
1896
1965
|
except Exception:
|
|
1897
1966
|
pass
|
|
1898
1967
|
|
|
1968
|
+
self._end_observability_span()
|
|
1969
|
+
if self.delete_on_close:
|
|
1970
|
+
try:
|
|
1971
|
+
# trigger server-side delete_conversation to release resources
|
|
1972
|
+
# like tmux sessions
|
|
1973
|
+
_send_request(
|
|
1974
|
+
self._client,
|
|
1975
|
+
"DELETE",
|
|
1976
|
+
f"{CONVERSATIONS_PATH}/{self.id}",
|
|
1977
|
+
)
|
|
1978
|
+
except Exception:
|
|
1979
|
+
pass
|
|
1980
|
+
self._cleanup_complete = True
|
|
1981
|
+
|
|
1899
1982
|
def __del__(self) -> None:
|
|
1900
1983
|
try:
|
|
1901
1984
|
self.close()
|