- Introduce new Xfyun ASR and TTS services, enabling integration with iFlytek's voice recognition and synthesis capabilities. - Update AssistantConfig model to include interface types for STT and TTS. - Enhance credential testing to validate Xfyun credentials. - Modify service factory to create Xfyun services based on configuration. - Update README with new configuration details for Xfyun integration. - Add new frontend components for visualizing audio streams and managing user interactions.
76 lines
3.2 KiB
Python
76 lines
3.2 KiB
Python
"""assistant_id → 运行时配置(把真 key 在服务端组装好)。
|
|
|
|
浏览器只传 assistant_id;真 key 在这里从 provider_credentials 取出注入。
|
|
助手按 FK(*_credential_id)引用凭证;取不到则回退该 type 默认凭证,再回退 .env。
|
|
"""
|
|
|
|
import config
|
|
from db.models import Assistant, ProviderCredential
|
|
from models import AssistantConfig
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
async def _default_credential(
|
|
session: AsyncSession, type_: str
|
|
) -> ProviderCredential | None:
|
|
"""该 type 的默认凭证(is_default 优先,否则按 id 取第一条)。"""
|
|
stmt = (
|
|
select(ProviderCredential)
|
|
.where(ProviderCredential.type == type_)
|
|
.order_by(ProviderCredential.is_default.desc(), ProviderCredential.id.asc())
|
|
.limit(1)
|
|
)
|
|
return (await session.execute(stmt)).scalar_one_or_none()
|
|
|
|
|
|
async def _resolve(
|
|
session: AsyncSession, cred_id: str | None, type_: str
|
|
) -> ProviderCredential | None:
|
|
"""按 FK id 取凭证;id 为空或失效 → 回退该 type 默认。"""
|
|
if cred_id:
|
|
cred = await session.get(ProviderCredential, cred_id)
|
|
if cred:
|
|
return cred
|
|
return await _default_credential(session, type_)
|
|
|
|
|
|
async def resolve_runtime_config(
|
|
session: AsyncSession, assistant_id: str
|
|
) -> AssistantConfig:
|
|
"""加载助手 + 解析凭证,产出可直接交给管线的运行时配置(含真 key)。"""
|
|
assistant = await session.get(Assistant, assistant_id)
|
|
if assistant is None:
|
|
raise ValueError(f"助手不存在: {assistant_id}")
|
|
|
|
llm = await _resolve(session, assistant.llm_credential_id, "LLM")
|
|
stt = await _resolve(session, assistant.asr_credential_id, "ASR")
|
|
tts = await _resolve(session, assistant.tts_credential_id, "TTS")
|
|
realtime = await _resolve(session, assistant.realtime_credential_id, "Realtime")
|
|
|
|
return AssistantConfig(
|
|
name=assistant.name,
|
|
greeting=assistant.greeting,
|
|
# prompt 现在是真列;外部类型由其平台编排,这里给个兜底
|
|
prompt=assistant.prompt or "你是一个有帮助的助手。",
|
|
runtimeMode=assistant.runtime_mode, # type: ignore[arg-type]
|
|
enableInterrupt=assistant.enable_interrupt,
|
|
# 模型/音色:凭证的模型ID优先
|
|
model=(llm.model_id if llm else ""),
|
|
asr=(stt.model_id if stt else ""),
|
|
tts_model=(tts.model_id if tts else ""),
|
|
voice=(tts.voice if tts else ""),
|
|
stt_language=(stt.language if stt else ""),
|
|
tts_speed=(tts.speed if tts else 1.0),
|
|
stt_interface_type=(stt.interface_type if stt else "openai"),
|
|
tts_interface_type=(tts.interface_type if tts else "openai"),
|
|
realtimeModel=(realtime.model_id if realtime else ""),
|
|
# 运行时连接信息(真 key + url):凭证优先,否则 .env 兜底
|
|
llm_api_key=(llm.api_key if llm else config.LLM_API_KEY),
|
|
llm_base_url=(llm.api_url if llm else config.LLM_BASE_URL),
|
|
stt_api_key=(stt.api_key if stt else config.STT_API_KEY),
|
|
stt_base_url=(stt.api_url if stt else config.STT_BASE_URL),
|
|
tts_api_key=(tts.api_key if tts else config.TTS_API_KEY),
|
|
tts_base_url=(tts.api_url if tts else config.TTS_BASE_URL),
|
|
)
|