Implement initial generated opener logic in DuplexPipeline to utilize tool-capable assistant turns when tools are available. Update tests to verify the correct behavior of the generated opener under various conditions, ensuring proper handling of user input and task management.

This commit is contained in:
Xin Wang
2026-03-02 02:47:30 +08:00
parent 4d553de34d
commit b5cdb76e52
2 changed files with 46 additions and 0 deletions

View File

@@ -955,6 +955,16 @@ class DuplexPipeline:
if not self._bot_starts_first():
return
if self._generated_opener_enabled() and self._resolved_tool_schemas():
# Run generated opener as a normal tool-capable assistant turn.
# Use an empty user input so the opener can be driven by system prompt policy.
if self._current_turn_task and not self._current_turn_task.done():
logger.info("Skip initial generated opener: assistant turn already in progress")
return
self._current_turn_task = asyncio.create_task(self._handle_turn(""))
logger.info("Initial generated opener started with tool-calling path")
return
greeting_to_speak = self.conversation.greeting
if self._generated_opener_enabled():
generated_greeting = await self._generate_runtime_greeting()

View File

@@ -246,6 +246,42 @@ async def test_generated_opener_prompt_uses_system_prompt_only(monkeypatch):
assert "额外风格提示" not in user_prompt
@pytest.mark.asyncio
async def test_generated_opener_uses_tool_capable_turn_when_tools_available(monkeypatch):
pipeline, _events = _build_pipeline(monkeypatch, [[LLMStreamEvent(type="done")]])
pipeline.apply_runtime_overrides(
{
"generatedOpenerEnabled": True,
"tools": [
{
"type": "function",
"executor": "client",
"function": {
"name": "text_msg_prompt",
"description": "Show a prompt",
"parameters": {"type": "object", "properties": {}},
},
}
],
}
)
called: Dict[str, Any] = {}
waiter = asyncio.Event()
async def _fake_handle_turn(user_text: str) -> None:
called["user_text"] = user_text
waiter.set()
monkeypatch.setattr(pipeline, "_handle_turn", _fake_handle_turn)
pipeline.conversation.greeting = "fallback greeting"
await pipeline.emit_initial_greeting()
await asyncio.wait_for(waiter.wait(), timeout=1.0)
assert called.get("user_text") == ""
@pytest.mark.asyncio
async def test_ws_message_parses_tool_call_results():
msg = parse_client_message(