cwadayi commited on
Commit
33e131c
ยท
verified ยท
1 Parent(s): 95277b0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +136 -19
app.py CHANGED
@@ -136,7 +136,103 @@ except Exception:
136
  return "\n".join(lines).strip()
137
 
138
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
139
- # 2) ไธญๆ–‡ๅญ—ๅž‹๏ผˆ่‹ฅๅฎนๅ™จๆœ‰ Noto/WenQuanYi ็ญ‰ๆœƒ่‡ชๅ‹•ๅฅ—็”จ๏ผ‰
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
141
  for fp in [
142
  "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
@@ -149,7 +245,7 @@ for fp in [
149
  break
150
 
151
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€๏ฟฝ๏ฟฝโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
152
- # 3) ๅŸบๆœฌ่จญๅฎš
153
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
154
  CHANNEL_ACCESS_TOKEN = os.getenv("CHANNEL_ACCESS_TOKEN")
155
  CHANNEL_SECRET = os.getenv("CHANNEL_SECRET")
@@ -172,7 +268,7 @@ configuration = Configuration(access_token=CHANNEL_ACCESS_TOKEN)
172
  handler = WebhookHandler(CHANNEL_SECRET)
173
 
174
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
175
- # 4) ้ฆ–้ ๏ผˆ็พŽ่ง€่ชชๆ˜Ž้ ๏ผ‰
176
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
177
  @app.route("/", methods=["GET"])
178
  def home():
@@ -225,16 +321,17 @@ def home():
225
  <section class="hero">
226
  <div class="badge">็‹€ๆ…‹๏ผš<span style="color:{'#86efac' if channel_ok=='โœ…' else '#fbbf24'}">{channel_ok}</span> LINE ้‡‘้‘ฐใ€€ยทใ€€HF Space๏ผš{space_ok}</div>
227
  <h1 class="title">ๅœฐ้œ‡้ ่ญฆ dayichen โ€“ LINE Bot</h1>
228
- <p class="subtitle">ๆŸฅ่ฉขๅ…จ็ƒ่ฟ‘ 24 ๅฐๆ™‚ๅœฐ้œ‡ใ€ไปŠๅนดๅฐ็ฃๅœฐ้œ‡ๆธ…ๅ–ฎ่ˆ‡ๅœฐๅœ–ใ€ไธญๅคฎๆฐฃ่ฑก็ฝฒๅœฐ้œ‡้ ่ญฆใ€‚</p>
229
  <div class="grid">
230
  <div class="card">
231
  <h3>๐Ÿš€ ๅฟซ้€Ÿ้–‹ๅง‹</h3>
232
  <ul>
233
- <li>ๅœจ LINE ๅฐ่ฉฑ่ผธๅ…ฅ <span class="kbd">/help</span> ๅ–ๅพ—ๆŒ‡ไปคๅˆ—่กจ</li>
234
- <li>่ฉฆ่ฉฆ <span class="kbd">ๅœฐ้œ‡</span> ๆˆ– <span class="kbd">quake</span>๏ผšๅ…จ็ƒ่ฟ‘ 24 ๅฐๆ™‚ Mโ‰ฅ5.0</li>
235
  <li><span class="kbd">่‡บ็ฃๅœฐ้œ‡</span>/<span class="kbd">ๅฐ็ฃๅœฐ้œ‡</span>๏ผšไปŠๅนดๅฐ็ฃๅ€ๅŸŸๆธ…ๅ–ฎ๏ผˆๅซๆ—ฅๆœŸๆ™‚้–“๏ผ‰</li>
236
  <li><span class="kbd">่‡บ็ฃๅœฐ้œ‡็•ซๅœ–</span>/<span class="kbd">ๅฐ็ฃๅœฐ้œ‡็•ซๅœ–</span>๏ผšๅ›žๅ‚ณๅœฐๅœ–ๅœ–็‰‡</li>
237
  <li><span class="kbd">ๅœฐ้œ‡้ ่ญฆ</span>๏ผšCWA ๅœฐ้œ‡้ ่ญฆ๏ผˆๆœ€ๆ–ฐ 5 ็ญ†๏ผ‰</li>
 
238
  </ul>
239
  </div>
240
  <div class="card">
@@ -248,9 +345,9 @@ def home():
248
  <div class="card">
249
  <h3>โ„น๏ธ ๅ‚™่จป</h3>
250
  <ul>
251
- <li>ๆธ…ๅ–ฎๆ™‚้–“้กฏ็คบ๏ผšๅฐ็ฃๆ™‚้–“่ˆ‡ UTCใ€‚</li>
 
252
  <li>ๅœฐๅœ–ไปฅ Matplotlib ็”ข็”Ÿๆ–ผ <span class="mono">/tmp/static</span>๏ผŒ็”ฑ <span class="mono">/static/</span> ๆไพ›ใ€‚</li>
253
- <li>่‹ฅๅœ–็‰‡ๅœจ LINE ้กฏ็คบ็ฉบ็™ฝ๏ผŒ่ซ‹็ขบ่ชๆœฌ้ ใ€ŒWebhook ่ˆ‡้œๆ…‹ๆช”ใ€็ถฒๅ€่ƒฝๅพžๅค–็ถฒๅญ˜ๅ–ใ€‚</li>
254
  </ul>
255
  </div>
256
  </div>
@@ -270,7 +367,7 @@ def serve_static(filename):
270
  return send_from_directory(STATIC_DIR, filename)
271
 
272
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
273
- # 5) ่ณ‡ๆ–™ๆŸฅ่ฉข๏ผˆUSGS๏ผ‰
274
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
275
  USGS_API_BASE_URL = "https://earthquake.usgs.gov/fdsnws/event/1/query"
276
 
@@ -334,7 +431,7 @@ def fetch_taiwan_df_this_year(min_mag=5.0) -> pd.DataFrame | str:
334
  return f"โŒ ๆŸฅ่ฉขๅคฑๆ•—: {e}"
335
 
336
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
337
- # 6) ่ฃฝๅœ–
338
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
339
  def create_and_save_map(df: pd.DataFrame) -> str:
340
  fig, ax = plt.subplots(figsize=(9, 6), dpi=150)
@@ -367,7 +464,7 @@ def _base_url_for_images() -> str:
367
  return HF_SPACE_URL.rstrip("/") if HF_SPACE_URL else request.url_root.rstrip("/")
368
 
369
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
370
- # 7) LINE Webhook
371
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
372
  @app.route("/callback", methods=["POST"])
373
  def callback():
@@ -381,11 +478,13 @@ def callback():
381
 
382
  @handler.add(MessageEvent, message=TextMessageContent)
383
  def handle_message(event):
384
- user_message = (event.message.text or "").strip().lower()
 
 
385
  with ApiClient(configuration) as api_client:
386
  line_bot_api = MessagingApi(api_client)
387
 
388
- # ๅœฐ้œ‡้ ่ญฆ๏ผˆCWA๏ผ‰
389
  if "ๅœฐ้œ‡้ ่ญฆ" in user_message:
390
  reply_text = fetch_cwa_alarm_list(limit=5)
391
  line_bot_api.reply_message_with_http_info(
@@ -393,7 +492,7 @@ def handle_message(event):
393
  )
394
  return
395
 
396
- # ่‡บ็ฃๅœฐ้œ‡็•ซๅœ–
397
  if ("่‡บ็ฃๅœฐ้œ‡็•ซๅœ–" in user_message) or ("ๅฐ็ฃๅœฐ้œ‡็•ซๅœ–" in user_message):
398
  result = fetch_taiwan_df_this_year()
399
  if isinstance(result, pd.DataFrame):
@@ -411,7 +510,7 @@ def handle_message(event):
411
  line_bot_api.reply_message_with_http_info(reply)
412
  return
413
 
414
- # ่ชชๆ˜Ž
415
  if user_message == "/help":
416
  text = (
417
  "๐Ÿ“– ๅœฐ้œ‡้ ่ญฆ dayichen ๆŒ‡ไปค\n\n"
@@ -420,6 +519,7 @@ def handle_message(event):
420
  "โžก๏ธ ่‡บ็ฃๅœฐ้œ‡ / ๅฐ็ฃๅœฐ้œ‡๏ผˆไปŠๅนดๅฐ็ฃๅ€ๅŸŸๆธ…ๅ–ฎ๏ผ‰\n"
421
  "โžก๏ธ ่‡บ็ฃๅœฐ้œ‡็•ซๅœ– / ๅฐ็ฃๅœฐ้œ‡็•ซๅœ–๏ผˆไปŠๅนดๅฐ็ฃๅ€ๅŸŸๅˆ†ไฝˆๅœ–๏ผ‰\n"
422
  "โžก๏ธ ๅœฐ้œ‡้ ่ญฆ๏ผˆCWA ๆœ€ๆ–ฐ 5 ็ญ†๏ผ‰\n"
 
423
  "โžก๏ธ ไฝ ๅฅฝ"
424
  )
425
  line_bot_api.reply_message_with_http_info(
@@ -427,7 +527,7 @@ def handle_message(event):
427
  )
428
  return
429
 
430
- # ๅฐ็ฃๆธ…ๅ–ฎ
431
  if ("่‡บ็ฃๅœฐ้œ‡" in user_message) or ("ๅฐ็ฃๅœฐ้œ‡" in user_message):
432
  result = fetch_taiwan_df_this_year()
433
  if isinstance(result, pd.DataFrame):
@@ -446,7 +546,7 @@ def handle_message(event):
446
  )
447
  return
448
 
449
- # ๅ…จ็ƒ 24 ๅฐๆ™‚
450
  if ("ๅœฐ้œ‡" in user_message) or ("quake" in user_message):
451
  reply_text = fetch_global_last24h_text()
452
  line_bot_api.reply_message_with_http_info(
@@ -454,7 +554,18 @@ def handle_message(event):
454
  )
455
  return
456
 
457
- # ๆ‹›ๅ‘ผ
 
 
 
 
 
 
 
 
 
 
 
458
  if ("ไฝ ๅฅฝ" in user_message) or ("hi" in user_message):
459
  line_bot_api.reply_message_with_http_info(
460
  ReplyMessageRequest(
@@ -462,4 +573,10 @@ def handle_message(event):
462
  messages=[TextMessage(text="๐Ÿ‘‹ ไฝ ๅฅฝ๏ผ่ผธๅ…ฅ /help ๆŸฅ็œ‹ๆŒ‡ไปคใ€‚")]
463
  )
464
  )
465
- return
 
 
 
 
 
 
 
136
  return "\n".join(lines).strip()
137
 
138
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
139
+ # 2) AI ๅฐ่ฉฑ๏ผˆTransformers๏ผ‰โ€” ๅปถ้ฒ่ผ‰ๅ…ฅ่ˆ‡ๅฎน้Œฏ
140
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
141
+ _LLM = {"loaded": False, "ok": False, "err": None, "model": None, "tokenizer": None, "device": "cpu"}
142
+
143
+ def _ensure_llm():
144
+ """
145
+ ๆ‡ถ่ผ‰ๅ…ฅ LLMใ€‚้ ่จญไธญๆ–‡ๆจกๅž‹ ckiplab/gpt2-base-chinese๏ผŒๅฏ็”จ็’ฐๅขƒ่ฎŠๆ•ธ่ฆ†ๅฏซ๏ผš
146
+ - LLM_MODEL (้ ่จญ ckiplab/gpt2-base-chinese)
147
+ - LLM_DEVICE (cuda / cpu)
148
+ ๅ›žๅ‚ณ (ok: bool, err: str|None)
149
+ """
150
+ if _LLM["loaded"]:
151
+ return _LLM["ok"], _LLM["err"]
152
+
153
+ _LLM["loaded"] = True
154
+ try:
155
+ # ๅŒฏๅ…ฅๆ”พๅœจๅ‡ฝๅผๅ…ง๏ผŒ้ฟๅ…ๆœๅ‹™ๅ•Ÿๅ‹•้Žๆ…ขๆˆ–ๅฅ—ไปถ็ผบๅคฑๆ™‚็›ดๆŽฅๅดฉๆฝฐ
156
+ import torch # noqa: F401
157
+ from transformers import AutoTokenizer, AutoModelForCausalLM # noqa: F401
158
+
159
+ device = os.getenv("LLM_DEVICE")
160
+ if device not in ("cuda", "cpu"):
161
+ # ่‡ชๅ‹•ๅตๆธฌ
162
+ try:
163
+ import torch as _t
164
+ device = "cuda" if _t.cuda.is_available() else "cpu"
165
+ _t.set_num_threads(max(1, int(os.getenv("LLM_THREADS", "1"))))
166
+ except Exception:
167
+ device = "cpu"
168
+
169
+ model_name = os.getenv("LLM_MODEL", "ckiplab/gpt2-base-chinese") # ๅฏๆ”นๆˆ "gpt2"
170
+ from transformers import AutoTokenizer, AutoModelForCausalLM
171
+ tok = AutoTokenizer.from_pretrained(model_name)
172
+ mdl = AutoModelForCausalLM.from_pretrained(model_name)
173
+ try:
174
+ mdl = mdl.to(device)
175
+ except Exception:
176
+ device = "cpu"
177
+ mdl = mdl.to(device)
178
+
179
+ _LLM.update({"ok": True, "model": mdl, "tokenizer": tok, "device": device})
180
+ return True, None
181
+ except Exception as e:
182
+ _LLM["err"] = f"{e}"
183
+ _LLM["ok"] = False
184
+ return False, _LLM["err"]
185
+
186
+ def generate_ai_text(user_prompt: str) -> str:
187
+ ok, err = _ensure_llm()
188
+ if not ok:
189
+ return ("๐Ÿค– AI ๅฐšๆœชๅ•Ÿ็”จ๏ผš็ผบๅฐ‘ไพ่ณดๆˆ–ๆจกๅž‹ๆœชไธ‹่ผ‰ใ€‚\n"
190
+ "่ซ‹ๅœจ requirements.txt ๅŠ ๅ…ฅ๏ผštransformersใ€torchใ€accelerateใ€safetensorsใ€‚\n"
191
+ f"่ฉณ็ดฐ้Œฏ่ชค๏ผš{err}")
192
+
193
+ tok = _LLM["tokenizer"]
194
+ mdl = _LLM["model"]
195
+ device = _LLM["device"]
196
+
197
+ max_new = int(os.getenv("LLM_MAX_NEW_TOKENS", "120"))
198
+ top_k = int(os.getenv("LLM_TOP_K", "50"))
199
+ temperature = float(os.getenv("LLM_TEMPERATURE", "0.7"))
200
+
201
+ # ๅปบ็ซ‹็ณป็ตฑๅŒ–ๆ็คบ๏ผŒ่ฎ“ๅ›ž็ญ”ๆ›ด่ฒผ่ฟ‘ๆฉŸๅ™จไบบไธป้กŒ
202
+ system_prefix = (
203
+ "ไฝ ๆ˜ฏไธ€ๅ€‹ๅœฐ้œ‡่ณ‡่จŠ่ˆ‡ไธ€่ˆฌๅ•็ญ”็š„ LINE ๅŠฉ็†ใ€‚ๅ›ž็ญ”่ฆ็ฒพ็ฐกใ€ๆธ…ๆฅš๏ผŒ"
204
+ "่‹ฅ่ˆ‡ๅœฐ้œ‡็›ธ้—œๅฏๅŠ ๅ…ฅๆณจๆ„ไบ‹้ …ใ€‚่‹ฅๅ•้กŒ่ˆ‡ๅœฐ้œ‡็„ก้—œ๏ผŒๅฐฑไธ€่ˆฌๅ›ž่ฆ†ใ€‚\n\n"
205
+ "ไฝฟ็”จ่€…๏ผš"
206
+ )
207
+ prompt = system_prefix + user_prompt
208
+
209
+ try:
210
+ import torch
211
+ inputs = tok(prompt, return_tensors="pt").to(device)
212
+ with torch.no_grad():
213
+ output = mdl.generate(
214
+ input_ids=inputs["input_ids"],
215
+ attention_mask=inputs.get("attention_mask"),
216
+ max_new_tokens=max_new,
217
+ do_sample=True,
218
+ top_k=top_k,
219
+ temperature=temperature,
220
+ pad_token_id=tok.eos_token_id,
221
+ )
222
+ text = tok.decode(output[0], skip_special_tokens=True)
223
+ # ๅ˜—่ฉฆๆŠŠ system_prefix ๅŽปๆމ๏ผŒ็•™ไธ‹ไฝฟ็”จ่€…ๅพŒ็š„ๅ›žๆ‡‰
224
+ if system_prefix in text:
225
+ text = text.split(system_prefix, 1)[-1]
226
+ # GPT-2 ๅฏ่ƒฝๆœƒๆŠŠ prompt ไนŸๅธถๅ›ž๏ผŒ็ฐกๅ–ฎ่ฃๅ‰ช
227
+ if user_prompt in text:
228
+ text = text.split(user_prompt, 1)[-1].strip()
229
+ # ๅฎ‰ๅ…จ้•ทๅบฆๆŽงๅˆถ
230
+ return text.strip()[:1200] if text else "๏ผˆๆฒ’ๆœ‰็”ข็”Ÿๅ…งๅฎน๏ผ‰"
231
+ except Exception as e:
232
+ return f"AI ็”ข็”Ÿ็™ผ็”Ÿ้Œฏ่ชค๏ผš{e}"
233
+
234
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
235
+ # 3) ไธญๆ–‡ๅญ—ๅž‹๏ผˆ่‹ฅๅฎนๅ™จๆœ‰ Noto/WenQuanYi ็ญ‰ๆœƒ่‡ชๅ‹•ๅฅ—็”จ๏ผ‰
236
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
237
  for fp in [
238
  "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
 
245
  break
246
 
247
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€๏ฟฝ๏ฟฝโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
248
+ # 4) ๅŸบๆœฌ่จญๅฎš
249
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
250
  CHANNEL_ACCESS_TOKEN = os.getenv("CHANNEL_ACCESS_TOKEN")
251
  CHANNEL_SECRET = os.getenv("CHANNEL_SECRET")
 
268
  handler = WebhookHandler(CHANNEL_SECRET)
269
 
270
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
271
+ # 5) ้ฆ–้ ๏ผˆ็พŽ่ง€่ชชๆ˜Ž้ ๏ผ‰
272
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
273
  @app.route("/", methods=["GET"])
274
  def home():
 
321
  <section class="hero">
322
  <div class="badge">็‹€ๆ…‹๏ผš<span style="color:{'#86efac' if channel_ok=='โœ…' else '#fbbf24'}">{channel_ok}</span> LINE ้‡‘้‘ฐใ€€ยทใ€€HF Space๏ผš{space_ok}</div>
323
  <h1 class="title">ๅœฐ้œ‡้ ่ญฆ dayichen โ€“ LINE Bot</h1>
324
+ <p class="subtitle">ๆŒ‡ไปค๏ผš/helpใ€ๅœฐ้œ‡/quakeใ€่‡บ็ฃๅœฐ้œ‡/ๅฐ็ฃๅœฐ้œ‡ใ€่‡บ็ฃๅœฐ้œ‡็•ซๅœ–/ๅฐ็ฃๅœฐ้œ‡็•ซๅœ–ใ€ๅœฐ้œ‡้ ่ญฆใ€AI ๅฐ่ฉฑ๏ผˆai + ๅ•้กŒ๏ผ‰ใ€‚</p>
325
  <div class="grid">
326
  <div class="card">
327
  <h3>๐Ÿš€ ๅฟซ้€Ÿ้–‹ๅง‹</h3>
328
  <ul>
329
+ <li><span class="kbd">/help</span>๏ผš้กฏ็คบๆ‰€ๆœ‰ๆŒ‡ไปค</li>
330
+ <li><span class="kbd">ๅœฐ้œ‡</span>/<span class="kbd">quake</span>๏ผšๅ…จ็ƒ่ฟ‘ 24 ๅฐๆ™‚ Mโ‰ฅ5.0</li>
331
  <li><span class="kbd">่‡บ็ฃๅœฐ้œ‡</span>/<span class="kbd">ๅฐ็ฃๅœฐ้œ‡</span>๏ผšไปŠๅนดๅฐ็ฃๅ€ๅŸŸๆธ…ๅ–ฎ๏ผˆๅซๆ—ฅๆœŸๆ™‚้–“๏ผ‰</li>
332
  <li><span class="kbd">่‡บ็ฃๅœฐ้œ‡็•ซๅœ–</span>/<span class="kbd">ๅฐ็ฃๅœฐ้œ‡็•ซๅœ–</span>๏ผšๅ›žๅ‚ณๅœฐๅœ–ๅœ–็‰‡</li>
333
  <li><span class="kbd">ๅœฐ้œ‡้ ่ญฆ</span>๏ผšCWA ๅœฐ้œ‡้ ่ญฆ๏ผˆๆœ€ๆ–ฐ 5 ็ญ†๏ผ‰</li>
334
+ <li><span class="kbd">ai ไฝ ็š„ๅ•้กŒ</span>๏ผšๅ•Ÿ็”จ AI ๅฐ่ฉฑ๏ผŒไพ‹ๅฆ‚ <span class="kbd">ai ไปŠๆ—ฅๅฐ็ฃๅœฐ้œ‡้ขจ้šช้œ€่ฆๆณจๆ„ไป€้บผ๏ผŸ</span></li>
335
  </ul>
336
  </div>
337
  <div class="card">
 
345
  <div class="card">
346
  <h3>โ„น๏ธ ๅ‚™่จป</h3>
347
  <ul>
348
+ <li>AI ้œ€่ฆๅฎ‰่ฃ <span class="mono">transformers / torch</span> ็ญ‰ไพ่ณด๏ผ›ๆœชๅฎ‰่ฃๆ™‚ๆœๅ‹™ๆœƒ็ตฆๅ‡บๆ็คบใ€‚</li>
349
+ <li>ๆจกๅž‹๏ผš<span class="mono">{os.getenv("LLM_MODEL","ckiplab/gpt2-base-chinese")}</span> ยท ่ฃ็ฝฎ๏ผš<span class="mono">{os.getenv("LLM_DEVICE","auto")}</span></li>
350
  <li>ๅœฐๅœ–ไปฅ Matplotlib ็”ข็”Ÿๆ–ผ <span class="mono">/tmp/static</span>๏ผŒ็”ฑ <span class="mono">/static/</span> ๆไพ›ใ€‚</li>
 
351
  </ul>
352
  </div>
353
  </div>
 
367
  return send_from_directory(STATIC_DIR, filename)
368
 
369
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
370
+ # 6) ่ณ‡ๆ–™ๆŸฅ่ฉข๏ผˆUSGS๏ผ‰
371
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
372
  USGS_API_BASE_URL = "https://earthquake.usgs.gov/fdsnws/event/1/query"
373
 
 
431
  return f"โŒ ๆŸฅ่ฉขๅคฑๆ•—: {e}"
432
 
433
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
434
+ # 7) ่ฃฝๅœ–
435
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
436
  def create_and_save_map(df: pd.DataFrame) -> str:
437
  fig, ax = plt.subplots(figsize=(9, 6), dpi=150)
 
464
  return HF_SPACE_URL.rstrip("/") if HF_SPACE_URL else request.url_root.rstrip("/")
465
 
466
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
467
+ # 8) LINE Webhook
468
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
469
  @app.route("/callback", methods=["POST"])
470
  def callback():
 
478
 
479
  @handler.add(MessageEvent, message=TextMessageContent)
480
  def handle_message(event):
481
+ user_message_raw = (event.message.text or "").strip()
482
+ user_message = user_message_raw.lower()
483
+
484
  with ApiClient(configuration) as api_client:
485
  line_bot_api = MessagingApi(api_client)
486
 
487
+ # 1) ๅœฐ้œ‡้ ่ญฆ๏ผˆCWA๏ผ‰
488
  if "ๅœฐ้œ‡้ ่ญฆ" in user_message:
489
  reply_text = fetch_cwa_alarm_list(limit=5)
490
  line_bot_api.reply_message_with_http_info(
 
492
  )
493
  return
494
 
495
+ # 2) ่‡บ็ฃๅœฐ้œ‡็•ซๅœ–
496
  if ("่‡บ็ฃๅœฐ้œ‡็•ซๅœ–" in user_message) or ("ๅฐ็ฃๅœฐ้œ‡็•ซๅœ–" in user_message):
497
  result = fetch_taiwan_df_this_year()
498
  if isinstance(result, pd.DataFrame):
 
510
  line_bot_api.reply_message_with_http_info(reply)
511
  return
512
 
513
+ # 3) ่ชชๆ˜Ž
514
  if user_message == "/help":
515
  text = (
516
  "๐Ÿ“– ๅœฐ้œ‡้ ่ญฆ dayichen ๆŒ‡ไปค\n\n"
 
519
  "โžก๏ธ ่‡บ็ฃๅœฐ้œ‡ / ๅฐ็ฃๅœฐ้œ‡๏ผˆไปŠๅนดๅฐ็ฃๅ€ๅŸŸๆธ…ๅ–ฎ๏ผ‰\n"
520
  "โžก๏ธ ่‡บ็ฃๅœฐ้œ‡็•ซๅœ– / ๅฐ็ฃๅœฐ้œ‡็•ซๅœ–๏ผˆไปŠๅนดๅฐ็ฃๅ€ๅŸŸๅˆ†ไฝˆๅœ–๏ผ‰\n"
521
  "โžก๏ธ ๅœฐ้œ‡้ ่ญฆ๏ผˆCWA ๆœ€ๆ–ฐ 5 ็ญ†๏ผ‰\n"
522
+ "โžก๏ธ ai ไฝ ็š„ๅ•้กŒ๏ผˆAI ๅฐ่ฉฑ๏ผ‰\n"
523
  "โžก๏ธ ไฝ ๅฅฝ"
524
  )
525
  line_bot_api.reply_message_with_http_info(
 
527
  )
528
  return
529
 
530
+ # 4) ๅฐ็ฃๆธ…ๅ–ฎ
531
  if ("่‡บ็ฃๅœฐ้œ‡" in user_message) or ("ๅฐ็ฃๅœฐ้œ‡" in user_message):
532
  result = fetch_taiwan_df_this_year()
533
  if isinstance(result, pd.DataFrame):
 
546
  )
547
  return
548
 
549
+ # 5) ๅ…จ็ƒ 24 ๅฐๆ™‚
550
  if ("ๅœฐ้œ‡" in user_message) or ("quake" in user_message):
551
  reply_text = fetch_global_last24h_text()
552
  line_bot_api.reply_message_with_http_info(
 
554
  )
555
  return
556
 
557
+ # 6) AI ๅฐ่ฉฑ๏ผˆ่งธ็™ผ่ฉž๏ผšไปฅ "ai "ใ€"ai๏ผš"ใ€"ai:" ้–‹้ ญ๏ผ›ๆˆ–ๆœ€ๅพŒๅ…œๅบ•๏ผ‰
558
+ if user_message.startswith("ai ") or user_message.startswith("ai:") or user_message.startswith("ai๏ผš"):
559
+ prompt = user_message_raw[2:].lstrip(" ๏ผš:").strip()
560
+ if not prompt:
561
+ prompt = "่ซ‹ไฝœ็‚บไธ€ๅ€‹ๅœฐ้œ‡่ณ‡่จŠๅŠฉ็†๏ผŒ็ฐก่ฆไป‹็ดนไฝ ็š„ๅŠŸ่ƒฝใ€‚"
562
+ ai_text = generate_ai_text(prompt)
563
+ line_bot_api.reply_message_with_http_info(
564
+ ReplyMessageRequest(reply_token=event.reply_token, messages=[TextMessage(text=ai_text)])
565
+ )
566
+ return
567
+
568
+ # 7) ๆ‹›ๅ‘ผ
569
  if ("ไฝ ๅฅฝ" in user_message) or ("hi" in user_message):
570
  line_bot_api.reply_message_with_http_info(
571
  ReplyMessageRequest(
 
573
  messages=[TextMessage(text="๐Ÿ‘‹ ไฝ ๅฅฝ๏ผ่ผธๅ…ฅ /help ๆŸฅ็œ‹ๆŒ‡ไปคใ€‚")]
574
  )
575
  )
576
+ return
577
+
578
+ # 8) ๅ…œๅบ•๏ผš่‹ฅ้žไธŠ่ฟฐไปปไฝ•ๆŒ‡ไปค๏ผŒไบค็ตฆ AI ็”ข็”Ÿๅ›ž่ฆ†๏ผˆๆ›ดๆ™บ่ƒฝ๏ผ‰
579
+ fallback = generate_ai_text(user_message_raw)
580
+ line_bot_api.reply_message_with_http_info(
581
+ ReplyMessageRequest(reply_token=event.reply_token, messages=[TextMessage(text=fallback)])
582
+ )