fix(proxy): pass the ApiPilot catalog with the correct argument
This commit is contained in:
@@ -4549,7 +4549,7 @@ async def realtime_proxy(ws: WebSocket):
|
|||||||
pass
|
pass
|
||||||
session_ctx_apipilot_defaults = dict(_apipilot_defaults_p1)
|
session_ctx_apipilot_defaults = dict(_apipilot_defaults_p1)
|
||||||
injected_apipilot_pass1 = _resolve_apipilot_reasoning_context_block(
|
injected_apipilot_pass1 = _resolve_apipilot_reasoning_context_block(
|
||||||
tools=apipilot_tools_pass1, context_defaults=_apipilot_defaults_p1,
|
tools_list=apipilot_tools_pass1, context_defaults=_apipilot_defaults_p1,
|
||||||
) or ""
|
) or ""
|
||||||
if injected_apipilot_pass1.strip():
|
if injected_apipilot_pass1.strip():
|
||||||
logger.info(
|
logger.info(
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from pathlib import Path
|
|||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import ANY, AsyncMock, Mock, patch
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
@@ -173,6 +173,65 @@ class BackendContractTests(unittest.TestCase):
|
|||||||
|
|
||||||
|
|
||||||
class VoiceProxyTests(unittest.TestCase):
|
class VoiceProxyTests(unittest.TestCase):
|
||||||
|
def test_apipilot_catalog_and_caller_defaults_reach_the_voice_session(self):
|
||||||
|
closed = threading.Event()
|
||||||
|
catalog = AsyncMock(return_value=[{
|
||||||
|
"name": "get_caller_briefing",
|
||||||
|
"description": "Retrieve the caller's local test briefing.",
|
||||||
|
"requires_auth": True,
|
||||||
|
"parameters": {"required": ["phone", "episode_id", "email"]},
|
||||||
|
}])
|
||||||
|
login = AsyncMock(return_value=("fixture-token", {}))
|
||||||
|
profile = AsyncMock(return_value={"user": {"email": "caller@example.test"}})
|
||||||
|
|
||||||
|
def voice(socket):
|
||||||
|
try:
|
||||||
|
socket.send(json.dumps({"type": "session.created"}))
|
||||||
|
for frame in socket:
|
||||||
|
socket.send(frame)
|
||||||
|
finally:
|
||||||
|
closed.set()
|
||||||
|
|
||||||
|
with serve(voice, "127.0.0.1", 0) as upstream:
|
||||||
|
thread = threading.Thread(target=upstream.serve_forever, daemon=True)
|
||||||
|
thread.start()
|
||||||
|
self.addCleanup(thread.join, 5)
|
||||||
|
target = f"ws://127.0.0.1:{upstream.socket.getsockname()[1]}/v1/realtime"
|
||||||
|
with (
|
||||||
|
patch.object(server, "S2S_PROXY_TARGET", target),
|
||||||
|
patch.object(server, "S2S_INJECT_PROFILE", False),
|
||||||
|
patch.object(server, "S2S_APIPILOT_TOOLS_URL", "http://catalog.test/tools"),
|
||||||
|
patch.object(server, "S2S_LOGIN_BY_PHONE", "http://profile.test/login"),
|
||||||
|
patch.object(server, "VOCERO_USER_PHONE_AUTH_URL", "http://profile.test/user"),
|
||||||
|
patch.object(server, "_apipilot_fetch_tools", catalog),
|
||||||
|
patch.object(server, "_login_by_phone", login),
|
||||||
|
patch.object(server, "_fetch_user_profile_for_phone", profile),
|
||||||
|
patch.object(server.httpx, "AsyncClient", side_effect=AssertionError("Unexpected HTTP request")),
|
||||||
|
TestClient(server.app) as client,
|
||||||
|
):
|
||||||
|
with client.websocket_connect(
|
||||||
|
"/v1/realtime?phoneNumber=%2B56000000000&episode_id=fixture-episode",
|
||||||
|
) as socket:
|
||||||
|
self.assertEqual(socket.receive_json(), {"type": "session.created"})
|
||||||
|
socket.send_json({"type": "session.update", "session": {"instructions": "Local fixture"}})
|
||||||
|
update = socket.receive_json()
|
||||||
|
self.assertEqual(update["type"], "session.update")
|
||||||
|
instructions = update["session"]["instructions"]
|
||||||
|
for expected in (
|
||||||
|
"Local fixture", server.APIPILOT_PASS1_LIST_MARKER, "name: get_caller_briefing",
|
||||||
|
'phone = "+56000000000"', 'episode_id = "fixture-episode"',
|
||||||
|
'email = "caller@example.test"',
|
||||||
|
):
|
||||||
|
self.assertIn(expected, instructions)
|
||||||
|
# Configuration injection must preserve ordinary audio forwarding.
|
||||||
|
audio = {"type": "input_audio_buffer.append", "audio": "AAA="}
|
||||||
|
socket.send_json(audio)
|
||||||
|
self.assertEqual(socket.receive_json(), audio)
|
||||||
|
self.assertTrue(closed.wait(3), "Client disconnect must close the upstream WebSocket")
|
||||||
|
login.assert_awaited_once_with("+56000000000")
|
||||||
|
profile.assert_awaited_once_with("+56000000000", auth_token="fixture-token")
|
||||||
|
catalog.assert_awaited_once_with(auth_header="Bearer fixture-token", demo_session=ANY, refresh=False)
|
||||||
|
|
||||||
def test_proxy_voice_frames_keepalive_and_client_disconnect(self):
|
def test_proxy_voice_frames_keepalive_and_client_disconnect(self):
|
||||||
closed = threading.Event()
|
closed = threading.Event()
|
||||||
received = []
|
received = []
|
||||||
|
|||||||
Reference in New Issue
Block a user