import json from pathlib import Path from typing import Any import httpx from app.config import get_settings from app.services.mock_data_service import mock_ai_cleaning_result from app.services.qa_parser import normalize_ai_item from app.utils.json_validator import validate_ai_qa_items PROMPT_PATH = Path(__file__).resolve().parents[1] / "prompts" / "qa_cleaning_prompt.md" class AICleaner: def __init__(self) -> None: self.settings = get_settings() def _prompt(self) -> str: return PROMPT_PATH.read_text(encoding="utf-8") async def clean_transcript(self, session_id: int, transcript: str) -> list[dict[str, Any]]: if not self.settings.openai_configured: return [normalize_ai_item(item) for item in mock_ai_cleaning_result(session_id)] payload = { "model": self.settings.model_name, "messages": [ {"role": "system", "content": self._prompt()}, { "role": "user", "content": f"场次 ID: {session_id}\n\n以下是转写稿:\n{transcript}", }, ], "temperature": 0.1, } base_url = (self.settings.openai_base_url or "https://api.openai.com/v1").rstrip("/") headers = {"Authorization": f"Bearer {self.settings.openai_api_key}"} async with httpx.AsyncClient(timeout=60) as client: response = await client.post(f"{base_url}/chat/completions", headers=headers, json=payload) response.raise_for_status() content = response.json()["choices"][0]["message"]["content"] try: parsed = json.loads(content) except json.JSONDecodeError as exc: raise ValueError(f"AI 返回非法 JSON: {exc}") from exc return [normalize_ai_item(item) for item in validate_ai_qa_items(parsed)]