openhands-sdk 1.49.6__tar.gz → 1.50.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.49.6 → openhands_sdk-1.50.0}/PKG-INFO +1 -1
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/agent.py +70 -3
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/response_dispatch.py +24 -3
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/condenser/llm_summarizing_condenser.py +65 -20
- openhands_sdk-1.50.0/openhands/sdk/context/condenser/prompts/summarizing_events.j2 +7 -0
- openhands_sdk-1.49.6/openhands/sdk/context/condenser/prompts/summarizing_prompt.j2 → openhands_sdk-1.50.0/openhands/sdk/context/condenser/prompts/summarizing_system.j2 +0 -8
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/goal/judge.py +12 -3
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/goal/prompts.py +31 -13
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/impl/local_conversation.py +46 -33
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/title_utils.py +22 -3
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/hooks/conversation_hooks.py +4 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/hooks/executor.py +9 -1
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/hooks/manager.py +3 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/__init__.py +2 -0
- openhands_sdk-1.50.0/openhands/sdk/llm/call_context.py +86 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/exceptions/classifier.py +26 -10
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/fallback_strategy.py +13 -1
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/llm.py +26 -27
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/options/chat_options.py +11 -3
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/options/common.py +1 -29
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/options/responses_options.py +11 -3
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/model_features.py +7 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/telemetry.py +5 -1
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/grayswan/analyzer.py +25 -3
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/builtins/classify_and_switch_llm.py +4 -2
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/schema.py +5 -1
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands_sdk.egg-info/PKG-INFO +1 -1
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands_sdk.egg-info/SOURCES.txt +6 -2
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/pyproject.toml +1 -1
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/acp_agent.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/acp_file_credentials.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/acp_models.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/acp_tracing.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/base.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/critic_mixin.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/parallel_executor.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/stream_context.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/utils.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/banner.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/agent_context.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/condenser/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/condenser/base.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/condenser/no_op_condenser.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/condenser/pipeline_condenser.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/condenser/utils.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/memory.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/presets.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/prompt.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/registry.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/section.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/sections/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/sections/dynamic.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/sections/planning.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/sections/static.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/templates/ask_agent_template.j2 +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/manipulation_indices.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/properties/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/properties/base.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/properties/batch_atomicity.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/properties/observation_uniqueness.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/properties/tool_call_matching.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/properties/tool_loop_atomicity.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/view.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/base.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/cancellation.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/conversation.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/conversation_stats.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/event_store.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/events_list_base.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/exceptions.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/fifo_lock.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/goal/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/goal/controller.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/goal/runner.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/impl/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/impl/remote_conversation.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/persistence_const.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/request.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/resource_lock_manager.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/response_utils.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/secret_registry.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/state.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/stuck_detector.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/types.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/visualizer/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/visualizer/base.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/visualizer/default.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/credential.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/base.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/agent_finished.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/api/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/api/chat_template.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/api/client.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/api/critic.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/api/taxonomy.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/empty_patch.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/pass_critic.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/result.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/acp_tool_call.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/base.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/condenser.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/conversation_error.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/conversation_state.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/error_classification.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/hook_execution.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/llm_completion_log.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/llm_convertible/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/llm_convertible/action.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/llm_convertible/message.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/llm_convertible/observation.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/llm_convertible/reasoning_utils.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/llm_convertible/system.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/resume_transcript.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/streaming_delta.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/token.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/types.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/user_action.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/extensions/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/extensions/fetch.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/extensions/installation/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/extensions/installation/info.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/extensions/installation/interface.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/extensions/installation/manager.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/extensions/installation/metadata.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/extensions/installation/utils.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/git/cached_repo.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/git/exceptions.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/git/git_changes.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/git/git_commits.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/git/git_diff.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/git/models.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/git/utils.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/hooks/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/hooks/config.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/hooks/types.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/io/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/io/base.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/io/cache.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/io/local.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/io/memory.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/auth/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/auth/credentials.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/auth/openai.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/cleanup_profile.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/exceptions/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/exceptions/mapping.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/exceptions/types.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/llm_profile_store.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/llm_registry.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/llm_response.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/message.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/meta_profile_store.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/mixins/fn_call_converter.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/mixins/fn_call_examples.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/mixins/non_native_fc.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/options/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/provider_connection_store.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/router/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/router/base.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/router/impl/multimodal.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/router/impl/random.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/streaming.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/image_inline.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/image_resize.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/litellm_provider.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/metrics.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/model_info.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/model_prompt_spec.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/openhands_provider.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/providers/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/providers/openrouter.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/responses_serialization.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/retry_mixin.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/runtime_metadata.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/unverified_models.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/verified_models.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/vertex_preflight.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/logger/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/logger/logger.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/logger/rolling.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/marketplace/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/marketplace/registration.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/marketplace/registry.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/marketplace/types.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/mcp/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/mcp/client.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/mcp/config.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/mcp/definition.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/mcp/exceptions.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/mcp/oauth.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/mcp/tool.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/mcp/utils.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/observability/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/observability/laminar.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/observability/utils.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/discovery.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/fetch.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/format/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/format/agent_plugins.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/format/agent_plugins_mcp.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/format/base.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/format/claude_code.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/format/schemas/mcp-1.0.0.schema.json +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/format/schemas/plugin-1.0.0.schema.json +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/installed.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/loader.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/plugin.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/source.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/types.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/profiles/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/profiles/agent_profile.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/profiles/agent_profile_store.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/profiles/profile_refs.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/profiles/resolver.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/profiles/seed.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/py.typed +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/secret/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/secret/secrets.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/_shell_ast.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/analyzer.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/confirmation_policy.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/defense_in_depth/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/defense_in_depth/pattern.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/defense_in_depth/policy_rails.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/defense_in_depth/shell_semantics.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/defense_in_depth/utils.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/ensemble.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/grayswan/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/grayswan/utils.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/llm_analyzer.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/risk.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/shell_parser.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/toolshield_helpers.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/toolshield_llm_analyzer.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/settings/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/settings/acp_install_catalog.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/settings/acp_providers.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/settings/api_models.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/settings/metadata.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/settings/model.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/exceptions.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/execute.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/fetch.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/installed.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/skill.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/trigger.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/types.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/utils.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/subagent/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/subagent/load.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/subagent/registry.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/subagent/schema.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/testing/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/testing/test_llm.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/builtins/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/builtins/finish.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/builtins/invoke_skill.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/builtins/switch_llm.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/builtins/think.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/builtins/vision_inspect.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/client_tool.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/defaults.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/registry.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/spec.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/tool.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/async_executor.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/async_utils.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/cipher.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/command.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/datetime.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/deprecation.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/files.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/github.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/json.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/masking.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/models.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/paging.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/path.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/pydantic_secrets.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/redact.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/truncate.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/visualize.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/base.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/local.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/models.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/remote/__init__.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/remote/async_remote_workspace.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/remote/base.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/remote/remote_workspace_mixin.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/repo.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/workspace.py +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands_sdk.egg-info/dependency_links.txt +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands_sdk.egg-info/requires.txt +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands_sdk.egg-info/top_level.txt +0 -0
- {openhands_sdk-1.49.6 → openhands_sdk-1.50.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.50.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
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import json
|
|
4
5
|
import re
|
|
5
6
|
from collections.abc import Callable
|
|
@@ -61,6 +62,7 @@ from openhands.sdk.llm import (
|
|
|
61
62
|
TextContent,
|
|
62
63
|
ThinkingBlock,
|
|
63
64
|
)
|
|
65
|
+
from openhands.sdk.llm.call_context import llm_call_context_scope
|
|
64
66
|
from openhands.sdk.llm.exceptions import (
|
|
65
67
|
FunctionCallValidationError,
|
|
66
68
|
LLMContentPolicyViolationError,
|
|
@@ -120,6 +122,18 @@ INIT_STATE_PREFIX_SCAN_WINDOW = 3
|
|
|
120
122
|
|
|
121
123
|
|
|
122
124
|
def _latest_user_message_contains_image(messages: list[Message]) -> bool:
|
|
125
|
+
"""Check if the most recent user message contains image content.
|
|
126
|
+
|
|
127
|
+
Scans the message list in reverse order to find the latest user message
|
|
128
|
+
and checks whether it contains any image attachments.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
messages: List of conversation messages to search.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
True if the most recent user message contains images, False otherwise.
|
|
135
|
+
Returns False if no user messages are found.
|
|
136
|
+
"""
|
|
123
137
|
for message in reversed(messages):
|
|
124
138
|
if message.role == "user":
|
|
125
139
|
return message.contains_image
|
|
@@ -127,6 +141,18 @@ def _latest_user_message_contains_image(messages: list[Message]) -> bool:
|
|
|
127
141
|
|
|
128
142
|
|
|
129
143
|
def _non_multimodal_image_message(model: str) -> Message:
|
|
144
|
+
"""Create an error message for when images are sent to a non-vision model.
|
|
145
|
+
|
|
146
|
+
Constructs a user-friendly assistant message explaining that the current
|
|
147
|
+
model does not support image understanding and suggesting the user switch
|
|
148
|
+
to a multimodal model.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
model: The name of the current model that does not support vision.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
A Message object containing an assistant response with the error explanation.
|
|
155
|
+
"""
|
|
130
156
|
return Message(
|
|
131
157
|
role="assistant",
|
|
132
158
|
content=[
|
|
@@ -144,6 +170,20 @@ def _non_multimodal_image_message(model: str) -> Message:
|
|
|
144
170
|
def _replace_latest_user_images_with_references(
|
|
145
171
|
messages: list[Message],
|
|
146
172
|
) -> list[Message]:
|
|
173
|
+
"""Replace images in the most recent user message containing images.
|
|
174
|
+
|
|
175
|
+
Searches backward, skipping user messages without images. Each image URL
|
|
176
|
+
becomes a textual reference to the inspect_image_with_vision tool, with a
|
|
177
|
+
zero-based image index within the selected message.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
messages: List of conversation messages to process.
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
A new list with the selected message copied and its images replaced.
|
|
184
|
+
Other messages and non-image content are unchanged. If no user message
|
|
185
|
+
contains images, returns a shallow copy of the original list.
|
|
186
|
+
"""
|
|
147
187
|
rewritten = list(messages)
|
|
148
188
|
for index in range(len(rewritten) - 1, -1, -1):
|
|
149
189
|
message = rewritten[index]
|
|
@@ -639,7 +679,11 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
639
679
|
on_event: ConversationCallbackType,
|
|
640
680
|
on_token: ConversationTokenCallbackType | None = None,
|
|
641
681
|
) -> None:
|
|
642
|
-
|
|
682
|
+
call_context = conversation.get_llm_call_context()
|
|
683
|
+
with (
|
|
684
|
+
llm_call_context_scope(call_context),
|
|
685
|
+
StreamContext.open(conversation, on_token) as stream,
|
|
686
|
+
):
|
|
643
687
|
self._step(conversation, on_event, stream)
|
|
644
688
|
|
|
645
689
|
def _step(
|
|
@@ -849,7 +893,11 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
849
893
|
parallel calls with :func:`asyncio.gather`, keeping the event
|
|
850
894
|
loop responsive during blocking tool I/O.
|
|
851
895
|
"""
|
|
852
|
-
|
|
896
|
+
call_context = conversation.get_llm_call_context()
|
|
897
|
+
with (
|
|
898
|
+
llm_call_context_scope(call_context),
|
|
899
|
+
StreamContext.open(conversation, on_token) as stream,
|
|
900
|
+
):
|
|
853
901
|
await self._astep(conversation, on_event, stream)
|
|
854
902
|
|
|
855
903
|
async def _astep(
|
|
@@ -1022,6 +1070,18 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
1022
1070
|
|
|
1023
1071
|
message: Message = llm_response.message
|
|
1024
1072
|
response_type = classify_response(message)
|
|
1073
|
+
if response_type is not LLMResponseType.TOOL_CALLS:
|
|
1074
|
+
# Resolve outside the event loop and state lock. A lookup may call
|
|
1075
|
+
# this server, and update_secrets() may register another source while
|
|
1076
|
+
# we await it. Repeat until the registry is stable under the lock.
|
|
1077
|
+
while True:
|
|
1078
|
+
sources = dict(state.secret_registry.secret_sources)
|
|
1079
|
+
async with conversation._released_state_lock_during_io():
|
|
1080
|
+
message = await asyncio.to_thread(
|
|
1081
|
+
self._mask_secrets, message, conversation
|
|
1082
|
+
)
|
|
1083
|
+
if sources == state.secret_registry.secret_sources:
|
|
1084
|
+
break
|
|
1025
1085
|
|
|
1026
1086
|
match response_type:
|
|
1027
1087
|
case LLMResponseType.TOOL_CALLS:
|
|
@@ -1030,7 +1090,13 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
1030
1090
|
)
|
|
1031
1091
|
case LLMResponseType.CONTENT:
|
|
1032
1092
|
self._handle_content_response(
|
|
1033
|
-
message,
|
|
1093
|
+
message,
|
|
1094
|
+
llm_response,
|
|
1095
|
+
conversation,
|
|
1096
|
+
state,
|
|
1097
|
+
on_event,
|
|
1098
|
+
stream,
|
|
1099
|
+
mask_secrets=False,
|
|
1034
1100
|
)
|
|
1035
1101
|
case LLMResponseType.REASONING_ONLY | LLMResponseType.EMPTY:
|
|
1036
1102
|
self._handle_no_content_response(
|
|
@@ -1041,6 +1107,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
1041
1107
|
on_event,
|
|
1042
1108
|
stream,
|
|
1043
1109
|
response_type=response_type,
|
|
1110
|
+
mask_secrets=False,
|
|
1044
1111
|
)
|
|
1045
1112
|
|
|
1046
1113
|
def _requires_user_confirmation(
|
|
@@ -253,9 +253,18 @@ class ResponseDispatchMixin:
|
|
|
253
253
|
state: ConversationState,
|
|
254
254
|
on_event: ConversationCallbackType,
|
|
255
255
|
stream: StreamContext | None = None,
|
|
256
|
+
*,
|
|
257
|
+
mask_secrets: bool = True,
|
|
256
258
|
) -> None:
|
|
257
259
|
"""Handle LLM response with text content — finishes conversation."""
|
|
258
|
-
self._emit_message_event(
|
|
260
|
+
self._emit_message_event(
|
|
261
|
+
message,
|
|
262
|
+
llm_response,
|
|
263
|
+
conversation,
|
|
264
|
+
on_event,
|
|
265
|
+
stream,
|
|
266
|
+
mask_secrets=mask_secrets,
|
|
267
|
+
)
|
|
259
268
|
self._maybe_emit_vllm_tokens(llm_response, on_event)
|
|
260
269
|
logger.debug("LLM produced a message response - awaits user input")
|
|
261
270
|
state.execution_status = ConversationExecutionStatus.FINISHED
|
|
@@ -270,6 +279,7 @@ class ResponseDispatchMixin:
|
|
|
270
279
|
stream: StreamContext | None = None,
|
|
271
280
|
*,
|
|
272
281
|
response_type: LLMResponseType,
|
|
282
|
+
mask_secrets: bool = True,
|
|
273
283
|
) -> None:
|
|
274
284
|
"""Handle LLM response with no user-facing content.
|
|
275
285
|
|
|
@@ -279,7 +289,14 @@ class ResponseDispatchMixin:
|
|
|
279
289
|
"""
|
|
280
290
|
if response_type is LLMResponseType.EMPTY:
|
|
281
291
|
logger.warning("LLM produced empty response - continuing agent loop")
|
|
282
|
-
self._emit_message_event(
|
|
292
|
+
self._emit_message_event(
|
|
293
|
+
message,
|
|
294
|
+
llm_response,
|
|
295
|
+
conversation,
|
|
296
|
+
on_event,
|
|
297
|
+
stream,
|
|
298
|
+
mask_secrets=mask_secrets,
|
|
299
|
+
)
|
|
283
300
|
self._maybe_emit_vllm_tokens(llm_response, on_event)
|
|
284
301
|
self._send_corrective_nudge(on_event)
|
|
285
302
|
|
|
@@ -290,6 +307,8 @@ class ResponseDispatchMixin:
|
|
|
290
307
|
conversation: LocalConversation,
|
|
291
308
|
on_event: ConversationCallbackType,
|
|
292
309
|
stream: StreamContext | None = None,
|
|
310
|
+
*,
|
|
311
|
+
mask_secrets: bool = True,
|
|
293
312
|
) -> MessageEvent:
|
|
294
313
|
"""Create and emit a MessageEvent, running critic if configured.
|
|
295
314
|
|
|
@@ -302,7 +321,9 @@ class ResponseDispatchMixin:
|
|
|
302
321
|
msg_event = MessageEvent(
|
|
303
322
|
**minted,
|
|
304
323
|
source="agent",
|
|
305
|
-
llm_message=self._mask_secrets(message, conversation)
|
|
324
|
+
llm_message=self._mask_secrets(message, conversation)
|
|
325
|
+
if mask_secrets
|
|
326
|
+
else message,
|
|
306
327
|
llm_response_id=llm_response.id,
|
|
307
328
|
)
|
|
308
329
|
if self.critic is not None and self.critic.mode == "finish_and_message":
|
|
@@ -18,6 +18,7 @@ from openhands.sdk.context.prompts import render_template
|
|
|
18
18
|
from openhands.sdk.context.view import View
|
|
19
19
|
from openhands.sdk.event.base import LLMConvertibleEvent
|
|
20
20
|
from openhands.sdk.event.condenser import Condensation
|
|
21
|
+
from openhands.sdk.event.llm_convertible import SystemPromptEvent
|
|
21
22
|
from openhands.sdk.llm import LLM, Message, TextContent
|
|
22
23
|
from openhands.sdk.logger import get_logger
|
|
23
24
|
from openhands.sdk.observability.laminar import observe
|
|
@@ -27,6 +28,21 @@ from openhands.sdk.utils import maybe_truncate
|
|
|
27
28
|
logger = get_logger(__name__)
|
|
28
29
|
|
|
29
30
|
|
|
31
|
+
def _leading_system_prompt_index(events: Sequence[LLMConvertibleEvent]) -> int | None:
|
|
32
|
+
"""Index of the first ``SystemPromptEvent`` in ``events``, or ``None``.
|
|
33
|
+
|
|
34
|
+
The agent loop guarantees the ``SystemPromptEvent`` sits at the head of the
|
|
35
|
+
view (index 0), so condensation must never forget it -- otherwise the
|
|
36
|
+
condensed view would open with a non-system message and violate the
|
|
37
|
+
repo-wide "system before first user" invariant. Returns ``None`` when no
|
|
38
|
+
system prompt is present (e.g. unit-test views built without one).
|
|
39
|
+
"""
|
|
40
|
+
for i, event in enumerate(events):
|
|
41
|
+
if isinstance(event, SystemPromptEvent):
|
|
42
|
+
return i
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
|
|
30
46
|
class Reason(Enum):
|
|
31
47
|
"""Reasons for condensation."""
|
|
32
48
|
|
|
@@ -186,6 +202,25 @@ class LLMSummarizingCondenser(RollingCondenser):
|
|
|
186
202
|
if Reason.REQUEST in reasons:
|
|
187
203
|
return CondensationRequirement.HARD
|
|
188
204
|
|
|
205
|
+
def _build_summary_messages(self, event_strings: Sequence[str]) -> list[Message]:
|
|
206
|
+
"""Build the messages sent to the summarization LLM.
|
|
207
|
+
|
|
208
|
+
The summarization instructions are sent as a ``system`` message and the
|
|
209
|
+
events to summarize as a ``user`` message. Splitting roles keeps the
|
|
210
|
+
steering instructions in the provider's ``instructions``/``system`` slot
|
|
211
|
+
and the event payload in the ``input``/``user`` slot, which is the
|
|
212
|
+
canonical shape for both the Chat Completions and Responses APIs.
|
|
213
|
+
"""
|
|
214
|
+
prompt_dir = os.path.join(os.path.dirname(__file__), "prompts")
|
|
215
|
+
system_prompt = render_template(prompt_dir, "summarizing_system.j2")
|
|
216
|
+
events_prompt = render_template(
|
|
217
|
+
prompt_dir, "summarizing_events.j2", events=event_strings
|
|
218
|
+
)
|
|
219
|
+
return [
|
|
220
|
+
Message(role="system", content=[TextContent(text=system_prompt)]),
|
|
221
|
+
Message(role="user", content=[TextContent(text=events_prompt)]),
|
|
222
|
+
]
|
|
223
|
+
|
|
189
224
|
def _generate_condensation(
|
|
190
225
|
self,
|
|
191
226
|
forgotten_events: Sequence[LLMConvertibleEvent],
|
|
@@ -215,13 +250,7 @@ class LLMSummarizingCondenser(RollingCondenser):
|
|
|
215
250
|
for forgotten_event in forgotten_events
|
|
216
251
|
]
|
|
217
252
|
|
|
218
|
-
|
|
219
|
-
os.path.join(os.path.dirname(__file__), "prompts"),
|
|
220
|
-
"summarizing_prompt.j2",
|
|
221
|
-
events=event_strings,
|
|
222
|
-
)
|
|
223
|
-
|
|
224
|
-
messages = [Message(role="user", content=[TextContent(text=prompt)])]
|
|
253
|
+
messages = self._build_summary_messages(event_strings)
|
|
225
254
|
|
|
226
255
|
# Do not pass extra_body explicitly. The LLM handles forwarding
|
|
227
256
|
# litellm_extra_body only when it is non-empty.
|
|
@@ -302,8 +331,16 @@ class LLMSummarizingCondenser(RollingCondenser):
|
|
|
302
331
|
# Calculate naive forgetting end (without considering atomic boundaries)
|
|
303
332
|
naive_end = len(view) - events_from_tail
|
|
304
333
|
|
|
305
|
-
#
|
|
306
|
-
|
|
334
|
+
# The leading SystemPromptEvent must never be forgotten, otherwise the
|
|
335
|
+
# condensed view would open with a non-system message. Floor the start
|
|
336
|
+
# of the forgetting range just past it (this also covers keep_first=0).
|
|
337
|
+
protected_prefix = self.keep_first
|
|
338
|
+
system_idx = _leading_system_prompt_index(view.events)
|
|
339
|
+
if system_idx is not None:
|
|
340
|
+
protected_prefix = max(protected_prefix, system_idx + 1)
|
|
341
|
+
|
|
342
|
+
# Find actual forgetting_start: smallest manipulation index >= protected_prefix
|
|
343
|
+
forgetting_start = view.manipulation_indices.find_next(protected_prefix)
|
|
307
344
|
|
|
308
345
|
# Find actual forgetting_end: smallest manipulation index >= naive_end
|
|
309
346
|
forgetting_end = view.manipulation_indices.find_next(naive_end)
|
|
@@ -326,14 +363,22 @@ class LLMSummarizingCondenser(RollingCondenser):
|
|
|
326
363
|
the view is too large for the summarizing LLM to handle). In that case, we keep
|
|
327
364
|
trimming down the contents until a summary can be generated.
|
|
328
365
|
"""
|
|
366
|
+
# Preserve the leading SystemPromptEvent: it must stay at the head of the
|
|
367
|
+
# view so the request still opens with a system message. Summarize the
|
|
368
|
+
# events *after* it and insert the summary right behind it.
|
|
369
|
+
system_idx = _leading_system_prompt_index(view.events)
|
|
370
|
+
preserve = (system_idx + 1) if system_idx is not None else 0
|
|
371
|
+
forgotten_events = view.events[preserve:]
|
|
372
|
+
summary_offset = preserve
|
|
373
|
+
|
|
329
374
|
max_event_str_length: int | None = None
|
|
330
375
|
attempts_remaining: int = self.hard_context_reset_max_retries
|
|
331
376
|
|
|
332
377
|
while attempts_remaining > 0:
|
|
333
378
|
try:
|
|
334
379
|
return self._generate_condensation(
|
|
335
|
-
forgotten_events=
|
|
336
|
-
summary_offset=
|
|
380
|
+
forgotten_events=forgotten_events,
|
|
381
|
+
summary_offset=summary_offset,
|
|
337
382
|
max_event_str_length=max_event_str_length,
|
|
338
383
|
)
|
|
339
384
|
except Exception as e:
|
|
@@ -411,13 +456,7 @@ class LLMSummarizingCondenser(RollingCondenser):
|
|
|
411
456
|
for fe in forgotten_events
|
|
412
457
|
]
|
|
413
458
|
|
|
414
|
-
|
|
415
|
-
os.path.join(os.path.dirname(__file__), "prompts"),
|
|
416
|
-
"summarizing_prompt.j2",
|
|
417
|
-
events=event_strings,
|
|
418
|
-
)
|
|
419
|
-
|
|
420
|
-
messages = [Message(role="user", content=[TextContent(text=prompt)])]
|
|
459
|
+
messages = self._build_summary_messages(event_strings)
|
|
421
460
|
|
|
422
461
|
try:
|
|
423
462
|
llm_response = await self.llm.agenerate(messages=messages, store=False)
|
|
@@ -477,14 +516,20 @@ class LLMSummarizingCondenser(RollingCondenser):
|
|
|
477
516
|
agent_llm: LLM | None = None, # noqa: ARG002
|
|
478
517
|
) -> Condensation | None:
|
|
479
518
|
"""Async variant of :meth:`hard_context_reset`."""
|
|
519
|
+
# Preserve the leading SystemPromptEvent (see hard_context_reset).
|
|
520
|
+
system_idx = _leading_system_prompt_index(view.events)
|
|
521
|
+
preserve = (system_idx + 1) if system_idx is not None else 0
|
|
522
|
+
forgotten_events = view.events[preserve:]
|
|
523
|
+
summary_offset = preserve
|
|
524
|
+
|
|
480
525
|
max_event_str_length: int | None = None
|
|
481
526
|
attempts_remaining: int = self.hard_context_reset_max_retries
|
|
482
527
|
|
|
483
528
|
while attempts_remaining > 0:
|
|
484
529
|
try:
|
|
485
530
|
return await self._agenerate_condensation(
|
|
486
|
-
forgotten_events=
|
|
487
|
-
summary_offset=
|
|
531
|
+
forgotten_events=forgotten_events,
|
|
532
|
+
summary_offset=summary_offset,
|
|
488
533
|
max_event_str_length=max_event_str_length,
|
|
489
534
|
)
|
|
490
535
|
except Exception as e:
|
|
@@ -45,11 +45,3 @@ USER_CONTEXT: Write 20 haikus based on coin flip results
|
|
|
45
45
|
COMPLETED: 15 haikus written for results [T,H,T,H,T,H,T,T,H,T,H,T,H,T,H]
|
|
46
46
|
PENDING: 5 more haikus needed
|
|
47
47
|
CURRENT_STATE: Last flip: Heads, Haiku count: 15/20
|
|
48
|
-
|
|
49
|
-
{% for event in events %}
|
|
50
|
-
<EVENT>
|
|
51
|
-
{{ event }}
|
|
52
|
-
</EVENT>
|
|
53
|
-
{% endfor %}
|
|
54
|
-
|
|
55
|
-
Now summarize the events using the rules above.
|
|
@@ -14,7 +14,10 @@ from typing import Any
|
|
|
14
14
|
|
|
15
15
|
from pydantic import BaseModel, Field
|
|
16
16
|
|
|
17
|
-
from openhands.sdk.conversation.goal.prompts import
|
|
17
|
+
from openhands.sdk.conversation.goal.prompts import (
|
|
18
|
+
JUDGE_SYSTEM_PROMPT,
|
|
19
|
+
JUDGE_USER_PROMPT,
|
|
20
|
+
)
|
|
18
21
|
from openhands.sdk.event import Event, LLMConvertibleEvent
|
|
19
22
|
from openhands.sdk.llm import LLM, Message, TextContent, content_to_str
|
|
20
23
|
from openhands.sdk.logger import get_logger
|
|
@@ -55,15 +58,21 @@ def judge_goal(judge_llm: LLM, objective: str, events: Sequence[Event]) -> GoalV
|
|
|
55
58
|
"""
|
|
56
59
|
convertible = [e for e in events if isinstance(e, LLMConvertibleEvent)]
|
|
57
60
|
transcript = _render_transcript(convertible)
|
|
58
|
-
|
|
61
|
+
system_prompt = JUDGE_SYSTEM_PROMPT
|
|
62
|
+
user_prompt = JUDGE_USER_PROMPT.format(objective=objective, transcript=transcript)
|
|
59
63
|
|
|
60
64
|
# The judge only needs the verdict text. Force non-streaming so reusing a
|
|
61
65
|
# streaming agent LLM as the judge does not trip completion()'s requirement
|
|
62
66
|
# of an on_token callback when stream=True.
|
|
63
67
|
if judge_llm.stream:
|
|
64
68
|
judge_llm = judge_llm.model_copy(update={"stream": False})
|
|
69
|
+
# Build a canonical system + user pair: steering/format instructions live in
|
|
70
|
+
# the system message, the objective + transcript payload in the user message.
|
|
65
71
|
response = judge_llm.completion(
|
|
66
|
-
messages=[
|
|
72
|
+
messages=[
|
|
73
|
+
Message(role="system", content=[TextContent(text=system_prompt)]),
|
|
74
|
+
Message(role="user", content=[TextContent(text=user_prompt)]),
|
|
75
|
+
]
|
|
67
76
|
)
|
|
68
77
|
verdict = _parse_verdict(response.message)
|
|
69
78
|
logger.debug("judge_goal verdict: %s", verdict)
|
|
@@ -3,26 +3,44 @@
|
|
|
3
3
|
from typing import Final
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
been COMPLETED by an AI
|
|
8
|
-
|
|
9
|
-
<objective>
|
|
10
|
-
{objective}
|
|
11
|
-
</objective>
|
|
6
|
+
JUDGE_SYSTEM_PROMPT: Final[str] = """\
|
|
7
|
+
You are auditing whether a long-running GOAL has been COMPLETED by an AI \
|
|
8
|
+
software agent.
|
|
12
9
|
|
|
13
10
|
Derive the concrete requirements implied by the objective. For EACH requirement,
|
|
14
|
-
look for authoritative evidence in the transcript
|
|
11
|
+
look for authoritative evidence in the transcript: file contents, command
|
|
15
12
|
output, or test results produced by the agent. Treat missing, uncertain, or
|
|
16
13
|
merely-claimed-but-unverified evidence as NOT satisfied.
|
|
17
14
|
|
|
15
|
+
Respond with STRICT JSONand nothing else, in exactly this shape:
|
|
16
|
+
{"score": <float 0.0-1.0, probability the FULL objective is provably done>, \
|
|
17
|
+
"complete": <true|false>, "missing": "<concise description of what remains, or \
|
|
18
|
+
an empty string if complete>"}"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
JUDGE_USER_PROMPT: Final[str] = """\
|
|
22
|
+
<objective>
|
|
23
|
+
{objective}
|
|
24
|
+
</objective>
|
|
25
|
+
|
|
18
26
|
<transcript>
|
|
19
27
|
{transcript}
|
|
20
|
-
</transcript>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
|
|
28
|
+
</transcript>"""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
JUDGE_PROMPT: Final[str] = (
|
|
32
|
+
JUDGE_SYSTEM_PROMPT.replace("{", "{{").replace("}", "}}")
|
|
33
|
+
+ "\n\n"
|
|
34
|
+
+ JUDGE_USER_PROMPT
|
|
35
|
+
)
|
|
36
|
+
"""Backwards-compatible concatenation of the system steering prompt and the user
|
|
37
|
+
payload template. Kept so external callers that still format a single prompt
|
|
38
|
+
continue to work; new code should use ``JUDGE_SYSTEM_PROMPT`` and
|
|
39
|
+
``JUDGE_USER_PROMPT`` separately to build a ``system + user`` message pair.
|
|
40
|
+
|
|
41
|
+
JUDGE_SYSTEM_PROMPT carries the raw, un-escaped JSON shape because it is sent
|
|
42
|
+
unformatted ins ``judge_goal``; ``JUDGE_PROMPT`` re-escapes it so the combined
|
|
43
|
+
template survives ``.format(objective=..., transcript=...)``."""
|
|
26
44
|
|
|
27
45
|
|
|
28
46
|
FOLLOWUP_PROMPT: Final[str] = """The goal is NOT yet complete (audit iteration \
|