Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -18,7 +18,7 @@ MODEL_ID = "zai-org/GLM-4.7-Flash"
|
|
| 18 |
print(f"[Init] Loading tokenizer from {MODEL_ID}...")
|
| 19 |
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
|
| 20 |
|
| 21 |
-
model = None
|
| 22 |
|
| 23 |
def get_model():
|
| 24 |
global model
|
|
@@ -41,7 +41,7 @@ def get_model():
|
|
| 41 |
def extract_text_from_pdf(file_path: str) -> str:
|
| 42 |
"""PDF νμΌμμ ν
μ€νΈ μΆμΆ"""
|
| 43 |
try:
|
| 44 |
-
import fitz
|
| 45 |
doc = fitz.open(file_path)
|
| 46 |
text_parts = []
|
| 47 |
for page_num, page in enumerate(doc, 1):
|
|
@@ -70,13 +70,10 @@ def extract_text_from_docx(file_path: str) -> str:
|
|
| 70 |
try:
|
| 71 |
from docx import Document
|
| 72 |
doc = Document(file_path)
|
| 73 |
-
|
| 74 |
text_parts = []
|
| 75 |
-
|
| 76 |
for para in doc.paragraphs:
|
| 77 |
if para.text.strip():
|
| 78 |
text_parts.append(para.text)
|
| 79 |
-
|
| 80 |
for table_idx, table in enumerate(doc.tables, 1):
|
| 81 |
table_text = [f"\n[ν {table_idx}]"]
|
| 82 |
for row in table.rows:
|
|
@@ -85,7 +82,6 @@ def extract_text_from_docx(file_path: str) -> str:
|
|
| 85 |
table_text.append(row_text)
|
| 86 |
if len(table_text) > 1:
|
| 87 |
text_parts.append("\n".join(table_text))
|
| 88 |
-
|
| 89 |
return "\n\n".join(text_parts) if text_parts else "[DOCXμμ ν
μ€νΈλ₯Ό μΆμΆν μ μμ΅λλ€]"
|
| 90 |
except Exception as e:
|
| 91 |
return f"[DOCX μ½κΈ° μ€λ₯: {str(e)}]"
|
|
@@ -197,13 +193,11 @@ def execute_tool(tool_name: str, arguments: dict) -> str:
|
|
| 197 |
def parse_tool_calls(response: str) -> list:
|
| 198 |
"""μλ΅μμ λꡬ νΈμΆ νμ±"""
|
| 199 |
tool_calls = []
|
| 200 |
-
|
| 201 |
patterns = [
|
| 202 |
r'<\|tool_call\|>(\{.*?\})<\|/tool_call\|>',
|
| 203 |
r'```json\s*(\{[^`]*"name"[^`]*\})\s*```',
|
| 204 |
r'\{"name":\s*"(\w+)",\s*"arguments":\s*(\{[^}]+\})\}',
|
| 205 |
]
|
| 206 |
-
|
| 207 |
for pattern in patterns:
|
| 208 |
matches = re.findall(pattern, response, re.DOTALL)
|
| 209 |
for match in matches:
|
|
@@ -215,11 +209,10 @@ def parse_tool_calls(response: str) -> list:
|
|
| 215 |
tool_calls.append(tool_call)
|
| 216 |
except:
|
| 217 |
continue
|
| 218 |
-
|
| 219 |
return tool_calls
|
| 220 |
|
| 221 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 222 |
-
# π¬ μ€νΈλ¦¬λ° μ±ν
ν¨μ
|
| 223 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 224 |
|
| 225 |
file_context = {"name": "", "content": ""}
|
|
@@ -235,16 +228,16 @@ def chat_streaming(
|
|
| 235 |
enable_thinking: bool,
|
| 236 |
enable_tools: bool,
|
| 237 |
):
|
| 238 |
-
"""μ€νΈλ¦¬λ° μ±ν
μμ±"""
|
| 239 |
global file_context
|
| 240 |
|
| 241 |
if not message.strip():
|
| 242 |
-
yield history
|
| 243 |
return
|
| 244 |
|
| 245 |
model = get_model()
|
| 246 |
-
messages = []
|
| 247 |
|
|
|
|
| 248 |
sys_content = system_prompt if system_prompt.strip() else "You are a helpful AI assistant."
|
| 249 |
|
| 250 |
if file_context["content"]:
|
|
@@ -260,20 +253,27 @@ You have access to these tools:
|
|
| 260 |
"""
|
| 261 |
sys_content += f"\n\n{tool_desc}"
|
| 262 |
|
| 263 |
-
|
|
|
|
| 264 |
|
|
|
|
| 265 |
for h in history:
|
| 266 |
-
if h
|
| 267 |
-
messages.append({"role": "
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
user_content = message
|
| 272 |
if enable_thinking:
|
| 273 |
user_content = f"<think>\nLet me think step by step.\n</think>\n\n{message}"
|
| 274 |
|
| 275 |
messages.append({"role": "user", "content": user_content})
|
| 276 |
|
|
|
|
| 277 |
try:
|
| 278 |
inputs = tokenizer.apply_chat_template(
|
| 279 |
messages,
|
|
@@ -283,34 +283,51 @@ You have access to these tools:
|
|
| 283 |
return_tensors="pt",
|
| 284 |
).to(model.device)
|
| 285 |
except Exception as e:
|
| 286 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 287 |
return
|
| 288 |
|
|
|
|
| 289 |
streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
|
| 290 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 291 |
generation_kwargs = {
|
| 292 |
**inputs,
|
| 293 |
"streamer": streamer,
|
| 294 |
-
"
|
| 295 |
-
"temperature": temperature if temperature > 0 else 0.01,
|
| 296 |
-
"top_p": top_p,
|
| 297 |
-
"do_sample": temperature > 0,
|
| 298 |
-
"pad_token_id": tokenizer.pad_token_id or tokenizer.eos_token_id,
|
| 299 |
}
|
| 300 |
|
| 301 |
thread = Thread(target=model.generate, kwargs=generation_kwargs)
|
| 302 |
thread.start()
|
| 303 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 304 |
partial_response = ""
|
| 305 |
-
new_history = history + [[message, ""]]
|
| 306 |
|
| 307 |
for new_token in streamer:
|
| 308 |
partial_response += new_token
|
| 309 |
-
new_history[-1][
|
| 310 |
-
yield new_history
|
| 311 |
|
| 312 |
thread.join()
|
| 313 |
|
|
|
|
| 314 |
if enable_tools:
|
| 315 |
tool_calls = parse_tool_calls(partial_response)
|
| 316 |
if tool_calls:
|
|
@@ -321,9 +338,9 @@ You have access to these tools:
|
|
| 321 |
|
| 322 |
if tool_results:
|
| 323 |
final_response = partial_response + "\n\nπ **λꡬ μ€ν κ²°κ³Ό:**\n" + "\n".join(tool_results)
|
| 324 |
-
new_history[-1][
|
| 325 |
|
| 326 |
-
yield new_history
|
| 327 |
|
| 328 |
def handle_file_upload(file):
|
| 329 |
"""νμΌ μ
λ‘λ μ²λ¦¬"""
|
|
@@ -354,10 +371,10 @@ def clear_file():
|
|
| 354 |
|
| 355 |
def clear_chat():
|
| 356 |
"""μ±ν
μ΄κΈ°ν"""
|
| 357 |
-
return []
|
| 358 |
|
| 359 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 360 |
-
# π¨ Gradio UI (6.0 νΈν)
|
| 361 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 362 |
|
| 363 |
with gr.Blocks(title="GLM-4.7-Flash Chatbot") as demo:
|
|
@@ -373,6 +390,7 @@ with gr.Blocks(title="GLM-4.7-Flash Chatbot") as demo:
|
|
| 373 |
chatbot = gr.Chatbot(
|
| 374 |
label="λν",
|
| 375 |
height=500,
|
|
|
|
| 376 |
)
|
| 377 |
|
| 378 |
with gr.Row():
|
|
@@ -425,20 +443,26 @@ with gr.Blocks(title="GLM-4.7-Flash Chatbot") as demo:
|
|
| 425 |
inputs=message,
|
| 426 |
)
|
| 427 |
|
| 428 |
-
# μ΄λ²€νΈ
|
| 429 |
submit_event = submit_btn.click(
|
| 430 |
fn=chat_streaming,
|
| 431 |
inputs=[message, chatbot, system_prompt, max_tokens, temperature, top_p, enable_thinking, enable_tools],
|
| 432 |
-
outputs=[chatbot
|
|
|
|
|
|
|
|
|
|
| 433 |
)
|
| 434 |
|
| 435 |
message.submit(
|
| 436 |
fn=chat_streaming,
|
| 437 |
inputs=[message, chatbot, system_prompt, max_tokens, temperature, top_p, enable_thinking, enable_tools],
|
| 438 |
-
outputs=[chatbot
|
|
|
|
|
|
|
|
|
|
| 439 |
)
|
| 440 |
|
| 441 |
-
clear_btn.click(fn=clear_chat, outputs=[chatbot
|
| 442 |
stop_btn.click(fn=None, cancels=[submit_event])
|
| 443 |
|
| 444 |
file_upload.change(fn=handle_file_upload, inputs=[file_upload], outputs=[file_status])
|
|
|
|
| 18 |
print(f"[Init] Loading tokenizer from {MODEL_ID}...")
|
| 19 |
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
|
| 20 |
|
| 21 |
+
model = None
|
| 22 |
|
| 23 |
def get_model():
|
| 24 |
global model
|
|
|
|
| 41 |
def extract_text_from_pdf(file_path: str) -> str:
|
| 42 |
"""PDF νμΌμμ ν
μ€νΈ μΆμΆ"""
|
| 43 |
try:
|
| 44 |
+
import fitz
|
| 45 |
doc = fitz.open(file_path)
|
| 46 |
text_parts = []
|
| 47 |
for page_num, page in enumerate(doc, 1):
|
|
|
|
| 70 |
try:
|
| 71 |
from docx import Document
|
| 72 |
doc = Document(file_path)
|
|
|
|
| 73 |
text_parts = []
|
|
|
|
| 74 |
for para in doc.paragraphs:
|
| 75 |
if para.text.strip():
|
| 76 |
text_parts.append(para.text)
|
|
|
|
| 77 |
for table_idx, table in enumerate(doc.tables, 1):
|
| 78 |
table_text = [f"\n[ν {table_idx}]"]
|
| 79 |
for row in table.rows:
|
|
|
|
| 82 |
table_text.append(row_text)
|
| 83 |
if len(table_text) > 1:
|
| 84 |
text_parts.append("\n".join(table_text))
|
|
|
|
| 85 |
return "\n\n".join(text_parts) if text_parts else "[DOCXμμ ν
μ€νΈλ₯Ό μΆμΆν μ μμ΅λλ€]"
|
| 86 |
except Exception as e:
|
| 87 |
return f"[DOCX μ½κΈ° μ€λ₯: {str(e)}]"
|
|
|
|
| 193 |
def parse_tool_calls(response: str) -> list:
|
| 194 |
"""μλ΅μμ λꡬ νΈμΆ νμ±"""
|
| 195 |
tool_calls = []
|
|
|
|
| 196 |
patterns = [
|
| 197 |
r'<\|tool_call\|>(\{.*?\})<\|/tool_call\|>',
|
| 198 |
r'```json\s*(\{[^`]*"name"[^`]*\})\s*```',
|
| 199 |
r'\{"name":\s*"(\w+)",\s*"arguments":\s*(\{[^}]+\})\}',
|
| 200 |
]
|
|
|
|
| 201 |
for pattern in patterns:
|
| 202 |
matches = re.findall(pattern, response, re.DOTALL)
|
| 203 |
for match in matches:
|
|
|
|
| 209 |
tool_calls.append(tool_call)
|
| 210 |
except:
|
| 211 |
continue
|
|
|
|
| 212 |
return tool_calls
|
| 213 |
|
| 214 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 215 |
+
# π¬ μ€νΈλ¦¬λ° μ±ν
ν¨μ (Gradio 6.0 messages format)
|
| 216 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 217 |
|
| 218 |
file_context = {"name": "", "content": ""}
|
|
|
|
| 228 |
enable_thinking: bool,
|
| 229 |
enable_tools: bool,
|
| 230 |
):
|
| 231 |
+
"""μ€νΈλ¦¬λ° μ±ν
μμ± - Gradio 6.0 messages format"""
|
| 232 |
global file_context
|
| 233 |
|
| 234 |
if not message.strip():
|
| 235 |
+
yield history
|
| 236 |
return
|
| 237 |
|
| 238 |
model = get_model()
|
|
|
|
| 239 |
|
| 240 |
+
# μμ€ν
ν둬ννΈ κ΅¬μ±
|
| 241 |
sys_content = system_prompt if system_prompt.strip() else "You are a helpful AI assistant."
|
| 242 |
|
| 243 |
if file_context["content"]:
|
|
|
|
| 253 |
"""
|
| 254 |
sys_content += f"\n\n{tool_desc}"
|
| 255 |
|
| 256 |
+
# λͺ¨λΈμ© λ©μμ§ κ΅¬μ±
|
| 257 |
+
messages = [{"role": "system", "content": sys_content}]
|
| 258 |
|
| 259 |
+
# νμ€ν 리 λ³ν (Gradio 6.0 format -> λͺ¨λΈ format)
|
| 260 |
for h in history:
|
| 261 |
+
if isinstance(h, dict):
|
| 262 |
+
messages.append({"role": h["role"], "content": h["content"]})
|
| 263 |
+
elif isinstance(h, (list, tuple)) and len(h) == 2:
|
| 264 |
+
if h[0]:
|
| 265 |
+
messages.append({"role": "user", "content": h[0]})
|
| 266 |
+
if h[1]:
|
| 267 |
+
messages.append({"role": "assistant", "content": h[1]})
|
| 268 |
+
|
| 269 |
+
# νμ¬ λ©μμ§
|
| 270 |
user_content = message
|
| 271 |
if enable_thinking:
|
| 272 |
user_content = f"<think>\nLet me think step by step.\n</think>\n\n{message}"
|
| 273 |
|
| 274 |
messages.append({"role": "user", "content": user_content})
|
| 275 |
|
| 276 |
+
# ν ν¬λμ΄μ¦
|
| 277 |
try:
|
| 278 |
inputs = tokenizer.apply_chat_template(
|
| 279 |
messages,
|
|
|
|
| 283 |
return_tensors="pt",
|
| 284 |
).to(model.device)
|
| 285 |
except Exception as e:
|
| 286 |
+
new_history = history + [
|
| 287 |
+
{"role": "user", "content": message},
|
| 288 |
+
{"role": "assistant", "content": f"ν ν¬λμ΄μ¦ μ€λ₯: {str(e)}"}
|
| 289 |
+
]
|
| 290 |
+
yield new_history
|
| 291 |
return
|
| 292 |
|
| 293 |
+
# μ€νΈλ¦¬λ¨Έ μ€μ
|
| 294 |
streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
|
| 295 |
|
| 296 |
+
# GenerationConfig μ¬μ©
|
| 297 |
+
from transformers import GenerationConfig
|
| 298 |
+
gen_config = GenerationConfig(
|
| 299 |
+
max_new_tokens=max_tokens,
|
| 300 |
+
temperature=temperature if temperature > 0 else 0.01,
|
| 301 |
+
top_p=top_p,
|
| 302 |
+
do_sample=temperature > 0,
|
| 303 |
+
pad_token_id=tokenizer.pad_token_id or tokenizer.eos_token_id,
|
| 304 |
+
)
|
| 305 |
+
|
| 306 |
generation_kwargs = {
|
| 307 |
**inputs,
|
| 308 |
"streamer": streamer,
|
| 309 |
+
"generation_config": gen_config,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 310 |
}
|
| 311 |
|
| 312 |
thread = Thread(target=model.generate, kwargs=generation_kwargs)
|
| 313 |
thread.start()
|
| 314 |
|
| 315 |
+
# Gradio 6.0 messages formatμΌλ‘ νμ€ν 리 ꡬμ±
|
| 316 |
+
new_history = history + [
|
| 317 |
+
{"role": "user", "content": message},
|
| 318 |
+
{"role": "assistant", "content": ""}
|
| 319 |
+
]
|
| 320 |
+
|
| 321 |
partial_response = ""
|
|
|
|
| 322 |
|
| 323 |
for new_token in streamer:
|
| 324 |
partial_response += new_token
|
| 325 |
+
new_history[-1]["content"] = partial_response
|
| 326 |
+
yield new_history
|
| 327 |
|
| 328 |
thread.join()
|
| 329 |
|
| 330 |
+
# Tool νΈμΆ μ²λ¦¬
|
| 331 |
if enable_tools:
|
| 332 |
tool_calls = parse_tool_calls(partial_response)
|
| 333 |
if tool_calls:
|
|
|
|
| 338 |
|
| 339 |
if tool_results:
|
| 340 |
final_response = partial_response + "\n\nπ **λꡬ μ€ν κ²°κ³Ό:**\n" + "\n".join(tool_results)
|
| 341 |
+
new_history[-1]["content"] = final_response
|
| 342 |
|
| 343 |
+
yield new_history
|
| 344 |
|
| 345 |
def handle_file_upload(file):
|
| 346 |
"""νμΌ μ
λ‘λ μ²λ¦¬"""
|
|
|
|
| 371 |
|
| 372 |
def clear_chat():
|
| 373 |
"""μ±ν
μ΄κΈ°ν"""
|
| 374 |
+
return []
|
| 375 |
|
| 376 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 377 |
+
# π¨ Gradio UI (6.0 νΈν - messages format)
|
| 378 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 379 |
|
| 380 |
with gr.Blocks(title="GLM-4.7-Flash Chatbot") as demo:
|
|
|
|
| 390 |
chatbot = gr.Chatbot(
|
| 391 |
label="λν",
|
| 392 |
height=500,
|
| 393 |
+
type="messages", # Gradio 6.0 messages format
|
| 394 |
)
|
| 395 |
|
| 396 |
with gr.Row():
|
|
|
|
| 443 |
inputs=message,
|
| 444 |
)
|
| 445 |
|
| 446 |
+
# μ΄λ²€νΈ - Gradio 6.0μμλ chatbotλ§ output
|
| 447 |
submit_event = submit_btn.click(
|
| 448 |
fn=chat_streaming,
|
| 449 |
inputs=[message, chatbot, system_prompt, max_tokens, temperature, top_p, enable_thinking, enable_tools],
|
| 450 |
+
outputs=[chatbot],
|
| 451 |
+
).then(
|
| 452 |
+
fn=lambda: "",
|
| 453 |
+
outputs=[message],
|
| 454 |
)
|
| 455 |
|
| 456 |
message.submit(
|
| 457 |
fn=chat_streaming,
|
| 458 |
inputs=[message, chatbot, system_prompt, max_tokens, temperature, top_p, enable_thinking, enable_tools],
|
| 459 |
+
outputs=[chatbot],
|
| 460 |
+
).then(
|
| 461 |
+
fn=lambda: "",
|
| 462 |
+
outputs=[message],
|
| 463 |
)
|
| 464 |
|
| 465 |
+
clear_btn.click(fn=clear_chat, outputs=[chatbot])
|
| 466 |
stop_btn.click(fn=None, cancels=[submit_event])
|
| 467 |
|
| 468 |
file_upload.change(fn=handle_file_upload, inputs=[file_upload], outputs=[file_status])
|