Spaces:
Sleeping
Sleeping
Jeong-hun Kim
commited on
Commit
ยท
aab927d
1
Parent(s):
52718fa
add "start with localhost", code refactored
Browse files- .vscode/settings.json +6 -0
- app.py +14 -138
- app/main.py +19 -0
- {app โ core}/__init__.py.py +0 -0
- core/context_manager.py +29 -0
- core/launch_gradio.py +56 -0
- core/make_pipeline.py +70 -0
- core/make_reply.py +68 -0
- {app โ core}/utils.py +0 -0
- howToStart.txt +6 -3
- requirements.txt +4 -4
- requirements_local.txt +69 -0
- todo.txt +2 -1
.vscode/settings.json
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"python.analysis.extraPaths": [
|
3 |
+
"."
|
4 |
+
],
|
5 |
+
"python.envFile": "${workspaceFolder}/.env"
|
6 |
+
}
|
app.py
CHANGED
@@ -1,145 +1,21 @@
|
|
1 |
-
from transformers import pipeline, AutoTokenizer, AutoModelForCausalLM
|
2 |
import gradio as gr
|
3 |
-
import torch
|
4 |
-
import re
|
5 |
-
import os
|
6 |
|
7 |
-
|
8 |
-
|
|
|
9 |
|
10 |
-
|
11 |
-
|
|
|
12 |
|
13 |
-
#
|
14 |
-
|
|
|
15 |
|
16 |
-
|
17 |
-
|
18 |
-
model_id,
|
19 |
-
token=access_token
|
20 |
-
)
|
21 |
-
model.eval()
|
22 |
-
llm = pipeline(
|
23 |
-
"text-generation",
|
24 |
-
model=model,
|
25 |
-
tokenizer=tokenizer,
|
26 |
-
torch_dtype=torch.float16
|
27 |
-
)
|
28 |
|
29 |
-
#
|
30 |
-
|
31 |
-
|
32 |
-
prompt = f.read().strip()
|
33 |
-
|
34 |
-
for turn in history[-16:]:
|
35 |
-
role = user_name if turn["role"] == "user" else bot_name
|
36 |
-
prompt += f"{role}: {turn['text']}\n"
|
37 |
-
|
38 |
-
prompt += f"{user_name}: {user_msg}\n"
|
39 |
-
prompt += f"{bot_name}:"
|
40 |
-
return prompt
|
41 |
-
|
42 |
-
# ์ถ๋ ฅ์์ ์๋ต ์ถ์ถ
|
43 |
-
def extract_response(full_text, prompt, bot_name="Tanjiro"):
|
44 |
-
if full_text.startswith(prompt):
|
45 |
-
reply = full_text[len(prompt):].strip()
|
46 |
-
else:
|
47 |
-
reply = full_text.split(f"{bot_name}:")[-1].strip()
|
48 |
-
user_token = "\nUser:"
|
49 |
-
if user_token in reply:
|
50 |
-
reply = reply.split(user_token)[0].strip()
|
51 |
-
return reply
|
52 |
-
|
53 |
-
# ์ถ๋ ฅ ์์ฑ ํจ์
|
54 |
-
def character_chat(user_msg, history):
|
55 |
-
print("[debug] generationg...")
|
56 |
-
prompt = build_prompt(history, user_msg)
|
57 |
-
outputs = llm(
|
58 |
-
prompt,
|
59 |
-
do_sample=True,
|
60 |
-
max_new_tokens=96,
|
61 |
-
temperature=0.7,
|
62 |
-
top_p=0.9,
|
63 |
-
repetition_penalty=1.05,
|
64 |
-
eos_token_id=tokenizer.eos_token_id,
|
65 |
-
return_full_text=True
|
66 |
-
)
|
67 |
-
full_text = outputs[0]['generated_text']
|
68 |
-
response = extract_response(full_text, prompt)
|
69 |
-
return response
|
70 |
-
|
71 |
-
# ์ค๋จ๋ ์๋ต ์ฌ๋ถ ๊ฒ์ฌ
|
72 |
-
def is_truncated_response(text: str) -> bool:
|
73 |
-
return re.search(r"[.?!โฆ\u2026\u2639\u263A\u2764\uD83D\uDE0A\uD83D\uDE22]$", text.strip()) is None
|
74 |
-
|
75 |
-
# ๋ต๋ณ ์ ํจ์ฑ ๊ฒ์ฌ
|
76 |
-
def is_valid_response(text: str, bot_name="Tanjiro", user_name="User") -> bool:
|
77 |
-
if user_name + ":" in text:
|
78 |
-
return False
|
79 |
-
if bot_name + ":" in text:
|
80 |
-
return False
|
81 |
-
return True
|
82 |
-
|
83 |
-
# ๋ต๋ณ ํ์ ์ ๋ฆฌ
|
84 |
-
def clean_response(text: str, bot_name="Tanjiro"):
|
85 |
-
return re.sub(rf"{bot_name}:\\s*", "", text).strip()
|
86 |
-
|
87 |
-
# Gradio ์ธํฐํ์ด์ค
|
88 |
-
with gr.Blocks(css="""
|
89 |
-
.chat-box { max-height: 500px; overflow-y: auto; padding: 10px; border: 1px solid #ccc; border-radius: 10px; }
|
90 |
-
.bubble-left { background-color: #f1f0f0; border-radius: 10px; padding: 10px; margin: 5px; max-width: 70%; float: left; clear: both; }
|
91 |
-
.bubble-right { background-color: #d1e7ff; border-radius: 10px; padding: 10px; margin: 5px; max-width: 70%; float: right; clear: both; text-align: right; }
|
92 |
-
.reset-btn-container { text-align: right; margin-bottom: 10px; }
|
93 |
-
""") as demo:
|
94 |
-
gr.Markdown("### ํ์ง๋ก์ ๋ํํ๊ธฐ")
|
95 |
-
with gr.Column():
|
96 |
-
with gr.Row():
|
97 |
-
gr.Markdown("")
|
98 |
-
reset_btn = gr.Button("๐ ๋ํ ์ด๊ธฐํ", elem_classes="reset-btn-container", scale=1)
|
99 |
-
chat_output = gr.HTML(elem_id="chat-box")
|
100 |
-
user_input = gr.Textbox(label="๋ฉ์์ง ์
๋ ฅ", placeholder="ํ์ง๋ก์๊ฒ ๋ง์ ๊ฑธ์ด๋ณด์ธ์")
|
101 |
-
state = gr.State([])
|
102 |
-
|
103 |
-
def render_chat(history):
|
104 |
-
html = ""
|
105 |
-
for item in history:
|
106 |
-
if item["role"] == "user":
|
107 |
-
html += f"<div class='bubble-right'>{item['text']}</div>"
|
108 |
-
elif item["role"] == "bot":
|
109 |
-
html += f"<div class='bubble-left'>{item['text']}</div>"
|
110 |
-
return gr.update(value=html)
|
111 |
-
|
112 |
-
def on_submit(user_msg, history):
|
113 |
-
history.append({"role": "user", "text": user_msg})
|
114 |
-
html = render_chat(history)
|
115 |
-
yield html, "", history
|
116 |
-
|
117 |
-
#์๋ต ์์ฑ
|
118 |
-
while True:
|
119 |
-
response = character_chat(user_msg, history)
|
120 |
-
if is_valid_response(response):
|
121 |
-
break
|
122 |
-
response = clean_response(response)
|
123 |
-
history.append({"role": "bot", "text": response})
|
124 |
-
|
125 |
-
#์ค๊ฐ์ ์๋ต์ด ๋๊ธด ๊ฒฝ์ฐ ์ถ๊ฐ ์์ฑ
|
126 |
-
if is_truncated_response(response):
|
127 |
-
while True:
|
128 |
-
continuation = character_chat(response, history)
|
129 |
-
if is_valid_response(continuation):
|
130 |
-
break
|
131 |
-
continuation = clean_response(continuation)
|
132 |
-
history.append({"role": "bot", "text": continuation})
|
133 |
-
|
134 |
-
html = render_chat(history)
|
135 |
-
yield html, "", history
|
136 |
-
|
137 |
-
def reset_chat():
|
138 |
-
return gr.update(value=""), "", []
|
139 |
-
|
140 |
-
user_input.submit(on_submit, inputs=[user_input, state], outputs=[chat_output, user_input, state], queue=True)
|
141 |
-
reset_btn.click(reset_chat, inputs=None, outputs=[chat_output, user_input, state])
|
142 |
-
|
143 |
-
#ํ๊น
ํ์ด์ค์์ ์คํ
|
144 |
-
demo.launch()
|
145 |
|
|
|
|
|
1 |
import gradio as gr
|
|
|
|
|
|
|
2 |
|
3 |
+
from core.make_pipeline import MakePipeline
|
4 |
+
from core.context_manager import ContextManager
|
5 |
+
from core.launch_gradio import create_interface
|
6 |
|
7 |
+
###########################
|
8 |
+
# Upload to Huggling Face #
|
9 |
+
###########################
|
10 |
|
11 |
+
# ๋ชจ๋ธ ๋ถ๋ฌ์ค๊ธฐ
|
12 |
+
makePipeline = MakePipeline()
|
13 |
+
makePipeline.build("hf")
|
14 |
|
15 |
+
# ์ฑํ
๊ธฐ๋ก ๊ด๋ฆฌ์
|
16 |
+
ctx = ContextManager()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
+
# Gradio ์ธํฐํ์ด์ค ์์
|
19 |
+
demo = create_interface(ctx, makePipeline)
|
20 |
+
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
app/main.py
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from core.make_pipeline import MakePipeline
|
2 |
+
from core.context_manager import ContextManager
|
3 |
+
from core.launch_gradio import create_interface
|
4 |
+
|
5 |
+
########################
|
6 |
+
# Start with localhost #
|
7 |
+
########################
|
8 |
+
|
9 |
+
if __name__ == "__main__":
|
10 |
+
# ๋ชจ๋ธ ๋ถ๋ฌ์ค๊ธฐ
|
11 |
+
makePipeline = MakePipeline()
|
12 |
+
makePipeline.build("hf")
|
13 |
+
|
14 |
+
# ์ฑํ
๊ธฐ๋ก ๊ด๋ฆฌ์
|
15 |
+
ctx = ContextManager()
|
16 |
+
|
17 |
+
# Gradio ์ธํฐํ์ด์ค ์์
|
18 |
+
demo = create_interface(ctx, makePipeline)
|
19 |
+
demo.launch()
|
{app โ core}/__init__.py.py
RENAMED
File without changes
|
core/context_manager.py
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
class ContextManager:
|
2 |
+
# ์ ์ญ ์์ ์ค์
|
3 |
+
USER_NAME = "User"
|
4 |
+
BOT_NAME = "Tanjiro"
|
5 |
+
|
6 |
+
def __init__(self):
|
7 |
+
self.user_name = self.USER_NAME
|
8 |
+
self.bot_name = self.BOT_NAME
|
9 |
+
self.history = []
|
10 |
+
|
11 |
+
def getUserName(self) -> str:
|
12 |
+
return self.user_name
|
13 |
+
|
14 |
+
def getBotName(self) -> str:
|
15 |
+
return self.bot_name
|
16 |
+
|
17 |
+
def getHistory(self) -> str:
|
18 |
+
return self.history
|
19 |
+
|
20 |
+
def setHistory(self, new_history: list):
|
21 |
+
self.history = new_history
|
22 |
+
|
23 |
+
# ๋ํ ๊ธฐ๋ก์ history์ ์ถ๊ฐ
|
24 |
+
def addHistory(self, role: str, text: str):
|
25 |
+
self.history.append({"role": role, "text": text})
|
26 |
+
|
27 |
+
# ๋ํ ๊ธฐ๋ก ์ด๊ธฐํ
|
28 |
+
def clearHistory(self):
|
29 |
+
self.history = []
|
core/launch_gradio.py
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from context_manager import ContextManager
|
3 |
+
from make_pipeline import MakePipeline
|
4 |
+
from make_reply import generate_reply
|
5 |
+
|
6 |
+
def create_interface(ctx: ContextManager, makePipeline: MakePipeline):
|
7 |
+
with gr.Blocks(css="""
|
8 |
+
.chat-box { max-height: 500px; overflow-y: auto; padding: 10px; border: 1px solid #ccc; border-radius: 10px; }
|
9 |
+
.bubble-left { background-color: #f1f0f0; border-radius: 10px; padding: 10px; margin: 5px; max-width: 70%; float: left; clear: both; }
|
10 |
+
.bubble-right { background-color: #d1e7ff; border-radius: 10px; padding: 10px; margin: 5px; max-width: 70%; float: right; clear: both; text-align: right; }
|
11 |
+
.reset-btn-container { text-align: right; margin-bottom: 10px; }
|
12 |
+
""") as demo:
|
13 |
+
gr.Markdown("### ํ์ง๋ก์ ๋ํํ๊ธฐ")
|
14 |
+
|
15 |
+
with gr.Column():
|
16 |
+
with gr.Row():
|
17 |
+
gr.Markdown("")
|
18 |
+
reset_btn = gr.Button("๐ ๋ํ ์ด๊ธฐํ", elem_classes="reset-btn-container", scale=1)
|
19 |
+
chat_output = gr.HTML(elem_id="chat-box")
|
20 |
+
user_input = gr.Textbox(label="๋ฉ์์ง ์
๋ ฅ", placeholder="ํ์ง๋ก์๊ฒ ๋ง์ ๊ฑธ์ด๋ณด์ธ์")
|
21 |
+
state = gr.State(ctx)
|
22 |
+
|
23 |
+
# history ์ฝ์ด์ ํ๋ฉด์ ๋ฟ๋ฆฌ๋ ์ญํ
|
24 |
+
def render_chat(ctx: ContextManager):
|
25 |
+
html = ""
|
26 |
+
for item in ctx.getHistory():
|
27 |
+
if item["role"] == ctx.getUserName():
|
28 |
+
html += f"<div class='bubble-right'>{item['text']}</div>"
|
29 |
+
elif item["role"] == ctx.getBotName():
|
30 |
+
html += f"<div class='bubble-left'>{item['text']}</div>"
|
31 |
+
return gr.update(value=html)
|
32 |
+
|
33 |
+
def on_submit(user_msg: str, ctx: ContextManager):
|
34 |
+
# ์ฌ์ฉ์ ๋ฉ์ธ์ง ์ถ๊ฐ
|
35 |
+
ctx.addMessage("user", user_msg)
|
36 |
+
|
37 |
+
# ์ ์ ๋ต๋ณ์ ํฌํจํ HTML ๋ ๋๋ง
|
38 |
+
html = render_chat(ctx)
|
39 |
+
yield html, "", ctx.getHistory()
|
40 |
+
|
41 |
+
# ๋ด ์๋ต ์์ฑ
|
42 |
+
generate_reply(ctx, makePipeline, user_msg)
|
43 |
+
|
44 |
+
# ๋ด ๋ต๋ณ์ ํฌํจํ HTML ๋ ๋๋ง
|
45 |
+
html = render_chat(ctx)
|
46 |
+
yield html, "", ctx.getHistory()
|
47 |
+
|
48 |
+
# history ์ด๊ธฐํ
|
49 |
+
def reset_chat():
|
50 |
+
ctx.resetHistory()
|
51 |
+
return gr.update(value=""), "", ctx.getHistory()
|
52 |
+
|
53 |
+
user_input.submit(on_submit, inputs=[user_input, state], outputs=[chat_output, user_input, state], queue=True)
|
54 |
+
reset_btn.click(reset_chat, inputs=None, outputs=[chat_output, user_input, state])
|
55 |
+
|
56 |
+
return demo
|
core/make_pipeline.py
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from transformers import pipeline, AutoTokenizer, AutoModelForCausalLM
|
2 |
+
import torch
|
3 |
+
import os
|
4 |
+
|
5 |
+
class MakePipeline:
|
6 |
+
# ๋ชจ๋ธ๋ช
|
7 |
+
MODEL_ID = "naver-hyperclovax/HyperCLOVAX-SEED-Text-Instruct-1.5B"
|
8 |
+
|
9 |
+
# ๋ณ์์ด๊ธฐํ
|
10 |
+
# model_id
|
11 |
+
# tokenizer
|
12 |
+
# llm
|
13 |
+
def __init__(self, model_id: str = MODEL_ID):
|
14 |
+
print("[torch] is available:", torch.cuda.is_available())
|
15 |
+
print("[device] default:", torch.device("cuda" if torch.cuda.is_available() else "cpu"))
|
16 |
+
self.model_id = model_id
|
17 |
+
self.tokenizer = None
|
18 |
+
self.llm = None
|
19 |
+
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
20 |
+
|
21 |
+
# ๋ชจ๋ธ ๋ถ๋ฌ์ค๊ธฐ
|
22 |
+
def build(self, type: str):
|
23 |
+
if(type == 'hf'):
|
24 |
+
# ํ๊น
ํ์ด์ค secret์ ๋ฑ๋ก๋ ํ ํฐ ๋ก๋
|
25 |
+
access_token = os.environ.get("HF_TOKEN")
|
26 |
+
else:
|
27 |
+
# ๋ก์ปฌ ์คํ์ token.txt์์ ํ ํฐ ๋ก๋
|
28 |
+
with open("token.txt", "r") as f:
|
29 |
+
access_token = f.read().strip()
|
30 |
+
|
31 |
+
tokenizer = AutoTokenizer.from_pretrained(self.model_id, token=access_token)
|
32 |
+
model = AutoModelForCausalLM.from_pretrained(self.model_id, token=access_token)
|
33 |
+
self.tokenizer = tokenizer
|
34 |
+
|
35 |
+
# ํ๊น
ํ์ด์ค ์
๋ก๋ ์ f16 ์ฌ์ฉ ์ ํจ
|
36 |
+
if(type == 'hf'):
|
37 |
+
llm = pipeline(
|
38 |
+
"text-generation",
|
39 |
+
model=model,
|
40 |
+
tokenizer=tokenizer,
|
41 |
+
)
|
42 |
+
|
43 |
+
else:
|
44 |
+
model.eval()
|
45 |
+
llm = pipeline(
|
46 |
+
"text-generation",
|
47 |
+
model=model,
|
48 |
+
tokenizer=tokenizer,
|
49 |
+
torch_dtype=torch.float16
|
50 |
+
)
|
51 |
+
if torch.cuda.is_available():
|
52 |
+
model.to("cuda")
|
53 |
+
|
54 |
+
self.llm = llm
|
55 |
+
|
56 |
+
# ๋ชจ๋ธ ์ถ๋ ฅ ์์ฑ ํจ์
|
57 |
+
def character_chat(self, prompt):
|
58 |
+
print("[debug] generating...")
|
59 |
+
outputs = self.llm(
|
60 |
+
prompt,
|
61 |
+
do_sample=True,
|
62 |
+
max_new_tokens=96,
|
63 |
+
temperature=0.7,
|
64 |
+
top_p=0.9,
|
65 |
+
repetition_penalty=1.05,
|
66 |
+
eos_token_id=self.tokenizer.eos_token_id,
|
67 |
+
return_full_text=True
|
68 |
+
)
|
69 |
+
full_text = outputs[0]['generated_text']
|
70 |
+
return full_text
|
core/make_reply.py
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
|
3 |
+
from core.make_pipeline import MakePipeline
|
4 |
+
|
5 |
+
# ์์ฑ๋ ๋ชจ๋ ๋ด ์๋ต ๊ธฐ๋ก
|
6 |
+
def generate_reply(ctx, makePipeLine: MakePipeline, user_msg):
|
7 |
+
|
8 |
+
# ์ต์ด ์๋ต
|
9 |
+
response = generate_valid_response(ctx, makePipeLine, user_msg)
|
10 |
+
ctx.addHistory("bot", response)
|
11 |
+
|
12 |
+
# ์๋ต์ด ๋๊ฒผ๋ค๋ฉด ์ถ๊ฐ ์์ฑ
|
13 |
+
if is_truncated_response(response):
|
14 |
+
continuation = generate_valid_response(ctx, makePipeLine, response)
|
15 |
+
ctx.addHistory("bot", continuation)
|
16 |
+
|
17 |
+
# ๋ด ์๋ต 1ํ ์์ฑ
|
18 |
+
def generate_valid_response(ctx, makePipeline: MakePipeline, user_msg: str) -> str:
|
19 |
+
user_name = ctx.getUserName()
|
20 |
+
bot_name = ctx.getBotName()
|
21 |
+
|
22 |
+
while True:
|
23 |
+
prompt = build_prompt(ctx.getHistory(), user_msg, user_name, bot_name)
|
24 |
+
full_text = makePipeline.character_chat(prompt)
|
25 |
+
response = extract_response(full_text, prompt, user_name, bot_name)
|
26 |
+
if is_valid_response(response, user_name, bot_name):
|
27 |
+
break
|
28 |
+
return clean_response(response, bot_name)
|
29 |
+
|
30 |
+
# ์
๋ ฅ ํ๋กฌํํธ ์ ๋ฆฌ
|
31 |
+
def build_prompt(history, user_msg, user_name, bot_name):
|
32 |
+
with open("assets/prompt/init.txt", "r", encoding="utf-8") as f:
|
33 |
+
prompt = f.read().strip()
|
34 |
+
|
35 |
+
for turn in history[-16:]:
|
36 |
+
role = user_name if turn["role"] == "user" else bot_name
|
37 |
+
prompt += f"{role}: {turn['text']}\n"
|
38 |
+
|
39 |
+
prompt += f"{user_name}: {user_msg}\n"
|
40 |
+
prompt += f"{bot_name}:"
|
41 |
+
return prompt
|
42 |
+
|
43 |
+
# ์ถ๋ ฅ์์ ์๋ต ์ถ์ถ
|
44 |
+
def extract_response(full_text, prompt, user_name, bot_name):
|
45 |
+
if full_text.startswith(prompt):
|
46 |
+
reply = full_text[len(prompt):].strip()
|
47 |
+
else:
|
48 |
+
reply = full_text.split(f"{bot_name}:")[-1].strip()
|
49 |
+
user_token = f"\n{user_name}:"
|
50 |
+
if user_token in reply:
|
51 |
+
reply = reply.split(user_token)[0].strip()
|
52 |
+
return reply
|
53 |
+
|
54 |
+
# ์๋ต ์ ํจ์ฑ ๊ฒ์ฌ
|
55 |
+
def is_valid_response(text: str, user_name, bot_name) -> bool:
|
56 |
+
if user_name + ":" in text:
|
57 |
+
return False
|
58 |
+
if bot_name + ":" in text:
|
59 |
+
return False
|
60 |
+
return True
|
61 |
+
|
62 |
+
# ์๋ต ํ์ ์ ๋ฆฌ
|
63 |
+
def clean_response(text: str, bot_name):
|
64 |
+
return re.sub(rf"{bot_name}:\\s*", "", text).strip()
|
65 |
+
|
66 |
+
# ์ค๋จ๋ ์๋ต ์ฌ๋ถ ๊ฒ์ฌ
|
67 |
+
def is_truncated_response(text: str) -> bool:
|
68 |
+
return re.search(r"[.?!โฆ\u2026\u2639\u263A\u2764\uD83D\uDE0A\uD83D\uDE22]$", text.strip()) is None
|
{app โ core}/utils.py
RENAMED
File without changes
|
howToStart.txt
CHANGED
@@ -7,11 +7,14 @@ python -m venv venv
|
|
7 |
2. activate venv
|
8 |
venv\Scripts\activate
|
9 |
|
10 |
-
3. install
|
11 |
-
pip install -r
|
|
|
12 |
|
13 |
3.2. recieve hugging face token
|
14 |
and write it into `./token.txt`
|
15 |
|
16 |
4. start server
|
17 |
-
python app\main.py
|
|
|
|
|
|
7 |
2. activate venv
|
8 |
venv\Scripts\activate
|
9 |
|
10 |
+
3. install requirements_local
|
11 |
+
pip install -r requirements_local.txt
|
12 |
+
!! not `requirements` !!
|
13 |
|
14 |
3.2. recieve hugging face token
|
15 |
and write it into `./token.txt`
|
16 |
|
17 |
4. start server
|
18 |
+
python app\main.py
|
19 |
+
|
20 |
+
5. connect to localhost
|
requirements.txt
CHANGED
@@ -25,8 +25,8 @@ markdown-it-py==3.0.0
|
|
25 |
MarkupSafe==3.0.2
|
26 |
mdurl==0.1.2
|
27 |
mpmath==1.3.0
|
28 |
-
networkx==3.
|
29 |
-
numpy==
|
30 |
orjson==3.10.18
|
31 |
packaging==25.0
|
32 |
pandas==2.3.1
|
@@ -57,7 +57,7 @@ sympy==1.13.3
|
|
57 |
tiktoken==0.9.0
|
58 |
tokenizers==0.21.2
|
59 |
tomlkit==0.13.3
|
60 |
-
torch
|
61 |
tqdm==4.67.1
|
62 |
transformers==4.53.2
|
63 |
typer==0.16.0
|
@@ -66,4 +66,4 @@ typing_extensions==4.14.1
|
|
66 |
tzdata==2025.2
|
67 |
urllib3==2.5.0
|
68 |
uvicorn==0.35.0
|
69 |
-
websockets==15.0.1
|
|
|
25 |
MarkupSafe==3.0.2
|
26 |
mdurl==0.1.2
|
27 |
mpmath==1.3.0
|
28 |
+
networkx==3.4.2
|
29 |
+
numpy==1.26.4
|
30 |
orjson==3.10.18
|
31 |
packaging==25.0
|
32 |
pandas==2.3.1
|
|
|
57 |
tiktoken==0.9.0
|
58 |
tokenizers==0.21.2
|
59 |
tomlkit==0.13.3
|
60 |
+
torch>=2.2,<2.8
|
61 |
tqdm==4.67.1
|
62 |
transformers==4.53.2
|
63 |
typer==0.16.0
|
|
|
66 |
tzdata==2025.2
|
67 |
urllib3==2.5.0
|
68 |
uvicorn==0.35.0
|
69 |
+
websockets==15.0.1
|
requirements_local.txt
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
aiofiles==24.1.0
|
2 |
+
annotated-types==0.7.0
|
3 |
+
anyio==4.9.0
|
4 |
+
blobfile==3.0.0
|
5 |
+
Brotli==1.1.0
|
6 |
+
certifi==2025.7.14
|
7 |
+
charset-normalizer==3.4.2
|
8 |
+
click==8.2.1
|
9 |
+
colorama==0.4.6
|
10 |
+
fastapi==0.116.1
|
11 |
+
ffmpy==0.6.0
|
12 |
+
filelock==3.18.0
|
13 |
+
fsspec==2025.5.1
|
14 |
+
gradio==5.37.0
|
15 |
+
gradio_client==1.10.4
|
16 |
+
groovy==0.1.2
|
17 |
+
h11==0.16.0
|
18 |
+
httpcore==1.0.9
|
19 |
+
httpx==0.28.1
|
20 |
+
huggingface-hub==0.33.4
|
21 |
+
idna==3.10
|
22 |
+
Jinja2==3.1.6
|
23 |
+
lxml==6.0.0
|
24 |
+
markdown-it-py==3.0.0
|
25 |
+
MarkupSafe==3.0.2
|
26 |
+
mdurl==0.1.2
|
27 |
+
mpmath==1.3.0
|
28 |
+
networkx==3.5
|
29 |
+
numpy==2.3.1
|
30 |
+
orjson==3.10.18
|
31 |
+
packaging==25.0
|
32 |
+
pandas==2.3.1
|
33 |
+
pillow==11.3.0
|
34 |
+
protobuf==6.31.1
|
35 |
+
pycryptodomex==3.23.0
|
36 |
+
pydantic==2.11.7
|
37 |
+
pydantic_core==2.33.2
|
38 |
+
pydub==0.25.1
|
39 |
+
Pygments==2.19.2
|
40 |
+
python-dateutil==2.9.0.post0
|
41 |
+
python-multipart==0.0.20
|
42 |
+
pytz==2025.2
|
43 |
+
PyYAML==6.0.2
|
44 |
+
regex==2024.11.6
|
45 |
+
requests==2.32.4
|
46 |
+
rich==14.0.0
|
47 |
+
ruff==0.12.3
|
48 |
+
safehttpx==0.1.6
|
49 |
+
safetensors==0.5.3
|
50 |
+
semantic-version==2.10.0
|
51 |
+
sentencepiece==0.2.0
|
52 |
+
shellingham==1.5.4
|
53 |
+
six==1.17.0
|
54 |
+
sniffio==1.3.1
|
55 |
+
starlette==0.47.1
|
56 |
+
sympy==1.13.3
|
57 |
+
tiktoken==0.9.0
|
58 |
+
tokenizers==0.21.2
|
59 |
+
tomlkit==0.13.3
|
60 |
+
torch==2.7.1+cu118
|
61 |
+
tqdm==4.67.1
|
62 |
+
transformers==4.53.2
|
63 |
+
typer==0.16.0
|
64 |
+
typing-inspection==0.4.1
|
65 |
+
typing_extensions==4.14.1
|
66 |
+
tzdata==2025.2
|
67 |
+
urllib3==2.5.0
|
68 |
+
uvicorn==0.35.0
|
69 |
+
websockets==15.0.1
|
todo.txt
CHANGED
@@ -2,4 +2,5 @@
|
|
2 |
๋์ ๋๋ ๋ํ๋ฅผ ํ๋กฌํํธ๋ก ๋ค์ ์ฌ์ฉํ ๋ ์ ์ ํ ๊ธธ์ด ์ฐพ๊ธฐ
|
3 |
์ด๋ฏธ์ง ์ถ๋ ฅ ๊ธฐ๋ฅ ์ถ๊ฐ
|
4 |
์ถ๋ ฅ ํ๋กฌํํธ ํ์ฑ ๊ธฐ๋ฅ ์ถ๊ฐ
|
5 |
-
์
๋ ฅ ํ๋กฌํํธ ํ์ฑ ๊ธฐ๋ฅ
|
|
|
|
2 |
๋์ ๋๋ ๋ํ๋ฅผ ํ๋กฌํํธ๋ก ๋ค์ ์ฌ์ฉํ ๋ ์ ์ ํ ๊ธธ์ด ์ฐพ๊ธฐ
|
3 |
์ด๋ฏธ์ง ์ถ๋ ฅ ๊ธฐ๋ฅ ์ถ๊ฐ
|
4 |
์ถ๋ ฅ ํ๋กฌํํธ ํ์ฑ ๊ธฐ๋ฅ ์ถ๊ฐ
|
5 |
+
์
๋ ฅ ํ๋กฌํํธ ํ์ฑ ๊ธฐ๋ฅ
|
6 |
+
character_chat ํจ์ ์ค์ json์ผ๋ก ๋ง๋ค๊ธฐ, gradio์์ ์ค์ ๊ฐ๋ฅํ๊ฒ ๋ง๋ค๊ธฐ
|