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:
+5
-336
@@ -2,7 +2,7 @@
|
||||
"http": {
|
||||
"config-default": {
|
||||
"status": 200,
|
||||
"body": "{\"search\":false,\"lb\":false,\"allowDirect\":true,\"s2sUrl\":\"\",\"primaryPrompt\":\"\",\"reasoningType\":\"PROXY\",\"injectProfile\":false,\"debugPrompt\":false,\"startSessionMode\":\"\",\"theme\":\"\",\"apipilot\":false}",
|
||||
"body": "{\"search\":false,\"lb\":false,\"allowDirect\":true,\"s2sUrl\":\"\",\"primaryPrompt\":\"\",\"reasoningType\":\"PROXY\",\"injectProfile\":false,\"debugPrompt\":false,\"startSessionMode\":\"\",\"theme\":\"\"}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"config-proxy": {
|
||||
"status": 200,
|
||||
"body": "{\"search\":false,\"lb\":false,\"allowDirect\":true,\"s2sUrl\":\"wss://voice.test/v1/realtime\",\"primaryPrompt\":\"\",\"reasoningType\":\"PROXY\",\"injectProfile\":false,\"debugPrompt\":false,\"startSessionMode\":\"\",\"theme\":\"\",\"apipilot\":false}",
|
||||
"body": "{\"search\":false,\"lb\":false,\"allowDirect\":true,\"s2sUrl\":\"wss://voice.test/v1/realtime\",\"primaryPrompt\":\"\",\"reasoningType\":\"PROXY\",\"injectProfile\":false,\"debugPrompt\":false,\"startSessionMode\":\"\",\"theme\":\"\"}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"config-direct": {
|
||||
"status": 200,
|
||||
"body": "{\"search\":false,\"lb\":false,\"allowDirect\":true,\"s2sUrl\":\"wss://voice.test/realtime?token=fixture\",\"primaryPrompt\":\"\",\"reasoningType\":\"PROXY\",\"injectProfile\":false,\"debugPrompt\":false,\"startSessionMode\":\"\",\"theme\":\"\",\"apipilot\":false}",
|
||||
"body": "{\"search\":false,\"lb\":false,\"allowDirect\":true,\"s2sUrl\":\"wss://voice.test/realtime?token=fixture\",\"primaryPrompt\":\"\",\"reasoningType\":\"PROXY\",\"injectProfile\":false,\"debugPrompt\":false,\"startSessionMode\":\"\",\"theme\":\"\"}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
@@ -26,7 +26,7 @@
|
||||
},
|
||||
"config-lb": {
|
||||
"status": 200,
|
||||
"body": "{\"search\":false,\"lb\":true,\"allowDirect\":false,\"s2sUrl\":\"\",\"primaryPrompt\":\"\",\"reasoningType\":\"PROXY\",\"injectProfile\":false,\"debugPrompt\":false,\"startSessionMode\":\"\",\"theme\":\"\",\"apipilot\":false}",
|
||||
"body": "{\"search\":false,\"lb\":true,\"allowDirect\":false,\"s2sUrl\":\"\",\"primaryPrompt\":\"\",\"reasoningType\":\"PROXY\",\"injectProfile\":false,\"debugPrompt\":false,\"startSessionMode\":\"\",\"theme\":\"\"}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
@@ -736,7 +736,7 @@
|
||||
},
|
||||
"upstream": []
|
||||
},
|
||||
"reasoning-OPENAI-legacy-unbound-token": {
|
||||
"reasoning-openai-profile-disabled": {
|
||||
"status": 500,
|
||||
"body": "Internal Server Error",
|
||||
"headers": {
|
||||
@@ -775,279 +775,6 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"apipilot-disabled-GET": {
|
||||
"status": 501,
|
||||
"body": "{\"success\":false,\"data\":null,\"error\":{\"code\":\"APIPILOT_NOT_CONFIGURED\",\"message\":\"ApiPilot no está disponible en este despliegue (S2S_APIPILOT_TOOLS_URL no establecido).\",\"status_code\":501,\"raw\":null}}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"upstream": []
|
||||
},
|
||||
"apipilot-disabled-POST": {
|
||||
"status": 501,
|
||||
"body": "{\"success\":false,\"data\":null,\"error\":{\"code\":\"APIPILOT_NOT_CONFIGURED\",\"message\":\"ApiPilot no está disponible en este despliegue (S2S_APIPILOT_TOOLS_URL no establecido).\",\"status_code\":501,\"raw\":null}}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"upstream": []
|
||||
},
|
||||
"tools-query-validation": {
|
||||
"status": 422,
|
||||
"body": "{\"detail\":[{\"type\":\"bool_parsing\",\"loc\":[\"query\",\"refresh\"],\"msg\":\"Input should be a valid boolean, unable to interpret input\",\"input\":\"invalid\"}]}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"upstream": []
|
||||
},
|
||||
"tools-list": {
|
||||
"status": 200,
|
||||
"body": "{\"success\":true,\"data\":{\"count\":1,\"include_unauthorized\":false,\"tools\":[{\"name\":\"fixture_tool\",\"description\":\"A fixture tool.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"count\":{\"type\":\"integer\"}},\"required\":[\"count\"]},\"requires_auth\":false}]},\"error\":null}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"upstream": [
|
||||
{
|
||||
"method": "GET",
|
||||
"url": "http://tools.test/apipilot/tools?include_unauthorized=false",
|
||||
"body": "",
|
||||
"headers": {
|
||||
"authorization": "Bearer fixture-token",
|
||||
"x-demo-session": "fixture-session"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"tools-result": {
|
||||
"status": 200,
|
||||
"body": "{\"success\":true,\"data\":{\"count\":1,\"include_unauthorized\":false,\"tools\":[{\"name\":\"fixture_tool\",\"description\":\"A fixture tool.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"count\":{\"type\":\"integer\"}},\"required\":[\"count\"]},\"requires_auth\":false}]},\"error\":null}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"upstream": [
|
||||
{
|
||||
"method": "GET",
|
||||
"url": "http://tools.test/apipilot/tools?include_unauthorized=false",
|
||||
"body": "",
|
||||
"headers": {
|
||||
"authorization": "Bearer fixture-token",
|
||||
"x-demo-session": "fixture-session"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"tools-envelope": {
|
||||
"status": 200,
|
||||
"body": "{\"success\":true,\"data\":{\"count\":1,\"include_unauthorized\":false,\"tools\":[{\"name\":\"fixture_tool\",\"description\":\"A fixture tool.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"count\":{\"type\":\"integer\"}},\"required\":[\"count\"]},\"requires_auth\":false}]},\"error\":null}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"upstream": [
|
||||
{
|
||||
"method": "GET",
|
||||
"url": "http://tools.test/apipilot/tools?include_unauthorized=false",
|
||||
"body": "",
|
||||
"headers": {
|
||||
"authorization": "Bearer fixture-token",
|
||||
"x-demo-session": "fixture-session"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"tools-opaque": {
|
||||
"status": 200,
|
||||
"body": "{\"success\":true,\"data\":{\"count\":0,\"include_unauthorized\":false,\"tools\":[]},\"error\":null}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"upstream": [
|
||||
{
|
||||
"method": "GET",
|
||||
"url": "http://tools.test/apipilot/tools?include_unauthorized=false",
|
||||
"body": "",
|
||||
"headers": {
|
||||
"authorization": "Bearer fixture-token",
|
||||
"x-demo-session": "fixture-session"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"tools-upstream-error": {
|
||||
"status": 502,
|
||||
"body": "{\"success\":false,\"data\":null,\"error\":{\"code\":\"APIPILOT_BAD_UPSTREAM\",\"message\":\"Backend ApiPilot tools devolvió HTTP 403\",\"status_code\":403}}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"upstream": [
|
||||
{
|
||||
"method": "GET",
|
||||
"url": "http://tools.test/apipilot/tools?include_unauthorized=true",
|
||||
"body": "",
|
||||
"headers": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
"execute-discovery-error-envelope": {
|
||||
"status": 502,
|
||||
"body": "{\"detail\":{\"code\":\"APIPILOT_BAD_UPSTREAM\",\"message\":\"Backend ApiPilot tools devolvió HTTP 503\",\"status_code\":503}}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"upstream": [
|
||||
{
|
||||
"method": "GET",
|
||||
"url": "http://tools.test/apipilot/tools?include_unauthorized=true",
|
||||
"body": "",
|
||||
"headers": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
"execute-unknown-tool": {
|
||||
"status": 404,
|
||||
"body": "{\"success\":false,\"data\":null,\"error\":{\"code\":\"TOOL_NOT_FOUND\",\"message\":\"Herramienta «unknown» no encontrada en el catálogo ApiPilot. Actualice la lista de herramientas y vuelva a intentarlo.\",\"status_code\":404,\"raw\":null}}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"upstream": [
|
||||
{
|
||||
"method": "GET",
|
||||
"url": "http://tools.test/apipilot/tools?include_unauthorized=true",
|
||||
"body": "",
|
||||
"headers": {}
|
||||
},
|
||||
{
|
||||
"method": "GET",
|
||||
"url": "http://tools.test/apipilot/tools?include_unauthorized=true",
|
||||
"body": "",
|
||||
"headers": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
"execute-missing-argument": {
|
||||
"status": 400,
|
||||
"body": "{\"success\":false,\"data\":null,\"error\":{\"code\":\"INVALID_ARGUMENTS\",\"message\":\"Falta el parámetro requerido: count\",\"status_code\":400,\"raw\":{\"removed\":[]}}}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"upstream": [
|
||||
{
|
||||
"method": "GET",
|
||||
"url": "http://tools.test/apipilot/tools?include_unauthorized=true",
|
||||
"body": "",
|
||||
"headers": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
"execute-api-key-precheck": {
|
||||
"status": 401,
|
||||
"body": "{\"success\":false,\"data\":null,\"error\":{\"code\":\"AUTH_REQUIRED\",\"message\":\"La herramienta «fixture_tool» requiere autenticación. Inicie sesión o proporcione un token para continuar.\",\"status_code\":401,\"raw\":null}}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"upstream": [
|
||||
{
|
||||
"method": "GET",
|
||||
"url": "http://tools.test/apipilot/tools?include_unauthorized=true",
|
||||
"body": "",
|
||||
"headers": {
|
||||
"authorization": "Bearer fixture-key",
|
||||
"x-demo-session": "fixture-key",
|
||||
"x-api-key": "fixture-key"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"execute-envelope": {
|
||||
"status": 200,
|
||||
"body": "{\"success\":true,\"data\":{\"future\":[1,null]},\"error\":null,\"future_envelope\":true}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"upstream": [
|
||||
{
|
||||
"method": "GET",
|
||||
"url": "http://tools.test/apipilot/tools?include_unauthorized=true",
|
||||
"body": "",
|
||||
"headers": {}
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"url": "http://tools.test/apipilot/execute",
|
||||
"body": "{\"name\":\"fixture_tool\",\"arguments\":{\"count\":\"wrong-type-is-accepted\"}}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"execute-raw-list": {
|
||||
"status": 200,
|
||||
"body": "{\"success\":true,\"data\":[{\"future\":true}],\"error\":null}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"upstream": [
|
||||
{
|
||||
"method": "GET",
|
||||
"url": "http://tools.test/apipilot/tools?include_unauthorized=true",
|
||||
"body": "",
|
||||
"headers": {}
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"url": "http://tools.test/apipilot/execute",
|
||||
"body": "{\"name\":\"fixture_tool\",\"arguments\":{\"count\":\"wrong-type-is-accepted\"}}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"execute-minimal-envelope": {
|
||||
"status": 200,
|
||||
"body": "{\"success\":true}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"upstream": [
|
||||
{
|
||||
"method": "GET",
|
||||
"url": "http://tools.test/apipilot/tools?include_unauthorized=true",
|
||||
"body": "",
|
||||
"headers": {}
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"url": "http://tools.test/apipilot/execute",
|
||||
"body": "{\"name\":\"fixture_tool\",\"arguments\":{\"count\":\"wrong-type-is-accepted\"}}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"execute-error": {
|
||||
"status": 409,
|
||||
"body": "{\"success\":false,\"data\":null,\"error\":{\"status_code\":409,\"code\":\"FIXTURE\",\"message\":\"Fixture\",\"future\":true}}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
"upstream": [
|
||||
{
|
||||
"method": "GET",
|
||||
"url": "http://tools.test/apipilot/tools?include_unauthorized=true",
|
||||
"body": "",
|
||||
"headers": {}
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
"url": "http://tools.test/apipilot/execute",
|
||||
"body": "{\"name\":\"fixture_tool\",\"arguments\":{\"count\":\"wrong-type-is-accepted\"}}",
|
||||
"headers": {
|
||||
"content-type": "application/json"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"openapi_requests": {
|
||||
@@ -1055,44 +782,6 @@
|
||||
"GET /api/config": {
|
||||
"operationId": "config_api_config_get"
|
||||
},
|
||||
"GET /api/apipilot/tools": {
|
||||
"operationId": "apipilot_list_tools_api_apipilot_tools_get",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "include_unauthorized",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"title": "Include Unauthorized"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "refresh",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"title": "Refresh"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"POST /api/apipilot/execute": {
|
||||
"operationId": "apipilot_execute_api_apipilot_execute_post",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApipilotExecuteRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
"POST /api/reasoning": {
|
||||
"operationId": "reasoning_api_reasoning_post",
|
||||
"requestBody": {
|
||||
@@ -1264,26 +953,6 @@
|
||||
],
|
||||
"title": "ReasoningRequest"
|
||||
},
|
||||
"ApipilotExecuteRequest": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"title": "Name"
|
||||
},
|
||||
"arguments": {
|
||||
"additionalProperties": true,
|
||||
"type": "object",
|
||||
"title": "Arguments",
|
||||
"default": {}
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"title": "ApipilotExecuteRequest",
|
||||
"description": "Body for the ApiPilot execute proxy endpoint. Mirrors §2.2 request shape."
|
||||
},
|
||||
"HTTPValidationError": {
|
||||
"properties": {
|
||||
"detail": {
|
||||
|
||||
@@ -19,7 +19,6 @@ from fastapi.testclient import TestClient
|
||||
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
|
||||
from vocero_api.realtime import proxy
|
||||
from vocero_api.services import context
|
||||
|
||||
@@ -42,21 +41,10 @@ BASE_CONFIG = {
|
||||
"S2S_LOGIN_BY_PHONE": "",
|
||||
"S2S_LATEST_SUMMARY": "",
|
||||
"S2S_LATEST_SUMMARY_TEXT": "",
|
||||
"S2S_APIPILOT_TOOLS_URL": "",
|
||||
"S2S_APIPILOT_TOOLS_EXECUTE_URL": "",
|
||||
"S2S_APIPILOT_SERVICE_TOKEN": "",
|
||||
"S2S_APIPILOT_API_KEY": "",
|
||||
"OPENAI_API_KEY": "",
|
||||
"S2S_REPORTS_QUICK_HINT_MAX": 5,
|
||||
"S2S_REPORTS_INJECT_MODE": "minimal",
|
||||
}
|
||||
TOOL = {
|
||||
"name": "fixture_tool",
|
||||
"description": "A fixture tool.",
|
||||
"parameters": {"type": "object", "properties": {"count": {"type": "integer"}}, "required": ["count"]},
|
||||
"requires_auth": False,
|
||||
"future_definition_field": {"nested": True},
|
||||
}
|
||||
|
||||
|
||||
def cases():
|
||||
@@ -268,7 +256,7 @@ def cases():
|
||||
for name, payload in (("missing", {}), ("proxy", {"transcript": "hello"}), ("proxy-blank", {"transcript": " "})):
|
||||
yield f"reasoning-{name}", "POST", "/api/reasoning", {"json": payload}, {}, []
|
||||
yield (
|
||||
"reasoning-OPENAI-legacy-unbound-token",
|
||||
"reasoning-openai-profile-disabled",
|
||||
"POST",
|
||||
"/api/reasoning",
|
||||
{"json": {"transcript": "hello"}},
|
||||
@@ -323,91 +311,6 @@ def cases():
|
||||
],
|
||||
)
|
||||
|
||||
for method, path, kwargs in (
|
||||
("GET", "/api/apipilot/tools", {}),
|
||||
("POST", "/api/apipilot/execute", {"json": {"name": "fixture_tool"}}),
|
||||
):
|
||||
yield f"apipilot-disabled-{method}", method, path, kwargs, {}, []
|
||||
tool_config = {"S2S_APIPILOT_TOOLS_URL": "http://tools.test/apipilot"}
|
||||
yield "tools-query-validation", "GET", "/api/apipilot/tools?refresh=invalid", {}, tool_config, []
|
||||
for name, payload in (
|
||||
("list", [TOOL]),
|
||||
("result", {"result": [TOOL]}),
|
||||
("envelope", {"data": {"tools": [TOOL]}, "future": True}),
|
||||
("opaque", "anything"),
|
||||
):
|
||||
yield (
|
||||
f"tools-{name}",
|
||||
"GET",
|
||||
"/api/apipilot/tools?include_unauthorized=false&refresh=true",
|
||||
{"headers": {"Authorization": "Bearer fixture-token", "x-demo-session": "fixture-session"}},
|
||||
tool_config,
|
||||
[(200, payload)],
|
||||
)
|
||||
yield "tools-upstream-error", "GET", "/api/apipilot/tools", {}, tool_config, [(403, {"error": "fixture"})]
|
||||
yield (
|
||||
"execute-discovery-error-envelope",
|
||||
"POST",
|
||||
"/api/apipilot/execute",
|
||||
{"json": {"name": "fixture_tool"}},
|
||||
tool_config,
|
||||
[(503, {})],
|
||||
)
|
||||
yield (
|
||||
"execute-unknown-tool",
|
||||
"POST",
|
||||
"/api/apipilot/execute",
|
||||
{"json": {"name": "unknown"}},
|
||||
tool_config,
|
||||
[(200, [TOOL]), (200, [TOOL])],
|
||||
)
|
||||
yield (
|
||||
"execute-missing-argument",
|
||||
"POST",
|
||||
"/api/apipilot/execute",
|
||||
{"json": {"name": "fixture_tool"}},
|
||||
tool_config,
|
||||
[(200, [TOOL])],
|
||||
)
|
||||
yield (
|
||||
"execute-api-key-precheck",
|
||||
"POST",
|
||||
"/api/apipilot/execute",
|
||||
{"json": {"name": "fixture_tool", "arguments": {"count": 1}}},
|
||||
{
|
||||
**tool_config,
|
||||
"S2S_APIPILOT_API_KEY": "fixture-key",
|
||||
},
|
||||
[(200, [{**TOOL, "requires_auth": True}])],
|
||||
)
|
||||
for name, payload in (
|
||||
("envelope", {"success": True, "data": {"future": [1, None]}, "error": None, "future_envelope": True}),
|
||||
("raw-list", [{"future": True}]),
|
||||
("minimal-envelope", {"success": True}),
|
||||
(
|
||||
"error",
|
||||
{
|
||||
"success": False,
|
||||
"data": None,
|
||||
"error": {"status_code": 409, "code": "FIXTURE", "message": "Fixture", "future": True},
|
||||
},
|
||||
),
|
||||
):
|
||||
yield (
|
||||
f"execute-{name}",
|
||||
"POST",
|
||||
"/api/apipilot/execute",
|
||||
{
|
||||
"json": {
|
||||
"name": "fixture_tool",
|
||||
"arguments": {"count": "wrong-type-is-accepted", "unknown": 1},
|
||||
"future": True,
|
||||
}
|
||||
},
|
||||
tool_config,
|
||||
[(200, [TOOL]), (200, payload)],
|
||||
)
|
||||
|
||||
|
||||
def capture_http():
|
||||
captures = {}
|
||||
@@ -442,7 +345,6 @@ def capture_http():
|
||||
|
||||
with (
|
||||
patch.multiple(settings, **{**BASE_CONFIG, **config}),
|
||||
patch.object(apipilot, "_APIPILOT_CACHE", {}),
|
||||
patch.object(context, "_build_today_fixed_context_block", return_value="Fixture current date: 2026-01-04"),
|
||||
patch.object(
|
||||
httpx,
|
||||
@@ -482,7 +384,6 @@ def capture_openapi_requests():
|
||||
for name in (
|
||||
"SearchRequest",
|
||||
"ReasoningRequest",
|
||||
"ApipilotExecuteRequest",
|
||||
"HTTPValidationError",
|
||||
"ValidationError",
|
||||
)
|
||||
|
||||
@@ -143,8 +143,8 @@ def test_http_chains_tools_retains_history_and_ignores_browser_identity(enabled,
|
||||
assert client.post("/api/reasoning", json=body).status_code == 401
|
||||
assert client.get("/api/user/phone?phoneNumber=123").status_code == 409
|
||||
assert client.get("/api/user/reports").status_code == 409
|
||||
assert client.get("/api/apipilot/tools").status_code == 409
|
||||
assert client.post("/api/apipilot/execute", json={"name": "get_report"}).status_code == 409
|
||||
assert client.get("/api/apipilot/tools").status_code == 404
|
||||
assert client.post("/api/apipilot/execute", json={"name": "get_report"}).status_code == 404
|
||||
enabled.assert_awaited_once_with("+56000000001")
|
||||
|
||||
|
||||
|
||||
@@ -5,14 +5,13 @@ from fastapi.responses import JSONResponse
|
||||
from pydantic import ValidationError
|
||||
|
||||
from vocero_api.api.responses import validated_json_response
|
||||
from vocero_api.models.requests import ApipilotExecuteRequest, QueueEndPayload, SearchRequest
|
||||
from vocero_api.models.requests import QueueEndPayload, SearchRequest
|
||||
from vocero_api.models.responses import (
|
||||
AllocationResponse,
|
||||
QueueCancellationResponse,
|
||||
ReasoningResponse,
|
||||
ReportsResponse,
|
||||
SearchResponse,
|
||||
ToolExecutionResponse,
|
||||
UserProfileResponse,
|
||||
)
|
||||
|
||||
@@ -22,9 +21,7 @@ from vocero_api.models.responses import (
|
||||
[
|
||||
(AllocationResponse, {}),
|
||||
(AllocationResponse, {"state": [1, None], "connectUrl": "wss://host", "future": {"x": 1.0}}),
|
||||
(ToolExecutionResponse, {"success": True, "unknown": {"nested": [None, 1.0, False]}}),
|
||||
(ToolExecutionResponse, {"success": False, "data": 42, "error": "loose error", "future": []}),
|
||||
(ReasoningResponse, {"answer": "á", "usage_pass2": [1, None], "future": True}),
|
||||
(ReasoningResponse, {"answer": "á", "future": [1, None, True]}),
|
||||
(SearchResponse, {"query": "q", "answer": {"x": 2}, "results": [{"title": [], "snippet": 7, "url": None}]}),
|
||||
(
|
||||
ReportsResponse,
|
||||
@@ -47,10 +44,8 @@ def test_owned_response_fields_are_actually_validated(payload):
|
||||
validated_json_response(payload, QueueCancellationResponse)
|
||||
|
||||
|
||||
def test_request_models_keep_legacy_extra_field_and_argument_behavior():
|
||||
def test_request_models_keep_legacy_extra_field_and_beacon_behavior():
|
||||
assert SearchRequest.model_validate({"query": "q", "future": True}).model_dump() == {"query": "q", "key": None}
|
||||
arguments = {"arbitrary": {"nested": [1, None]}, "unknown": True}
|
||||
assert ApipilotExecuteRequest.model_validate({"name": "tool", "arguments": arguments}).arguments == arguments
|
||||
for value in [None, "id", 42, False, [], {}, {"future": True}]:
|
||||
assert QueueEndPayload.model_validate({"queueId": value, "future": 1}).queueId == value
|
||||
assert QueueEndPayload.model_validate({}).queueId == ""
|
||||
|
||||
@@ -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