Add VOICE_CONFIG env var to select the voice pipeline config file.
Defaults to config/voice.json; relative paths resolve from project root. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -8,3 +8,6 @@ APP_ID=683ea1bc86197e19f71fc1ae
|
|||||||
DELETE_SESSION_URL=http://127.0.0.1:3030/api/core/chat/delHistory?chatId={chatId}&appId={appId}
|
DELETE_SESSION_URL=http://127.0.0.1:3030/api/core/chat/delHistory?chatId={chatId}&appId={appId}
|
||||||
DELETE_CHAT_URL=http://127.0.0.1:3030/api/core/chat/item/delete?contentId={contentId}&chatId={chatId}&appId={appId}
|
DELETE_CHAT_URL=http://127.0.0.1:3030/api/core/chat/item/delete?contentId={contentId}&chatId={chatId}&appId={appId}
|
||||||
GET_CHAT_RECORDS_URL=http://127.0.0.1:3030/api/core/chat/getPaginationRecords
|
GET_CHAT_RECORDS_URL=http://127.0.0.1:3030/api/core/chat/getPaginationRecords
|
||||||
|
|
||||||
|
# Voice demo (Pipecat /ws-product). Relative to project root, or an absolute path.
|
||||||
|
VOICE_CONFIG=config/voice.json
|
||||||
|
|||||||
@@ -1,11 +1,30 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
||||||
DEFAULT_VOICE_CONFIG = PROJECT_ROOT / "config" / "voice.json"
|
DEFAULT_VOICE_CONFIG_REL = "config/voice.json"
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_voice_config_path() -> Path:
|
||||||
|
"""Return the voice config path from VOICE_CONFIG or the default."""
|
||||||
|
configured = os.getenv("VOICE_CONFIG", DEFAULT_VOICE_CONFIG_REL).strip()
|
||||||
|
if not configured:
|
||||||
|
configured = DEFAULT_VOICE_CONFIG_REL
|
||||||
|
path = Path(configured)
|
||||||
|
if not path.is_absolute():
|
||||||
|
path = PROJECT_ROOT / path
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_VOICE_CONFIG = resolve_voice_config_path()
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@@ -143,7 +162,7 @@ class EngineConfig:
|
|||||||
|
|
||||||
|
|
||||||
def load_config(path: str | Path | None = None) -> EngineConfig:
|
def load_config(path: str | Path | None = None) -> EngineConfig:
|
||||||
config_path = Path(path) if path is not None else DEFAULT_VOICE_CONFIG
|
config_path = Path(path) if path is not None else resolve_voice_config_path()
|
||||||
if not config_path.is_absolute():
|
if not config_path.is_absolute():
|
||||||
config_path = PROJECT_ROOT / config_path
|
config_path = PROJECT_ROOT / config_path
|
||||||
data = json.loads(config_path.read_text(encoding="utf-8"))
|
data = json.loads(config_path.read_text(encoding="utf-8"))
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
|||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from .config import DEFAULT_VOICE_CONFIG, EngineConfig, load_config
|
from .config import EngineConfig, load_config, resolve_voice_config_path
|
||||||
from .pipeline import run_product_voice_pipeline
|
from .pipeline import run_product_voice_pipeline
|
||||||
|
|
||||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
||||||
@@ -19,7 +19,12 @@ router = APIRouter(tags=["voice"])
|
|||||||
|
|
||||||
@lru_cache(maxsize=1)
|
@lru_cache(maxsize=1)
|
||||||
def get_voice_config() -> EngineConfig:
|
def get_voice_config() -> EngineConfig:
|
||||||
return load_config(DEFAULT_VOICE_CONFIG)
|
return load_config()
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=1)
|
||||||
|
def get_voice_config_path() -> Path:
|
||||||
|
return resolve_voice_config_path()
|
||||||
|
|
||||||
|
|
||||||
def _normalize_mount_path(path: str) -> str:
|
def _normalize_mount_path(path: str) -> str:
|
||||||
@@ -39,6 +44,7 @@ async def voice_health() -> dict[str, object]:
|
|||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
"status": "healthy",
|
"status": "healthy",
|
||||||
|
"config": str(get_voice_config_path()),
|
||||||
"protocols": {
|
"protocols": {
|
||||||
"/ws-product": "va.ws.v1.json_base64",
|
"/ws-product": "va.ws.v1.json_base64",
|
||||||
},
|
},
|
||||||
@@ -62,12 +68,14 @@ async def product_websocket_endpoint(websocket: WebSocket) -> None:
|
|||||||
|
|
||||||
def register_voice(app: FastAPI) -> None:
|
def register_voice(app: FastAPI) -> None:
|
||||||
"""Mount voice websocket routes and optional browser demo static files."""
|
"""Mount voice websocket routes and optional browser demo static files."""
|
||||||
if not DEFAULT_VOICE_CONFIG.exists():
|
voice_config_path = get_voice_config_path()
|
||||||
logger.warning(f"Voice config not found at {DEFAULT_VOICE_CONFIG}; voice demo disabled")
|
if not voice_config_path.exists():
|
||||||
|
logger.warning(f"Voice config not found at {voice_config_path}; voice demo disabled")
|
||||||
return
|
return
|
||||||
|
|
||||||
config = get_voice_config()
|
config = get_voice_config()
|
||||||
app.include_router(router)
|
app.include_router(router)
|
||||||
|
logger.info(f"Voice config loaded from {voice_config_path}")
|
||||||
|
|
||||||
if config.server.cors_origins:
|
if config.server.cors_origins:
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
|
|||||||
Reference in New Issue
Block a user