ILRDF-Lowking's picture
Update app.py
df8dadb verified
Raw
History Blame Contribute Delete
4.39 kB
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:]
@app.get("/")
def read_root():
return {"status": "ILRDF Avatar Text Brain is Running Successfully!", "model": MODEL_NAME}
@app.post("/api/chat")
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)