sofiamak commited on
Commit
690956f
·
verified ·
1 Parent(s): 85a989b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +175 -36
app.py CHANGED
@@ -1,59 +1,208 @@
1
- import os, html
2
  import gradio as gr
3
  from openai import OpenAI
4
 
 
5
  API_KEY = os.getenv("OPENAI_API_KEY")
6
  MODEL_NAME = "gpt-4o-mini"
7
  client = OpenAI(api_key=API_KEY)
8
 
 
9
  SYSTEM_PROMPT = """You are a playful, high-creativity pet name generator.
10
  Goal: Produce EXACTLY ONE cute, kid-pronounceable pet name + one authentic quote line.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  """
12
 
13
  def build_prompt(species, look, personality, gender, language, inspiration):
14
  return f"""
15
  Requested language: {language}
16
- Primary inspiration source: {inspiration}
17
- Secondary refinements:
18
  - Species: {species or '(unspecified)'}
19
  - Appearance: {look or '(unspecified)'}
20
  - Personality: {personality or '(unspecified)'}
21
  - Gender: {gender}
 
22
  Task:
23
- 1) Generate one cute pet name
24
- 2) Provide one authentic quote
 
25
  """
26
 
27
- def generate_name(species, look, personality, gender, language, inspiration):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  try:
29
- prompt = build_prompt(species, look, personality, gender, language, inspiration)
 
 
 
 
 
30
  resp = client.chat.completions.create(
31
  model=MODEL_NAME,
32
- temperature=0.9,
 
33
  max_tokens=280,
34
  messages=[
35
  {"role": "system", "content": SYSTEM_PROMPT},
36
- {"role": "user", "content": prompt}
37
  ]
38
  )
39
  raw = (resp.choices[0].message.content or "").strip()
40
- return raw
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  except Exception as e:
42
- return f"⚠️ 出错:{html.escape(str(e))}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
 
44
  theme = gr.themes.Soft(primary_hue="pink", secondary_hue="cyan", neutral_hue="slate")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
- with gr.Blocks(title="Pawfect Match", theme=theme) as demo:
47
- # 顶栏(只保留标题,没有副标题)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  gr.HTML("""
49
- <div style="display:flex;align-items:center;justify-content:space-between;
50
- padding:12px;background:linear-gradient(90deg,#ffe8f3,#e8fbff);
51
- border-radius:12px;border:1px solid #eddcf3;">
52
- <div style="display:flex;align-items:center;gap:10px;">
53
- <div style="font-size:24px;">🐾</div>
54
- <div style="font-weight:800;font-size:20px;color:#111;">Pawfect Match</div>
55
  </div>
56
- <div style="color:#6b7280;font-size:12px;">v1.6</div>
57
  </div>
58
  """)
59
 
@@ -70,28 +219,18 @@ with gr.Blocks(title="Pawfect Match", theme=theme) as demo:
70
  btn = gr.Button("✨ 起名", variant="primary")
71
  clear_btn = gr.Button("清空", variant="secondary")
72
 
73
- # 点击生成后,不输出到文本框,而是触发前端弹窗
74
- out = gr.Textbox(visible=False)
75
 
76
  btn.click(
77
- generate_name,
78
  inputs=[species, look, personality, gender, language, inspiration],
79
- outputs=out,
80
- js="""
81
- (result) => {
82
- if (result) {
83
- alert(result);
84
- } else {
85
- alert("没有生成结果,请重试。");
86
- }
87
- return result;
88
- }
89
- """
90
  )
91
 
92
  clear_btn.click(
93
- lambda: ("","","","male","English","生活"),
94
- outputs=[species, look, personality, gender, language, inspiration]
95
  )
96
 
97
  if __name__ == "__main__":
 
1
+ import os, html, random
2
  import gradio as gr
3
  from openai import OpenAI
4
 
5
+ # ===== 基础配置 =====
6
  API_KEY = os.getenv("OPENAI_API_KEY")
7
  MODEL_NAME = "gpt-4o-mini"
8
  client = OpenAI(api_key=API_KEY)
9
 
10
+ # ===== 系统提示(可爱名字 + 真实名言,灵感优先)=====
11
  SYSTEM_PROMPT = """You are a playful, high-creativity pet name generator.
12
  Goal: Produce EXACTLY ONE cute, kid-pronounceable pet name + one authentic quote line.
13
+
14
+ Creative balance:
15
+ - Inspiration source (山海经 / 迪士尼 / 哈利波特 / 生活) drives the concept (~70%).
16
+ - User inputs (species/appearance/personality/gender) refine the vibe (~30%).
17
+
18
+ NAME rules (kid-friendly):
19
+ - Cute, easy to call; allow coinages that sound natural.
20
+ - Chinese: 1–2 simple characters; English: 1–2 syllables; Japanese: 2–3 mora; Korean: 2 simple syllables; French: 1–2 syllables with soft endings.
21
+ - Output in requested language (Chinese/English/Japanese/Korean/French).
22
+ - ONE short meaning (≤12 words).
23
+
24
+ QUOTE rules (must be real):
25
+ - ONE short, warm, authentic quote about animals/love/companionship/playfulness with attribution (e.g., — Anatole France).
26
+ - Translate quote text into the requested language if needed; keep attribution name in Latin script.
27
+
28
+ STRICT OUTPUT (exactly two lines):
29
+ Name — short meaning
30
+ Quote: “...” — Attribution
31
  """
32
 
33
  def build_prompt(species, look, personality, gender, language, inspiration):
34
  return f"""
35
  Requested language: {language}
36
+ Primary inspiration: {inspiration}
37
+ Secondary refinements (may be empty):
38
  - Species: {species or '(unspecified)'}
39
  - Appearance: {look or '(unspecified)'}
40
  - Personality: {personality or '(unspecified)'}
41
  - Gender: {gender}
42
+
43
  Task:
44
+ 1) Create EXACTLY ONE cute, kid-pronounceable pet name (≤12-word meaning).
45
+ 2) Then output ONE authentic, warm animal-related quote with attribution.
46
+ Keep both lines in {language}. Inspiration ~70%, traits ~30%.
47
  """
48
 
49
+ # 真实名言兜底(防止偶尔没返回 quote 行)
50
+ FALLBACK_QUOTES = {
51
+ "English": [
52
+ ('"Until one has loved an animal, a part of one\'s soul remains unawakened."', "Anatole France"),
53
+ ('"An animal\'s eyes have the power to speak a great language."', "Martin Buber"),
54
+ ('"Animals are such agreeable friends—they ask no questions; they pass no criticisms."', "George Eliot"),
55
+ ],
56
+ "中文": [
57
+ ('“一个人若未曾爱过动物,他的灵魂尚未苏醒。”', "Anatole France"),
58
+ ('“动物的眼睛自有它们的语言。”', "Martin Buber"),
59
+ ('“动物是极好的朋友——它们不发问,也不挑剔。”', "George Eliot"),
60
+ ],
61
+ "日本語": [
62
+ ('「動物を愛したことのない者の魂は、まだ目覚めていない。」', "Anatole France"),
63
+ ('「動物の瞳は偉大な言葉を語る力を持つ。」', "Martin Buber"),
64
+ ('「動物は実に快い友である——問いも批判もない。」', "George Eliot"),
65
+ ],
66
+ "한국어": [
67
+ ('“동물을 사랑해 보지 않은 영혼은 아직 깨어나지 않았다.”', "Anatole France"),
68
+ ('“동물의 눈에는 위대한 언어가 담겨 있다.”', "Martin Buber"),
69
+ ('“동물은 참으로 상냥한 친구다—묻지도 비난하지도 않는다.”', "George Eliot"),
70
+ ],
71
+ "Français": [
72
+ ("« Jusqu'à ce qu'on ait aimé un animal, une partie de notre âme reste endormie. »", "Anatole France"),
73
+ ("« Les yeux d’un animal ont le pouvoir de parler un grand langage. »", "Martin Buber"),
74
+ ("« Les animaux sont de si agréables amis — ils ne posent aucune question et ne font aucune critique. »", "George Eliot"),
75
+ ],
76
+ }
77
+
78
+ def _fallback_quote(language: str) -> str:
79
+ lst = FALLBACK_QUOTES.get(language, FALLBACK_QUOTES["English"])
80
+ text, author = random.choice(lst)
81
+ return f'Quote: {text} — {author}'
82
+
83
+ def _ensure_two_lines(content: str, language: str):
84
+ if not content:
85
+ return None, None
86
+ lines = [ln.strip() for ln in content.splitlines() if ln.strip()]
87
+ name_line = lines[0] if lines else ""
88
+ quote_line = next((ln for ln in lines[1:] if ln.lower().startswith("quote:")), None)
89
+ if not quote_line:
90
+ quote_line = _fallback_quote(language)
91
+ return name_line, quote_line
92
+
93
+ def generate_modal_html(species, look, personality, gender, language, inspiration):
94
+ """返回一个HTML覆盖层(伪弹窗),不使用 js 参数,兼容所有版本。"""
95
  try:
96
+ prompt = build_prompt(
97
+ (species or "").strip(),
98
+ (look or "").strip(),
99
+ (personality or "").strip(),
100
+ gender, language, inspiration
101
+ )
102
  resp = client.chat.completions.create(
103
  model=MODEL_NAME,
104
+ temperature=0.95,
105
+ top_p=0.95,
106
  max_tokens=280,
107
  messages=[
108
  {"role": "system", "content": SYSTEM_PROMPT},
109
+ {"role": "user", "content": prompt}
110
  ]
111
  )
112
  raw = (resp.choices[0].message.content or "").strip()
113
+ name_line, quote_line = _ensure_two_lines(raw, language)
114
+ if not name_line:
115
+ name_line = "(没有生成内容)"
116
+ if not quote_line:
117
+ quote_line = _fallback_quote(language)
118
+
119
+ # 构造覆盖层HTML(带关闭按钮,纯CSS/HTML,不依赖 .click 的 js 参数)
120
+ name_html = html.escape(name_line)
121
+ quote_html = html.escape(quote_line)
122
+ return f"""
123
+ <div class="pm-overlay" onclick="this.remove()">
124
+ <div class="pm-modal" onclick="event.stopPropagation()">
125
+ <div class="pm-modal-title">Pawfect Match</div>
126
+ <div class="pm-modal-body">
127
+ <div class="pm-name">{name_html}</div>
128
+ <div class="pm-quote">{quote_html}</div>
129
+ </div>
130
+ <div class="pm-modal-actions">
131
+ <button class="pm-btn" onclick="document.querySelector('.pm-overlay')?.remove()">关闭</button>
132
+ </div>
133
+ </div>
134
+ </div>
135
+ """
136
  except Exception as e:
137
+ err = html.escape(str(e))
138
+ return f"""
139
+ <div class="pm-overlay" onclick="this.remove()">
140
+ <div class="pm-modal" onclick="event.stopPropagation()">
141
+ <div class="pm-modal-title">Pawfect Match</div>
142
+ <div class="pm-modal-body">
143
+ <div class="pm-name">⚠️ 出错</div>
144
+ <div class="pm-quote">{err}</div>
145
+ </div>
146
+ <div class="pm-modal-actions">
147
+ <button class="pm-btn" onclick="document.querySelector('.pm-overlay')?.remove()">关闭</button>
148
+ </div>
149
+ </div>
150
+ </div>
151
+ """
152
 
153
+ # ===== 主题 & CSS =====
154
  theme = gr.themes.Soft(primary_hue="pink", secondary_hue="cyan", neutral_hue="slate")
155
+ CUSTOM_CSS = """
156
+ :root{
157
+ --pm-bg:#faf7fb; --pm-surface:#ffffff; --pm-border:#eddcf3; --pm-text:#111; --pm-muted:#6b7280;
158
+ --pm-accent:#ff4fa3;
159
+ }
160
+ body, .gradio-container{ background:var(--pm-bg) !important; }
161
+ .pm-topbar{
162
+ display:flex; align-items:center; justify-content:space-between;
163
+ padding:12px; background:linear-gradient(90deg,#ffe8f3,#e8fbff);
164
+ border-radius:12px; border:1px solid var(--pm-border);
165
+ }
166
+ .pm-brand{ display:flex; align-items:center; gap:10px; }
167
+ .pm-logo{ font-size:24px; }
168
+ .pm-title{ font-weight:800; font-size:20px; color:var(--pm-text); }
169
 
170
+ /* 覆盖层(伪弹窗) */
171
+ .pm-overlay{
172
+ position:fixed; inset:0; background:rgba(0,0,0,.35);
173
+ display:flex; align-items:center; justify-content:center; z-index:9999;
174
+ }
175
+ .pm-modal{
176
+ width:min(560px, 92vw); background:var(--pm-surface); border:1px solid var(--pm-border);
177
+ border-radius:14px; box-shadow:0 20px 60px rgba(0,0,0,.18);
178
+ overflow:hidden; animation:pm-pop .14s ease-out;
179
+ }
180
+ .pm-modal-title{
181
+ padding:14px 16px; font-weight:800; border-bottom:1px solid var(--pm-border);
182
+ }
183
+ .pm-modal-body{ padding:16px; }
184
+ .pm-name{ font-weight:800; font-size:22px; margin-bottom:8px; color:#111; }
185
+ .pm-quote{ font-weight:600; font-size:14px; color:#374151; opacity:.95; }
186
+ .pm-modal-actions{ padding:12px 16px; display:flex; justify-content:flex-end; gap:8px; border-top:1px solid var(--pm-border); }
187
+ .pm-btn{
188
+ padding:10px 14px; border-radius:10px; border:1px solid var(--pm-border); background:var(--pm-surface);
189
+ cursor:pointer; font-weight:700;
190
+ }
191
+ .pm-btn:hover{ border-color: var(--pm-accent); color: var(--pm-accent); }
192
+
193
+ @keyframes pm-pop{ from{ transform:translateY(6px); opacity:0; } to{ transform:translateY(0); opacity:1; } }
194
+ """
195
+
196
+ # ===== 界面 =====
197
+ with gr.Blocks(title="Pawfect Match", theme=theme, css=CUSTOM_CSS) as demo:
198
+ # 顶栏(去掉副标题)
199
  gr.HTML("""
200
+ <div class="pm-topbar">
201
+ <div class="pm-brand">
202
+ <div class="pm-logo">🐾</div>
203
+ <div class="pm-title">Pawfect Match</div>
 
 
204
  </div>
205
+ <div style="color:#6b7280;font-size:12px;">v1.7</div>
206
  </div>
207
  """)
208
 
 
219
  btn = gr.Button("✨ 起名", variant="primary")
220
  clear_btn = gr.Button("清空", variant="secondary")
221
 
222
+ # 输出:只放一个隐藏的 HTML 容器,点“起名”时返回覆盖层 HTML
223
+ modal = gr.HTML(visible=True, value="") # 初始为空,不显示
224
 
225
  btn.click(
226
+ generate_modal_html,
227
  inputs=[species, look, personality, gender, language, inspiration],
228
+ outputs=modal
 
 
 
 
 
 
 
 
 
 
229
  )
230
 
231
  clear_btn.click(
232
+ lambda: ("","","","male","English","生活",""),
233
+ outputs=[species, look, personality, gender, language, inspiration, modal]
234
  )
235
 
236
  if __name__ == "__main__":