openhands-sdk 1.49.4__tar.gz → 1.49.6__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.4 → openhands_sdk-1.49.6}/PKG-INFO +1 -1
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/view/properties/tool_call_matching.py +4 -9
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/impl/local_conversation.py +31 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/secret_registry.py +64 -10
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/extensions/installation/metadata.py +2 -2
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/hooks/executor.py +4 -3
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/__init__.py +10 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/llm.py +150 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/message.py +3 -2
- openhands_sdk-1.49.6/openhands/sdk/llm/meta_profile_store.py +274 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/model_features.py +6 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/telemetry.py +127 -126
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/verified_models.py +7 -3
- openhands_sdk-1.49.6/openhands/sdk/mcp/oauth.py +85 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/mcp/tool.py +60 -2
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/mcp/utils.py +3 -2
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/settings/api_models.py +9 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/settings/model.py +66 -1
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/tool/builtins/__init__.py +11 -0
- openhands_sdk-1.49.6/openhands/sdk/tool/builtins/classify_and_switch_llm.py +503 -0
- openhands_sdk-1.49.6/openhands/sdk/utils/masking.py +13 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands_sdk.egg-info/PKG-INFO +1 -1
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands_sdk.egg-info/SOURCES.txt +8 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/pyproject.toml +1 -1
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/agent/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/agent/acp_agent.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/agent/acp_file_credentials.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/agent/acp_models.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/agent/acp_tracing.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/agent/agent.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/agent/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/agent/critic_mixin.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/agent/parallel_executor.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/agent/response_dispatch.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/agent/stream_context.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/agent/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/banner.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/agent_context.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/condenser/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/condenser/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/condenser/llm_summarizing_condenser.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/condenser/no_op_condenser.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/condenser/pipeline_condenser.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/condenser/prompts/summarizing_prompt.j2 +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/condenser/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/memory.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/prompts/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/prompts/presets.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/prompts/prompt.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/prompts/registry.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/prompts/section.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/prompts/sections/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/prompts/sections/dynamic.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/prompts/sections/planning.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/prompts/sections/static.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/prompts/templates/ask_agent_template.j2 +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/view/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/view/manipulation_indices.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/view/properties/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/view/properties/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/view/properties/batch_atomicity.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/view/properties/observation_uniqueness.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/view/properties/tool_loop_atomicity.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/context/view/view.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/cancellation.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/conversation.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/conversation_stats.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/event_store.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/events_list_base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/exceptions.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/fifo_lock.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/goal/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/goal/controller.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/goal/judge.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/goal/prompts.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/goal/runner.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/impl/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/impl/remote_conversation.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/persistence_const.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/request.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/resource_lock_manager.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/response_utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/state.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/stuck_detector.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/title_utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/types.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/visualizer/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/visualizer/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/visualizer/default.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/credential.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/critic/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/critic/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/critic/impl/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/critic/impl/agent_finished.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/critic/impl/api/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/critic/impl/api/chat_template.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/critic/impl/api/client.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/critic/impl/api/critic.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/critic/impl/api/taxonomy.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/critic/impl/empty_patch.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/critic/impl/pass_critic.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/critic/result.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/acp_tool_call.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/condenser.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/conversation_error.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/conversation_state.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/error_classification.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/hook_execution.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/llm_completion_log.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/llm_convertible/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/llm_convertible/action.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/llm_convertible/message.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/llm_convertible/observation.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/llm_convertible/reasoning_utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/llm_convertible/system.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/resume_transcript.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/streaming_delta.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/token.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/types.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/event/user_action.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/extensions/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/extensions/fetch.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/extensions/installation/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/extensions/installation/info.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/extensions/installation/interface.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/extensions/installation/manager.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/extensions/installation/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/git/cached_repo.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/git/exceptions.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/git/git_changes.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/git/git_commits.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/git/git_diff.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/git/models.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/git/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/hooks/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/hooks/config.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/hooks/conversation_hooks.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/hooks/manager.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/hooks/types.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/io/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/io/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/io/cache.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/io/local.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/io/memory.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/auth/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/auth/credentials.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/auth/openai.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/cleanup_profile.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/exceptions/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/exceptions/classifier.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/exceptions/mapping.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/exceptions/types.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/fallback_strategy.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/llm_profile_store.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/llm_registry.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/llm_response.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/mixins/fn_call_converter.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/mixins/fn_call_examples.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/mixins/non_native_fc.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/options/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/options/chat_options.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/options/common.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/options/responses_options.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/provider_connection_store.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/router/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/router/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/router/impl/multimodal.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/router/impl/random.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/streaming.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/image_inline.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/image_resize.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/litellm_provider.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/metrics.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/model_info.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/model_prompt_spec.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/openhands_provider.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/providers/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/providers/openrouter.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/responses_serialization.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/retry_mixin.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/runtime_metadata.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/unverified_models.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/llm/utils/vertex_preflight.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/logger/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/logger/logger.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/logger/rolling.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/marketplace/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/marketplace/registration.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/marketplace/registry.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/marketplace/types.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/mcp/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/mcp/client.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/mcp/config.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/mcp/definition.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/mcp/exceptions.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/observability/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/observability/laminar.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/observability/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/plugin/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/plugin/discovery.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/plugin/fetch.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/plugin/format/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/plugin/format/agent_plugins.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/plugin/format/agent_plugins_mcp.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/plugin/format/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/plugin/format/claude_code.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/plugin/format/schemas/mcp-1.0.0.schema.json +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/plugin/format/schemas/plugin-1.0.0.schema.json +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/plugin/installed.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/plugin/loader.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/plugin/plugin.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/plugin/source.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/plugin/types.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/profiles/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/profiles/agent_profile.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/profiles/agent_profile_store.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/profiles/profile_refs.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/profiles/resolver.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/profiles/seed.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/py.typed +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/secret/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/secret/secrets.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/_shell_ast.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/analyzer.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/confirmation_policy.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/defense_in_depth/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/defense_in_depth/pattern.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/defense_in_depth/policy_rails.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/defense_in_depth/shell_semantics.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/defense_in_depth/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/ensemble.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/grayswan/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/grayswan/analyzer.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/grayswan/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/llm_analyzer.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/risk.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/shell_parser.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/toolshield_helpers.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/security/toolshield_llm_analyzer.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/settings/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/settings/acp_install_catalog.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/settings/acp_providers.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/settings/metadata.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/skills/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/skills/exceptions.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/skills/execute.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/skills/fetch.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/skills/installed.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/skills/skill.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/skills/trigger.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/skills/types.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/skills/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/subagent/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/subagent/load.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/subagent/registry.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/subagent/schema.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/testing/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/testing/test_llm.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/tool/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/tool/builtins/finish.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/tool/builtins/invoke_skill.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/tool/builtins/switch_llm.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/tool/builtins/think.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/tool/builtins/vision_inspect.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/tool/client_tool.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/tool/defaults.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/tool/registry.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/tool/schema.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/tool/spec.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/tool/tool.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/async_executor.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/async_utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/cipher.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/command.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/datetime.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/deprecation.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/files.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/github.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/json.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/models.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/paging.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/path.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/pydantic_secrets.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/redact.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/truncate.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/utils/visualize.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/workspace/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/workspace/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/workspace/local.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/workspace/models.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/workspace/remote/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/workspace/remote/async_remote_workspace.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/workspace/remote/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/workspace/remote/remote_workspace_mixin.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/workspace/repo.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/workspace/workspace.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands_sdk.egg-info/dependency_links.txt +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands_sdk.egg-info/requires.txt +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands_sdk.egg-info/top_level.txt +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-sdk
|
|
3
|
-
Version: 1.49.
|
|
3
|
+
Version: 1.49.6
|
|
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
|
|
@@ -64,8 +64,9 @@ class ToolCallMatchingProperty(ViewPropertyBase):
|
|
|
64
64
|
) -> ManipulationIndices:
|
|
65
65
|
"""Calculate manipulation indices for tool call matching.
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
Indices between an action and its matching observation are protected.
|
|
68
|
+
Observation-like events without a pending action do not affect boundaries;
|
|
69
|
+
property enforcement separately removes orphaned and duplicate observations.
|
|
69
70
|
"""
|
|
70
71
|
# Start with a complete set of manipulation indices, then we'll remove those
|
|
71
72
|
# between actions and their paired observations.
|
|
@@ -84,13 +85,7 @@ class ToolCallMatchingProperty(ViewPropertyBase):
|
|
|
84
85
|
case ActionEvent():
|
|
85
86
|
pending_tool_call_ids.add(event.tool_call_id)
|
|
86
87
|
case ObservationBaseEvent():
|
|
87
|
-
|
|
88
|
-
# observation-like event for the same tool_call_id means the
|
|
89
|
-
# view has already violated the 1 action -> 1 result
|
|
90
|
-
# invariant that downstream LLM APIs expect. That case must
|
|
91
|
-
# be fixed by de-duplicating the view before serialization,
|
|
92
|
-
# not by silently tolerating it here.
|
|
93
|
-
pending_tool_call_ids.remove(event.tool_call_id)
|
|
88
|
+
pending_tool_call_ids.discard(event.tool_call_id)
|
|
94
89
|
|
|
95
90
|
if pending_tool_call_ids:
|
|
96
91
|
# The enumeration index corresponds to the position of the event, but we
|
{openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/conversation/impl/local_conversation.py
RENAMED
|
@@ -55,6 +55,7 @@ from openhands.sdk.hooks import HookConfig, HookEventProcessor, create_hook_call
|
|
|
55
55
|
from openhands.sdk.io import FileStore, LocalFileStore
|
|
56
56
|
from openhands.sdk.llm import LLM, Message, TextContent, content_to_str
|
|
57
57
|
from openhands.sdk.llm.auth.openai import create_subscription_llm_from_config
|
|
58
|
+
from openhands.sdk.llm.exceptions import LLMAuthenticationError
|
|
58
59
|
from openhands.sdk.llm.llm import LLMCallContext
|
|
59
60
|
from openhands.sdk.llm.llm_profile_store import LLMProfileStore
|
|
60
61
|
from openhands.sdk.llm.llm_registry import LLMRegistry
|
|
@@ -2040,6 +2041,21 @@ class LocalConversation(BaseConversation):
|
|
|
2040
2041
|
)
|
|
2041
2042
|
)
|
|
2042
2043
|
break
|
|
2044
|
+
except LLMAuthenticationError as e:
|
|
2045
|
+
with self._state:
|
|
2046
|
+
self._state.execution_status = ConversationExecutionStatus.ERROR
|
|
2047
|
+
self._on_event(
|
|
2048
|
+
ConversationErrorEvent(
|
|
2049
|
+
source="environment",
|
|
2050
|
+
code="LLMAuthenticationError",
|
|
2051
|
+
detail=(
|
|
2052
|
+
"Your LLM API key appears to be invalid or has expired."
|
|
2053
|
+
),
|
|
2054
|
+
)
|
|
2055
|
+
)
|
|
2056
|
+
raise ConversationRunError(
|
|
2057
|
+
self._state.id, e, persistence_dir=self._state.persistence_dir
|
|
2058
|
+
) from e
|
|
2043
2059
|
except Exception as e:
|
|
2044
2060
|
with self._state:
|
|
2045
2061
|
self._state.execution_status = ConversationExecutionStatus.ERROR
|
|
@@ -2540,6 +2556,21 @@ class LocalConversation(BaseConversation):
|
|
|
2540
2556
|
|
|
2541
2557
|
self._state.execution_status = ConversationExecutionStatus.PAUSED
|
|
2542
2558
|
self._on_event(InterruptEvent())
|
|
2559
|
+
except LLMAuthenticationError as e:
|
|
2560
|
+
with self._state:
|
|
2561
|
+
self._state.execution_status = ConversationExecutionStatus.ERROR
|
|
2562
|
+
self._on_event(
|
|
2563
|
+
ConversationErrorEvent(
|
|
2564
|
+
source="environment",
|
|
2565
|
+
code="LLMAuthenticationError",
|
|
2566
|
+
detail=(
|
|
2567
|
+
"Your LLM API key appears to be invalid or has expired."
|
|
2568
|
+
),
|
|
2569
|
+
)
|
|
2570
|
+
)
|
|
2571
|
+
raise ConversationRunError(
|
|
2572
|
+
self._state.id, e, persistence_dir=self._state.persistence_dir
|
|
2573
|
+
) from e
|
|
2543
2574
|
except Exception as e:
|
|
2544
2575
|
with self._state:
|
|
2545
2576
|
updated_agent_state = dict(self._state.agent_state)
|
|
@@ -11,6 +11,7 @@ from pydantic import BaseModel, Field, PrivateAttr, SecretStr
|
|
|
11
11
|
|
|
12
12
|
from openhands.sdk.logger import get_logger
|
|
13
13
|
from openhands.sdk.secret import SecretSource, SecretValue, StaticSecret
|
|
14
|
+
from openhands.sdk.utils.masking import PreserveDataUrls, SkipSecretMasking
|
|
14
15
|
from openhands.sdk.utils.models import OpenHandsModel
|
|
15
16
|
|
|
16
17
|
|
|
@@ -20,8 +21,14 @@ logger = get_logger(__name__)
|
|
|
20
21
|
FAILED_LOOKUP_RETRY_SECONDS: Final[float] = 60.0
|
|
21
22
|
|
|
22
23
|
|
|
23
|
-
def _mask_value(
|
|
24
|
-
|
|
24
|
+
def _mask_value(
|
|
25
|
+
value: Any,
|
|
26
|
+
mask: Callable[[str], str],
|
|
27
|
+
*,
|
|
28
|
+
skip_secret_masking: bool = False,
|
|
29
|
+
preserve_data_urls: bool = False,
|
|
30
|
+
) -> Any:
|
|
31
|
+
"""Recursively mask strings, respecting opaque payload field metadata."""
|
|
25
32
|
match value:
|
|
26
33
|
case Enum():
|
|
27
34
|
# A str-subclass enum is a str, but masking it would downgrade the
|
|
@@ -29,20 +36,56 @@ def _mask_value(value: Any, mask: Callable[[str], str]) -> Any:
|
|
|
29
36
|
# vocabulary is fixed, so it can never hold a secret anyway.
|
|
30
37
|
return value
|
|
31
38
|
case str():
|
|
39
|
+
if skip_secret_masking:
|
|
40
|
+
return value
|
|
41
|
+
if preserve_data_urls and value.startswith("data:") and ";base64," in value:
|
|
42
|
+
return value
|
|
32
43
|
return mask(value)
|
|
33
44
|
case BaseModel():
|
|
34
45
|
return _mask_model(value, mask)
|
|
35
46
|
case list():
|
|
36
|
-
return [
|
|
47
|
+
return [
|
|
48
|
+
_mask_value(
|
|
49
|
+
item,
|
|
50
|
+
mask,
|
|
51
|
+
skip_secret_masking=skip_secret_masking,
|
|
52
|
+
preserve_data_urls=preserve_data_urls,
|
|
53
|
+
)
|
|
54
|
+
for item in value
|
|
55
|
+
]
|
|
37
56
|
case tuple():
|
|
38
|
-
items = [
|
|
57
|
+
items = [
|
|
58
|
+
_mask_value(
|
|
59
|
+
item,
|
|
60
|
+
mask,
|
|
61
|
+
skip_secret_masking=skip_secret_masking,
|
|
62
|
+
preserve_data_urls=preserve_data_urls,
|
|
63
|
+
)
|
|
64
|
+
for item in value
|
|
65
|
+
]
|
|
39
66
|
# Rebuild a NamedTuple through its own constructor; tuple(items)
|
|
40
67
|
# would downgrade it the same way masking an Enum member does.
|
|
41
68
|
return type(value)(*items) if hasattr(value, "_fields") else tuple(items)
|
|
42
69
|
case set() | frozenset():
|
|
43
|
-
return type(value)(
|
|
70
|
+
return type(value)(
|
|
71
|
+
_mask_value(
|
|
72
|
+
item,
|
|
73
|
+
mask,
|
|
74
|
+
skip_secret_masking=skip_secret_masking,
|
|
75
|
+
preserve_data_urls=preserve_data_urls,
|
|
76
|
+
)
|
|
77
|
+
for item in value
|
|
78
|
+
)
|
|
44
79
|
case dict():
|
|
45
|
-
return {
|
|
80
|
+
return {
|
|
81
|
+
key: _mask_value(
|
|
82
|
+
item,
|
|
83
|
+
mask,
|
|
84
|
+
skip_secret_masking=skip_secret_masking,
|
|
85
|
+
preserve_data_urls=preserve_data_urls,
|
|
86
|
+
)
|
|
87
|
+
for key, item in value.items()
|
|
88
|
+
}
|
|
46
89
|
case _:
|
|
47
90
|
return value
|
|
48
91
|
|
|
@@ -53,10 +96,21 @@ def _mask_model[ModelT: BaseModel](model: ModelT, mask: Callable[[str], str]) ->
|
|
|
53
96
|
``model_copy`` is used rather than a validate round-trip so private
|
|
54
97
|
attributes survive and no field is re-coerced.
|
|
55
98
|
"""
|
|
56
|
-
updates = {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
99
|
+
updates = {}
|
|
100
|
+
for name, field in type(model).model_fields.items():
|
|
101
|
+
metadata = field.metadata
|
|
102
|
+
skip_secret_masking = any(
|
|
103
|
+
isinstance(item, SkipSecretMasking) for item in metadata
|
|
104
|
+
)
|
|
105
|
+
preserve_data_urls = any(
|
|
106
|
+
isinstance(item, PreserveDataUrls) for item in metadata
|
|
107
|
+
)
|
|
108
|
+
updates[name] = _mask_value(
|
|
109
|
+
getattr(model, name),
|
|
110
|
+
mask,
|
|
111
|
+
skip_secret_masking=skip_secret_masking,
|
|
112
|
+
preserve_data_urls=preserve_data_urls,
|
|
113
|
+
)
|
|
60
114
|
return model.model_copy(update=updates)
|
|
61
115
|
|
|
62
116
|
|
{openhands_sdk-1.49.4 → openhands_sdk-1.49.6}/openhands/sdk/extensions/installation/metadata.py
RENAMED
|
@@ -150,7 +150,7 @@ class InstallationMetadata(BaseModel):
|
|
|
150
150
|
return cls()
|
|
151
151
|
|
|
152
152
|
try:
|
|
153
|
-
return cls.model_validate_json(metadata_path.read_text())
|
|
153
|
+
return cls.model_validate_json(metadata_path.read_text(encoding="utf-8"))
|
|
154
154
|
except Exception as e:
|
|
155
155
|
logger.warning(f"Failed to load installed extension metadata: {e}")
|
|
156
156
|
return cls()
|
|
@@ -159,7 +159,7 @@ class InstallationMetadata(BaseModel):
|
|
|
159
159
|
"""Save metadata to the installed extensions directory."""
|
|
160
160
|
metadata_path = self.get_metadata_path(installed_dir)
|
|
161
161
|
metadata_path.parent.mkdir(parents=True, exist_ok=True)
|
|
162
|
-
metadata_path.write_text(self.model_dump_json(indent=2))
|
|
162
|
+
metadata_path.write_text(self.model_dump_json(indent=2), encoding="utf-8")
|
|
163
163
|
|
|
164
164
|
def validate_tracked(self, installed_dir: Path) -> list[InstallationInfo]:
|
|
165
165
|
"""Validate tracked extensions exist on disk.
|
|
@@ -566,9 +566,10 @@ class HookExecutor:
|
|
|
566
566
|
try:
|
|
567
567
|
output_data = json.loads(result.stdout)
|
|
568
568
|
if isinstance(output_data, dict):
|
|
569
|
-
#
|
|
570
|
-
|
|
571
|
-
|
|
569
|
+
# Non-string values represent no decision.
|
|
570
|
+
decision_value = output_data.get("decision")
|
|
571
|
+
if isinstance(decision_value, str):
|
|
572
|
+
decision_str = decision_value.lower()
|
|
572
573
|
if decision_str == "allow":
|
|
573
574
|
hook_result.decision = HookDecision.ALLOW
|
|
574
575
|
elif decision_str == "deny":
|
|
@@ -28,6 +28,12 @@ from openhands.sdk.llm.message import (
|
|
|
28
28
|
ThinkingBlock,
|
|
29
29
|
content_to_str,
|
|
30
30
|
)
|
|
31
|
+
from openhands.sdk.llm.meta_profile_store import (
|
|
32
|
+
MetaProfile,
|
|
33
|
+
MetaProfileClass,
|
|
34
|
+
MetaProfileLimitExceeded,
|
|
35
|
+
MetaProfileStore,
|
|
36
|
+
)
|
|
31
37
|
from openhands.sdk.llm.router import RouterLLM
|
|
32
38
|
from openhands.sdk.llm.streaming import (
|
|
33
39
|
AsyncTokenCallbackType,
|
|
@@ -61,6 +67,10 @@ __all__ = [
|
|
|
61
67
|
"LLMProfileLoader",
|
|
62
68
|
"LLMProfileMutator",
|
|
63
69
|
"LLMProfileStore",
|
|
70
|
+
"MetaProfile",
|
|
71
|
+
"MetaProfileClass",
|
|
72
|
+
"MetaProfileLimitExceeded",
|
|
73
|
+
"MetaProfileStore",
|
|
64
74
|
"RouterLLM",
|
|
65
75
|
"RegistryEvent",
|
|
66
76
|
# Messages
|
|
@@ -96,6 +96,7 @@ from openhands.sdk.llm.exceptions import (
|
|
|
96
96
|
LLMNoResponseError,
|
|
97
97
|
is_prompt_cache_too_small,
|
|
98
98
|
is_quota_exhaustion_error,
|
|
99
|
+
looks_like_auth_error,
|
|
99
100
|
map_provider_exception,
|
|
100
101
|
)
|
|
101
102
|
|
|
@@ -661,6 +662,19 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
|
|
|
661
662
|
# a result, so the read/check and store are kept atomic. ClassVar (shared);
|
|
662
663
|
# critical sections are tiny and never cover network I/O.
|
|
663
664
|
_runtime_metadata_lock: ClassVar[threading.Lock] = threading.Lock()
|
|
665
|
+
# Optional callback that returns a freshly-resolved API key. When set, an
|
|
666
|
+
# authentication failure (HTTP 401, e.g. a rotated/healed managed proxy key
|
|
667
|
+
# that LiteLLM reports as ``token_not_found_in_db``) triggers a single
|
|
668
|
+
# re-resolve of the key followed by one retry of the call. This is a
|
|
669
|
+
# near-term mitigation for stale credentials reaching a sandbox runtime
|
|
670
|
+
# agent; the durable fix is reference-only credential delivery (#4288).
|
|
671
|
+
# Held as a PrivateAttr so it is never serialized into conversation state.
|
|
672
|
+
_api_key_refresh_hook: Callable[[], str | SecretStr | None] | None = PrivateAttr(
|
|
673
|
+
default=None
|
|
674
|
+
)
|
|
675
|
+
# Recursion guard: set on the refreshed copy so a still-failing key does not
|
|
676
|
+
# loop. One refresh + one retry per call chain.
|
|
677
|
+
_auth_refresh_attempted: bool = PrivateAttr(default=False)
|
|
664
678
|
model_config: ClassVar[ConfigDict] = ConfigDict(
|
|
665
679
|
extra="ignore", arbitrary_types_allowed=True
|
|
666
680
|
)
|
|
@@ -1011,6 +1025,72 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
|
|
|
1011
1025
|
raise mapped from error
|
|
1012
1026
|
raise
|
|
1013
1027
|
|
|
1028
|
+
# =========================================================================
|
|
1029
|
+
# Authentication refresh (refresh-on-401)
|
|
1030
|
+
# =========================================================================
|
|
1031
|
+
def set_api_key_refresh_hook(
|
|
1032
|
+
self, hook: Callable[[], str | SecretStr | None] | None
|
|
1033
|
+
) -> None:
|
|
1034
|
+
"""Register a callback that re-resolves this LLM's API key on a 401.
|
|
1035
|
+
|
|
1036
|
+
When a completion/responses call fails with an authentication error,
|
|
1037
|
+
the hook is invoked once to obtain a fresh key; if it returns a new
|
|
1038
|
+
value the call is retried a single time with that key. This mitigates
|
|
1039
|
+
stale managed/proxy credentials reaching a sandbox runtime agent (e.g.
|
|
1040
|
+
after a server-side managed-key rotation or heal), where the first call
|
|
1041
|
+
would otherwise fail with ``401 token_not_found_in_db``.
|
|
1042
|
+
|
|
1043
|
+
The hook must be non-blocking / cheap; on the async paths it is invoked
|
|
1044
|
+
in a worker thread. It is stored as a private attribute and is never
|
|
1045
|
+
serialized into conversation state.
|
|
1046
|
+
"""
|
|
1047
|
+
self._api_key_refresh_hook = hook
|
|
1048
|
+
|
|
1049
|
+
def _resolve_refreshed_api_key(self, error: Exception) -> SecretStr | None:
|
|
1050
|
+
"""Return a fresh API key to retry with, or ``None`` to skip refresh.
|
|
1051
|
+
|
|
1052
|
+
Returns ``None`` unless every condition holds: the error looks like an
|
|
1053
|
+
authentication failure, a refresh hook is configured, no refresh has
|
|
1054
|
+
already been attempted in this call chain, this LLM uses ``api_key``
|
|
1055
|
+
auth, and the hook yields a key that differs from the current one.
|
|
1056
|
+
"""
|
|
1057
|
+
if self._auth_refresh_attempted:
|
|
1058
|
+
return None
|
|
1059
|
+
if self._api_key_refresh_hook is None:
|
|
1060
|
+
return None
|
|
1061
|
+
if self.auth_type != "api_key":
|
|
1062
|
+
return None
|
|
1063
|
+
if not looks_like_auth_error(error):
|
|
1064
|
+
return None
|
|
1065
|
+
try:
|
|
1066
|
+
new_key = self._api_key_refresh_hook()
|
|
1067
|
+
except Exception:
|
|
1068
|
+
# A flaky hook must not mask the original authentication error;
|
|
1069
|
+
# log and skip the refresh so the caller sees the real 401.
|
|
1070
|
+
logger.warning(
|
|
1071
|
+
"API key refresh hook raised; skipping refresh and "
|
|
1072
|
+
"surfacing the original authentication error.",
|
|
1073
|
+
exc_info=True,
|
|
1074
|
+
)
|
|
1075
|
+
return None
|
|
1076
|
+
if new_key is None:
|
|
1077
|
+
return None
|
|
1078
|
+
new_secret = new_key if isinstance(new_key, SecretStr) else SecretStr(new_key)
|
|
1079
|
+
if not new_secret.get_secret_value():
|
|
1080
|
+
return None
|
|
1081
|
+
current = self._get_api_key_value()
|
|
1082
|
+
if current is not None and current == new_secret.get_secret_value():
|
|
1083
|
+
# A refresh that returns the same (still-rejected) key is useless;
|
|
1084
|
+
# skip the retry and surface the original error.
|
|
1085
|
+
return None
|
|
1086
|
+
return new_secret
|
|
1087
|
+
|
|
1088
|
+
def _auth_refreshed_llm(self, new_api_key: SecretStr) -> LLM:
|
|
1089
|
+
"""Copy this LLM with a refreshed key and the recursion guard set."""
|
|
1090
|
+
refreshed = self.model_copy(update={"api_key": new_api_key})
|
|
1091
|
+
refreshed._auth_refresh_attempted = True
|
|
1092
|
+
return refreshed
|
|
1093
|
+
|
|
1014
1094
|
# =========================================================================
|
|
1015
1095
|
# Shared helpers for completion / acompletion / responses / aresponses
|
|
1016
1096
|
# =========================================================================
|
|
@@ -1668,6 +1748,21 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
|
|
|
1668
1748
|
on_token=on_token,
|
|
1669
1749
|
**_caller_kwargs,
|
|
1670
1750
|
)
|
|
1751
|
+
# If the credential was rejected (401) and a refresh hook can supply
|
|
1752
|
+
# a fresh key, re-resolve it and retry the call exactly once.
|
|
1753
|
+
refreshed_key = self._resolve_refreshed_api_key(e)
|
|
1754
|
+
if refreshed_key is not None:
|
|
1755
|
+
logger.warning(
|
|
1756
|
+
"Authentication error; refreshing API key and retrying once."
|
|
1757
|
+
)
|
|
1758
|
+
return self._auth_refreshed_llm(refreshed_key).completion(
|
|
1759
|
+
messages,
|
|
1760
|
+
tools,
|
|
1761
|
+
add_security_risk_prediction=add_security_risk_prediction,
|
|
1762
|
+
on_token=on_token,
|
|
1763
|
+
call_context=call_context,
|
|
1764
|
+
**_caller_kwargs,
|
|
1765
|
+
)
|
|
1671
1766
|
return self._handle_error(
|
|
1672
1767
|
e,
|
|
1673
1768
|
lambda fb: fb.completion(
|
|
@@ -1772,6 +1867,24 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
|
|
|
1772
1867
|
on_token=on_token,
|
|
1773
1868
|
**_caller_kwargs,
|
|
1774
1869
|
)
|
|
1870
|
+
# If the credential was rejected (401) and a refresh hook can supply
|
|
1871
|
+
# a fresh key, re-resolve it (off the event loop) and retry once.
|
|
1872
|
+
loop = asyncio.get_running_loop()
|
|
1873
|
+
refreshed_key = await loop.run_in_executor(
|
|
1874
|
+
None, self._resolve_refreshed_api_key, e
|
|
1875
|
+
)
|
|
1876
|
+
if refreshed_key is not None:
|
|
1877
|
+
logger.warning(
|
|
1878
|
+
"Authentication error; refreshing API key and retrying once."
|
|
1879
|
+
)
|
|
1880
|
+
return await self._auth_refreshed_llm(refreshed_key).acompletion(
|
|
1881
|
+
messages,
|
|
1882
|
+
tools,
|
|
1883
|
+
add_security_risk_prediction=add_security_risk_prediction,
|
|
1884
|
+
on_token=on_token,
|
|
1885
|
+
call_context=call_context,
|
|
1886
|
+
**_caller_kwargs,
|
|
1887
|
+
)
|
|
1775
1888
|
# Fallback is synchronous; cast the token callback since the
|
|
1776
1889
|
# fallback LLM's sync path accepts TokenCallbackType.
|
|
1777
1890
|
_fb_token = cast("TokenCallbackType | None", on_token)
|
|
@@ -1938,6 +2051,23 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
|
|
|
1938
2051
|
on_token=on_token,
|
|
1939
2052
|
**_caller_kwargs,
|
|
1940
2053
|
)
|
|
2054
|
+
# If the credential was rejected (401) and a refresh hook can supply
|
|
2055
|
+
# a fresh key, re-resolve it and retry the call exactly once.
|
|
2056
|
+
refreshed_key = self._resolve_refreshed_api_key(e)
|
|
2057
|
+
if refreshed_key is not None:
|
|
2058
|
+
logger.warning(
|
|
2059
|
+
"Authentication error; refreshing API key and retrying once."
|
|
2060
|
+
)
|
|
2061
|
+
return self._auth_refreshed_llm(refreshed_key).responses(
|
|
2062
|
+
messages,
|
|
2063
|
+
tools,
|
|
2064
|
+
include,
|
|
2065
|
+
store,
|
|
2066
|
+
add_security_risk_prediction=add_security_risk_prediction,
|
|
2067
|
+
on_token=on_token,
|
|
2068
|
+
call_context=call_context,
|
|
2069
|
+
**_caller_kwargs,
|
|
2070
|
+
)
|
|
1941
2071
|
return self._handle_error(
|
|
1942
2072
|
e,
|
|
1943
2073
|
lambda fb: fb.responses(
|
|
@@ -2115,6 +2245,26 @@ class LLM(BaseModel, RetryMixin, NonNativeToolCallingMixin):
|
|
|
2115
2245
|
on_token=on_token,
|
|
2116
2246
|
**_caller_kwargs,
|
|
2117
2247
|
)
|
|
2248
|
+
# If the credential was rejected (401) and a refresh hook can supply
|
|
2249
|
+
# a fresh key, re-resolve it (off the event loop) and retry once.
|
|
2250
|
+
loop = asyncio.get_running_loop()
|
|
2251
|
+
refreshed_key = await loop.run_in_executor(
|
|
2252
|
+
None, self._resolve_refreshed_api_key, e
|
|
2253
|
+
)
|
|
2254
|
+
if refreshed_key is not None:
|
|
2255
|
+
logger.warning(
|
|
2256
|
+
"Authentication error; refreshing API key and retrying once."
|
|
2257
|
+
)
|
|
2258
|
+
return await self._auth_refreshed_llm(refreshed_key).aresponses(
|
|
2259
|
+
messages,
|
|
2260
|
+
tools,
|
|
2261
|
+
include,
|
|
2262
|
+
store,
|
|
2263
|
+
add_security_risk_prediction=add_security_risk_prediction,
|
|
2264
|
+
on_token=on_token,
|
|
2265
|
+
call_context=call_context,
|
|
2266
|
+
**_caller_kwargs,
|
|
2267
|
+
)
|
|
2118
2268
|
_fb_token = cast("TokenCallbackType | None", on_token)
|
|
2119
2269
|
return await self._ahandle_error(
|
|
2120
2270
|
e,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from abc import abstractmethod
|
|
3
3
|
from collections.abc import Sequence
|
|
4
|
-
from typing import Any, ClassVar, Literal
|
|
4
|
+
from typing import Annotated, Any, ClassVar, Literal
|
|
5
5
|
|
|
6
6
|
from litellm import ChatCompletionMessageToolCall, ResponseFunctionToolCall
|
|
7
7
|
from litellm.types.responses.main import (
|
|
@@ -16,6 +16,7 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_valida
|
|
|
16
16
|
from openhands.sdk.logger import get_logger
|
|
17
17
|
from openhands.sdk.utils import DEFAULT_TEXT_CONTENT_LIMIT, maybe_truncate
|
|
18
18
|
from openhands.sdk.utils.deprecation import handle_deprecated_model_fields
|
|
19
|
+
from openhands.sdk.utils.masking import PreserveDataUrls
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
logger = get_logger(__name__)
|
|
@@ -203,7 +204,7 @@ class TextContent(BaseContent):
|
|
|
203
204
|
|
|
204
205
|
class ImageContent(BaseContent):
|
|
205
206
|
type: Literal["image"] = "image"
|
|
206
|
-
image_urls: list[str]
|
|
207
|
+
image_urls: Annotated[list[str], PreserveDataUrls()]
|
|
207
208
|
|
|
208
209
|
def to_llm_dict(self) -> list[dict[str, str | dict[str, str]]]:
|
|
209
210
|
"""Convert to LLM API format."""
|