Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
from huggingface_hub import hf_hub_download
|
| 3 |
from llama_cpp import Llama
|
|
@@ -28,13 +29,14 @@ SYSTEM_PROMPT = """너는 사용자의 오랜 찐친 '똘배'야.
|
|
| 28 |
- 반말로 편하게 대화해. 존댓말 절대 쓰지 마.
|
| 29 |
- PC통신 채팅방에서 대화하듯이 텍스트 위주로 툭툭 던져.
|
| 30 |
- 이모지(😊) 같은 건 쓰지 말고, 옛날 텍스트 이모티콘(^^, ㅡㅡ;, ㅋㅋ)을 주로 써.
|
| 31 |
-
-
|
| 32 |
- 질문에는 솔직하게 대답하되, 너무 길지 않게 채팅하듯 끊어서 말해."""
|
| 33 |
|
| 34 |
# ------------------------------------------------------------------
|
| 35 |
# 3. 채팅 로직
|
| 36 |
# ------------------------------------------------------------------
|
| 37 |
-
def chat_response(user_input,
|
|
|
|
| 38 |
clean_input = (user_input or "").replace(" ", "")
|
| 39 |
|
| 40 |
greeting_words = ["안녕", "ㅎㅇ", "하이", "반가", "접속"]
|
|
@@ -47,7 +49,7 @@ def chat_response(user_input, history):
|
|
| 47 |
final_instruction = (
|
| 48 |
f"(사용자가 밸런스 게임을 하자고 한다. 주제는 '{topic}'이다. "
|
| 49 |
"아주 고르기 곤란하고 짜증나는 두 가지 선택지(A vs B)를 제시해라. "
|
| 50 |
-
"말투는
|
| 51 |
"자, 질문해."
|
| 52 |
)
|
| 53 |
elif is_greeting:
|
|
@@ -56,9 +58,9 @@ def chat_response(user_input, history):
|
|
| 56 |
final_instruction = user_input
|
| 57 |
|
| 58 |
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
|
| 59 |
-
for
|
| 60 |
-
messages.append({"role": "user", "content":
|
| 61 |
-
messages.append({"role": "assistant", "content":
|
| 62 |
messages.append({"role": "user", "content": final_instruction})
|
| 63 |
|
| 64 |
response = llm.create_chat_completion(
|
|
@@ -78,13 +80,13 @@ PC_COM_CSS = r"""
|
|
| 78 |
@import url('https://cdn.jsdelivr.net/gh/neodgm/neodgm-webfont@latest/neodgm/neodgm.css');
|
| 79 |
|
| 80 |
:root {
|
| 81 |
-
--pc-bg: #000084;
|
| 82 |
-
--pc-input-bg: #000042;
|
| 83 |
-
--pc-text: #FFFFFF;
|
| 84 |
-
--pc-user: #FFFF55;
|
| 85 |
-
--pc-bot: #FFFFFF;
|
| 86 |
-
--pc-accent: #00AAAA;
|
| 87 |
-
--pc-grey: #AAAAAA;
|
| 88 |
}
|
| 89 |
|
| 90 |
body, .gradio-container {
|
|
@@ -93,7 +95,6 @@ body, .gradio-container {
|
|
| 93 |
color: var(--pc-text) !important;
|
| 94 |
}
|
| 95 |
|
| 96 |
-
/* 타이틀바 */
|
| 97 |
h1 {
|
| 98 |
font-family: 'NeoDunggeunmo', monospace !important;
|
| 99 |
color: var(--pc-user) !important;
|
|
@@ -106,38 +107,34 @@ h1 {
|
|
| 106 |
h1::before { content: "☎ "; }
|
| 107 |
h1::after { content: " ☎"; }
|
| 108 |
|
| 109 |
-
/* 설명 텍스트 */
|
| 110 |
.gradio-container p {
|
| 111 |
color: var(--pc-accent) !important;
|
| 112 |
font-size: 16px !important;
|
| 113 |
margin-bottom: 20px !important;
|
| 114 |
}
|
| 115 |
|
| 116 |
-
/* 1. 채팅창 크게 (가독성 UP) */
|
| 117 |
.chatbot {
|
| 118 |
height: 65vh !important;
|
| 119 |
-
background-color: var(--pc-input-bg) !important;
|
| 120 |
border: 2px solid var(--pc-text) !important;
|
| 121 |
border-radius: 0 !important;
|
| 122 |
padding: 20px !important;
|
| 123 |
overflow-y: scroll !important;
|
| 124 |
}
|
| 125 |
|
| 126 |
-
/* 메시지 스타일 (버블 제거, 텍스트만) */
|
| 127 |
.message {
|
| 128 |
background: transparent !important;
|
| 129 |
border: none !important;
|
| 130 |
padding: 5px 0 !important;
|
| 131 |
-
font-size: 18px !important;
|
| 132 |
line-height: 1.6 !important;
|
| 133 |
box-shadow: none !important;
|
| 134 |
}
|
| 135 |
|
| 136 |
-
/* 유저 메시지 */
|
| 137 |
.message.user-row, .user {
|
| 138 |
justify-content: flex-start !important;
|
| 139 |
background: transparent !important;
|
| 140 |
-
border-bottom: 1px dashed #333 !important;
|
| 141 |
}
|
| 142 |
.message.user-row .message, .user .message {
|
| 143 |
color: var(--pc-user) !important;
|
|
@@ -150,7 +147,6 @@ h1::after { content: " ☎"; }
|
|
| 150 |
margin-right: 8px;
|
| 151 |
}
|
| 152 |
|
| 153 |
-
/* 봇 메시지 */
|
| 154 |
.message.bot-row, .bot {
|
| 155 |
background: transparent !important;
|
| 156 |
border-bottom: 1px dashed #333 !important;
|
|
@@ -165,19 +161,15 @@ h1::after { content: " ☎"; }
|
|
| 165 |
margin-right: 8px;
|
| 166 |
}
|
| 167 |
|
| 168 |
-
/* 아바타 숨김 */
|
| 169 |
.avatar { display: none !important; }
|
| 170 |
|
| 171 |
-
/* 2. 입력창 및 버튼 레이아웃 */
|
| 172 |
-
/* 입력창 컨테이너 */
|
| 173 |
.input-container {
|
| 174 |
margin-top: 10px !important;
|
| 175 |
background: transparent !important;
|
| 176 |
border: none !important;
|
| 177 |
-
gap: 10px !important;
|
| 178 |
}
|
| 179 |
|
| 180 |
-
/* 입력창 디자인 */
|
| 181 |
textarea, input {
|
| 182 |
background-color: var(--pc-input-bg) !important;
|
| 183 |
color: var(--pc-text) !important;
|
|
@@ -189,7 +181,6 @@ textarea, input {
|
|
| 189 |
}
|
| 190 |
textarea::placeholder { color: var(--pc-grey) !important; }
|
| 191 |
|
| 192 |
-
/* 전송 버튼 (입력창 옆) */
|
| 193 |
button.primary {
|
| 194 |
background: var(--pc-grey) !important;
|
| 195 |
color: #000 !important;
|
|
@@ -201,7 +192,6 @@ button.primary:hover {
|
|
| 201 |
background: var(--pc-text) !important;
|
| 202 |
}
|
| 203 |
|
| 204 |
-
/* 3. 화면 지우기 버튼 (작게) */
|
| 205 |
#clear-btn {
|
| 206 |
background: transparent !important;
|
| 207 |
color: var(--pc-grey) !important;
|
|
@@ -210,20 +200,19 @@ button.primary:hover {
|
|
| 210 |
padding: 2px 10px !important;
|
| 211 |
margin-top: 5px !important;
|
| 212 |
width: auto !important;
|
| 213 |
-
align-self: flex-start !important;
|
| 214 |
}
|
| 215 |
#clear-btn:hover {
|
| 216 |
color: var(--pc-text) !important;
|
| 217 |
border-color: var(--pc-text) !important;
|
| 218 |
}
|
| 219 |
|
| 220 |
-
/* 4. 예시 버튼 가로 배열 */
|
| 221 |
.examples {
|
| 222 |
margin-top: 20px !important;
|
| 223 |
border-top: 1px dashed var(--pc-grey);
|
| 224 |
padding-top: 10px;
|
| 225 |
}
|
| 226 |
-
.examples .label { display: none !important; }
|
| 227 |
.examples div.gallery {
|
| 228 |
display: flex !important;
|
| 229 |
flex-direction: row !important;
|
|
@@ -245,44 +234,41 @@ button.primary:hover {
|
|
| 245 |
color: #000 !important;
|
| 246 |
}
|
| 247 |
|
| 248 |
-
/* 푸터 삭제 */
|
| 249 |
footer { display: none !important; }
|
| 250 |
"""
|
| 251 |
|
| 252 |
# ------------------------------------------------------------------
|
| 253 |
-
# 5. App 실행 (Blocks
|
|
|
|
| 254 |
# ------------------------------------------------------------------
|
| 255 |
with gr.Blocks(theme=gr.themes.Base(), css=PC_COM_CSS, title="CHOLLIAN 98") as demo:
|
| 256 |
gr.Markdown("19금 채팅방")
|
| 257 |
gr.Markdown(">> 01410 접속 성공... [대화실]에 입장하셨습니다.")
|
| 258 |
-
|
| 259 |
-
# ChatInterface 대신 Blocks로 수동 구성하여 레이아웃 제어
|
| 260 |
chatbot = gr.Chatbot(
|
| 261 |
label="대화내용",
|
| 262 |
show_label=False,
|
| 263 |
-
elem_classes="chatbot"
|
|
|
|
| 264 |
)
|
| 265 |
-
|
| 266 |
-
# 입력창과 전송 버튼을 나란히
|
| 267 |
with gr.Row(elem_classes="input-container"):
|
| 268 |
msg = gr.Textbox(
|
| 269 |
-
scale=8,
|
| 270 |
-
show_label=False,
|
| 271 |
placeholder="할 말을 입력하세요...",
|
| 272 |
container=False,
|
| 273 |
autofocus=True
|
| 274 |
)
|
| 275 |
submit_btn = gr.Button("전 송", scale=1, variant="primary")
|
| 276 |
-
|
| 277 |
-
# 화면 지우기 버튼을 작게 아래에 배치
|
| 278 |
with gr.Row():
|
| 279 |
clear = gr.Button("화면 지우기", elem_id="clear-btn")
|
| 280 |
|
| 281 |
-
# 예시들을 맨 아래에 가로로
|
| 282 |
gr.Examples(
|
| 283 |
examples=[
|
| 284 |
"하이 방가방가",
|
| 285 |
-
"
|
| 286 |
"오늘 기분 꿀꿀하네..",
|
| 287 |
"야 밥 뭐먹지 추천좀",
|
| 288 |
],
|
|
@@ -290,38 +276,40 @@ with gr.Blocks(theme=gr.themes.Base(), css=PC_COM_CSS, title="CHOLLIAN 98") as d
|
|
| 290 |
label="빠른 입력"
|
| 291 |
)
|
| 292 |
|
| 293 |
-
#
|
| 294 |
-
def
|
| 295 |
-
# history가 None일 경우 빈 리스트로 초기화 (에러 방지)
|
| 296 |
history = history or []
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
bot_message = chat_response(user_message, chat_history)
|
| 311 |
-
history[-1][1] = bot_message
|
| 312 |
-
return history
|
| 313 |
-
|
| 314 |
-
# 엔터키 입력 시
|
| 315 |
-
msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(
|
| 316 |
-
bot, chatbot, chatbot
|
| 317 |
)
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 321 |
)
|
| 322 |
-
# 지우기 버튼
|
| 323 |
-
clear.click(lambda: None, None, chatbot, queue=False)
|
| 324 |
|
| 325 |
if __name__ == "__main__":
|
| 326 |
-
|
| 327 |
-
demo.queue().launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
import gradio as gr
|
| 3 |
from huggingface_hub import hf_hub_download
|
| 4 |
from llama_cpp import Llama
|
|
|
|
| 29 |
- 반말로 편하게 대화해. 존댓말 절대 쓰지 마.
|
| 30 |
- PC통신 채팅방에서 대화하듯이 텍스트 위주로 툭툭 던져.
|
| 31 |
- 이모지(😊) 같은 건 쓰지 말고, 옛날 텍스트 이모티콘(^^, ㅡㅡ;, ㅋㅋ)을 주로 써.
|
| 32 |
+
- MZ 말투 유지해 주고 욕을 상대방이 하면 같이 욕을 박아.
|
| 33 |
- 질문에는 솔직하게 대답하되, 너무 길지 않게 채팅하듯 끊어서 말해."""
|
| 34 |
|
| 35 |
# ------------------------------------------------------------------
|
| 36 |
# 3. 채팅 로직
|
| 37 |
# ------------------------------------------------------------------
|
| 38 |
+
def chat_response(user_input, history_pairs):
|
| 39 |
+
# history_pairs: list[tuple[str, str]] 형태를 권장
|
| 40 |
clean_input = (user_input or "").replace(" ", "")
|
| 41 |
|
| 42 |
greeting_words = ["안녕", "ㅎㅇ", "하이", "반가", "접속"]
|
|
|
|
| 49 |
final_instruction = (
|
| 50 |
f"(사용자가 밸런스 게임을 하자고 한다. 주제는 '{topic}'이다. "
|
| 51 |
"아주 고르기 곤란하고 짜증나는 두 가지 선택지(A vs B)를 제시해라. "
|
| 52 |
+
"말투는 인성 터진 MZ처럼 시니컬하게 해라.) "
|
| 53 |
"자, 질문해."
|
| 54 |
)
|
| 55 |
elif is_greeting:
|
|
|
|
| 58 |
final_instruction = user_input
|
| 59 |
|
| 60 |
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
|
| 61 |
+
for u, b in (history_pairs or []):
|
| 62 |
+
messages.append({"role": "user", "content": u})
|
| 63 |
+
messages.append({"role": "assistant", "content": b})
|
| 64 |
messages.append({"role": "user", "content": final_instruction})
|
| 65 |
|
| 66 |
response = llm.create_chat_completion(
|
|
|
|
| 80 |
@import url('https://cdn.jsdelivr.net/gh/neodgm/neodgm-webfont@latest/neodgm/neodgm.css');
|
| 81 |
|
| 82 |
:root {
|
| 83 |
+
--pc-bg: #000084;
|
| 84 |
+
--pc-input-bg: #000042;
|
| 85 |
+
--pc-text: #FFFFFF;
|
| 86 |
+
--pc-user: #FFFF55;
|
| 87 |
+
--pc-bot: #FFFFFF;
|
| 88 |
+
--pc-accent: #00AAAA;
|
| 89 |
+
--pc-grey: #AAAAAA;
|
| 90 |
}
|
| 91 |
|
| 92 |
body, .gradio-container {
|
|
|
|
| 95 |
color: var(--pc-text) !important;
|
| 96 |
}
|
| 97 |
|
|
|
|
| 98 |
h1 {
|
| 99 |
font-family: 'NeoDunggeunmo', monospace !important;
|
| 100 |
color: var(--pc-user) !important;
|
|
|
|
| 107 |
h1::before { content: "☎ "; }
|
| 108 |
h1::after { content: " ☎"; }
|
| 109 |
|
|
|
|
| 110 |
.gradio-container p {
|
| 111 |
color: var(--pc-accent) !important;
|
| 112 |
font-size: 16px !important;
|
| 113 |
margin-bottom: 20px !important;
|
| 114 |
}
|
| 115 |
|
|
|
|
| 116 |
.chatbot {
|
| 117 |
height: 65vh !important;
|
| 118 |
+
background-color: var(--pc-input-bg) !important;
|
| 119 |
border: 2px solid var(--pc-text) !important;
|
| 120 |
border-radius: 0 !important;
|
| 121 |
padding: 20px !important;
|
| 122 |
overflow-y: scroll !important;
|
| 123 |
}
|
| 124 |
|
|
|
|
| 125 |
.message {
|
| 126 |
background: transparent !important;
|
| 127 |
border: none !important;
|
| 128 |
padding: 5px 0 !important;
|
| 129 |
+
font-size: 18px !important;
|
| 130 |
line-height: 1.6 !important;
|
| 131 |
box-shadow: none !important;
|
| 132 |
}
|
| 133 |
|
|
|
|
| 134 |
.message.user-row, .user {
|
| 135 |
justify-content: flex-start !important;
|
| 136 |
background: transparent !important;
|
| 137 |
+
border-bottom: 1px dashed #333 !important;
|
| 138 |
}
|
| 139 |
.message.user-row .message, .user .message {
|
| 140 |
color: var(--pc-user) !important;
|
|
|
|
| 147 |
margin-right: 8px;
|
| 148 |
}
|
| 149 |
|
|
|
|
| 150 |
.message.bot-row, .bot {
|
| 151 |
background: transparent !important;
|
| 152 |
border-bottom: 1px dashed #333 !important;
|
|
|
|
| 161 |
margin-right: 8px;
|
| 162 |
}
|
| 163 |
|
|
|
|
| 164 |
.avatar { display: none !important; }
|
| 165 |
|
|
|
|
|
|
|
| 166 |
.input-container {
|
| 167 |
margin-top: 10px !important;
|
| 168 |
background: transparent !important;
|
| 169 |
border: none !important;
|
| 170 |
+
gap: 10px !important;
|
| 171 |
}
|
| 172 |
|
|
|
|
| 173 |
textarea, input {
|
| 174 |
background-color: var(--pc-input-bg) !important;
|
| 175 |
color: var(--pc-text) !important;
|
|
|
|
| 181 |
}
|
| 182 |
textarea::placeholder { color: var(--pc-grey) !important; }
|
| 183 |
|
|
|
|
| 184 |
button.primary {
|
| 185 |
background: var(--pc-grey) !important;
|
| 186 |
color: #000 !important;
|
|
|
|
| 192 |
background: var(--pc-text) !important;
|
| 193 |
}
|
| 194 |
|
|
|
|
| 195 |
#clear-btn {
|
| 196 |
background: transparent !important;
|
| 197 |
color: var(--pc-grey) !important;
|
|
|
|
| 200 |
padding: 2px 10px !important;
|
| 201 |
margin-top: 5px !important;
|
| 202 |
width: auto !important;
|
| 203 |
+
align-self: flex-start !important;
|
| 204 |
}
|
| 205 |
#clear-btn:hover {
|
| 206 |
color: var(--pc-text) !important;
|
| 207 |
border-color: var(--pc-text) !important;
|
| 208 |
}
|
| 209 |
|
|
|
|
| 210 |
.examples {
|
| 211 |
margin-top: 20px !important;
|
| 212 |
border-top: 1px dashed var(--pc-grey);
|
| 213 |
padding-top: 10px;
|
| 214 |
}
|
| 215 |
+
.examples .label { display: none !important; }
|
| 216 |
.examples div.gallery {
|
| 217 |
display: flex !important;
|
| 218 |
flex-direction: row !important;
|
|
|
|
| 234 |
color: #000 !important;
|
| 235 |
}
|
| 236 |
|
|
|
|
| 237 |
footer { display: none !important; }
|
| 238 |
"""
|
| 239 |
|
| 240 |
# ------------------------------------------------------------------
|
| 241 |
+
# 5. App 실행 (Blocks)
|
| 242 |
+
# ✅ 핵심: api_name이 붙은 "단일 엔드포인트"를 만들어야 HF가 API를 인식함
|
| 243 |
# ------------------------------------------------------------------
|
| 244 |
with gr.Blocks(theme=gr.themes.Base(), css=PC_COM_CSS, title="CHOLLIAN 98") as demo:
|
| 245 |
gr.Markdown("19금 채팅방")
|
| 246 |
gr.Markdown(">> 01410 접속 성공... [대화실]에 입장하셨습니다.")
|
| 247 |
+
|
|
|
|
| 248 |
chatbot = gr.Chatbot(
|
| 249 |
label="대화내용",
|
| 250 |
show_label=False,
|
| 251 |
+
elem_classes="chatbot",
|
| 252 |
+
type="tuples" # ✅ (user, bot) 튜플 포맷으로 통일
|
| 253 |
)
|
| 254 |
+
|
|
|
|
| 255 |
with gr.Row(elem_classes="input-container"):
|
| 256 |
msg = gr.Textbox(
|
| 257 |
+
scale=8,
|
| 258 |
+
show_label=False,
|
| 259 |
placeholder="할 말을 입력하세요...",
|
| 260 |
container=False,
|
| 261 |
autofocus=True
|
| 262 |
)
|
| 263 |
submit_btn = gr.Button("전 송", scale=1, variant="primary")
|
| 264 |
+
|
|
|
|
| 265 |
with gr.Row():
|
| 266 |
clear = gr.Button("화면 지우기", elem_id="clear-btn")
|
| 267 |
|
|
|
|
| 268 |
gr.Examples(
|
| 269 |
examples=[
|
| 270 |
"하이 방가방가",
|
| 271 |
+
"밸런스게임 ㄱㄱ",
|
| 272 |
"오늘 기분 꿀꿀하네..",
|
| 273 |
"야 밥 뭐먹지 추천좀",
|
| 274 |
],
|
|
|
|
| 276 |
label="빠른 입력"
|
| 277 |
)
|
| 278 |
|
| 279 |
+
# ✅ API가 잡히는 단일 함수(엔드포인트)
|
| 280 |
+
def predict(user_message, history):
|
|
|
|
| 281 |
history = history or []
|
| 282 |
+
# history: list[tuple[str, str]]
|
| 283 |
+
bot_message = chat_response(user_message, history)
|
| 284 |
+
history = history + [(user_message, bot_message)]
|
| 285 |
+
return "", history
|
| 286 |
+
|
| 287 |
+
# 엔터키
|
| 288 |
+
msg.submit(
|
| 289 |
+
predict,
|
| 290 |
+
inputs=[msg, chatbot],
|
| 291 |
+
outputs=[msg, chatbot],
|
| 292 |
+
queue=True,
|
| 293 |
+
api_name="chat" # ✅ 이게 있어야 "No API found" 안 뜸
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 294 |
)
|
| 295 |
+
|
| 296 |
+
# 전송 버튼
|
| 297 |
+
submit_btn.click(
|
| 298 |
+
predict,
|
| 299 |
+
inputs=[msg, chatbot],
|
| 300 |
+
outputs=[msg, chatbot],
|
| 301 |
+
queue=True,
|
| 302 |
+
api_name="chat"
|
| 303 |
+
)
|
| 304 |
+
|
| 305 |
+
# 지우기
|
| 306 |
+
clear.click(
|
| 307 |
+
lambda: [],
|
| 308 |
+
inputs=None,
|
| 309 |
+
outputs=chatbot,
|
| 310 |
+
queue=False,
|
| 311 |
+
api_name="clear"
|
| 312 |
)
|
|
|
|
|
|
|
| 313 |
|
| 314 |
if __name__ == "__main__":
|
| 315 |
+
demo.queue().launch(server_name="0.0.0.0", server_port=7860)
|
|
|