CoolFace
Apppublic

sonygod/flash2

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
app.py493 linesDownload Raw Back to root
1from fastapi import FastAPI, HTTPException, UploadFile, File, Form2from fastapi.middleware.cors import CORSMiddleware3from fastapi.responses import HTMLResponse4from pydantic import BaseModel5from fastapi import FastAPI, HTTPException, Request6from asyncio import TimeoutError7import asyncio8from typing import Optional9import requests10import uvicorn11import shutil12import datetime  # Add this import13import logging14from logging.handlers import RotatingFileHandler15import time16from typing import List, Dict, Optional17import json18import os19import psutil20import sys21from typing import Dict22import tempfile23import re24import random25import aiohttp 26 27from fastapi.templating import Jinja2Templates28from fastapi.staticfiles import StaticFiles29import ollama30app = FastAPI()31 32# Add USER_AGENTS constant33USER_AGENTS = [34    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",35    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",36    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"37]38# Configure logging39# Configure detailed logging40logging.basicConfig(41    level=logging.INFO,42    format='%(asctime)s - %(levelname)s - %(message)s'43)44logger = logging.getLogger(__name__)45# Request counter46request_counter = {47    "analyze": 0,48    "compareAnalyze": 0,49    "total": 050}51 52# Add CORS middleware53# Add CORS middleware with both HTTP and HTTPS54app.add_middleware(55    CORSMiddleware,56    allow_origins=[57        "http://*",58        "https://*"59    ],60    allow_credentials=True,61    allow_methods=["*"],62    allow_headers=["*"],63)64 65class AskRequest(BaseModel):66    prompt: str67    model: str = "GEMINI"68 69 70@app.get("/")71async def health_check():72    return {73        "health": "ok",74        "timestamp": datetime.datetime.now().isoformat(),75        "service": "AI API Forwarding Service",76        "version": "1.0"77    }78        79 80@app.post("/ask")81async def forward_ask(request: AskRequest):82    request_counter["total"] += 183    try:84        response = requests.post(85            "http://s5.serv00.com:9081/ask",86            headers={'Content-Type': 'application/json'},87            json=request.dict()88        )89        return response.json()90    except Exception as e:91        raise HTTPException(status_code=500, detail=str(e))92 93@app.post("/analyze")94async def forward_analyze(image: UploadFile = File(...), model: str = Form(...)):95    request_counter["analyze"] += 196    request_counter["total"] += 197    try:98        files = {'image': (image.filename, image.file, image.content_type)}99        data = {'model': model}100        response = requests.post(101            "http://s5.serv00.com:9081/analyze",102            files=files,103            data=data104        )105        return response.json()106    except Exception as e:107        raise HTTPException(status_code=500, detail=str(e))108 109@app.post("/compareAnalyze")110async def forward_compare_analyze(image: UploadFile = File(...)):111    request_counter["compareAnalyze"] += 1112    request_counter["total"] += 1113    try:114        files = {'image': (image.filename, image.file, image.content_type)}115        response = requests.post(116            "http://s5.serv00.com:9081/compareAnalyze",117            files=files118        )119        return response.json()120    except Exception as e:121        raise HTTPException(status_code=500, detail=str(e))122 123@app.get("/status")124async def forward_status():125    start_time = time.time()126    logger.info(f"Received status request at {datetime.datetime.now()}")127    logger.info(f"Current request counter: {request_counter}")128    129    try:130        logger.info("Attempting to contact upstream server...")131        response = requests.get("http://s5.serv00.com:9081/status")132        elapsed_time = time.time() - start_time133        134        logger.info(f"Upstream server responded in {elapsed_time:.2f} seconds")135        logger.info(f"Response status code: {response.status_code}")136        logger.info(f"Response content: {response.text[:200]}...")137        138        return response.json()139    except Exception as e:140        logger.error(f"Error occurred: {str(e)}")141        logger.error(f"Error type: {type(e).__name__}")142        return {143            "status": "running",144            "requests": request_counter,145            "error": str(e),146            "timestamp": datetime.datetime.now().isoformat()147        }148 149@app.get("/check", response_class=HTMLResponse)150async def forward_check():151    try:152        response = requests.get("http://s5.serv00.com:9081/check")153        return response.text154    except Exception as e:155        raise HTTPException(status_code=500, detail=str(e))156 157 158 159# Add new models160class Translation(BaseModel):161    translation: str162    type: str163 164class Phrase(BaseModel):165    phrase: str166    translation: str167 168class Word(BaseModel):169    word: str170    translations: List[dict]  # Changed to accept dictionary format171    phrases: List[Phrase] = []  # Made optional with default empty list172    level: str = ""  # Add level field with default empty string173 174# Add global word map175word_map: Dict[str, Word] = {}176 177def get_level_from_filename(filename: str) -> str:178    # Extract level from filenames like "1-初中-顺序.json"179    match = re.match(r'\d+-(.+?)-顺序\.json', filename)180    return match.group(1) if match else "unknown"181# Add initialization function182def init_word_map():183    current_dir = os.path.dirname(os.path.abspath(__file__))184    json_dir = os.path.join(current_dir, "json")185    stats = {186        "total_words": 0,187        "total_files": 0,188        "file_stats": {}189    }190    191    try:192        for filename in os.listdir(json_dir):193            if filename.endswith('.json'):194                try:195                    level = get_level_from_filename(filename)196                    with open(os.path.join(json_dir, filename), 'r', encoding='utf-8') as f:197                        words = json.load(f)198                        word_count = len(words)199                        stats["total_words"] += word_count200                        stats["total_files"] += 1201                        stats["file_stats"][filename] = word_count202                        for word_data in words:203                            # Convert legacy format to new format204                            if 'translations' not in word_data:205                                word_data['translations'] = [{206                                    'translation': word_data.get('translation', ''),207                                    'type': word_data.get('type', '')208                                }]209                            if 'phrases' not in word_data:210                                word_data['phrases'] = []211                                212                            word_data['level'] = level213                            word = Word(**word_data)214                            word_map[word.word.lower()] = word215                    logger.info(f"Loaded {filename}: {word_count} words")216                except Exception as e:217                    logger.error(f"Error loading {filename}: {str(e)}")218                    continue219        220        logger.info(f"Dictionary initialization complete:")221        logger.info(f"Total files processed: {stats['total_files']}")222        logger.info(f"Total words loaded: {stats['total_words']}")223        return stats224    except Exception as e:225        logger.error(f"Fatal error in init_word_map: {str(e)}")226        return stats227 228 229# Add cache configuration230# Update cache file location231CACHE_DIR = os.path.join(tempfile.gettempdir(), "flash_api_cache")232CACHE_FILE = os.path.join(CACHE_DIR, "ai_translation_cache.json")233ai_cache: Dict[str, dict] = {}234 235# Load cache on startup236def save_cache():237    try:238        # Create cache directory if it doesn't exist239        os.makedirs(CACHE_DIR, exist_ok=True)240        241        with open(CACHE_FILE, 'w', encoding='utf-8') as f:242            json.dump(ai_cache, f, ensure_ascii=False, indent=2)243            logger.info(f"Cache saved to: {CACHE_FILE}")244    except PermissionError as pe:245        logger.error(f"Permission denied writing to cache: {pe}")246    except Exception as e:247        logger.error(f"Error saving cache: {e}")248 249def load_cache():250    global ai_cache251    try:252        if os.path.exists(CACHE_FILE):253            with open(CACHE_FILE, 'r', encoding='utf-8') as f:254                ai_cache = json.load(f)255                logger.info(f"Loaded {len(ai_cache)} cached translations from: {CACHE_FILE}")256    except Exception as e:257        logger.error(f"Error loading cache: {e}")258        ai_cache = {}259 260# Add translate endpoint261@app.get("/translate/{word}")262async def translate_word(word: str):263    start_time = time.time()264    logger.info(f"Translation request received for word: {word}")265    266    try:267        word = word.lower().strip()268        logger.debug(f"Processed word: {word}")269        270        # Check word map271        # if word in word_map:272        #     logger.info(f"Word found in map: {word}")273        #     word_data = word_map[word]274        #     logger.debug(f"Word data: {word_data}")275            276        #     # Format all translations277        #     translations_text = []278        #     for trans in word_data.translations:279        #         translation = trans['translation']280        #         type_info = trans['type']281        #         translations_text.append(f"({type_info}) {translation}")282            283        #     # Join translations with separators284        #     translations_combined = " | ".join(translations_text)285        #     logger.debug(f"Combined translations: {translations_combined}")286            287        #     # Handle examples288        #     examples = []289        #     if word_data.phrases:290        #         examples = [f"{p.phrase}: {p.translation}" for p in word_data.phrases[:3]]291        #         logger.debug(f"Examples found: {examples}")292            293        #     # Build response with proper formatting294        #     formatted_response = f"{word} [{word_data.level}]: {translations_combined}"295        #     if examples:296        #         formatted_response += f"\n\n例句:\n{chr(10).join(examples)}"297            298        #     elapsed = time.time() - start_time299        #     logger.info(f"Word map translation completed in {elapsed:.2f}s")300            301        #     return {302        #         "status": 200,303        #         "data": {304        #             "response": formatted_response,305        #             "word": word,306        #             "level": word_data.level,  # Add level info here307        #             "translations": word_data.translations,308        #             "examples": examples309        #         }310        #     }311        312        # Check AI cache313        # if word in ai_cache:314        #     logger.info(f"Word found in AI cache: {word}")315        #     elapsed = time.time() - start_time316        #     logger.info(f"Cache hit completed in {elapsed:.2f}s")317        #     return ai_cache[word]318        319        # Fallback to AI translation320        logger.info("Word not found in cache, calling AI API")321        # Fallback to AI translation322        logger.info("Word not found in map, falling back to AI translation")323        try:324            request = AskRequest(325                prompt=f'''翻译以下英文326            {word}327            每行一个 格式参考,不要任何md格式,分别要有音标,单词属性(名词,动词,形容词),中文翻译,英文解析,例句,近义词,反义词,词性328            格式参考:329            hello:/həˈləʊ/| n. vt. int.|你好,问候语,|例句:Hello, how are you? 你好,你好吗?|近义词:hi, hey, |反义词:sick, bad.''',330                model="GEMINI"331            )332            logger.debug(f"AI Request: {request}")333            334            result = await forward_ask(request)335            336             # Cache the result337            #ai_cache[word] = result338           #save_cache()339            logger.debug(f"AI Response: {result}")340            341            elapsed = time.time() - start_time342            logger.info(f"AI translation completed in {elapsed:.2f}s")343            return result344            345        except Exception as e:346            logger.error(f"AI translation error: {str(e)}", exc_info=True)347            raise HTTPException(status_code=500, detail=str(e))348            349    except Exception as e:350        logger.error(f"Translation error: {str(e)}", exc_info=True)351        raise HTTPException(status_code=500, detail=str(e))352 353# Add cleanup functions354def cleanup_temp_files():355    try:356        # Clean temp directory357        temp_dir = os.path.join(tempfile.gettempdir(), "flash_api_cache")358        if os.path.exists(temp_dir):359            shutil.rmtree(temp_dir)360            logger.info(f"Cleaned up temp directory: {temp_dir}")361    except Exception as e:362        logger.error(f"Error cleaning temp files: {e}")363 364def cleanup_cache():365    global ai_cache366    ai_cache = {}367    logger.info("Cache cleared")368 369 370# Initialize word map on startup371@app.on_event("startup")372async def startup_event():373    #init_word_map()// waste of memory374    #load_cache()375    cleanup_temp_files()376    cleanup_cache()377    logger.info(f"Memory usage after init: {get_memory_usage()}")378 379@app.on_event("shutdown")380async def shutdown_event():381    # Cleanup on shutdown382    cleanup_temp_files()383    cleanup_cache()384    logger.info("Application shutdown cleanup complete")385def get_memory_usage():386    process = psutil.Process()387    memory_info = process.memory_info()388    389    # Get system memory info390    system = psutil.virtual_memory()391    392    return {393        "process": {394            "rss": f"{memory_info.rss / 1024 / 1024:.2f} MB",395            "rss_percent": f"{memory_info.rss / system.total * 100:.2f}%",396            "vms": f"{memory_info.vms / 1024 / 1024:.2f} MB",397            "vms_percent": f"{memory_info.vms / system.total * 100:.2f}%"398        },399        "system": {400            "total": f"{system.total / 1024 / 1024:.2f} MB",401            "available": f"{system.available / 1024 / 1024:.2f} MB",402            "used_percent": f"{system.percent:.2f}%"403        },404        "word_map": {405            "entries": len(word_map),406            "memory": f"{sys.getsizeof(word_map) / 1024 / 1024:.2f} MB",407            "memory_percent": f"{sys.getsizeof(word_map) / system.total * 100:.4f}%"408        }409    }410 411@app.get("/memory")412async def memory_status():413    return get_memory_usage()414 415# Add new endpoint416@app.get("/proxy")417async def proxy_request(url: str, request: Request):418    try:419        # Get random user agent420        user_agent = random.choice(USER_AGENTS)421        422        #print url423        424        logger.info(f"Proxy request received for: {url}")425        426        # Prepare headers427        headers = {428            'User-Agent': user_agent,429            'Accept': 'application/json, text/plain, */*',430            'Accept-Language': 'en-US,en;q=0.9',431            'Origin': 'https://www.youtube.com',432            'Referer': 'https://www.youtube.com/',433            'Sec-Fetch-Dest': 'empty',434            'Sec-Fetch-Mode': 'cors',435            'Sec-Fetch-Site': 'same-site',436            'Connection': 'keep-alive'437        }438 439        # Set timeout440        timeout = aiohttp.ClientTimeout(total=10)  # 10 seconds timeout441        442        async with aiohttp.ClientSession(timeout=timeout) as session:443            async with session.get(url, headers=headers) as response:444                # Check HTTP status445                if response.status != 200:446                    raise HTTPException(447                        status_code=response.status,448                        detail=f"HTTP error: {response.status}"449                    )450                451                # Parse JSON response452                data = await response.json()453                454                #print data's length455                456                logger.info(f"Received youtube subtile data: {len(data)} bytes")457                458                459                # Validate data format460                if not data or 'events' not in data:461                    raise HTTPException(462                        status_code=400,463                        detail="Invalid subtitle data format"464                    )465                466                return data467 468    except TimeoutError:469        raise HTTPException(status_code=408, detail="Request timeout")470    except Exception as e:471        logger.error(f"Proxy error: {str(e)}")472        raise HTTPException(status_code=500, detail=str(e))473 474templates = Jinja2Templates(directory="templates")475 476@app.get("/chat")477async def chat_page(request: Request):478    return templates.TemplateResponse("chat.html", {"request": request})479 480@app.post("/testchat")481async def test_chat(data: dict):482    try:483        response = ollama.chat(model='gemma:2b', messages=[484            {485                'role': 'user',486                'content': data['prompt']487            }488        ])489        return {"response": response['message']['content']}490    except Exception as e:491        raise HTTPException(status_code=500, detail=str(e))492if __name__ == "__main__":493    uvicorn.run(app, host="0.0.0.0", port=7860)