Enhance DuplexPipeline to support follow-up context for manual opener tool calls
- Introduced logic to trigger a follow-up turn when the manual opener greeting is empty. - Updated `_execute_manual_opener_tool_calls` to return structured tool call and result data. - Added `_build_manual_opener_follow_up_context` method to construct context for follow-up turns. - Modified `_handle_turn` to accept system context for improved conversation management. - Enhanced tests to validate the new follow-up behavior and ensure proper context handling.
This commit is contained in:
@@ -350,6 +350,76 @@ async def test_manual_opener_legacy_voice_message_prompt_is_normalized(monkeypat
|
||||
assert tool_events[0].get("arguments") == {"msg": "您好"}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_manual_opener_empty_greeting_triggers_follow_up_turn(monkeypatch):
|
||||
pipeline, _events = _build_pipeline(monkeypatch, [[LLMStreamEvent(type="done")]])
|
||||
pipeline.apply_runtime_overrides(
|
||||
{
|
||||
"generatedOpenerEnabled": False,
|
||||
"greeting": "",
|
||||
"output": {"mode": "text"},
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"executor": "client",
|
||||
"waitForResponse": True,
|
||||
"function": {
|
||||
"name": "text_choice_prompt",
|
||||
"description": "Prompt choice",
|
||||
"parameters": {"type": "object", "properties": {"question": {"type": "string"}}},
|
||||
},
|
||||
}
|
||||
],
|
||||
"manualOpenerToolCalls": [
|
||||
{
|
||||
"toolName": "text_choice_prompt",
|
||||
"arguments": {
|
||||
"question": "请选择业务类型",
|
||||
"options": ["账单", "报修"],
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
async def _fake_manual_opener_calls() -> Dict[str, List[Dict[str, Any]]]:
|
||||
return {
|
||||
"toolCalls": [
|
||||
{
|
||||
"tool_call_id": "call_opener_1",
|
||||
"tool_name": "text_choice_prompt",
|
||||
"arguments": {"question": "请选择业务类型", "options": ["账单", "报修"]},
|
||||
}
|
||||
],
|
||||
"toolResults": [
|
||||
{
|
||||
"tool_call_id": "call_opener_1",
|
||||
"name": "text_choice_prompt",
|
||||
"output": {"selected": "报修"},
|
||||
"status": {"code": 200, "message": "ok"},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
called: Dict[str, Any] = {}
|
||||
waiter = asyncio.Event()
|
||||
|
||||
async def _fake_handle_turn(user_text: str, system_context: str | None = None) -> None:
|
||||
called["user_text"] = user_text
|
||||
called["system_context"] = system_context or ""
|
||||
waiter.set()
|
||||
|
||||
monkeypatch.setattr(pipeline, "_execute_manual_opener_tool_calls", _fake_manual_opener_calls)
|
||||
monkeypatch.setattr(pipeline, "_handle_turn", _fake_handle_turn)
|
||||
|
||||
await pipeline.emit_initial_greeting()
|
||||
await asyncio.wait_for(waiter.wait(), timeout=1.0)
|
||||
|
||||
assert called.get("user_text") == ""
|
||||
assert "opener_tool_results" in called.get("system_context", "")
|
||||
assert "报修" in called.get("system_context", "")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ws_message_parses_tool_call_results():
|
||||
msg = parse_client_message(
|
||||
|
||||
Reference in New Issue
Block a user