fix: standardize Spanish user-facing copy
This commit is contained in:
+52
-52
@@ -3495,10 +3495,10 @@ async def apipilot_execute(request: Request, body: ApipilotExecuteRequest):
|
||||
@app.post("/api/reasoning")
|
||||
async def reasoning(req: ReasoningRequest):
|
||||
if S2S_REASONING_TYPE == "PROXY":
|
||||
raise HTTPException(status_code=409, detail="Reasoning endpoint is disabled in PROXY mode.")
|
||||
raise HTTPException(status_code=409, detail="El servicio de razonamiento está deshabilitado en el modo PROXY.")
|
||||
transcript = (req.transcript or "").strip()
|
||||
if not transcript:
|
||||
raise HTTPException(status_code=400, detail="transcript is required.")
|
||||
raise HTTPException(status_code=400, detail="Se requiere el campo transcript.")
|
||||
|
||||
# ── S2S_INJECT_PROFILE: resolve podcast episode + caller profile + reports list + latest summary ──
|
||||
# (When S2S_INJECT_PROFILE=false this entire block is a no-op; user_context
|
||||
@@ -3516,7 +3516,7 @@ async def reasoning(req: ReasoningRequest):
|
||||
)
|
||||
else:
|
||||
needs_phone_authed = bool(VOCERO_USER_PHONE_AUTH_URL.strip())
|
||||
# 2026-09-11 user fix: remove mass injection of scheduled reports.
|
||||
# 2026-09-11 user fix: remove mass injection of scheduled reports.
|
||||
# treats S2S_REPORT_LIST as "need to fetch reports" ONLY when BOTH the URL is
|
||||
# configured AND the new opt-in S2S_INJECT_REPORTS flag is true. When
|
||||
# S2S_INJECT_REPORTS=false (default) needs_reports stays False here → we skip
|
||||
@@ -3822,7 +3822,7 @@ async def reasoning(req: ReasoningRequest):
|
||||
if not NODE_RED_REASONING_URL:
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail="S2S_REASONING_NODE_RED_URL is not configured. Set the env var to the Node-RED endpoint (e.g. http://localhost:1881).",
|
||||
detail="S2S_REASONING_NODE_RED_URL no está configurado. Configure esta variable de entorno con la dirección de Node-RED (por ejemplo, http://localhost:1881).",
|
||||
)
|
||||
logger.info(
|
||||
"[INJECT-PROFILE] REASONING->NODE_RED body fields. instructionsLen=%d userCtxLen=%d phoneLen=%d episodeIdLen=%d",
|
||||
@@ -3857,7 +3857,7 @@ async def reasoning(req: ReasoningRequest):
|
||||
)
|
||||
except httpx.RequestError as exc:
|
||||
logger.warning("[EXT][NODE_RED] unreachable: %r", exc)
|
||||
raise HTTPException(status_code=502, detail="Node-RED reasoning engine is unreachable.")
|
||||
raise HTTPException(status_code=502, detail="No se puede conectar con el motor de razonamiento de Node-RED.")
|
||||
logger.debug(
|
||||
"[EXT][NODE_RED] reasoning RESP status=%s len=%dB",
|
||||
resp.status_code, len(resp.content or b""),
|
||||
@@ -3866,23 +3866,23 @@ async def reasoning(req: ReasoningRequest):
|
||||
logger.warning("[EXT][NODE_RED] failed %s: %s", resp.status_code, resp.text[:500])
|
||||
raise HTTPException(
|
||||
status_code=502,
|
||||
detail=f"Node-RED reasoning failed (HTTP {resp.status_code}).",
|
||||
detail=f"El razonamiento de Node-RED falló (HTTP {resp.status_code}).",
|
||||
)
|
||||
try:
|
||||
payload = resp.json()
|
||||
except Exception:
|
||||
logger.warning("[EXT][NODE_RED] invalid JSON: %s", resp.text[:500])
|
||||
raise HTTPException(status_code=502, detail="Node-RED returned invalid JSON.")
|
||||
raise HTTPException(status_code=502, detail="Node-RED devolvió un JSON no válido.")
|
||||
answer = payload.get("answer") if isinstance(payload, dict) else None
|
||||
log_only = bool(isinstance(payload, dict) and payload.get("log_only") is True)
|
||||
if not isinstance(answer, str):
|
||||
logger.warning("[EXT][NODE_RED] invalid answer type: %s", json.dumps(payload, ensure_ascii=True)[:800])
|
||||
raise HTTPException(status_code=502, detail="Node-RED returned invalid answer field.")
|
||||
raise HTTPException(status_code=502, detail="Node-RED devolvió un campo answer no válido.")
|
||||
answer_stripped = answer.strip()
|
||||
# An empty answer is intentional in log-only mode; forward the flag.
|
||||
if not answer_stripped and not log_only:
|
||||
logger.warning("[EXT][NODE_RED] empty answer and !log_only: %s", json.dumps(payload, ensure_ascii=True)[:800])
|
||||
raise HTTPException(status_code=502, detail="Node-RED returned no answer without log_only flag.")
|
||||
raise HTTPException(status_code=502, detail="Node-RED no devolvió una respuesta ni la marca log_only.")
|
||||
usage = payload.get("usage") if isinstance(payload, dict) else None
|
||||
if isinstance(usage, dict):
|
||||
logger.debug(
|
||||
@@ -3920,7 +3920,7 @@ async def reasoning(req: ReasoningRequest):
|
||||
tool_name_p1,
|
||||
json.dumps(tool_args_p1 or {}, ensure_ascii=True)[:300],
|
||||
)
|
||||
logger.info("[APIPILOT-CALL-NAME] USE=NODE_RED_PASS2_EXEC_HELPER name=%r → forward a _apipilot_execute_tool", tool_name_p1)
|
||||
logger.info("[APIPILOT-CALL-NAME] USE=NODE_RED_PASS2_EXEC_HELPER name=%r → forward to _apipilot_execute_tool", tool_name_p1)
|
||||
tool_env_p1 = await _apipilot_execute_tool(
|
||||
auth_header=apipilot_auth_header,
|
||||
demo_session=None,
|
||||
@@ -3951,7 +3951,7 @@ async def reasoning(req: ReasoningRequest):
|
||||
},
|
||||
)
|
||||
logger.info(
|
||||
"[APIPILOT] REASONING PASS-2 execute name=%s success=%s httpResultLen=%d trigger seconda chiamata Node-RED",
|
||||
"[APIPILOT] REASONING PASS-2 execute name=%s success=%s httpResultLen=%d trigger second Node-RED call",
|
||||
tool_name_p1,
|
||||
str(not is_tool_err).lower(),
|
||||
len(tool_text_p1),
|
||||
@@ -4020,15 +4020,15 @@ async def reasoning(req: ReasoningRequest):
|
||||
)
|
||||
except httpx.RequestError as exc:
|
||||
logger.warning("[EXT][NODE_RED PASS-2] unreachable: %r", exc)
|
||||
raise HTTPException(status_code=502, detail="Node-RED reasoning engine is unreachable (pass 2).")
|
||||
raise HTTPException(status_code=502, detail="No se puede conectar con el motor de razonamiento de Node-RED (segunda pasada).")
|
||||
if resp2.status_code != 200:
|
||||
logger.warning("[EXT][NODE_RED PASS-2] failed %s: %s", resp2.status_code, resp2.text[:500])
|
||||
raise HTTPException(status_code=502, detail=f"Node-RED reasoning pass 2 failed (HTTP {resp2.status_code}).")
|
||||
raise HTTPException(status_code=502, detail=f"La segunda pasada del razonamiento de Node-RED falló (HTTP {resp2.status_code}).")
|
||||
try:
|
||||
payload2 = resp2.json()
|
||||
except Exception:
|
||||
logger.warning("[EXT][NODE_RED PASS-2] invalid JSON: %s", resp2.text[:500])
|
||||
raise HTTPException(status_code=502, detail="Node-RED returned invalid JSON (pass 2).")
|
||||
raise HTTPException(status_code=502, detail="Node-RED devolvió un JSON no válido (segunda pasada).")
|
||||
final_ans = payload2.get("answer") if isinstance(payload2, dict) else None
|
||||
if not isinstance(final_ans, str):
|
||||
# Fallback: the first message and raw result.
|
||||
@@ -4058,7 +4058,7 @@ async def reasoning(req: ReasoningRequest):
|
||||
# Mode B: OPENAI calls OpenAI Chat Completions directly.
|
||||
# ────────────────────────────────────────────────────────────────────
|
||||
if not OPENAI_API_KEY:
|
||||
raise HTTPException(status_code=503, detail="OPENAI_API_KEY is not configured.")
|
||||
raise HTTPException(status_code=503, detail="OPENAI_API_KEY no está configurado.")
|
||||
system_parts = []
|
||||
instructions = effective_instructions or ((req.instructions or "").strip())
|
||||
if instructions:
|
||||
@@ -4114,19 +4114,19 @@ async def reasoning(req: ReasoningRequest):
|
||||
)
|
||||
except httpx.RequestError as exc:
|
||||
logger.warning("OpenAI reasoning unreachable: %r", exc)
|
||||
raise HTTPException(status_code=502, detail="OpenAI reasoning unreachable.")
|
||||
raise HTTPException(status_code=502, detail="No se puede conectar con el servicio de razonamiento de OpenAI.")
|
||||
logger.debug(
|
||||
"[EXT][OPENAI] reasoning RESP status=%s len=%dB",
|
||||
resp.status_code, len(resp.content or b""),
|
||||
)
|
||||
if resp.status_code != 200:
|
||||
logger.warning("OpenAI reasoning failed %s: %s", resp.status_code, resp.text[:400])
|
||||
raise HTTPException(status_code=502, detail=f"OpenAI reasoning failed ({resp.status_code}).")
|
||||
raise HTTPException(status_code=502, detail=f"El razonamiento de OpenAI falló ({resp.status_code}).")
|
||||
try:
|
||||
payload = resp.json()
|
||||
except Exception:
|
||||
logger.warning("OpenAI reasoning returned invalid JSON: %s", resp.text[:400])
|
||||
raise HTTPException(status_code=502, detail="OpenAI reasoning returned invalid JSON.")
|
||||
raise HTTPException(status_code=502, detail="El servicio de razonamiento de OpenAI devolvió un JSON no válido.")
|
||||
answer = ""
|
||||
choices = payload.get("choices")
|
||||
if isinstance(choices, list) and choices:
|
||||
@@ -4143,7 +4143,7 @@ async def reasoning(req: ReasoningRequest):
|
||||
)
|
||||
if not answer:
|
||||
logger.warning("OpenAI reasoning returned empty answer: %s", json.dumps(payload, ensure_ascii=True)[:800])
|
||||
raise HTTPException(status_code=502, detail="OpenAI reasoning returned no answer.")
|
||||
raise HTTPException(status_code=502, detail="El servicio de razonamiento de OpenAI no devolvió una respuesta.")
|
||||
logger.info("[Vocero REASONING] engine=OPENAI transcript=%s answer=%s", transcript[:300], answer[:500])
|
||||
# Capture the ApiPilot marker, execute its tool and run OpenAI pass 2.
|
||||
tool_name_p1_oai, tool_args_p1_oai = _apipilot_parse_call_marker(answer)
|
||||
@@ -4154,7 +4154,7 @@ async def reasoning(req: ReasoningRequest):
|
||||
tool_name_p1_oai,
|
||||
json.dumps(tool_args_p1_oai or {}, ensure_ascii=True)[:300],
|
||||
)
|
||||
logger.info("[APIPILOT-CALL-NAME] USE=OPENAI_PASS2_EXEC_HELPER name=%r → forward a _apipilot_execute_tool", tool_name_p1_oai)
|
||||
logger.info("[APIPILOT-CALL-NAME] USE=OPENAI_PASS2_EXEC_HELPER name=%r → forward to _apipilot_execute_tool", tool_name_p1_oai)
|
||||
tool_env_oai_p1 = await _apipilot_execute_tool(
|
||||
auth_header=apipilot_auth_header,
|
||||
demo_session=None,
|
||||
@@ -4185,7 +4185,7 @@ async def reasoning(req: ReasoningRequest):
|
||||
},
|
||||
)
|
||||
logger.info(
|
||||
"[APIPILOT] REASONING PASS-2 OPENAI execute name=%s success=%s resultLen=%d → seconda chiamata chat/completions",
|
||||
"[APIPILOT] REASONING PASS-2 OPENAI execute name=%s success=%s resultLen=%d → second chat/completions call",
|
||||
tool_name_p1_oai,
|
||||
str(not is_tool_err_oai).lower(),
|
||||
len(tool_text_oai),
|
||||
@@ -4263,15 +4263,15 @@ async def reasoning(req: ReasoningRequest):
|
||||
)
|
||||
except httpx.RequestError as exc:
|
||||
logger.warning("[EXT][OPENAI PASS-2] unreachable: %r", exc)
|
||||
raise HTTPException(status_code=502, detail="OpenAI reasoning unreachable (pass 2).")
|
||||
raise HTTPException(status_code=502, detail="No se puede conectar con el servicio de razonamiento de OpenAI (segunda pasada).")
|
||||
if resp_oai_2.status_code != 200:
|
||||
logger.warning("[EXT][OPENAI PASS-2] failed %s: %s", resp_oai_2.status_code, resp_oai_2.text[:400])
|
||||
raise HTTPException(status_code=502, detail=f"OpenAI reasoning pass 2 failed ({resp_oai_2.status_code}).")
|
||||
raise HTTPException(status_code=502, detail=f"La segunda pasada del razonamiento de OpenAI falló ({resp_oai_2.status_code}).")
|
||||
try:
|
||||
payload_oai_2 = resp_oai_2.json()
|
||||
except Exception:
|
||||
logger.warning("[EXT][OPENAI PASS-2] invalid JSON: %s", resp_oai_2.text[:400])
|
||||
raise HTTPException(status_code=502, detail="OpenAI reasoning returned invalid JSON (pass 2).")
|
||||
raise HTTPException(status_code=502, detail="El servicio de razonamiento de OpenAI devolvió un JSON no válido (segunda pasada).")
|
||||
final_oai_ans = ""
|
||||
choices_oai_2 = payload_oai_2.get("choices")
|
||||
if isinstance(choices_oai_2, list) and choices_oai_2:
|
||||
@@ -4346,7 +4346,7 @@ async def realtime_proxy(ws: WebSocket):
|
||||
if not S2S_PROXY_TARGET:
|
||||
logger.debug("[EXT][S2S-PROXY] client=%s rejected: S2S_PROXY_TARGET not configured", client_ip)
|
||||
await ws.accept()
|
||||
await ws.close(code=1013, reason="Realtime proxy not configured.")
|
||||
await ws.close(code=1013, reason="El proxy de tiempo real no está configurado.")
|
||||
return
|
||||
|
||||
await ws.accept()
|
||||
@@ -4753,7 +4753,7 @@ async def realtime_proxy(ws: WebSocket):
|
||||
logger.warning("Realtime proxy failed: %r", exc)
|
||||
logger.debug("[EXT][S2S-PROXY] client=%s ERROR %s", client_ip, type(exc).__name__)
|
||||
try:
|
||||
await ws.close(code=1011, reason="Upstream realtime unavailable.")
|
||||
await ws.close(code=1011, reason="El servicio de tiempo real no está disponible.")
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
@@ -4783,11 +4783,11 @@ async def user_by_phone(phoneNumber: str):
|
||||
if not VOCERO_USER_PHONE_AUTH_URL:
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail="VOCERO_USER_PHONE_AUTH_URL is not configured on this deploy.",
|
||||
detail="VOCERO_USER_PHONE_AUTH_URL no está configurado en este despliegue.",
|
||||
)
|
||||
phone = (phoneNumber or "").strip()
|
||||
if not phone:
|
||||
raise HTTPException(status_code=400, detail="phoneNumber is required.")
|
||||
raise HTTPException(status_code=400, detail="Se requiere el campo phoneNumber.")
|
||||
|
||||
# 0) Login-by-phone (optional — only if S2S_LOGIN_BY_PHONE env is set).
|
||||
login_token = None
|
||||
@@ -4809,7 +4809,7 @@ async def user_by_phone(phoneNumber: str):
|
||||
resp = await http.get(target_url, headers=headers or None)
|
||||
except httpx.RequestError as exc:
|
||||
logger.warning("[EXT][USER-BY-PHONE] unreachable: %r", exc)
|
||||
raise HTTPException(status_code=502, detail="Upstream user-info endpoint is unreachable.")
|
||||
raise HTTPException(status_code=502, detail="No se puede conectar con el servicio de información del usuario.")
|
||||
# Forward the upstream status + JSON verbatim; never rewrite the payload.
|
||||
if resp.status_code >= 500:
|
||||
logger.warning(
|
||||
@@ -4821,7 +4821,7 @@ async def user_by_phone(phoneNumber: str):
|
||||
except Exception:
|
||||
raise HTTPException(
|
||||
status_code=502,
|
||||
detail="Upstream user-info endpoint returned invalid JSON.",
|
||||
detail="El servicio de información del usuario devolvió un JSON no válido.",
|
||||
)
|
||||
return JSONResponse(status_code=resp.status_code if resp.status_code != 204 else 200, content=payload)
|
||||
|
||||
@@ -4870,14 +4870,14 @@ async def user_reports(phoneNumber: str | None = None):
|
||||
"hints": [],
|
||||
"entries": [],
|
||||
"skipped": True,
|
||||
"reason": "S2S_INJECT_REPORTS is disabled on this deploy; no scheduled reports exposed to UI or LLM.",
|
||||
"reason": "S2S_INJECT_REPORTS está deshabilitado en este despliegue; no se muestran reportes programados en la interfaz ni se envían al modelo.",
|
||||
},
|
||||
)
|
||||
# ───────────────────────────────────────────────────────────────────────
|
||||
if not S2S_REPORT_LIST:
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail="S2S_REPORT_LIST is not configured on this deploy.",
|
||||
detail="S2S_REPORT_LIST no está configurado en este despliegue.",
|
||||
)
|
||||
phone = (phoneNumber or "").strip() or None
|
||||
# 0) Login-by-phone if enabled. Bearer token used only if login succeeded
|
||||
@@ -4897,7 +4897,7 @@ async def user_reports(phoneNumber: str | None = None):
|
||||
"hints": [],
|
||||
"entries": [],
|
||||
"skipped": True,
|
||||
"reason": "empty list / upstream error / nothing to inject",
|
||||
"reason": "Lista vacía, error del servicio de origen o ningún contenido disponible.",
|
||||
})
|
||||
# Rebuild hints + normalized entries with the exact filtered output
|
||||
items_list: list = reports_raw
|
||||
@@ -4951,8 +4951,8 @@ async def user_reports(phoneNumber: str | None = None):
|
||||
if total_raw_ui > cap_ui:
|
||||
filtered = filtered[:cap_ui]
|
||||
truncated_ui_note = (
|
||||
f"Showing newest {cap_ui} of {total_raw_ui} upstream reports. "
|
||||
f"Adjust S2S_REPORTS_QUICK_HINT_MAX env to raise/lower the window."
|
||||
f"Se muestran los {cap_ui} reportes más recientes de un total de {total_raw_ui}. "
|
||||
"Ajuste la variable de entorno S2S_REPORTS_QUICK_HINT_MAX para cambiar la cantidad."
|
||||
)
|
||||
import datetime as _dt
|
||||
# Deduplicate by label for the UI JSON response only.
|
||||
@@ -5001,12 +5001,12 @@ async def user_reports(phoneNumber: str | None = None):
|
||||
date_piece = str(raw_d)[:32]
|
||||
if label_piece and date_piece:
|
||||
hints_list.append(
|
||||
f"• Report #{idx}: '{label_piece}' — scheduled on {date_piece}"
|
||||
f"• Reporte #{idx}: '{label_piece}' — programado para {date_piece}"
|
||||
)
|
||||
elif label_piece:
|
||||
hints_list.append(f"• Report #{idx}: '{label_piece}'")
|
||||
hints_list.append(f"• Reporte #{idx}: '{label_piece}'")
|
||||
elif date_piece:
|
||||
hints_list.append(f"• Report #{idx}: (no label) — scheduled on {date_piece}")
|
||||
hints_list.append(f"• Reporte #{idx}: (sin título) — programado para {date_piece}")
|
||||
resp_body = {
|
||||
"count": len(entries_out),
|
||||
"rawCount": total_raw_ui,
|
||||
@@ -5069,13 +5069,13 @@ async def search(req: SearchRequest):
|
||||
the user brought their own (then theirs is used for this request only)."""
|
||||
query = (req.query or "").strip()
|
||||
if not query:
|
||||
raise HTTPException(status_code=400, detail="Empty query.")
|
||||
raise HTTPException(status_code=400, detail="La consulta está vacía.")
|
||||
|
||||
key = (req.key or "").strip() or SERPER_KEY
|
||||
has_user_key = bool((req.key or "").strip())
|
||||
if not key:
|
||||
# No server key and the user didn't supply one — search is unavailable.
|
||||
raise HTTPException(status_code=503, detail="Search is not configured.")
|
||||
raise HTTPException(status_code=503, detail="La búsqueda no está configurada.")
|
||||
|
||||
headers = {"X-API-KEY": key, "Content-Type": "application/json"}
|
||||
payload = {"q": query, "num": MAX_RESULTS}
|
||||
@@ -5088,7 +5088,7 @@ async def search(req: SearchRequest):
|
||||
resp = await http.post(SERPER_URL, headers=headers, json=payload)
|
||||
except httpx.RequestError as exc:
|
||||
logger.warning("Serper unreachable: %r", exc)
|
||||
raise HTTPException(status_code=502, detail="Search provider unreachable.")
|
||||
raise HTTPException(status_code=502, detail="No se puede conectar con el proveedor de búsqueda.")
|
||||
logger.debug(
|
||||
"[EXT][SERPER] search RESP status=%s len=%dB",
|
||||
resp.status_code, len(resp.content or b""),
|
||||
@@ -5104,7 +5104,7 @@ async def search(req: SearchRequest):
|
||||
msg = resp.json().get("message")
|
||||
except Exception:
|
||||
pass
|
||||
detail = f"Search provider error ({resp.status_code})"
|
||||
detail = f"Error del proveedor de búsqueda ({resp.status_code})"
|
||||
if msg:
|
||||
detail += f": {msg}"
|
||||
raise HTTPException(status_code=502, detail=detail)
|
||||
@@ -5149,7 +5149,7 @@ async def session(request: Request):
|
||||
if not LOAD_BALANCER_URL:
|
||||
# No LB configured — this deploy is direct-mode only; the browser should
|
||||
# never call this. 404 so it's indistinguishable from a missing route.
|
||||
raise HTTPException(status_code=404, detail="Not found.")
|
||||
raise HTTPException(status_code=404, detail="No encontrado.")
|
||||
|
||||
tier, keys, set_cookie = auth.resolve_identity(request)
|
||||
# Metering runs only on the deployed Space; off-Space the LB still proxies but
|
||||
@@ -5181,7 +5181,7 @@ async def session(request: Request):
|
||||
lb = await http.post(url, headers={"Content-Type": "application/json"}, content="{}")
|
||||
except httpx.RequestError as exc:
|
||||
logger.warning("Load balancer unreachable: %r", exc)
|
||||
raise HTTPException(status_code=502, detail="Speech service unreachable.")
|
||||
raise HTTPException(status_code=502, detail="No se puede conectar con el servicio de voz.")
|
||||
logger.debug(
|
||||
"[EXT][LB] session RESP status=%s len=%dB",
|
||||
lb.status_code, len(lb.content or b""),
|
||||
@@ -5202,7 +5202,7 @@ async def session(request: Request):
|
||||
# The LB's error body may name the reason (e.g. capacity); it carries no
|
||||
# secret, so relay a trimmed copy.
|
||||
logger.warning("Session handshake failed %s: %s", lb.status_code, lb.text[:300])
|
||||
raise HTTPException(status_code=502, detail=f"Session handshake failed ({lb.status_code}).")
|
||||
raise HTTPException(status_code=502, detail=f"No se pudo establecer la sesión ({lb.status_code}).")
|
||||
|
||||
data = lb.json()
|
||||
state = data.get("state", "?")
|
||||
@@ -5236,7 +5236,7 @@ async def queue_status(queue_id: str, request: Request):
|
||||
daily budget at claim, since a multi-minute wait could have spent it elsewhere."""
|
||||
client_ip = request.client.host if request.client else "?"
|
||||
if not LOAD_BALANCER_URL:
|
||||
raise HTTPException(status_code=404, detail="Not found.")
|
||||
raise HTTPException(status_code=404, detail="No encontrado.")
|
||||
|
||||
tier, keys, set_cookie = auth.resolve_identity(request)
|
||||
tracked = LIMITER_ENABLED and limiter.budget_for(tier) is not None
|
||||
@@ -5252,7 +5252,7 @@ async def queue_status(queue_id: str, request: Request):
|
||||
lb = await http.get(url)
|
||||
except httpx.RequestError as exc:
|
||||
logger.warning("Load balancer unreachable: %r", exc)
|
||||
raise HTTPException(status_code=502, detail="Speech service unreachable.")
|
||||
raise HTTPException(status_code=502, detail="No se puede conectar con el servicio de voz.")
|
||||
logger.debug(
|
||||
"[EXT][LB] queue RESP status=%s len=%dB",
|
||||
lb.status_code, len(lb.content or b""),
|
||||
@@ -5269,7 +5269,7 @@ async def queue_status(queue_id: str, request: Request):
|
||||
|
||||
if lb.status_code != 200:
|
||||
logger.warning("Queue poll failed %s: %s", lb.status_code, lb.text[:300])
|
||||
raise HTTPException(status_code=502, detail=f"Queue poll failed ({lb.status_code}).")
|
||||
raise HTTPException(status_code=502, detail=f"No se pudo consultar la cola de espera ({lb.status_code}).")
|
||||
|
||||
data = lb.json()
|
||||
state = data.get("state", "?")
|
||||
@@ -5311,7 +5311,7 @@ async def queue_status(queue_id: str, request: Request):
|
||||
async def queue_leave(queue_id: str):
|
||||
"""Leave the queue from the explicit 'Leave queue' button (a real fetch)."""
|
||||
if not LOAD_BALANCER_URL:
|
||||
raise HTTPException(status_code=404, detail="Not found.")
|
||||
raise HTTPException(status_code=404, detail="No encontrado.")
|
||||
logger.debug("[EXT][LB] queue LEAVE (button) queueId=%s", queue_id[:16])
|
||||
await _lb_leave(queue_id)
|
||||
return {"ok": True}
|
||||
@@ -5322,7 +5322,7 @@ async def queue_end(request: Request):
|
||||
"""Leave the queue on teardown/tab-close (navigator.sendBeacon, which can only
|
||||
POST). Body: { queueId }. Best-effort; the LB reaps the ticket on TTL anyway."""
|
||||
if not LOAD_BALANCER_URL:
|
||||
raise HTTPException(status_code=404, detail="Not found.")
|
||||
raise HTTPException(status_code=404, detail="No encontrado.")
|
||||
qid = await _queue_id(request)
|
||||
if qid:
|
||||
logger.debug("[EXT][LB] queue LEAVE (teardown) queueId=%s", qid[:16])
|
||||
@@ -6495,7 +6495,7 @@ async def session_heartbeat(request: Request):
|
||||
budget is spent — the client then tears down."""
|
||||
client_ip = request.client.host if request.client else "?"
|
||||
if not LIMITER_ENABLED:
|
||||
raise HTTPException(status_code=404, detail="Not found.")
|
||||
raise HTTPException(status_code=404, detail="No encontrado.")
|
||||
sid = await _session_id(request)
|
||||
alive = bool(sid) and await asyncio.to_thread(limiter.heartbeat, sid)
|
||||
logger.debug(
|
||||
@@ -6511,7 +6511,7 @@ async def session_end(request: Request):
|
||||
chunk. Sent via navigator.sendBeacon, so it must succeed without a response."""
|
||||
client_ip = request.client.host if request.client else "?"
|
||||
if not LIMITER_ENABLED:
|
||||
raise HTTPException(status_code=404, detail="Not found.")
|
||||
raise HTTPException(status_code=404, detail="No encontrado.")
|
||||
sid = await _session_id(request)
|
||||
logger.debug(
|
||||
"[INTERNAL] /api/session/end client=%s sessionId=%s",
|
||||
|
||||
Reference in New Issue
Block a user