Spaces:
Running
Running
| import asyncio | |
| import aiohttp | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel | |
| from typing import List, Optional | |
| import uvicorn | |
| import os | |
| from core.matcher import PoetryMatcher | |
| app = FastAPI( | |
| title="名句匹配API", | |
| version="1.0.0", | |
| description="输入句子或关键词,从海量名句库中找到最契合的名句" | |
| ) | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| matcher = None | |
| class MatchRequest(BaseModel): | |
| query: str | |
| top_k: int = 5 | |
| class QuoteResponse(BaseModel): | |
| id: str | |
| text: str | |
| author: Optional[str] = None | |
| source: Optional[str] = None | |
| dynasty: Optional[str] = None | |
| type: Optional[str] = None | |
| score: float | |
| semantic_score: Optional[float] = None | |
| keyword_score: Optional[float] = None | |
| synonym_score: Optional[float] = None | |
| class MatchResponse(BaseModel): | |
| query: str | |
| results: List[QuoteResponse] | |
| async def self_ping(): | |
| await asyncio.sleep(60) | |
| port = os.getenv("PORT", "7860") | |
| while True: | |
| try: | |
| async with aiohttp.ClientSession() as session: | |
| async with session.get(f"http://localhost:{port}/health"): | |
| pass | |
| except: | |
| pass | |
| await asyncio.sleep(240) | |
| async def startup_event(): | |
| global matcher | |
| print("Loading matcher...") | |
| matcher = PoetryMatcher() | |
| print(f"Matcher loaded with {matcher.get_quotes_count()} quotes") | |
| asyncio.create_task(self_ping()) | |
| async def root(): | |
| return { | |
| "name": "quote-finder", | |
| "version": "1.0.0", | |
| "description": "名句匹配API", | |
| "endpoints": { | |
| "POST /match": "匹配名句", | |
| "GET /health": "健康检查" | |
| } | |
| } | |
| async def match_quotes(request: MatchRequest): | |
| if matcher is None: | |
| raise HTTPException(status_code=503, detail="Service not ready") | |
| try: | |
| results = matcher.match(request.query, request.top_k) | |
| # 确保每个结果都有id字段 | |
| formatted_results = [] | |
| for i, r in enumerate(results): | |
| if 'id' not in r: | |
| r['id'] = f'generated_{i}' | |
| formatted_results.append(QuoteResponse(**r)) | |
| return MatchResponse( | |
| query=request.query, | |
| results=formatted_results | |
| ) | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def health_check(): | |
| return { | |
| "status": "healthy", | |
| "quotes_count": matcher.get_quotes_count() if matcher else 0, | |
| "model": "moka-ai/m3e-base" | |
| } | |
| if __name__ == "__main__": | |
| port = int(os.getenv("PORT", "7860")) | |
| uvicorn.run(app, host="0.0.0.0", port=port) | |