File size: 3,809 Bytes
a5d799f
fe3a1da
 
 
820139d
84ade32
fe3a1da
820139d
 
 
 
 
 
 
 
 
a5d799f
820139d
 
 
84ade32
fe3a1da
820139d
 
 
 
 
 
 
 
 
 
 
 
 
 
fe3a1da
820139d
 
 
 
 
 
 
fe3a1da
820139d
7e85690
820139d
 
 
 
 
 
 
 
 
7e85690
820139d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1cb0751
d2d5095
820139d
 
 
 
 
 
 
 
 
 
0e8c1ba
7e85690
9fbb44e
1cb0751
820139d
1cb0751
820139d
 
fe3a1da
820139d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import gradio as gr
import torch
import json
import html
import traceback
from transformers import AutoModelForCausalLM, AutoTokenizer

print("Loading model...")
model_name = "Qwen/Qwen2.5-0.5B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.float16,
    device_map="auto"
)
print("Model loaded.")

SYSTEM_PROMPT = """You are an English learning assistant. Extract 8-20 useful expressions from the text.
For each expression, output a JSON object with keys: expression, meaning, explanation, original_context, extra_example.
Meaning and explanation should be in Chinese.
Output must be a JSON array. No extra text."""

def analyze(text):
    try:
        if not text or len(text.strip()) < 20:
            return "<div style='color:red'>⚠️ Please enter at least 20 characters.</div>"

        messages = [
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": text}
        ]
        inputs = tokenizer.apply_chat_template(
            messages,
            add_generation_prompt=True,
            return_tensors="pt"
        ).to(model.device)

        with torch.no_grad():
            outputs = model.generate(
                inputs,
                max_new_tokens=1024,
                do_sample=False,
                temperature=1.0
            )

        response = tokenizer.decode(outputs[0][inputs.shape[1]:], skip_special_tokens=True)

        # 提取 JSON
        if "```json" in response:
            response = response.split("```json")[1].split("```")[0]
        elif "```" in response:
            response = response.split("```")[1].split("```")[0]
        start = response.find("[")
        end = response.rfind("]") + 1
        if start == -1 or end == 0:
            return f"<div style='color:red'>No JSON array found. Raw response:<br>{html.escape(response[:300])}</div>"

        json_str = response[start:end]
        data = json.loads(json_str)

        cards = ""
        for e in data:
            cards += f"""
            <div style="background:white;border-radius:16px;border:1px solid #ddd;padding:1rem;margin-bottom:1rem;">
                <b style="font-size:1.2rem;">{html.escape(str(e.get('expression', '')))}</b><br>
                <b>Meaning</b><br>{html.escape(str(e.get('meaning', '')))}<br>
                <b>Explanation</b><br>{html.escape(str(e.get('explanation', '')))}<br>
                <b>Original Context</b><br>{html.escape(str(e.get('original_context', '')))}<br>
                <b>Extra Example</b><br>{html.escape(str(e.get('extra_example', '')))}
            </div>
            """
        return cards if cards else "<div>No expressions extracted.</div>"
    except Exception as e:
        error_html = f"<div style='color:red; background:#ffe0e0; padding:1rem; border-radius:8px;'>"
        error_html += f"<b>Error:</b> {html.escape(str(e))}<br><br>"
        error_html += f"<details><summary>Full traceback</summary><pre>{html.escape(traceback.format_exc())}</pre></details>"
        error_html += "</div>"
        return error_html

# 浅色主题
theme = gr.themes.Soft(
    primary_hue="neutral",
    secondary_hue="neutral",
    font=gr.themes.GoogleFont("Inter"),
).set(
    body_background_fill="#fafaf9",
    button_primary_background_fill="#1a1a1a",
    button_primary_text_color="white",
    block_background_fill="white",
)

with gr.Blocks(theme=theme, title="InContext") as demo:
    gr.Markdown("# InContext\n### Learn English Expressions Through Real Content")
    with gr.Row():
        txt = gr.Textbox(lines=10, placeholder="Paste English content here...", label="")
    btn = gr.Button("Analyze", variant="primary")
    out = gr.HTML()
    btn.click(analyze, txt, out)

demo.launch()