chore(vocero): remove the legacy ApiPilot integration (#3)
Vocero now retrieves AquaPortal content through authenticated MCP. This removes the obsolete ApiPilot integration from the API and browser while preserving the separately deployed speech pipeline. Release notes: - Removed ApiPilot endpoints, configuration, credential overrides, and HTTP/realtime tool execution. - Removed browser ApiPilot catalog loading and prompt generation while retaining authenticated MCP discovery. - Updated regression coverage and operating documentation for the retired routes and settings. Reviewed-on: AquaPortal/s2s#3
This commit is contained in:
@@ -7,10 +7,9 @@ import json
|
||||
import os
|
||||
import threading
|
||||
import unittest
|
||||
from unittest.mock import ANY, AsyncMock, Mock, patch
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
import httpx
|
||||
from fastapi import Request
|
||||
from fastapi.testclient import TestClient
|
||||
from websockets.sync.server import serve
|
||||
|
||||
@@ -18,8 +17,7 @@ from websockets.sync.server import serve
|
||||
with patch.dict(os.environ, {}, clear=True), patch("dotenv.load_dotenv", return_value=False):
|
||||
from vocero_api import main as server
|
||||
from vocero_api.core import config as settings
|
||||
from vocero_api.providers import apipilot, caller
|
||||
from vocero_api.services import tool_context
|
||||
from vocero_api.providers import caller
|
||||
|
||||
|
||||
GRANT = {
|
||||
@@ -182,34 +180,27 @@ class BackendContractTests(unittest.TestCase):
|
||||
self.assertEqual(lookup.headers["authorization"], "Bearer fixture-token")
|
||||
self.assertEqual(lookup.url.params["phoneNumber"], "+56000000000")
|
||||
|
||||
def test_caller_auth_headers_remain_independent(self):
|
||||
request = Request(
|
||||
{
|
||||
"type": "http",
|
||||
"headers": [
|
||||
(b"authorization", b"Bearer fixture-token"),
|
||||
(b"x-demo-session", b"fixture-demo"),
|
||||
],
|
||||
}
|
||||
)
|
||||
self.assertEqual(apipilot._apipilot_parse_authorization(request), ("Bearer fixture-token", "fixture-demo"))
|
||||
def test_apipilot_routes_and_contracts_are_retired(self):
|
||||
self.assertNotIn("apipilot", self.client.get("/api/config").json())
|
||||
spec = self.client.get("/openapi.json").json()
|
||||
for method, path in (("GET", "/api/apipilot/tools"), ("POST", "/api/apipilot/execute")):
|
||||
self.assertNotIn(path, spec["paths"])
|
||||
response = self.client.request(method, path, json={"name": "get_report"})
|
||||
self.assertEqual(response.status_code, 404)
|
||||
schemas = spec["components"]["schemas"]
|
||||
for name in ("ApipilotExecuteRequest", "ToolCatalogResponse", "ToolExecutionResponse", "ToolInvocation"):
|
||||
self.assertNotIn(name, schemas)
|
||||
self.assertNotIn("apipilot", schemas["ConfigResponse"]["properties"])
|
||||
self.assertEqual(set(schemas["ReasoningResponse"]["properties"]), {"answer"})
|
||||
self.upstream.assert_not_called()
|
||||
|
||||
|
||||
class VoiceProxyTests(unittest.TestCase):
|
||||
def test_apipilot_catalog_and_caller_defaults_reach_the_voice_session(self):
|
||||
def test_caller_and_episode_context_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"}})
|
||||
profile = AsyncMock(return_value={"user": {"name": "Local Caller", "email": "caller@example.test"}})
|
||||
episode = AsyncMock(return_value={"title": "Local episode", "text": "Local episode text"})
|
||||
|
||||
def voice(socket):
|
||||
try:
|
||||
@@ -226,13 +217,12 @@ class VoiceProxyTests(unittest.TestCase):
|
||||
target = f"ws://127.0.0.1:{upstream.socket.getsockname()[1]}/v1/realtime"
|
||||
with (
|
||||
patch.object(settings, "S2S_PROXY_TARGET", target),
|
||||
patch.object(settings, "S2S_INJECT_PROFILE", False),
|
||||
patch.object(settings, "S2S_APIPILOT_TOOLS_URL", "http://catalog.test/tools"),
|
||||
patch.object(settings, "S2S_INJECT_PROFILE", True),
|
||||
patch.object(settings, "S2S_LOGIN_BY_PHONE", "http://profile.test/login"),
|
||||
patch.object(settings, "VOCERO_USER_PHONE_AUTH_URL", "http://profile.test/user"),
|
||||
patch.object(apipilot, "_apipilot_fetch_tools", catalog),
|
||||
patch.object(caller, "_login_by_phone", login),
|
||||
patch.object(caller, "_fetch_user_profile_for_phone", profile),
|
||||
patch.object(caller, "_fetch_podcast_episode", episode),
|
||||
patch.object(httpx, "AsyncClient", side_effect=AssertionError("Unexpected HTTP request")),
|
||||
TestClient(server.app) as client,
|
||||
):
|
||||
@@ -246,11 +236,9 @@ class VoiceProxyTests(unittest.TestCase):
|
||||
instructions = update["session"]["instructions"]
|
||||
for expected in (
|
||||
"Local fixture",
|
||||
tool_context.APIPILOT_PASS1_LIST_MARKER,
|
||||
"name: get_caller_briefing",
|
||||
'phone = "+56000000000"',
|
||||
'episode_id = "fixture-episode"',
|
||||
'email = "caller@example.test"',
|
||||
"Local Caller",
|
||||
"Local episode text",
|
||||
"caller@example.test",
|
||||
):
|
||||
self.assertIn(expected, instructions)
|
||||
# Configuration injection must preserve ordinary audio forwarding.
|
||||
@@ -260,7 +248,7 @@ class VoiceProxyTests(unittest.TestCase):
|
||||
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)
|
||||
episode.assert_awaited_once_with("fixture-episode")
|
||||
|
||||
def test_proxy_voice_frames_keepalive_and_client_disconnect(self):
|
||||
closed = threading.Event()
|
||||
|
||||
Reference in New Issue
Block a user