gbrabbit commited on
Commit
abb24e2
ยท
1 Parent(s): b9ecb65

Auto commit at 07-2025-08 4:57:05

Browse files
Files changed (2) hide show
  1. app.py +74 -64
  2. app_250807_0446.py +177 -0
app.py CHANGED
@@ -6,62 +6,85 @@ import torch
6
  import fitz # PyMuPDF
7
  from PIL import Image
8
  import io
 
9
 
10
  # --- 1. ์ „์—ญ ๋ณ€์ˆ˜ ๋ฐ ํ™˜๊ฒฝ ์„ค์ • ---
11
  tokenizer = None
12
  model = None
13
  MODEL_LOADED = False
 
 
 
 
14
 
15
  # .env ํŒŒ์ผ์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋กœ๋“œ (์ฃผ๋กœ ๋กœ์ปฌ์—์„œ ์‚ฌ์šฉ)
16
  try:
17
  from dotenv import load_dotenv
18
- load_dotenv()
19
- print("โœ… .env ํŒŒ์ผ ๋กœ๋“œ๋จ")
 
20
  except ImportError:
21
  print("โš ๏ธ python-dotenv๊ฐ€ ์„ค์น˜๋˜์ง€ ์•Š์Œ, ์‹œ์Šคํ…œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์‚ฌ์šฉ")
22
 
23
  # ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ ํ† ํฐ ๋ฐ ๋ชจ๋ธ ์ด๋ฆ„ ๊ฐ€์ ธ์˜ค๊ธฐ
24
  HF_TOKEN = os.getenv("HF_TOKEN")
25
- MODEL_NAME = os.getenv("MODEL_NAME", "gbrabbit/lily-math-model")
 
 
 
 
26
 
27
- print(f"๐Ÿ” ๋ชจ๋ธ: {MODEL_NAME}")
 
 
 
28
  print(f"๐Ÿ” HF ํ† ํฐ: {'โœ… ์„ค์ •๋จ'if HF_TOKEN else 'โŒ ์„ค์ •๋˜์ง€ ์•Š์Œ'}")
 
29
 
30
- # --- 2. ํ•ต์‹ฌ ๋กœ์ง: ๋ชจ๋ธ ๋ฐ ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ---
 
31
  try:
32
  print("๐Ÿ”ง ๋ชจ๋ธ ๋ฐ ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์‹œ์ž‘...")
33
-
34
- # ์ปค์Šคํ…€ ๋ชจ๋ธ ํด๋ž˜์Šค import
35
- from modeling import KananaVForConditionalGeneration
36
-
37
- if HF_TOKEN:
38
- tokenizer = AutoTokenizer.from_pretrained(
39
- MODEL_NAME,
40
- token=HF_TOKEN,
41
- trust_remote_code=True
42
- )
43
  model = KananaVForConditionalGeneration.from_pretrained(
44
- MODEL_NAME,
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  token=HF_TOKEN,
46
  torch_dtype=torch.float16,
47
  trust_remote_code=True,
48
- device_map="auto" # GPU ์ž๋™ ํ• ๋‹น (์„œ๋ฒ„ ํ™˜๊ฒฝ์— ํ•„์ˆ˜)
49
  )
50
- MODEL_LOADED = True
51
- print("โœ… ์ปค์Šคํ…€ ๋ชจ๋ธ ๋กœ๋”ฉ ์™„๋ฃŒ!")
52
- else:
53
- print("โš ๏ธ HF ํ† ํฐ์ด ์—†์–ด ๊ณต๊ฐœ ๋ชจ๋ธ(DialoGPT)๋กœ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค.")
54
- MODEL_NAME = "microsoft/DialoGPT-medium"
55
- tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
56
- model = AutoModelForCausalLM.from_pretrained(MODEL_NAME, torch_dtype=torch.float16, device_map="auto")
57
- MODEL_LOADED = True
58
 
59
  except Exception as e:
60
  print(f"โŒ ๋ชจ๋ธ ๋กœ๋”ฉ ์‹คํŒจ: {e}")
61
  traceback.print_exc()
62
  MODEL_LOADED = False
63
 
64
- # --- 3. ํŒŒ์ผ ์ฒ˜๋ฆฌ ์œ ํ‹ธ๋ฆฌํ‹ฐ ---
 
 
65
  def extract_text_from_pdf(pdf_file):
66
  try:
67
  doc = fitz.open(stream=pdf_file.read(), filetype="pdf")
@@ -73,44 +96,28 @@ def extract_text_from_pdf(pdf_file):
73
  return f"PDF ํŒŒ์ผ์„ ์ฝ๋Š” ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}"
74
 
75
  def process_uploaded_file(file):
76
- """์—…๋กœ๋“œ๋œ ํŒŒ์ผ์„ ํ…์ŠคํŠธ์™€ ์ด๋ฏธ์ง€ ๊ฐ์ฒด๋กœ ๋ถ„๋ฆฌ"""
77
  if file is None:
78
- return "", None # ํ…์ŠคํŠธ, ์ด๋ฏธ์ง€ ์—†์Œ
79
-
80
  file_path = file.name
81
  file_extension = os.path.splitext(file_path)[1].lower()
82
-
83
  if file_extension == '.pdf':
84
  text_content = extract_text_from_pdf(file)
85
- return text_content, None # PDF๋Š” ํ…์ŠคํŠธ๋งŒ, ์ด๋ฏธ์ง€๋Š” ์—†์Œ
86
  elif file_extension in ['.png', '.jpg', '.jpeg']:
87
  image = Image.open(file).convert('RGB')
88
- # ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ž์ฒด๋ฅผ ๋ฐ˜ํ™˜ (OCR ๋Œ€์‹  ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ์ž…๋ ฅ์œผ๋กœ ์‚ฌ์šฉ)
89
  return "์—…๋กœ๋“œ๋œ ์ด๋ฏธ์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.", image
90
  else:
91
  return f"์ง€์›ํ•˜์ง€ ์•Š๋Š” ํŒŒ์ผ ํ˜•์‹: {file_extension}", None
92
 
93
- # --- 4. ํ•ต์‹ฌ ๋กœ์ง: ํ†ตํ•ฉ ์‘๋‹ต ์ƒ์„ฑ ํ•จ์ˆ˜ ---
94
  def generate_response(prompt_template: str, message: str, file: Optional = None):
95
- """ํ…์ŠคํŠธ์™€ ์ด๋ฏธ์ง€๋ฅผ ๋ชจ๋‘ ์ฒ˜๋ฆฌํ•˜๋Š” ํ†ตํ•ฉ ์‘๋‹ต ์ƒ์„ฑ ํ•จ์ˆ˜"""
96
  if not MODEL_LOADED:
97
  return "โŒ ๋ชจ๋ธ์ด ๋กœ๋“œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž์—๊ฒŒ ๋ฌธ์˜ํ•˜์„ธ์š”."
98
-
99
  try:
100
- # 1. ํŒŒ์ผ ์ฒ˜๋ฆฌ
101
  file_text, pil_image = process_uploaded_file(file)
102
-
103
- # 2. ์ „์ฒด ํ”„๋กฌํ”„ํŠธ ๊ตฌ์„ฑ
104
- full_message = message
105
- if file_text:
106
- full_message += f"\n\n[์ฒจ๋ถ€ ํŒŒ์ผ ๋‚ด์šฉ]\n{file_text}"
107
-
108
  full_prompt = prompt_template.format(message=full_message)
109
-
110
- # 3. ํ† ํฌ๋‚˜์ด์ €๋กœ ํ…์ŠคํŠธ ์ž…๋ ฅ ๋ณ€ํ™˜
111
- inputs = tokenizer(full_prompt, return_tensors="pt").to(model.device)
112
 
113
- # 4. ์ƒ์„ฑ ํŒŒ๋ผ๋ฏธํ„ฐ ์ค€๋น„
114
  generation_args = {
115
  "max_new_tokens": 512,
116
  "temperature": 0.7,
@@ -118,38 +125,35 @@ def generate_response(prompt_template: str, message: str, file: Optional = None)
118
  "pad_token_id": tokenizer.eos_token_id
119
  }
120
 
121
- # 5. ์ด๋ฏธ์ง€๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ, ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ์ž…๋ ฅ ์ถ”๊ฐ€
122
  if pil_image:
123
  print("๐Ÿ–ผ๏ธ ์ด๋ฏธ์ง€ ํฌํ•จ, ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋“œ๋กœ ์ƒ์„ฑ")
124
- # KananaV ๋ชจ๋ธ์— ๋งž๋Š” ํ˜•ํƒœ๋กœ ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ
125
- # (๋ชจ๋ธ์˜ ์š”๊ตฌ์‚ฌํ•ญ์— ๋”ฐ๋ผ ์ด ๋ถ€๋ถ„์€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)
126
- pixel_values = model.vision_model.image_processor(pil_image, return_tensors='pt')['pixel_values']
127
- generation_args["pixel_values"] = pixel_values.to(model.device, dtype=torch.float16)
128
- else:
129
- print("๐Ÿ“„ ํ…์ŠคํŠธ๋งŒ์œผ๋กœ ์ƒ์„ฑ")
130
-
131
- # 6. ๋ชจ๋ธ์„ ํ†ตํ•ด ์‘๋‹ต ์ƒ์„ฑ (๋‹จ ํ•œ ๋ฒˆ์˜ ์˜ฌ๋ฐ”๋ฅธ ํ˜ธ์ถœ)
132
  with torch.no_grad():
133
  outputs = model.generate(**inputs, **generation_args)
134
 
135
- # 7. ์ƒ์„ฑ๋œ ํ† ํฐ ID๋ฅผ ํ…์ŠคํŠธ๋กœ ๋””์ฝ”๋”ฉ
136
- # ์ž…๋ ฅ ํ”„๋กฌํ”„ํŠธ ๋ถ€๋ถ„์„ ์ œ์™ธํ•˜๊ณ  ์ˆœ์ˆ˜ํ•œ ๋‹ต๋ณ€๋งŒ ์ถ”์ถœ
137
  input_length = inputs["input_ids"].shape[1]
138
  response_ids = outputs[0][input_length:]
139
  response = tokenizer.decode(response_ids, skip_special_tokens=True).strip()
140
 
141
  return response
142
-
143
  except Exception as e:
144
  print(f"โŒ ์‘๋‹ต ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {e}")
145
  traceback.print_exc()
146
  return f"์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}"
147
 
148
- # --- 5. Gradio UI ๋ฐ ์‹คํ–‰ ---
 
149
  with gr.Blocks(title="Lily Math RAG System", theme=gr.themes.Soft()) as demo:
 
150
  gr.Markdown("# ๐Ÿงฎ Lily Math RAG System")
151
  gr.Markdown("์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ ๋ฐ ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋Œ€ํ™”๋ฅผ ์œ„ํ•œ AI ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค.")
152
-
153
  with gr.Tabs():
154
  with gr.Tab("๐Ÿ’ฌ ์ฑ„ํŒ…"):
155
  chat_prompt = "<|im_start|>user\n{message}<|im_end|>\n<|im_start|>assistant\n"
@@ -165,13 +169,19 @@ with gr.Blocks(title="Lily Math RAG System", theme=gr.themes.Soft()) as demo:
165
  chat_history.append({"role": "user", "content": message})
166
  chat_history.append({"role": "assistant", "content": bot_message})
167
  return "", chat_history
168
-
169
  msg.submit(respond, [msg, chatbot, file_input], [msg, chatbot])
170
 
171
  with gr.Tab("โš™๏ธ ์‹œ์Šคํ…œ ์ •๋ณด"):
172
- gr.Markdown(f"**๋ชจ๋ธ**: `{MODEL_NAME}`")
 
173
  gr.Markdown(f"**๋ชจ๋ธ ์ƒํƒœ**: `{'โœ… ๋กœ๋“œ๋จ' if MODEL_LOADED else 'โŒ ๋กœ๋“œ ์‹คํŒจ'}`")
174
 
175
  if __name__ == "__main__":
176
- # share=True๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์™ธ๋ถ€์—์„œ๋„ ์ ‘์† ๊ฐ€๋Šฅํ•œ ๊ณต๊ฐœ ๋งํฌ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
177
- demo.launch(share=True)
 
 
 
 
 
 
 
6
  import fitz # PyMuPDF
7
  from PIL import Image
8
  import io
9
+ from typing import Optional
10
 
11
  # --- 1. ์ „์—ญ ๋ณ€์ˆ˜ ๋ฐ ํ™˜๊ฒฝ ์„ค์ • ---
12
  tokenizer = None
13
  model = None
14
  MODEL_LOADED = False
15
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
16
+
17
+ # ๋กœ์ปฌ/์„œ๋ฒ„ ํ™˜๊ฒฝ ์ž๋™ ๊ฐ์ง€
18
+ IS_LOCAL = os.path.exists('.env') or os.path.exists('../.env') or os.getenv('IS_LOCAL') == 'true'
19
 
20
  # .env ํŒŒ์ผ์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋กœ๋“œ (์ฃผ๋กœ ๋กœ์ปฌ์—์„œ ์‚ฌ์šฉ)
21
  try:
22
  from dotenv import load_dotenv
23
+ if IS_LOCAL:
24
+ load_dotenv()
25
+ print("โœ… .env ํŒŒ์ผ ๋กœ๋“œ๋จ")
26
  except ImportError:
27
  print("โš ๏ธ python-dotenv๊ฐ€ ์„ค์น˜๋˜์ง€ ์•Š์Œ, ์‹œ์Šคํ…œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์‚ฌ์šฉ")
28
 
29
  # ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ ํ† ํฐ ๋ฐ ๋ชจ๋ธ ์ด๋ฆ„ ๊ฐ€์ ธ์˜ค๊ธฐ
30
  HF_TOKEN = os.getenv("HF_TOKEN")
31
+ MODEL_NAME_SERVER = os.getenv("MODEL_NAME", "gbrabbit/lily-math-model")
32
+ MODEL_PATH_LOCAL = "../lily_llm_core/models/kanana-1.5-v-3b-instruct" # ์‚ฌ์šฉ์ž ๋กœ์ปฌ ๋ชจ๋ธ ๊ฒฝ๋กœ
33
+
34
+ # ์ตœ์ข… ๋ชจ๋ธ ๊ฒฝ๋กœ ์„ค์ •
35
+ MODEL_PATH = MODEL_PATH_LOCAL if IS_LOCAL else MODEL_NAME_SERVER
36
 
37
+ print(f"============== ์‹œ์Šคํ…œ ํ™˜๊ฒฝ ์ •๋ณด ==============")
38
+ print(f"๐Ÿ” ์‹คํ–‰ ํ™˜๊ฒฝ: {'๋กœ์ปฌ' if IS_LOCAL else '์„œ๋ฒ„'}")
39
+ print(f"๐Ÿ” ๋ชจ๋ธ ๊ฒฝ๋กœ: {MODEL_PATH}")
40
+ print(f"๐Ÿ” ์‚ฌ์šฉ ๋””๋ฐ”์ด์Šค: {DEVICE.upper()}")
41
  print(f"๐Ÿ” HF ํ† ํฐ: {'โœ… ์„ค์ •๋จ'if HF_TOKEN else 'โŒ ์„ค์ •๋˜์ง€ ์•Š์Œ'}")
42
+ print("==========================================")
43
 
44
+
45
+ # --- 2. ํ•ต์‹ฌ ๋กœ์ง: ์กฐ๊ฑด๋ถ€ ๋ชจ๋ธ ๋ฐ ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ---
46
  try:
47
  print("๐Ÿ”ง ๋ชจ๋ธ ๋ฐ ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์‹œ์ž‘...")
48
+ from modeling import KananaVForConditionalGeneration # ์ปค์Šคํ…€ ๋ชจ๋ธ ํด๋ž˜์Šค import
49
+
50
+ if IS_LOCAL:
51
+ # ๋กœ์ปฌ ํŒŒ์ผ ์‹œ์Šคํ…œ์—์„œ ๋ชจ๋ธ ๋กœ๋“œ
52
+ if not os.path.exists(MODEL_PATH):
53
+ raise FileNotFoundError(f"๋กœ์ปฌ ๋ชจ๋ธ ๊ฒฝ๋กœ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {MODEL_PATH}")
54
+
55
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH, trust_remote_code=True, local_files_only=True)
 
 
56
  model = KananaVForConditionalGeneration.from_pretrained(
57
+ MODEL_PATH,
58
+ torch_dtype=torch.float16,
59
+ trust_remote_code=True,
60
+ local_files_only=True
61
+ ).to(DEVICE) # ๋กœ์ปฌ GPU/CPU์— ํ• ๋‹น
62
+ print("โœ… ๋กœ์ปฌ ๋ชจ๋ธ ๋กœ๋”ฉ ์™„๋ฃŒ!")
63
+
64
+ else: # ์„œ๋ฒ„ ํ™˜๊ฒฝ
65
+ if not HF_TOKEN:
66
+ raise ValueError("์„œ๋ฒ„ ํ™˜๊ฒฝ์—์„œ๋Š” Hugging Face ํ† ํฐ(HF_TOKEN)์ด ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")
67
+
68
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH, token=HF_TOKEN, trust_remote_code=True)
69
+ model = KananaVForConditionalGeneration.from_pretrained(
70
+ MODEL_PATH,
71
  token=HF_TOKEN,
72
  torch_dtype=torch.float16,
73
  trust_remote_code=True,
74
+ device_map="auto" # ์„œ๋ฒ„ GPU ์ž๋™ ํ• ๋‹น
75
  )
76
+ print("โœ… ์„œ๋ฒ„ ๋ชจ๋ธ ๋กœ๋”ฉ ์™„๋ฃŒ!")
77
+
78
+ MODEL_LOADED = True
 
 
 
 
 
79
 
80
  except Exception as e:
81
  print(f"โŒ ๋ชจ๋ธ ๋กœ๋”ฉ ์‹คํŒจ: {e}")
82
  traceback.print_exc()
83
  MODEL_LOADED = False
84
 
85
+
86
+ # --- 3. ํŒŒ์ผ ์ฒ˜๋ฆฌ ๋ฐ ์‘๋‹ต ์ƒ์„ฑ ๋กœ์ง (์ด์ „๊ณผ ๋™์ผ) ---
87
+
88
  def extract_text_from_pdf(pdf_file):
89
  try:
90
  doc = fitz.open(stream=pdf_file.read(), filetype="pdf")
 
96
  return f"PDF ํŒŒ์ผ์„ ์ฝ๋Š” ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}"
97
 
98
  def process_uploaded_file(file):
 
99
  if file is None:
100
+ return "", None
 
101
  file_path = file.name
102
  file_extension = os.path.splitext(file_path)[1].lower()
 
103
  if file_extension == '.pdf':
104
  text_content = extract_text_from_pdf(file)
105
+ return text_content, None
106
  elif file_extension in ['.png', '.jpg', '.jpeg']:
107
  image = Image.open(file).convert('RGB')
 
108
  return "์—…๋กœ๋“œ๋œ ์ด๋ฏธ์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.", image
109
  else:
110
  return f"์ง€์›ํ•˜์ง€ ์•Š๋Š” ํŒŒ์ผ ํ˜•์‹: {file_extension}", None
111
 
 
112
  def generate_response(prompt_template: str, message: str, file: Optional = None):
 
113
  if not MODEL_LOADED:
114
  return "โŒ ๋ชจ๋ธ์ด ๋กœ๋“œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž์—๊ฒŒ ๋ฌธ์˜ํ•˜์„ธ์š”."
 
115
  try:
 
116
  file_text, pil_image = process_uploaded_file(file)
117
+ full_message = message + (f"\n\n[์ฒจ๋ถ€ ํŒŒ์ผ ๋‚ด์šฉ]\n{file_text}" if file_text else "")
 
 
 
 
 
118
  full_prompt = prompt_template.format(message=full_message)
119
+ inputs = tokenizer(full_prompt, return_tensors="pt").to(model.device if IS_LOCAL else model.device)
 
 
120
 
 
121
  generation_args = {
122
  "max_new_tokens": 512,
123
  "temperature": 0.7,
 
125
  "pad_token_id": tokenizer.eos_token_id
126
  }
127
 
 
128
  if pil_image:
129
  print("๐Ÿ–ผ๏ธ ์ด๋ฏธ์ง€ ํฌํ•จ, ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋“œ๋กœ ์ƒ์„ฑ")
130
+ # ์ฐธ๊ณ : ์ด image_processor ๋ถ€๋ถ„์€ ์‹ค์ œ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋ธ์— ๋งž์ถฐ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
131
+ # KananaV ๋ชจ๋ธ์˜ image_processor๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.
132
+ if hasattr(model, 'vision_model') and hasattr(model.vision_model, 'image_processor'):
133
+ pixel_values = model.vision_model.image_processor(pil_image, return_tensors='pt')['pixel_values']
134
+ generation_args["pixel_values"] = pixel_values.to(inputs.input_ids.device, dtype=torch.float16)
135
+ else:
136
+ print("โš ๏ธ ํ˜„์žฌ ๋ชจ๋ธ์— 'image_processor'๊ฐ€ ์ •์˜๋˜์ง€ ์•Š์•„ ์ด๋ฏธ์ง€๋ฅผ ๋ฌด์‹œํ•ฉ๋‹ˆ๋‹ค.")
137
+
138
  with torch.no_grad():
139
  outputs = model.generate(**inputs, **generation_args)
140
 
 
 
141
  input_length = inputs["input_ids"].shape[1]
142
  response_ids = outputs[0][input_length:]
143
  response = tokenizer.decode(response_ids, skip_special_tokens=True).strip()
144
 
145
  return response
 
146
  except Exception as e:
147
  print(f"โŒ ์‘๋‹ต ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {e}")
148
  traceback.print_exc()
149
  return f"์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}"
150
 
151
+ # --- 4. Gradio UI ๋ฐ ์กฐ๊ฑด๋ถ€ ์‹คํ–‰ ---
152
+
153
  with gr.Blocks(title="Lily Math RAG System", theme=gr.themes.Soft()) as demo:
154
+ # UI ๊ตฌ์„ฑ์€ ์ด์ „๊ณผ ๋™์ผ
155
  gr.Markdown("# ๐Ÿงฎ Lily Math RAG System")
156
  gr.Markdown("์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ ๋ฐ ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋Œ€ํ™”๋ฅผ ์œ„ํ•œ AI ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค.")
 
157
  with gr.Tabs():
158
  with gr.Tab("๐Ÿ’ฌ ์ฑ„ํŒ…"):
159
  chat_prompt = "<|im_start|>user\n{message}<|im_end|>\n<|im_start|>assistant\n"
 
169
  chat_history.append({"role": "user", "content": message})
170
  chat_history.append({"role": "assistant", "content": bot_message})
171
  return "", chat_history
 
172
  msg.submit(respond, [msg, chatbot, file_input], [msg, chatbot])
173
 
174
  with gr.Tab("โš™๏ธ ์‹œ์Šคํ…œ ์ •๋ณด"):
175
+ gr.Markdown(f"**์‹คํ–‰ ํ™˜๊ฒฝ**: `{'๋กœ์ปฌ' if IS_LOCAL else '์„œ๋ฒ„'}`")
176
+ gr.Markdown(f"**๋ชจ๋ธ ๊ฒฝ๋กœ**: `{MODEL_PATH}`")
177
  gr.Markdown(f"**๋ชจ๋ธ ์ƒํƒœ**: `{'โœ… ๋กœ๋“œ๋จ' if MODEL_LOADED else 'โŒ ๋กœ๋“œ ์‹คํŒจ'}`")
178
 
179
  if __name__ == "__main__":
180
+ if IS_LOCAL:
181
+ # ๋กœ์ปฌ ํ™˜๊ฒฝ์—๏ฟฝ๏ฟฝ๋Š” ๊ณ ์ •๋œ ์ฃผ์†Œ์™€ ํฌํŠธ๋กœ ์‹คํ–‰
182
+ print("\n๐Ÿš€ ๋กœ์ปฌ ์„œ๋ฒ„๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. http://127.0.0.1:7860")
183
+ demo.launch(server_name="127.0.0.1", server_port=7860)
184
+ else:
185
+ # ์„œ๋ฒ„ ํ™˜๊ฒฝ์—์„œ๋Š” ๊ณต๊ฐœ ๋งํฌ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์‹คํ–‰
186
+ print("\n๐Ÿš€ ์„œ๋ฒ„๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค...")
187
+ demo.launch(share=True)
app_250807_0446.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import traceback
4
+ from transformers import AutoTokenizer, AutoModelForCausalLM
5
+ import torch
6
+ import fitz # PyMuPDF
7
+ from PIL import Image
8
+ import io
9
+
10
+ # --- 1. ์ „์—ญ ๋ณ€์ˆ˜ ๋ฐ ํ™˜๊ฒฝ ์„ค์ • ---
11
+ tokenizer = None
12
+ model = None
13
+ MODEL_LOADED = False
14
+
15
+ # .env ํŒŒ์ผ์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋กœ๋“œ (์ฃผ๋กœ ๋กœ์ปฌ์—์„œ ์‚ฌ์šฉ)
16
+ try:
17
+ from dotenv import load_dotenv
18
+ load_dotenv()
19
+ print("โœ… .env ํŒŒ์ผ ๋กœ๋“œ๋จ")
20
+ except ImportError:
21
+ print("โš ๏ธ python-dotenv๊ฐ€ ์„ค์น˜๋˜์ง€ ์•Š์Œ, ์‹œ์Šคํ…œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์‚ฌ์šฉ")
22
+
23
+ # ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ ํ† ํฐ ๋ฐ ๋ชจ๋ธ ์ด๋ฆ„ ๊ฐ€์ ธ์˜ค๊ธฐ
24
+ HF_TOKEN = os.getenv("HF_TOKEN")
25
+ MODEL_NAME = os.getenv("MODEL_NAME", "gbrabbit/lily-math-model")
26
+
27
+ print(f"๐Ÿ” ๋ชจ๋ธ: {MODEL_NAME}")
28
+ print(f"๐Ÿ” HF ํ† ํฐ: {'โœ… ์„ค์ •๋จ'if HF_TOKEN else 'โŒ ์„ค์ •๋˜์ง€ ์•Š์Œ'}")
29
+
30
+ # --- 2. ํ•ต์‹ฌ ๋กœ์ง: ๋ชจ๋ธ ๋ฐ ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ---
31
+ try:
32
+ print("๐Ÿ”ง ๋ชจ๋ธ ๋ฐ ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์‹œ์ž‘...")
33
+
34
+ # ์ปค์Šคํ…€ ๋ชจ๋ธ ํด๋ž˜์Šค import
35
+ from modeling import KananaVForConditionalGeneration
36
+
37
+ if HF_TOKEN:
38
+ tokenizer = AutoTokenizer.from_pretrained(
39
+ MODEL_NAME,
40
+ token=HF_TOKEN,
41
+ trust_remote_code=True
42
+ )
43
+ model = KananaVForConditionalGeneration.from_pretrained(
44
+ MODEL_NAME,
45
+ token=HF_TOKEN,
46
+ torch_dtype=torch.float16,
47
+ trust_remote_code=True,
48
+ device_map="auto" # GPU ์ž๋™ ํ• ๋‹น (์„œ๋ฒ„ ํ™˜๊ฒฝ์— ํ•„์ˆ˜)
49
+ )
50
+ MODEL_LOADED = True
51
+ print("โœ… ์ปค์Šคํ…€ ๋ชจ๋ธ ๋กœ๋”ฉ ์™„๋ฃŒ!")
52
+ else:
53
+ print("โš ๏ธ HF ํ† ํฐ์ด ์—†์–ด ๊ณต๊ฐœ ๋ชจ๋ธ(DialoGPT)๋กœ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค.")
54
+ MODEL_NAME = "microsoft/DialoGPT-medium"
55
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
56
+ model = AutoModelForCausalLM.from_pretrained(MODEL_NAME, torch_dtype=torch.float16, device_map="auto")
57
+ MODEL_LOADED = True
58
+
59
+ except Exception as e:
60
+ print(f"โŒ ๋ชจ๋ธ ๋กœ๋”ฉ ์‹คํŒจ: {e}")
61
+ traceback.print_exc()
62
+ MODEL_LOADED = False
63
+
64
+ # --- 3. ํŒŒ์ผ ์ฒ˜๋ฆฌ ์œ ํ‹ธ๋ฆฌํ‹ฐ ---
65
+ def extract_text_from_pdf(pdf_file):
66
+ try:
67
+ doc = fitz.open(stream=pdf_file.read(), filetype="pdf")
68
+ text = "".join(page.get_text() for page in doc)
69
+ doc.close()
70
+ return text
71
+ except Exception as e:
72
+ print(f"PDF ์ฒ˜๋ฆฌ ์˜ค๋ฅ˜: {e}")
73
+ return f"PDF ํŒŒ์ผ์„ ์ฝ๋Š” ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}"
74
+
75
+ def process_uploaded_file(file):
76
+ """์—…๋กœ๋“œ๋œ ํŒŒ์ผ์„ ํ…์ŠคํŠธ์™€ ์ด๋ฏธ์ง€ ๊ฐ์ฒด๋กœ ๋ถ„๋ฆฌ"""
77
+ if file is None:
78
+ return "", None # ํ…์ŠคํŠธ, ์ด๋ฏธ์ง€ ์—†์Œ
79
+
80
+ file_path = file.name
81
+ file_extension = os.path.splitext(file_path)[1].lower()
82
+
83
+ if file_extension == '.pdf':
84
+ text_content = extract_text_from_pdf(file)
85
+ return text_content, None # PDF๋Š” ํ…์ŠคํŠธ๋งŒ, ์ด๋ฏธ์ง€๋Š” ์—†์Œ
86
+ elif file_extension in ['.png', '.jpg', '.jpeg']:
87
+ image = Image.open(file).convert('RGB')
88
+ # ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ž์ฒด๋ฅผ ๋ฐ˜ํ™˜ (OCR ๋Œ€์‹  ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ์ž…๋ ฅ์œผ๋กœ ์‚ฌ์šฉ)
89
+ return "์—…๋กœ๋“œ๋œ ์ด๋ฏธ์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.", image
90
+ else:
91
+ return f"์ง€์›ํ•˜์ง€ ์•Š๋Š” ํŒŒ์ผ ํ˜•์‹: {file_extension}", None
92
+
93
+ # --- 4. ํ•ต์‹ฌ ๋กœ์ง: ํ†ตํ•ฉ ์‘๋‹ต ์ƒ์„ฑ ํ•จ์ˆ˜ ---
94
+ def generate_response(prompt_template: str, message: str, file: Optional = None):
95
+ """ํ…์ŠคํŠธ์™€ ์ด๋ฏธ์ง€๋ฅผ ๋ชจ๋‘ ์ฒ˜๋ฆฌํ•˜๋Š” ํ†ตํ•ฉ ์‘๋‹ต ์ƒ์„ฑ ํ•จ์ˆ˜"""
96
+ if not MODEL_LOADED:
97
+ return "โŒ ๋ชจ๋ธ์ด ๋กœ๋“œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž์—๊ฒŒ ๋ฌธ์˜ํ•˜์„ธ์š”."
98
+
99
+ try:
100
+ # 1. ํŒŒ์ผ ์ฒ˜๋ฆฌ
101
+ file_text, pil_image = process_uploaded_file(file)
102
+
103
+ # 2. ์ „์ฒด ํ”„๋กฌํ”„ํŠธ ๊ตฌ์„ฑ
104
+ full_message = message
105
+ if file_text:
106
+ full_message += f"\n\n[์ฒจ๋ถ€ ํŒŒ์ผ ๋‚ด์šฉ]\n{file_text}"
107
+
108
+ full_prompt = prompt_template.format(message=full_message)
109
+
110
+ # 3. ํ† ํฌ๋‚˜์ด์ €๋กœ ํ…์ŠคํŠธ ์ž…๋ ฅ ๋ณ€ํ™˜
111
+ inputs = tokenizer(full_prompt, return_tensors="pt").to(model.device)
112
+
113
+ # 4. ์ƒ์„ฑ ํŒŒ๋ผ๋ฏธํ„ฐ ์ค€๋น„
114
+ generation_args = {
115
+ "max_new_tokens": 512,
116
+ "temperature": 0.7,
117
+ "do_sample": True,
118
+ "pad_token_id": tokenizer.eos_token_id
119
+ }
120
+
121
+ # 5. ์ด๋ฏธ์ง€๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ, ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ์ž…๋ ฅ ์ถ”๊ฐ€
122
+ if pil_image:
123
+ print("๐Ÿ–ผ๏ธ ์ด๋ฏธ์ง€ ํฌํ•จ, ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋“œ๋กœ ์ƒ์„ฑ")
124
+ # KananaV ๋ชจ๋ธ์— ๋งž๋Š” ํ˜•ํƒœ๋กœ ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ
125
+ # (๋ชจ๋ธ์˜ ์š”๊ตฌ์‚ฌํ•ญ์— ๋”ฐ๋ผ ์ด ๋ถ€๋ถ„์€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค)
126
+ pixel_values = model.vision_model.image_processor(pil_image, return_tensors='pt')['pixel_values']
127
+ generation_args["pixel_values"] = pixel_values.to(model.device, dtype=torch.float16)
128
+ else:
129
+ print("๐Ÿ“„ ํ…์ŠคํŠธ๋งŒ์œผ๋กœ ์ƒ์„ฑ")
130
+
131
+ # 6. ๋ชจ๋ธ์„ ํ†ตํ•ด ์‘๋‹ต ์ƒ์„ฑ (๋‹จ ํ•œ ๋ฒˆ์˜ ์˜ฌ๋ฐ”๋ฅธ ํ˜ธ์ถœ)
132
+ with torch.no_grad():
133
+ outputs = model.generate(**inputs, **generation_args)
134
+
135
+ # 7. ์ƒ์„ฑ๋œ ํ† ํฐ ID๋ฅผ ํ…์ŠคํŠธ๋กœ ๋””์ฝ”๋”ฉ
136
+ # ์ž…๋ ฅ ํ”„๋กฌํ”„ํŠธ ๋ถ€๋ถ„์„ ์ œ์™ธํ•˜๊ณ  ์ˆœ์ˆ˜ํ•œ ๋‹ต๋ณ€๋งŒ ์ถ”์ถœ
137
+ input_length = inputs["input_ids"].shape[1]
138
+ response_ids = outputs[0][input_length:]
139
+ response = tokenizer.decode(response_ids, skip_special_tokens=True).strip()
140
+
141
+ return response
142
+
143
+ except Exception as e:
144
+ print(f"โŒ ์‘๋‹ต ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {e}")
145
+ traceback.print_exc()
146
+ return f"์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}"
147
+
148
+ # --- 5. Gradio UI ๋ฐ ์‹คํ–‰ ---
149
+ with gr.Blocks(title="Lily Math RAG System", theme=gr.themes.Soft()) as demo:
150
+ gr.Markdown("# ๐Ÿงฎ Lily Math RAG System")
151
+ gr.Markdown("์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ ๋ฐ ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋Œ€ํ™”๋ฅผ ์œ„ํ•œ AI ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค.")
152
+
153
+ with gr.Tabs():
154
+ with gr.Tab("๐Ÿ’ฌ ์ฑ„ํŒ…"):
155
+ chat_prompt = "<|im_start|>user\n{message}<|im_end|>\n<|im_start|>assistant\n"
156
+ chatbot = gr.Chatbot(height=500, label="๋Œ€ํ™”์ฐฝ", type="messages")
157
+ with gr.Row():
158
+ with gr.Column(scale=4):
159
+ msg = gr.Textbox(label="๋ฉ”์‹œ์ง€", placeholder="์ด๋ฏธ์ง€๋‚˜ PDF๋ฅผ ์ฒจ๋ถ€ํ•˜๊ณ  ์งˆ๋ฌธํ•ด๋ณด์„ธ์š”!", lines=3, show_label=False)
160
+ with gr.Column(scale=1, min_width=150):
161
+ file_input = gr.File(label="ํŒŒ์ผ ์—…๋กœ๋“œ", file_types=[".pdf", ".png", ".jpg", ".jpeg"])
162
+
163
+ def respond(message, chat_history, file):
164
+ bot_message = generate_response(chat_prompt, message, file)
165
+ chat_history.append({"role": "user", "content": message})
166
+ chat_history.append({"role": "assistant", "content": bot_message})
167
+ return "", chat_history
168
+
169
+ msg.submit(respond, [msg, chatbot, file_input], [msg, chatbot])
170
+
171
+ with gr.Tab("โš™๏ธ ์‹œ์Šคํ…œ ์ •๋ณด"):
172
+ gr.Markdown(f"**๋ชจ๋ธ**: `{MODEL_NAME}`")
173
+ gr.Markdown(f"**๋ชจ๋ธ ์ƒํƒœ**: `{'โœ… ๋กœ๋“œ๋จ' if MODEL_LOADED else 'โŒ ๋กœ๋“œ ์‹คํŒจ'}`")
174
+
175
+ if __name__ == "__main__":
176
+ # share=True๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์™ธ๋ถ€์—์„œ๋„ ์ ‘์† ๊ฐ€๋Šฅํ•œ ๊ณต๊ฐœ ๋งํฌ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
177
+ demo.launch(share=True)