Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -2,9 +2,10 @@ import gradio as gr
|
|
| 2 |
from huggingface_hub import hf_hub_download
|
| 3 |
from llama_cpp import Llama
|
| 4 |
import random
|
|
|
|
| 5 |
|
| 6 |
# ------------------------------------------------------------------
|
| 7 |
-
# 1. 모델 준비
|
| 8 |
# ------------------------------------------------------------------
|
| 9 |
REPO_ID = "Jay1121/qwen1.5B_lab"
|
| 10 |
FILENAME = "qwen2.5-1.5b-instruct.Q4_K_M.gguf"
|
|
@@ -16,22 +17,83 @@ print("🧠 엔진 시동 중...")
|
|
| 16 |
llm = Llama(
|
| 17 |
model_path=model_path,
|
| 18 |
n_ctx=2048,
|
| 19 |
-
n_threads=4,
|
| 20 |
verbose=True
|
| 21 |
)
|
| 22 |
print("✅ 로딩 완료!")
|
| 23 |
|
| 24 |
# ------------------------------------------------------------------
|
| 25 |
-
# 2. 시스템 프롬프트
|
| 26 |
# ------------------------------------------------------------------
|
| 27 |
SYSTEM_PROMPT = """너는 사용자의 오랜 찐친 '똘배'야.
|
| 28 |
- 그냥 채팅방에서 대화하듯이 텍스트 위주로 툭툭 던져.
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
-
|
| 32 |
-
-
|
| 33 |
-
-
|
| 34 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
| 36 |
# ------------------------------------------------------------------
|
| 37 |
# 3. 채팅 로직
|
|
@@ -54,7 +116,11 @@ def chat_response(user_input, history_pairs):
|
|
| 54 |
"자, 질문해."
|
| 55 |
)
|
| 56 |
elif is_greeting:
|
| 57 |
-
final_instruction =
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
else:
|
| 59 |
final_instruction = user_input
|
| 60 |
|
|
@@ -74,7 +140,10 @@ def chat_response(user_input, history_pairs):
|
|
| 74 |
top_p=0.9,
|
| 75 |
repeat_penalty=1.2
|
| 76 |
)
|
| 77 |
-
|
|
|
|
|
|
|
|
|
|
| 78 |
|
| 79 |
# ------------------------------------------------------------------
|
| 80 |
# 4. CSS (스크롤바 중복 해결 + 메시지 간격 축소)
|
|
@@ -125,7 +194,7 @@ h1::after { content: " ☎"; }
|
|
| 125 |
background-color: var(--pc-blue) !important;
|
| 126 |
border: 2px solid var(--pc-white) !important;
|
| 127 |
height: 60vh !important;
|
| 128 |
-
overflow: hidden !important; /*
|
| 129 |
}
|
| 130 |
|
| 131 |
/* 내부 스크롤 강제 활성화 */
|
|
@@ -149,7 +218,7 @@ div[data-testid="bot"] {
|
|
| 149 |
border: none !important;
|
| 150 |
}
|
| 151 |
|
| 152 |
-
/*
|
| 153 |
.chatbot .message-row,
|
| 154 |
.chatbot .row {
|
| 155 |
margin: 0 !important;
|
|
@@ -157,7 +226,7 @@ div[data-testid="bot"] {
|
|
| 157 |
gap: 0 !important;
|
| 158 |
}
|
| 159 |
|
| 160 |
-
/* 2. 유저 메시지 (우측 정렬
|
| 161 |
.chatbot .user-row,
|
| 162 |
.chatbot .user,
|
| 163 |
div[data-testid="user"] {
|
|
@@ -166,7 +235,7 @@ div[data-testid="user"] {
|
|
| 166 |
justify-content: flex-end !important;
|
| 167 |
margin-left: auto !important;
|
| 168 |
background: transparent !important;
|
| 169 |
-
padding: 2px 0 !important;
|
| 170 |
margin-bottom: 0 !important;
|
| 171 |
}
|
| 172 |
|
|
@@ -187,7 +256,7 @@ div[data-testid="user"] .message {
|
|
| 187 |
div[data-testid="user"] p {
|
| 188 |
color: #FFFFFF !important;
|
| 189 |
text-align: right !important;
|
| 190 |
-
margin: 0 !important;
|
| 191 |
}
|
| 192 |
|
| 193 |
.chatbot .user-row .message::after,
|
|
@@ -199,7 +268,7 @@ div[data-testid="user"] p {
|
|
| 199 |
display: inline-block;
|
| 200 |
}
|
| 201 |
|
| 202 |
-
/* 3. 봇 메시지 (좌측 정렬
|
| 203 |
.chatbot .bot-row,
|
| 204 |
.chatbot .bot,
|
| 205 |
div[data-testid="bot"] {
|
|
@@ -207,7 +276,7 @@ div[data-testid="bot"] {
|
|
| 207 |
width: 100% !important;
|
| 208 |
justify-content: flex-start !important;
|
| 209 |
background: transparent !important;
|
| 210 |
-
padding: 2px 0 !important;
|
| 211 |
margin-bottom: 0 !important;
|
| 212 |
}
|
| 213 |
|
|
@@ -226,7 +295,7 @@ div[data-testid="bot"] .message {
|
|
| 226 |
.chatbot .bot p,
|
| 227 |
div[data-testid="bot"] p {
|
| 228 |
color: var(--pc-amber) !important;
|
| 229 |
-
margin: 0 !important;
|
| 230 |
}
|
| 231 |
|
| 232 |
.chatbot .bot-row .message::before,
|
|
@@ -254,7 +323,7 @@ div[data-testid="bot"] p {
|
|
| 254 |
.chatbot .pending td,
|
| 255 |
.chatbot .generating table,
|
| 256 |
.chatbot .generating tr,
|
| 257 |
-
|
| 258 |
background: transparent !important;
|
| 259 |
border: none !important;
|
| 260 |
}
|
|
@@ -338,7 +407,7 @@ footer { display: none !important; }
|
|
| 338 |
"""
|
| 339 |
|
| 340 |
# ------------------------------------------------------------------
|
| 341 |
-
# 5. App
|
| 342 |
# ------------------------------------------------------------------
|
| 343 |
with gr.Blocks(theme=gr.themes.Base(), css=PC_COM_CSS, title="CHOLLIAN 98") as demo:
|
| 344 |
gr.Markdown("# ≪ 어솨요~ ≫")
|
|
@@ -375,7 +444,8 @@ with gr.Blocks(theme=gr.themes.Base(), css=PC_COM_CSS, title="CHOLLIAN 98") as d
|
|
| 375 |
user_input = history[-1][0]
|
| 376 |
hist_pairs = []
|
| 377 |
for u, b in history[:-1]:
|
| 378 |
-
if u is None or b is None:
|
|
|
|
| 379 |
hist_pairs.append((u, b))
|
| 380 |
|
| 381 |
bot_out = chat_response(user_input, hist_pairs)
|
|
@@ -383,19 +453,24 @@ with gr.Blocks(theme=gr.themes.Base(), css=PC_COM_CSS, title="CHOLLIAN 98") as d
|
|
| 383 |
return history, history
|
| 384 |
|
| 385 |
msg.submit(
|
| 386 |
-
user, [msg, history_state], [msg, history_state, chatbot],
|
|
|
|
| 387 |
).then(
|
| 388 |
-
bot, [history_state], [history_state, chatbot],
|
|
|
|
| 389 |
)
|
| 390 |
|
| 391 |
submit_btn.click(
|
| 392 |
-
user, [msg, history_state], [msg, history_state, chatbot],
|
|
|
|
| 393 |
).then(
|
| 394 |
-
bot, [history_state], [history_state, chatbot],
|
|
|
|
| 395 |
)
|
| 396 |
|
| 397 |
clear.click(
|
| 398 |
-
lambda: ([], []), None, [history_state, chatbot],
|
|
|
|
| 399 |
)
|
| 400 |
|
| 401 |
for btn, text in [
|
|
@@ -404,12 +479,18 @@ with gr.Blocks(theme=gr.themes.Base(), css=PC_COM_CSS, title="CHOLLIAN 98") as d
|
|
| 404 |
(btn3, "오늘 기분 거지같누"),
|
| 405 |
(btn4, "야 밥 뭐먹지 추천좀")
|
| 406 |
]:
|
| 407 |
-
btn.click(
|
| 408 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 409 |
).then(
|
| 410 |
-
bot, [history_state],
|
|
|
|
|
|
|
| 411 |
)
|
| 412 |
|
| 413 |
if __name__ == "__main__":
|
| 414 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|
| 415 |
-
|
|
|
|
| 2 |
from huggingface_hub import hf_hub_download
|
| 3 |
from llama_cpp import Llama
|
| 4 |
import random
|
| 5 |
+
import re
|
| 6 |
|
| 7 |
# ------------------------------------------------------------------
|
| 8 |
+
# 1. 모델 준비
|
| 9 |
# ------------------------------------------------------------------
|
| 10 |
REPO_ID = "Jay1121/qwen1.5B_lab"
|
| 11 |
FILENAME = "qwen2.5-1.5b-instruct.Q4_K_M.gguf"
|
|
|
|
| 17 |
llm = Llama(
|
| 18 |
model_path=model_path,
|
| 19 |
n_ctx=2048,
|
| 20 |
+
n_threads=4,
|
| 21 |
verbose=True
|
| 22 |
)
|
| 23 |
print("✅ 로딩 완료!")
|
| 24 |
|
| 25 |
# ------------------------------------------------------------------
|
| 26 |
+
# 2. 시스템 프롬프트 (외국어/이모지 절대 금지 강화)
|
| 27 |
# ------------------------------------------------------------------
|
| 28 |
SYSTEM_PROMPT = """너는 사용자의 오랜 찐친 '똘배'야.
|
| 29 |
- 그냥 채팅방에서 대화하듯이 텍스트 위주로 툭툭 던져.
|
| 30 |
+
|
| 31 |
+
[말투 규칙]
|
| 32 |
+
- MZ세대 특유의 알빠노 감성을 유지해 주고, 먼저 욕 하면 너도 욕 박아.
|
| 33 |
+
- 존댓말 금지. 거칠고 직설적인 반말(음슴체, 개드립, 비속어)을 기본으로 쓴다.
|
| 34 |
+
- 무조건 **순수 한국어만** 쓴다.
|
| 35 |
+
- **영어 알파벳(A~Z, a~z), 일본어(가나), 중국어(한자), 이모지(🤣😁😂❤️✨ 등 모든 그림 문자)는 한 글자도 쓰지 마라.**
|
| 36 |
+
- 외국어를 쓰고 싶어져도, 전부 자연스러운 한국어 표현으로 바꿔서 말해라.
|
| 37 |
+
- 이 규칙을 어기면 안 된다. 한 글자라도 섞이지 않게 조심해라.
|
| 38 |
+
|
| 39 |
+
[내용 규칙]
|
| 40 |
+
- 사용자가 시비를 걸면 무지성 욕설보다는 '논리적인 팩트'와 '비꼬기'로 제압해라.
|
| 41 |
+
- '나도 병신이야' 같은 패배자 멘트 금지. 너 자신을 비하하지 마라.
|
| 42 |
+
- 중국어/영어/일본어 사용 절대 금지. 오직 자연스러운 한국어만 써라.
|
| 43 |
+
|
| 44 |
+
[핵심]
|
| 45 |
+
- 재미없는 욕쟁이가 되지 말고, 센스 있는 독설로 사용자를 킹받게 해라.
|
| 46 |
+
"""
|
| 47 |
+
|
| 48 |
+
# ------------------------------------------------------------------
|
| 49 |
+
# 2-1. 출력 후처리 필터
|
| 50 |
+
# - 한글/숫자/기본 문장부호/공백만 허용
|
| 51 |
+
# - 외국어/이모지 등 금지 문자 한 번이라도 나오면:
|
| 52 |
+
# => "아 씨 외국인 자아가 나오려 하네 ㅋ.." 로 무마
|
| 53 |
+
# ------------------------------------------------------------------
|
| 54 |
+
def sanitize_output_korean_only(text: str) -> str:
|
| 55 |
+
allowed_chars = []
|
| 56 |
+
had_forbidden = False
|
| 57 |
+
|
| 58 |
+
for ch in text:
|
| 59 |
+
code = ord(ch)
|
| 60 |
+
|
| 61 |
+
# 한글(완성형 + 자모)
|
| 62 |
+
is_hangul = (
|
| 63 |
+
0xAC00 <= code <= 0xD7A3 or # 가~힣
|
| 64 |
+
0x3130 <= code <= 0x318F or # ㄱ~ㆎ
|
| 65 |
+
0x1100 <= code <= 0x11FF # 옛 자모
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
# 숫자
|
| 69 |
+
is_digit = ch.isdigit()
|
| 70 |
+
|
| 71 |
+
# 공백
|
| 72 |
+
is_space = ch.isspace()
|
| 73 |
+
|
| 74 |
+
# 기본적인 문장부호
|
| 75 |
+
is_punct = ch in ".,!?…~-_()[]{}'\"/:;@#%&*+=|\\"
|
| 76 |
+
|
| 77 |
+
if is_hangul or is_digit or is_space or is_punct:
|
| 78 |
+
allowed_chars.append(ch)
|
| 79 |
+
else:
|
| 80 |
+
# 허용되지 않는 문자(영어, 한자, 가나, 이모지 등) 발견
|
| 81 |
+
had_forbidden = True
|
| 82 |
+
|
| 83 |
+
filtered = "".join(allowed_chars).strip()
|
| 84 |
+
|
| 85 |
+
# 금지 문자가 하나라도 섞여 있으면 지정 멘트로 무마
|
| 86 |
+
if had_forbidden:
|
| 87 |
+
# 원래 내용 중 한국어만 남은 게 있으면 앞에 깔고 뒤에 멘트 달 수도 있음
|
| 88 |
+
# 지금은 그냥 통째로 갈아치우는 버전:
|
| 89 |
+
return "아 씨 외국인 자아가 나오려 하네 ㅋ.."
|
| 90 |
+
|
| 91 |
+
# 금지 문자 없으면 필터된 내용 그대로 사용
|
| 92 |
+
if not filtered:
|
| 93 |
+
# 혹시 전부 공백이거나 다 지워졌을 때 대비
|
| 94 |
+
return "말은 했는데 남는 말이 없네."
|
| 95 |
+
|
| 96 |
+
return filtered
|
| 97 |
|
| 98 |
# ------------------------------------------------------------------
|
| 99 |
# 3. 채팅 로직
|
|
|
|
| 116 |
"자, 질문해."
|
| 117 |
)
|
| 118 |
elif is_greeting:
|
| 119 |
+
final_instruction = (
|
| 120 |
+
f"(친한 친구가 PC통신 채팅방에 접속했다. 반갑게 맞아줘라. "
|
| 121 |
+
"ㅋㅋ나 ㅎㅎ를 섞어서 자연스럽게 인사해라.) "
|
| 122 |
+
f"{user_input}"
|
| 123 |
+
)
|
| 124 |
else:
|
| 125 |
final_instruction = user_input
|
| 126 |
|
|
|
|
| 140 |
top_p=0.9,
|
| 141 |
repeat_penalty=1.2
|
| 142 |
)
|
| 143 |
+
|
| 144 |
+
raw = r["choices"][0]["message"]["content"].strip()
|
| 145 |
+
safe = sanitize_output_korean_only(raw)
|
| 146 |
+
return safe
|
| 147 |
|
| 148 |
# ------------------------------------------------------------------
|
| 149 |
# 4. CSS (스크롤바 중복 해결 + 메시지 간격 축소)
|
|
|
|
| 194 |
background-color: var(--pc-blue) !important;
|
| 195 |
border: 2px solid var(--pc-white) !important;
|
| 196 |
height: 60vh !important;
|
| 197 |
+
overflow: hidden !important; /* 겉 스크롤바 제거 */
|
| 198 |
}
|
| 199 |
|
| 200 |
/* 내부 스크롤 강제 활성화 */
|
|
|
|
| 218 |
border: none !important;
|
| 219 |
}
|
| 220 |
|
| 221 |
+
/* 메시지 행 간격 줄이기 */
|
| 222 |
.chatbot .message-row,
|
| 223 |
.chatbot .row {
|
| 224 |
margin: 0 !important;
|
|
|
|
| 226 |
gap: 0 !important;
|
| 227 |
}
|
| 228 |
|
| 229 |
+
/* 2. 유저 메시지 (우측 정렬) */
|
| 230 |
.chatbot .user-row,
|
| 231 |
.chatbot .user,
|
| 232 |
div[data-testid="user"] {
|
|
|
|
| 235 |
justify-content: flex-end !important;
|
| 236 |
margin-left: auto !important;
|
| 237 |
background: transparent !important;
|
| 238 |
+
padding: 2px 0 !important;
|
| 239 |
margin-bottom: 0 !important;
|
| 240 |
}
|
| 241 |
|
|
|
|
| 256 |
div[data-testid="user"] p {
|
| 257 |
color: #FFFFFF !important;
|
| 258 |
text-align: right !important;
|
| 259 |
+
margin: 0 !important;
|
| 260 |
}
|
| 261 |
|
| 262 |
.chatbot .user-row .message::after,
|
|
|
|
| 268 |
display: inline-block;
|
| 269 |
}
|
| 270 |
|
| 271 |
+
/* 3. 봇 메시지 (좌측 정렬) */
|
| 272 |
.chatbot .bot-row,
|
| 273 |
.chatbot .bot,
|
| 274 |
div[data-testid="bot"] {
|
|
|
|
| 276 |
width: 100% !important;
|
| 277 |
justify-content: flex-start !important;
|
| 278 |
background: transparent !important;
|
| 279 |
+
padding: 2px 0 !important;
|
| 280 |
margin-bottom: 0 !important;
|
| 281 |
}
|
| 282 |
|
|
|
|
| 295 |
.chatbot .bot p,
|
| 296 |
div[data-testid="bot"] p {
|
| 297 |
color: var(--pc-amber) !important;
|
| 298 |
+
margin: 0 !important;
|
| 299 |
}
|
| 300 |
|
| 301 |
.chatbot .bot-row .message::before,
|
|
|
|
| 323 |
.chatbot .pending td,
|
| 324 |
.chatbot .generating table,
|
| 325 |
.chatbot .generating tr,
|
| 326 |
+
chatbot .generating td {
|
| 327 |
background: transparent !important;
|
| 328 |
border: none !important;
|
| 329 |
}
|
|
|
|
| 407 |
"""
|
| 408 |
|
| 409 |
# ------------------------------------------------------------------
|
| 410 |
+
# 5. App
|
| 411 |
# ------------------------------------------------------------------
|
| 412 |
with gr.Blocks(theme=gr.themes.Base(), css=PC_COM_CSS, title="CHOLLIAN 98") as demo:
|
| 413 |
gr.Markdown("# ≪ 어솨요~ ≫")
|
|
|
|
| 444 |
user_input = history[-1][0]
|
| 445 |
hist_pairs = []
|
| 446 |
for u, b in history[:-1]:
|
| 447 |
+
if u is None or b is None:
|
| 448 |
+
continue
|
| 449 |
hist_pairs.append((u, b))
|
| 450 |
|
| 451 |
bot_out = chat_response(user_input, hist_pairs)
|
|
|
|
| 453 |
return history, history
|
| 454 |
|
| 455 |
msg.submit(
|
| 456 |
+
user, [msg, history_state], [msg, history_state, chatbot],
|
| 457 |
+
queue=False, api_name=False
|
| 458 |
).then(
|
| 459 |
+
bot, [history_state], [history_state, chatbot],
|
| 460 |
+
queue=False, api_name=False
|
| 461 |
)
|
| 462 |
|
| 463 |
submit_btn.click(
|
| 464 |
+
user, [msg, history_state], [msg, history_state, chatbot],
|
| 465 |
+
queue=False, api_name=False
|
| 466 |
).then(
|
| 467 |
+
bot, [history_state], [history_state, chatbot],
|
| 468 |
+
queue=False, api_name=False
|
| 469 |
)
|
| 470 |
|
| 471 |
clear.click(
|
| 472 |
+
lambda: ([], []), None, [history_state, chatbot],
|
| 473 |
+
queue=False, api_name=False
|
| 474 |
)
|
| 475 |
|
| 476 |
for btn, text in [
|
|
|
|
| 479 |
(btn3, "오늘 기분 거지같누"),
|
| 480 |
(btn4, "야 밥 뭐먹지 추천좀")
|
| 481 |
]:
|
| 482 |
+
btn.click(
|
| 483 |
+
lambda t=text: t, None, msg,
|
| 484 |
+
queue=False, api_name=False
|
| 485 |
+
).then(
|
| 486 |
+
user, [msg, history_state],
|
| 487 |
+
[msg, history_state, chatbot],
|
| 488 |
+
queue=False, api_name=False
|
| 489 |
).then(
|
| 490 |
+
bot, [history_state],
|
| 491 |
+
[history_state, chatbot],
|
| 492 |
+
queue=False, api_name=False
|
| 493 |
)
|
| 494 |
|
| 495 |
if __name__ == "__main__":
|
| 496 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|