seawolf2357 commited on
Commit
ced8ba1
ยท
verified ยท
1 Parent(s): 56354e9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +160 -74
app.py CHANGED
@@ -20,7 +20,7 @@ import pandas as pd
20
  # PDF ํ…์ŠคํŠธ ์ถ”์ถœ
21
  import PyPDF2
22
 
23
- MAX_CONTENT_CHARS = 8000 # ๋„ˆ๋ฌด ํฐ ํŒŒ์ผ์„ ๋ง‰๊ธฐ ์œ„ํ•ด ์ตœ๋Œ€ ํ‘œ์‹œ 8000์ž
24
 
25
  model_id = os.getenv("MODEL_ID", "google/gemma-3-27b-it")
26
  processor = AutoProcessor.from_pretrained(model_id, padding_side="left")
@@ -43,6 +43,10 @@ def analyze_csv_file(path: str) -> str:
43
  """
44
  try:
45
  df = pd.read_csv(path)
 
 
 
 
46
  df_str = df.to_string()
47
  if len(df_str) > MAX_CONTENT_CHARS:
48
  df_str = df_str[:MAX_CONTENT_CHARS] + "\n...(truncated)..."
@@ -73,11 +77,20 @@ def pdf_to_markdown(pdf_path: str) -> str:
73
  try:
74
  with open(pdf_path, "rb") as f:
75
  reader = PyPDF2.PdfReader(f)
76
- for page_num, page in enumerate(reader.pages, start=1):
 
 
 
77
  page_text = page.extract_text() or ""
78
  page_text = page_text.strip()
79
  if page_text:
80
- text_chunks.append(f"## Page {page_num}\n\n{page_text}\n")
 
 
 
 
 
 
81
  except Exception as e:
82
  return f"Failed to read PDF ({os.path.basename(pdf_path)}): {str(e)}"
83
 
@@ -97,7 +110,7 @@ def count_files_in_new_message(paths: list[str]) -> tuple[int, int]:
97
  for path in paths:
98
  if path.endswith(".mp4"):
99
  video_count += 1
100
- else:
101
  image_count += 1
102
  return image_count, video_count
103
 
@@ -108,10 +121,13 @@ def count_files_in_history(history: list[dict]) -> tuple[int, int]:
108
  for item in history:
109
  if item["role"] != "user" or isinstance(item["content"], str):
110
  continue
111
- if item["content"][0].endswith(".mp4"):
112
- video_count += 1
113
- else:
114
- image_count += 1
 
 
 
115
  return image_count, video_count
116
 
117
 
@@ -123,11 +139,9 @@ def validate_media_constraints(message: dict, history: list[dict]) -> bool:
123
  - <image> ํƒœ๊ทธ๊ฐ€ ์žˆ์œผ๋ฉด ํƒœ๊ทธ ์ˆ˜์™€ ์‹ค์ œ ์ด๋ฏธ์ง€ ์ˆ˜ ์ผ์น˜
124
  - CSV, TXT, PDF ๋“ฑ์€ ์—ฌ๊ธฐ์„œ ์ œํ•œํ•˜์ง€ ์•Š์Œ
125
  """
 
126
  media_files = []
127
  for f in message["files"]:
128
- # ์ด๋ฏธ์ง€: png/jpg/jpeg/gif/webp
129
- # ๋น„๋””์˜ค: mp4
130
- # cf) PDF, CSV, TXT ๋“ฑ์€ ์ œ์™ธ
131
  if re.search(r"\.(png|jpg|jpeg|gif|webp)$", f, re.IGNORECASE) or f.endswith(".mp4"):
132
  media_files.append(f)
133
 
@@ -149,9 +163,15 @@ def validate_media_constraints(message: dict, history: list[dict]) -> bool:
149
  if video_count == 0 and image_count > MAX_NUM_IMAGES:
150
  gr.Warning(f"You can upload up to {MAX_NUM_IMAGES} images.")
151
  return False
152
- if "<image>" in message["text"] and message["text"].count("<image>") != new_image_count:
153
- gr.Warning("The number of <image> tags in the text does not match the number of images.")
154
- return False
 
 
 
 
 
 
155
 
156
  return True
157
 
@@ -164,7 +184,8 @@ def downsample_video(video_path: str) -> list[tuple[Image.Image, float]]:
164
  fps = vidcap.get(cv2.CAP_PROP_FPS)
165
  total_frames = int(vidcap.get(cv2.CAP_PROP_FRAME_COUNT))
166
 
167
- frame_interval = int(fps / 3)
 
168
  frames = []
169
 
170
  for i in range(0, total_frames, frame_interval):
@@ -175,6 +196,10 @@ def downsample_video(video_path: str) -> list[tuple[Image.Image, float]]:
175
  pil_image = Image.fromarray(image)
176
  timestamp = round(i / fps, 2)
177
  frames.append((pil_image, timestamp))
 
 
 
 
178
 
179
  vidcap.release()
180
  return frames
@@ -200,9 +225,13 @@ def process_interleaved_images(message: dict) -> list[dict]:
200
  parts = re.split(r"(<image>)", message["text"])
201
  content = []
202
  image_index = 0
 
 
 
 
203
  for part in parts:
204
- if part == "<image>":
205
- content.append({"type": "image", "url": message["files"][image_index]})
206
  image_index += 1
207
  elif part.strip():
208
  content.append({"type": "text", "text": part.strip()})
@@ -216,13 +245,30 @@ def process_interleaved_images(message: dict) -> list[dict]:
216
  ##################################################
217
  # PDF + CSV + TXT + ์ด๋ฏธ์ง€/๋น„๋””์˜ค
218
  ##################################################
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  def process_new_user_message(message: dict) -> list[dict]:
220
  if not message["files"]:
221
  return [{"type": "text", "text": message["text"]}]
222
 
223
  # 1) ํŒŒ์ผ ๋ถ„๋ฅ˜
224
- video_files = [f for f in message["files"] if f.endswith(".mp4")]
225
- image_files = [f for f in message["files"] if re.search(r"\.(png|jpg|jpeg|gif|webp)$", f, re.IGNORECASE)]
226
  csv_files = [f for f in message["files"] if f.lower().endswith(".csv")]
227
  txt_files = [f for f in message["files"] if f.lower().endswith(".txt")]
228
  pdf_files = [f for f in message["files"] if f.lower().endswith(".pdf")]
@@ -251,9 +297,13 @@ def process_new_user_message(message: dict) -> list[dict]:
251
  return content_list
252
 
253
  # 7) ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ
254
- if "<image>" in message["text"] and image_files: # ์ด๋ฏธ์ง€ ํŒŒ์ผ์ด ์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ
255
  # interleaved
256
- return process_interleaved_images({"text": message["text"], "files": image_files})
 
 
 
 
257
  else:
258
  # ์ผ๋ฐ˜ ์—ฌ๋Ÿฌ ์žฅ
259
  for img_path in image_files:
@@ -261,6 +311,7 @@ def process_new_user_message(message: dict) -> list[dict]:
261
 
262
  return content_list
263
 
 
264
  ##################################################
265
  # history -> LLM ๋ฉ”์‹œ์ง€ ๋ณ€ํ™˜
266
  ##################################################
@@ -280,9 +331,18 @@ def process_history(history: list[dict]) -> list[dict]:
280
  content = item["content"]
281
  if isinstance(content, str):
282
  current_user_content.append({"type": "text", "text": content})
283
- else:
284
- # ์ด๋ฏธ์ง€๋‚˜ ๊ธฐํƒ€
285
- current_user_content.append({"type": "image", "url": content[0]})
 
 
 
 
 
 
 
 
 
286
  return messages
287
 
288
 
@@ -295,55 +355,79 @@ def run(message: dict, history: list[dict], system_prompt: str = "", max_new_tok
295
  yield ""
296
  return
297
 
298
- messages = []
299
- if system_prompt:
300
- messages.append({"role": "system", "content": [{"type": "text", "text": system_prompt}]})
301
- messages.extend(process_history(history))
302
-
303
- # ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ
304
- user_content = process_new_user_message(message)
305
-
306
- # ์ด๋ฏธ์ง€๊ฐ€ ์•„๋‹Œ ํŒŒ์ผ๋“ค๋งŒ ํ…์ŠคํŠธ๋กœ ๋ณ€ํ™˜
307
- processed_content = []
308
- for item in user_content:
309
- if item["type"] == "image":
310
- # ์ด๋ฏธ์ง€ ํŒŒ์ผ์ธ์ง€ ํ™•์ธ
311
- if re.search(r"\.(png|jpg|jpeg|gif|webp)$", item["url"], re.IGNORECASE):
312
- processed_content.append(item)
313
- else:
314
- # ์ด๋ฏธ์ง€๊ฐ€ ์•„๋‹Œ ํŒŒ์ผ์€ ํ…์ŠคํŠธ๋กœ ๋ณ€ํ™˜
315
- processed_content.append({"type": "text", "text": f"[File: {os.path.basename(item['url'])}]"})
316
- else:
317
- processed_content.append(item)
318
-
319
- messages.append({"role": "user", "content": processed_content})
320
-
321
- # LLM ์ฒ˜๋ฆฌ๋Š” ๊ทธ๋Œ€๋กœ ์ง„ํ–‰
322
- inputs = processor.apply_chat_template(
323
- messages,
324
- add_generation_prompt=True,
325
- tokenize=True,
326
- return_dict=True,
327
- return_tensors="pt",
328
- ).to(device=model.device, dtype=torch.bfloat16)
329
-
330
- streamer = TextIteratorStreamer(processor, timeout=30.0, skip_prompt=True, skip_special_tokens=True)
331
- gen_kwargs = dict(
332
- inputs,
333
- streamer=streamer,
334
- max_new_tokens=max_new_tokens,
335
- )
336
- t = Thread(target=model.generate, kwargs=gen_kwargs)
337
- t.start()
338
-
339
- output = ""
340
- for new_text in streamer:
341
- output += new_text
342
- yield output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
 
344
- ##################################################
345
- # ์˜ˆ์‹œ๋“ค (๊ธฐ์กด)
346
- ##################################################
347
  ##################################################
348
  # ์˜ˆ์‹œ๋“ค (ํ•œ๊ธ€ํ™” ๋ฒ„์ „)
349
  ##################################################
@@ -477,7 +561,9 @@ examples = [
477
  ]
478
 
479
 
480
-
 
 
481
  demo = gr.ChatInterface(
482
  fn=run,
483
  type="messages",
@@ -512,4 +598,4 @@ demo = gr.ChatInterface(
512
  )
513
 
514
  if __name__ == "__main__":
515
- demo.launch()
 
20
  # PDF ํ…์ŠคํŠธ ์ถ”์ถœ
21
  import PyPDF2
22
 
23
+ MAX_CONTENT_CHARS = 4000 # ๋„ˆ๋ฌด ํฐ ํŒŒ์ผ์„ ๋ง‰๊ธฐ ์œ„ํ•ด ์ตœ๋Œ€ ํ‘œ์‹œ 4000์ž
24
 
25
  model_id = os.getenv("MODEL_ID", "google/gemma-3-27b-it")
26
  processor = AutoProcessor.from_pretrained(model_id, padding_side="left")
 
43
  """
44
  try:
45
  df = pd.read_csv(path)
46
+ # ๋ฐ์ดํ„ฐ ํ”„๋ ˆ์ž„ ํฌ๊ธฐ ์ œํ•œ (ํ–‰/์—ด ์ˆ˜๊ฐ€ ๋งŽ์€ ๊ฒฝ์šฐ)
47
+ if df.shape[0] > 50 or df.shape[1] > 10:
48
+ df = df.iloc[:50, :10]
49
+
50
  df_str = df.to_string()
51
  if len(df_str) > MAX_CONTENT_CHARS:
52
  df_str = df_str[:MAX_CONTENT_CHARS] + "\n...(truncated)..."
 
77
  try:
78
  with open(pdf_path, "rb") as f:
79
  reader = PyPDF2.PdfReader(f)
80
+ # ์ตœ๋Œ€ 5ํŽ˜์ด์ง€๋งŒ ์ฒ˜๋ฆฌ
81
+ max_pages = min(5, len(reader.pages))
82
+ for page_num in range(max_pages):
83
+ page = reader.pages[page_num]
84
  page_text = page.extract_text() or ""
85
  page_text = page_text.strip()
86
  if page_text:
87
+ # ํŽ˜์ด์ง€๋ณ„ ํ…์ŠคํŠธ๋„ ์ œํ•œ
88
+ if len(page_text) > MAX_CONTENT_CHARS // max_pages:
89
+ page_text = page_text[:MAX_CONTENT_CHARS // max_pages] + "...(truncated)"
90
+ text_chunks.append(f"## Page {page_num+1}\n\n{page_text}\n")
91
+
92
+ if len(reader.pages) > max_pages:
93
+ text_chunks.append(f"\n...(Showing {max_pages} of {len(reader.pages)} pages)...")
94
  except Exception as e:
95
  return f"Failed to read PDF ({os.path.basename(pdf_path)}): {str(e)}"
96
 
 
110
  for path in paths:
111
  if path.endswith(".mp4"):
112
  video_count += 1
113
+ elif re.search(r"\.(png|jpg|jpeg|gif|webp)$", path, re.IGNORECASE):
114
  image_count += 1
115
  return image_count, video_count
116
 
 
121
  for item in history:
122
  if item["role"] != "user" or isinstance(item["content"], str):
123
  continue
124
+ if isinstance(item["content"], list) and len(item["content"]) > 0:
125
+ file_path = item["content"][0]
126
+ if isinstance(file_path, str):
127
+ if file_path.endswith(".mp4"):
128
+ video_count += 1
129
+ elif re.search(r"\.(png|jpg|jpeg|gif|webp)$", file_path, re.IGNORECASE):
130
+ image_count += 1
131
  return image_count, video_count
132
 
133
 
 
139
  - <image> ํƒœ๊ทธ๊ฐ€ ์žˆ์œผ๋ฉด ํƒœ๊ทธ ์ˆ˜์™€ ์‹ค์ œ ์ด๋ฏธ์ง€ ์ˆ˜ ์ผ์น˜
140
  - CSV, TXT, PDF ๋“ฑ์€ ์—ฌ๊ธฐ์„œ ์ œํ•œํ•˜์ง€ ์•Š์Œ
141
  """
142
+ # ์ด๋ฏธ์ง€์™€ ๋น„๋””์˜ค ํŒŒ์ผ๋งŒ ํ•„ํ„ฐ๋ง
143
  media_files = []
144
  for f in message["files"]:
 
 
 
145
  if re.search(r"\.(png|jpg|jpeg|gif|webp)$", f, re.IGNORECASE) or f.endswith(".mp4"):
146
  media_files.append(f)
147
 
 
163
  if video_count == 0 and image_count > MAX_NUM_IMAGES:
164
  gr.Warning(f"You can upload up to {MAX_NUM_IMAGES} images.")
165
  return False
166
+
167
+ # ์ด๋ฏธ์ง€ ํƒœ๊ทธ ๊ฒ€์ฆ (์‹ค์ œ ์ด๋ฏธ์ง€ ํŒŒ์ผ๋งŒ ๊ณ„์‚ฐ)
168
+ if "<image>" in message["text"]:
169
+ # ์ด๋ฏธ์ง€ ํŒŒ์ผ๋งŒ ํ•„ํ„ฐ๋ง
170
+ image_files = [f for f in message["files"] if re.search(r"\.(png|jpg|jpeg|gif|webp)$", f, re.IGNORECASE)]
171
+ image_tag_count = message["text"].count("<image>")
172
+ if image_tag_count != len(image_files):
173
+ gr.Warning("The number of <image> tags in the text does not match the number of image files.")
174
+ return False
175
 
176
  return True
177
 
 
184
  fps = vidcap.get(cv2.CAP_PROP_FPS)
185
  total_frames = int(vidcap.get(cv2.CAP_PROP_FRAME_COUNT))
186
 
187
+ # ๋” ์ ์€ ํ”„๋ ˆ์ž„์„ ์ถ”์ถœํ•˜๋„๋ก ์กฐ์ •
188
+ frame_interval = max(int(fps), int(total_frames / 10)) # ์ดˆ๋‹น 1ํ”„๋ ˆ์ž„ ๋˜๋Š” ์ตœ๋Œ€ 10ํ”„๋ ˆ์ž„
189
  frames = []
190
 
191
  for i in range(0, total_frames, frame_interval):
 
196
  pil_image = Image.fromarray(image)
197
  timestamp = round(i / fps, 2)
198
  frames.append((pil_image, timestamp))
199
+
200
+ # ์ตœ๋Œ€ 5ํ”„๋ ˆ์ž„๋งŒ ์‚ฌ์šฉ
201
+ if len(frames) >= 5:
202
+ break
203
 
204
  vidcap.release()
205
  return frames
 
225
  parts = re.split(r"(<image>)", message["text"])
226
  content = []
227
  image_index = 0
228
+
229
+ # ์ด๋ฏธ์ง€ ํŒŒ์ผ๋งŒ ํ•„ํ„ฐ๋ง
230
+ image_files = [f for f in message["files"] if re.search(r"\.(png|jpg|jpeg|gif|webp)$", f, re.IGNORECASE)]
231
+
232
  for part in parts:
233
+ if part == "<image>" and image_index < len(image_files):
234
+ content.append({"type": "image", "url": image_files[image_index]})
235
  image_index += 1
236
  elif part.strip():
237
  content.append({"type": "text", "text": part.strip()})
 
245
  ##################################################
246
  # PDF + CSV + TXT + ์ด๋ฏธ์ง€/๋น„๋””์˜ค
247
  ##################################################
248
+ def is_image_file(file_path: str) -> bool:
249
+ """์ด๋ฏธ์ง€ ํŒŒ์ผ์ธ์ง€ ํ™•์ธ"""
250
+ return bool(re.search(r"\.(png|jpg|jpeg|gif|webp)$", file_path, re.IGNORECASE))
251
+
252
+
253
+ def is_video_file(file_path: str) -> bool:
254
+ """๋น„๋””์˜ค ํŒŒ์ผ์ธ์ง€ ํ™•์ธ"""
255
+ return file_path.endswith(".mp4")
256
+
257
+
258
+ def is_document_file(file_path: str) -> bool:
259
+ """๋ฌธ์„œ ํŒŒ์ผ์ธ์ง€ ํ™•์ธ (PDF, CSV, TXT)"""
260
+ return (file_path.lower().endswith(".pdf") or
261
+ file_path.lower().endswith(".csv") or
262
+ file_path.lower().endswith(".txt"))
263
+
264
+
265
  def process_new_user_message(message: dict) -> list[dict]:
266
  if not message["files"]:
267
  return [{"type": "text", "text": message["text"]}]
268
 
269
  # 1) ํŒŒ์ผ ๋ถ„๋ฅ˜
270
+ video_files = [f for f in message["files"] if is_video_file(f)]
271
+ image_files = [f for f in message["files"] if is_image_file(f)]
272
  csv_files = [f for f in message["files"] if f.lower().endswith(".csv")]
273
  txt_files = [f for f in message["files"] if f.lower().endswith(".txt")]
274
  pdf_files = [f for f in message["files"] if f.lower().endswith(".pdf")]
 
297
  return content_list
298
 
299
  # 7) ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ
300
+ if "<image>" in message["text"] and image_files:
301
  # interleaved
302
+ interleaved_content = process_interleaved_images({"text": message["text"], "files": image_files})
303
+ # ์›๋ณธ content_list ์•ž๋ถ€๋ถ„(ํ…์ŠคํŠธ)์„ ์ œ๊ฑฐํ•˜๊ณ  interleaved๋กœ ๋Œ€์ฒด
304
+ if content_list[0]["type"] == "text":
305
+ content_list = content_list[1:] # ์›๋ณธ ํ…์ŠคํŠธ ์ œ๊ฑฐ
306
+ return interleaved_content + content_list # interleaved + ๋‚˜๋จธ์ง€ ๋ฌธ์„œ ๋ถ„์„ ๋‚ด์šฉ
307
  else:
308
  # ์ผ๋ฐ˜ ์—ฌ๋Ÿฌ ์žฅ
309
  for img_path in image_files:
 
311
 
312
  return content_list
313
 
314
+
315
  ##################################################
316
  # history -> LLM ๋ฉ”์‹œ์ง€ ๋ณ€ํ™˜
317
  ##################################################
 
331
  content = item["content"]
332
  if isinstance(content, str):
333
  current_user_content.append({"type": "text", "text": content})
334
+ elif isinstance(content, list) and len(content) > 0:
335
+ file_path = content[0]
336
+ if is_image_file(file_path):
337
+ current_user_content.append({"type": "image", "url": file_path})
338
+ else:
339
+ # ๋น„์ด๋ฏธ์ง€ ํŒŒ์ผ์€ ํ…์ŠคํŠธ๋กœ ์ฒ˜๋ฆฌ
340
+ current_user_content.append({"type": "text", "text": f"[File: {os.path.basename(file_path)}]"})
341
+
342
+ # ๋งˆ์ง€๋ง‰ ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€๊ฐ€ ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ถ”๊ฐ€
343
+ if current_user_content:
344
+ messages.append({"role": "user", "content": current_user_content})
345
+
346
  return messages
347
 
348
 
 
355
  yield ""
356
  return
357
 
358
+ try:
359
+ messages = []
360
+ if system_prompt:
361
+ messages.append({"role": "system", "content": [{"type": "text", "text": system_prompt}]})
362
+ messages.extend(process_history(history))
363
+
364
+ # ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ
365
+ user_content = process_new_user_message(message)
366
+
367
+ # ํ† ํฐ ์ˆ˜๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•ด ๋„ˆ๋ฌด ๊ธด ํ…์ŠคํŠธ๋Š” ์ž˜๋ผ๋‚ด๊ธฐ
368
+ for item in user_content:
369
+ if item["type"] == "text" and len(item["text"]) > MAX_CONTENT_CHARS:
370
+ item["text"] = item["text"][:MAX_CONTENT_CHARS] + "\n...(truncated)..."
371
+
372
+ messages.append({"role": "user", "content": user_content})
373
+
374
+ # ๋ชจ๋ธ ์ž…๋ ฅ ์ƒ์„ฑ ์ „ ์ตœ์ข… ํ™•์ธ
375
+ # ์ด๋ฏธ์ง€๋‚˜ ๋น„๋””์˜ค๊ฐ€ ์•„๋‹Œ ํŒŒ์ผ๋“ค์€ ๋ชจ๋ธ์˜ "image" ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ์ „๋‹ฌ๋˜์ง€ ์•Š๋„๋ก ํ•„ํ„ฐ๋ง
376
+ for msg in messages:
377
+ if msg["role"] != "user":
378
+ continue
379
+
380
+ filtered_content = []
381
+ for item in msg["content"]:
382
+ if item["type"] == "image":
383
+ if is_image_file(item["url"]):
384
+ filtered_content.append(item)
385
+ else:
386
+ # ์ด๋ฏธ์ง€ ํŒŒ์ผ์ด ์•„๋‹Œ ๊ฒฝ์šฐ ํ…์ŠคํŠธ๋กœ ๋ณ€ํ™˜
387
+ filtered_content.append({
388
+ "type": "text",
389
+ "text": f"[Non-image file: {os.path.basename(item['url'])}]"
390
+ })
391
+ else:
392
+ filtered_content.append(item)
393
+
394
+ msg["content"] = filtered_content
395
+
396
+ # ๋ชจ๋ธ ์ž…๋ ฅ ์ƒ์„ฑ
397
+ inputs = processor.apply_chat_template(
398
+ messages,
399
+ add_generation_prompt=True,
400
+ tokenize=True,
401
+ return_dict=True,
402
+ return_tensors="pt",
403
+ ).to(device=model.device, dtype=torch.bfloat16)
404
+
405
+ # ํ…์ŠคํŠธ ์ƒ์„ฑ ์ŠคํŠธ๋ฆฌ๋จธ ์„ค์ •
406
+ streamer = TextIteratorStreamer(processor, timeout=30.0, skip_prompt=True, skip_special_tokens=True)
407
+ gen_kwargs = dict(
408
+ inputs,
409
+ streamer=streamer,
410
+ max_new_tokens=max_new_tokens,
411
+ )
412
+
413
+ # ๋ณ„๋„ ์Šค๋ ˆ๋“œ์—์„œ ํ…์ŠคํŠธ ์ƒ์„ฑ
414
+ t = Thread(target=model.generate, kwargs=gen_kwargs)
415
+ t.start()
416
+
417
+ # ๊ฒฐ๊ณผ ์ŠคํŠธ๋ฆฌ๋ฐ
418
+ output = ""
419
+ for new_text in streamer:
420
+ output += new_text
421
+ yield output
422
+
423
+ except Exception as e:
424
+ logger.error(f"Error in run: {str(e)}")
425
+ yield f"์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}"
426
+
427
+
428
+
429
+
430
 
 
 
 
431
  ##################################################
432
  # ์˜ˆ์‹œ๋“ค (ํ•œ๊ธ€ํ™” ๋ฒ„์ „)
433
  ##################################################
 
561
  ]
562
 
563
 
564
+ ##################################################
565
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์„ค์ •
566
+ ##################################################
567
  demo = gr.ChatInterface(
568
  fn=run,
569
  type="messages",
 
598
  )
599
 
600
  if __name__ == "__main__":
601
+ demo.launch()