openhands-sdk 1.29.3__tar.gz → 1.30.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/PKG-INFO +1 -1
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/__init__.py +2 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/agent/agent.py +74 -2
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/agent/base.py +36 -13
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/agent/utils.py +8 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/agent_context.py +10 -5
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/condenser/llm_summarizing_condenser.py +10 -0
- openhands_sdk-1.30.0/openhands/sdk/context/prompts/presets.py +109 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/prompts/sections/__init__.py +4 -1
- openhands_sdk-1.29.3/openhands/sdk/agent/prompts/system_prompt_planning.j2 → openhands_sdk-1.30.0/openhands/sdk/context/prompts/sections/planning.py +40 -2
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/base.py +20 -1
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/conversation.py +12 -1
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/impl/local_conversation.py +48 -27
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/impl/remote_conversation.py +13 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/persistence_const.py +2 -1
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/request.py +9 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/title_utils.py +5 -1
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/types.py +29 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/__init__.py +7 -1
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/auth/openai.py +17 -2
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/llm.py +91 -9
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/llm_profile_store.py +28 -1
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/options/chat_options.py +21 -4
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/options/responses_options.py +18 -3
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/router/base.py +9 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/marketplace/registration.py +11 -2
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/mcp/tool.py +30 -4
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/observability/laminar.py +23 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/plugin/types.py +10 -1
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/profiles/__init__.py +14 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/profiles/agent_profile.py +35 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/profiles/agent_profile_store.py +131 -2
- openhands_sdk-1.30.0/openhands/sdk/profiles/profile_refs.py +149 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/profiles/resolver.py +3 -3
- openhands_sdk-1.30.0/openhands/sdk/profiles/seed.py +71 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/settings/model.py +8 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/subagent/__init__.py +4 -1
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/subagent/load.py +45 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/subagent/schema.py +10 -1
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/testing/test_llm.py +4 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/utils/redact.py +14 -10
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands_sdk.egg-info/PKG-INFO +1 -1
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands_sdk.egg-info/SOURCES.txt +4 -2
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/pyproject.toml +1 -1
- openhands_sdk-1.29.3/openhands/sdk/context/prompts/presets.py +0 -71
- openhands_sdk-1.29.3/openhands/sdk/profiles/profile_refs.py +0 -173
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/agent/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/agent/acp_agent.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/agent/acp_models.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/agent/critic_mixin.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/agent/parallel_executor.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/agent/response_dispatch.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/banner.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/condenser/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/condenser/base.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/condenser/no_op_condenser.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/condenser/pipeline_condenser.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/condenser/prompts/summarizing_prompt.j2 +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/condenser/utils.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/prompts/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/prompts/prompt.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/prompts/registry.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/prompts/section.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/prompts/sections/dynamic.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/prompts/sections/static.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/prompts/templates/ask_agent_template.j2 +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/view/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/view/manipulation_indices.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/view/properties/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/view/properties/base.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/view/properties/batch_atomicity.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/view/properties/observation_uniqueness.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/view/properties/tool_call_matching.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/view/properties/tool_loop_atomicity.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/view/view.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/cancellation.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/conversation_stats.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/event_store.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/events_list_base.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/exceptions.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/fifo_lock.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/goal/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/goal/controller.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/goal/judge.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/goal/prompts.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/goal/runner.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/impl/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/resource_lock_manager.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/response_utils.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/secret_registry.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/state.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/stuck_detector.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/visualizer/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/visualizer/base.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/conversation/visualizer/default.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/critic/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/critic/base.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/critic/impl/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/critic/impl/agent_finished.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/critic/impl/api/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/critic/impl/api/chat_template.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/critic/impl/api/client.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/critic/impl/api/critic.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/critic/impl/api/taxonomy.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/critic/impl/empty_patch.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/critic/impl/pass_critic.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/critic/result.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/acp_tool_call.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/base.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/condenser.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/conversation_error.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/conversation_state.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/hook_execution.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/llm_completion_log.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/llm_convertible/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/llm_convertible/action.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/llm_convertible/message.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/llm_convertible/observation.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/llm_convertible/reasoning_utils.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/llm_convertible/system.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/resume_transcript.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/streaming_delta.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/token.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/types.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/event/user_action.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/extensions/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/extensions/fetch.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/extensions/installation/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/extensions/installation/info.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/extensions/installation/interface.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/extensions/installation/manager.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/extensions/installation/metadata.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/extensions/installation/utils.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/git/cached_repo.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/git/exceptions.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/git/git_changes.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/git/git_diff.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/git/models.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/git/utils.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/hooks/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/hooks/config.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/hooks/conversation_hooks.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/hooks/executor.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/hooks/manager.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/hooks/types.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/io/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/io/base.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/io/cache.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/io/local.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/io/memory.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/auth/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/auth/credentials.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/exceptions/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/exceptions/classifier.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/exceptions/mapping.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/exceptions/types.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/fallback_strategy.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/llm_registry.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/llm_response.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/message.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/mixins/fn_call_converter.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/mixins/fn_call_examples.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/mixins/non_native_fc.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/options/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/options/common.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/router/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/router/impl/multimodal.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/router/impl/random.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/streaming.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/utils/image_inline.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/utils/image_resize.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/utils/litellm_provider.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/utils/metrics.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/utils/model_features.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/utils/model_info.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/utils/model_prompt_spec.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/utils/openhands_provider.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/utils/responses_serialization.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/utils/retry_mixin.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/utils/telemetry.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/utils/unverified_models.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/utils/verified_models.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/llm/utils/vertex_preflight.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/logger/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/logger/logger.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/logger/rolling.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/marketplace/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/marketplace/registry.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/marketplace/types.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/mcp/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/mcp/client.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/mcp/definition.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/mcp/exceptions.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/mcp/utils.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/observability/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/observability/utils.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/plugin/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/plugin/discovery.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/plugin/fetch.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/plugin/installed.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/plugin/loader.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/plugin/plugin.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/plugin/source.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/py.typed +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/secret/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/secret/secrets.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/security/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/security/_shell_ast.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/security/analyzer.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/security/confirmation_policy.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/security/defense_in_depth/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/security/defense_in_depth/pattern.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/security/defense_in_depth/policy_rails.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/security/defense_in_depth/utils.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/security/ensemble.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/security/grayswan/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/security/grayswan/analyzer.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/security/grayswan/utils.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/security/llm_analyzer.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/security/risk.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/security/shell_parser.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/settings/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/settings/acp_providers.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/settings/api_models.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/settings/metadata.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/skills/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/skills/exceptions.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/skills/execute.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/skills/fetch.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/skills/installed.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/skills/skill.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/skills/trigger.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/skills/types.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/skills/utils.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/subagent/registry.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/testing/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/tool/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/tool/builtins/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/tool/builtins/finish.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/tool/builtins/invoke_skill.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/tool/builtins/switch_llm.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/tool/builtins/think.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/tool/client_tool.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/tool/registry.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/tool/schema.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/tool/spec.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/tool/tool.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/utils/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/utils/async_executor.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/utils/async_utils.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/utils/cipher.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/utils/command.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/utils/datetime.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/utils/deprecation.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/utils/github.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/utils/json.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/utils/models.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/utils/paging.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/utils/path.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/utils/pydantic_secrets.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/utils/truncate.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/utils/visualize.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/workspace/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/workspace/base.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/workspace/local.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/workspace/models.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/workspace/remote/__init__.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/workspace/remote/async_remote_workspace.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/workspace/remote/base.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/workspace/remote/remote_workspace_mixin.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/workspace/repo.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/workspace/workspace.py +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands_sdk.egg-info/dependency_links.txt +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands_sdk.egg-info/requires.txt +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands_sdk.egg-info/top_level.txt +0 -0
- {openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.30.0
|
|
4
4
|
Summary: OpenHands SDK - Core functionality for building AI agents
|
|
5
5
|
Project-URL: Source, https://github.com/OpenHands/software-agent-sdk
|
|
6
6
|
Project-URL: Homepage, https://github.com/OpenHands/software-agent-sdk
|
|
@@ -85,6 +85,7 @@ from openhands.sdk.skills import (
|
|
|
85
85
|
)
|
|
86
86
|
from openhands.sdk.subagent import (
|
|
87
87
|
agent_definition_to_factory,
|
|
88
|
+
discover_agents,
|
|
88
89
|
load_agents_from_dir,
|
|
89
90
|
load_project_agents,
|
|
90
91
|
load_user_agents,
|
|
@@ -197,6 +198,7 @@ __all__ = [
|
|
|
197
198
|
"load_project_agents",
|
|
198
199
|
"load_user_agents",
|
|
199
200
|
"load_agents_from_dir",
|
|
201
|
+
"discover_agents",
|
|
200
202
|
"agent_definition_to_factory",
|
|
201
203
|
"load_project_skills",
|
|
202
204
|
"load_skills_from_dir",
|
|
@@ -27,7 +27,7 @@ from openhands.sdk.agent.utils import (
|
|
|
27
27
|
parse_tool_call_arguments,
|
|
28
28
|
prepare_llm_messages,
|
|
29
29
|
)
|
|
30
|
-
from openhands.sdk.context.prompts.presets import create_registry
|
|
30
|
+
from openhands.sdk.context.prompts.presets import PromptPreset, create_registry
|
|
31
31
|
from openhands.sdk.conversation import (
|
|
32
32
|
CancellationToken,
|
|
33
33
|
ConversationCallbackType,
|
|
@@ -51,6 +51,7 @@ from openhands.sdk.event.condenser import (
|
|
|
51
51
|
CondensationRequest,
|
|
52
52
|
)
|
|
53
53
|
from openhands.sdk.llm import (
|
|
54
|
+
LLM,
|
|
54
55
|
LLMResponse,
|
|
55
56
|
Message,
|
|
56
57
|
MessageToolCall,
|
|
@@ -65,6 +66,7 @@ from openhands.sdk.llm.exceptions import (
|
|
|
65
66
|
LLMContextWindowExceedError,
|
|
66
67
|
LLMMalformedConversationHistoryError,
|
|
67
68
|
)
|
|
69
|
+
from openhands.sdk.llm.router.base import RouterLLM
|
|
68
70
|
from openhands.sdk.logger import get_logger
|
|
69
71
|
from openhands.sdk.observability.laminar import (
|
|
70
72
|
maybe_init_laminar,
|
|
@@ -79,6 +81,7 @@ from openhands.sdk.tool import (
|
|
|
79
81
|
|
|
80
82
|
|
|
81
83
|
if TYPE_CHECKING:
|
|
84
|
+
from openhands.sdk.llm.llm import LLMCallContext
|
|
82
85
|
from openhands.sdk.tool import ToolDefinition
|
|
83
86
|
from openhands.sdk.mcp.tool import MCPToolDefinition
|
|
84
87
|
from openhands.sdk.tool.builtins import (
|
|
@@ -113,6 +116,36 @@ def _tool_has_summary_param(tool: ToolDefinition) -> bool:
|
|
|
113
116
|
INIT_STATE_PREFIX_SCAN_WINDOW = 3
|
|
114
117
|
|
|
115
118
|
|
|
119
|
+
def _latest_user_message_contains_image(messages: list[Message]) -> bool:
|
|
120
|
+
for message in reversed(messages):
|
|
121
|
+
if message.role == "user":
|
|
122
|
+
return message.contains_image
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _non_multimodal_image_message(model: str) -> Message:
|
|
127
|
+
return Message(
|
|
128
|
+
role="assistant",
|
|
129
|
+
content=[
|
|
130
|
+
TextContent(
|
|
131
|
+
text=(
|
|
132
|
+
"I received your image, but the currently selected model "
|
|
133
|
+
f"({model}) does not support image understanding. Please "
|
|
134
|
+
"switch to a multimodal model to analyze the image."
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
],
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _should_handle_non_multimodal_image_input(
|
|
142
|
+
llm: LLM, messages: list[Message]
|
|
143
|
+
) -> bool:
|
|
144
|
+
if isinstance(llm, RouterLLM):
|
|
145
|
+
return False
|
|
146
|
+
return _latest_user_message_contains_image(messages) and not llm.vision_is_active()
|
|
147
|
+
|
|
148
|
+
|
|
116
149
|
@dataclass(frozen=True, slots=True)
|
|
117
150
|
class _ActionBatch:
|
|
118
151
|
"""Immutable result of preparing a batch of actions for execution.
|
|
@@ -474,7 +507,10 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
474
507
|
secret_infos = state.secret_registry.get_secret_infos()
|
|
475
508
|
|
|
476
509
|
ctx = self._build_prompt_context(additional_secret_infos=secret_infos)
|
|
477
|
-
|
|
510
|
+
# The dynamic tier is preset-independent; fall back to the default tier for a
|
|
511
|
+
# custom Jinja template (preset None), as before.
|
|
512
|
+
preset = self._prompt_preset or PromptPreset.DEFAULT
|
|
513
|
+
return create_registry(preset).build(ctx).dynamic
|
|
478
514
|
|
|
479
515
|
def _execute_actions(
|
|
480
516
|
self,
|
|
@@ -572,6 +608,10 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
572
608
|
"skipping hook check for legacy conversation state."
|
|
573
609
|
)
|
|
574
610
|
|
|
611
|
+
# Build per-conversation context once and thread it through all
|
|
612
|
+
# LLM calls in this step (avoids shared mutable state on the LLM).
|
|
613
|
+
call_context: LLMCallContext = conversation.get_llm_call_context()
|
|
614
|
+
|
|
575
615
|
# Prepare LLM messages from the cached, incrementally-maintained view.
|
|
576
616
|
# See https://github.com/OpenHands/software-agent-sdk/issues/3053.
|
|
577
617
|
_messages_or_condensation = prepare_llm_messages(
|
|
@@ -585,6 +625,20 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
585
625
|
|
|
586
626
|
_messages = _messages_or_condensation
|
|
587
627
|
|
|
628
|
+
if _should_handle_non_multimodal_image_input(self.llm, _messages):
|
|
629
|
+
logger.info(
|
|
630
|
+
"Image input received while selected model does not support vision: %s",
|
|
631
|
+
self.llm.model,
|
|
632
|
+
)
|
|
633
|
+
on_event(
|
|
634
|
+
MessageEvent(
|
|
635
|
+
source="agent",
|
|
636
|
+
llm_message=_non_multimodal_image_message(self.llm.model),
|
|
637
|
+
)
|
|
638
|
+
)
|
|
639
|
+
state.execution_status = ConversationExecutionStatus.FINISHED
|
|
640
|
+
return
|
|
641
|
+
|
|
588
642
|
logger.debug(
|
|
589
643
|
"Sending messages to LLM: "
|
|
590
644
|
f"{json.dumps([m.model_dump() for m in _messages[1:]], indent=2)}"
|
|
@@ -596,6 +650,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
596
650
|
_messages,
|
|
597
651
|
tools=list(self.tools_map.values()),
|
|
598
652
|
on_token=on_token,
|
|
653
|
+
call_context=call_context,
|
|
599
654
|
)
|
|
600
655
|
except FunctionCallValidationError as e:
|
|
601
656
|
logger.warning(f"LLM generated malformed function call: {e}")
|
|
@@ -735,6 +790,8 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
735
790
|
"skipping hook check for legacy conversation state."
|
|
736
791
|
)
|
|
737
792
|
|
|
793
|
+
call_context: LLMCallContext = conversation.get_llm_call_context()
|
|
794
|
+
|
|
738
795
|
# Prepare LLM messages from the cached, incrementally-maintained view.
|
|
739
796
|
# See https://github.com/OpenHands/software-agent-sdk/issues/3053.
|
|
740
797
|
_messages_or_condensation = await aprepare_llm_messages(
|
|
@@ -747,6 +804,20 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
747
804
|
|
|
748
805
|
_messages = _messages_or_condensation
|
|
749
806
|
|
|
807
|
+
if _should_handle_non_multimodal_image_input(self.llm, _messages):
|
|
808
|
+
logger.info(
|
|
809
|
+
"Image input received while selected model does not support vision: %s",
|
|
810
|
+
self.llm.model,
|
|
811
|
+
)
|
|
812
|
+
on_event(
|
|
813
|
+
MessageEvent(
|
|
814
|
+
source="agent",
|
|
815
|
+
llm_message=_non_multimodal_image_message(self.llm.model),
|
|
816
|
+
)
|
|
817
|
+
)
|
|
818
|
+
state.execution_status = ConversationExecutionStatus.FINISHED
|
|
819
|
+
return
|
|
820
|
+
|
|
750
821
|
logger.debug(
|
|
751
822
|
"Sending messages to LLM: "
|
|
752
823
|
f"{json.dumps([m.model_dump() for m in _messages[1:]], indent=2)}"
|
|
@@ -758,6 +829,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
758
829
|
_messages,
|
|
759
830
|
tools=list(self.tools_map.values()),
|
|
760
831
|
on_token=on_token,
|
|
832
|
+
call_context=call_context,
|
|
761
833
|
)
|
|
762
834
|
except FunctionCallValidationError as e:
|
|
763
835
|
logger.warning(f"LLM generated malformed function call: {e}")
|
|
@@ -26,7 +26,7 @@ from pydantic import (
|
|
|
26
26
|
|
|
27
27
|
from openhands.sdk.context.agent_context import AgentContext
|
|
28
28
|
from openhands.sdk.context.condenser import CondenserBase
|
|
29
|
-
from openhands.sdk.context.prompts.presets import create_registry
|
|
29
|
+
from openhands.sdk.context.prompts.presets import PromptPreset, create_registry
|
|
30
30
|
from openhands.sdk.context.prompts.prompt import render_template
|
|
31
31
|
from openhands.sdk.context.prompts.section import Platform, PromptContext
|
|
32
32
|
from openhands.sdk.critic.base import CriticBase
|
|
@@ -111,12 +111,21 @@ _DEFAULT_SOUL = (
|
|
|
111
111
|
" with a computer to solve tasks."
|
|
112
112
|
)
|
|
113
113
|
|
|
114
|
-
# Built-in prompt dir. The registry only stands in for
|
|
115
|
-
# subclass with its own prompts/
|
|
114
|
+
# Built-in prompt dir. The registry only stands in for built-in prompts here; a
|
|
115
|
+
# subclass with its own prompts/ keeps the Jinja render path.
|
|
116
116
|
_BUILTIN_PROMPT_DIR = os.path.realpath(
|
|
117
117
|
os.path.join(os.path.dirname(__file__), "prompts")
|
|
118
118
|
)
|
|
119
119
|
|
|
120
|
+
# Built-in ``system_prompt_filename`` values are back-compat sentinels (the .j2 files
|
|
121
|
+
# were removed) that select a registry preset. ``system_prompt_planning.j2`` keeps its
|
|
122
|
+
# historical name so ``get_planning_agent`` needs no change. Any other filename -- or a
|
|
123
|
+
# subclass's own ``prompt_dir`` -- falls through to the Jinja escape hatch.
|
|
124
|
+
_PRESET_BY_FILENAME: dict[str, PromptPreset] = {
|
|
125
|
+
"system_prompt.j2": PromptPreset.DEFAULT,
|
|
126
|
+
"system_prompt_planning.j2": PromptPreset.PLANNING,
|
|
127
|
+
}
|
|
128
|
+
|
|
120
129
|
|
|
121
130
|
def _load_soul_md() -> str:
|
|
122
131
|
"""Load ``~/.openhands/SOUL.md``, falling back to the built-in default."""
|
|
@@ -461,6 +470,18 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
461
470
|
"""Returns the name of the Agent."""
|
|
462
471
|
return self.__class__.__name__
|
|
463
472
|
|
|
473
|
+
@property
|
|
474
|
+
def _prompt_preset(self) -> PromptPreset | None:
|
|
475
|
+
"""The registry preset for this agent's built-in prompt.
|
|
476
|
+
|
|
477
|
+
``None`` means "take the Jinja escape hatch": a subclass with its own
|
|
478
|
+
``prompt_dir``, or a ``system_prompt_filename`` that is not a known built-in
|
|
479
|
+
sentinel (e.g. a custom relative name or an absolute path).
|
|
480
|
+
"""
|
|
481
|
+
if os.path.realpath(self.prompt_dir) != _BUILTIN_PROMPT_DIR:
|
|
482
|
+
return None
|
|
483
|
+
return _PRESET_BY_FILENAME.get(self.system_prompt_filename)
|
|
484
|
+
|
|
464
485
|
@property
|
|
465
486
|
def static_system_message(self) -> str:
|
|
466
487
|
"""Compute the static portion of the system message.
|
|
@@ -469,10 +490,11 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
469
490
|
per-conversation context. This static portion can be cached and reused
|
|
470
491
|
across conversations for better prompt caching efficiency.
|
|
471
492
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
``
|
|
493
|
+
Built-in prompts (the ``default`` and ``planning`` presets) are assembled from
|
|
494
|
+
the typed section registry, which also resolves a custom
|
|
495
|
+
``security_policy_filename``. Escape hatches keep the Jinja path: an inline
|
|
496
|
+
``system_prompt`` is returned verbatim; a custom ``system_prompt_filename`` or
|
|
497
|
+
subclass ``prompt_dir`` renders its own template.
|
|
476
498
|
|
|
477
499
|
Returns:
|
|
478
500
|
The static system prompt without dynamic context.
|
|
@@ -482,17 +504,15 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
482
504
|
|
|
483
505
|
# Escape hatch: a custom filename or a subclass's own prompt_dir renders its
|
|
484
506
|
# own Jinja template; everything else (incl. custom policies) uses the registry.
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
or os.path.realpath(self.prompt_dir) != _BUILTIN_PROMPT_DIR
|
|
488
|
-
):
|
|
507
|
+
preset = self._prompt_preset
|
|
508
|
+
if preset is None:
|
|
489
509
|
return render_template(
|
|
490
510
|
prompt_dir=self.prompt_dir,
|
|
491
511
|
template_name=self.system_prompt_filename,
|
|
492
512
|
**self._resolved_template_kwargs(),
|
|
493
513
|
)
|
|
494
514
|
|
|
495
|
-
return create_registry().build(self._build_prompt_context()).static
|
|
515
|
+
return create_registry(preset).build(self._build_prompt_context()).static
|
|
496
516
|
|
|
497
517
|
def _resolved_template_kwargs(self) -> dict[str, object]:
|
|
498
518
|
"""Resolve the system-prompt template kwargs.
|
|
@@ -640,7 +660,10 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
640
660
|
"""
|
|
641
661
|
if not self.agent_context:
|
|
642
662
|
return None
|
|
643
|
-
|
|
663
|
+
# The dynamic tier is preset-independent, so a custom Jinja template (preset
|
|
664
|
+
# None) still gets the default dynamic block, exactly as before.
|
|
665
|
+
preset = self._prompt_preset or PromptPreset.DEFAULT
|
|
666
|
+
return create_registry(preset).build(self._build_prompt_context()).dynamic
|
|
644
667
|
|
|
645
668
|
def init_state(
|
|
646
669
|
self,
|
|
@@ -31,6 +31,7 @@ from openhands.sdk.tool import Action, ToolDefinition
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
if TYPE_CHECKING:
|
|
34
|
+
from openhands.sdk.llm.llm import LLMCallContext
|
|
34
35
|
from openhands.sdk.llm.streaming import AnyTokenCallbackType
|
|
35
36
|
|
|
36
37
|
|
|
@@ -604,6 +605,7 @@ def make_llm_completion(
|
|
|
604
605
|
messages: list[Message],
|
|
605
606
|
tools: list[ToolDefinition] | None = None,
|
|
606
607
|
on_token: ConversationTokenCallbackType | None = None,
|
|
608
|
+
call_context: LLMCallContext | None = None,
|
|
607
609
|
) -> LLMResponse:
|
|
608
610
|
"""Make an LLM completion call with the provided messages and tools.
|
|
609
611
|
|
|
@@ -612,6 +614,7 @@ def make_llm_completion(
|
|
|
612
614
|
messages: The messages to send to the LLM
|
|
613
615
|
tools: Optional list of tools to provide to the LLM
|
|
614
616
|
on_token: Optional callback for streaming token updates
|
|
617
|
+
call_context: Per-conversation context for cache/session affinity.
|
|
615
618
|
|
|
616
619
|
Returns:
|
|
617
620
|
LLMResponse from the LLM completion call
|
|
@@ -636,6 +639,7 @@ def make_llm_completion(
|
|
|
636
639
|
store=False,
|
|
637
640
|
add_security_risk_prediction=True,
|
|
638
641
|
on_token=on_token,
|
|
642
|
+
call_context=call_context,
|
|
639
643
|
)
|
|
640
644
|
else:
|
|
641
645
|
return llm.completion(
|
|
@@ -643,6 +647,7 @@ def make_llm_completion(
|
|
|
643
647
|
tools=tools or [],
|
|
644
648
|
add_security_risk_prediction=True,
|
|
645
649
|
on_token=on_token,
|
|
650
|
+
call_context=call_context,
|
|
646
651
|
)
|
|
647
652
|
|
|
648
653
|
|
|
@@ -686,6 +691,7 @@ async def amake_llm_completion(
|
|
|
686
691
|
messages: list[Message],
|
|
687
692
|
tools: list[ToolDefinition] | None = None,
|
|
688
693
|
on_token: AnyTokenCallbackType | None = None,
|
|
694
|
+
call_context: LLMCallContext | None = None,
|
|
689
695
|
) -> LLMResponse:
|
|
690
696
|
"""Async variant of :func:`make_llm_completion`."""
|
|
691
697
|
if llm.uses_responses_api():
|
|
@@ -696,6 +702,7 @@ async def amake_llm_completion(
|
|
|
696
702
|
store=False,
|
|
697
703
|
add_security_risk_prediction=True,
|
|
698
704
|
on_token=on_token,
|
|
705
|
+
call_context=call_context,
|
|
699
706
|
)
|
|
700
707
|
else:
|
|
701
708
|
return await llm.acompletion(
|
|
@@ -703,4 +710,5 @@ async def amake_llm_completion(
|
|
|
703
710
|
tools=tools or [],
|
|
704
711
|
add_security_risk_prediction=True,
|
|
705
712
|
on_token=on_token,
|
|
713
|
+
call_context=call_context,
|
|
706
714
|
)
|
|
@@ -118,7 +118,8 @@ class AgentContext(BaseModel):
|
|
|
118
118
|
default_factory=list,
|
|
119
119
|
description=(
|
|
120
120
|
"Marketplace registrations for plugin resolution. Registrations with "
|
|
121
|
-
"auto_load=True are resolved by
|
|
121
|
+
"auto_load=True or a list of plugin names are resolved by "
|
|
122
|
+
"LocalConversation at startup."
|
|
122
123
|
),
|
|
123
124
|
json_schema_extra={"acp_compatible": True},
|
|
124
125
|
)
|
|
@@ -149,8 +150,8 @@ class AgentContext(BaseModel):
|
|
|
149
150
|
json_schema_extra={"acp_compatible": True},
|
|
150
151
|
)
|
|
151
152
|
current_datetime: datetime | str | None = Field(
|
|
152
|
-
# Timezone-aware local "now"
|
|
153
|
-
#
|
|
153
|
+
# Timezone-aware local "now"; get_formatted_datetime renders it to the
|
|
154
|
+
# minute for the prompt.
|
|
154
155
|
default_factory=lambda: datetime.now().astimezone(),
|
|
155
156
|
description=(
|
|
156
157
|
"Current date and time information to provide to the agent. "
|
|
@@ -265,13 +266,17 @@ class AgentContext(BaseModel):
|
|
|
265
266
|
|
|
266
267
|
Returns:
|
|
267
268
|
Formatted datetime string, or None if current_datetime is not set.
|
|
268
|
-
If current_datetime is a datetime object, it's formatted as
|
|
269
|
+
If current_datetime is a datetime object, it's formatted as
|
|
270
|
+
"YYYY-MM-DDTHH:MM" (no seconds, microseconds, or UTC offset).
|
|
269
271
|
If current_datetime is already a string, it's returned as-is.
|
|
270
272
|
"""
|
|
271
273
|
if self.current_datetime is None:
|
|
272
274
|
return None
|
|
273
275
|
if isinstance(self.current_datetime, datetime):
|
|
274
|
-
|
|
276
|
+
# Local wall-clock to the minute: drop seconds, microseconds, offset.
|
|
277
|
+
return self.current_datetime.replace(tzinfo=None).isoformat(
|
|
278
|
+
timespec="minutes"
|
|
279
|
+
)
|
|
275
280
|
return self.current_datetime
|
|
276
281
|
|
|
277
282
|
def _partition_skills(self) -> tuple[list[Skill], list[Skill]]:
|
|
@@ -80,6 +80,16 @@ class LLMSummarizingCondenser(RollingCondenser):
|
|
|
80
80
|
)
|
|
81
81
|
return self
|
|
82
82
|
|
|
83
|
+
@model_validator(mode="after")
|
|
84
|
+
def _disable_streaming_for_summary(self):
|
|
85
|
+
# Summaries are consumed whole with no on_token callback, which a
|
|
86
|
+
# streaming LLM requires. Disable streaming once so every summary path
|
|
87
|
+
# is covered. model_copy is non-mutating and shares usage_id/metrics,
|
|
88
|
+
# so summary tokens stay attributed to the conversation.
|
|
89
|
+
if self.llm.stream:
|
|
90
|
+
self.llm = self.llm.model_copy(update={"stream": False})
|
|
91
|
+
return self
|
|
92
|
+
|
|
83
93
|
def handles_condensation_requests(self) -> bool:
|
|
84
94
|
return True
|
|
85
95
|
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Named :class:`PromptRegistry` presets -- ready-to-use section compositions.
|
|
2
|
+
|
|
3
|
+
``create_registry()`` selects a section composition over the same engine. The
|
|
4
|
+
``"default"`` preset registers the static-tier sections in the exact order
|
|
5
|
+
``agent/prompts/system_prompt.j2`` emitted them, so ``registry.build(ctx).static``
|
|
6
|
+
reproduces ``AgentBase.static_system_message``. The ``"planning"`` preset is a
|
|
7
|
+
distinct standalone composition (ported from ``system_prompt_planning.j2``) that
|
|
8
|
+
omits the default OpenHands sections. The dynamic-tier sections are **shared** --
|
|
9
|
+
datetime/repo/skills/suffix/secrets are preset-independent -- so a planning agent
|
|
10
|
+
with an ``agent_context`` still gets its dynamic block.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from enum import StrEnum
|
|
14
|
+
from typing import Final
|
|
15
|
+
|
|
16
|
+
from openhands.sdk.context.prompts.registry import PromptRegistry
|
|
17
|
+
from openhands.sdk.context.prompts.section import PromptSection
|
|
18
|
+
from openhands.sdk.context.prompts.sections.dynamic import (
|
|
19
|
+
AvailableSkillsSection,
|
|
20
|
+
CustomSecretsSection,
|
|
21
|
+
CustomSuffixSection,
|
|
22
|
+
DateTimeSection,
|
|
23
|
+
RepoContextSection,
|
|
24
|
+
)
|
|
25
|
+
from openhands.sdk.context.prompts.sections.planning import PlanningSection
|
|
26
|
+
from openhands.sdk.context.prompts.sections.static import (
|
|
27
|
+
BrowserSection,
|
|
28
|
+
CodeQualitySection,
|
|
29
|
+
EfficiencySection,
|
|
30
|
+
EnvironmentSetupSection,
|
|
31
|
+
ExternalServicesSection,
|
|
32
|
+
FileSystemSection,
|
|
33
|
+
MemorySection,
|
|
34
|
+
ModelSpecificSection,
|
|
35
|
+
ProblemSolvingSection,
|
|
36
|
+
ProcessManagementSection,
|
|
37
|
+
PullRequestsSection,
|
|
38
|
+
RoleSection,
|
|
39
|
+
SecurityRiskAssessmentSection,
|
|
40
|
+
SecuritySection,
|
|
41
|
+
SelfDocumentationSection,
|
|
42
|
+
SoulSection,
|
|
43
|
+
TroubleshootingSection,
|
|
44
|
+
VersionControlSection,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
__all__ = ["PromptPreset", "create_registry"]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class PromptPreset(StrEnum):
|
|
52
|
+
"""Names a :func:`create_registry` section composition."""
|
|
53
|
+
|
|
54
|
+
DEFAULT = "default"
|
|
55
|
+
PLANNING = "planning"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
_DEFAULT_STATIC_SECTIONS: Final[tuple[PromptSection, ...]] = (
|
|
59
|
+
SoulSection(),
|
|
60
|
+
RoleSection(),
|
|
61
|
+
MemorySection(),
|
|
62
|
+
EfficiencySection(),
|
|
63
|
+
FileSystemSection(),
|
|
64
|
+
CodeQualitySection(),
|
|
65
|
+
VersionControlSection(),
|
|
66
|
+
PullRequestsSection(),
|
|
67
|
+
ProblemSolvingSection(),
|
|
68
|
+
SelfDocumentationSection(),
|
|
69
|
+
SecuritySection(), # guard: security_policy_filename set
|
|
70
|
+
SecurityRiskAssessmentSection(), # guard: llm_security_analyzer
|
|
71
|
+
BrowserSection(), # guard: ctx.enable_browser
|
|
72
|
+
ExternalServicesSection(),
|
|
73
|
+
EnvironmentSetupSection(),
|
|
74
|
+
TroubleshootingSection(),
|
|
75
|
+
ProcessManagementSection(),
|
|
76
|
+
ModelSpecificSection(), # guard: model_family resolved
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
_PLANNING_STATIC_SECTIONS: Final[tuple[PromptSection, ...]] = (PlanningSection(),)
|
|
80
|
+
|
|
81
|
+
_DYNAMIC_SECTIONS: Final[tuple[PromptSection, ...]] = (
|
|
82
|
+
DateTimeSection(),
|
|
83
|
+
RepoContextSection(), # guard: gated repo skills present
|
|
84
|
+
AvailableSkillsSection(), # guard: available_skills_prompt
|
|
85
|
+
CustomSuffixSection(), # guard: system_message_suffix
|
|
86
|
+
CustomSecretsSection(), # guard: secret_infos present
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def create_registry(preset: PromptPreset = PromptPreset.DEFAULT) -> PromptRegistry:
|
|
91
|
+
"""Build the section registry for ``preset``.
|
|
92
|
+
|
|
93
|
+
``DEFAULT`` is the standard OpenHands composition; ``PLANNING`` is the read-only
|
|
94
|
+
analysis composition (no ``<SECURITY>``/``<SOUL>``/``<MEMORY>`` ...). Both share
|
|
95
|
+
the dynamic tier. Sections are stateless, so the per-preset sequences are reused
|
|
96
|
+
across calls.
|
|
97
|
+
"""
|
|
98
|
+
match preset:
|
|
99
|
+
case PromptPreset.PLANNING:
|
|
100
|
+
static_sections = _PLANNING_STATIC_SECTIONS
|
|
101
|
+
case PromptPreset.DEFAULT:
|
|
102
|
+
static_sections = _DEFAULT_STATIC_SECTIONS
|
|
103
|
+
case _:
|
|
104
|
+
raise ValueError(f"Unknown prompt preset: {preset!r}")
|
|
105
|
+
|
|
106
|
+
r = PromptRegistry()
|
|
107
|
+
for section in (*static_sections, *_DYNAMIC_SECTIONS):
|
|
108
|
+
r.register(section)
|
|
109
|
+
return r
|
{openhands_sdk-1.29.3 → openhands_sdk-1.30.0}/openhands/sdk/context/prompts/sections/__init__.py
RENAMED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
Sections are pure, synchronous functions of a :class:`PromptContext` -- no Jinja
|
|
4
4
|
environment, no filesystem loader, no ``render_template`` bootstrap -- so each one
|
|
5
5
|
unit-tests in isolation. The static tier (ported from ``system_prompt.j2``) lives
|
|
6
|
-
in :mod:`.static
|
|
6
|
+
in :mod:`.static`; the standalone planning composition (ported from
|
|
7
|
+
``system_prompt_planning.j2``) in :mod:`.planning`.
|
|
7
8
|
"""
|
|
8
9
|
|
|
9
10
|
from openhands.sdk.context.prompts.sections.dynamic import (
|
|
@@ -13,6 +14,7 @@ from openhands.sdk.context.prompts.sections.dynamic import (
|
|
|
13
14
|
DateTimeSection,
|
|
14
15
|
RepoContextSection,
|
|
15
16
|
)
|
|
17
|
+
from openhands.sdk.context.prompts.sections.planning import PlanningSection
|
|
16
18
|
from openhands.sdk.context.prompts.sections.static import (
|
|
17
19
|
BrowserSection,
|
|
18
20
|
CodeQualitySection,
|
|
@@ -48,6 +50,7 @@ __all__ = [
|
|
|
48
50
|
"FileSystemSection",
|
|
49
51
|
"MemorySection",
|
|
50
52
|
"ModelSpecificSection",
|
|
53
|
+
"PlanningSection",
|
|
51
54
|
"ProblemSolvingSection",
|
|
52
55
|
"ProcessManagementSection",
|
|
53
56
|
"PullRequestsSection",
|
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
"""The planning system prompt.
|
|
2
|
+
|
|
3
|
+
Unlike the default composition -- whose blocks each carry a guard and can be overridden
|
|
4
|
+
individually -- the planning prompt is a single standalone STATIC block with no
|
|
5
|
+
per-section guards, so it is one section. The only substitution is ``plan_structure``;
|
|
6
|
+
``tests/sdk/context/prompts/test_planning_registry.py`` pins the output against a golden
|
|
7
|
+
snapshot. No ``_refine``: the text mentions no ``bash``/``terminal`` (its ``<EFFICIENCY>``
|
|
8
|
+
says ``glob and grep``), so the Windows shell substitution is a no-op.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
# The body is verbatim long-form prompt text; wrapping a line would change the rendered
|
|
12
|
+
# bytes, so line-length (E501) is disabled for this whole file.
|
|
13
|
+
# ruff: noqa: E501
|
|
14
|
+
|
|
15
|
+
from openhands.sdk.context.prompts.section import CacheTier, PromptContext
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
__all__ = ["PlanningSection"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PlanningSection:
|
|
22
|
+
"""The full planning system prompt as one STATIC block.
|
|
23
|
+
|
|
24
|
+
The only substitution is ``plan_structure`` (an empty value reproduces the
|
|
25
|
+
template's ``<PLAN_STRUCTURE>\\n\\n</PLAN_STRUCTURE>`` output verbatim).
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
name = "planning"
|
|
29
|
+
cache_tier = CacheTier.STATIC
|
|
30
|
+
|
|
31
|
+
_BODY = """\
|
|
1
32
|
You are a Planning Agent that analyzes codebases and helps the user make a detailed plan for their requested changes.
|
|
2
33
|
|
|
3
34
|
<ROLE>
|
|
@@ -92,5 +123,12 @@ Follow this enhanced planning workflow to create well-researched, user-aligned p
|
|
|
92
123
|
</PLAN_SCOPE>
|
|
93
124
|
|
|
94
125
|
<PLAN_STRUCTURE>
|
|
95
|
-
{
|
|
96
|
-
</PLAN_STRUCTURE>
|
|
126
|
+
{plan_structure}
|
|
127
|
+
</PLAN_STRUCTURE>"""
|
|
128
|
+
|
|
129
|
+
def guard(self, ctx: PromptContext) -> bool: # noqa: ARG002
|
|
130
|
+
return True
|
|
131
|
+
|
|
132
|
+
def render(self, ctx: PromptContext) -> str | None:
|
|
133
|
+
plan_structure = str(ctx.template_kwargs.get("plan_structure", ""))
|
|
134
|
+
return self._BODY.replace("{plan_structure}", plan_structure)
|
|
@@ -18,6 +18,7 @@ from openhands.sdk.observability.laminar import (
|
|
|
18
18
|
RootSpan,
|
|
19
19
|
end_root_span,
|
|
20
20
|
should_enable_observability,
|
|
21
|
+
start_child_span,
|
|
21
22
|
start_root_span,
|
|
22
23
|
)
|
|
23
24
|
from openhands.sdk.security.analyzer import SecurityAnalyzerBase
|
|
@@ -139,6 +140,7 @@ class BaseConversation(ABC):
|
|
|
139
140
|
def _start_observability_span(
|
|
140
141
|
self,
|
|
141
142
|
session_id: str,
|
|
143
|
+
span_name: str = "conversation",
|
|
142
144
|
user_id: str | None = None,
|
|
143
145
|
metadata: dict[str, TraceMetadataValue] | None = None,
|
|
144
146
|
tags: list[str] | None = None,
|
|
@@ -148,6 +150,7 @@ class BaseConversation(ABC):
|
|
|
148
150
|
|
|
149
151
|
Args:
|
|
150
152
|
session_id: The session ID to associate with the trace
|
|
153
|
+
span_name: Optional child span name to emit under the conversation root.
|
|
151
154
|
user_id: Optional user ID to associate with the trace
|
|
152
155
|
metadata: Optional trace-level metadata to attach to observability backends
|
|
153
156
|
tags: Optional span tags to attach to the conversation root span
|
|
@@ -166,12 +169,15 @@ class BaseConversation(ABC):
|
|
|
166
169
|
tags=tags,
|
|
167
170
|
attributes=_conversation_tag_attributes(conversation_tags),
|
|
168
171
|
)
|
|
172
|
+
if span_name != "conversation":
|
|
173
|
+
start_child_span(self._observability_root_span, span_name, tags=tags)
|
|
169
174
|
|
|
170
175
|
def _end_observability_span(self) -> None:
|
|
171
176
|
"""End the observability span if it hasn't been ended already."""
|
|
172
177
|
if self._span_ended:
|
|
173
178
|
return
|
|
174
|
-
|
|
179
|
+
if self._observability_root_span is not None:
|
|
180
|
+
end_root_span(self._observability_root_span)
|
|
175
181
|
self._observability_root_span = None
|
|
176
182
|
self._span_ended = True
|
|
177
183
|
|
|
@@ -378,6 +384,19 @@ class BaseConversation(ABC):
|
|
|
378
384
|
"""
|
|
379
385
|
...
|
|
380
386
|
|
|
387
|
+
def load_plugin(self, plugin_ref: str) -> None:
|
|
388
|
+
"""Load a plugin from a registered marketplace.
|
|
389
|
+
|
|
390
|
+
Implementations that support marketplace-registered plugins resolve the
|
|
391
|
+
reference against the conversation agent's registered marketplaces and
|
|
392
|
+
merge the plugin's skills, hooks, and MCP configuration into the agent.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
plugin_ref: Plugin reference, either ``plugin-name`` or
|
|
396
|
+
``plugin-name@marketplace-name``.
|
|
397
|
+
"""
|
|
398
|
+
raise NotImplementedError("This conversation does not support loading plugins")
|
|
399
|
+
|
|
381
400
|
@abstractmethod
|
|
382
401
|
def fork(
|
|
383
402
|
self,
|