Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,13 +1,12 @@
|
|
| 1 |
import os
|
| 2 |
import shutil
|
| 3 |
import hashlib
|
| 4 |
-
from fastapi import FastAPI,
|
| 5 |
from fastapi.middleware.cors import CORSMiddleware
|
| 6 |
from fastapi.staticfiles import StaticFiles
|
| 7 |
from fastapi.responses import JSONResponse
|
| 8 |
-
from pydub import AudioSegment
|
| 9 |
import google.generativeai as genai
|
| 10 |
-
from gradio_client import Client
|
| 11 |
import uvicorn
|
| 12 |
|
| 13 |
app = FastAPI()
|
|
@@ -26,27 +25,21 @@ if not os.path.exists("static"):
|
|
| 26 |
os.makedirs("static")
|
| 27 |
app.mount("/static", StaticFiles(directory="static"), name="static")
|
| 28 |
|
| 29 |
-
# 💡 3. API Keys & Models 初始化 (對
|
| 30 |
GOOGLE_API_KEY = os.getenv('GEMINI_KEY', '請填入您的GEMINI_KEY')
|
| 31 |
genai.configure(api_key=GOOGLE_API_KEY)
|
| 32 |
MODEL_NAME = 'gemini-3.5-flash'
|
| 33 |
model = genai.GenerativeModel(MODEL_NAME)
|
| 34 |
|
| 35 |
-
# 💡 4.
|
| 36 |
-
asr_client = Client("https://ai-labs.ilrdf.org.tw/sapolita-kaldi/")
|
| 37 |
tts_client = Client("https://ai-labs.ilrdf.org.tw/hnang-kari-ai-asi-sluhay/")
|
| 38 |
mt_client = Client("https://ai-labs.ilrdf.org.tw/kari-seejiq-tnpusu-ai-hmjil/")
|
| 39 |
|
| 40 |
-
# 【完全複製你原本的族語配置】
|
| 41 |
TRIBE_CONFIG = {
|
| 42 |
-
"阿美": {"
|
| 43 |
-
"
|
| 44 |
-
"
|
| 45 |
-
"
|
| 46 |
-
"雅美": {"asr": "formosan_tao", "mt": "雅美"}, "邵": {"asr": "formosan_ssf", "mt": "邵"},
|
| 47 |
-
"噶瑪蘭": {"asr": "formosan_ckv", "mt": "噶瑪蘭"}, "太魯閣": {"asr": "formosan_trv", "mt": "太魯閣"},
|
| 48 |
-
"撒奇萊雅": {"asr": "formosan_szy", "mt": "撒奇萊雅"}, "賽德克": {"asr": "formosan_sdq", "mt": "賽德克"},
|
| 49 |
-
"拉阿魯哇": {"asr": "formosan_sxr", "mt": "拉阿魯哇"}, "卡那卡那富": {"asr": "formosan_xnb", "mt": "卡那卡那富"}
|
| 50 |
}
|
| 51 |
|
| 52 |
def get_clean_value(res):
|
|
@@ -62,50 +55,31 @@ def format_native_text(text, tribe):
|
|
| 62 |
|
| 63 |
@app.get("/")
|
| 64 |
def read_root():
|
| 65 |
-
return {"status": "ILRDF Avatar
|
| 66 |
|
| 67 |
@app.post("/api/chat")
|
| 68 |
async def avatar_chat(
|
| 69 |
tribe: str = Form(...),
|
| 70 |
-
|
| 71 |
):
|
| 72 |
try:
|
| 73 |
config = TRIBE_CONFIG.get(tribe)
|
| 74 |
if not config:
|
| 75 |
return JSONResponse({"error": f"不支援的族語: {tribe}"}, status_code=400)
|
| 76 |
|
| 77 |
-
|
| 78 |
-
temp_in = f"static/in_{audio.filename}"
|
| 79 |
-
with open(temp_in, "wb") as buffer:
|
| 80 |
-
shutil.copyfileobj(audio.file, buffer)
|
| 81 |
|
| 82 |
-
|
| 83 |
-
audio_segment = AudioSegment.from_file(temp_in)
|
| 84 |
-
audio_segment.export(wav_path, format="wav")
|
| 85 |
-
|
| 86 |
-
# --- 步驟 B: 【100% 複製你 handle_audio 裡的 ASR 程式碼】 ---
|
| 87 |
-
native_in = asr_client.predict(
|
| 88 |
-
dialect_id=config["asr"],
|
| 89 |
-
audio_data=handle_file(wav_path),
|
| 90 |
-
api_name="/automatic_speech_recognition"
|
| 91 |
-
)
|
| 92 |
-
|
| 93 |
-
# --- 步驟 C: 【100% 複製你 handle_audio 裡的 中文翻譯 程式碼】 ---
|
| 94 |
-
go_code = get_clean_value(mt_client.predict(ethnicity=config["mt"], api_name="/lambda"))
|
| 95 |
-
zh_in = get_clean_value(mt_client.predict(text=native_in, src_lang=go_code, tgt_lang="zho_Hant", api_name="/translate"))
|
| 96 |
-
native_in = format_native_text(native_in, tribe)
|
| 97 |
-
|
| 98 |
-
# --- 步驟 D: AI 大腦生成對話 (Gemini 3.5 Flash) ---
|
| 99 |
prompt = f"你現在是與我對話的{tribe}族朋友。請用中文聊天。回覆規則:1.口吻生活化親切。2.不要說教解釋。3.字數一定要少(15字內),限一個短句。\n\n使用者說:{zh_in}"
|
| 100 |
response = model.generate_content(prompt)
|
| 101 |
ai_zh = response.text.strip()
|
| 102 |
|
| 103 |
-
# --- 步驟
|
| 104 |
back_code = get_clean_value(mt_client.predict(ethnicity=config["mt"], api_name="/lambda_1"))
|
| 105 |
ai_native = get_clean_value(mt_client.predict(text=ai_zh, src_lang="zho_Hant", tgt_lang=back_code, api_name="/translate_1"))
|
| 106 |
ai_native = format_native_text(ai_native, tribe)
|
| 107 |
|
| 108 |
-
# --- 步驟
|
| 109 |
speaker = get_clean_value(tts_client.predict(ethnicity=config["mt"], api_name="/lambda"))
|
| 110 |
if tribe == "阿美": speaker = "阿美_秀姑巒_女聲1"
|
| 111 |
temp_tts = tts_client.predict(ref=speaker, gen_text_input=ai_native, api_name="/default_speaker_tts")
|
|
@@ -114,7 +88,7 @@ async def avatar_chat(
|
|
| 114 |
out_filepath = f"static/{unique_str}"
|
| 115 |
shutil.move(temp_tts, out_filepath)
|
| 116 |
|
| 117 |
-
# --- 步驟
|
| 118 |
base_url = os.getenv('SPACE_HOST', '')
|
| 119 |
if base_url:
|
| 120 |
base_url = f"https://{base_url}"
|
|
@@ -123,11 +97,8 @@ async def avatar_chat(
|
|
| 123 |
|
| 124 |
audio_url = f"{base_url}/{out_filepath}"
|
| 125 |
|
| 126 |
-
os.remove(temp_in)
|
| 127 |
-
os.remove(wav_path)
|
| 128 |
-
|
| 129 |
return {
|
| 130 |
-
"
|
| 131 |
"ai_zh_text": ai_zh,
|
| 132 |
"ai_native_text": ai_native,
|
| 133 |
"audio_url": audio_url
|
|
|
|
| 1 |
import os
|
| 2 |
import shutil
|
| 3 |
import hashlib
|
| 4 |
+
from fastapi import FastAPI, Form
|
| 5 |
from fastapi.middleware.cors import CORSMiddleware
|
| 6 |
from fastapi.staticfiles import StaticFiles
|
| 7 |
from fastapi.responses import JSONResponse
|
|
|
|
| 8 |
import google.generativeai as genai
|
| 9 |
+
from gradio_client import Client
|
| 10 |
import uvicorn
|
| 11 |
|
| 12 |
app = FastAPI()
|
|
|
|
| 25 |
os.makedirs("static")
|
| 26 |
app.mount("/static", StaticFiles(directory="static"), name="static")
|
| 27 |
|
| 28 |
+
# 💡 3. API Keys & Models 初始化 (對接 2026 最新 Gemini 3.5 Flash)
|
| 29 |
GOOGLE_API_KEY = os.getenv('GEMINI_KEY', '請填入您的GEMINI_KEY')
|
| 30 |
genai.configure(api_key=GOOGLE_API_KEY)
|
| 31 |
MODEL_NAME = 'gemini-3.5-flash'
|
| 32 |
model = genai.GenerativeModel(MODEL_NAME)
|
| 33 |
|
| 34 |
+
# 💡 4. 初始化原語會的翻譯與 TTS 伺服器
|
|
|
|
| 35 |
tts_client = Client("https://ai-labs.ilrdf.org.tw/hnang-kari-ai-asi-sluhay/")
|
| 36 |
mt_client = Client("https://ai-labs.ilrdf.org.tw/kari-seejiq-tnpusu-ai-hmjil/")
|
| 37 |
|
|
|
|
| 38 |
TRIBE_CONFIG = {
|
| 39 |
+
"阿美": {"mt": "阿美"}, "泰雅": {"mt": "泰雅"}, "排灣": {"mt": "排灣"}, "布農": {"mt": "布農"},
|
| 40 |
+
"卑南": {"mt": "卑南"}, "魯凱": {"mt": "魯凱"}, "鄒": {"mt": "鄒"}, "賽夏": {"mt": "賽夏"},
|
| 41 |
+
"雅美": {"mt": "雅美"}, "邵": {"mt": "邵"}, "噶瑪蘭": {"mt": "噶瑪蘭"}, "太魯閣": {"mt": "太魯閣"},
|
| 42 |
+
"撒奇萊雅": {"mt": "撒奇萊雅"}, "賽德克": {"mt": "賽德克"}, "拉阿魯哇": {"mt": "拉阿魯哇"}, "卡那卡那富": {"mt": "卡那卡那富"}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
}
|
| 44 |
|
| 45 |
def get_clean_value(res):
|
|
|
|
| 55 |
|
| 56 |
@app.get("/")
|
| 57 |
def read_root():
|
| 58 |
+
return {"status": "ILRDF Avatar Text Brain is Running Successfully!", "model": MODEL_NAME}
|
| 59 |
|
| 60 |
@app.post("/api/chat")
|
| 61 |
async def avatar_chat(
|
| 62 |
tribe: str = Form(...),
|
| 63 |
+
text: str = Form(...) # 👈 接收前端秒傳過來的中文文字
|
| 64 |
):
|
| 65 |
try:
|
| 66 |
config = TRIBE_CONFIG.get(tribe)
|
| 67 |
if not config:
|
| 68 |
return JSONResponse({"error": f"不支援的族語: {tribe}"}, status_code=400)
|
| 69 |
|
| 70 |
+
zh_in = text.strip()
|
|
|
|
|
|
|
|
|
|
| 71 |
|
| 72 |
+
# --- 步驟 A: AI 大腦生成對話 (Gemini 3.5 Flash) ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
prompt = f"你現在是與我對話的{tribe}族朋友。請用中文聊天。回覆規則:1.口吻生活化親切。2.不要說教解釋。3.字數一定要少(15字內),限一個短句。\n\n使用者說:{zh_in}"
|
| 74 |
response = model.generate_content(prompt)
|
| 75 |
ai_zh = response.text.strip()
|
| 76 |
|
| 77 |
+
# --- 步驟 B: 翻譯回族語 ---
|
| 78 |
back_code = get_clean_value(mt_client.predict(ethnicity=config["mt"], api_name="/lambda_1"))
|
| 79 |
ai_native = get_clean_value(mt_client.predict(text=ai_zh, src_lang="zho_Hant", tgt_lang=back_code, api_name="/translate_1"))
|
| 80 |
ai_native = format_native_text(ai_native, tribe)
|
| 81 |
|
| 82 |
+
# --- 步驟 C: 語音合成 (TTS) ---
|
| 83 |
speaker = get_clean_value(tts_client.predict(ethnicity=config["mt"], api_name="/lambda"))
|
| 84 |
if tribe == "阿美": speaker = "阿美_秀姑巒_女聲1"
|
| 85 |
temp_tts = tts_client.predict(ref=speaker, gen_text_input=ai_native, api_name="/default_speaker_tts")
|
|
|
|
| 88 |
out_filepath = f"static/{unique_str}"
|
| 89 |
shutil.move(temp_tts, out_filepath)
|
| 90 |
|
| 91 |
+
# --- 步驟 D: 組合完整語音網址 ---
|
| 92 |
base_url = os.getenv('SPACE_HOST', '')
|
| 93 |
if base_url:
|
| 94 |
base_url = f"https://{base_url}"
|
|
|
|
| 97 |
|
| 98 |
audio_url = f"{base_url}/{out_filepath}"
|
| 99 |
|
|
|
|
|
|
|
|
|
|
| 100 |
return {
|
| 101 |
+
"user_zh_text": zh_in,
|
| 102 |
"ai_zh_text": ai_zh,
|
| 103 |
"ai_native_text": ai_native,
|
| 104 |
"audio_url": audio_url
|