diff --git a/astrbot/core/astr_agent_tool_exec.py b/astrbot/core/astr_agent_tool_exec.py index 0dc8b9eeb7..3cd5165f05 100644 --- a/astrbot/core/astr_agent_tool_exec.py +++ b/astrbot/core/astr_agent_tool_exec.py @@ -303,6 +303,7 @@ async def _execute_handoff( tools=toolset, contexts=contexts, max_steps=agent_max_step, + tool_call_timeout=run_context.tool_call_timeout, stream=stream, ) yield mcp.types.CallToolResult( diff --git a/astrbot/core/provider/sources/openai_embedding_source.py b/astrbot/core/provider/sources/openai_embedding_source.py index 2b62d865c2..ed4691718c 100644 --- a/astrbot/core/provider/sources/openai_embedding_source.py +++ b/astrbot/core/provider/sources/openai_embedding_source.py @@ -1,3 +1,5 @@ +from urllib.parse import urlparse + import httpx from openai import AsyncOpenAI @@ -27,6 +29,9 @@ def __init__(self, provider_config: dict, provider_settings: dict) -> None: api_base = provider_config.get( "embedding_api_base", "https://api.openai.com/v1" ).strip() + parsed = urlparse(api_base) + if not parsed.path or parsed.path == "/": + api_base = api_base.rstrip("/") + "/v1" logger.info(f"[OpenAI Embedding] {provider_id} Using API Base: {api_base}") self.client = AsyncOpenAI( api_key=provider_config.get("embedding_api_key"), diff --git a/tests/unit/test_astr_agent_tool_exec.py b/tests/unit/test_astr_agent_tool_exec.py index 9d405f1ab5..5fab9fe0a2 100644 --- a/tests/unit/test_astr_agent_tool_exec.py +++ b/tests/unit/test_astr_agent_tool_exec.py @@ -272,6 +272,55 @@ async def _fake_convert_to_file_path(self): assert image_urls == [] +@pytest.mark.asyncio +async def test_execute_handoff_passes_tool_call_timeout_to_tool_loop_agent( + monkeypatch: pytest.MonkeyPatch, +): + captured: dict = {} + + async def _fake_get_current_chat_provider_id(_umo): + return "provider-id" + + async def _fake_tool_loop_agent(**kwargs): + captured.update(kwargs) + return SimpleNamespace(completion_text="ok") + + context = SimpleNamespace( + get_current_chat_provider_id=_fake_get_current_chat_provider_id, + tool_loop_agent=_fake_tool_loop_agent, + get_config=lambda **_kwargs: {"provider_settings": {}}, + ) + event = _DummyEvent([]) + run_context = ContextWrapper( + context=SimpleNamespace(event=event, context=context), + tool_call_timeout=120, + ) + tool = SimpleNamespace( + name="transfer_to_subagent", + provider_id=None, + agent=SimpleNamespace( + name="subagent", + tools=[], + instructions="subagent-instructions", + begin_dialogs=[], + run_hooks=None, + ), + ) + + results = [] + async for result in FunctionToolExecutor._execute_handoff( + tool, + run_context, + image_urls_prepared=True, + input="hello", + image_urls=[], + ): + results.append(result) + + assert len(results) == 1 + assert captured["tool_call_timeout"] == 120 + + @pytest.mark.asyncio async def test_collect_handoff_image_urls_filters_extensionless_file_outside_temp_root( monkeypatch: pytest.MonkeyPatch,