Spaces:
Build error
Build error
Update app.py
Browse files
app.py
CHANGED
|
@@ -3429,16 +3429,19 @@ def handle_chat(n_send, n_submit, quick_clicks,
|
|
| 3429 |
return chat_items, history, ""
|
| 3430 |
|
| 3431 |
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 3432 |
-
# 15. LLM API ์ฐ๋
|
| 3433 |
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 3434 |
|
| 3435 |
# โโ ์ค์ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 3436 |
-
|
| 3437 |
-
|
| 3438 |
-
|
| 3439 |
-
|
|
|
|
|
|
|
|
|
|
| 3440 |
LLM_MAX_TOKENS = int(os.environ.get("LLM_MAX_TOKENS", "1024"))
|
| 3441 |
-
LLM_TEMP = float(os.environ.get("LLM_TEMP",
|
| 3442 |
|
| 3443 |
SYSTEM_PROMPT = """๋น์ ์ ์ผ๊ตฌ ๋ฐ์ดํฐ ๋ถ์ ์ ๋ฌธ๊ฐ AI์
๋๋ค.
|
| 3444 |
KBO, NPB, ๋ง์ด๋๋ฆฌ๊ทธ ํฌ์ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํ๊ณ ์ธ์ฌ์ดํธ๋ฅผ ์ ๊ณตํฉ๋๋ค.
|
|
@@ -3449,36 +3452,18 @@ KBO, NPB, ๋ง์ด๋๋ฆฌ๊ทธ ํฌ์ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํ๊ณ ์ธ์ฌ์ดํธ๋ฅผ ์
|
|
| 3449 |
"""
|
| 3450 |
|
| 3451 |
|
| 3452 |
-
# โโ
|
| 3453 |
-
def _call_openai(question, context, history):
|
| 3454 |
-
import openai
|
| 3455 |
-
client = openai.OpenAI(api_key=LLM_API_KEY)
|
| 3456 |
-
|
| 3457 |
-
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
|
| 3458 |
-
for h in (history or [])[-10:]:
|
| 3459 |
-
messages.append({"role": h["role"], "content": h["content"]})
|
| 3460 |
-
messages.append({
|
| 3461 |
-
"role": "user",
|
| 3462 |
-
"content": f"[๋ฐ์ดํฐ ์ปจํ
์คํธ]\n{context}\n\n[์ง๋ฌธ]\n{question}",
|
| 3463 |
-
})
|
| 3464 |
-
|
| 3465 |
-
resp = client.chat.completions.create(
|
| 3466 |
-
model = LLM_MODEL,
|
| 3467 |
-
messages = messages,
|
| 3468 |
-
max_tokens = LLM_MAX_TOKENS,
|
| 3469 |
-
temperature = LLM_TEMP,
|
| 3470 |
-
)
|
| 3471 |
-
return resp.choices[0].message.content.strip()
|
| 3472 |
-
|
| 3473 |
-
|
| 3474 |
-
# โโ Anthropic ํธ์ถ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 3475 |
def _call_anthropic(question, context, history):
|
| 3476 |
import anthropic
|
|
|
|
| 3477 |
client = anthropic.Anthropic(api_key=LLM_API_KEY)
|
| 3478 |
|
|
|
|
| 3479 |
messages = []
|
| 3480 |
for h in (history or [])[-10:]:
|
| 3481 |
-
|
|
|
|
|
|
|
| 3482 |
messages.append({
|
| 3483 |
"role": "user",
|
| 3484 |
"content": f"[๋ฐ์ดํฐ ์ปจํ
์คํธ]\n{context}\n\n[์ง๋ฌธ]\n{question}",
|
|
@@ -3493,67 +3478,30 @@ def _call_anthropic(question, context, history):
|
|
| 3493 |
return resp.content[0].text.strip()
|
| 3494 |
|
| 3495 |
|
| 3496 |
-
# โโ Local / Custom Endpoint ํธ์ถ โโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 3497 |
-
def _call_local(question, context, history):
|
| 3498 |
-
import requests as req
|
| 3499 |
-
|
| 3500 |
-
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
|
| 3501 |
-
for h in (history or [])[-10:]:
|
| 3502 |
-
messages.append({"role": h["role"], "content": h["content"]})
|
| 3503 |
-
messages.append({
|
| 3504 |
-
"role": "user",
|
| 3505 |
-
"content": f"[๋ฐ์ดํฐ ์ปจํ
์คํธ]\n{context}\n\n[์ง๋ฌธ]\n{question}",
|
| 3506 |
-
})
|
| 3507 |
-
|
| 3508 |
-
payload = {
|
| 3509 |
-
"model": LLM_MODEL,
|
| 3510 |
-
"messages": messages,
|
| 3511 |
-
"max_tokens": LLM_MAX_TOKENS,
|
| 3512 |
-
"temperature": LLM_TEMP,
|
| 3513 |
-
"stream": False,
|
| 3514 |
-
}
|
| 3515 |
-
headers = {}
|
| 3516 |
-
if LLM_API_KEY:
|
| 3517 |
-
headers["Authorization"] = f"Bearer {LLM_API_KEY}"
|
| 3518 |
-
|
| 3519 |
-
r = req.post(
|
| 3520 |
-
LLM_API_URL or "http://localhost:11434/v1/chat/completions",
|
| 3521 |
-
json = payload,
|
| 3522 |
-
headers = headers,
|
| 3523 |
-
timeout = 60,
|
| 3524 |
-
)
|
| 3525 |
-
r.raise_for_status()
|
| 3526 |
-
data = r.json()
|
| 3527 |
-
return data["choices"][0]["message"]["content"].strip()
|
| 3528 |
-
|
| 3529 |
-
|
| 3530 |
# โโ ํตํฉ ํธ์ถ ํจ์ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 3531 |
def call_llm_api(question, context="", history=None):
|
| 3532 |
"""
|
| 3533 |
-
|
| 3534 |
-
|
| 3535 |
"""
|
| 3536 |
-
if not LLM_API_KEY
|
| 3537 |
return _rule_based_answer(question, context)
|
| 3538 |
|
| 3539 |
try:
|
| 3540 |
-
|
| 3541 |
-
|
| 3542 |
-
|
| 3543 |
-
|
| 3544 |
-
|
| 3545 |
-
|
| 3546 |
-
except ImportError as e:
|
| 3547 |
-
return f"โ ๏ธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฏธ์ค์น: {e}\n`pip install openai` ๋๋ `pip install anthropic`"
|
| 3548 |
except Exception as e:
|
| 3549 |
-
return f"โ ๏ธ
|
| 3550 |
|
| 3551 |
|
| 3552 |
# โโ ๊ท์น ๊ธฐ๋ฐ ํด๋ฐฑ (API ํค ์์ ๋) โโโโโโโโโโโโโโโโโโโโโโ
|
| 3553 |
def _rule_based_answer(question, context):
|
| 3554 |
q = question.lower()
|
| 3555 |
|
| 3556 |
-
# ์ปจํ
์คํธ ํ์ฑ
|
| 3557 |
ctx = {}
|
| 3558 |
for part in context.split("|"):
|
| 3559 |
part = part.strip()
|
|
@@ -3561,62 +3509,78 @@ def _rule_based_answer(question, context):
|
|
| 3561 |
k, v = part.split("=", 1)
|
| 3562 |
ctx[k.strip()] = v.strip()
|
| 3563 |
|
| 3564 |
-
pitcher = ctx.get("ํฌ์",
|
| 3565 |
-
era = ctx.get("ERA",
|
| 3566 |
-
k9 = ctx.get("K9",
|
| 3567 |
-
bb9 = ctx.get("BB9",
|
| 3568 |
-
whip = ctx.get("WHIP",
|
| 3569 |
-
fip = ctx.get("FIP",
|
| 3570 |
-
spd = ctx.get("ํ๊ท ๊ตฌ์",
|
| 3571 |
-
pitches = ctx.get("์ฃผ๋ ฅ ๊ตฌ์ข
",
|
| 3572 |
|
| 3573 |
if any(w in q for w in ["๊ตฌ์ข
", "์ฃผ๋ ฅ", "pitch"]):
|
| 3574 |
-
|
| 3575 |
-
|
| 3576 |
-
|
| 3577 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3578 |
|
| 3579 |
if any(w in q for w in ["์ฝ์ ", "์นด์ดํธ", "count"]):
|
| 3580 |
-
return (
|
| 3581 |
-
|
| 3582 |
-
|
|
|
|
|
|
|
| 3583 |
|
| 3584 |
if any(w in q for w in ["์ขํ", "์ฐํ", "stand", "ํ์ ์ ํ"]):
|
| 3585 |
-
return (
|
| 3586 |
-
|
| 3587 |
-
|
|
|
|
|
|
|
| 3588 |
|
| 3589 |
if any(w in q for w in ["๊ตฌ์", "์๋", "speed", "ํธ๋ ๋"]):
|
| 3590 |
-
return (
|
| 3591 |
-
|
| 3592 |
-
|
|
|
|
|
|
|
| 3593 |
|
| 3594 |
if any(w in q for w in ["๋ฆฌ๊ทธ", "๋น๊ต", "์์", "rank"]):
|
| 3595 |
-
return (
|
| 3596 |
-
|
| 3597 |
-
|
| 3598 |
-
|
| 3599 |
-
|
| 3600 |
-
|
| 3601 |
-
|
|
|
|
|
|
|
| 3602 |
|
| 3603 |
if any(w in q for w in ["๊ฐ์ ", "์ํ", "good", "best"]):
|
| 3604 |
-
return (
|
| 3605 |
-
|
| 3606 |
-
|
| 3607 |
-
|
| 3608 |
-
|
| 3609 |
-
|
| 3610 |
-
|
| 3611 |
-
|
| 3612 |
-
|
| 3613 |
-
|
| 3614 |
-
|
| 3615 |
-
|
| 3616 |
-
|
| 3617 |
-
|
| 3618 |
-
|
| 3619 |
-
|
|
|
|
|
|
|
| 3620 |
|
| 3621 |
|
| 3622 |
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
|
| 3429 |
return chat_items, history, ""
|
| 3430 |
|
| 3431 |
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 3432 |
+
# 15. LLM API ์ฐ๋ (Claude / Anthropic ์ ์ฉ)
|
| 3433 |
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 3434 |
|
| 3435 |
# โโ ์ค์ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 3436 |
+
# โ
ํ๊ฒฝ๋ณ์ ์ฐ์ , ์์ผ๋ฉด ์๋ ๊ธฐ๋ณธ๊ฐ ์ฌ์ฉ
|
| 3437 |
+
LLM_PROVIDER = "anthropic"
|
| 3438 |
+
LLM_MODEL = os.environ.get("LLM_MODEL", "claude-sonnet-4-5")
|
| 3439 |
+
LLM_API_KEY = os.environ.get(
|
| 3440 |
+
"ANTHROPIC_API_KEY",
|
| 3441 |
+
"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"
|
| 3442 |
+
)
|
| 3443 |
LLM_MAX_TOKENS = int(os.environ.get("LLM_MAX_TOKENS", "1024"))
|
| 3444 |
+
LLM_TEMP = float(os.environ.get("LLM_TEMP", "0.4"))
|
| 3445 |
|
| 3446 |
SYSTEM_PROMPT = """๋น์ ์ ์ผ๊ตฌ ๋ฐ์ดํฐ ๋ถ์ ์ ๋ฌธ๊ฐ AI์
๋๋ค.
|
| 3447 |
KBO, NPB, ๋ง์ด๋๋ฆฌ๊ทธ ํฌ์ ๋ฐ์ดํฐ๋ฅผ ๋ถ์ํ๊ณ ์ธ์ฌ์ดํธ๋ฅผ ์ ๊ณตํฉ๋๋ค.
|
|
|
|
| 3452 |
"""
|
| 3453 |
|
| 3454 |
|
| 3455 |
+
# โโ Anthropic (Claude) ํธ์ถ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3456 |
def _call_anthropic(question, context, history):
|
| 3457 |
import anthropic
|
| 3458 |
+
|
| 3459 |
client = anthropic.Anthropic(api_key=LLM_API_KEY)
|
| 3460 |
|
| 3461 |
+
# user/assistant ๊ต๋ ๋ฉ์์ง ๊ตฌ์ฑ
|
| 3462 |
messages = []
|
| 3463 |
for h in (history or [])[-10:]:
|
| 3464 |
+
if h["role"] in ("user", "assistant"):
|
| 3465 |
+
messages.append({"role": h["role"], "content": h["content"]})
|
| 3466 |
+
|
| 3467 |
messages.append({
|
| 3468 |
"role": "user",
|
| 3469 |
"content": f"[๋ฐ์ดํฐ ์ปจํ
์คํธ]\n{context}\n\n[์ง๋ฌธ]\n{question}",
|
|
|
|
| 3478 |
return resp.content[0].text.strip()
|
| 3479 |
|
| 3480 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3481 |
# โโ ํตํฉ ํธ์ถ ํจ์ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 3482 |
def call_llm_api(question, context="", history=None):
|
| 3483 |
"""
|
| 3484 |
+
Claude API๋ฅผ ํธ์ถํฉ๋๋ค.
|
| 3485 |
+
API ํค ๋ฏธ์ค์ ์ ๊ท์น ๊ธฐ๋ฐ ํด๋ฐฑ์ ๋ฐํํฉ๋๋ค.
|
| 3486 |
"""
|
| 3487 |
+
if not LLM_API_KEY:
|
| 3488 |
return _rule_based_answer(question, context)
|
| 3489 |
|
| 3490 |
try:
|
| 3491 |
+
return _call_anthropic(question, context, history)
|
| 3492 |
+
except ImportError:
|
| 3493 |
+
return (
|
| 3494 |
+
"โ ๏ธ anthropic ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ค์น๋์ง ์์์ต๋๋ค.\n"
|
| 3495 |
+
"`pip install anthropic` ์ ์คํํด ์ฃผ์ธ์."
|
| 3496 |
+
)
|
|
|
|
|
|
|
| 3497 |
except Exception as e:
|
| 3498 |
+
return f"โ ๏ธ Claude API ํธ์ถ ์ค๋ฅ: {e}"
|
| 3499 |
|
| 3500 |
|
| 3501 |
# โโ ๊ท์น ๊ธฐ๋ฐ ํด๋ฐฑ (API ํค ์์ ๋) โโโโโโโโโโโโโโโโโโโโโโ
|
| 3502 |
def _rule_based_answer(question, context):
|
| 3503 |
q = question.lower()
|
| 3504 |
|
|
|
|
| 3505 |
ctx = {}
|
| 3506 |
for part in context.split("|"):
|
| 3507 |
part = part.strip()
|
|
|
|
| 3509 |
k, v = part.split("=", 1)
|
| 3510 |
ctx[k.strip()] = v.strip()
|
| 3511 |
|
| 3512 |
+
pitcher = ctx.get("ํฌ์", "ํด๋น ํฌ์")
|
| 3513 |
+
era = ctx.get("ERA", "โ")
|
| 3514 |
+
k9 = ctx.get("K9", "โ")
|
| 3515 |
+
bb9 = ctx.get("BB9", "โ")
|
| 3516 |
+
whip = ctx.get("WHIP", "โ")
|
| 3517 |
+
fip = ctx.get("FIP", "โ")
|
| 3518 |
+
spd = ctx.get("ํ๊ท ๊ตฌ์","โ")
|
| 3519 |
+
pitches = ctx.get("์ฃผ๋ ฅ ๊ตฌ์ข
","โ")
|
| 3520 |
|
| 3521 |
if any(w in q for w in ["๊ตฌ์ข
", "์ฃผ๋ ฅ", "pitch"]):
|
| 3522 |
+
try:
|
| 3523 |
+
k9_val = float(k9)
|
| 3524 |
+
k9_txt = "ํ์ผ์ง ๋ฅ๋ ฅ์ด ์ฐ์ํฉ๋๋ค." if k9_val > 8 else "์ ๊ตฌ๋ ฅ ์ค์ฌ์ ํฌ์์
๋๋ค."
|
| 3525 |
+
except ValueError:
|
| 3526 |
+
k9_txt = "์ ๊ตฌ๋ ฅ ์ค์ฌ์ ํฌ์์
๋๋ค."
|
| 3527 |
+
return (
|
| 3528 |
+
f"**{pitcher}**์ ์ฃผ๋ ฅ ๊ตฌ์ข
์ **{pitches}** ์
๋๋ค.\n\n"
|
| 3529 |
+
f"ํ๊ท ๊ตฌ์์ **{spd} km/h** ์ด๋ฉฐ, "
|
| 3530 |
+
f"K9={k9}, BB9={bb9} ๊ธฐ์ค์ผ๋ก {k9_txt}"
|
| 3531 |
+
)
|
| 3532 |
|
| 3533 |
if any(w in q for w in ["์ฝ์ ", "์นด์ดํธ", "count"]):
|
| 3534 |
+
return (
|
| 3535 |
+
"์นด์ดํธ๋ณ ์ฝ์ ๋ถ์์ **์นด์ดํธ๋ณ ํญ**์์ ํํธ๋งต์ ํ์ธํด ์ฃผ์ธ์.\n\n"
|
| 3536 |
+
"์ผ๋ฐ์ ์ผ๋ก ๋ณผ์นด์ดํธ๊ฐ ๋ถ๋ฆฌํ ๋(2-0, 3-1) ํจ์คํธ๋ณผ ๋น์จ์ด ๋์์ง๋ฏ๋ก "
|
| 3537 |
+
"ํ์๊ฐ ๋
ธ๋ฆฌ๊ธฐ ์ฌ์ด ๊ฒฝํฅ์ด ์์ต๋๋ค."
|
| 3538 |
+
)
|
| 3539 |
|
| 3540 |
if any(w in q for w in ["์ขํ", "์ฐํ", "stand", "ํ์ ์ ํ"]):
|
| 3541 |
+
return (
|
| 3542 |
+
"์ขํ/์ฐํ ๋ถ์์ **์ฌ์ด๋๋ฐ์ ํ์ ์ ํ ํํฐ**๋ฅผ ๋ณ๊ฒฝํ์ฌ "
|
| 3543 |
+
"๊ฐ ํญ์์ ๋น๊ตํด ๋ณด์ธ์.\n\n"
|
| 3544 |
+
"๋ฌด๋ธ๋จผํธ ์ฐจํธ์ ๋ก์ผ์ด์
์ฐจํธ์์ ์ ํ๋ณ ์ฐจ์ด๋ฅผ ํ์ธํ ์ ์์ต๋๋ค."
|
| 3545 |
+
)
|
| 3546 |
|
| 3547 |
if any(w in q for w in ["๊ตฌ์", "์๋", "speed", "ํธ๋ ๋"]):
|
| 3548 |
+
return (
|
| 3549 |
+
f"**{pitcher}**์ ํ๊ท ๊ตฌ์์ **{spd} km/h** ์
๋๋ค.\n\n"
|
| 3550 |
+
"๊ฒ์๋ก๊ทธ ํญ์์ ๋ ์ง๋ณ ๊ตฌ์ ์ถ์ด๋ฅผ ํ์ธํ๊ฑฐ๋, "
|
| 3551 |
+
"๋ฌด๋ธ๋จผํธ ํญ์ ๊ตฌ์ vs ๋ฌด๋ธ๋จผํธ ์ค์บํฐ ์ฐจํธ๋ฅผ ์ฐธ๊ณ ํ์ธ์."
|
| 3552 |
+
)
|
| 3553 |
|
| 3554 |
if any(w in q for w in ["๋ฆฌ๊ทธ", "๋น๊ต", "์์", "rank"]):
|
| 3555 |
+
return (
|
| 3556 |
+
f"**{pitcher}** ๋ฆฌ๊ทธ ๋น๊ต ์งํ:\n\n"
|
| 3557 |
+
f"- ERA: **{era}**\n"
|
| 3558 |
+
f"- FIP: **{fip}**\n"
|
| 3559 |
+
f"- WHIP: **{whip}**\n"
|
| 3560 |
+
f"- K9: **{k9}**\n"
|
| 3561 |
+
f"- BB9: **{bb9}**\n\n"
|
| 3562 |
+
"์์ธํ ๋ฆฌ๊ทธ ์์๋ **๋ฆฌ๊ทธ๋น๊ต ํญ**์ ํ์ธํด ์ฃผ์ธ์."
|
| 3563 |
+
)
|
| 3564 |
|
| 3565 |
if any(w in q for w in ["๊ฐ์ ", "์ํ", "good", "best"]):
|
| 3566 |
+
return (
|
| 3567 |
+
f"**{pitcher}**์ ์ฃผ์ ์งํ:\n\n"
|
| 3568 |
+
f"ERA={era}, K9={k9}, BB9={bb9}, WHIP={whip}\n\n"
|
| 3569 |
+
"์์น๊ฐ ๋ฎ์ ERA/WHIP/BB9์ ๋์ K9์ด ๊ฐ์ ์งํ์
๋๋ค. "
|
| 3570 |
+
"๋ฆฌ๊ทธ๋น๊ต ํญ์์ ํ๊ท ๋๋น ์ฐ์ ํญ๋ชฉ์ ํ์ธํ์ธ์."
|
| 3571 |
+
)
|
| 3572 |
+
|
| 3573 |
+
return (
|
| 3574 |
+
f"**{pitcher}** ({ctx.get('์ฐ๋','โ')}, {ctx.get('๋ฆฌ๊ทธ','โ')}) ๊ธฐ๋ณธ ์คํฏ:\n\n"
|
| 3575 |
+
f"| ์งํ | ๊ฐ |\n|------|----|\n"
|
| 3576 |
+
f"| ERA | {era} |\n"
|
| 3577 |
+
f"| FIP | {fip} |\n"
|
| 3578 |
+
f"| WHIP | {whip} |\n"
|
| 3579 |
+
f"| K9 | {k9} |\n"
|
| 3580 |
+
f"| BB9 | {bb9} |\n"
|
| 3581 |
+
f"| ํ๊ท ๊ตฌ์ | {spd} |\n\n"
|
| 3582 |
+
"๋ ๊ตฌ์ฒด์ ์ธ ์ง๋ฌธ์ ํด์ฃผ์๋ฉด ๋ ์ ํํ ๋ถ์์ ๋๋ฆด ์ ์์ต๋๋ค. ๐"
|
| 3583 |
+
)
|
| 3584 |
|
| 3585 |
|
| 3586 |
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|