from fastapi import APIRouter, HTTPException, Response, Depends from typing import Optional, Dict, Any from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from app.services.translation import TranslationService from app.services.tts import tts_service from app.services.preference import UserPreferenceService from app.services.credit import CreditService from app.core.security import decode_token from app.api.v1.deps import get_current_user_id router = APIRouter() class TranslateRequest(BaseModel): text: str target_lang: str source_lang: Optional[str] = "auto" context: Optional[str] = None class ReplyRequest(BaseModel): inquiry: str tone: str = "professional" count: int = 3 context: Optional[Dict[str, Any]] = None class ExtractRequest(BaseModel): text: str extract_type: str = "auto" @router.post("") async def translate_text( data: TranslateRequest, user_id: str = Depends(get_current_user_id), db: AsyncSession = Depends(get_db), ): service = TranslationService() result = await service.translate( text=data.text, target_lang=data.target_lang, source_lang=data.source_lang, context=data.context, user_id=user_id, ) credit_svc = CreditService(db) char_count = len(data.text) await credit_svc.deduct( user_id, "translate", metadata={"chars": char_count, "target_lang": data.target_lang}, ) return result @router.post("/reply") async def generate_reply( data: ReplyRequest, user_id: str = Depends(get_current_user_id), db: AsyncSession = Depends(get_db), ): pref_service = UserPreferenceService(db) credit_svc = CreditService(db) ok, balance = await credit_svc.deduct(user_id, "reply_suggest") if not ok: raise HTTPException( status_code=402, detail=f"次数不足 (剩余 {balance:.1f}, 需要 2)" ) pref_context = await pref_service.get_preference_context(user_id, "reply") service = TranslationService() results = await service.generate_reply( inquiry=data.inquiry, context=data.context, tone=data.tone, count=data.count, preference_context=pref_context, ) return {"suggestions": results, "inquiry": data.inquiry, "count": len(results)} @router.post("/extract") async def extract_info( data: ExtractRequest, user_id: str = Depends(get_current_user_id), db: AsyncSession = Depends(get_db), ): credit_svc = CreditService(db) ok, balance = await credit_svc.deduct(user_id, "info_extract") if not ok: raise HTTPException( status_code=402, detail=f"次数不足 (剩余 {balance:.1f}, 需要 1)" ) service = TranslationService() result = await service.extract_info(data.text, data.extract_type) return {"extracted": result, "type": data.extract_type} class TTSRequest(BaseModel): text: str lang: str = "en" rate: str = "0%" pitch: str = "0Hz" @router.post("/tts") async def text_to_speech( data: TTSRequest, user_id: str = Depends(get_current_user_id), ): audio = await tts_service.synthesize(data.text, data.lang, data.rate, data.pitch) if not audio: raise HTTPException(status_code=501, detail="TTS not available (edge-tts not installed or synthesis failed)") return Response(content=audio, media_type="audio/mpeg", headers={ "Content-Disposition": f'attachment; filename="tts-{data.lang}.mp3"', }) @router.get("/tts") async def text_to_speech_get( text: str = "", lang: str = "en", user_id: str = Depends(get_current_user_id), ): if not text.strip(): raise HTTPException(status_code=400, detail="Text is required") audio = await tts_service.synthesize(text, lang) if not audio: raise HTTPException(status_code=501, detail="TTS not available") return Response(content=audio, media_type="audio/mpeg") @router.post("/feedback") async def feedback( data: dict, user_id: str = Depends(get_current_user_id), ): from app.ai.trade_corpus import TradeCorpus corpus = TradeCorpus() entry_id = data.get("entry_id") rating = data.get("rating") if entry_id and rating: await corpus.rate_entry(entry_id, rating) return {"status": "ok"} public_router = APIRouter(tags=["translate-public"]) @public_router.post("/translate") async def public_translate(data: TranslateRequest): service = TranslationService() result = await service.translate( text=data.text, target_lang=data.target_lang, source_lang=data.source_lang, context=data.context, user_id=None, ) return result @public_router.post("/extract") async def public_extract(data: ExtractRequest): service = TranslationService() result = await service.extract_info(data.text, data.extract_type) return {"extracted": result, "type": data.extract_type}