Spaces:
Runtime error
Runtime error
| import os | |
| import shutil | |
| import hashlib | |
| from fastapi import FastAPI, Form | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.staticfiles import StaticFiles | |
| from fastapi.responses import JSONResponse | |
| import google.generativeai as genai | |
| from gradio_client import Client | |
| import uvicorn | |
| app = FastAPI() | |
| # 💡 1. 開放跨網域連線 (CORS) | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # 💡 2. 準備靜態檔案資料夾 | |
| if not os.path.exists("static"): | |
| os.makedirs("static") | |
| app.mount("/static", StaticFiles(directory="static"), name="static") | |
| # 💡 3. API Keys & Models 初始化 (對接 2026 最新 Gemini 3.5 Flash) | |
| GOOGLE_API_KEY = os.getenv('GEMINI_KEY', '請填入您的GEMINI_KEY') | |
| genai.configure(api_key=GOOGLE_API_KEY) | |
| MODEL_NAME = 'gemini-3.5-flash' | |
| model = genai.GenerativeModel(MODEL_NAME) | |
| # 💡 4. 初始化原語會的翻譯與 TTS 伺服器 | |
| tts_client = Client("https://ai-labs.ilrdf.org.tw/hnang-kari-ai-asi-sluhay/") | |
| mt_client = Client("https://ai-labs.ilrdf.org.tw/kari-seejiq-tnpusu-ai-hmjil/") | |
| TRIBE_CONFIG = { | |
| "阿美": {"mt": "阿美"}, "泰雅": {"mt": "泰雅"}, "排灣": {"mt": "排灣"}, "布農": {"mt": "布農"}, | |
| "卑南": {"mt": "卑南"}, "魯凱": {"mt": "魯凱"}, "鄒": {"mt": "鄒"}, "賽夏": {"mt": "賽夏"}, | |
| "雅美": {"mt": "雅美"}, "邵": {"mt": "邵"}, "噶瑪蘭": {"mt": "噶瑪蘭"}, "太魯閣": {"mt": "太魯閣"}, | |
| "撒奇萊雅": {"mt": "撒奇萊雅"}, "賽德克": {"mt": "賽德克"}, "拉阿魯哇": {"mt": "拉阿魯哇"}, "卡那卡那富": {"mt": "卡那卡那富"} | |
| } | |
| def get_clean_value(res): | |
| if isinstance(res, dict) and 'value' in res: return res['value'] | |
| if isinstance(res, list) and len(res) > 0: return res[0] | |
| return res | |
| def format_native_text(text, tribe): | |
| if not text: return text | |
| text = text.strip() | |
| if tribe in ["賽夏", "噶瑪蘭", "泰雅"]: return text[0].lower() + text[1:] | |
| else: return text[0].upper() + text[1:] | |
| def read_root(): | |
| return {"status": "ILRDF Avatar Text Brain is Running Successfully!", "model": MODEL_NAME} | |
| async def avatar_chat( | |
| tribe: str = Form(...), | |
| text: str = Form(...) # 👈 接收前端秒傳過來的中文文字 | |
| ): | |
| try: | |
| config = TRIBE_CONFIG.get(tribe) | |
| if not config: | |
| return JSONResponse({"error": f"不支援的族語: {tribe}"}, status_code=400) | |
| zh_in = text.strip() | |
| # --- 步驟 A: AI 大腦生成對話 (Gemini 3.5 Flash) --- | |
| prompt = f"你現在是與我對話的{tribe}族朋友。請用中文聊天。回覆規則:1.口吻生活化親切。2.不要說教解釋。3.字數一定要少(15字內),限一個短句。\n\n使用者說:{zh_in}" | |
| response = model.generate_content(prompt) | |
| ai_zh = response.text.strip() | |
| # --- 步驟 B: 翻譯回族語 --- | |
| back_code = get_clean_value(mt_client.predict(ethnicity=config["mt"], api_name="/lambda_1")) | |
| ai_native = get_clean_value(mt_client.predict(text=ai_zh, src_lang="zho_Hant", tgt_lang=back_code, api_name="/translate_1")) | |
| ai_native = format_native_text(ai_native, tribe) | |
| # --- 步驟 C: 語音合成 (TTS) --- | |
| speaker = get_clean_value(tts_client.predict(ethnicity=config["mt"], api_name="/lambda")) | |
| if tribe == "阿美": speaker = "阿美_秀姑巒_女聲1" | |
| temp_tts = tts_client.predict(ref=speaker, gen_text_input=ai_native, api_name="/default_speaker_tts") | |
| unique_str = f"reply_{hashlib.md5(ai_native.encode()).hexdigest()[:8]}.wav" | |
| out_filepath = f"static/{unique_str}" | |
| shutil.move(temp_tts, out_filepath) | |
| # --- 步驟 D: 組合完整語音網址 --- | |
| base_url = os.getenv('SPACE_HOST', '') | |
| if base_url: | |
| base_url = f"https://{base_url}" | |
| else: | |
| base_url = "http://127.0.0.1:7860" | |
| audio_url = f"{base_url}/{out_filepath}" | |
| return { | |
| "user_zh_text": zh_in, | |
| "ai_zh_text": ai_zh, | |
| "ai_native_text": ai_native, | |
| "audio_url": audio_url | |
| } | |
| except Exception as e: | |
| return JSONResponse({"error": str(e)}, status_code=500) | |
| if __name__ == "__main__": | |
| uvicorn.run(app, host="0.0.0.0", port=7860) |