openhands-sdk 1.56.0__tar.gz → 1.58.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.56.0 → openhands_sdk-1.58.0}/PKG-INFO +1 -1
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/agent/agent.py +7 -11
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/agent/base.py +1 -3
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/condenser/base.py +4 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/condenser/llm_summarizing_condenser.py +4 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/base.py +29 -1
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/goal/controller.py +6 -4
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/impl/local_conversation.py +19 -6
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/impl/remote_conversation.py +57 -41
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/request.py +99 -1
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/state.py +8 -8
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/title_utils.py +29 -12
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/llm_convertible/action.py +30 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/resume_transcript.py +1 -1
- openhands_sdk-1.58.0/openhands/sdk/git/cached_repo.py +1181 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/git/git_diff.py +43 -5
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/git/utils.py +2 -2
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/hooks/conversation_hooks.py +43 -1
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/meta_profile_store.py +6 -1
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/verified_models.py +3 -1
- openhands_sdk-1.58.0/openhands/sdk/logger/_rich_handler.py +62 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/logger/logger.py +3 -2
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/marketplace/registry.py +24 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/marketplace/types.py +8 -3
- openhands_sdk-1.58.0/openhands/sdk/mcp/oauth.py +203 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/mcp/utils.py +7 -2
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/profiles/agent_profile_store.py +16 -1
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/settings/metadata.py +2 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/settings/model.py +140 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/skills/skill.py +6 -8
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/skills/utils.py +10 -11
- openhands_sdk-1.58.0/openhands/sdk/utils/cipher.py +131 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/command.py +11 -10
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/models.py +14 -1
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/pydantic_secrets.py +49 -5
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/workspace/remote/base.py +2 -2
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/workspace/repo.py +52 -11
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands_sdk.egg-info/PKG-INFO +1 -1
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands_sdk.egg-info/SOURCES.txt +2 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/pyproject.toml +1 -1
- openhands_sdk-1.56.0/openhands/sdk/git/cached_repo.py +0 -517
- openhands_sdk-1.56.0/openhands/sdk/mcp/oauth.py +0 -85
- openhands_sdk-1.56.0/openhands/sdk/utils/cipher.py +0 -81
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/agent/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/agent/acp_agent.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/agent/acp_contracts.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/agent/acp_file_credentials.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/agent/acp_model_discovery.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/agent/acp_models.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/agent/acp_tracing.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/agent/critic_mixin.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/agent/parallel_executor.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/agent/response_dispatch.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/agent/stream_context.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/agent/utils.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/automation/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/banner.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/agent_context.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/condenser/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/condenser/no_op_condenser.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/condenser/pipeline_condenser.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/condenser/prompts/summarizing_events.j2 +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/condenser/prompts/summarizing_system.j2 +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/condenser/utils.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/memory.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/prompts/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/prompts/presets.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/prompts/prompt.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/prompts/registry.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/prompts/section.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/prompts/sections/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/prompts/sections/dynamic.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/prompts/sections/planning.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/prompts/sections/static.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/prompts/templates/ask_agent_template.j2 +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/view/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/view/manipulation_indices.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/view/properties/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/view/properties/base.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/view/properties/batch_atomicity.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/view/properties/observation_uniqueness.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/view/properties/tool_call_matching.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/view/properties/tool_loop_atomicity.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/context/view/view.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/cancellation.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/conversation.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/conversation_stats.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/event_store.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/events_list_base.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/exceptions.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/fifo_lock.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/goal/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/goal/judge.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/goal/prompts.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/goal/runner.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/impl/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/message_request.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/persistence_const.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/resource_lock_manager.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/response_utils.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/secret_registry.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/stuck_detector.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/types.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/visualizer/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/visualizer/base.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/visualizer/default.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/credential.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/critic/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/critic/base.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/critic/impl/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/critic/impl/agent_finished.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/critic/impl/api/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/critic/impl/api/chat_template.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/critic/impl/api/client.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/critic/impl/api/critic.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/critic/impl/api/taxonomy.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/critic/impl/empty_patch.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/critic/impl/pass_critic.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/critic/result.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/acp_tool_call.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/base.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/condenser.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/conversation_error.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/conversation_state.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/error_classification.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/hook_execution.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/llm_completion_log.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/llm_convertible/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/llm_convertible/message.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/llm_convertible/observation.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/llm_convertible/reasoning_utils.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/llm_convertible/system.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/security.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/streaming_delta.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/token.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/types.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/event/user_action.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/extensions/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/extensions/fetch.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/extensions/installation/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/extensions/installation/info.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/extensions/installation/interface.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/extensions/installation/manager.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/extensions/installation/metadata.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/extensions/installation/utils.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/git/exceptions.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/git/git_changes.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/git/git_commits.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/git/models.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/hooks/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/hooks/config.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/hooks/executor.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/hooks/manager.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/hooks/types.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/io/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/io/base.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/io/cache.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/io/local.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/io/memory.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/launch/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/launch/errors.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/launch/finalize.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/launch/preview.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/launch/resolve.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/_message_normalization.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/_response_stream.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/_tokenizer.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/auth/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/auth/credentials.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/auth/openai.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/call_context.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/cleanup_profile.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/exceptions/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/exceptions/classifier.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/exceptions/mapping.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/exceptions/types.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/fallback_strategy.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/llm.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/llm_profile_store.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/llm_registry.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/llm_response.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/message.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/mixins/fn_call_converter.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/mixins/fn_call_examples.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/mixins/non_native_fc.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/options/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/options/chat_options.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/options/common.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/options/responses_options.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/provider_connection_store.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/router/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/router/base.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/router/impl/multimodal.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/router/impl/random.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/streaming.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/image_inline.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/image_resize.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/litellm_provider.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/metrics.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/model_features.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/model_info.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/model_prompt_spec.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/openhands_provider.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/providers/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/providers/openrouter.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/responses_serialization.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/retry_mixin.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/runtime_metadata.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/telemetry.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/unverified_models.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/llm/utils/vertex_preflight.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/logger/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/logger/rolling.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/marketplace/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/marketplace/registration.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/mcp/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/mcp/client.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/mcp/config.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/mcp/definition.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/mcp/exceptions.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/mcp/tool.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/observability/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/observability/laminar.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/observability/utils.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/discovery.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/fetch.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/format/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/format/agent_plugins.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/format/agent_plugins_mcp.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/format/base.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/format/claude_code.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/format/schemas/mcp-1.0.0.schema.json +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/format/schemas/plugin-1.0.0.schema.json +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/installed.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/loader.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/paths.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/plugin.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/source.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/strict_fetch.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/plugin/types.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/profiles/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/profiles/agent_profile.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/profiles/profile_refs.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/profiles/resolver.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/profiles/seed.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/py.typed +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/secret/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/secret/secrets.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/_shell_ast.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/analyzer.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/confirmation_policy.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/defense_in_depth/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/defense_in_depth/pattern.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/defense_in_depth/policy_rails.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/defense_in_depth/shell_semantics.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/defense_in_depth/utils.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/ensemble.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/grayswan/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/grayswan/analyzer.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/grayswan/utils.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/llm_analyzer.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/risk.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/shell_parser.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/toolshield_helpers.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/security/toolshield_llm_analyzer.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/settings/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/settings/acp_install_catalog.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/settings/acp_providers.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/settings/api_models.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/skills/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/skills/exceptions.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/skills/execute.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/skills/fetch.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/skills/installed.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/skills/trigger.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/skills/types.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/subagent/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/subagent/load.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/subagent/registry.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/subagent/schema.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/subagent/scope.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/testing/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/testing/test_llm.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/tool/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/tool/builtins/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/tool/builtins/classify_and_switch_llm.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/tool/builtins/finish.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/tool/builtins/invoke_skill.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/tool/builtins/switch_llm.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/tool/builtins/think.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/tool/builtins/vision_inspect.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/tool/client_tool.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/tool/defaults.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/tool/registry.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/tool/schema.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/tool/spec.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/tool/tool.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/async_executor.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/async_utils.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/datetime.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/deprecation.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/files.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/github.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/json.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/masking.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/paging.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/path.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/redact.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/truncate.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/utils/visualize.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/workspace/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/workspace/base.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/workspace/local.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/workspace/models.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/workspace/remote/__init__.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/workspace/remote/async_remote_workspace.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/workspace/remote/remote_workspace_mixin.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/workspace/workspace.py +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands_sdk.egg-info/dependency_links.txt +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands_sdk.egg-info/requires.txt +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands_sdk.egg-info/top_level.txt +0 -0
- {openhands_sdk-1.56.0 → openhands_sdk-1.58.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.58.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
|
|
@@ -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
|
)
|
|
@@ -404,9 +404,7 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
404
404
|
"model_family" not in template_kwargs
|
|
405
405
|
or "model_variant" not in template_kwargs
|
|
406
406
|
):
|
|
407
|
-
spec = get_model_prompt_spec(
|
|
408
|
-
self.llm.model, getattr(self.llm, "model_canonical_name", None)
|
|
409
|
-
)
|
|
407
|
+
spec = get_model_prompt_spec(self.llm.model, self.llm.model_canonical_name)
|
|
410
408
|
if "model_family" not in template_kwargs and spec.family:
|
|
411
409
|
template_kwargs["model_family"] = spec.family
|
|
412
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,
|
|
@@ -103,12 +103,14 @@ class GoalController:
|
|
|
103
103
|
def on_run_finished(self, events: Sequence[Event]) -> GoalStep:
|
|
104
104
|
"""Judge the objective after a run and decide whether to continue.
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
and returns a :class:`GoalContinue` (with a
|
|
108
|
-
:class:`GoalDone` (with a :class:`GoalOutcome`).
|
|
106
|
+
Audits ``events`` with the judge LLM, counts the round once the judge
|
|
107
|
+
has returned a verdict, and returns a :class:`GoalContinue` (with a
|
|
108
|
+
followup) or a terminal :class:`GoalDone` (with a :class:`GoalOutcome`).
|
|
109
|
+
A judge call that raises leaves the count unchanged: a failed or
|
|
110
|
+
abandoned audit is not a completed round.
|
|
109
111
|
"""
|
|
110
|
-
self.iteration += 1
|
|
111
112
|
verdict = judge_goal(self.judge_llm, self.objective, events)
|
|
113
|
+
self.iteration += 1
|
|
112
114
|
logger.info(
|
|
113
115
|
"Goal audit %d/%d: score=%.2f complete=%s",
|
|
114
116
|
self.iteration,
|
{openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/impl/local_conversation.py
RENAMED
|
@@ -446,8 +446,14 @@ class LocalConversation(BaseConversation):
|
|
|
446
446
|
# Single chokepoint: stamps parent_id (catching any event a hook
|
|
447
447
|
# swapped in downstream of _tree_stamping) and advances HEAD.
|
|
448
448
|
self._state.append_event(e)
|
|
449
|
+
|
|
450
|
+
def _track_last_user_message(e):
|
|
449
451
|
# Track user MessageEvent IDs here so hook callbacks (which may
|
|
450
452
|
# synthesize or alter user messages) are captured in one place.
|
|
453
|
+
# This runs after the caller-supplied callbacks: setting the field
|
|
454
|
+
# can emit a ConversationStateUpdateEvent through on_state_change
|
|
455
|
+
# (the agent server wires it to this chain), and that update must
|
|
456
|
+
# reach subscribers after the message that caused it.
|
|
451
457
|
if isinstance(e, MessageEvent) and e.source == "user":
|
|
452
458
|
# Track the latest real user message ID for hook-blocked checks.
|
|
453
459
|
# Stop-hook feedback is emitted with source="environment".
|
|
@@ -461,7 +467,7 @@ class LocalConversation(BaseConversation):
|
|
|
461
467
|
# list never run. The visualizer is prepended below and so still renders
|
|
462
468
|
# ahead of persist — that is local terminal output, not an announcement
|
|
463
469
|
# a client can act on.
|
|
464
|
-
composed_list = [_default_callback] + callback_list
|
|
470
|
+
composed_list = [_default_callback] + callback_list + [_track_last_user_message]
|
|
465
471
|
# Handle visualization configuration
|
|
466
472
|
if isinstance(visualizer, ConversationVisualizerBase):
|
|
467
473
|
# Use custom visualizer instance
|
|
@@ -538,8 +544,8 @@ class LocalConversation(BaseConversation):
|
|
|
538
544
|
# Idempotent with the create_request() lift; lower priority than
|
|
539
545
|
# request.secrets (below). On resume, fill-if-absent so a persisted
|
|
540
546
|
# value is never downgraded (or lost to redacted/no-cipher serialization).
|
|
541
|
-
if
|
|
542
|
-
ctx_secrets =
|
|
547
|
+
if self.agent is not None and self.agent.agent_context is not None:
|
|
548
|
+
ctx_secrets = self.agent.agent_context.secrets
|
|
543
549
|
if ctx_secrets:
|
|
544
550
|
existing_sources = self._state.secret_registry.secret_sources
|
|
545
551
|
fill_secrets: dict[str, SecretValue] = {}
|
|
@@ -1605,6 +1611,13 @@ class LocalConversation(BaseConversation):
|
|
|
1605
1611
|
self._state,
|
|
1606
1612
|
on_event=self._on_event,
|
|
1607
1613
|
)
|
|
1614
|
+
|
|
1615
|
+
# Deliver any SessionStart additional_context now that
|
|
1616
|
+
# init_state() has emitted the SystemPromptEvent, so the system
|
|
1617
|
+
# prompt stays first in the LLM request. The hook processor
|
|
1618
|
+
# collected it earlier during _ensure_plugins_loaded().
|
|
1619
|
+
if self._hook_processor is not None:
|
|
1620
|
+
self._hook_processor.inject_pending_additional_context()
|
|
1608
1621
|
except Exception:
|
|
1609
1622
|
self._close_runtime_tools(runtime_mcp_tools)
|
|
1610
1623
|
raise
|
|
@@ -2846,9 +2859,9 @@ class LocalConversation(BaseConversation):
|
|
|
2846
2859
|
|
|
2847
2860
|
def close(self) -> None:
|
|
2848
2861
|
"""Close the conversation and clean up all tool executors."""
|
|
2849
|
-
if
|
|
2862
|
+
if self._cleanup_complete:
|
|
2850
2863
|
return
|
|
2851
|
-
first_attempt = not
|
|
2864
|
+
first_attempt = not self._cleanup_initiated
|
|
2852
2865
|
if first_attempt:
|
|
2853
2866
|
self._cleanup_initiated = True
|
|
2854
2867
|
|
|
@@ -2863,7 +2876,7 @@ class LocalConversation(BaseConversation):
|
|
|
2863
2876
|
logger.debug(f"Could not register accumulated cost: {e}")
|
|
2864
2877
|
|
|
2865
2878
|
logger.debug("Closing conversation and cleaning up tool executors")
|
|
2866
|
-
hook_processor =
|
|
2879
|
+
hook_processor = self._hook_processor
|
|
2867
2880
|
if hook_processor is not None:
|
|
2868
2881
|
hook_processor.run_session_end()
|
|
2869
2882
|
try:
|
{openhands_sdk-1.56.0 → openhands_sdk-1.58.0}/openhands/sdk/conversation/impl/remote_conversation.py
RENAMED
|
@@ -29,7 +29,10 @@ from openhands.sdk.conversation.exceptions import (
|
|
|
29
29
|
ConversationRunError,
|
|
30
30
|
WebSocketConnectionError,
|
|
31
31
|
)
|
|
32
|
-
from openhands.sdk.conversation.secret_registry import
|
|
32
|
+
from openhands.sdk.conversation.secret_registry import (
|
|
33
|
+
SecretRegistry,
|
|
34
|
+
SecretValue,
|
|
35
|
+
)
|
|
33
36
|
from openhands.sdk.conversation.state import ConversationExecutionStatus
|
|
34
37
|
from openhands.sdk.conversation.title_utils import generate_conversation_title
|
|
35
38
|
from openhands.sdk.conversation.types import (
|
|
@@ -719,6 +722,15 @@ class RemoteState(ConversationStateProtocol):
|
|
|
719
722
|
return HookConfig.model_validate(hook_config_data)
|
|
720
723
|
return None
|
|
721
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
|
+
|
|
722
734
|
def model_dump(self, **_kwargs):
|
|
723
735
|
"""Get a dictionary representation of the remote state."""
|
|
724
736
|
info = self._get_conversation_info()
|
|
@@ -817,6 +829,7 @@ class RemoteConversation(BaseConversation):
|
|
|
817
829
|
token_callbacks: Optional callbacks invoked with each streamed token
|
|
818
830
|
delta the server sends (agent LLM with ``stream=True``).
|
|
819
831
|
"""
|
|
832
|
+
super().__init__()
|
|
820
833
|
observability_metadata = merge_observability_metadata(observability_metadata)
|
|
821
834
|
observability_parent_span_context = (
|
|
822
835
|
observability_parent_span_context
|
|
@@ -1917,52 +1930,55 @@ class RemoteConversation(BaseConversation):
|
|
|
1917
1930
|
The workspace owns the client and will close it during its own cleanup.
|
|
1918
1931
|
Closing it here would prevent the workspace from making cleanup API calls.
|
|
1919
1932
|
"""
|
|
1920
|
-
if self.
|
|
1933
|
+
if self._cleanup_complete:
|
|
1921
1934
|
return
|
|
1922
|
-
self._cleanup_initiated
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
cost
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
try:
|
|
1946
|
-
# Stop WebSocket client if it exists
|
|
1947
|
-
if self._ws_client:
|
|
1948
|
-
self._ws_client.stop()
|
|
1949
|
-
self._ws_client = None
|
|
1950
|
-
except Exception:
|
|
1951
|
-
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}")
|
|
1952
1958
|
|
|
1953
|
-
|
|
1954
|
-
if self.delete_on_close:
|
|
1959
|
+
# SessionEnd hooks are executed server-side (via hook_config in payload).
|
|
1955
1960
|
try:
|
|
1956
|
-
#
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
self.
|
|
1960
|
-
"DELETE",
|
|
1961
|
-
f"{CONVERSATIONS_PATH}/{self.id}",
|
|
1962
|
-
)
|
|
1961
|
+
# Stop WebSocket client if it exists
|
|
1962
|
+
if self._ws_client:
|
|
1963
|
+
self._ws_client.stop()
|
|
1964
|
+
self._ws_client = None
|
|
1963
1965
|
except Exception:
|
|
1964
1966
|
pass
|
|
1965
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
|
+
|
|
1966
1982
|
def __del__(self) -> None:
|
|
1967
1983
|
try:
|
|
1968
1984
|
self.close()
|
|
@@ -41,7 +41,16 @@ from openhands.sdk.security.confirmation_policy import (
|
|
|
41
41
|
ConfirmationPolicyBase,
|
|
42
42
|
NeverConfirm,
|
|
43
43
|
)
|
|
44
|
-
from openhands.sdk.settings.model import
|
|
44
|
+
from openhands.sdk.settings.model import (
|
|
45
|
+
DEFAULT_WORKTREE_BRANCH_PREFIX,
|
|
46
|
+
DOCKER_MEMORY_PATTERN,
|
|
47
|
+
GIT_BRANCH_PREFIX_PATTERN,
|
|
48
|
+
GIT_REF_PATTERN,
|
|
49
|
+
MIN_CONTAINER_CPUS,
|
|
50
|
+
ConversationRuntime,
|
|
51
|
+
WorktreeBase,
|
|
52
|
+
validate_agent_settings,
|
|
53
|
+
)
|
|
45
54
|
from openhands.sdk.skills import Skill
|
|
46
55
|
from openhands.sdk.subagent.schema import AgentDefinition
|
|
47
56
|
from openhands.sdk.tool.client_tool import ClientToolSpec
|
|
@@ -151,6 +160,68 @@ class AgentLaunchAdditions(BaseModel):
|
|
|
151
160
|
)
|
|
152
161
|
|
|
153
162
|
|
|
163
|
+
class ConversationContainerOptions(BaseModel):
|
|
164
|
+
"""Resources for a conversation's container.
|
|
165
|
+
|
|
166
|
+
Only the Docker runtime applies them, when it creates the conversation's
|
|
167
|
+
container; a local conversation ignores them. An unset field keeps the
|
|
168
|
+
server's configured limit; a set one replaces it and is not capped by it.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
model_config = ConfigDict(extra="forbid")
|
|
172
|
+
|
|
173
|
+
memory: str | None = Field(
|
|
174
|
+
default=None,
|
|
175
|
+
pattern=DOCKER_MEMORY_PATTERN,
|
|
176
|
+
description=(
|
|
177
|
+
"Container memory limit in `docker run --memory` syntax; replaces "
|
|
178
|
+
"the server's limit and is not capped by it."
|
|
179
|
+
),
|
|
180
|
+
)
|
|
181
|
+
cpus: float | None = Field(
|
|
182
|
+
default=None,
|
|
183
|
+
ge=MIN_CONTAINER_CPUS,
|
|
184
|
+
description=(
|
|
185
|
+
"Number of CPUs the container may use (`docker run --cpus`); "
|
|
186
|
+
"replaces the server's limit and is not capped by it."
|
|
187
|
+
),
|
|
188
|
+
)
|
|
189
|
+
browser: bool = Field(
|
|
190
|
+
default=True,
|
|
191
|
+
description=(
|
|
192
|
+
"False turns the browser off. True cannot turn it on for an image "
|
|
193
|
+
"without one."
|
|
194
|
+
),
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class ConversationWorktreeOptions(BaseModel):
|
|
199
|
+
"""How the server creates a conversation's worktree (``worktree=True``)."""
|
|
200
|
+
|
|
201
|
+
model_config = ConfigDict(extra="forbid")
|
|
202
|
+
|
|
203
|
+
base: WorktreeBase = Field(
|
|
204
|
+
default="default_branch",
|
|
205
|
+
description="What the worktree branches from; `ref` uses `ref`.",
|
|
206
|
+
)
|
|
207
|
+
ref: str | None = Field(
|
|
208
|
+
default=None,
|
|
209
|
+
pattern=GIT_REF_PATTERN,
|
|
210
|
+
description="Branch, tag or commit to branch from when `base` is `ref`.",
|
|
211
|
+
)
|
|
212
|
+
branch_prefix: str = Field(
|
|
213
|
+
default=DEFAULT_WORKTREE_BRANCH_PREFIX,
|
|
214
|
+
pattern=GIT_BRANCH_PREFIX_PATTERN,
|
|
215
|
+
description="Start of the worktree's branch name; the conversation id ends it.",
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
@model_validator(mode="after")
|
|
219
|
+
def _ref_for_ref_base(self) -> ConversationWorktreeOptions:
|
|
220
|
+
if self.base == "ref" and self.ref is None:
|
|
221
|
+
raise ValueError("A `ref` base needs a `ref`")
|
|
222
|
+
return self
|
|
223
|
+
|
|
224
|
+
|
|
154
225
|
class ConversationConfig(BaseModel):
|
|
155
226
|
"""Shared conversation configuration — everything except the agent.
|
|
156
227
|
|
|
@@ -175,6 +246,10 @@ class ConversationConfig(BaseModel):
|
|
|
175
246
|
"`/tmp/conversation-worktrees/<conversation_id>/<project_name>`."
|
|
176
247
|
),
|
|
177
248
|
)
|
|
249
|
+
worktree_options: ConversationWorktreeOptions | None = Field(
|
|
250
|
+
default=None,
|
|
251
|
+
description="How the worktree is created (`worktree=True`); unset: defaults.",
|
|
252
|
+
)
|
|
178
253
|
conversation_id: UUID | None = Field(
|
|
179
254
|
default=None,
|
|
180
255
|
description=(
|
|
@@ -346,6 +421,17 @@ class ConversationConfig(BaseModel):
|
|
|
346
421
|
"the agent's LLM."
|
|
347
422
|
),
|
|
348
423
|
)
|
|
424
|
+
prompt: str | None = Field(
|
|
425
|
+
default=None,
|
|
426
|
+
max_length=2000,
|
|
427
|
+
description=(
|
|
428
|
+
"Optional prompt that replaces the default title-generation user "
|
|
429
|
+
"message. Use {conversation_content} to place the first user "
|
|
430
|
+
"message and {max_length} to place the title length limit. If the "
|
|
431
|
+
"conversation placeholder is omitted, the message is appended "
|
|
432
|
+
"automatically. Empty or unset values use the default prompt."
|
|
433
|
+
),
|
|
434
|
+
)
|
|
349
435
|
|
|
350
436
|
|
|
351
437
|
class StartConversationRequest(ConversationConfig):
|
|
@@ -379,6 +465,18 @@ class StartConversationRequest(ConversationConfig):
|
|
|
379
465
|
),
|
|
380
466
|
)
|
|
381
467
|
agent: AgentBase = Field(default=cast(AgentBase, None))
|
|
468
|
+
conversation_runtime: ConversationRuntime | None = Field(
|
|
469
|
+
default=None,
|
|
470
|
+
description=(
|
|
471
|
+
"Runtime that executes this conversation. Unset uses the server's "
|
|
472
|
+
"configured `conversation_runtime`; another value is only accepted "
|
|
473
|
+
"when the server lists it in `available_conversation_runtimes`."
|
|
474
|
+
),
|
|
475
|
+
)
|
|
476
|
+
container: ConversationContainerOptions | None = Field(
|
|
477
|
+
default=None,
|
|
478
|
+
description="Resources for the conversation's container (Docker runtime).",
|
|
479
|
+
)
|
|
382
480
|
|
|
383
481
|
@model_validator(mode="before")
|
|
384
482
|
@classmethod
|
|
@@ -5,7 +5,7 @@ from collections.abc import Callable, Sequence
|
|
|
5
5
|
from contextlib import AbstractContextManager
|
|
6
6
|
from enum import Enum
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Any, Self
|
|
8
|
+
from typing import Any, Self, cast
|
|
9
9
|
|
|
10
10
|
from pydantic import Field, PrivateAttr
|
|
11
11
|
|
|
@@ -230,7 +230,7 @@ class ConversationState(OpenHandsModel):
|
|
|
230
230
|
)
|
|
231
231
|
|
|
232
232
|
# ===== Private attrs (NOT Fields) =====
|
|
233
|
-
_fs: FileStore = PrivateAttr() # filestore for persistence
|
|
233
|
+
_fs: FileStore = PrivateAttr(default=cast(Any, None)) # filestore for persistence
|
|
234
234
|
_events: EventLog = PrivateAttr() # now the storage for events
|
|
235
235
|
# Cached projection of `_events` for the *active branch*, lazily updated on
|
|
236
236
|
# read. Derived state — never persisted. `_view_branch_leaf` is the resolved
|
|
@@ -603,8 +603,8 @@ class ConversationState(OpenHandsModel):
|
|
|
603
603
|
super().__setattr__(name, value)
|
|
604
604
|
|
|
605
605
|
is_field = name in self.__class__.model_fields
|
|
606
|
-
autosave_enabled =
|
|
607
|
-
fs =
|
|
606
|
+
autosave_enabled = self._autosave_enabled
|
|
607
|
+
fs = self._fs
|
|
608
608
|
|
|
609
609
|
if not (autosave_enabled and is_field and fs is not None):
|
|
610
610
|
return
|
|
@@ -612,7 +612,7 @@ class ConversationState(OpenHandsModel):
|
|
|
612
612
|
if old is _sentinel or old != value:
|
|
613
613
|
# Inside a context-manager block, defer the save until __exit__
|
|
614
614
|
# so that multiple field mutations produce a single I/O write.
|
|
615
|
-
if
|
|
615
|
+
if self._save_depth > 0:
|
|
616
616
|
self._dirty = True
|
|
617
617
|
else:
|
|
618
618
|
try:
|
|
@@ -625,7 +625,7 @@ class ConversationState(OpenHandsModel):
|
|
|
625
625
|
# skipped: they are internal HEAD bookkeeping (leaf_event_id changes
|
|
626
626
|
# every event, so broadcasting it would ~double the persisted log) and
|
|
627
627
|
# the HEAD is recoverable from the just-appended event.
|
|
628
|
-
callback =
|
|
628
|
+
callback = self._on_state_change
|
|
629
629
|
if (
|
|
630
630
|
callback is not None
|
|
631
631
|
and old is not _sentinel
|
|
@@ -755,8 +755,8 @@ class ConversationState(OpenHandsModel):
|
|
|
755
755
|
try:
|
|
756
756
|
self._save_depth -= 1
|
|
757
757
|
if self._save_depth == 0 and self._dirty:
|
|
758
|
-
fs =
|
|
759
|
-
autosave_enabled =
|
|
758
|
+
fs = self._fs
|
|
759
|
+
autosave_enabled = self._autosave_enabled
|
|
760
760
|
if autosave_enabled and fs is not None:
|
|
761
761
|
self._save_base_state(fs)
|
|
762
762
|
self._dirty = False
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
from collections.abc import Callable, Sequence
|
|
5
|
+
from typing import Final
|
|
5
6
|
|
|
6
7
|
from openhands.sdk.event import MessageEvent
|
|
7
8
|
from openhands.sdk.event.base import Event
|
|
@@ -30,6 +31,9 @@ categories = [
|
|
|
30
31
|
{"emoji": "♻️", "name": "refactor", "description": "Code refactoring"},
|
|
31
32
|
]
|
|
32
33
|
|
|
34
|
+
CONVERSATION_CONTENT_PLACEHOLDER: Final[str] = "{conversation_content}"
|
|
35
|
+
MAX_LENGTH_PLACEHOLDER: Final[str] = "{max_length}"
|
|
36
|
+
|
|
33
37
|
|
|
34
38
|
def extract_message_text(event: MessageEvent) -> str | None:
|
|
35
39
|
"""Extract plain-text content from a message event."""
|
|
@@ -83,6 +87,7 @@ def generate_title_with_llm(
|
|
|
83
87
|
*,
|
|
84
88
|
call_context: LLMCallContext | None = None,
|
|
85
89
|
on_error: Callable[[Exception], None] | None = None,
|
|
90
|
+
prompt: str | None = None,
|
|
86
91
|
) -> str | None:
|
|
87
92
|
"""Generate a conversation title using LLM.
|
|
88
93
|
|
|
@@ -93,6 +98,10 @@ def generate_title_with_llm(
|
|
|
93
98
|
on_error: Optional callback invoked with the exception when the LLM
|
|
94
99
|
call fails. Title generation still falls back (returns None); the
|
|
95
100
|
callback lets callers surface the otherwise-swallowed error.
|
|
101
|
+
prompt: Optional user-message prompt override. The
|
|
102
|
+
``{conversation_content}`` and ``{max_length}`` placeholders are
|
|
103
|
+
replaced when present. If the conversation placeholder is omitted,
|
|
104
|
+
the message content is appended automatically.
|
|
96
105
|
|
|
97
106
|
Returns:
|
|
98
107
|
Generated title, or None if LLM fails or returns empty response.
|
|
@@ -107,6 +116,23 @@ def generate_title_with_llm(
|
|
|
107
116
|
f"{c['emoji']} {c['name']}: {c['description']}" for c in categories
|
|
108
117
|
)
|
|
109
118
|
|
|
119
|
+
template = (prompt or "").strip()
|
|
120
|
+
if template:
|
|
121
|
+
user_prompt = template.replace(
|
|
122
|
+
CONVERSATION_CONTENT_PLACEHOLDER, truncated_message
|
|
123
|
+
).replace(MAX_LENGTH_PLACEHOLDER, str(max_length))
|
|
124
|
+
if CONVERSATION_CONTENT_PLACEHOLDER not in template:
|
|
125
|
+
user_prompt = f"{user_prompt}\n\nConversation content:\n{truncated_message}"
|
|
126
|
+
else:
|
|
127
|
+
user_prompt = (
|
|
128
|
+
f"Generate a title (maximum {max_length} characters) "
|
|
129
|
+
f"for a conversation that starts with this message:\n\n"
|
|
130
|
+
f"{truncated_message}."
|
|
131
|
+
"Also make sure to include ONE most relevant emoji at "
|
|
132
|
+
"the start of the title."
|
|
133
|
+
f" Choose the emoji from this list:{emojis_descriptions} "
|
|
134
|
+
)
|
|
135
|
+
|
|
110
136
|
try:
|
|
111
137
|
# Create messages for the LLM to generate a title
|
|
112
138
|
messages = [
|
|
@@ -130,18 +156,7 @@ def generate_title_with_llm(
|
|
|
130
156
|
),
|
|
131
157
|
Message(
|
|
132
158
|
role="user",
|
|
133
|
-
content=[
|
|
134
|
-
TextContent(
|
|
135
|
-
text=(
|
|
136
|
-
f"Generate a title (maximum {max_length} characters) "
|
|
137
|
-
f"for a conversation that starts with this message:\n\n"
|
|
138
|
-
f"{truncated_message}."
|
|
139
|
-
"Also make sure to include ONE most relevant emoji at "
|
|
140
|
-
"the start of the title."
|
|
141
|
-
f" Choose the emoji from this list:{emojis_descriptions} "
|
|
142
|
-
)
|
|
143
|
-
)
|
|
144
|
-
],
|
|
159
|
+
content=[TextContent(text=user_prompt)],
|
|
145
160
|
),
|
|
146
161
|
]
|
|
147
162
|
|
|
@@ -204,6 +219,7 @@ def generate_title_from_message(
|
|
|
204
219
|
*,
|
|
205
220
|
call_context: LLMCallContext | None = None,
|
|
206
221
|
on_error: Callable[[Exception], None] | None = None,
|
|
222
|
+
prompt: str | None = None,
|
|
207
223
|
) -> str:
|
|
208
224
|
"""Generate a title from an already-extracted user message."""
|
|
209
225
|
# Skip the ACP sentinel LLM — it has no credentials and cannot be
|
|
@@ -218,6 +234,7 @@ def generate_title_from_message(
|
|
|
218
234
|
max_length,
|
|
219
235
|
call_context=call_context,
|
|
220
236
|
on_error=on_error,
|
|
237
|
+
prompt=prompt,
|
|
221
238
|
)
|
|
222
239
|
if llm_title:
|
|
223
240
|
return llm_title
|