"""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 ""), voice="", # 音色无独立列,留空 → service_factory 回退 .env TTS_VOICE 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), )