openhands-sdk 1.29.2__tar.gz → 1.29.3__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.2 → openhands_sdk-1.29.3}/PKG-INFO +1 -1
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/agent/agent.py +45 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/agent/base.py +43 -16
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/agent_context.py +19 -26
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/prompts/presets.py +3 -3
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/prompts/sections/dynamic.py +1 -2
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/prompts/sections/static.py +10 -5
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/impl/local_conversation.py +60 -1
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/exceptions/__init__.py +4 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/exceptions/classifier.py +38 -21
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/exceptions/mapping.py +7 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/exceptions/types.py +14 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/utils/retry_mixin.py +9 -1
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/plugin/__init__.py +9 -0
- openhands_sdk-1.29.3/openhands/sdk/plugin/discovery.py +203 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands_sdk.egg-info/PKG-INFO +1 -1
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands_sdk.egg-info/SOURCES.txt +2 -28
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/pyproject.toml +1 -1
- openhands_sdk-1.29.2/openhands/sdk/agent/prompts/in_context_learning_example.j2 +0 -175
- openhands_sdk-1.29.2/openhands/sdk/agent/prompts/in_context_learning_example_suffix.j2 +0 -3
- openhands_sdk-1.29.2/openhands/sdk/agent/prompts/model_specific/anthropic_claude.j2 +0 -3
- openhands_sdk-1.29.2/openhands/sdk/agent/prompts/model_specific/google_gemini.j2 +0 -1
- openhands_sdk-1.29.2/openhands/sdk/agent/prompts/model_specific/openai_gpt/gpt-5-codex.j2 +0 -2
- openhands_sdk-1.29.2/openhands/sdk/agent/prompts/model_specific/openai_gpt/gpt-5.j2 +0 -18
- openhands_sdk-1.29.2/openhands/sdk/agent/prompts/security_policy.j2 +0 -25
- openhands_sdk-1.29.2/openhands/sdk/agent/prompts/security_risk_assessment.j2 +0 -31
- openhands_sdk-1.29.2/openhands/sdk/agent/prompts/self_documentation.j2 +0 -15
- openhands_sdk-1.29.2/openhands/sdk/agent/prompts/system_prompt.j2 +0 -152
- openhands_sdk-1.29.2/openhands/sdk/agent/prompts/system_prompt_interactive.j2 +0 -14
- openhands_sdk-1.29.2/openhands/sdk/agent/prompts/system_prompt_long_horizon.j2 +0 -40
- openhands_sdk-1.29.2/openhands/sdk/agent/prompts/system_prompt_tech_philosophy.j2 +0 -122
- openhands_sdk-1.29.2/openhands/sdk/context/prompts/templates/system_message_suffix.j2 +0 -52
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/agent/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/agent/acp_agent.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/agent/acp_models.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/agent/critic_mixin.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/agent/parallel_executor.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/agent/prompts/system_prompt_planning.j2 +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/agent/response_dispatch.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/agent/utils.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/banner.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/condenser/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/condenser/base.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/condenser/llm_summarizing_condenser.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/condenser/no_op_condenser.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/condenser/pipeline_condenser.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/condenser/prompts/summarizing_prompt.j2 +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/condenser/utils.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/prompts/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/prompts/prompt.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/prompts/registry.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/prompts/section.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/prompts/sections/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/prompts/templates/ask_agent_template.j2 +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/view/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/view/manipulation_indices.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/view/properties/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/view/properties/base.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/view/properties/batch_atomicity.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/view/properties/observation_uniqueness.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/view/properties/tool_call_matching.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/view/properties/tool_loop_atomicity.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/view/view.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/base.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/cancellation.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/conversation.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/conversation_stats.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/event_store.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/events_list_base.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/exceptions.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/fifo_lock.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/goal/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/goal/controller.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/goal/judge.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/goal/prompts.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/goal/runner.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/impl/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/impl/remote_conversation.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/persistence_const.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/request.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/resource_lock_manager.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/response_utils.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/secret_registry.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/state.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/stuck_detector.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/title_utils.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/types.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/visualizer/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/visualizer/base.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/visualizer/default.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/critic/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/critic/base.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/critic/impl/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/critic/impl/agent_finished.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/critic/impl/api/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/critic/impl/api/chat_template.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/critic/impl/api/client.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/critic/impl/api/critic.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/critic/impl/api/taxonomy.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/critic/impl/empty_patch.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/critic/impl/pass_critic.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/critic/result.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/acp_tool_call.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/base.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/condenser.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/conversation_error.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/conversation_state.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/hook_execution.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/llm_completion_log.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/llm_convertible/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/llm_convertible/action.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/llm_convertible/message.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/llm_convertible/observation.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/llm_convertible/reasoning_utils.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/llm_convertible/system.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/resume_transcript.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/streaming_delta.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/token.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/types.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/event/user_action.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/extensions/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/extensions/fetch.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/extensions/installation/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/extensions/installation/info.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/extensions/installation/interface.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/extensions/installation/manager.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/extensions/installation/metadata.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/extensions/installation/utils.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/git/cached_repo.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/git/exceptions.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/git/git_changes.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/git/git_diff.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/git/models.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/git/utils.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/hooks/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/hooks/config.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/hooks/conversation_hooks.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/hooks/executor.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/hooks/manager.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/hooks/types.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/io/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/io/base.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/io/cache.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/io/local.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/io/memory.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/auth/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/auth/credentials.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/auth/openai.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/fallback_strategy.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/llm.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/llm_profile_store.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/llm_registry.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/llm_response.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/message.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/mixins/fn_call_converter.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/mixins/fn_call_examples.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/mixins/non_native_fc.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/options/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/options/chat_options.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/options/common.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/options/responses_options.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/router/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/router/base.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/router/impl/multimodal.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/router/impl/random.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/streaming.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/utils/image_inline.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/utils/image_resize.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/utils/litellm_provider.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/utils/metrics.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/utils/model_features.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/utils/model_info.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/utils/model_prompt_spec.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/utils/openhands_provider.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/utils/responses_serialization.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/utils/telemetry.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/utils/unverified_models.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/utils/verified_models.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/llm/utils/vertex_preflight.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/logger/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/logger/logger.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/logger/rolling.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/marketplace/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/marketplace/registration.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/marketplace/registry.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/marketplace/types.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/mcp/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/mcp/client.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/mcp/definition.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/mcp/exceptions.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/mcp/tool.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/mcp/utils.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/observability/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/observability/laminar.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/observability/utils.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/plugin/fetch.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/plugin/installed.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/plugin/loader.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/plugin/plugin.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/plugin/source.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/plugin/types.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/profiles/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/profiles/agent_profile.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/profiles/agent_profile_store.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/profiles/profile_refs.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/profiles/resolver.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/py.typed +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/secret/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/secret/secrets.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/security/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/security/_shell_ast.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/security/analyzer.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/security/confirmation_policy.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/security/defense_in_depth/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/security/defense_in_depth/pattern.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/security/defense_in_depth/policy_rails.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/security/defense_in_depth/utils.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/security/ensemble.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/security/grayswan/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/security/grayswan/analyzer.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/security/grayswan/utils.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/security/llm_analyzer.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/security/risk.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/security/shell_parser.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/settings/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/settings/acp_providers.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/settings/api_models.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/settings/metadata.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/settings/model.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/skills/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/skills/exceptions.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/skills/execute.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/skills/fetch.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/skills/installed.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/skills/skill.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/skills/trigger.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/skills/types.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/skills/utils.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/subagent/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/subagent/load.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/subagent/registry.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/subagent/schema.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/testing/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/testing/test_llm.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/tool/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/tool/builtins/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/tool/builtins/finish.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/tool/builtins/invoke_skill.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/tool/builtins/switch_llm.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/tool/builtins/think.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/tool/client_tool.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/tool/registry.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/tool/schema.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/tool/spec.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/tool/tool.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/utils/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/utils/async_executor.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/utils/async_utils.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/utils/cipher.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/utils/command.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/utils/datetime.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/utils/deprecation.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/utils/github.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/utils/json.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/utils/models.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/utils/paging.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/utils/path.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/utils/pydantic_secrets.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/utils/redact.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/utils/truncate.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/utils/visualize.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/workspace/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/workspace/base.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/workspace/local.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/workspace/models.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/workspace/remote/__init__.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/workspace/remote/async_remote_workspace.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/workspace/remote/base.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/workspace/remote/remote_workspace_mixin.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/workspace/repo.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/workspace/workspace.py +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands_sdk.egg-info/dependency_links.txt +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands_sdk.egg-info/requires.txt +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands_sdk.egg-info/top_level.txt +0 -0
- {openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-sdk
|
|
3
|
-
Version: 1.29.
|
|
3
|
+
Version: 1.29.3
|
|
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
|
|
@@ -61,6 +61,7 @@ from openhands.sdk.llm import (
|
|
|
61
61
|
)
|
|
62
62
|
from openhands.sdk.llm.exceptions import (
|
|
63
63
|
FunctionCallValidationError,
|
|
64
|
+
LLMContentPolicyViolationError,
|
|
64
65
|
LLMContextWindowExceedError,
|
|
65
66
|
LLMMalformedConversationHistoryError,
|
|
66
67
|
)
|
|
@@ -607,6 +608,28 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
607
608
|
)
|
|
608
609
|
on_event(error_message)
|
|
609
610
|
return
|
|
611
|
+
except LLMContentPolicyViolationError as e:
|
|
612
|
+
# Content-policy blocks are deterministic; nudge the model and let the
|
|
613
|
+
# run loop continue instead of emitting a fatal error.
|
|
614
|
+
logger.warning(f"LLM output blocked by content filter: {e}")
|
|
615
|
+
on_event(
|
|
616
|
+
MessageEvent(
|
|
617
|
+
source="user",
|
|
618
|
+
llm_message=Message(
|
|
619
|
+
role="user",
|
|
620
|
+
content=[
|
|
621
|
+
TextContent(
|
|
622
|
+
text=(
|
|
623
|
+
"Your previous response was blocked by the "
|
|
624
|
+
"model's content filter. Please continue, "
|
|
625
|
+
"rephrasing to avoid the flagged content."
|
|
626
|
+
)
|
|
627
|
+
)
|
|
628
|
+
],
|
|
629
|
+
),
|
|
630
|
+
)
|
|
631
|
+
)
|
|
632
|
+
return
|
|
610
633
|
except LLMMalformedConversationHistoryError as e:
|
|
611
634
|
# The provider rejected the current message history as structurally
|
|
612
635
|
# invalid (for example, broken tool_use/tool_result pairing). Route
|
|
@@ -747,6 +770,28 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
|
|
|
747
770
|
)
|
|
748
771
|
on_event(error_message)
|
|
749
772
|
return
|
|
773
|
+
except LLMContentPolicyViolationError as e:
|
|
774
|
+
# Content-policy blocks are deterministic; nudge the model and let the
|
|
775
|
+
# run loop continue instead of emitting a fatal error.
|
|
776
|
+
logger.warning(f"LLM output blocked by content filter: {e}")
|
|
777
|
+
on_event(
|
|
778
|
+
MessageEvent(
|
|
779
|
+
source="user",
|
|
780
|
+
llm_message=Message(
|
|
781
|
+
role="user",
|
|
782
|
+
content=[
|
|
783
|
+
TextContent(
|
|
784
|
+
text=(
|
|
785
|
+
"Your previous response was blocked by the "
|
|
786
|
+
"model's content filter. Please continue, "
|
|
787
|
+
"rephrasing to avoid the flagged content."
|
|
788
|
+
)
|
|
789
|
+
)
|
|
790
|
+
],
|
|
791
|
+
),
|
|
792
|
+
)
|
|
793
|
+
)
|
|
794
|
+
return
|
|
750
795
|
except LLMMalformedConversationHistoryError as e:
|
|
751
796
|
# The provider rejected the current message history as
|
|
752
797
|
# structurally invalid (for example, broken
|
|
@@ -9,6 +9,7 @@ from abc import ABC, abstractmethod
|
|
|
9
9
|
from collections import Counter
|
|
10
10
|
from collections.abc import Generator, Iterable, Sequence
|
|
11
11
|
from concurrent.futures import ThreadPoolExecutor
|
|
12
|
+
from pathlib import Path
|
|
12
13
|
from typing import TYPE_CHECKING, Any, Literal
|
|
13
14
|
|
|
14
15
|
from pydantic import (
|
|
@@ -248,10 +249,14 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
248
249
|
security_policy_filename: str = Field(
|
|
249
250
|
default="security_policy.j2",
|
|
250
251
|
description=(
|
|
251
|
-
"Security policy
|
|
252
|
-
"-
|
|
253
|
-
"
|
|
254
|
-
"
|
|
252
|
+
"Security policy filename. The default 'security_policy.j2' is a "
|
|
253
|
+
"back-compat sentinel (the file was removed) that selects the built-in "
|
|
254
|
+
"default policy from the prompt registry -- it is not loaded from disk. "
|
|
255
|
+
"Any other value names a custom policy file whose contents are inserted "
|
|
256
|
+
"verbatim (NOT rendered as a Jinja template). Can be either:\n"
|
|
257
|
+
"- A relative filename (e.g., 'custom_security_policy.md') loaded from "
|
|
258
|
+
"the agent's prompts directory\n"
|
|
259
|
+
"- An absolute path (e.g., '/path/to/custom_security_policy.md')\n"
|
|
255
260
|
"- Empty string to disable security policy"
|
|
256
261
|
),
|
|
257
262
|
)
|
|
@@ -464,12 +469,10 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
464
469
|
per-conversation context. This static portion can be cached and reused
|
|
465
470
|
across conversations for better prompt caching efficiency.
|
|
466
471
|
|
|
467
|
-
The default prompt is assembled from the typed section registry
|
|
468
|
-
|
|
469
|
-
``system_prompt`` is returned verbatim; a custom
|
|
470
|
-
``system_prompt_filename``
|
|
471
|
-
with its own ``prompt_dir`` still renders its default-named template; and a
|
|
472
|
-
custom ``security_policy_filename`` renders so its policy file is included.
|
|
472
|
+
The default prompt is assembled from the typed section registry, which also
|
|
473
|
+
resolves a custom ``security_policy_filename``. Escape hatches keep the Jinja
|
|
474
|
+
path: an inline ``system_prompt`` is returned verbatim; a custom
|
|
475
|
+
``system_prompt_filename`` or subclass ``prompt_dir`` renders its own template.
|
|
473
476
|
|
|
474
477
|
Returns:
|
|
475
478
|
The static system prompt without dynamic context.
|
|
@@ -477,14 +480,11 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
477
480
|
if self.system_prompt is not None:
|
|
478
481
|
return self.system_prompt
|
|
479
482
|
|
|
480
|
-
# Escape hatch: custom
|
|
481
|
-
#
|
|
482
|
-
# the built-in default prompt (default template + default policy); a
|
|
483
|
-
# non-default security_policy_filename must keep the Jinja include path.
|
|
483
|
+
# Escape hatch: a custom filename or a subclass's own prompt_dir renders its
|
|
484
|
+
# own Jinja template; everything else (incl. custom policies) uses the registry.
|
|
484
485
|
if (
|
|
485
486
|
self.system_prompt_filename != "system_prompt.j2"
|
|
486
487
|
or os.path.realpath(self.prompt_dir) != _BUILTIN_PROMPT_DIR
|
|
487
|
-
or self.security_policy_filename != "security_policy.j2"
|
|
488
488
|
):
|
|
489
489
|
return render_template(
|
|
490
490
|
prompt_dir=self.prompt_dir,
|
|
@@ -525,6 +525,24 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
525
525
|
template_kwargs["model_variant"] = spec.variant
|
|
526
526
|
return template_kwargs
|
|
527
527
|
|
|
528
|
+
def _read_custom_security_policy(self) -> str | None:
|
|
529
|
+
"""Raw contents of a custom security policy file -- inserted verbatim, NOT
|
|
530
|
+
rendered as a Jinja template.
|
|
531
|
+
|
|
532
|
+
Returns ``None`` -- so ``SecuritySection`` keeps its built-in default policy
|
|
533
|
+
-- when ``security_policy_filename`` is the default sentinel
|
|
534
|
+
``"security_policy.j2"`` (a string only; the file was removed, so it is never
|
|
535
|
+
read) or ``""`` (an empty *filename*, which disables the policy). A configured
|
|
536
|
+
file whose own contents are empty still returns ``""`` (an empty custom
|
|
537
|
+
policy), not ``None``.
|
|
538
|
+
|
|
539
|
+
Relative names resolve against ``prompt_dir``; absolute paths are used as-is.
|
|
540
|
+
"""
|
|
541
|
+
filename = self.security_policy_filename
|
|
542
|
+
if not filename or filename == "security_policy.j2":
|
|
543
|
+
return None
|
|
544
|
+
return (Path(self.prompt_dir) / filename).read_text(encoding="utf-8")
|
|
545
|
+
|
|
528
546
|
def _build_prompt_context(
|
|
529
547
|
self,
|
|
530
548
|
additional_secret_infos: list[dict[str, str | None]] | None = None,
|
|
@@ -578,8 +596,17 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
|
|
|
578
596
|
# secrets (additional_secret_infos), matching what <CUSTOM_SECRETS> shows.
|
|
579
597
|
secret_names = tuple(name for name, _ in secret_infos if name)
|
|
580
598
|
|
|
599
|
+
template_kwargs = self._resolved_template_kwargs()
|
|
600
|
+
# A custom security policy's content for SecuritySection (registry path only).
|
|
601
|
+
policy_content = self._read_custom_security_policy()
|
|
602
|
+
if policy_content is not None:
|
|
603
|
+
template_kwargs = {
|
|
604
|
+
**template_kwargs,
|
|
605
|
+
"security_policy_content": policy_content,
|
|
606
|
+
}
|
|
607
|
+
|
|
581
608
|
return PromptContext(
|
|
582
|
-
template_kwargs=
|
|
609
|
+
template_kwargs=template_kwargs,
|
|
583
610
|
tool_names=tuple(t.name for t in self.tools),
|
|
584
611
|
platform=Platform.current(),
|
|
585
612
|
working_dir=None,
|
|
@@ -16,6 +16,8 @@ from pydantic import (
|
|
|
16
16
|
)
|
|
17
17
|
|
|
18
18
|
from openhands.sdk.context.prompts import render_template
|
|
19
|
+
from openhands.sdk.context.prompts.presets import create_registry
|
|
20
|
+
from openhands.sdk.context.prompts.section import PromptContext
|
|
19
21
|
from openhands.sdk.llm import Message, TextContent
|
|
20
22
|
from openhands.sdk.llm.utils.model_prompt_spec import get_model_prompt_spec
|
|
21
23
|
from openhands.sdk.logger import get_logger
|
|
@@ -41,8 +43,8 @@ PROMPT_DIR = pathlib.Path(__file__).parent / "prompts" / "templates"
|
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
class ResolvedDynamicData(NamedTuple):
|
|
44
|
-
"""Dynamic-tier inputs resolved once,
|
|
45
|
-
|
|
46
|
+
"""Dynamic-tier inputs resolved once, fed into the section registry
|
|
47
|
+
(skills gated by model family, secrets merged)."""
|
|
46
48
|
|
|
47
49
|
repo_skills: list[Skill]
|
|
48
50
|
available_skills_prompt: str
|
|
@@ -327,27 +329,16 @@ class AgentContext(BaseModel):
|
|
|
327
329
|
data = self._resolve_dynamic_data(
|
|
328
330
|
llm_model, llm_model_canonical, additional_secret_infos
|
|
329
331
|
)
|
|
330
|
-
|
|
331
|
-
data.
|
|
332
|
-
|
|
333
|
-
or
|
|
334
|
-
or
|
|
335
|
-
|
|
332
|
+
ctx = PromptContext(
|
|
333
|
+
now=data.formatted_datetime,
|
|
334
|
+
repo_skills=tuple((s.name, s.content) for s in data.repo_skills),
|
|
335
|
+
available_skills_prompt=data.available_skills_prompt or None,
|
|
336
|
+
custom_suffix=self.system_message_suffix or None,
|
|
337
|
+
secret_infos=tuple(
|
|
338
|
+
(info["name"] or "", info["description"]) for info in data.secret_infos
|
|
339
|
+
),
|
|
336
340
|
)
|
|
337
|
-
|
|
338
|
-
formatted_text = render_template(
|
|
339
|
-
prompt_dir=str(PROMPT_DIR),
|
|
340
|
-
template_name="system_message_suffix.j2",
|
|
341
|
-
repo_skills=data.repo_skills,
|
|
342
|
-
system_message_suffix=self.system_message_suffix or "",
|
|
343
|
-
secret_infos=data.secret_infos,
|
|
344
|
-
available_skills_prompt=data.available_skills_prompt,
|
|
345
|
-
current_datetime=data.formatted_datetime,
|
|
346
|
-
).strip()
|
|
347
|
-
return formatted_text
|
|
348
|
-
elif self.system_message_suffix and self.system_message_suffix.strip():
|
|
349
|
-
return self.system_message_suffix.strip()
|
|
350
|
-
return None
|
|
341
|
+
return create_registry().build(ctx).dynamic
|
|
351
342
|
|
|
352
343
|
def _resolve_dynamic_data(
|
|
353
344
|
self,
|
|
@@ -433,9 +424,10 @@ class AgentContext(BaseModel):
|
|
|
433
424
|
this adapter only emits prompt-only context. Unsupported AgentContext
|
|
434
425
|
fields are rejected by :meth:`validate_acp_compatibility`.
|
|
435
426
|
|
|
436
|
-
The rendering reuses :meth:`get_system_message_suffix
|
|
437
|
-
|
|
438
|
-
identical prompt layout as the regular agent. This
|
|
427
|
+
The rendering reuses :meth:`get_system_message_suffix`, which assembles
|
|
428
|
+
the dynamic-tier sections via the shared prompt registry, so that ACP
|
|
429
|
+
agents receive the identical prompt layout as the regular agent. This
|
|
430
|
+
includes the
|
|
439
431
|
``<CUSTOM_SECRETS>`` block when secrets are present, informing the ACP
|
|
440
432
|
subprocess which environment variables are available. The actual secret
|
|
441
433
|
values are injected into the subprocess environment by
|
|
@@ -455,7 +447,8 @@ class AgentContext(BaseModel):
|
|
|
455
447
|
"""
|
|
456
448
|
self.validate_acp_compatibility()
|
|
457
449
|
# No model-specific skill filtering for ACP — delegate to the shared
|
|
458
|
-
#
|
|
450
|
+
# builder, whose dynamic-tier sections also emit the <CUSTOM_SECRETS>
|
|
451
|
+
# block from secrets.
|
|
459
452
|
return self.get_system_message_suffix(
|
|
460
453
|
additional_secret_infos=additional_secret_infos
|
|
461
454
|
)
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
``create_registry()`` registers the static-tier sections in the exact order
|
|
4
4
|
``agent/prompts/system_prompt.j2`` emits them, so ``registry.build(ctx).static``
|
|
5
|
-
reproduces ``AgentBase.static_system_message``. The dynamic-tier sections
|
|
6
|
-
|
|
5
|
+
reproduces ``AgentBase.static_system_message``. The dynamic-tier sections are
|
|
6
|
+
appended separately. It will gain a ``preset``
|
|
7
7
|
flag to select among prompt variants (interactive, planning, ...) over the same
|
|
8
8
|
engine; today it returns the default composition.
|
|
9
9
|
"""
|
|
@@ -62,7 +62,7 @@ def create_registry() -> PromptRegistry:
|
|
|
62
62
|
r.register(TroubleshootingSection())
|
|
63
63
|
r.register(ProcessManagementSection())
|
|
64
64
|
r.register(ModelSpecificSection()) # guard: model_family resolved
|
|
65
|
-
# dynamic tier
|
|
65
|
+
# dynamic tier (#3610)
|
|
66
66
|
r.register(DateTimeSection())
|
|
67
67
|
r.register(RepoContextSection()) # guard: gated repo skills present
|
|
68
68
|
r.register(AvailableSkillsSection()) # guard: available_skills_prompt
|
{openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/prompts/sections/dynamic.py
RENAMED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
"""Dynamic-tier prompt sections
|
|
2
|
-
``context/prompts/templates/system_message_suffix.j2``.
|
|
1
|
+
"""Dynamic-tier prompt sections.
|
|
3
2
|
|
|
4
3
|
These render per-conversation content (datetime, repo context, available skills,
|
|
5
4
|
custom suffix, secrets) into the ``DYNAMIC`` block. All inputs are resolved into the
|
{openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/context/prompts/sections/static.py
RENAMED
|
@@ -219,11 +219,9 @@ Always provide links to the relevant documentation pages for users who want to l
|
|
|
219
219
|
|
|
220
220
|
|
|
221
221
|
class SecuritySection(_StaticTextSection):
|
|
222
|
-
"""The ``<SECURITY>`` block
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
the default policy; a custom ``security_policy_filename`` would resolve its content
|
|
226
|
-
into the context instead (a follow-up; not exercised by the snapshot matrix).
|
|
222
|
+
"""The ``<SECURITY>`` block: the built-in default policy, or a custom policy's
|
|
223
|
+
``security_policy_content`` when configured. Guarded by ``security_policy_filename``
|
|
224
|
+
(empty string disables it).
|
|
227
225
|
"""
|
|
228
226
|
|
|
229
227
|
name = "security"
|
|
@@ -261,6 +259,13 @@ class SecuritySection(_StaticTextSection):
|
|
|
261
259
|
def guard(self, ctx: PromptContext) -> bool:
|
|
262
260
|
return bool(ctx.template_kwargs.get("security_policy_filename"))
|
|
263
261
|
|
|
262
|
+
def render(self, ctx: PromptContext) -> str | None:
|
|
263
|
+
content = ctx.template_kwargs.get("security_policy_content")
|
|
264
|
+
# `is not None`: an explicitly empty custom policy must not fall back to body.
|
|
265
|
+
if content is not None:
|
|
266
|
+
return _refine(f"<SECURITY>\n\n{content}\n\n</SECURITY>", ctx.platform)
|
|
267
|
+
return self.body
|
|
268
|
+
|
|
264
269
|
|
|
265
270
|
class SecurityRiskAssessmentSection:
|
|
266
271
|
"""``<SECURITY_RISK_ASSESSMENT>`` -- the LOW/MEDIUM/HIGH tiers swap with ``cli_mode``."""
|
{openhands_sdk-1.29.2 → openhands_sdk-1.29.3}/openhands/sdk/conversation/impl/local_conversation.py
RENAMED
|
@@ -61,6 +61,7 @@ from openhands.sdk.plugin import (
|
|
|
61
61
|
PluginSource,
|
|
62
62
|
ResolvedPluginSource,
|
|
63
63
|
fetch_plugin_with_resolution,
|
|
64
|
+
load_available_plugins,
|
|
64
65
|
)
|
|
65
66
|
from openhands.sdk.secret import StaticSecret
|
|
66
67
|
from openhands.sdk.security.analyzer import SecurityAnalyzerBase
|
|
@@ -662,6 +663,10 @@ class LocalConversation(BaseConversation):
|
|
|
662
663
|
|
|
663
664
|
all_plugin_hooks: list[HookConfig] = []
|
|
664
665
|
all_plugin_agents: list[AgentDefinition] = []
|
|
666
|
+
# Names of explicitly-attached plugins (populated in the loop below). Used
|
|
667
|
+
# to keep explicit attach authoritative over ambient installed/local
|
|
668
|
+
# plugins (and to avoid double-registering their hooks/agents).
|
|
669
|
+
explicit_plugin_names: set[str] = set()
|
|
665
670
|
|
|
666
671
|
merged_context = self.agent.agent_context
|
|
667
672
|
merged_mcp = dict(self.agent.mcp_config) if self.agent.mcp_config else {}
|
|
@@ -743,6 +748,7 @@ class LocalConversation(BaseConversation):
|
|
|
743
748
|
f"Loaded plugin '{plugin.manifest.name}'"
|
|
744
749
|
+ (f" @ {resolved_ref[:8]}" if resolved_ref else "")
|
|
745
750
|
)
|
|
751
|
+
explicit_plugin_names.add(plugin.name)
|
|
746
752
|
|
|
747
753
|
# Merge plugin contents
|
|
748
754
|
merged_context = plugin.add_skills_to(merged_context)
|
|
@@ -759,6 +765,54 @@ class LocalConversation(BaseConversation):
|
|
|
759
765
|
|
|
760
766
|
logger.info(f"Loaded {len(plugins_to_load)} plugin(s) via Conversation")
|
|
761
767
|
|
|
768
|
+
# Ambient plugins: enabled installed plugins plus local user/project
|
|
769
|
+
# plugins, mirroring how installed/local skills already auto-load. These
|
|
770
|
+
# are additive to the explicit plugins above, de-duplicated by plugin
|
|
771
|
+
# name. Explicit attach wins: an ambient plugin whose name was already
|
|
772
|
+
# attached above is skipped (this also avoids double-registering its
|
|
773
|
+
# hooks/agents). Best-effort — a failure here must not prevent the
|
|
774
|
+
# conversation from starting.
|
|
775
|
+
#
|
|
776
|
+
# Ambient plugins have no pinned commit SHA, so (unlike explicit attach)
|
|
777
|
+
# they are intentionally NOT recorded in self._resolved_plugins. On
|
|
778
|
+
# resume they are re-discovered from disk / current enabled state, just
|
|
779
|
+
# like project skills. Automation/sandbox runs lack the user's installed
|
|
780
|
+
# and home directories, so discovery naturally yields nothing there.
|
|
781
|
+
ambient_plugins_loaded = False
|
|
782
|
+
try:
|
|
783
|
+
ambient_plugins = load_available_plugins(
|
|
784
|
+
work_dir=self.workspace.working_dir,
|
|
785
|
+
include_user=True,
|
|
786
|
+
include_project=True,
|
|
787
|
+
)
|
|
788
|
+
except Exception:
|
|
789
|
+
logger.warning(
|
|
790
|
+
"Failed to load ambient (installed/local) plugins; "
|
|
791
|
+
"continuing without them",
|
|
792
|
+
exc_info=True,
|
|
793
|
+
)
|
|
794
|
+
ambient_plugins = {}
|
|
795
|
+
|
|
796
|
+
for plugin in ambient_plugins.values():
|
|
797
|
+
if plugin.name in explicit_plugin_names:
|
|
798
|
+
logger.debug(
|
|
799
|
+
f"Skipping ambient plugin '{plugin.name}' "
|
|
800
|
+
"(explicitly attached to this conversation)"
|
|
801
|
+
)
|
|
802
|
+
continue
|
|
803
|
+
|
|
804
|
+
merged_context = plugin.add_skills_to(merged_context)
|
|
805
|
+
merged_mcp = plugin.add_mcp_config_to(merged_mcp)
|
|
806
|
+
has_mcp_config = has_mcp_config or bool(merged_mcp)
|
|
807
|
+
|
|
808
|
+
if plugin.hooks and not plugin.hooks.is_empty():
|
|
809
|
+
all_plugin_hooks.append(plugin.hooks)
|
|
810
|
+
if plugin.agents:
|
|
811
|
+
all_plugin_agents.extend(plugin.agents)
|
|
812
|
+
|
|
813
|
+
ambient_plugins_loaded = True
|
|
814
|
+
logger.debug(f"Loaded ambient plugin '{plugin.name}'")
|
|
815
|
+
|
|
762
816
|
# Resolve project skills from the workspace. AgentContext can't do this
|
|
763
817
|
# itself (the workspace path is unknown at validation time), so it is done
|
|
764
818
|
# here, where the path is known. Project skills take precedence over
|
|
@@ -810,7 +864,12 @@ class LocalConversation(BaseConversation):
|
|
|
810
864
|
|
|
811
865
|
# Update agent with merged content only if something changed.
|
|
812
866
|
# Skip update otherwise to avoid unnecessary agent state mutations.
|
|
813
|
-
if
|
|
867
|
+
if (
|
|
868
|
+
plugins_to_load
|
|
869
|
+
or has_mcp_config
|
|
870
|
+
or project_skills_loaded
|
|
871
|
+
or ambient_plugins_loaded
|
|
872
|
+
):
|
|
814
873
|
self.agent = self.agent.model_copy(
|
|
815
874
|
update={
|
|
816
875
|
"agent_context": merged_context,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from .classifier import (
|
|
2
|
+
is_content_policy_violation,
|
|
2
3
|
is_context_window_exceeded,
|
|
3
4
|
is_prompt_cache_too_small,
|
|
4
5
|
looks_like_auth_error,
|
|
@@ -11,6 +12,7 @@ from .types import (
|
|
|
11
12
|
FunctionCallValidationError,
|
|
12
13
|
LLMAuthenticationError,
|
|
13
14
|
LLMBadRequestError,
|
|
15
|
+
LLMContentPolicyViolationError,
|
|
14
16
|
LLMContextWindowExceedError,
|
|
15
17
|
LLMContextWindowTooSmallError,
|
|
16
18
|
LLMError,
|
|
@@ -40,6 +42,7 @@ __all__ = [
|
|
|
40
42
|
"LLMContextWindowExceedError",
|
|
41
43
|
"LLMMalformedConversationHistoryError",
|
|
42
44
|
"LLMContextWindowTooSmallError",
|
|
45
|
+
"LLMContentPolicyViolationError",
|
|
43
46
|
"LLMAuthenticationError",
|
|
44
47
|
"LLMRateLimitError",
|
|
45
48
|
"LLMTimeoutError",
|
|
@@ -48,6 +51,7 @@ __all__ = [
|
|
|
48
51
|
"UserCancelledError",
|
|
49
52
|
"OperationCancelled",
|
|
50
53
|
# Helpers
|
|
54
|
+
"is_content_policy_violation",
|
|
51
55
|
"is_context_window_exceeded",
|
|
52
56
|
"is_prompt_cache_too_small",
|
|
53
57
|
"looks_like_auth_error",
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import Final
|
|
4
|
+
|
|
3
5
|
from litellm.exceptions import (
|
|
4
6
|
APIConnectionError,
|
|
5
7
|
AuthenticationError,
|
|
6
8
|
BadRequestError,
|
|
9
|
+
ContentPolicyViolationError,
|
|
7
10
|
ContextWindowExceededError,
|
|
8
11
|
InternalServerError,
|
|
9
12
|
OpenAIError,
|
|
@@ -17,7 +20,7 @@ from .types import (
|
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
# Minimal, provider-agnostic context-window detection
|
|
20
|
-
LONG_PROMPT_PATTERNS: list[str] = [
|
|
23
|
+
LONG_PROMPT_PATTERNS: Final[list[str]] = [
|
|
21
24
|
"contextwindowexceedederror",
|
|
22
25
|
"prompt is too long",
|
|
23
26
|
"input length and `max_tokens` exceed context limit",
|
|
@@ -32,7 +35,7 @@ LONG_PROMPT_PATTERNS: list[str] = [
|
|
|
32
35
|
# provider. They are tracked separately from true context-window errors so the
|
|
33
36
|
# logs and agent control flow can preserve that distinction while still routing
|
|
34
37
|
# into condensation-based recovery.
|
|
35
|
-
MALFORMED_HISTORY_PATTERNS: list[str] = [
|
|
38
|
+
MALFORMED_HISTORY_PATTERNS: Final[list[str]] = [
|
|
36
39
|
"tool_use ids were found without `tool_result` blocks immediately after",
|
|
37
40
|
# Anthropic backtick variant
|
|
38
41
|
"`tool_use` ids were found without `tool_result` blocks immediately after",
|
|
@@ -54,6 +57,30 @@ MALFORMED_HISTORY_PATTERNS: list[str] = [
|
|
|
54
57
|
"failed to parse tool call arguments as json",
|
|
55
58
|
]
|
|
56
59
|
|
|
60
|
+
# Vertex AI (Gemini) rejects context-caching requests when the cached content
|
|
61
|
+
# is below the provider's minimum token threshold (currently 4096 tokens).
|
|
62
|
+
# Example error: "The cached content is of 1171 tokens. The minimum token
|
|
63
|
+
# count to start caching is 4096." — the `.lower()` comparison handles case
|
|
64
|
+
# variation across providers but won't match reworded messages; update this
|
|
65
|
+
# pattern if the API phrasing changes.
|
|
66
|
+
PROMPT_CACHE_TOO_SMALL_PATTERNS: Final[list[str]] = [
|
|
67
|
+
"minimum token count to start caching",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
AUTH_PATTERNS: Final[list[str]] = [
|
|
71
|
+
"invalid api key",
|
|
72
|
+
"unauthorized",
|
|
73
|
+
"missing api key",
|
|
74
|
+
"invalid authentication",
|
|
75
|
+
"access denied",
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
CONTENT_POLICY_PATTERNS: Final[list[str]] = [
|
|
79
|
+
"content_policy",
|
|
80
|
+
"content filtering policy",
|
|
81
|
+
"output blocked by content filtering",
|
|
82
|
+
]
|
|
83
|
+
|
|
57
84
|
|
|
58
85
|
def is_context_window_exceeded(exception: Exception) -> bool:
|
|
59
86
|
if isinstance(exception, (ContextWindowExceededError, LLMContextWindowExceedError)):
|
|
@@ -86,25 +113,6 @@ def looks_like_malformed_conversation_history_error(exception: Exception) -> boo
|
|
|
86
113
|
return any(p in s for p in MALFORMED_HISTORY_PATTERNS)
|
|
87
114
|
|
|
88
115
|
|
|
89
|
-
# Vertex AI (Gemini) rejects context-caching requests when the cached content
|
|
90
|
-
# is below the provider's minimum token threshold (currently 4096 tokens).
|
|
91
|
-
# Example error: "The cached content is of 1171 tokens. The minimum token
|
|
92
|
-
# count to start caching is 4096." — the `.lower()` comparison handles case
|
|
93
|
-
# variation across providers but won't match reworded messages; update this
|
|
94
|
-
# pattern if the API phrasing changes.
|
|
95
|
-
PROMPT_CACHE_TOO_SMALL_PATTERNS: list[str] = [
|
|
96
|
-
"minimum token count to start caching",
|
|
97
|
-
]
|
|
98
|
-
|
|
99
|
-
AUTH_PATTERNS: list[str] = [
|
|
100
|
-
"invalid api key",
|
|
101
|
-
"unauthorized",
|
|
102
|
-
"missing api key",
|
|
103
|
-
"invalid authentication",
|
|
104
|
-
"access denied",
|
|
105
|
-
]
|
|
106
|
-
|
|
107
|
-
|
|
108
116
|
def is_prompt_cache_too_small(exception: Exception) -> bool:
|
|
109
117
|
"""Return True if the error indicates the prompt cache content is too small.
|
|
110
118
|
|
|
@@ -134,3 +142,12 @@ def looks_like_auth_error(exception: Exception) -> bool:
|
|
|
134
142
|
if code in s:
|
|
135
143
|
return True
|
|
136
144
|
return False
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def is_content_policy_violation(exception: Exception) -> bool:
|
|
148
|
+
if isinstance(exception, ContentPolicyViolationError):
|
|
149
|
+
return True
|
|
150
|
+
if not isinstance(exception, (BadRequestError, OpenAIError)):
|
|
151
|
+
return False
|
|
152
|
+
s = str(exception).lower()
|
|
153
|
+
return any(p in s for p in CONTENT_POLICY_PATTERNS)
|
|
@@ -10,6 +10,7 @@ from litellm.exceptions import (
|
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
from .classifier import (
|
|
13
|
+
is_content_policy_violation,
|
|
13
14
|
is_context_window_exceeded,
|
|
14
15
|
looks_like_auth_error,
|
|
15
16
|
looks_like_malformed_conversation_history_error,
|
|
@@ -17,6 +18,7 @@ from .classifier import (
|
|
|
17
18
|
from .types import (
|
|
18
19
|
LLMAuthenticationError,
|
|
19
20
|
LLMBadRequestError,
|
|
21
|
+
LLMContentPolicyViolationError,
|
|
20
22
|
LLMContextWindowExceedError,
|
|
21
23
|
LLMMalformedConversationHistoryError,
|
|
22
24
|
LLMRateLimitError,
|
|
@@ -56,6 +58,11 @@ def map_provider_exception(exception: Exception) -> Exception:
|
|
|
56
58
|
):
|
|
57
59
|
return LLMServiceUnavailableError(str(exception))
|
|
58
60
|
|
|
61
|
+
# Content-policy blocks are deterministic 4xx; distinguish them from generic
|
|
62
|
+
# bad requests so the agent can recover softly instead of hard-erroring.
|
|
63
|
+
if is_content_policy_violation(exception):
|
|
64
|
+
return LLMContentPolicyViolationError(str(exception))
|
|
65
|
+
|
|
59
66
|
# Generic client-side 4xx errors
|
|
60
67
|
if isinstance(exception, BadRequestError):
|
|
61
68
|
return LLMBadRequestError(str(exception))
|
|
@@ -131,6 +131,20 @@ class LLMBadRequestError(LLMError):
|
|
|
131
131
|
super().__init__(message)
|
|
132
132
|
|
|
133
133
|
|
|
134
|
+
class LLMContentPolicyViolationError(LLMBadRequestError):
|
|
135
|
+
"""Provider blocked the request/response via its content filter.
|
|
136
|
+
|
|
137
|
+
Subclasses LLMBadRequestError for back-compat. Deterministic in
|
|
138
|
+
(messages, model): a bare retry trips the same filter, so recovery
|
|
139
|
+
requires changing the request, not re-sending it.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
def __init__(
|
|
143
|
+
self, message: str = "Output blocked by content filtering policy"
|
|
144
|
+
) -> None:
|
|
145
|
+
super().__init__(message)
|
|
146
|
+
|
|
147
|
+
|
|
134
148
|
# Other
|
|
135
149
|
class UserCancelledError(Exception):
|
|
136
150
|
def __init__(self, message: str = "User cancelled the request") -> None:
|
|
@@ -50,7 +50,15 @@ class RetryMixin:
|
|
|
50
50
|
if isinstance(exc, LLMNoResponseError):
|
|
51
51
|
kwargs = getattr(retry_state, "kwargs", None)
|
|
52
52
|
if isinstance(kwargs, dict):
|
|
53
|
-
current_temp = kwargs.get(
|
|
53
|
+
current_temp = kwargs.get(
|
|
54
|
+
"temperature", getattr(self, "temperature", None)
|
|
55
|
+
)
|
|
56
|
+
if current_temp is None:
|
|
57
|
+
logger.warning(
|
|
58
|
+
"LLMNoResponseError with no configured temperature, "
|
|
59
|
+
"leaving temperature unset for next attempt."
|
|
60
|
+
)
|
|
61
|
+
return
|
|
54
62
|
if current_temp == 0:
|
|
55
63
|
kwargs["temperature"] = 1.0
|
|
56
64
|
logger.warning(
|
|
@@ -12,6 +12,11 @@ user's home directory (~/.openhands/plugins/installed/).
|
|
|
12
12
|
Note: Marketplace classes live in ``openhands.sdk.marketplace``.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
+
from openhands.sdk.plugin.discovery import (
|
|
16
|
+
load_available_plugins,
|
|
17
|
+
load_project_plugins,
|
|
18
|
+
load_user_plugins,
|
|
19
|
+
)
|
|
15
20
|
from openhands.sdk.plugin.fetch import (
|
|
16
21
|
PluginFetchError,
|
|
17
22
|
fetch_plugin_with_resolution,
|
|
@@ -58,6 +63,10 @@ __all__ = [
|
|
|
58
63
|
# Plugin loading
|
|
59
64
|
"load_plugins",
|
|
60
65
|
"fetch_plugin_with_resolution",
|
|
66
|
+
# Local plugin discovery (ambient auto-load)
|
|
67
|
+
"load_user_plugins",
|
|
68
|
+
"load_project_plugins",
|
|
69
|
+
"load_available_plugins",
|
|
61
70
|
# Source path utilities
|
|
62
71
|
"GitHubURLComponents",
|
|
63
72
|
"parse_github_url",
|