idiom-finder / src /app.py
Mel Seto
default to traditional, remove checkbox
51a8ded
raw
history blame
5.89 kB
import os
import gradio as gr
import backoff
from cerebras.cloud.sdk import APIConnectionError, APIStatusError, Cerebras, RateLimitError
from dotenv import load_dotenv
from opencc import OpenCC
from utils.utils import get_pinyin
from verification.verifier import verify_idiom_exists
# ======================
# Config
# ======================
load_dotenv()
MODEL = "gpt-oss-120b"
USE_MOCK = False # ✅ Toggle between mock and real API
# simplified to traditional Chinese character converter
char_converter = OpenCC('s2t')
# Set default to Traditional characters due to user base
IS_TRADITIONAL = True
# ======================
# Instantiate client (if not mocking)
# ======================
CLIENT = None
if not USE_MOCK:
CLIENT = Cerebras(api_key=os.environ.get("CEREBRAS_API_KEY"))
def format_explanation(pinyin_text: str, translation: str, meaning: str) -> str:
return f"""
<div style="line-height: 1.4; margin: 0;">
<p style="margin: 0;">
{pinyin_text}
</p>
<div style="margin-top: 8px;">
<i>{translation}</i><br>
{meaning}
</div>
</div>
"""
# ======================
# Mock function for UI testing
# ======================
def find_idiom_mock():
idiom = "对症下药"
pinyin_text = "duì zhèng xià yào"
translation = "To prescribe the right medicine; to take the right approach to a problem."
meaning = "add a meaning for the mock"
explanation = format_explanation(pinyin_text, translation, meaning)
idiom_output = f"{idiom}<br>"
return idiom_output, explanation
# ======================
# Real API function
# ======================
# Global cache for repeated situations
EXAMPLE_CACHE = {}
@backoff.on_exception(backoff.expo, RateLimitError)
def find_idiom(situation: str, max_attempts: int = 3):
"""
Find a verified Chinese idiom for a given situation.
Uses verify_idiom_exists() to confirm idiom validity.
"""
if situation in EXAMPLE_CACHE:
return EXAMPLE_CACHE[situation]
prompt = f"""You are a wise assistant. Given a situation, respond with exactly:
1. A Chinese idiom (includes 成語、俗語、諺語),
written in simplified Chinese characters,
that conveys the idea of the given situation.
2. Its literal English translation
3. Explain idiom in English. Keep explanation to 2-3 concise sentences.
Format:
Idiom
Literal translation
Explanation
Situation: {situation}
Answer:"""
response = CLIENT.chat.completions.create(
model=MODEL,
messages=[{"role": "user", "content": prompt}],
)
generated_text = response.choices[0].message.content.strip()
lines = [line.strip() for line in generated_text.split("\n") if line.strip()]
llm_idiom = lines[0] if lines else generated_text
trad_idiom = char_converter.convert(llm_idiom) if char_converter else None
# 2️⃣ Verify idiom using CC-CEDICT + Wiktionary
if verify_idiom_exists(llm_idiom):
pinyin_text = get_pinyin(llm_idiom)
if len(lines) >= 3:
translation = lines[1]
meaning = " ".join(lines[2:])
else:
translation = ""
meaning = " ".join(lines[1:])
explanation = format_explanation(pinyin_text, translation, meaning)
EXAMPLE_CACHE[situation] = (llm_idiom, explanation)
idiom_output = f"{llm_idiom}<br>{trad_idiom}"
return idiom_output, explanation
# Fallback if no verified idiom found
fallback_idiom = "未找到成语"
fallback_explanation = "No verified idiom found for this situation."
return fallback_idiom, fallback_explanation
# ======================
# UI Wrapper
# ======================
def update_ui(situation):
if USE_MOCK:
idiom, explanation = find_idiom_mock()
else:
try:
idiom, explanation = find_idiom(situation)
except RateLimitError:
idiom = ""
explanation = "<div class='error-message'>Too many requests. Please try again later.</div>"
idiom, explanation = find_idiom(situation)
idiom_output = char_converter.convert(idiom.split("<br>")[0]) if IS_TRADITIONAL else idiom
return (
f"<div class='idiom-output'>{idiom_output}</div>",
f"<div class='explanation-output' style='margin-top: 1px;'>{explanation}</div>",
)
# ======================
# Launch app
# ======================
def launch_app():
with gr.Blocks(css="style.css") as demo:
gr.Markdown("# 🎋 Chinese Idiom Finder")
with gr.Row():
with gr.Column():
situation = gr.Textbox(
label="Enter a situation",
lines=2,
placeholder="e.g., When facing a big challenge",
)
generate_btn = gr.Button("✨ Find Idiom")
# ✅ Example situations
gr.Examples(
examples=[
["When facing a big challenge"],
["When someone helps you in a time of need"],
["When you need to stay calm under pressure"],
["When teamwork is important to succeed"],
["When rushing leads to mistakes"],
],
inputs=situation,
)
with gr.Column():
idiom_output = gr.HTML(label="Idiom")
explanation_output = gr.HTML(label="Explanation")
# pylint: disable=no-member
generate_btn.click(
fn=update_ui,
inputs=[situation],
outputs=[idiom_output, explanation_output],
)
demo.launch()
if __name__ == "__main__":
launch_app()