liumaolin
commited on
Commit
·
94c7b78
1
Parent(s):
a28f7e3
Enhance system management and audio capture services: implement `SystemStatusResponse` updates with detailed state tracking, add `audio_capture` service creation and lifecycle management, and refactor API `/system` routes for improved status and control handling.
Browse files
src/VoiceDialogue/api/app.py
CHANGED
|
@@ -8,7 +8,7 @@ from .core.config import AppConfig
|
|
| 8 |
from .core.lifespan import lifespan
|
| 9 |
from .middleware.logging import LoggingMiddleware
|
| 10 |
from .middleware.rate_limit import RateLimitMiddleware
|
| 11 |
-
from .routes import tts_routes, asr_routes
|
| 12 |
|
| 13 |
# 配置日志
|
| 14 |
logging.basicConfig(
|
|
@@ -56,7 +56,8 @@ def _register_routes(app: FastAPI):
|
|
| 56 |
v1_router = APIRouter(prefix="/api/v1")
|
| 57 |
v1_router.include_router(tts_routes.router, prefix="/tts", tags=["TTS模型管理"])
|
| 58 |
v1_router.include_router(asr_routes.router, prefix="/asr", tags=["ASR模型管理"])
|
| 59 |
-
|
|
|
|
| 60 |
app.include_router(v1_router)
|
| 61 |
|
| 62 |
# 根路径和健康检查
|
|
|
|
| 8 |
from .core.lifespan import lifespan
|
| 9 |
from .middleware.logging import LoggingMiddleware
|
| 10 |
from .middleware.rate_limit import RateLimitMiddleware
|
| 11 |
+
from .routes import tts_routes, asr_routes, system_routes
|
| 12 |
|
| 13 |
# 配置日志
|
| 14 |
logging.basicConfig(
|
|
|
|
| 56 |
v1_router = APIRouter(prefix="/api/v1")
|
| 57 |
v1_router.include_router(tts_routes.router, prefix="/tts", tags=["TTS模型管理"])
|
| 58 |
v1_router.include_router(asr_routes.router, prefix="/asr", tags=["ASR模型管理"])
|
| 59 |
+
v1_router.include_router(system_routes.router, prefix="/system", tags=["系统管理"])
|
| 60 |
+
|
| 61 |
app.include_router(v1_router)
|
| 62 |
|
| 63 |
# 根路径和健康检查
|
src/VoiceDialogue/api/core/service_factories.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
| 1 |
-
from typing import Any
|
| 2 |
-
|
| 3 |
from services.audio import EchoCancellingAudioCapture, TTSAudioGenerator, AudioStreamPlayer
|
| 4 |
from services.audio.audio_generator import BaseTTSConfig, tts_config_registry
|
| 5 |
from services.core.constants import (
|
|
@@ -127,6 +125,16 @@ def get_core_voice_service_definitions(system_language: str, tts_config: BaseTTS
|
|
| 127 |
]
|
| 128 |
|
| 129 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
def get_service_health_checkers() -> dict:
|
| 131 |
"""获取服务健康检查器映射"""
|
| 132 |
return {
|
|
|
|
|
|
|
|
|
|
| 1 |
from services.audio import EchoCancellingAudioCapture, TTSAudioGenerator, AudioStreamPlayer
|
| 2 |
from services.audio.audio_generator import BaseTTSConfig, tts_config_registry
|
| 3 |
from services.core.constants import (
|
|
|
|
| 125 |
]
|
| 126 |
|
| 127 |
|
| 128 |
+
def get_audio_capture_service_definition() -> ServiceDefinition:
|
| 129 |
+
"""获取音频捕获服务定义"""
|
| 130 |
+
return ServiceDefinition(
|
| 131 |
+
name="audio_capture",
|
| 132 |
+
factory=ServiceFactories.create_audio_capture,
|
| 133 |
+
dependencies=[],
|
| 134 |
+
health_check=lambda service: hasattr(service, 'is_ready') and service.is_ready
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
def get_service_health_checkers() -> dict:
|
| 139 |
"""获取服务健康检查器映射"""
|
| 140 |
return {
|
src/VoiceDialogue/api/routes/__init__.py
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
-
from . import tts_routes, asr_routes
|
| 2 |
|
| 3 |
-
__all__ = ["tts_routes", "asr_routes"]
|
|
|
|
| 1 |
+
from . import tts_routes, asr_routes, system_routes
|
| 2 |
|
| 3 |
+
__all__ = ["tts_routes", "asr_routes", "system_routes"]
|
src/VoiceDialogue/api/routes/system_routes.py
CHANGED
|
@@ -2,8 +2,9 @@ import asyncio
|
|
| 2 |
import logging
|
| 3 |
import time
|
| 4 |
|
| 5 |
-
from fastapi import APIRouter, HTTPException, BackgroundTasks
|
| 6 |
|
|
|
|
| 7 |
from ..schemas.system_schemas import (
|
| 8 |
SystemStatusResponse, SystemConfig,
|
| 9 |
SystemStartRequest, SystemResponse
|
|
@@ -21,42 +22,54 @@ _system_status = {
|
|
| 21 |
}
|
| 22 |
|
| 23 |
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
#
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
#
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
#
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
|
| 53 |
@router.post("/start", response_model=SystemResponse, summary="启动系统")
|
| 54 |
async def start_system(
|
| 55 |
request: SystemStartRequest,
|
|
|
|
| 56 |
background_tasks: BackgroundTasks
|
| 57 |
):
|
| 58 |
"""
|
| 59 |
-
启动语音对话系统
|
| 60 |
"""
|
| 61 |
try:
|
| 62 |
if _system_status["status"] in ["running", "starting"]:
|
|
@@ -72,7 +85,8 @@ async def start_system(
|
|
| 72 |
# 在后台启动系统
|
| 73 |
background_tasks.add_task(
|
| 74 |
_start_system_background,
|
| 75 |
-
request.config
|
|
|
|
| 76 |
)
|
| 77 |
|
| 78 |
return SystemResponse(
|
|
@@ -87,9 +101,9 @@ async def start_system(
|
|
| 87 |
|
| 88 |
|
| 89 |
@router.post("/stop", response_model=SystemResponse, summary="停止系统")
|
| 90 |
-
async def stop_system():
|
| 91 |
"""
|
| 92 |
-
停止语音对话系统
|
| 93 |
"""
|
| 94 |
try:
|
| 95 |
if _system_status["status"] == "stopped":
|
|
@@ -101,8 +115,29 @@ async def stop_system():
|
|
| 101 |
# 更新状态
|
| 102 |
_system_status["status"] = "stopping"
|
| 103 |
|
| 104 |
-
#
|
| 105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
|
| 107 |
_system_status["status"] = "stopped"
|
| 108 |
_system_status["start_time"] = None
|
|
@@ -122,6 +157,7 @@ async def stop_system():
|
|
| 122 |
@router.post("/restart", response_model=SystemResponse, summary="重启系统")
|
| 123 |
async def restart_system(
|
| 124 |
request: SystemStartRequest,
|
|
|
|
| 125 |
background_tasks: BackgroundTasks
|
| 126 |
):
|
| 127 |
"""
|
|
@@ -130,28 +166,41 @@ async def restart_system(
|
|
| 130 |
try:
|
| 131 |
# 先停止
|
| 132 |
if _system_status["status"] != "stopped":
|
| 133 |
-
await stop_system()
|
| 134 |
|
| 135 |
# 再启动
|
| 136 |
-
return await start_system(request, background_tasks)
|
| 137 |
|
| 138 |
except Exception as e:
|
| 139 |
logger.error(f"系统重启失败: {e}", exc_info=True)
|
| 140 |
raise HTTPException(status_code=500, detail=f"系统重启失败: {str(e)}")
|
| 141 |
|
| 142 |
|
| 143 |
-
async def _start_system_background(config: SystemConfig):
|
| 144 |
"""
|
| 145 |
-
后台启动系统的实际逻辑
|
| 146 |
"""
|
| 147 |
try:
|
| 148 |
logger.info("开始启动语音对话系统...")
|
| 149 |
|
| 150 |
-
#
|
| 151 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
|
| 153 |
-
|
| 154 |
-
# 类似于原来main.py中的launch_system函数
|
| 155 |
|
| 156 |
_system_status["status"] = "running"
|
| 157 |
_system_status["start_time"] = time.time()
|
|
|
|
| 2 |
import logging
|
| 3 |
import time
|
| 4 |
|
| 5 |
+
from fastapi import APIRouter, HTTPException, BackgroundTasks, Request
|
| 6 |
|
| 7 |
+
from ..core.service_factories import get_audio_capture_service_definition
|
| 8 |
from ..schemas.system_schemas import (
|
| 9 |
SystemStatusResponse, SystemConfig,
|
| 10 |
SystemStartRequest, SystemResponse
|
|
|
|
| 22 |
}
|
| 23 |
|
| 24 |
|
| 25 |
+
@router.get("/status", response_model=SystemStatusResponse, summary="获取系统状态")
|
| 26 |
+
async def get_system_status(request: Request):
|
| 27 |
+
"""
|
| 28 |
+
获取系统整体状态,包含服务运行状态
|
| 29 |
+
"""
|
| 30 |
+
try:
|
| 31 |
+
# 从应用状态获取服务管理器
|
| 32 |
+
service_manager = getattr(request.app.state, "service_manager", None)
|
| 33 |
+
system_running = getattr(request.app.state, "system_running", False)
|
| 34 |
+
|
| 35 |
+
# 获取服务状态
|
| 36 |
+
services_status = {}
|
| 37 |
+
if service_manager:
|
| 38 |
+
services_status = service_manager.get_service_status()
|
| 39 |
+
|
| 40 |
+
# 检查audio_capture服务状态
|
| 41 |
+
audio_capture_running = False
|
| 42 |
+
audio_capture_ready = False
|
| 43 |
+
if service_manager and service_manager.is_service_running("audio_capture"):
|
| 44 |
+
audio_capture_service = service_manager.get_service("audio_capture")
|
| 45 |
+
if audio_capture_service:
|
| 46 |
+
audio_capture_running = True
|
| 47 |
+
audio_capture_ready = audio_capture_service.is_ready
|
| 48 |
+
|
| 49 |
+
return SystemStatusResponse(
|
| 50 |
+
status=_system_status["status"],
|
| 51 |
+
uptime=time.time() - _system_status["start_time"] if _system_status["start_time"] else None,
|
| 52 |
+
active_sessions=_system_status["active_sessions"],
|
| 53 |
+
system_running=system_running,
|
| 54 |
+
services_count=services_status.get("total_services", 0),
|
| 55 |
+
audio_capture_running=audio_capture_running,
|
| 56 |
+
audio_capture_ready=audio_capture_ready,
|
| 57 |
+
services_details=services_status
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
except Exception as e:
|
| 61 |
+
logger.error(f"获取系统状态失败: {e}", exc_info=True)
|
| 62 |
+
raise HTTPException(status_code=500, detail=f"获取系统状态失败: {str(e)}")
|
| 63 |
|
| 64 |
|
| 65 |
@router.post("/start", response_model=SystemResponse, summary="启动系统")
|
| 66 |
async def start_system(
|
| 67 |
request: SystemStartRequest,
|
| 68 |
+
fastapi_request: Request,
|
| 69 |
background_tasks: BackgroundTasks
|
| 70 |
):
|
| 71 |
"""
|
| 72 |
+
启动语音对话系统 - 创建audio_capture服务
|
| 73 |
"""
|
| 74 |
try:
|
| 75 |
if _system_status["status"] in ["running", "starting"]:
|
|
|
|
| 85 |
# 在后台启动系统
|
| 86 |
background_tasks.add_task(
|
| 87 |
_start_system_background,
|
| 88 |
+
request.config,
|
| 89 |
+
fastapi_request
|
| 90 |
)
|
| 91 |
|
| 92 |
return SystemResponse(
|
|
|
|
| 101 |
|
| 102 |
|
| 103 |
@router.post("/stop", response_model=SystemResponse, summary="停止系统")
|
| 104 |
+
async def stop_system(request: Request):
|
| 105 |
"""
|
| 106 |
+
停止语音对话系统 - 停止audio_capture服务
|
| 107 |
"""
|
| 108 |
try:
|
| 109 |
if _system_status["status"] == "stopped":
|
|
|
|
| 115 |
# 更新状态
|
| 116 |
_system_status["status"] = "stopping"
|
| 117 |
|
| 118 |
+
# 获取服务管理器
|
| 119 |
+
service_manager = getattr(request.app.state, "service_manager", None)
|
| 120 |
+
if service_manager:
|
| 121 |
+
# 停止audio_capture服务
|
| 122 |
+
if service_manager.is_service_running("audio_capture"):
|
| 123 |
+
audio_capture_service = service_manager.get_service("audio_capture")
|
| 124 |
+
if audio_capture_service:
|
| 125 |
+
try:
|
| 126 |
+
audio_capture_service.stop()
|
| 127 |
+
logger.info("音频捕获服务已停止")
|
| 128 |
+
|
| 129 |
+
# 等待服务停止
|
| 130 |
+
timeout = 5
|
| 131 |
+
start_time = time.time()
|
| 132 |
+
while audio_capture_service.is_alive() and (time.time() - start_time) < timeout:
|
| 133 |
+
await asyncio.sleep(0.1)
|
| 134 |
+
|
| 135 |
+
# 从服务管理器中移除
|
| 136 |
+
if "audio_capture" in service_manager.services:
|
| 137 |
+
del service_manager.services["audio_capture"]
|
| 138 |
+
|
| 139 |
+
except Exception as e:
|
| 140 |
+
logger.error(f"停止音频捕获服务时发生错误: {e}", exc_info=True)
|
| 141 |
|
| 142 |
_system_status["status"] = "stopped"
|
| 143 |
_system_status["start_time"] = None
|
|
|
|
| 157 |
@router.post("/restart", response_model=SystemResponse, summary="重启系统")
|
| 158 |
async def restart_system(
|
| 159 |
request: SystemStartRequest,
|
| 160 |
+
fastapi_request: Request,
|
| 161 |
background_tasks: BackgroundTasks
|
| 162 |
):
|
| 163 |
"""
|
|
|
|
| 166 |
try:
|
| 167 |
# 先停止
|
| 168 |
if _system_status["status"] != "stopped":
|
| 169 |
+
await stop_system(fastapi_request)
|
| 170 |
|
| 171 |
# 再启动
|
| 172 |
+
return await start_system(request, fastapi_request, background_tasks)
|
| 173 |
|
| 174 |
except Exception as e:
|
| 175 |
logger.error(f"系统重启失败: {e}", exc_info=True)
|
| 176 |
raise HTTPException(status_code=500, detail=f"系统重启失败: {str(e)}")
|
| 177 |
|
| 178 |
|
| 179 |
+
async def _start_system_background(config: SystemConfig, request: Request):
|
| 180 |
"""
|
| 181 |
+
后台启动系统的实际逻辑 - 创建并启动audio_capture服务
|
| 182 |
"""
|
| 183 |
try:
|
| 184 |
logger.info("开始启动语音对话系统...")
|
| 185 |
|
| 186 |
+
# 获取服务管理器
|
| 187 |
+
service_manager = getattr(request.app.state, "service_manager", None)
|
| 188 |
+
if not service_manager:
|
| 189 |
+
raise RuntimeError("服务管理器未初始化")
|
| 190 |
+
|
| 191 |
+
# 检查audio_capture服务是否已存在
|
| 192 |
+
if service_manager.is_service_running("audio_capture"):
|
| 193 |
+
logger.info("音频捕获服务已在运行")
|
| 194 |
+
else:
|
| 195 |
+
# 创建audio_capture服务定义
|
| 196 |
+
audio_capture_def = get_audio_capture_service_definition()
|
| 197 |
+
|
| 198 |
+
# 启动audio_capture服务
|
| 199 |
+
success = service_manager.start_service(audio_capture_def)
|
| 200 |
+
if not success:
|
| 201 |
+
raise RuntimeError("音频捕获服务启动失败")
|
| 202 |
|
| 203 |
+
logger.info("音频捕获服务启动成功")
|
|
|
|
| 204 |
|
| 205 |
_system_status["status"] = "running"
|
| 206 |
_system_status["start_time"] = time.time()
|
src/VoiceDialogue/api/schemas/system_schemas.py
CHANGED
|
@@ -1,22 +1,25 @@
|
|
| 1 |
-
from typing import Optional, Literal
|
| 2 |
|
| 3 |
from pydantic import BaseModel, Field
|
| 4 |
|
| 5 |
|
| 6 |
class SystemStatusResponse(BaseModel):
|
| 7 |
-
"""
|
| 8 |
status: Literal['running', 'stopped', 'starting', 'stopping'] = Field(..., description="系统状态")
|
| 9 |
uptime: Optional[float] = Field(None, description="运行时间(秒)")
|
| 10 |
active_sessions: int = Field(default=0, description="活跃会话数")
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
|
| 14 |
class SystemConfig(BaseModel):
|
| 15 |
"""系统配置"""
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
llm_model: Literal['7B', '14B'] = Field(default='14B', description="语言模型规模")
|
| 20 |
|
| 21 |
|
| 22 |
class SystemStartRequest(BaseModel):
|
|
@@ -25,7 +28,6 @@ class SystemStartRequest(BaseModel):
|
|
| 25 |
|
| 26 |
|
| 27 |
class SystemResponse(BaseModel):
|
| 28 |
-
"""
|
| 29 |
success: bool = Field(..., description="操作是否成功")
|
| 30 |
message: str = Field(..., description="响应消息")
|
| 31 |
-
status: Optional[SystemStatusResponse] = Field(None, description="系统状态")
|
|
|
|
| 1 |
+
from typing import Optional, Literal, Dict, Any
|
| 2 |
|
| 3 |
from pydantic import BaseModel, Field
|
| 4 |
|
| 5 |
|
| 6 |
class SystemStatusResponse(BaseModel):
|
| 7 |
+
"""系统状态响应"""
|
| 8 |
status: Literal['running', 'stopped', 'starting', 'stopping'] = Field(..., description="系统状态")
|
| 9 |
uptime: Optional[float] = Field(None, description="运行时间(秒)")
|
| 10 |
active_sessions: int = Field(default=0, description="活跃会话数")
|
| 11 |
+
system_running: bool = Field(default=False, description="系统是否运行中")
|
| 12 |
+
services_count: int = Field(default=0, description="运行中的服务数量")
|
| 13 |
+
audio_capture_running: bool = Field(default=False, description="音频捕获服务是否运行")
|
| 14 |
+
audio_capture_ready: bool = Field(default=False, description="音频捕获服务是否就绪")
|
| 15 |
+
services_details: Optional[Dict[str, Any]] = Field(None, description="服务详细状态信息")
|
| 16 |
|
| 17 |
|
| 18 |
class SystemConfig(BaseModel):
|
| 19 |
"""系统配置"""
|
| 20 |
+
language: Optional[str] = Field(default="zh", description="系统语言")
|
| 21 |
+
sample_rate: Optional[int] = Field(default=16000, description="音频采样率")
|
| 22 |
+
enable_logging: Optional[bool] = Field(default=True, description="是否启用日志")
|
|
|
|
| 23 |
|
| 24 |
|
| 25 |
class SystemStartRequest(BaseModel):
|
|
|
|
| 28 |
|
| 29 |
|
| 30 |
class SystemResponse(BaseModel):
|
| 31 |
+
"""系统操作响应"""
|
| 32 |
success: bool = Field(..., description="操作是否成功")
|
| 33 |
message: str = Field(..., description="响应消息")
|
|
|