Implement unified story generation flow
This commit is contained in:
@@ -9,6 +9,7 @@ from app.services.adapters.asr import openai as _asr_openai_adapter # noqa: F40
|
||||
from app.services.adapters.base import AdapterConfig, BaseAdapter
|
||||
|
||||
# Image adapters
|
||||
from app.services.adapters.image import antigravity as _image_antigravity_adapter # noqa: F401
|
||||
from app.services.adapters.image import cqtai as _image_cqtai_adapter # noqa: F401
|
||||
from app.services.adapters.registry import AdapterRegistry
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from io import BytesIO
|
||||
|
||||
from fastapi import HTTPException
|
||||
from openai import AsyncOpenAI
|
||||
from openai import APIConnectionError, APIStatusError, APITimeoutError, AsyncOpenAI
|
||||
|
||||
from app.core.logging import get_logger
|
||||
from app.services.adapters.asr.models import TranscriptionOutput
|
||||
@@ -15,6 +16,14 @@ from app.services.adapters.registry import AdapterRegistry
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
def _mask_openai_error(message: str) -> str:
|
||||
"""Avoid leaking bearer tokens while keeping ASR smoke failures actionable."""
|
||||
|
||||
sanitized = message.replace("\n", " ").strip()
|
||||
sanitized = re.sub(r"Bearer\s+[A-Za-z0-9._-]+", "Bearer ***", sanitized)
|
||||
return re.sub(r"sk-[A-Za-z0-9_-]+", "sk-***", sanitized)
|
||||
|
||||
|
||||
@AdapterRegistry.register("asr", "openai_asr")
|
||||
class OpenAIASRAdapter(BaseAdapter[TranscriptionOutput]):
|
||||
"""Transcribe uploaded voice turn audio with OpenAI audio transcription."""
|
||||
@@ -37,7 +46,11 @@ class OpenAIASRAdapter(BaseAdapter[TranscriptionOutput]):
|
||||
detail="OPENAI_API_KEY 未配置,无法使用 OpenAI 语音转写。",
|
||||
)
|
||||
|
||||
client = AsyncOpenAI(api_key=self.config.api_key)
|
||||
client = AsyncOpenAI(
|
||||
api_key=self.config.api_key,
|
||||
base_url=self.config.api_base or None,
|
||||
timeout=self.config.timeout_ms / 1000,
|
||||
)
|
||||
audio_file = BytesIO(audio_bytes)
|
||||
audio_file.name = file_name or "voice-turn.webm"
|
||||
|
||||
@@ -51,11 +64,29 @@ class OpenAIASRAdapter(BaseAdapter[TranscriptionOutput]):
|
||||
language=language,
|
||||
prompt=prompt,
|
||||
)
|
||||
except APIStatusError as exc:
|
||||
detail = _mask_openai_error(getattr(exc, "message", str(exc)))
|
||||
logger.warning(
|
||||
"openai_asr_failed",
|
||||
status_code=exc.status_code,
|
||||
error=detail,
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail=f"OpenAI ASR 调用失败(HTTP {exc.status_code}):{detail}",
|
||||
) from exc
|
||||
except (APITimeoutError, APIConnectionError) as exc:
|
||||
detail = _mask_openai_error(str(exc))
|
||||
logger.warning("openai_asr_failed", error=detail)
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail=f"OpenAI ASR 网络连接失败:{detail}",
|
||||
) from exc
|
||||
except Exception as exc:
|
||||
logger.warning("openai_asr_failed", error=str(exc))
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail="语音转写服务暂时不可用,请稍后重试。",
|
||||
detail=f"OpenAI ASR 调用异常:{_mask_openai_error(str(exc))}",
|
||||
) from exc
|
||||
|
||||
transcript_text = (getattr(response, "text", "") or "").strip()
|
||||
|
||||
@@ -126,6 +126,11 @@ class MiniMaxTTSAdapter(BaseAdapter[bytes]):
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@property
|
||||
def estimated_cost(self) -> float:
|
||||
"""预估每次短文本语音合成成本 (USD)。"""
|
||||
return 0.01
|
||||
|
||||
@retry(
|
||||
stop=stop_after_attempt(3),
|
||||
wait=wait_exponential(multiplier=1, min=1, max=10),
|
||||
|
||||
Reference in New Issue
Block a user