fix: support photo client tool in debug preview
This commit is contained in:
@@ -13,7 +13,7 @@ import json
|
||||
from collections.abc import Coroutine
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, Request, WebSocket
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException, Request, WebSocket
|
||||
from loguru import logger
|
||||
from models import AssistantConfig, SignalingOffer
|
||||
from services.auth import require_admin, require_admin_websocket
|
||||
@@ -35,6 +35,7 @@ _active_connections = realtime_lifecycle.active_connections
|
||||
_pipeline_tasks = realtime_lifecycle.pipeline_tasks
|
||||
_connection_tasks = realtime_lifecycle.connection_tasks
|
||||
PIPELINE_CLOSE_GRACE_SECONDS = 10.0
|
||||
MAX_CLIENT_TOOL_NAME_LENGTH = 64
|
||||
|
||||
|
||||
def _start_pipeline_task(
|
||||
@@ -69,6 +70,53 @@ async def shutdown_active_sessions() -> None:
|
||||
)
|
||||
|
||||
|
||||
def _decode_client_tool_names(encoded: str | None) -> list[str] | None:
|
||||
"""Decode client capabilities carried outside the WebRTC SDP body.
|
||||
|
||||
Pipecat's browser transport replaces the body of a ``Request`` endpoint
|
||||
with its SDP offer and drops ``APIRequest.requestData``. Request headers are
|
||||
preserved, so they are the authoritative metadata channel for this path.
|
||||
``None`` means the header was absent and callers should use the legacy body.
|
||||
"""
|
||||
|
||||
if encoded is None:
|
||||
return None
|
||||
try:
|
||||
padding = "=" * (-len(encoded) % 4)
|
||||
value = json.loads(
|
||||
base64.urlsafe_b64decode(encoded + padding).decode("utf-8")
|
||||
)
|
||||
except (ValueError, UnicodeDecodeError, json.JSONDecodeError) as exc:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="客户端工具能力请求头格式无效",
|
||||
) from exc
|
||||
if not isinstance(value, list):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="客户端工具能力必须是函数名列表",
|
||||
)
|
||||
|
||||
names: list[str] = []
|
||||
seen: set[str] = set()
|
||||
for item in value:
|
||||
if not isinstance(item, str):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="客户端工具函数名必须是字符串",
|
||||
)
|
||||
name = item.strip()
|
||||
if not name or len(name) > MAX_CLIENT_TOOL_NAME_LENGTH:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="客户端工具函数名为空或过长",
|
||||
)
|
||||
if name not in seen:
|
||||
names.append(name)
|
||||
seen.add(name)
|
||||
return names
|
||||
|
||||
|
||||
@router.get("/api/webrtc/ice-servers", dependencies=[Depends(require_admin)])
|
||||
async def ice_servers():
|
||||
"""Browser fetches STUN/TURN config (with ephemeral TURN creds when configured)."""
|
||||
@@ -86,12 +134,22 @@ async def http_offer(request: Request, payload: dict = Body(...)):
|
||||
header_variables = json.loads(
|
||||
base64.urlsafe_b64decode(encoded_variables + padding).decode("utf-8")
|
||||
)
|
||||
header_client_tools = _decode_client_tool_names(
|
||||
request.headers.get("x-pipecat-client-tools")
|
||||
)
|
||||
client_tool_names = (
|
||||
header_client_tools
|
||||
if header_client_tools is not None
|
||||
else request_data.get("client_tool_names") or []
|
||||
)
|
||||
logger.debug(f"WebRTC 客户端工具上报: {client_tool_names or '<none>'}")
|
||||
offer_payload = {
|
||||
**payload,
|
||||
"assistant_id": request.headers.get("x-pipecat-assistant-id"),
|
||||
"dynamic_variables": header_variables,
|
||||
"client_tool_names": request_data.get("client_tool_names") or [],
|
||||
**request_data,
|
||||
# Keep header metadata authoritative when both paths are present.
|
||||
"client_tool_names": client_tool_names,
|
||||
}
|
||||
answer = await _handle_offer_payload(offer_payload, _http_peers)
|
||||
return answer
|
||||
|
||||
Reference in New Issue
Block a user