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:
2026-10-08 11:23:41 -04:00
parent 0311d3f1ff
commit dfed03ac7c
39 changed files with 198 additions and 3314 deletions
+24 -36
View File
@@ -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()