liumaolin commited on
Commit
757f3be
·
1 Parent(s): 51a672c

Refactor ASR routes and services: implement instance creation tracking with background task support, enhance `get_supported_languages` with current ASR language, and clean up unused schemas and routes for simplified management.

Browse files
src/VoiceDialogue/api/core/lifespan.py CHANGED
@@ -43,7 +43,8 @@ class LifespanManager:
43
  self._update_app_state({
44
  "service_manager": self.service_manager,
45
  "system_running": True,
46
- "system_language": system_language
 
47
  })
48
 
49
  # 记录启动信息
 
43
  self._update_app_state({
44
  "service_manager": self.service_manager,
45
  "system_running": True,
46
+ "system_language": system_language,
47
+ "current_asr_language": system_language,
48
  })
49
 
50
  # 记录启动信息
src/VoiceDialogue/api/core/service_factories.py CHANGED
@@ -135,6 +135,15 @@ def get_audio_capture_service_definition() -> ServiceDefinition:
135
  )
136
 
137
 
 
 
 
 
 
 
 
 
 
138
  def get_service_health_checkers() -> dict:
139
  """获取服务健康检查器映射"""
140
  return {
 
135
  )
136
 
137
 
138
+ def get_asr_worker_service_definition(language: str) -> ServiceDefinition:
139
+ """获取ASR服务定义"""
140
+ return ServiceDefinition(
141
+ name="asr_worker",
142
+ factory=lambda: ServiceFactories.create_asr_worker(language),
143
+ dependencies=[]
144
+ )
145
+
146
+
147
  def get_service_health_checkers() -> dict:
148
  """获取服务健康检查器映射"""
149
  return {
src/VoiceDialogue/api/routes/asr_routes.py CHANGED
@@ -1,19 +1,27 @@
1
  import logging
2
 
3
- from fastapi import APIRouter, HTTPException
4
 
5
  from services.speech.asr import asr_manager
 
6
  from ..schemas.asr_schemas import (
7
- SupportedLanguagesResponse, ASRInstanceRequest, ASRInstanceResponse, LanguageMappingRequest,
8
- LanguageMappingResponse, ASRValidationRequest, ASRValidationResponse, CleanupResponse
9
  )
10
 
11
  logger = logging.getLogger(__name__)
12
  router = APIRouter()
13
 
 
 
 
 
 
 
 
 
14
 
15
  @router.get("/languages", response_model=SupportedLanguagesResponse, summary="获取支持的识别语言")
16
- async def get_supported_languages():
17
  """
18
  获取系统支持的语音识别语言列表,包括语言映射和可用引擎
19
  """
@@ -21,11 +29,13 @@ async def get_supported_languages():
21
  available_languages = asr_manager.get_available_languages()
22
  language_mappings = asr_manager._language_to_asr_mapping
23
  asr_engines = list(asr_manager.list_registered_asr().keys())
 
24
 
25
  return SupportedLanguagesResponse(
26
  languages=available_languages,
27
  language_mappings=language_mappings,
28
- asr_engines=asr_engines
 
29
  )
30
  except Exception as e:
31
  logger.error(f"获取支持语言列表失败: {e}", exc_info=True)
@@ -33,161 +43,118 @@ async def get_supported_languages():
33
 
34
 
35
  @router.post("/instance/create", response_model=ASRInstanceResponse, summary="创建ASR实例")
36
- async def create_asr_instance(request: ASRInstanceRequest):
 
 
 
 
37
  """
38
  根据指定语言创建新的ASR实例
39
  """
40
  try:
41
- # 获取最优的ASR引擎
42
- asr_type = asr_manager._get_asr_type_for_language(request.language)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
- # 创建实例
45
- instance = asr_manager.create_asr(request.language)
46
 
47
  return ASRInstanceResponse(
48
  success=True,
49
- message=f"成功创建ASR实例",
50
  language=request.language,
51
  asr_type=asr_type,
52
  instance_id=f"{asr_type}_{request.language}"
53
  )
 
54
  except ValueError as e:
55
  logger.warning(f"创建ASR实例失败 - 参数错误: {e}")
 
 
56
  raise HTTPException(status_code=400, detail=str(e))
57
  except Exception as e:
58
  logger.error(f"创建ASR实例失败: {e}", exc_info=True)
 
 
59
  raise HTTPException(status_code=500, detail=f"创建ASR实例失败: {str(e)}")
60
 
61
 
62
- @router.post("/instance/get-or-create", response_model=ASRInstanceResponse, summary="获取或创建ASR实例")
63
- async def get_or_create_asr_instance(request: ASRInstanceRequest):
64
  """
65
- 获取现有的ASR实例,如果不存在则创建新实例(单例模式)
66
  """
67
  try:
 
 
68
  # 获取最优的ASR引擎
69
  asr_type = asr_manager._get_asr_type_for_language(request.language)
 
70
 
71
- # 获取或创建实例
72
- instance = asr_manager.get_or_create_asr(request.language)
 
 
73
 
74
- return ASRInstanceResponse(
75
- success=True,
76
- message=f"成功获取ASR实例",
77
- language=request.language,
78
- asr_type=asr_type,
79
- instance_id=f"{asr_type}_{request.language}"
80
- )
81
- except ValueError as e:
82
- logger.warning(f"获取ASR实例失败 - 参数错误: {e}")
83
- raise HTTPException(status_code=400, detail=str(e))
84
- except Exception as e:
85
- logger.error(f"获取ASR实例失败: {e}", exc_info=True)
86
- raise HTTPException(status_code=500, detail=f"获取ASR实例失败: {str(e)}")
87
 
 
 
 
 
88
 
89
- @router.post("/mapping", response_model=LanguageMappingResponse, summary="配置语言映射")
90
- async def set_language_mapping(request: LanguageMappingRequest):
91
- """
92
- 设置特定语言使用的ASR引擎
93
- """
94
- try:
95
- asr_manager.set_language_mapping(request.language, request.asr_type)
96
 
97
- return LanguageMappingResponse(
98
- success=True,
99
- message=f"成功设置语言映射: {request.language} -> {request.asr_type}",
100
- updated_mapping=asr_manager._language_to_asr_mapping.copy()
101
- )
102
- except ValueError as e:
103
- logger.warning(f"设置语言映射失败 - 参数错误: {e}")
104
- raise HTTPException(status_code=400, detail=str(e))
105
- except Exception as e:
106
- logger.error(f"设置语言映射失败: {e}", exc_info=True)
107
- raise HTTPException(status_code=500, detail=f"设置语言映射失败: {str(e)}")
108
 
 
 
 
109
 
110
- @router.post("/validate", response_model=ASRValidationResponse, summary="验证语言支持")
111
- async def validate_language_support(request: ASRValidationRequest):
112
- """
113
- 验证指定语言是否被支持,并返回相关信息
114
- """
115
- try:
116
- is_supported = asr_manager.validate_language_support(request.language)
117
- optimal_asr = asr_manager.get_optimal_asr_for_language(request.language)
118
-
119
- # 查找支持该语言的所有ASR引擎
120
- available_asrs = []
121
- supported_langs = asr_manager.get_supported_languages()
122
- for asr_key, languages in supported_langs.items():
123
- if request.language in languages:
124
- available_asrs.append(asr_key)
125
-
126
- return ASRValidationResponse(
127
- language=request.language,
128
- is_supported=is_supported,
129
- optimal_asr=optimal_asr,
130
- available_asrs=available_asrs
131
- )
132
- except Exception as e:
133
- logger.error(f"验证语言支持失败: {e}", exc_info=True)
134
- raise HTTPException(status_code=500, detail=f"验证语言支持失败: {str(e)}")
135
-
136
-
137
- @router.get("/validate/{language}", response_model=ASRValidationResponse, summary="验证语言支持(GET)")
138
- async def validate_language_support_get(language: str):
139
- """
140
- 通过GET方法验证指定语言是否被支持
141
- """
142
- request = ASRValidationRequest(language=language)
143
- return await validate_language_support(request)
144
-
145
-
146
- @router.delete("/cleanup", response_model=CleanupResponse, summary="清理ASR实例")
147
- async def cleanup_asr_instances():
148
- """
149
- 清理所有活动的ASR实例,释放资源
150
- """
151
- try:
152
- # 记录清理前的实例数量
153
- stats = asr_manager.get_asr_statistics()
154
- instances_count = stats['active_instances_count']
155
-
156
- # 执行清理
157
- asr_manager.cleanup()
158
-
159
- return CleanupResponse(
160
- success=True,
161
- message="成功清理所有ASR实例",
162
- cleared_instances_count=instances_count
163
- )
164
- except Exception as e:
165
- logger.error(f"清理ASR实例失败: {e}", exc_info=True)
166
- raise HTTPException(status_code=500, detail=f"清理ASR实例失败: {str(e)}")
167
 
 
168
 
169
- @router.get("/health", summary="ASR服务健康检查")
170
- async def asr_health_check():
171
- """
172
- ASR服务的健康检查接口
173
- """
174
- try:
175
- stats = asr_manager.get_asr_statistics()
176
-
177
- # 检查是否有已注册的ASR引擎
178
- is_healthy = stats['registered_asr_count'] > 0
179
-
180
- return {
181
- "healthy": is_healthy,
182
- "message": "ASR服务正常" if is_healthy else "没有可用的ASR引擎",
183
- "registered_engines": stats['registered_asr_count'],
184
- "active_instances": stats['active_instances_count'],
185
- "supported_languages": len(stats['supported_languages'])
186
- }
187
  except Exception as e:
188
- logger.error(f"ASR健康检查失败: {e}", exc_info=True)
189
- return {
190
- "healthy": False,
191
- "message": f"ASR服务异常: {str(e)}",
192
- "error": str(e)
193
- }
 
1
  import logging
2
 
3
+ from fastapi import APIRouter, HTTPException, Request, BackgroundTasks
4
 
5
  from services.speech.asr import asr_manager
6
+ from ..core.service_factories import get_asr_worker_service_definition
7
  from ..schemas.asr_schemas import (
8
+ SupportedLanguagesResponse, ASRInstanceRequest, ASRInstanceResponse
 
9
  )
10
 
11
  logger = logging.getLogger(__name__)
12
  router = APIRouter()
13
 
14
+ _asr_creation_status = {
15
+ "status": "idle", # idle, creating, completed, failed
16
+ "current_language": None,
17
+ "current_asr_type": None,
18
+ "instance_id": None,
19
+ "message": None
20
+ }
21
+
22
 
23
  @router.get("/languages", response_model=SupportedLanguagesResponse, summary="获取支持的识别语言")
24
+ async def get_supported_languages(fastapi_request: Request):
25
  """
26
  获取系统支持的语音识别语言列表,包括语言映射和可用引擎
27
  """
 
29
  available_languages = asr_manager.get_available_languages()
30
  language_mappings = asr_manager._language_to_asr_mapping
31
  asr_engines = list(asr_manager.list_registered_asr().keys())
32
+ current_asr_language = getattr(fastapi_request.state, "current_asr_language", None)
33
 
34
  return SupportedLanguagesResponse(
35
  languages=available_languages,
36
  language_mappings=language_mappings,
37
+ asr_engines=asr_engines,
38
+ current_asr_language=current_asr_language
39
  )
40
  except Exception as e:
41
  logger.error(f"获取支持语言列表失败: {e}", exc_info=True)
 
43
 
44
 
45
  @router.post("/instance/create", response_model=ASRInstanceResponse, summary="创建ASR实例")
46
+ async def create_asr_instance(
47
+ request: ASRInstanceRequest,
48
+ fastapi_request: Request,
49
+ background_tasks: BackgroundTasks
50
+ ):
51
  """
52
  根据指定语言创建新的ASR实例
53
  """
54
  try:
55
+ # 检查是否正在创建实例
56
+ if _asr_creation_status["status"] == "creating":
57
+ return ASRInstanceResponse(
58
+ success=False,
59
+ message="ASR实例正在创建中,请稍后重试",
60
+ language=request.language,
61
+ asr_type=None,
62
+ instance_id=None
63
+ )
64
+
65
+ # 检查当前语言是否已存在且相同
66
+ current_asr_language = getattr(fastapi_request.state, "current_asr_language", None)
67
+ if current_asr_language and current_asr_language == request.language:
68
+ asr_type = asr_manager._get_asr_type_for_language(request.language)
69
+ return ASRInstanceResponse(
70
+ success=True,
71
+ message=f"ASR实例已存在,语言: {request.language}",
72
+ language=request.language,
73
+ asr_type=asr_type,
74
+ instance_id=f"{asr_type}_{request.language}"
75
+ )
76
+
77
+ # 更新状态为创建中
78
+ _asr_creation_status["status"] = "creating"
79
+ _asr_creation_status["current_language"] = request.language
80
+ _asr_creation_status["message"] = "正在创建ASR实例..."
81
+
82
+ # 在后台执行创建任务
83
+ background_tasks.add_task(
84
+ _create_asr_instance_background,
85
+ request,
86
+ fastapi_request
87
+ )
88
 
89
+ # 获取预计使用的ASR类型
90
+ asr_type = asr_manager._get_asr_type_for_language(request.language)
91
 
92
  return ASRInstanceResponse(
93
  success=True,
94
+ message="ASR实例创建请求已提交,正在后台创建...",
95
  language=request.language,
96
  asr_type=asr_type,
97
  instance_id=f"{asr_type}_{request.language}"
98
  )
99
+
100
  except ValueError as e:
101
  logger.warning(f"创建ASR实例失败 - 参数错误: {e}")
102
+ _asr_creation_status["status"] = "failed"
103
+ _asr_creation_status["message"] = str(e)
104
  raise HTTPException(status_code=400, detail=str(e))
105
  except Exception as e:
106
  logger.error(f"创建ASR实例失败: {e}", exc_info=True)
107
+ _asr_creation_status["status"] = "failed"
108
+ _asr_creation_status["message"] = str(e)
109
  raise HTTPException(status_code=500, detail=f"创建ASR实例失败: {str(e)}")
110
 
111
 
112
+ async def _create_asr_instance_background(request: ASRInstanceRequest, fastapi_request: Request):
 
113
  """
114
+ 后台创建ASR实例的实际逻辑
115
  """
116
  try:
117
+ logger.info(f"开始创建ASR实例,语言: {request.language}")
118
+
119
  # 获取最优的ASR引擎
120
  asr_type = asr_manager._get_asr_type_for_language(request.language)
121
+ _asr_creation_status["current_asr_type"] = asr_type
122
 
123
+ # 获取服务管理器
124
+ service_manager = getattr(fastapi_request.app.state, "service_manager", None)
125
+ if not service_manager:
126
+ raise RuntimeError("服务管理器未初始化")
127
 
128
+ # 检查是否需要重新创建服务
129
+ current_asr_language = getattr(fastapi_request.state, "current_asr_language", None)
130
+ if current_asr_language and current_asr_language != request.language:
131
+ logger.info(f"请求语言({request.language})与当前语言({current_asr_language})不同,需要重新创建服务")
 
 
 
 
 
 
 
 
 
132
 
133
+ # 停止现有的ASR服务
134
+ if service_manager.is_service_running("asr_worker"):
135
+ service_manager._stop_single_service("asr_worker")
136
+ logger.info("已停止现有ASR服务")
137
 
138
+ # 启动新的ASR服务
139
+ asr_worker_def = get_asr_worker_service_definition(request.language)
140
+ success = service_manager.start_service(asr_worker_def)
141
+ if not success:
142
+ raise RuntimeError("ASR服务启动失败")
 
 
143
 
144
+ # 更新请求状态中的当前语言
145
+ fastapi_request.state.current_asr_language = request.language
 
 
 
 
 
 
 
 
 
146
 
147
+ # 生成实例ID
148
+ instance_id = f"{asr_type}_{request.language}"
149
+ _asr_creation_status["instance_id"] = instance_id
150
 
151
+ # 更新状态为完成
152
+ _asr_creation_status["status"] = "completed"
153
+ _asr_creation_status["message"] = f"成功创建ASR实例: {instance_id}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
 
155
+ logger.info(f"ASR实例创建成功: {instance_id}")
156
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  except Exception as e:
158
+ logger.error(f"后台创建ASR实例失败: {e}", exc_info=True)
159
+ _asr_creation_status["status"] = "failed"
160
+ _asr_creation_status["message"] = str(e)
 
 
 
src/VoiceDialogue/api/schemas/asr_schemas.py CHANGED
@@ -1,30 +1,16 @@
1
  from typing import Literal, List, Dict, Optional
 
2
  from pydantic import BaseModel, Field
3
 
4
 
5
  class SupportedLanguagesResponse(BaseModel):
6
  """支持的语言响应模式"""
7
  languages: List[str] = Field(..., description="支持的语言列表")
 
8
  language_mappings: Dict[str, str] = Field(..., description="语言到ASR引擎的映射")
9
  asr_engines: List[str] = Field(..., description="可用的ASR引擎列表")
10
 
11
 
12
- class ASRRegistryResponse(BaseModel):
13
- """ASR注册表响应模式"""
14
- registered_asr_types: List[str] = Field(..., description="已注册的ASR类型")
15
- supported_languages_by_engine: Dict[str, List[str]] = Field(..., description="各引擎支持的语言")
16
- total_registered_count: int = Field(..., description="注册的ASR引擎总数")
17
-
18
-
19
- class ASRStatisticsResponse(BaseModel):
20
- """ASR统计信息响应模式"""
21
- registered_asr_count: int = Field(..., description="已注册的ASR引擎数量")
22
- active_instances_count: int = Field(..., description="活动实例数量")
23
- supported_languages: List[str] = Field(..., description="支持的语言列表")
24
- language_mappings: Dict[str, str] = Field(..., description="语言映射配置")
25
- registered_asr_types: List[str] = Field(..., description="已注册的ASR类型")
26
-
27
-
28
  class ASRInstanceRequest(BaseModel):
29
  """ASR实例请求模式"""
30
  language: Literal["zh", "en", "auto"] = Field(..., description="目标语言")
@@ -37,36 +23,3 @@ class ASRInstanceResponse(BaseModel):
37
  language: str = Field(..., description="语言类型")
38
  asr_type: str = Field(..., description="使用的ASR引擎类型")
39
  instance_id: Optional[str] = Field(None, description="实例标识符")
40
-
41
-
42
- class LanguageMappingRequest(BaseModel):
43
- """语言映射配置请求模式"""
44
- language: str = Field(..., description="语言代码")
45
- asr_type: str = Field(..., description="ASR引擎类型")
46
-
47
-
48
- class LanguageMappingResponse(BaseModel):
49
- """语言映射配置响应模式"""
50
- success: bool = Field(..., description="操作是否成功")
51
- message: str = Field(..., description="操作结果消息")
52
- updated_mapping: Dict[str, str] = Field(..., description="更新后的映射关系")
53
-
54
-
55
- class ASRValidationRequest(BaseModel):
56
- """ASR语言验证请求模式"""
57
- language: str = Field(..., description="要验证的语言代码")
58
-
59
-
60
- class ASRValidationResponse(BaseModel):
61
- """ASR语言验证响应模式"""
62
- language: str = Field(..., description="语言代码")
63
- is_supported: bool = Field(..., description="是否支持")
64
- optimal_asr: Optional[str] = Field(None, description="最优ASR引擎")
65
- available_asrs: List[str] = Field(default_factory=list, description="支持该语言的ASR引擎列表")
66
-
67
-
68
- class CleanupResponse(BaseModel):
69
- """清理操作响应模式"""
70
- success: bool = Field(..., description="清理是否成功")
71
- message: str = Field(..., description="清理结果消息")
72
- cleared_instances_count: int = Field(..., description="清理的实例数量")
 
1
  from typing import Literal, List, Dict, Optional
2
+
3
  from pydantic import BaseModel, Field
4
 
5
 
6
  class SupportedLanguagesResponse(BaseModel):
7
  """支持的语言响应模式"""
8
  languages: List[str] = Field(..., description="支持的语言列表")
9
+ current_asr_language: str = Field(..., description="当前使用的ASR语言")
10
  language_mappings: Dict[str, str] = Field(..., description="语言到ASR引擎的映射")
11
  asr_engines: List[str] = Field(..., description="可用的ASR引擎列表")
12
 
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  class ASRInstanceRequest(BaseModel):
15
  """ASR实例请求模式"""
16
  language: Literal["zh", "en", "auto"] = Field(..., description="目标语言")
 
23
  language: str = Field(..., description="语言类型")
24
  asr_type: str = Field(..., description="使用的ASR引擎类型")
25
  instance_id: Optional[str] = Field(None, description="实例标识符")