openhands-sdk 1.7.2__tar.gz → 1.7.4__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.7.2 → openhands_sdk-1.7.4}/PKG-INFO +2 -2
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/agent.py +26 -10
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/base.py +53 -15
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/condenser/__init__.py +2 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/condenser/base.py +59 -8
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/condenser/llm_summarizing_condenser.py +42 -4
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/skills/skill.py +336 -118
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/view.py +2 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/impl/remote_conversation.py +110 -29
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/state.py +9 -5
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/llm.py +1 -2
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/options/chat_options.py +4 -1
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/utils/model_features.py +1 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/utils/verified_models.py +1 -1
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/mcp/tool.py +3 -1
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/tool/registry.py +23 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/tool/schema.py +6 -3
- openhands_sdk-1.7.4/openhands/sdk/utils/models.py +296 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/workspace/base.py +22 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/workspace/local.py +16 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands_sdk.egg-info/PKG-INFO +2 -2
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands_sdk.egg-info/requires.txt +1 -1
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/pyproject.toml +2 -2
- openhands_sdk-1.7.2/openhands/sdk/utils/models.py +0 -570
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/prompts/in_context_learning_example.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/prompts/in_context_learning_example_suffix.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/prompts/model_specific/anthropic_claude.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/prompts/model_specific/google_gemini.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/prompts/model_specific/openai_gpt/gpt-5-codex.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/prompts/model_specific/openai_gpt/gpt-5.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/prompts/security_policy.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/prompts/security_risk_assessment.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/prompts/self_documentation.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/prompts/system_prompt.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/prompts/system_prompt_interactive.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/prompts/system_prompt_long_horizon.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/prompts/system_prompt_planning.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/prompts/system_prompt_tech_philosophy.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/agent/utils.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/agent_context.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/condenser/no_op_condenser.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/condenser/pipeline_condenser.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/condenser/prompts/summarizing_prompt.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/condenser/utils.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/prompts/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/prompts/prompt.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/prompts/templates/ask_agent_template.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/prompts/templates/system_message_suffix.j2 +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/skills/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/skills/exceptions.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/skills/trigger.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/context/skills/types.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/base.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/conversation.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/conversation_stats.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/event_store.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/events_list_base.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/exceptions.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/fifo_lock.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/impl/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/impl/local_conversation.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/persistence_const.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/response_utils.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/secret_registry.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/serialization_diff.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/stuck_detector.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/title_utils.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/types.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/visualizer/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/visualizer/base.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/conversation/visualizer/default.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/critic/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/critic/base.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/critic/impl/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/critic/impl/agent_finished.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/critic/impl/empty_patch.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/critic/impl/pass_critic.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/event/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/event/base.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/event/condenser.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/event/conversation_error.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/event/conversation_state.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/event/llm_completion_log.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/event/llm_convertible/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/event/llm_convertible/action.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/event/llm_convertible/message.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/event/llm_convertible/observation.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/event/llm_convertible/system.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/event/token.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/event/types.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/event/user_action.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/git/exceptions.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/git/git_changes.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/git/git_diff.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/git/models.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/git/utils.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/hooks/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/hooks/config.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/hooks/conversation_hooks.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/hooks/executor.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/hooks/manager.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/hooks/types.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/io/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/io/base.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/io/cache.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/io/local.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/io/memory.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/exceptions/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/exceptions/classifier.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/exceptions/mapping.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/exceptions/types.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/llm_registry.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/llm_response.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/message.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/mixins/fn_call_converter.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/mixins/non_native_fc.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/options/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/options/common.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/options/responses_options.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/router/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/router/base.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/router/impl/multimodal.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/router/impl/random.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/streaming.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/utils/metrics.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/utils/model_info.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/utils/model_prompt_spec.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/utils/retry_mixin.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/utils/telemetry.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/llm/utils/unverified_models.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/logger/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/logger/logger.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/logger/rolling.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/mcp/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/mcp/client.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/mcp/definition.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/mcp/exceptions.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/mcp/utils.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/observability/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/observability/laminar.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/observability/utils.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/py.typed +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/secret/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/secret/secrets.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/security/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/security/analyzer.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/security/confirmation_policy.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/security/llm_analyzer.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/security/risk.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/tool/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/tool/builtins/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/tool/builtins/finish.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/tool/builtins/think.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/tool/spec.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/tool/tool.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/utils/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/utils/async_executor.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/utils/async_utils.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/utils/cipher.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/utils/command.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/utils/deprecation.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/utils/github.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/utils/json.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/utils/paging.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/utils/pydantic_diff.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/utils/pydantic_secrets.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/utils/truncate.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/utils/visualize.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/workspace/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/workspace/models.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/workspace/remote/__init__.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/workspace/remote/async_remote_workspace.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/workspace/remote/base.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/workspace/remote/remote_workspace_mixin.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands/sdk/workspace/workspace.py +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands_sdk.egg-info/SOURCES.txt +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands_sdk.egg-info/dependency_links.txt +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/openhands_sdk.egg-info/top_level.txt +0 -0
- {openhands_sdk-1.7.2 → openhands_sdk-1.7.4}/setup.cfg +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-sdk
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.4
|
|
4
4
|
Summary: OpenHands SDK - Core functionality for building AI agents
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Requires-Dist: deprecation>=2.1.0
|
|
7
7
|
Requires-Dist: fastmcp>=2.11.3
|
|
8
8
|
Requires-Dist: httpx>=0.27.0
|
|
9
9
|
Requires-Dist: litellm>=1.80.10
|
|
10
|
-
Requires-Dist: pydantic>=2.
|
|
10
|
+
Requires-Dist: pydantic>=2.12.5
|
|
11
11
|
Requires-Dist: python-frontmatter>=1.1.0
|
|
12
12
|
Requires-Dist: python-json-logger>=3.3.0
|
|
13
13
|
Requires-Dist: tenacity>=9.1.2
|
|
@@ -27,7 +27,10 @@ from openhands.sdk.event import (
|
|
|
27
27
|
TokenEvent,
|
|
28
28
|
UserRejectObservation,
|
|
29
29
|
)
|
|
30
|
-
from openhands.sdk.event.condenser import
|
|
30
|
+
from openhands.sdk.event.condenser import (
|
|
31
|
+
Condensation,
|
|
32
|
+
CondensationRequest,
|
|
33
|
+
)
|
|
31
34
|
from openhands.sdk.llm import (
|
|
32
35
|
LLMResponse,
|
|
33
36
|
Message,
|
|
@@ -502,16 +505,29 @@ class Agent(AgentBase):
|
|
|
502
505
|
)
|
|
503
506
|
|
|
504
507
|
# Execute actions!
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
508
|
+
try:
|
|
509
|
+
if should_enable_observability():
|
|
510
|
+
tool_name = extract_action_name(action_event)
|
|
511
|
+
observation: Observation = observe(name=tool_name, span_type="TOOL")(
|
|
512
|
+
tool
|
|
513
|
+
)(action_event.action, conversation)
|
|
514
|
+
else:
|
|
515
|
+
observation = tool(action_event.action, conversation)
|
|
516
|
+
assert isinstance(observation, Observation), (
|
|
517
|
+
f"Tool '{tool.name}' executor must return an Observation"
|
|
509
518
|
)
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
f"
|
|
514
|
-
|
|
519
|
+
except ValueError as e:
|
|
520
|
+
# Tool execution raised a ValueError (e.g., invalid argument combination)
|
|
521
|
+
# Convert to AgentErrorEvent so the agent can correct itself
|
|
522
|
+
err = f"Error executing tool '{tool.name}': {e}"
|
|
523
|
+
logger.warning(err)
|
|
524
|
+
error_event = AgentErrorEvent(
|
|
525
|
+
error=err,
|
|
526
|
+
tool_name=tool.name,
|
|
527
|
+
tool_call_id=action_event.tool_call.id,
|
|
528
|
+
)
|
|
529
|
+
on_event(error_event)
|
|
530
|
+
return error_event
|
|
515
531
|
|
|
516
532
|
obs_event = ObservationEvent(
|
|
517
533
|
observation=observation,
|
|
@@ -2,7 +2,7 @@ import os
|
|
|
2
2
|
import re
|
|
3
3
|
import sys
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
|
-
from collections.abc import Generator, Iterable
|
|
5
|
+
from collections.abc import Generator, Iterable, Sequence
|
|
6
6
|
from concurrent.futures import ThreadPoolExecutor
|
|
7
7
|
from typing import TYPE_CHECKING, Any
|
|
8
8
|
|
|
@@ -300,10 +300,19 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
300
300
|
NOTE: state will be mutated in-place.
|
|
301
301
|
"""
|
|
302
302
|
|
|
303
|
-
def resolve_diff_from_deserialized(
|
|
303
|
+
def resolve_diff_from_deserialized(
|
|
304
|
+
self,
|
|
305
|
+
persisted: "AgentBase",
|
|
306
|
+
events: "Sequence[Any] | None" = None,
|
|
307
|
+
) -> "AgentBase":
|
|
304
308
|
"""
|
|
305
309
|
Return a new AgentBase instance equivalent to `persisted` but with
|
|
306
310
|
explicitly whitelisted fields (e.g. api_key) taken from `self`.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
persisted: The persisted agent from the conversation state.
|
|
314
|
+
events: Optional event sequence to scan for used tools if tool
|
|
315
|
+
names don't match. Only scanned when needed (O(n) fallback).
|
|
307
316
|
"""
|
|
308
317
|
if persisted.__class__ is not self.__class__:
|
|
309
318
|
raise ValueError(
|
|
@@ -338,28 +347,57 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
338
347
|
if self.agent_context is not None:
|
|
339
348
|
updates["agent_context"] = self.agent_context
|
|
340
349
|
|
|
341
|
-
#
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
#
|
|
346
|
-
runtime_names
|
|
347
|
-
|
|
350
|
+
# Get tool names for comparison
|
|
351
|
+
runtime_names = {tool.name for tool in self.tools}
|
|
352
|
+
persisted_names = {tool.name for tool in persisted.tools}
|
|
353
|
+
|
|
354
|
+
# If tool names match exactly, no need to check event history
|
|
355
|
+
if runtime_names == persisted_names:
|
|
356
|
+
# Tools unchanged, proceed normally
|
|
357
|
+
pass
|
|
358
|
+
elif events is not None:
|
|
359
|
+
# Tool names differ - scan events to find which tools were actually used
|
|
360
|
+
# This is O(n) but only happens when tools change
|
|
361
|
+
from openhands.sdk.event import ActionEvent
|
|
362
|
+
|
|
363
|
+
used_tools = {
|
|
364
|
+
event.tool_name
|
|
365
|
+
for event in events
|
|
366
|
+
if isinstance(event, ActionEvent) and event.tool_name
|
|
367
|
+
}
|
|
348
368
|
|
|
349
|
-
|
|
369
|
+
# Only require tools that were actually used in history
|
|
370
|
+
missing_used_tools = used_tools - runtime_names
|
|
371
|
+
if missing_used_tools:
|
|
372
|
+
raise ValueError(
|
|
373
|
+
f"Cannot resume conversation: tools that were used in history "
|
|
374
|
+
f"are missing from runtime: {sorted(missing_used_tools)}. "
|
|
375
|
+
f"Available tools: {sorted(runtime_names)}"
|
|
376
|
+
)
|
|
377
|
+
# Update tools to match runtime (allows new tools to be added)
|
|
378
|
+
updates["tools"] = self.tools
|
|
379
|
+
else:
|
|
380
|
+
# No events provided - strict matching (legacy behavior)
|
|
350
381
|
missing_in_runtime = persisted_names - runtime_names
|
|
351
382
|
missing_in_persisted = runtime_names - persisted_names
|
|
352
383
|
error_msg = "Tools don't match between runtime and persisted agents."
|
|
353
384
|
if missing_in_runtime:
|
|
354
|
-
error_msg += f" Missing in runtime: {missing_in_runtime}."
|
|
385
|
+
error_msg += f" Missing in runtime: {sorted(missing_in_runtime)}."
|
|
355
386
|
if missing_in_persisted:
|
|
356
|
-
error_msg += f" Missing in persisted: {missing_in_persisted}."
|
|
387
|
+
error_msg += f" Missing in persisted: {sorted(missing_in_persisted)}."
|
|
357
388
|
raise ValueError(error_msg)
|
|
358
389
|
|
|
359
390
|
reconciled = persisted.model_copy(update=updates)
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
391
|
+
|
|
392
|
+
# Validate agent equality - exclude tools from comparison since we
|
|
393
|
+
# already validated tool requirements above
|
|
394
|
+
exclude_fields = {"tools"} if events is not None else set()
|
|
395
|
+
self_dump = self.model_dump(exclude_none=True, exclude=exclude_fields)
|
|
396
|
+
reconciled_dump = reconciled.model_dump(
|
|
397
|
+
exclude_none=True, exclude=exclude_fields
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
if self_dump != reconciled_dump:
|
|
363
401
|
raise ValueError(
|
|
364
402
|
"The Agent provided is different from the one in persisted state.\n"
|
|
365
403
|
f"Diff: {pretty_pydantic_diff(self, reconciled)}"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from openhands.sdk.context.condenser.base import (
|
|
2
2
|
CondenserBase,
|
|
3
|
+
NoCondensationAvailableException,
|
|
3
4
|
RollingCondenser,
|
|
4
5
|
)
|
|
5
6
|
from openhands.sdk.context.condenser.llm_summarizing_condenser import (
|
|
@@ -15,4 +16,5 @@ __all__ = [
|
|
|
15
16
|
"NoOpCondenser",
|
|
16
17
|
"PipelineCondenser",
|
|
17
18
|
"LLMSummarizingCondenser",
|
|
19
|
+
"NoCondensationAvailableException",
|
|
18
20
|
]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
+
from enum import Enum
|
|
2
3
|
from logging import getLogger
|
|
3
4
|
|
|
4
5
|
from openhands.sdk.context.view import View
|
|
@@ -66,6 +67,29 @@ class PipelinableCondenserBase(CondenserBase):
|
|
|
66
67
|
condenser should not nest another pipeline condenser)"""
|
|
67
68
|
|
|
68
69
|
|
|
70
|
+
class NoCondensationAvailableException(Exception):
|
|
71
|
+
"""Raised when a condenser is asked to provide a condensation but none is available.
|
|
72
|
+
|
|
73
|
+
This can happen if the condenser's `should_condense` method returns True, but due to
|
|
74
|
+
API constraints no condensation can be generated.
|
|
75
|
+
|
|
76
|
+
When this exception is raised from a rolling condenser's `get_condensation` method,
|
|
77
|
+
the agent will fall back to using the uncondensed view for the next agent step.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class CondensationRequirement(Enum):
|
|
82
|
+
"""The type of condensation required by a rolling condenser."""
|
|
83
|
+
|
|
84
|
+
HARD = "hard"
|
|
85
|
+
"""Indicates that a condensation is required right now, and the agent cannot proceed
|
|
86
|
+
without it.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
SOFT = "soft"
|
|
90
|
+
"""Indicates that a condensation is desired but not strictly required."""
|
|
91
|
+
|
|
92
|
+
|
|
69
93
|
class RollingCondenser(PipelinableCondenserBase, ABC):
|
|
70
94
|
"""Base class for a specialized condenser strategy that applies condensation to a
|
|
71
95
|
rolling history.
|
|
@@ -73,15 +97,27 @@ class RollingCondenser(PipelinableCondenserBase, ABC):
|
|
|
73
97
|
The rolling history is generated by `View.from_events`, which analyzes all events in
|
|
74
98
|
the history and produces a `View` object representing what will be sent to the LLM.
|
|
75
99
|
|
|
76
|
-
If `
|
|
77
|
-
`Condensation` object from the `View` object. This will be added to the
|
|
78
|
-
history which should -- when given to `get_view` -- produce the condensed
|
|
79
|
-
be passed to the LLM.
|
|
100
|
+
If `condensation_requirement` says so, the condenser is then responsible for
|
|
101
|
+
generating a `Condensation` object from the `View` object. This will be added to the
|
|
102
|
+
event history which should -- when given to `get_view` -- produce the condensed
|
|
103
|
+
`View` to be passed to the LLM.
|
|
80
104
|
"""
|
|
81
105
|
|
|
82
106
|
@abstractmethod
|
|
83
|
-
def
|
|
84
|
-
|
|
107
|
+
def condensation_requirement(
|
|
108
|
+
self, view: View, agent_llm: LLM | None = None
|
|
109
|
+
) -> CondensationRequirement | None:
|
|
110
|
+
"""Determine how a view should be condensed.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
view: The current view of the conversation history.
|
|
114
|
+
agent_llm: LLM instance used by the agent. Condensers use this for token
|
|
115
|
+
counting purposes. Defaults to None.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
CondensationRequirement | None: The type of condensation required, or None
|
|
119
|
+
if no condensation is needed.
|
|
120
|
+
"""
|
|
85
121
|
|
|
86
122
|
@abstractmethod
|
|
87
123
|
def get_condensation(
|
|
@@ -92,8 +128,23 @@ class RollingCondenser(PipelinableCondenserBase, ABC):
|
|
|
92
128
|
def condense(self, view: View, agent_llm: LLM | None = None) -> View | Condensation:
|
|
93
129
|
# If we trigger the condenser-specific condensation threshold, compute and
|
|
94
130
|
# return the condensation.
|
|
95
|
-
|
|
96
|
-
|
|
131
|
+
request = self.condensation_requirement(view, agent_llm=agent_llm)
|
|
132
|
+
if request is not None:
|
|
133
|
+
try:
|
|
134
|
+
return self.get_condensation(view, agent_llm=agent_llm)
|
|
135
|
+
|
|
136
|
+
except NoCondensationAvailableException as e:
|
|
137
|
+
logger.debug(f"No condensation available: {e}")
|
|
138
|
+
|
|
139
|
+
if request == CondensationRequirement.SOFT:
|
|
140
|
+
# For soft requests, we can just return the uncondensed view. This
|
|
141
|
+
# request will _eventually_ be handled, but it's not critical that
|
|
142
|
+
# we do so immediately.
|
|
143
|
+
return view
|
|
144
|
+
|
|
145
|
+
# Otherwise re-raise the exception.
|
|
146
|
+
else:
|
|
147
|
+
raise e
|
|
97
148
|
|
|
98
149
|
# Otherwise we're safe to just return the view.
|
|
99
150
|
else:
|
|
@@ -4,7 +4,11 @@ from enum import Enum
|
|
|
4
4
|
|
|
5
5
|
from pydantic import Field, model_validator
|
|
6
6
|
|
|
7
|
-
from openhands.sdk.context.condenser.base import
|
|
7
|
+
from openhands.sdk.context.condenser.base import (
|
|
8
|
+
CondensationRequirement,
|
|
9
|
+
NoCondensationAvailableException,
|
|
10
|
+
RollingCondenser,
|
|
11
|
+
)
|
|
8
12
|
from openhands.sdk.context.condenser.utils import (
|
|
9
13
|
get_suffix_length_for_token_reduction,
|
|
10
14
|
get_total_token_count,
|
|
@@ -84,9 +88,30 @@ class LLMSummarizingCondenser(RollingCondenser):
|
|
|
84
88
|
|
|
85
89
|
return reasons
|
|
86
90
|
|
|
87
|
-
def
|
|
91
|
+
def condensation_requirement(
|
|
92
|
+
self, view: View, agent_llm: LLM | None = None
|
|
93
|
+
) -> CondensationRequirement | None:
|
|
88
94
|
reasons = self.get_condensation_reasons(view, agent_llm)
|
|
89
|
-
|
|
95
|
+
|
|
96
|
+
# No reasons => no condensation needed.
|
|
97
|
+
if reasons == set():
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
# If the reasons are for resource constraints, we can treat it as a soft
|
|
101
|
+
# requirement. We want to condense when we can, but there's still space in the
|
|
102
|
+
# context window or we'd also see Reason.REQUEST. That means we can delay the
|
|
103
|
+
# condensation if there isn't one available (based on the view's manipulation
|
|
104
|
+
# indices).
|
|
105
|
+
resource_reasons = {Reason.TOKENS, Reason.EVENTS}
|
|
106
|
+
if reasons.issubset(resource_reasons):
|
|
107
|
+
return CondensationRequirement.SOFT
|
|
108
|
+
|
|
109
|
+
# Requests -- whether they come from the user or the agent -- are always hard
|
|
110
|
+
# requirements. We need to condense now because:
|
|
111
|
+
# 1. the user expects it
|
|
112
|
+
# 2. the agent has no more room in the context window and can't continue
|
|
113
|
+
if Reason.REQUEST in reasons:
|
|
114
|
+
return CondensationRequirement.HARD
|
|
90
115
|
|
|
91
116
|
def _get_summary_event_content(self, view: View) -> str:
|
|
92
117
|
"""Extract the text content from the summary event in the view, if any.
|
|
@@ -120,7 +145,12 @@ class LLMSummarizingCondenser(RollingCondenser):
|
|
|
120
145
|
|
|
121
146
|
Returns:
|
|
122
147
|
Condensation: The generated condensation object.
|
|
148
|
+
|
|
149
|
+
Raises:
|
|
150
|
+
ValueError: If forgotten_events is empty (0 events to condense).
|
|
123
151
|
"""
|
|
152
|
+
assert len(forgotten_events) > 0, "No events to condense."
|
|
153
|
+
|
|
124
154
|
# Convert events to strings for the template
|
|
125
155
|
event_strings = [str(forgotten_event) for forgotten_event in forgotten_events]
|
|
126
156
|
|
|
@@ -226,11 +256,19 @@ class LLMSummarizingCondenser(RollingCondenser):
|
|
|
226
256
|
) -> Condensation:
|
|
227
257
|
# The condensation is dependent on the events we want to drop and the previous
|
|
228
258
|
# summary.
|
|
229
|
-
summary_event_content = self._get_summary_event_content(view)
|
|
230
259
|
forgotten_events, summary_offset = self._get_forgotten_events(
|
|
231
260
|
view, agent_llm=agent_llm
|
|
232
261
|
)
|
|
233
262
|
|
|
263
|
+
if not forgotten_events:
|
|
264
|
+
raise NoCondensationAvailableException(
|
|
265
|
+
"Cannot condense 0 events. This typically occurs when a tool loop "
|
|
266
|
+
"spans almost the entire view, leaving no valid range for forgetting "
|
|
267
|
+
"events. Consider adjusting keep_first or max_size parameters."
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
summary_event_content = self._get_summary_event_content(view)
|
|
271
|
+
|
|
234
272
|
return self._generate_condensation(
|
|
235
273
|
summary_event_content=summary_event_content,
|
|
236
274
|
forgotten_events=forgotten_events,
|