openhands-sdk 1.49.4__tar.gz → 1.49.5__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.5}/PKG-INFO +1 -1
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/view/properties/tool_call_matching.py +4 -9
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/secret_registry.py +64 -10
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/extensions/installation/metadata.py +2 -2
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/message.py +3 -2
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/model_features.py +7 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/telemetry.py +127 -126
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/mcp/tool.py +60 -2
- openhands_sdk-1.49.5/openhands/sdk/utils/masking.py +13 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands_sdk.egg-info/PKG-INFO +1 -1
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands_sdk.egg-info/SOURCES.txt +2 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/pyproject.toml +1 -1
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/agent/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/agent/acp_agent.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/agent/acp_file_credentials.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/agent/acp_models.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/agent/acp_tracing.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/agent/agent.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/agent/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/agent/critic_mixin.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/agent/parallel_executor.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/agent/response_dispatch.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/agent/stream_context.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/agent/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/banner.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/agent_context.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/condenser/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/condenser/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/condenser/llm_summarizing_condenser.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/condenser/no_op_condenser.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/condenser/pipeline_condenser.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/condenser/prompts/summarizing_prompt.j2 +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/condenser/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/memory.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/prompts/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/prompts/presets.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/prompts/prompt.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/prompts/registry.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/prompts/section.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/prompts/sections/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/prompts/sections/dynamic.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/prompts/sections/planning.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/prompts/sections/static.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/prompts/templates/ask_agent_template.j2 +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/view/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/view/manipulation_indices.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/view/properties/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/view/properties/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/view/properties/batch_atomicity.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/view/properties/observation_uniqueness.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/view/properties/tool_loop_atomicity.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/context/view/view.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/cancellation.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/conversation.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/conversation_stats.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/event_store.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/events_list_base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/exceptions.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/fifo_lock.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/goal/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/goal/controller.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/goal/judge.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/goal/prompts.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/goal/runner.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/impl/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/impl/local_conversation.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/impl/remote_conversation.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/persistence_const.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/request.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/resource_lock_manager.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/response_utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/state.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/stuck_detector.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/title_utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/types.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/visualizer/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/visualizer/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/conversation/visualizer/default.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/credential.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/critic/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/critic/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/critic/impl/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/critic/impl/agent_finished.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/critic/impl/api/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/critic/impl/api/chat_template.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/critic/impl/api/client.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/critic/impl/api/critic.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/critic/impl/api/taxonomy.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/critic/impl/empty_patch.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/critic/impl/pass_critic.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/critic/result.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/acp_tool_call.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/condenser.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/conversation_error.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/conversation_state.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/error_classification.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/hook_execution.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/llm_completion_log.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/llm_convertible/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/llm_convertible/action.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/llm_convertible/message.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/llm_convertible/observation.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/llm_convertible/reasoning_utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/llm_convertible/system.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/resume_transcript.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/streaming_delta.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/token.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/types.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/event/user_action.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/extensions/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/extensions/fetch.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/extensions/installation/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/extensions/installation/info.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/extensions/installation/interface.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/extensions/installation/manager.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/extensions/installation/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/git/cached_repo.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/git/exceptions.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/git/git_changes.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/git/git_commits.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/git/git_diff.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/git/models.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/git/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/hooks/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/hooks/config.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/hooks/conversation_hooks.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/hooks/executor.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/hooks/manager.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/hooks/types.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/io/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/io/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/io/cache.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/io/local.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/io/memory.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/auth/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/auth/credentials.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/auth/openai.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/cleanup_profile.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/exceptions/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/exceptions/classifier.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/exceptions/mapping.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/exceptions/types.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/fallback_strategy.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/llm.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/llm_profile_store.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/llm_registry.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/llm_response.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/mixins/fn_call_converter.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/mixins/fn_call_examples.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/mixins/non_native_fc.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/options/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/options/chat_options.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/options/common.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/options/responses_options.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/provider_connection_store.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/router/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/router/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/router/impl/multimodal.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/router/impl/random.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/streaming.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/image_inline.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/image_resize.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/litellm_provider.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/metrics.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/model_info.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/model_prompt_spec.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/openhands_provider.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/providers/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/providers/openrouter.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/responses_serialization.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/retry_mixin.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/runtime_metadata.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/unverified_models.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/verified_models.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/llm/utils/vertex_preflight.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/logger/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/logger/logger.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/logger/rolling.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/marketplace/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/marketplace/registration.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/marketplace/registry.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/marketplace/types.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/mcp/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/mcp/client.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/mcp/config.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/mcp/definition.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/mcp/exceptions.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/mcp/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/observability/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/observability/laminar.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/observability/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/plugin/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/plugin/discovery.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/plugin/fetch.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/plugin/format/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/plugin/format/agent_plugins.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/plugin/format/agent_plugins_mcp.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/plugin/format/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/plugin/format/claude_code.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/plugin/format/schemas/mcp-1.0.0.schema.json +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/plugin/format/schemas/plugin-1.0.0.schema.json +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/plugin/installed.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/plugin/loader.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/plugin/plugin.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/plugin/source.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/plugin/types.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/profiles/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/profiles/agent_profile.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/profiles/agent_profile_store.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/profiles/profile_refs.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/profiles/resolver.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/profiles/seed.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/py.typed +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/secret/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/secret/secrets.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/_shell_ast.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/analyzer.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/confirmation_policy.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/defense_in_depth/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/defense_in_depth/pattern.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/defense_in_depth/policy_rails.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/defense_in_depth/shell_semantics.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/defense_in_depth/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/ensemble.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/grayswan/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/grayswan/analyzer.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/grayswan/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/llm_analyzer.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/risk.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/shell_parser.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/toolshield_helpers.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/security/toolshield_llm_analyzer.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/settings/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/settings/acp_install_catalog.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/settings/acp_providers.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/settings/api_models.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/settings/metadata.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/settings/model.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/skills/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/skills/exceptions.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/skills/execute.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/skills/fetch.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/skills/installed.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/skills/skill.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/skills/trigger.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/skills/types.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/skills/utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/subagent/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/subagent/load.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/subagent/registry.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/subagent/schema.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/testing/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/testing/test_llm.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/tool/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/tool/builtins/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/tool/builtins/finish.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/tool/builtins/invoke_skill.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/tool/builtins/switch_llm.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/tool/builtins/think.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/tool/builtins/vision_inspect.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/tool/client_tool.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/tool/defaults.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/tool/registry.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/tool/schema.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/tool/spec.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/tool/tool.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/async_executor.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/async_utils.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/cipher.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/command.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/datetime.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/deprecation.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/files.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/github.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/json.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/models.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/paging.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/path.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/pydantic_secrets.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/redact.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/truncate.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/utils/visualize.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/workspace/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/workspace/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/workspace/local.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/workspace/models.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/workspace/remote/__init__.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/workspace/remote/async_remote_workspace.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/workspace/remote/base.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/workspace/remote/remote_workspace_mixin.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/workspace/repo.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands/sdk/workspace/workspace.py +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands_sdk.egg-info/dependency_links.txt +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands_sdk.egg-info/requires.txt +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/openhands_sdk.egg-info/top_level.txt +0 -0
- {openhands_sdk-1.49.4 → openhands_sdk-1.49.5}/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.5
|
|
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
|
|
@@ -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.5}/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.
|
|
@@ -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."""
|
|
@@ -109,6 +109,7 @@ def _normalized_supported_openai_params(model: str | None) -> frozenset[str]:
|
|
|
109
109
|
|
|
110
110
|
|
|
111
111
|
REASONING_EFFORT_MODEL_OVERRIDES = {
|
|
112
|
+
"gpt-5.2-codex": "gpt-5.2-codex",
|
|
112
113
|
"kimi-k3": "moonshot/kimi-k3",
|
|
113
114
|
}
|
|
114
115
|
|
|
@@ -188,9 +189,15 @@ SUPPORTS_STOP_WORDS_FALSE_MODELS: list[str] = [
|
|
|
188
189
|
]
|
|
189
190
|
|
|
190
191
|
# Models that should use the OpenAI Responses API path by default
|
|
192
|
+
# NOTE: model_matches uses case-insensitive substring matching, so a bare family
|
|
193
|
+
# token like "gpt-5" / "gpt-6" covers all variants (mini, sol, luna, astra, ...).
|
|
191
194
|
RESPONSES_API_MODELS: list[str] = [
|
|
192
195
|
# OpenAI GPT-5 family (includes mini variants)
|
|
193
196
|
"gpt-5",
|
|
197
|
+
# OpenAI GPT-6 family (gpt-6-sol, gpt-6-luna, gpt-6-astra, ...). These reject
|
|
198
|
+
# function tools + reasoning_effort on /v1/chat/completions; /v1/responses
|
|
199
|
+
# supports both. See saas-deploy #1144.
|
|
200
|
+
"gpt-6",
|
|
194
201
|
# OpenAI Codex (uses Responses API)
|
|
195
202
|
"codex-mini-latest",
|
|
196
203
|
]
|
|
@@ -5,6 +5,7 @@ import traceback
|
|
|
5
5
|
import uuid
|
|
6
6
|
import warnings
|
|
7
7
|
from collections.abc import Callable
|
|
8
|
+
from dataclasses import dataclass
|
|
8
9
|
from typing import Any, ClassVar
|
|
9
10
|
|
|
10
11
|
from litellm.cost_calculator import completion_cost as litellm_completion_cost
|
|
@@ -21,6 +22,109 @@ from openhands.sdk.logger import get_logger
|
|
|
21
22
|
logger = get_logger(__name__)
|
|
22
23
|
|
|
23
24
|
|
|
25
|
+
@dataclass(frozen=True, slots=True)
|
|
26
|
+
class UsageSnapshot:
|
|
27
|
+
"""Provider-independent view of one response's token accounting.
|
|
28
|
+
|
|
29
|
+
LiteLLM reports usage in two disjoint shapes that name the same quantities
|
|
30
|
+
differently: ``Usage`` (Chat Completions) uses ``prompt_tokens`` /
|
|
31
|
+
``completion_tokens``, while ``ResponseAPIUsage`` (Responses API) uses
|
|
32
|
+
``input_tokens`` / ``output_tokens``. Normalizing once, at the boundary,
|
|
33
|
+
keeps every consumer -- metrics, span attributes and completion logs -- on
|
|
34
|
+
a single vocabulary instead of rediscovering the aliases at each call site.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
prompt_tokens: int = 0
|
|
38
|
+
completion_tokens: int = 0
|
|
39
|
+
reasoning_tokens: int = 0
|
|
40
|
+
cache_read_tokens: int = 0
|
|
41
|
+
cache_write_tokens: int = 0
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def has_tokens(self) -> bool:
|
|
45
|
+
"""Whether the provider reported any prompt or completion tokens."""
|
|
46
|
+
return self.prompt_tokens > 0 or self.completion_tokens > 0
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def normalize_usage(usage: Usage | ResponseAPIUsage | None) -> UsageSnapshot | None:
|
|
50
|
+
"""Return a :class:`UsageSnapshot` for either provider usage shape.
|
|
51
|
+
|
|
52
|
+
``None`` means "no token accounting": the provider reported no usage, or
|
|
53
|
+
reported a shape this module does not recognize. Unrecognized shapes are
|
|
54
|
+
ignored rather than guessed at, so adding a provider cannot silently feed
|
|
55
|
+
invented token counts into cost and cache accounting.
|
|
56
|
+
"""
|
|
57
|
+
if isinstance(usage, Usage):
|
|
58
|
+
prompt_details = usage.prompt_tokens_details
|
|
59
|
+
completion_details = usage.completion_tokens_details
|
|
60
|
+
# ``cache_creation_tokens`` defaults to ``None``, so presence in
|
|
61
|
+
# ``model_fields_set`` is what distinguishes "provider reported a cache
|
|
62
|
+
# write" from "provider said nothing about cache writes".
|
|
63
|
+
cache_write = 0
|
|
64
|
+
if (
|
|
65
|
+
prompt_details is not None
|
|
66
|
+
and "cache_creation_tokens" in prompt_details.model_fields_set
|
|
67
|
+
):
|
|
68
|
+
cache_write = int(prompt_details.cache_creation_tokens or 0)
|
|
69
|
+
return UsageSnapshot(
|
|
70
|
+
prompt_tokens=int(usage.prompt_tokens or 0),
|
|
71
|
+
completion_tokens=int(usage.completion_tokens or 0),
|
|
72
|
+
reasoning_tokens=(
|
|
73
|
+
int(completion_details.reasoning_tokens or 0)
|
|
74
|
+
if completion_details is not None
|
|
75
|
+
else 0
|
|
76
|
+
),
|
|
77
|
+
cache_read_tokens=(
|
|
78
|
+
int(prompt_details.cached_tokens or 0)
|
|
79
|
+
if prompt_details is not None
|
|
80
|
+
else 0
|
|
81
|
+
),
|
|
82
|
+
cache_write_tokens=cache_write,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if isinstance(usage, ResponseAPIUsage):
|
|
86
|
+
input_details = usage.input_tokens_details
|
|
87
|
+
output_details = usage.output_tokens_details
|
|
88
|
+
return UsageSnapshot(
|
|
89
|
+
prompt_tokens=int(usage.input_tokens or 0),
|
|
90
|
+
completion_tokens=int(usage.output_tokens or 0),
|
|
91
|
+
reasoning_tokens=(
|
|
92
|
+
int(output_details.reasoning_tokens or 0)
|
|
93
|
+
if output_details is not None
|
|
94
|
+
else 0
|
|
95
|
+
),
|
|
96
|
+
cache_read_tokens=(
|
|
97
|
+
int(input_details.cached_tokens or 0)
|
|
98
|
+
if input_details is not None
|
|
99
|
+
else 0
|
|
100
|
+
),
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _response_usage(
|
|
107
|
+
resp: ModelResponse | ResponsesAPIResponse,
|
|
108
|
+
) -> Usage | ResponseAPIUsage | None:
|
|
109
|
+
"""Return the usage object a response carries, if any.
|
|
110
|
+
|
|
111
|
+
``ResponsesAPIResponse`` declares ``usage`` as a field. ``ModelResponse``
|
|
112
|
+
only accepts it as a constructor argument, so pydantic keeps it in the
|
|
113
|
+
model's extra mapping instead of its fields; reading that mapping is the
|
|
114
|
+
typed equivalent of probing for a possibly-absent attribute.
|
|
115
|
+
"""
|
|
116
|
+
if isinstance(resp, ResponsesAPIResponse):
|
|
117
|
+
return resp.usage
|
|
118
|
+
if isinstance(resp, ModelResponse):
|
|
119
|
+
extra = resp.model_extra
|
|
120
|
+
if not extra:
|
|
121
|
+
return None
|
|
122
|
+
usage = extra.get("usage")
|
|
123
|
+
if isinstance(usage, (Usage, ResponseAPIUsage)):
|
|
124
|
+
return usage
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
|
|
24
128
|
class Telemetry(BaseModel):
|
|
25
129
|
"""
|
|
26
130
|
Handles latency, token/cost accounting, and optional logging.
|
|
@@ -106,10 +210,10 @@ class Telemetry(BaseModel):
|
|
|
106
210
|
if cost:
|
|
107
211
|
self.metrics.add_cost(cost)
|
|
108
212
|
|
|
109
|
-
# 3) tokens -
|
|
110
|
-
usage =
|
|
213
|
+
# 3) tokens - normalize the provider's usage shape exactly once
|
|
214
|
+
usage = normalize_usage(_response_usage(resp))
|
|
111
215
|
|
|
112
|
-
if usage and
|
|
216
|
+
if usage is not None and usage.has_tokens:
|
|
113
217
|
self._record_usage(
|
|
114
218
|
usage, response_id, self._req_ctx.get("context_window", 0)
|
|
115
219
|
)
|
|
@@ -176,96 +280,20 @@ class Telemetry(BaseModel):
|
|
|
176
280
|
return
|
|
177
281
|
|
|
178
282
|
# ---------- Helpers ----------
|
|
179
|
-
def _has_meaningful_usage(self, usage: Usage | ResponseAPIUsage | None) -> bool:
|
|
180
|
-
"""Check if usage has meaningful (non-zero) token counts.
|
|
181
|
-
|
|
182
|
-
Supports both Chat Completions Usage and Responses API Usage shapes.
|
|
183
|
-
"""
|
|
184
|
-
if usage is None:
|
|
185
|
-
return False
|
|
186
|
-
try:
|
|
187
|
-
prompt_tokens = getattr(usage, "prompt_tokens", None)
|
|
188
|
-
if prompt_tokens is None:
|
|
189
|
-
prompt_tokens = getattr(usage, "input_tokens", 0)
|
|
190
|
-
completion_tokens = getattr(usage, "completion_tokens", None)
|
|
191
|
-
if completion_tokens is None:
|
|
192
|
-
completion_tokens = getattr(usage, "output_tokens", 0)
|
|
193
|
-
|
|
194
|
-
pt = int(prompt_tokens or 0)
|
|
195
|
-
ct = int(completion_tokens or 0)
|
|
196
|
-
return pt > 0 or ct > 0
|
|
197
|
-
except Exception:
|
|
198
|
-
return False
|
|
199
|
-
|
|
200
283
|
def _record_usage(
|
|
201
|
-
self, usage:
|
|
284
|
+
self, usage: UsageSnapshot, response_id: str, context_window: int
|
|
202
285
|
) -> None:
|
|
203
|
-
"""
|
|
204
|
-
Record token usage, supporting both Chat Completions Usage and
|
|
205
|
-
Responses API Usage.
|
|
206
|
-
|
|
207
|
-
Chat shape:
|
|
208
|
-
- prompt_tokens, completion_tokens
|
|
209
|
-
- prompt_tokens_details.cached_tokens
|
|
210
|
-
- completion_tokens_details.reasoning_tokens
|
|
211
|
-
- _cache_creation_input_tokens for cache_write
|
|
212
|
-
Responses shape:
|
|
213
|
-
- input_tokens, output_tokens
|
|
214
|
-
- input_tokens_details.cached_tokens
|
|
215
|
-
- output_tokens_details.reasoning_tokens
|
|
216
|
-
"""
|
|
217
|
-
prompt_tokens = int(
|
|
218
|
-
getattr(usage, "prompt_tokens", None)
|
|
219
|
-
or getattr(usage, "input_tokens", 0)
|
|
220
|
-
or 0
|
|
221
|
-
)
|
|
222
|
-
completion_tokens = int(
|
|
223
|
-
getattr(usage, "completion_tokens", None)
|
|
224
|
-
or getattr(usage, "output_tokens", 0)
|
|
225
|
-
or 0
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
cache_read, cache_write = self._cache_buckets(usage)
|
|
229
|
-
|
|
230
|
-
reasoning_tokens = 0
|
|
231
|
-
c_details = getattr(usage, "completion_tokens_details", None) or getattr(
|
|
232
|
-
usage, "output_tokens_details", None
|
|
233
|
-
)
|
|
234
|
-
if c_details is not None:
|
|
235
|
-
reasoning_tokens = int(getattr(c_details, "reasoning_tokens", 0) or 0)
|
|
236
|
-
|
|
286
|
+
"""Record an already-normalized usage snapshot into ``metrics``."""
|
|
237
287
|
self.metrics.add_token_usage(
|
|
238
|
-
prompt_tokens=prompt_tokens,
|
|
239
|
-
completion_tokens=completion_tokens,
|
|
240
|
-
cache_read_tokens=
|
|
241
|
-
cache_write_tokens=
|
|
242
|
-
reasoning_tokens=reasoning_tokens,
|
|
288
|
+
prompt_tokens=usage.prompt_tokens,
|
|
289
|
+
completion_tokens=usage.completion_tokens,
|
|
290
|
+
cache_read_tokens=usage.cache_read_tokens,
|
|
291
|
+
cache_write_tokens=usage.cache_write_tokens,
|
|
292
|
+
reasoning_tokens=usage.reasoning_tokens,
|
|
243
293
|
context_window=context_window,
|
|
244
294
|
response_id=response_id,
|
|
245
295
|
)
|
|
246
296
|
|
|
247
|
-
@staticmethod
|
|
248
|
-
def _cache_buckets(usage: Usage | ResponseAPIUsage) -> tuple[int, int]:
|
|
249
|
-
"""Return ``(cache_read, cache_write)`` for either usage shape.
|
|
250
|
-
|
|
251
|
-
Single source of truth for both ``metrics`` and the span, so the trace
|
|
252
|
-
and the app's cost cannot disagree about the buckets.
|
|
253
|
-
"""
|
|
254
|
-
if isinstance(usage, Usage):
|
|
255
|
-
details = usage.prompt_tokens_details
|
|
256
|
-
if details is None:
|
|
257
|
-
return 0, 0
|
|
258
|
-
cache_write = (
|
|
259
|
-
details.cache_creation_tokens
|
|
260
|
-
if "cache_creation_tokens" in details.model_fields_set
|
|
261
|
-
else 0
|
|
262
|
-
)
|
|
263
|
-
return int(details.cached_tokens or 0), int(cache_write or 0)
|
|
264
|
-
|
|
265
|
-
details = usage.input_tokens_details
|
|
266
|
-
cache_read = details.cached_tokens if details is not None else 0
|
|
267
|
-
return int(cache_read or 0), 0
|
|
268
|
-
|
|
269
297
|
# ---------- Observability span ----------
|
|
270
298
|
# These bracket one LLM call: ``on_request`` -> transport -> ``on_response``
|
|
271
299
|
# all run inside a single retry attempt, so the span is current while
|
|
@@ -284,9 +312,7 @@ class Telemetry(BaseModel):
|
|
|
284
312
|
logger.debug("Failed to open LLM span", exc_info=True)
|
|
285
313
|
self._span = self._span_cm = None
|
|
286
314
|
|
|
287
|
-
def _annotate_span(
|
|
288
|
-
self, cost: float | None, usage: Usage | ResponseAPIUsage | None
|
|
289
|
-
) -> None:
|
|
315
|
+
def _annotate_span(self, cost: float | None, usage: UsageSnapshot | None) -> None:
|
|
290
316
|
span = self._span
|
|
291
317
|
if span is None:
|
|
292
318
|
return
|
|
@@ -297,13 +323,15 @@ class Telemetry(BaseModel):
|
|
|
297
323
|
# and priced by the real backend rather than by a name lookup.
|
|
298
324
|
span.set_attribute("gen_ai.usage.cost", float(cost))
|
|
299
325
|
if usage is not None:
|
|
300
|
-
cache_read, cache_write = self._cache_buckets(usage)
|
|
301
326
|
# Emitted unconditionally: lmnr only reports these when
|
|
302
327
|
# prompt_tokens_details is populated, which not every provider
|
|
303
328
|
# shape does.
|
|
304
|
-
span.set_attribute("gen_ai.usage.cache_read_input_tokens", cache_read)
|
|
305
329
|
span.set_attribute(
|
|
306
|
-
"gen_ai.usage.
|
|
330
|
+
"gen_ai.usage.cache_read_input_tokens", usage.cache_read_tokens
|
|
331
|
+
)
|
|
332
|
+
span.set_attribute(
|
|
333
|
+
"gen_ai.usage.cache_creation_input_tokens",
|
|
334
|
+
usage.cache_write_tokens,
|
|
307
335
|
)
|
|
308
336
|
except Exception:
|
|
309
337
|
logger.debug("Failed to annotate LLM span", exc_info=True)
|
|
@@ -339,7 +367,7 @@ class Telemetry(BaseModel):
|
|
|
339
367
|
extra_kwargs["custom_cost_per_token"] = cost_per_token
|
|
340
368
|
|
|
341
369
|
try:
|
|
342
|
-
hidden =
|
|
370
|
+
hidden = resp._hidden_params or {}
|
|
343
371
|
cost = hidden.get("additional_headers", {}).get(
|
|
344
372
|
"llm_provider-x-litellm-response-cost"
|
|
345
373
|
)
|
|
@@ -391,40 +419,13 @@ class Telemetry(BaseModel):
|
|
|
391
419
|
|
|
392
420
|
# Usage summary (prompt, completion, reasoning tokens) for quick inspection
|
|
393
421
|
try:
|
|
394
|
-
usage =
|
|
395
|
-
if usage:
|
|
396
|
-
prompt_tokens = int(
|
|
397
|
-
getattr(usage, "prompt_tokens", None)
|
|
398
|
-
or getattr(usage, "input_tokens", 0)
|
|
399
|
-
or 0
|
|
400
|
-
)
|
|
401
|
-
completion_tokens = int(
|
|
402
|
-
getattr(usage, "completion_tokens", None)
|
|
403
|
-
or getattr(usage, "output_tokens", 0)
|
|
404
|
-
or 0
|
|
405
|
-
)
|
|
406
|
-
details = getattr(
|
|
407
|
-
usage, "completion_tokens_details", None
|
|
408
|
-
) or getattr(usage, "output_tokens_details", None)
|
|
409
|
-
reasoning_tokens = (
|
|
410
|
-
int(getattr(details, "reasoning_tokens", 0) or 0)
|
|
411
|
-
if details
|
|
412
|
-
else 0
|
|
413
|
-
)
|
|
414
|
-
p_details = getattr(
|
|
415
|
-
usage, "prompt_tokens_details", None
|
|
416
|
-
) or getattr(usage, "input_tokens_details", None)
|
|
417
|
-
cache_read_tokens = (
|
|
418
|
-
int(getattr(p_details, "cached_tokens", 0) or 0)
|
|
419
|
-
if p_details
|
|
420
|
-
else 0
|
|
421
|
-
)
|
|
422
|
-
|
|
422
|
+
usage = normalize_usage(_response_usage(resp))
|
|
423
|
+
if usage is not None:
|
|
423
424
|
data["usage_summary"] = {
|
|
424
|
-
"prompt_tokens": prompt_tokens,
|
|
425
|
-
"completion_tokens": completion_tokens,
|
|
426
|
-
"reasoning_tokens": reasoning_tokens,
|
|
427
|
-
"cache_read_tokens": cache_read_tokens,
|
|
425
|
+
"prompt_tokens": usage.prompt_tokens,
|
|
426
|
+
"completion_tokens": usage.completion_tokens,
|
|
427
|
+
"reasoning_tokens": usage.reasoning_tokens,
|
|
428
|
+
"cache_read_tokens": usage.cache_read_tokens,
|
|
428
429
|
}
|
|
429
430
|
except Exception:
|
|
430
431
|
# Best-effort only; don't fail logging
|
|
@@ -15,7 +15,13 @@ if TYPE_CHECKING:
|
|
|
15
15
|
import mcp.types
|
|
16
16
|
from litellm import ChatCompletionToolParam
|
|
17
17
|
from openai.types.responses import FunctionToolParam
|
|
18
|
-
from pydantic import
|
|
18
|
+
from pydantic import (
|
|
19
|
+
Field,
|
|
20
|
+
SerializerFunctionWrapHandler,
|
|
21
|
+
ValidationError,
|
|
22
|
+
field_serializer,
|
|
23
|
+
field_validator,
|
|
24
|
+
)
|
|
19
25
|
|
|
20
26
|
from openhands.sdk.llm import TextContent
|
|
21
27
|
from openhands.sdk.logger import get_logger
|
|
@@ -42,6 +48,44 @@ logger = get_logger(__name__)
|
|
|
42
48
|
# Default timeout for MCP tool execution in seconds
|
|
43
49
|
MCP_TOOL_TIMEOUT_SECONDS = 300
|
|
44
50
|
|
|
51
|
+
# mcp 2.x dumps its snake_case attribute names unless by_alias=True; mcp 1.x
|
|
52
|
+
# only reads the camelCase wire names. Keys inside inputSchema/outputSchema
|
|
53
|
+
# are user JSON Schema and must never be renamed.
|
|
54
|
+
_MCP_TOOL_WIRE_KEYS: Final[dict[str, str]] = {
|
|
55
|
+
"input_schema": "inputSchema",
|
|
56
|
+
"output_schema": "outputSchema",
|
|
57
|
+
"meta": "_meta",
|
|
58
|
+
}
|
|
59
|
+
_MCP_NESTED_WIRE_KEYS: Final[dict[str, dict[str, str]]] = {
|
|
60
|
+
"annotations": {
|
|
61
|
+
"read_only_hint": "readOnlyHint",
|
|
62
|
+
"destructive_hint": "destructiveHint",
|
|
63
|
+
"idempotent_hint": "idempotentHint",
|
|
64
|
+
"open_world_hint": "openWorldHint",
|
|
65
|
+
},
|
|
66
|
+
"execution": {"task_support": "taskSupport"},
|
|
67
|
+
"icons": {"mime_type": "mimeType"},
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _mcp_tool_to_wire_keys(data: Any) -> Any:
|
|
72
|
+
"""Normalize a serialized mcp.types.Tool to the MCP spec's camelCase keys."""
|
|
73
|
+
if not isinstance(data, dict):
|
|
74
|
+
return data
|
|
75
|
+
out = {_MCP_TOOL_WIRE_KEYS.get(k, k): v for k, v in data.items()}
|
|
76
|
+
for key, renames in _MCP_NESTED_WIRE_KEYS.items():
|
|
77
|
+
value = out.get(key)
|
|
78
|
+
if isinstance(value, dict):
|
|
79
|
+
out[key] = {renames.get(k, k): v for k, v in value.items()}
|
|
80
|
+
elif isinstance(value, list):
|
|
81
|
+
out[key] = [
|
|
82
|
+
{renames.get(k, k): v for k, v in item.items()}
|
|
83
|
+
if isinstance(item, dict)
|
|
84
|
+
else item
|
|
85
|
+
for item in value
|
|
86
|
+
]
|
|
87
|
+
return out
|
|
88
|
+
|
|
45
89
|
|
|
46
90
|
# NOTE: We don't define MCPToolAction because it
|
|
47
91
|
# will be a pydantic BaseModel dynamically created from the MCP tool schema.
|
|
@@ -244,6 +288,20 @@ class MCPToolDefinition(ToolDefinition[MCPToolAction, MCPToolObservation]):
|
|
|
244
288
|
|
|
245
289
|
mcp_tool: mcp.types.Tool = Field(description="The MCP tool definition.")
|
|
246
290
|
|
|
291
|
+
@field_validator("mcp_tool", mode="before")
|
|
292
|
+
@classmethod
|
|
293
|
+
def _read_either_mcp_spelling(cls, v: Any) -> Any:
|
|
294
|
+
return _mcp_tool_to_wire_keys(v)
|
|
295
|
+
|
|
296
|
+
@field_serializer("mcp_tool", mode="wrap")
|
|
297
|
+
def _write_mcp_wire_spelling(
|
|
298
|
+
self, v: mcp.types.Tool, handler: SerializerFunctionWrapHandler
|
|
299
|
+
):
|
|
300
|
+
# Persisted events outlive the resolved mcp major; always write the
|
|
301
|
+
# spec's wire names so every mcp version can read them back. No return
|
|
302
|
+
# annotation, so the OpenAPI schema keeps referencing mcp.types.Tool.
|
|
303
|
+
return _mcp_tool_to_wire_keys(handler(v))
|
|
304
|
+
|
|
247
305
|
@property
|
|
248
306
|
def name(self) -> str: # type: ignore[override]
|
|
249
307
|
"""Return the MCP tool name instead of the class name."""
|
|
@@ -333,7 +391,7 @@ class MCPToolDefinition(ToolDefinition[MCPToolAction, MCPToolObservation]):
|
|
|
333
391
|
try:
|
|
334
392
|
annotations = (
|
|
335
393
|
ToolAnnotations.model_validate(
|
|
336
|
-
mcp_tool.annotations.model_dump(exclude_none=True)
|
|
394
|
+
mcp_tool.annotations.model_dump(exclude_none=True, by_alias=True)
|
|
337
395
|
)
|
|
338
396
|
if mcp_tool.annotations
|
|
339
397
|
else None
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Field metadata used by secret masking."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SkipSecretMasking:
|
|
5
|
+
"""Mark a field whose value must not be secret-masked."""
|
|
6
|
+
|
|
7
|
+
__slots__ = ()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PreserveDataUrls:
|
|
11
|
+
"""Mark string values that contain opaque base64 data URLs."""
|
|
12
|
+
|
|
13
|
+
__slots__ = ()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-sdk
|
|
3
|
-
Version: 1.49.
|
|
3
|
+
Version: 1.49.5
|
|
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
|
|
@@ -279,6 +279,7 @@ pyproject.toml
|
|
|
279
279
|
./openhands/sdk/utils/files.py
|
|
280
280
|
./openhands/sdk/utils/github.py
|
|
281
281
|
./openhands/sdk/utils/json.py
|
|
282
|
+
./openhands/sdk/utils/masking.py
|
|
282
283
|
./openhands/sdk/utils/models.py
|
|
283
284
|
./openhands/sdk/utils/paging.py
|
|
284
285
|
./openhands/sdk/utils/path.py
|
|
@@ -576,6 +577,7 @@ openhands/sdk/utils/deprecation.py
|
|
|
576
577
|
openhands/sdk/utils/files.py
|
|
577
578
|
openhands/sdk/utils/github.py
|
|
578
579
|
openhands/sdk/utils/json.py
|
|
580
|
+
openhands/sdk/utils/masking.py
|
|
579
581
|
openhands/sdk/utils/models.py
|
|
580
582
|
openhands/sdk/utils/paging.py
|
|
581
583
|
openhands/sdk/utils/path.py
|
|
File without changes
|