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

Auto commit at 07-2025-08 4:43:48

Browse files
Files changed (8) hide show
  1. .gitignore +219 -0
  2. app.py +101 -498
  3. app_250807_0427.py +574 -0
  4. app_local.py +245 -0
  5. app_local_250807_0427.py +245 -0
  6. test_input.py +100 -0
  7. test_text.py +100 -0
  8. test_tokenizer.py +159 -0
.gitignore ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .github/
2
+ .env
3
+ lily_llm_env/
4
+ lily_llm_core/models/
5
+ __pycache__/
6
+ *.pyc
7
+ .ipynb_checkpoints/
8
+ lily_llm_media/
9
+ vector_stores/
10
+ latex_ocr_env/
11
+ lily_llm_ignore/
12
+
13
+ # Byte-compiled / optimized / DLL files
14
+ __pycache__/
15
+ *.py[codz]
16
+ *$py.class
17
+
18
+ # C extensions
19
+ *.so
20
+
21
+ # Distribution / packaging
22
+ .Python
23
+ build/
24
+ develop-eggs/
25
+ dist/
26
+ downloads/
27
+ eggs/
28
+ .eggs/
29
+ lib/
30
+ lib64/
31
+ parts/
32
+ sdist/
33
+ var/
34
+ wheels/
35
+ share/python-wheels/
36
+ *.egg-info/
37
+ .installed.cfg
38
+ *.egg
39
+ MANIFEST
40
+
41
+ # PyInstaller
42
+ # Usually these files are written by a python script from a template
43
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
44
+ *.manifest
45
+ *.spec
46
+
47
+ # Installer logs
48
+ pip-log.txt
49
+ pip-delete-this-directory.txt
50
+
51
+ # Unit test / coverage reports
52
+ htmlcov/
53
+ .tox/
54
+ .nox/
55
+ .coverage
56
+ .coverage.*
57
+ .cache
58
+ nosetests.xml
59
+ coverage.xml
60
+ *.cover
61
+ *.py.cover
62
+ .hypothesis/
63
+ .pytest_cache/
64
+ cover/
65
+
66
+ # Translations
67
+ *.mo
68
+ *.pot
69
+
70
+ # Django stuff:
71
+ *.log
72
+ local_settings.py
73
+ db.sqlite3
74
+ db.sqlite3-journal
75
+
76
+ # Flask stuff:
77
+ instance/
78
+ .webassets-cache
79
+
80
+ # Scrapy stuff:
81
+ .scrapy
82
+
83
+ # Sphinx documentation
84
+ docs/_build/
85
+
86
+ # PyBuilder
87
+ .pybuilder/
88
+ target/
89
+
90
+ # Jupyter Notebook
91
+ .ipynb_checkpoints
92
+
93
+ # IPython
94
+ profile_default/
95
+ ipython_config.py
96
+
97
+ # pyenv
98
+ # For a library or package, you might want to ignore these files since the code is
99
+ # intended to run in multiple environments; otherwise, check them in:
100
+ # .python-version
101
+
102
+ # pipenv
103
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
104
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
105
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
106
+ # install all needed dependencies.
107
+ #Pipfile.lock
108
+
109
+ # UV
110
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
111
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
112
+ # commonly ignored for libraries.
113
+ #uv.lock
114
+
115
+ # poetry
116
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
117
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
118
+ # commonly ignored for libraries.
119
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
120
+ #poetry.lock
121
+ #poetry.toml
122
+
123
+ # pdm
124
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
125
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
126
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
127
+ #pdm.lock
128
+ #pdm.toml
129
+ .pdm-python
130
+ .pdm-build/
131
+
132
+ # pixi
133
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
134
+ #pixi.lock
135
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
136
+ # in the .venv directory. It is recommended not to include this directory in version control.
137
+ .pixi
138
+
139
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
140
+ __pypackages__/
141
+
142
+ # Celery stuff
143
+ celerybeat-schedule
144
+ celerybeat.pid
145
+
146
+ # SageMath parsed files
147
+ *.sage.py
148
+
149
+ # Environments
150
+ .env
151
+ .envrc
152
+ .venv
153
+ env/
154
+ venv/
155
+ ENV/
156
+ env.bak/
157
+ venv.bak/
158
+
159
+ # Spyder project settings
160
+ .spyderproject
161
+ .spyproject
162
+
163
+ # Rope project settings
164
+ .ropeproject
165
+
166
+ # mkdocs documentation
167
+ /site
168
+
169
+ # mypy
170
+ .mypy_cache/
171
+ .dmypy.json
172
+ dmypy.json
173
+
174
+ # Pyre type checker
175
+ .pyre/
176
+
177
+ # pytype static type analyzer
178
+ .pytype/
179
+
180
+ # Cython debug symbols
181
+ cython_debug/
182
+
183
+ # PyCharm
184
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
185
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
186
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
187
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
188
+ #.idea/
189
+
190
+ # Abstra
191
+ # Abstra is an AI-powered process automation framework.
192
+ # Ignore directories containing user credentials, local state, and settings.
193
+ # Learn more at https://abstra.io/docs
194
+ .abstra/
195
+
196
+ # Visual Studio Code
197
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
198
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
199
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
200
+ # you could uncomment the following to ignore the entire vscode folder
201
+ # .vscode/
202
+
203
+ # Ruff stuff:
204
+ .ruff_cache/
205
+
206
+ # PyPI configuration file
207
+ .pypirc
208
+
209
+ # Cursor
210
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
211
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
212
+ # refer to https://docs.cursor.com/context/ignore-files
213
+ .cursorignore
214
+ .cursorindexingignore
215
+
216
+ # Marimo
217
+ marimo/_static/
218
+ marimo/_lsp/
219
+ __marimo__/
app.py CHANGED
@@ -1,574 +1,177 @@
1
  import gradio as gr
2
  import os
3
- import requests
4
- import json
5
  import traceback
6
- from transformers import AutoTokenizer
7
  import torch
8
  import fitz # PyMuPDF
9
  from PIL import Image
10
  import io
11
- import base64
12
 
13
- # ์ „์—ญ ๋ณ€์ˆ˜๋กœ ์„ ์–ธ
14
  tokenizer = None
15
  model = None
16
  MODEL_LOADED = False
17
 
18
- # .env ํŒŒ์ผ์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋กœ๋“œ
19
  try:
20
  from dotenv import load_dotenv
21
  load_dotenv()
22
  print("โœ… .env ํŒŒ์ผ ๋กœ๋“œ๋จ")
23
  except ImportError:
24
  print("โš ๏ธ python-dotenv๊ฐ€ ์„ค์น˜๋˜์ง€ ์•Š์Œ, ์‹œ์Šคํ…œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์‚ฌ์šฉ")
25
- except Exception as e:
26
- print(f"โš ๏ธ .env ํŒŒ์ผ ๋กœ๋“œ ์‹คํŒจ: {e}")
27
 
28
- # ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ๋งŒ ํ† ํฐ ๊ฐ€์ ธ์˜ค๊ธฐ (๋ณด์•ˆ)
29
  HF_TOKEN = os.getenv("HF_TOKEN")
30
  MODEL_NAME = os.getenv("MODEL_NAME", "gbrabbit/lily-math-model")
31
 
32
- print("๐Ÿ” ์ƒ์„ธ ๋””๋ฒ„๊น… ์‹œ์ž‘")
33
- print("=" * 50)
34
- print(f"1. ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ํ™•์ธ:")
35
- print(f" HF_TOKEN: {'โœ… ์„ค์ •๋จ' if HF_TOKEN else 'โŒ ์„ค์ •๋˜์ง€ ์•Š์Œ'}")
36
- print(f" MODEL_NAME: {MODEL_NAME}")
37
- if HF_TOKEN:
38
- print(f" ํ† ํฐ ๊ธธ์ด: {len(HF_TOKEN)}")
39
- print(f" ํ† ํฐ ์‹œ์ž‘: {HF_TOKEN[:10]}...")
40
- print(f" ํ† ํฐ ๋: ...{HF_TOKEN[-10:]}")
41
 
42
- # ๋ชจ๋ธ ๋กœ๋“œ (์ปค์Šคํ…€ ๋ชจ๋ธ ํด๋ž˜์Šค ์‚ฌ์šฉ)
43
  try:
44
- print(f"\n2. ๋ชจ๋ธ ๋กœ๋”ฉ ์‹œ์ž‘:")
45
- print(f" ๋ชจ๋ธ: {MODEL_NAME}")
46
- print(f" ํ† ํฐ ์‚ฌ์šฉ: {'์˜ˆ' if HF_TOKEN else '์•„๋‹ˆ์˜ค'}")
47
-
 
48
  if HF_TOKEN:
49
- print(" ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์ค‘...")
50
  tokenizer = AutoTokenizer.from_pretrained(
51
  MODEL_NAME,
52
  token=HF_TOKEN,
 
 
 
 
 
 
53
  trust_remote_code=True,
54
- use_fast=False
55
  )
56
- print(" โœ… ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์™„๋ฃŒ")
57
- print(f" ํ† ํฌ๋‚˜์ด์ € ํƒ€์ž…: {type(tokenizer)}")
58
- print(f" ํ† ํฌ๋‚˜์ด์ € hasattr('encode'): {hasattr(tokenizer, 'encode')}")
59
-
60
- print(" ์ปค์Šคํ…€ ๋ชจ๋ธ ๋กœ๋”ฉ ์ค‘...")
61
- # ์ปค์Šคํ…€ ๋ชจ๋ธ ํด๋ž˜์Šค import (Space ํด๋”์˜ modeling.py ์‚ฌ์šฉ)
62
- try:
63
- from modeling import KananaVForConditionalGeneration
64
- print(" โœ… modeling.py import ์„ฑ๊ณต")
65
- except Exception as import_error:
66
- print(f" โŒ modeling.py import ์‹คํŒจ: {import_error}")
67
- raise import_error
68
-
69
- try:
70
- print(f" ๋ชจ๋ธ ๋กœ๋”ฉ ํŒŒ๋ผ๋ฏธํ„ฐ:")
71
- print(f" MODEL_NAME: {MODEL_NAME}")
72
- print(f" torch_dtype: {torch.float16}")
73
- print(f" trust_remote_code: True")
74
- print(f" device_map: None")
75
- print(f" low_cpu_mem_usage: True")
76
-
77
- model = KananaVForConditionalGeneration.from_pretrained(
78
- MODEL_NAME,
79
- token=HF_TOKEN,
80
- torch_dtype=torch.float16,
81
- trust_remote_code=True,
82
- device_map=None,
83
- low_cpu_mem_usage=True
84
- )
85
- print(" โœ… ์ปค์Šคํ…€ ๋ชจ๋ธ ๋กœ๋”ฉ ์™„๋ฃŒ")
86
- print(f" ๋ชจ๋ธ ํƒ€์ž…: {type(model)}")
87
- print(f" ๋ชจ๋ธ ๋””๋ฐ”์ด์Šค: {next(model.parameters()).device}")
88
- except Exception as model_error:
89
- print(f" โŒ ์ปค์Šคํ…€ ๋ชจ๋ธ ๋กœ๋”ฉ ์‹คํŒจ: {model_error}")
90
- print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(model_error).__name__}")
91
- import traceback
92
- traceback.print_exc()
93
- raise model_error
94
  else:
95
- print(" โš ๏ธ ํ† ํฐ์ด ์—†์–ด์„œ ๊ณต๊ฐœ ๋ชจ๋ธ ์‚ฌ์šฉ")
96
  MODEL_NAME = "microsoft/DialoGPT-medium"
97
- print(f" ๊ณต๊ฐœ ๋ชจ๋ธ: {MODEL_NAME}")
98
-
99
- print(" ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์ค‘...")
100
  tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
101
- print(" โœ… ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์™„๋ฃŒ")
102
-
103
- print(" ๋ชจ๋ธ ๋กœ๋”ฉ ์ค‘...")
104
- from transformers import AutoModelForCausalLM
105
- model = AutoModelForCausalLM.from_pretrained(MODEL_NAME, torch_dtype=torch.float16)
106
- print(" โœ… ๋ชจ๋ธ ๋กœ๋”ฉ ์™„๋ฃŒ")
107
-
108
- print("โœ… ๋ชจ๋ธ ๋กœ๋”ฉ ์™„๋ฃŒ!")
109
- MODEL_LOADED = True
110
 
111
  except Exception as e:
112
- print(f"โŒ ๋ชจ๋ธ ๋กœ๋”ฉ ์‹คํŒจ:")
113
- print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(e).__name__}")
114
- print(f" ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€: {str(e)}")
115
- print(f" ์ƒ์„ธ ์˜ค๋ฅ˜:")
116
  traceback.print_exc()
117
  MODEL_LOADED = False
118
 
119
- print(f"\n3. ์ตœ์ข… ์ƒํƒœ:")
120
- print(f" MODEL_LOADED: {MODEL_LOADED}")
121
- print(f" ์ตœ์ข… ๋ชจ๋ธ๋ช…: {MODEL_NAME}")
122
-
123
  def extract_text_from_pdf(pdf_file):
124
- """PDF์—์„œ ํ…์ŠคํŠธ ์ถ”์ถœ"""
125
  try:
126
  doc = fitz.open(stream=pdf_file.read(), filetype="pdf")
127
- text = ""
128
- for page in doc:
129
- text += page.get_text()
130
  doc.close()
131
  return text
132
  except Exception as e:
133
- return f"PDF ์ฝ๊ธฐ ์˜ค๋ฅ˜: {str(e)}"
134
-
135
- def extract_text_from_image(image_file):
136
- """์ด๋ฏธ์ง€์—์„œ OCR๋กœ ํ…์ŠคํŠธ ์ถ”์ถœ"""
137
- try:
138
- # PIL๋กœ ์ด๋ฏธ์ง€ ์—ด๊ธฐ
139
- image = Image.open(image_file)
140
-
141
- # ๊ฐ„๋‹จํ•œ OCR (์‹ค์ œ๋กœ๋Š” ๋” ์ •๊ตํ•œ OCR ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ ํ•„์š”)
142
- # ์—ฌ๊ธฐ์„œ๋Š” ์ด๋ฏธ์ง€ ์ •๋ณด๋งŒ ๋ฐ˜ํ™˜
143
- return f"์ด๋ฏธ์ง€ ํŒŒ์ผ: {image.size[0]}x{image.size[1]} ํ”ฝ์…€"
144
- except Exception as e:
145
- return f"์ด๋ฏธ์ง€ ์ฝ๊ธฐ ์˜ค๋ฅ˜: {str(e)}"
146
 
147
  def process_uploaded_file(file):
148
- """์—…๋กœ๋“œ๋œ ํŒŒ์ผ ์ฒ˜๋ฆฌ"""
149
  if file is None:
150
- return None, None
151
-
152
  file_path = file.name
153
- file_extension = file_path.lower().split('.')[-1]
154
 
155
- if file_extension == 'pdf':
156
  text_content = extract_text_from_pdf(file)
157
- return text_content, None
158
- elif file_extension in ['png', 'jpg', 'jpeg']:
159
- text_content = extract_text_from_image(file)
160
- return text_content, file
 
161
  else:
162
  return f"์ง€์›ํ•˜์ง€ ์•Š๋Š” ํŒŒ์ผ ํ˜•์‹: {file_extension}", None
163
 
164
- def chat_with_model(message, history, file=None):
165
- global tokenizer, model
166
- print(f"๐Ÿ” DEBUG: chat_with_model ์‹œ์ž‘")
167
- print(f" ๋ฉ”์‹œ์ง€: {message}")
168
- print(f" ํŒŒ์ผ: {file}")
169
- print(f" MODEL_LOADED: {MODEL_LOADED}")
170
-
171
  if not MODEL_LOADED:
172
- print("โŒ DEBUG: ๋ชจ๋ธ์ด ๋กœ๋“œ๋˜์ง€ ์•Š์Œ")
173
- return "โŒ ๋ชจ๋ธ์ด ๋กœ๋“œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."
174
-
175
  try:
176
- print("๐Ÿ“ DEBUG: ํŒŒ์ผ ์ฒ˜๋ฆฌ ์‹œ์ž‘")
177
- # ํŒŒ์ผ ์ฒ˜๋ฆฌ
178
- file_content = ""
179
- image_file = None
180
- if file is not None:
181
- print(f" ํŒŒ์ผ๋ช…: {file.name}")
182
- text_content, image_file = process_uploaded_file(file)
183
- print(f" ํ…์ŠคํŠธ ๋‚ด์šฉ: {text_content[:100] if text_content else 'None'}...")
184
- print(f" ์ด๋ฏธ์ง€ ํŒŒ์ผ: {image_file}")
185
- if text_content:
186
- file_content = f"\n[์—…๋กœ๋“œ๋œ ํŒŒ์ผ ๋‚ด์šฉ]\n{text_content}\n"
187
-
188
- # ๋ฉ”์‹œ์ง€์— ํŒŒ์ผ ๋‚ด์šฉ ์ถ”๊ฐ€
189
- full_message = message + file_content
190
- print(f"๐Ÿ“ DEBUG: ์ „์ฒด ๋ฉ”์‹œ์ง€: {full_message[:200]}...")
191
-
192
- print("๐Ÿ”ค DEBUG: ํ† ํฌ๋‚˜์ด์ € ์ฒ˜๋ฆฌ ์‹œ์ž‘")
193
- print(f" tokenizer ํƒ€์ž…: {type(tokenizer)}")
194
- print(f" tokenizer ๊ฐ’: {tokenizer}")
195
-
196
- # tokenizer๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธ
197
- if not hasattr(tokenizer, 'encode') or tokenizer is None or isinstance(tokenizer, bool):
198
- print("โŒ DEBUG: tokenizer๊ฐ€ ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์Œ")
199
- # tokenizer๋ฅผ ๋‹ค์‹œ ๋กœ๋“œ
200
- print("๐Ÿ”„ DEBUG: tokenizer ์žฌ๋กœ๋“œ ์‹œ๋„")
201
- try:
202
- tokenizer = AutoTokenizer.from_pretrained(
203
- MODEL_NAME,
204
- token=HF_TOKEN,
205
- trust_remote_code=True,
206
- use_fast=False
207
- )
208
- print("โœ… DEBUG: tokenizer ์žฌ๋กœ๋“œ ์„ฑ๊ณต")
209
- print(f" ์ƒˆ๋กœ์šด tokenizer ํƒ€์ž…: {type(tokenizer)}")
210
- except Exception as reload_error:
211
- print(f"โŒ DEBUG: tokenizer ์žฌ๋กœ๋“œ ์‹คํŒจ: {reload_error}")
212
- return f"ํ† ํฌ๋‚˜์ด์ € ์˜ค๋ฅ˜: {str(reload_error)}"
213
-
214
- inputs = tokenizer(full_message, return_tensors="pt")
215
- print(f" ์ž…๋ ฅ shape: {inputs['input_ids'].shape}")
216
- print(f" attention_mask shape: {inputs['attention_mask'].shape}")
217
-
218
- print("๐Ÿค– DEBUG: ๋ชจ๋ธ ์ถ”๋ก  ์‹œ์ž‘")
219
- with torch.no_grad():
220
- if image_file is not None:
221
- print("๐Ÿ–ผ๏ธ DEBUG: ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ๋ชจ๋“œ")
222
- # ์ด๋ฏธ์ง€๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ์ƒ์„ฑ
223
- import torchvision.transforms as transforms
224
-
225
- # ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ
226
- transform = transforms.Compose([
227
- transforms.Resize((224, 224)),
228
- transforms.ToTensor(),
229
- transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
230
- ])
231
-
232
- pil_image = Image.open(image_file).convert('RGB')
233
- pixel_values = transform(pil_image).unsqueeze(0)
234
- image_metas = {"vision_grid_thw": torch.tensor([[1, 14, 14]])} # ๊ธฐ๋ณธ ๊ทธ๋ฆฌ๋“œ ํฌ๊ธฐ
235
-
236
- print(f" ์ด๋ฏธ์ง€ shape: {pixel_values.shape}")
237
- print(f" ์ด๋ฏธ์ง€ ๋ฉ”ํƒ€: {image_metas}")
238
-
239
- # ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋ธ์˜ forward ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ
240
- print("๐Ÿ”„ DEBUG: ๋ชจ๋ธ ํ˜ธ์ถœ (๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ)")
241
- try:
242
- outputs = model(
243
- input_ids=inputs["input_ids"],
244
- attention_mask=inputs["attention_mask"],
245
- pixel_values=[pixel_values],
246
- image_metas=image_metas,
247
- max_new_tokens=200,
248
- temperature=0.7,
249
- do_sample=True,
250
- pad_token_id=tokenizer.eos_token_id
251
- )
252
- print("โœ… DEBUG: ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋ธ ํ˜ธ์ถœ ์„ฑ๊ณต")
253
- except Exception as model_error:
254
- print(f"โŒ DEBUG: ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋ธ ํ˜ธ์ถœ ์‹คํŒจ: {model_error}")
255
- print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(model_error).__name__}")
256
- raise model_error
257
- else:
258
- print("๐Ÿ“„ DEBUG: ํ…์ŠคํŠธ๋งŒ ์ฒ˜๋ฆฌ ๋ชจ๋“œ")
259
- # ํ…์ŠคํŠธ๋งŒ ์ƒ์„ฑ
260
- print("๐Ÿ”„ DEBUG: ๋ชจ๋ธ ํ˜ธ์ถœ (ํ…์ŠคํŠธ๋งŒ)")
261
- try:
262
- outputs = model(
263
- input_ids=inputs["input_ids"],
264
- attention_mask=inputs["attention_mask"],
265
- max_new_tokens=200,
266
- temperature=0.7,
267
- do_sample=True,
268
- pad_token_id=tokenizer.eos_token_id
269
- )
270
- print("โœ… DEBUG: ํ…์ŠคํŠธ ๋ชจ๋ธ ํ˜ธ์ถœ ์„ฑ๊ณต")
271
- except Exception as model_error:
272
- print(f"โŒ DEBUG: ํ…์ŠคํŠธ ๋ชจ๋ธ ํ˜ธ์ถœ ์‹คํŒจ: {model_error}")
273
- print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(model_error).__name__}")
274
- raise model_error
275
-
276
- print("๐Ÿ” DEBUG: ์ถœ๋ ฅ ์ฒ˜๋ฆฌ ์‹œ์ž‘")
277
- print(f" outputs ํƒ€์ž…: {type(outputs)}")
278
- print(f" outputs ๋‚ด์šฉ: {outputs}")
279
-
280
- # outputs๊ฐ€ ํŠœํ”Œ์ธ ๊ฒฝ์šฐ ์ฒซ ๋ฒˆ์งธ ์š”์†Œ ์‚ฌ์šฉ
281
- if isinstance(outputs, tuple):
282
- print("๐Ÿ“ฆ DEBUG: outputs๊ฐ€ ํŠœํ”Œ์ž„")
283
- logits = outputs[0]
284
- print(f" logits shape: {logits.shape}")
285
  else:
286
- print("๐Ÿ“ฆ DEBUG: outputs๊ฐ€ ๊ฐ์ฒด์ž„")
287
- if hasattr(outputs, 'logits'):
288
- logits = outputs.logits
289
- print(f" logits shape: {logits.shape}")
290
- else:
291
- logits = outputs
292
- print(f" outputs shape: {logits.shape}")
293
-
294
- print("๐ŸŽฏ DEBUG: ํ† ํฐ ์ƒ์„ฑ ์‹œ์ž‘")
295
- # ๊ฐ€์žฅ ๋†’์€ ํ™•๋ฅ ์˜ ํ† ํฐ ์„ ํƒ
296
- next_token = torch.argmax(logits[:, -1, :], dim=-1)
297
- generated_tokens = [next_token]
298
- print(f" ์ฒซ ๋ฒˆ์งธ ํ† ํฐ: {next_token.item()}")
299
-
300
- # ์ถ”๊ฐ€ ํ† ํฐ ์ƒ์„ฑ
301
- print("๐Ÿ”„ DEBUG: ๋ฐ˜๋ณต ํ† ํฐ ์ƒ์„ฑ ์‹œ์ž‘")
302
- for i in range(199): # max_new_tokens - 1
303
- if i % 50 == 0:
304
- print(f" ์ง„ํ–‰๋ฅ : {i}/199")
305
-
306
- inputs["input_ids"] = torch.cat([inputs["input_ids"], next_token.unsqueeze(-1)], dim=-1)
307
- inputs["attention_mask"] = torch.cat([inputs["attention_mask"], torch.ones_like(next_token.unsqueeze(-1))], dim=-1)
308
-
309
- with torch.no_grad():
310
- try:
311
- outputs = model(**inputs)
312
- if isinstance(outputs, tuple):
313
- logits = outputs[0]
314
- else:
315
- logits = outputs.logits if hasattr(outputs, 'logits') else outputs
316
-
317
- next_token = torch.argmax(logits[:, -1, :], dim=-1)
318
- generated_tokens.append(next_token)
319
-
320
- if next_token.item() == tokenizer.eos_token_id:
321
- print(f" EOS ํ† ํฐ ๋ฐœ๊ฒฌ: {i}๋ฒˆ์งธ")
322
- break
323
- except Exception as loop_error:
324
- print(f"โŒ DEBUG: ํ† ํฐ ์ƒ์„ฑ ๋ฃจํ”„ ์˜ค๋ฅ˜ (i={i}): {loop_error}")
325
- raise loop_error
326
-
327
- print("๐Ÿ”ค DEBUG: ํ† ํฐ ๋””์ฝ”๋”ฉ ์‹œ์ž‘")
328
- # ์ƒ์„ฑ๋œ ํ† ํฐ๋“ค์„ ๋””์ฝ”๋”ฉ
329
- generated_ids = torch.cat(generated_tokens, dim=0)
330
- response = tokenizer.decode(generated_ids, skip_special_tokens=True)
331
- print(f" ์›๋ณธ ์‘๋‹ต: {response[:200]}...")
332
-
333
- if full_message in response:
334
- response = response.replace(full_message, "").strip()
335
- print(f" ์ •๋ฆฌ๋œ ์‘๋‹ต: {response[:200]}...")
336
-
337
- print("โœ… DEBUG: chat_with_model ์™„๋ฃŒ")
338
- return response if response else "์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ์‘๋‹ต์„ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."
339
- except Exception as e:
340
- print(f"โŒ DEBUG: chat_with_model ์ „์ฒด ์˜ค๋ฅ˜: {e}")
341
- print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(e).__name__}")
342
- import traceback
343
- traceback.print_exc()
344
- return f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
345
 
346
- def solve_math_problem(problem, file=None):
347
- global tokenizer, model
348
- print(f"๐Ÿ” DEBUG: solve_math_problem ์‹œ์ž‘")
349
- print(f" ๋ฌธ์ œ: {problem}")
350
- print(f" ํŒŒ์ผ: {file}")
351
- print(f" MODEL_LOADED: {MODEL_LOADED}")
352
-
353
- if not MODEL_LOADED:
354
- print("โŒ DEBUG: ๋ชจ๋ธ์ด ๋กœ๋“œ๋˜์ง€ ์•Š์Œ")
355
- return "โŒ ๋ชจ๋ธ์ด ๋กœ๋“œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."
356
-
357
- try:
358
- print("๐Ÿ“ DEBUG: ํŒŒ์ผ ์ฒ˜๋ฆฌ ์‹œ์ž‘")
359
- # ํŒŒ์ผ ์ฒ˜๋ฆฌ
360
- file_content = ""
361
- image_file = None
362
- if file is not None:
363
- print(f" ํŒŒ์ผ๋ช…: {file.name}")
364
- text_content, image_file = process_uploaded_file(file)
365
- print(f" ํ…์ŠคํŠธ ๋‚ด์šฉ: {text_content[:100] if text_content else 'None'}...")
366
- print(f" ์ด๋ฏธ์ง€ ํŒŒ์ผ: {image_file}")
367
- if text_content:
368
- file_content = f"\n[์—…๋กœ๋“œ๋œ ํŒŒ์ผ ๋‚ด์šฉ]\n{text_content}\n"
369
-
370
- # ๋ฉ”์‹œ์ง€์— ํŒŒ์ผ ๋‚ด์šฉ ์ถ”๊ฐ€
371
- full_prompt = f"๋‹ค์Œ ์ˆ˜ํ•™ ๋ฌธ์ œ๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ํ’€์–ด์ฃผ์„ธ์š”: {problem}{file_content}"
372
- print(f"๐Ÿ“ DEBUG: ์ „์ฒด ํ”„๋กฌํ”„ํŠธ: {full_prompt[:200]}...")
373
-
374
- print("๐Ÿ”ค DEBUG: ํ† ํฌ๋‚˜์ด์ € ์ฒ˜๋ฆฌ ์‹œ์ž‘")
375
- print(f" tokenizer ํƒ€์ž…: {type(tokenizer)}")
376
- print(f" tokenizer ๊ฐ’: {tokenizer}")
377
-
378
- # tokenizer๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธ
379
- if not hasattr(tokenizer, 'encode') or tokenizer is None or isinstance(tokenizer, bool):
380
- print("โŒ DEBUG: tokenizer๊ฐ€ ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์Œ")
381
- # tokenizer๋ฅผ ๋‹ค์‹œ ๋กœ๋“œ
382
- print("๐Ÿ”„ DEBUG: tokenizer ์žฌ๋กœ๋“œ ์‹œ๋„")
383
- try:
384
- tokenizer = AutoTokenizer.from_pretrained(
385
- MODEL_NAME,
386
- token=HF_TOKEN,
387
- trust_remote_code=True,
388
- use_fast=False
389
- )
390
- print("โœ… DEBUG: tokenizer ์žฌ๋กœ๋“œ ์„ฑ๊ณต")
391
- print(f" ์ƒˆ๋กœ์šด tokenizer ํƒ€์ž…: {type(tokenizer)}")
392
- except Exception as reload_error:
393
- print(f"โŒ DEBUG: tokenizer ์žฌ๋กœ๋“œ ์‹คํŒจ: {reload_error}")
394
- return f"ํ† ํฌ๋‚˜์ด์ € ์˜ค๋ฅ˜: {str(reload_error)}"
395
-
396
- inputs = tokenizer(full_prompt, return_tensors="pt")
397
- print(f" ์ž…๋ ฅ shape: {inputs['input_ids'].shape}")
398
- print(f" attention_mask shape: {inputs['attention_mask'].shape}")
399
-
400
- print("๐Ÿค– DEBUG: ๋ชจ๋ธ ์ถ”๋ก  ์‹œ์ž‘")
401
  with torch.no_grad():
402
- if image_file is not None:
403
- print("๐Ÿ–ผ๏ธ DEBUG: ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ๋ชจ๋“œ")
404
- # ์ด๋ฏธ์ง€๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ์ƒ์„ฑ
405
- import torchvision.transforms as transforms
406
-
407
- # ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ
408
- transform = transforms.Compose([
409
- transforms.Resize((224, 224)),
410
- transforms.ToTensor(),
411
- transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
412
- ])
413
-
414
- pil_image = Image.open(image_file).convert('RGB')
415
- pixel_values = transform(pil_image).unsqueeze(0)
416
- image_metas = {"vision_grid_thw": torch.tensor([[1, 14, 14]])} # ๊ธฐ๋ณธ ๊ทธ๋ฆฌ๋“œ ํฌ๊ธฐ
417
-
418
- print(f" ์ด๋ฏธ์ง€ shape: {pixel_values.shape}")
419
- print(f" ์ด๋ฏธ์ง€ ๋ฉ”ํƒ€: {image_metas}")
420
-
421
- # ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋ธ์˜ forward ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ
422
- print("๐Ÿ”„ DEBUG: ๋ชจ๋ธ ํ˜ธ์ถœ (๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ)")
423
- try:
424
- outputs = model(
425
- input_ids=inputs["input_ids"],
426
- attention_mask=inputs["attention_mask"],
427
- pixel_values=[pixel_values],
428
- image_metas=image_metas,
429
- max_new_tokens=300,
430
- temperature=0.3,
431
- do_sample=True,
432
- pad_token_id=tokenizer.eos_token_id
433
- )
434
- print("โœ… DEBUG: ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋ธ ํ˜ธ์ถœ ์„ฑ๊ณต")
435
- except Exception as model_error:
436
- print(f"โŒ DEBUG: ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋ธ ํ˜ธ์ถœ ์‹คํŒจ: {model_error}")
437
- print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(model_error).__name__}")
438
- raise model_error
439
- else:
440
- print("๐Ÿ“„ DEBUG: ํ…์ŠคํŠธ๋งŒ ์ฒ˜๋ฆฌ ๋ชจ๋“œ")
441
- # ํ…์ŠคํŠธ๋งŒ ์ƒ์„ฑ
442
- print("๐Ÿ”„ DEBUG: ๋ชจ๋ธ ํ˜ธ์ถœ (ํ…์ŠคํŠธ๋งŒ)")
443
- try:
444
- outputs = model(
445
- input_ids=inputs["input_ids"],
446
- attention_mask=inputs["attention_mask"],
447
- max_new_tokens=300,
448
- temperature=0.3,
449
- do_sample=True,
450
- pad_token_id=tokenizer.eos_token_id
451
- )
452
- print("โœ… DEBUG: ํ…์ŠคํŠธ ๋ชจ๋ธ ํ˜ธ์ถœ ์„ฑ๊ณต")
453
- except Exception as model_error:
454
- print(f"โŒ DEBUG: ํ…์ŠคํŠธ ๋ชจ๋ธ ํ˜ธ์ถœ ์‹คํŒจ: {model_error}")
455
- print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(model_error).__name__}")
456
- raise model_error
457
-
458
- print("๐Ÿ” DEBUG: ์ถœ๋ ฅ ์ฒ˜๋ฆฌ ์‹œ์ž‘")
459
- print(f" outputs ํƒ€์ž…: {type(outputs)}")
460
- print(f" outputs ๋‚ด์šฉ: {outputs}")
461
-
462
- # outputs๊ฐ€ ํŠœํ”Œ์ธ ๊ฒฝ์šฐ ์ฒซ ๋ฒˆ์งธ ์š”์†Œ ์‚ฌ์šฉ
463
- if isinstance(outputs, tuple):
464
- print("๐Ÿ“ฆ DEBUG: outputs๊ฐ€ ํŠœํ”Œ์ž„")
465
- logits = outputs[0]
466
- print(f" logits shape: {logits.shape}")
467
- else:
468
- print("๐Ÿ“ฆ DEBUG: outputs๊ฐ€ ๊ฐ์ฒด์ž„")
469
- if hasattr(outputs, 'logits'):
470
- logits = outputs.logits
471
- print(f" logits shape: {logits.shape}")
472
- else:
473
- logits = outputs
474
- print(f" outputs shape: {logits.shape}")
475
-
476
- print("๐ŸŽฏ DEBUG: ํ† ํฐ ์ƒ์„ฑ ์‹œ์ž‘")
477
- # ๊ฐ€์žฅ ๋†’์€ ํ™•๋ฅ ์˜ ํ† ํฐ ์„ ํƒ
478
- next_token = torch.argmax(logits[:, -1, :], dim=-1)
479
- generated_tokens = [next_token]
480
- print(f" ์ฒซ ๋ฒˆ์งธ ํ† ํฐ: {next_token.item()}")
481
-
482
- # ์ถ”๊ฐ€ ํ† ํฐ ์ƒ์„ฑ
483
- print("๐Ÿ”„ DEBUG: ๋ฐ˜๋ณต ํ† ํฐ ์ƒ์„ฑ ์‹œ์ž‘")
484
- for i in range(299): # max_new_tokens - 1
485
- if i % 50 == 0:
486
- print(f" ์ง„ํ–‰๋ฅ : {i}/299")
487
-
488
- inputs["input_ids"] = torch.cat([inputs["input_ids"], next_token.unsqueeze(-1)], dim=-1)
489
- inputs["attention_mask"] = torch.cat([inputs["attention_mask"], torch.ones_like(next_token.unsqueeze(-1))], dim=-1)
490
-
491
- with torch.no_grad():
492
- try:
493
- outputs = model(**inputs)
494
- if isinstance(outputs, tuple):
495
- logits = outputs[0]
496
- else:
497
- logits = outputs.logits if hasattr(outputs, 'logits') else outputs
498
-
499
- next_token = torch.argmax(logits[:, -1, :], dim=-1)
500
- generated_tokens.append(next_token)
501
-
502
- if next_token.item() == tokenizer.eos_token_id:
503
- print(f" EOS ํ† ํฐ ๋ฐœ๊ฒฌ: {i}๋ฒˆ์งธ")
504
- break
505
- except Exception as loop_error:
506
- print(f"โŒ DEBUG: ํ† ํฐ ์ƒ์„ฑ ๋ฃจํ”„ ์˜ค๋ฅ˜ (i={i}): {loop_error}")
507
- raise loop_error
508
-
509
- print("๐Ÿ”ค DEBUG: ํ† ํฐ ๋””์ฝ”๋”ฉ ์‹œ์ž‘")
510
- # ์ƒ์„ฑ๋œ ํ† ํฐ๋“ค์„ ๋””์ฝ”๋”ฉ
511
- generated_ids = torch.cat(generated_tokens, dim=0)
512
- response = tokenizer.decode(generated_ids, skip_special_tokens=True)
513
- print(f" ์›๋ณธ ์‘๋‹ต: {response[:200]}...")
514
 
515
- if full_prompt in response:
516
- response = response.replace(full_prompt, "").strip()
517
- print(f" ์ •๋ฆฌ๋œ ์‘๋‹ต: {response[:200]}...")
 
 
518
 
519
- print("โœ… DEBUG: solve_math_problem ์™„๋ฃŒ")
520
- return response if response else "์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜ํ•™ ๋ฌธ์ œ๋ฅผ ํ’€ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."
521
  except Exception as e:
522
- print(f"โŒ DEBUG: solve_math_problem ์ „์ฒด ์˜ค๋ฅ˜: {e}")
523
- print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(e).__name__}")
524
- import traceback
525
  traceback.print_exc()
526
- return f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
527
 
 
528
  with gr.Blocks(title="Lily Math RAG System", theme=gr.themes.Soft()) as demo:
529
  gr.Markdown("# ๐Ÿงฎ Lily Math RAG System")
530
- gr.Markdown("์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ์„ ์œ„ํ•œ AI ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค.")
 
531
  with gr.Tabs():
532
  with gr.Tab("๐Ÿ’ฌ ์ฑ„ํŒ…"):
 
 
533
  with gr.Row():
534
- with gr.Column(scale=3):
535
- chatbot = gr.Chatbot(height=400, type="messages")
536
- msg = gr.Textbox(label="๋ฉ”์‹œ์ง€๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”", placeholder="์•ˆ๋…•ํ•˜์„ธ์š”! ์ˆ˜ํ•™ ๋ฌธ์ œ๋ฅผ ๋„์™€์ฃผ์„ธ์š”.", lines=2)
537
- clear = gr.Button("๋Œ€ํ™” ์ดˆ๊ธฐํ™”")
538
- with gr.Column(scale=1):
539
- gr.Markdown("### ๐Ÿ“ ํŒŒ์ผ ์—…๋กœ๋“œ")
540
- file_input = gr.File(label="PDF/์ด๋ฏธ์ง€ ํŒŒ์ผ (์„ ํƒ์‚ฌํ•ญ)", file_types=[".pdf", ".png", ".jpg", ".jpeg"])
541
- gr.Markdown("PDF๋‚˜ ์ด๋ฏธ์ง€ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•˜๋ฉด ๋ฌธ์„œ๋ฅผ ํ•ด์„ํ•˜์—ฌ ๋‹ต๋ณ€ํ•ฉ๋‹ˆ๋‹ค.")
542
 
543
  def respond(message, chat_history, file):
544
- bot_message = chat_with_model(message, chat_history, file)
545
  chat_history.append({"role": "user", "content": message})
546
  chat_history.append({"role": "assistant", "content": bot_message})
547
  return "", chat_history
 
548
  msg.submit(respond, [msg, chatbot, file_input], [msg, chatbot])
549
- clear.click(lambda: None, None, chatbot, queue=False)
550
-
551
- with gr.Tab("๐Ÿงฎ ์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ"):
552
- with gr.Row():
553
- with gr.Column(scale=2):
554
- math_input = gr.Textbox(label="์ˆ˜ํ•™ ๋ฌธ์ œ", placeholder="์˜ˆ: 2x + 5 = 13", lines=3)
555
- solve_btn = gr.Button("๋ฌธ์ œ ํ’€๊ธฐ", variant="primary")
556
- with gr.Column(scale=1):
557
- gr.Markdown("### ๐Ÿ“ ํŒŒ์ผ ์—…๋กœ๋“œ")
558
- math_file_input = gr.File(label="์ˆ˜ํ•™ ๋ฌธ์ œ ํŒŒ์ผ (์„ ํƒ์‚ฌํ•ญ)", file_types=[".pdf", ".png", ".jpg", ".jpeg"])
559
- gr.Markdown("์ˆ˜ํ•™ ๋ฌธ์ œ PDF๋‚˜ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๋ฉด ๋” ์ •ํ™•ํ•œ ๋‹ต๋ณ€์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")
560
- with gr.Column(scale=2):
561
- math_output = gr.Textbox(label="ํ•ด๋‹ต", lines=8, interactive=False)
562
- solve_btn.click(solve_math_problem, [math_input, math_file_input], math_output)
563
-
564
- with gr.Tab("โš™๏ธ ์„ค์ •"):
565
- gr.Markdown("## ์‹œ์Šคํ…œ ์ •๋ณด")
566
- gr.Markdown(f"**๋ชจ๋ธ**: {MODEL_NAME}")
567
- gr.Markdown(f"**๋ชจ๋ธ ์ƒํƒœ**: {'โœ… ๋กœ๋“œ๋จ' if MODEL_LOADED else 'โŒ ๋กœ๋“œ ์‹คํŒจ'}")
568
- gr.Markdown(f"**ํ† ํฐ ์ƒํƒœ**: {'โœ… ์„ค์ •๋จ' if HF_TOKEN else 'โŒ ์„ค์ •๋˜์ง€ ์•Š์Œ'}")
569
- gr.Markdown("**๋ฒ„์ „**: 3.0.0 (๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ)")
570
- gr.Markdown("**๊ธฐ๋Šฅ**: ํ…์ŠคํŠธ + ์ด๋ฏธ์ง€ ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋Œ€ํ™”")
571
 
572
  if __name__ == "__main__":
573
- demo.launch()
574
-
 
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)
app_250807_0427.py ADDED
@@ -0,0 +1,574 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import requests
4
+ import json
5
+ import traceback
6
+ from transformers import AutoTokenizer
7
+ import torch
8
+ import fitz # PyMuPDF
9
+ from PIL import Image
10
+ import io
11
+ import base64
12
+
13
+ # ์ „์—ญ ๋ณ€์ˆ˜๋กœ ์„ ์–ธ
14
+ tokenizer = None
15
+ model = None
16
+ MODEL_LOADED = False
17
+
18
+ # .env ํŒŒ์ผ์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋กœ๋“œ
19
+ try:
20
+ from dotenv import load_dotenv
21
+ load_dotenv()
22
+ print("โœ… .env ํŒŒ์ผ ๋กœ๋“œ๋จ")
23
+ except ImportError:
24
+ print("โš ๏ธ python-dotenv๊ฐ€ ์„ค์น˜๋˜์ง€ ์•Š์Œ, ์‹œ์Šคํ…œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์‚ฌ์šฉ")
25
+ except Exception as e:
26
+ print(f"โš ๏ธ .env ํŒŒ์ผ ๋กœ๋“œ ์‹คํŒจ: {e}")
27
+
28
+ # ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ๋งŒ ํ† ํฐ ๊ฐ€์ ธ์˜ค๊ธฐ (๋ณด์•ˆ)
29
+ HF_TOKEN = os.getenv("HF_TOKEN")
30
+ MODEL_NAME = os.getenv("MODEL_NAME", "gbrabbit/lily-math-model")
31
+
32
+ print("๐Ÿ” ์ƒ์„ธ ๋””๋ฒ„๊น… ์‹œ์ž‘")
33
+ print("=" * 50)
34
+ print(f"1. ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ํ™•์ธ:")
35
+ print(f" HF_TOKEN: {'โœ… ์„ค์ •๋จ' if HF_TOKEN else 'โŒ ์„ค์ •๋˜์ง€ ์•Š์Œ'}")
36
+ print(f" MODEL_NAME: {MODEL_NAME}")
37
+ if HF_TOKEN:
38
+ print(f" ํ† ํฐ ๊ธธ์ด: {len(HF_TOKEN)}")
39
+ print(f" ํ† ํฐ ์‹œ์ž‘: {HF_TOKEN[:10]}...")
40
+ print(f" ํ† ํฐ ๋: ...{HF_TOKEN[-10:]}")
41
+
42
+ # ๋ชจ๋ธ ๋กœ๋“œ (์ปค์Šคํ…€ ๋ชจ๋ธ ํด๋ž˜์Šค ์‚ฌ์šฉ)
43
+ try:
44
+ print(f"\n2. ๋ชจ๋ธ ๋กœ๋”ฉ ์‹œ์ž‘:")
45
+ print(f" ๋ชจ๋ธ: {MODEL_NAME}")
46
+ print(f" ํ† ํฐ ์‚ฌ์šฉ: {'์˜ˆ' if HF_TOKEN else '์•„๋‹ˆ์˜ค'}")
47
+
48
+ if HF_TOKEN:
49
+ print(" ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์ค‘...")
50
+ tokenizer = AutoTokenizer.from_pretrained(
51
+ MODEL_NAME,
52
+ token=HF_TOKEN,
53
+ trust_remote_code=True,
54
+ use_fast=False
55
+ )
56
+ print(" โœ… ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์™„๋ฃŒ")
57
+ print(f" ํ† ํฌ๋‚˜์ด์ € ํƒ€์ž…: {type(tokenizer)}")
58
+ print(f" ํ† ํฌ๋‚˜์ด์ € hasattr('encode'): {hasattr(tokenizer, 'encode')}")
59
+
60
+ print(" ์ปค์Šคํ…€ ๋ชจ๋ธ ๋กœ๋”ฉ ์ค‘...")
61
+ # ์ปค์Šคํ…€ ๋ชจ๋ธ ํด๋ž˜์Šค import (Space ํด๋”์˜ modeling.py ์‚ฌ์šฉ)
62
+ try:
63
+ from modeling import KananaVForConditionalGeneration
64
+ print(" โœ… modeling.py import ์„ฑ๊ณต")
65
+ except Exception as import_error:
66
+ print(f" โŒ modeling.py import ์‹คํŒจ: {import_error}")
67
+ raise import_error
68
+
69
+ try:
70
+ print(f" ๋ชจ๋ธ ๋กœ๋”ฉ ํŒŒ๋ผ๋ฏธํ„ฐ:")
71
+ print(f" MODEL_NAME: {MODEL_NAME}")
72
+ print(f" torch_dtype: {torch.float16}")
73
+ print(f" trust_remote_code: True")
74
+ print(f" device_map: None")
75
+ print(f" low_cpu_mem_usage: True")
76
+
77
+ model = KananaVForConditionalGeneration.from_pretrained(
78
+ MODEL_NAME,
79
+ token=HF_TOKEN,
80
+ torch_dtype=torch.float16,
81
+ trust_remote_code=True,
82
+ device_map=None,
83
+ low_cpu_mem_usage=True
84
+ )
85
+ print(" โœ… ์ปค์Šคํ…€ ๋ชจ๋ธ ๋กœ๋”ฉ ์™„๋ฃŒ")
86
+ print(f" ๋ชจ๋ธ ํƒ€์ž…: {type(model)}")
87
+ print(f" ๋ชจ๋ธ ๋””๋ฐ”์ด์Šค: {next(model.parameters()).device}")
88
+ except Exception as model_error:
89
+ print(f" โŒ ์ปค์Šคํ…€ ๋ชจ๋ธ ๋กœ๋”ฉ ์‹คํŒจ: {model_error}")
90
+ print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(model_error).__name__}")
91
+ import traceback
92
+ traceback.print_exc()
93
+ raise model_error
94
+ else:
95
+ print(" โš ๏ธ ํ† ํฐ์ด ์—†์–ด์„œ ๊ณต๊ฐœ ๋ชจ๋ธ ์‚ฌ์šฉ")
96
+ MODEL_NAME = "microsoft/DialoGPT-medium"
97
+ print(f" ๊ณต๊ฐœ ๋ชจ๋ธ: {MODEL_NAME}")
98
+
99
+ print(" ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์ค‘...")
100
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
101
+ print(" โœ… ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์™„๋ฃŒ")
102
+
103
+ print(" ๋ชจ๋ธ ๋กœ๋”ฉ ์ค‘...")
104
+ from transformers import AutoModelForCausalLM
105
+ model = AutoModelForCausalLM.from_pretrained(MODEL_NAME, torch_dtype=torch.float16)
106
+ print(" โœ… ๋ชจ๋ธ ๋กœ๋”ฉ ์™„๋ฃŒ")
107
+
108
+ print("โœ… ๋ชจ๋ธ ๋กœ๋”ฉ ์™„๋ฃŒ!")
109
+ MODEL_LOADED = True
110
+
111
+ except Exception as e:
112
+ print(f"โŒ ๋ชจ๋ธ ๋กœ๋”ฉ ์‹คํŒจ:")
113
+ print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(e).__name__}")
114
+ print(f" ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€: {str(e)}")
115
+ print(f" ์ƒ์„ธ ์˜ค๋ฅ˜:")
116
+ traceback.print_exc()
117
+ MODEL_LOADED = False
118
+
119
+ print(f"\n3. ์ตœ์ข… ์ƒํƒœ:")
120
+ print(f" MODEL_LOADED: {MODEL_LOADED}")
121
+ print(f" ์ตœ์ข… ๋ชจ๋ธ๋ช…: {MODEL_NAME}")
122
+
123
+ def extract_text_from_pdf(pdf_file):
124
+ """PDF์—์„œ ํ…์ŠคํŠธ ์ถ”์ถœ"""
125
+ try:
126
+ doc = fitz.open(stream=pdf_file.read(), filetype="pdf")
127
+ text = ""
128
+ for page in doc:
129
+ text += page.get_text()
130
+ doc.close()
131
+ return text
132
+ except Exception as e:
133
+ return f"PDF ์ฝ๊ธฐ ์˜ค๋ฅ˜: {str(e)}"
134
+
135
+ def extract_text_from_image(image_file):
136
+ """์ด๋ฏธ์ง€์—์„œ OCR๋กœ ํ…์ŠคํŠธ ์ถ”์ถœ"""
137
+ try:
138
+ # PIL๋กœ ์ด๋ฏธ์ง€ ์—ด๊ธฐ
139
+ image = Image.open(image_file)
140
+
141
+ # ๊ฐ„๋‹จํ•œ OCR (์‹ค์ œ๋กœ๋Š” ๋” ์ •๊ตํ•œ OCR ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ ํ•„์š”)
142
+ # ์—ฌ๊ธฐ์„œ๋Š” ์ด๋ฏธ์ง€ ์ •๋ณด๋งŒ ๋ฐ˜ํ™˜
143
+ return f"์ด๋ฏธ์ง€ ํŒŒ์ผ: {image.size[0]}x{image.size[1]} ํ”ฝ์…€"
144
+ except Exception as e:
145
+ return f"์ด๋ฏธ์ง€ ์ฝ๊ธฐ ์˜ค๋ฅ˜: {str(e)}"
146
+
147
+ def process_uploaded_file(file):
148
+ """์—…๋กœ๋“œ๋œ ํŒŒ์ผ ์ฒ˜๋ฆฌ"""
149
+ if file is None:
150
+ return None, None
151
+
152
+ file_path = file.name
153
+ file_extension = file_path.lower().split('.')[-1]
154
+
155
+ if file_extension == 'pdf':
156
+ text_content = extract_text_from_pdf(file)
157
+ return text_content, None
158
+ elif file_extension in ['png', 'jpg', 'jpeg']:
159
+ text_content = extract_text_from_image(file)
160
+ return text_content, file
161
+ else:
162
+ return f"์ง€์›ํ•˜์ง€ ์•Š๋Š” ํŒŒ์ผ ํ˜•์‹: {file_extension}", None
163
+
164
+ def chat_with_model(message, history, file=None):
165
+ global tokenizer, model
166
+ print(f"๐Ÿ” DEBUG: chat_with_model ์‹œ์ž‘")
167
+ print(f" ๋ฉ”์‹œ์ง€: {message}")
168
+ print(f" ํŒŒ์ผ: {file}")
169
+ print(f" MODEL_LOADED: {MODEL_LOADED}")
170
+
171
+ if not MODEL_LOADED:
172
+ print("โŒ DEBUG: ๋ชจ๋ธ์ด ๋กœ๋“œ๋˜์ง€ ์•Š์Œ")
173
+ return "โŒ ๋ชจ๋ธ์ด ๋กœ๋“œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."
174
+
175
+ try:
176
+ print("๐Ÿ“ DEBUG: ํŒŒ์ผ ์ฒ˜๋ฆฌ ์‹œ์ž‘")
177
+ # ํŒŒ์ผ ์ฒ˜๋ฆฌ
178
+ file_content = ""
179
+ image_file = None
180
+ if file is not None:
181
+ print(f" ํŒŒ์ผ๋ช…: {file.name}")
182
+ text_content, image_file = process_uploaded_file(file)
183
+ print(f" ํ…์ŠคํŠธ ๋‚ด์šฉ: {text_content[:100] if text_content else 'None'}...")
184
+ print(f" ์ด๋ฏธ์ง€ ํŒŒ์ผ: {image_file}")
185
+ if text_content:
186
+ file_content = f"\n[์—…๋กœ๋“œ๋œ ํŒŒ์ผ ๋‚ด์šฉ]\n{text_content}\n"
187
+
188
+ # ๋ฉ”์‹œ์ง€์— ํŒŒ์ผ ๋‚ด์šฉ ์ถ”๊ฐ€
189
+ full_message = message + file_content
190
+ print(f"๐Ÿ“ DEBUG: ์ „์ฒด ๋ฉ”์‹œ์ง€: {full_message[:200]}...")
191
+
192
+ print("๐Ÿ”ค DEBUG: ํ† ํฌ๋‚˜์ด์ € ์ฒ˜๋ฆฌ ์‹œ์ž‘")
193
+ print(f" tokenizer ํƒ€์ž…: {type(tokenizer)}")
194
+ print(f" tokenizer ๊ฐ’: {tokenizer}")
195
+
196
+ # tokenizer๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธ
197
+ if not hasattr(tokenizer, 'encode') or tokenizer is None or isinstance(tokenizer, bool):
198
+ print("โŒ DEBUG: tokenizer๊ฐ€ ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์Œ")
199
+ # tokenizer๋ฅผ ๋‹ค์‹œ ๋กœ๋“œ
200
+ print("๐Ÿ”„ DEBUG: tokenizer ์žฌ๋กœ๋“œ ์‹œ๋„")
201
+ try:
202
+ tokenizer = AutoTokenizer.from_pretrained(
203
+ MODEL_NAME,
204
+ token=HF_TOKEN,
205
+ trust_remote_code=True,
206
+ use_fast=False
207
+ )
208
+ print("โœ… DEBUG: tokenizer ์žฌ๋กœ๋“œ ์„ฑ๊ณต")
209
+ print(f" ์ƒˆ๋กœ์šด tokenizer ํƒ€์ž…: {type(tokenizer)}")
210
+ except Exception as reload_error:
211
+ print(f"โŒ DEBUG: tokenizer ์žฌ๋กœ๋“œ ์‹คํŒจ: {reload_error}")
212
+ return f"ํ† ํฌ๋‚˜์ด์ € ์˜ค๋ฅ˜: {str(reload_error)}"
213
+
214
+ inputs = tokenizer(full_message, return_tensors="pt")
215
+ print(f" ์ž…๋ ฅ shape: {inputs['input_ids'].shape}")
216
+ print(f" attention_mask shape: {inputs['attention_mask'].shape}")
217
+
218
+ print("๐Ÿค– DEBUG: ๋ชจ๋ธ ์ถ”๋ก  ์‹œ์ž‘")
219
+ with torch.no_grad():
220
+ if image_file is not None:
221
+ print("๐Ÿ–ผ๏ธ DEBUG: ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ๋ชจ๋“œ")
222
+ # ์ด๋ฏธ์ง€๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ์ƒ์„ฑ
223
+ import torchvision.transforms as transforms
224
+
225
+ # ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ
226
+ transform = transforms.Compose([
227
+ transforms.Resize((224, 224)),
228
+ transforms.ToTensor(),
229
+ transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
230
+ ])
231
+
232
+ pil_image = Image.open(image_file).convert('RGB')
233
+ pixel_values = transform(pil_image).unsqueeze(0)
234
+ image_metas = {"vision_grid_thw": torch.tensor([[1, 14, 14]])} # ๊ธฐ๋ณธ ๊ทธ๋ฆฌ๋“œ ํฌ๊ธฐ
235
+
236
+ print(f" ์ด๋ฏธ์ง€ shape: {pixel_values.shape}")
237
+ print(f" ์ด๋ฏธ์ง€ ๋ฉ”ํƒ€: {image_metas}")
238
+
239
+ # ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋ธ์˜ forward ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ
240
+ print("๐Ÿ”„ DEBUG: ๋ชจ๋ธ ํ˜ธ์ถœ (๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ)")
241
+ try:
242
+ outputs = model(
243
+ input_ids=inputs["input_ids"],
244
+ attention_mask=inputs["attention_mask"],
245
+ pixel_values=[pixel_values],
246
+ image_metas=image_metas,
247
+ max_new_tokens=200,
248
+ temperature=0.7,
249
+ do_sample=True,
250
+ pad_token_id=tokenizer.eos_token_id
251
+ )
252
+ print("โœ… DEBUG: ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋ธ ํ˜ธ์ถœ ์„ฑ๊ณต")
253
+ except Exception as model_error:
254
+ print(f"โŒ DEBUG: ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋ธ ํ˜ธ์ถœ ์‹คํŒจ: {model_error}")
255
+ print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(model_error).__name__}")
256
+ raise model_error
257
+ else:
258
+ print("๐Ÿ“„ DEBUG: ํ…์ŠคํŠธ๋งŒ ์ฒ˜๋ฆฌ ๋ชจ๋“œ")
259
+ # ํ…์ŠคํŠธ๋งŒ ์ƒ์„ฑ
260
+ print("๐Ÿ”„ DEBUG: ๋ชจ๋ธ ํ˜ธ์ถœ (ํ…์ŠคํŠธ๋งŒ)")
261
+ try:
262
+ outputs = model(
263
+ input_ids=inputs["input_ids"],
264
+ attention_mask=inputs["attention_mask"],
265
+ max_new_tokens=200,
266
+ temperature=0.7,
267
+ do_sample=True,
268
+ pad_token_id=tokenizer.eos_token_id
269
+ )
270
+ print("โœ… DEBUG: ํ…์ŠคํŠธ ๋ชจ๋ธ ํ˜ธ์ถœ ์„ฑ๊ณต")
271
+ except Exception as model_error:
272
+ print(f"โŒ DEBUG: ํ…์ŠคํŠธ ๋ชจ๋ธ ํ˜ธ์ถœ ์‹คํŒจ: {model_error}")
273
+ print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(model_error).__name__}")
274
+ raise model_error
275
+
276
+ print("๐Ÿ” DEBUG: ์ถœ๋ ฅ ์ฒ˜๋ฆฌ ์‹œ์ž‘")
277
+ print(f" outputs ํƒ€์ž…: {type(outputs)}")
278
+ print(f" outputs ๋‚ด์šฉ: {outputs}")
279
+
280
+ # outputs๊ฐ€ ํŠœํ”Œ์ธ ๊ฒฝ์šฐ ์ฒซ ๋ฒˆ์งธ ์š”์†Œ ์‚ฌ์šฉ
281
+ if isinstance(outputs, tuple):
282
+ print("๐Ÿ“ฆ DEBUG: outputs๊ฐ€ ํŠœํ”Œ์ž„")
283
+ logits = outputs[0]
284
+ print(f" logits shape: {logits.shape}")
285
+ else:
286
+ print("๐Ÿ“ฆ DEBUG: outputs๊ฐ€ ๊ฐ์ฒด์ž„")
287
+ if hasattr(outputs, 'logits'):
288
+ logits = outputs.logits
289
+ print(f" logits shape: {logits.shape}")
290
+ else:
291
+ logits = outputs
292
+ print(f" outputs shape: {logits.shape}")
293
+
294
+ print("๐ŸŽฏ DEBUG: ํ† ํฐ ์ƒ์„ฑ ์‹œ์ž‘")
295
+ # ๊ฐ€์žฅ ๋†’์€ ํ™•๋ฅ ์˜ ํ† ํฐ ์„ ํƒ
296
+ next_token = torch.argmax(logits[:, -1, :], dim=-1)
297
+ generated_tokens = [next_token]
298
+ print(f" ์ฒซ ๋ฒˆ์งธ ํ† ํฐ: {next_token.item()}")
299
+
300
+ # ์ถ”๊ฐ€ ํ† ํฐ ์ƒ์„ฑ
301
+ print("๐Ÿ”„ DEBUG: ๋ฐ˜๋ณต ํ† ํฐ ์ƒ์„ฑ ์‹œ์ž‘")
302
+ for i in range(199): # max_new_tokens - 1
303
+ if i % 50 == 0:
304
+ print(f" ์ง„ํ–‰๋ฅ : {i}/199")
305
+
306
+ inputs["input_ids"] = torch.cat([inputs["input_ids"], next_token.unsqueeze(-1)], dim=-1)
307
+ inputs["attention_mask"] = torch.cat([inputs["attention_mask"], torch.ones_like(next_token.unsqueeze(-1))], dim=-1)
308
+
309
+ with torch.no_grad():
310
+ try:
311
+ outputs = model(**inputs)
312
+ if isinstance(outputs, tuple):
313
+ logits = outputs[0]
314
+ else:
315
+ logits = outputs.logits if hasattr(outputs, 'logits') else outputs
316
+
317
+ next_token = torch.argmax(logits[:, -1, :], dim=-1)
318
+ generated_tokens.append(next_token)
319
+
320
+ if next_token.item() == tokenizer.eos_token_id:
321
+ print(f" EOS ํ† ํฐ ๋ฐœ๊ฒฌ: {i}๋ฒˆ์งธ")
322
+ break
323
+ except Exception as loop_error:
324
+ print(f"โŒ DEBUG: ํ† ํฐ ์ƒ์„ฑ ๋ฃจํ”„ ์˜ค๋ฅ˜ (i={i}): {loop_error}")
325
+ raise loop_error
326
+
327
+ print("๐Ÿ”ค DEBUG: ํ† ํฐ ๋””์ฝ”๋”ฉ ์‹œ์ž‘")
328
+ # ์ƒ์„ฑ๋œ ํ† ํฐ๋“ค์„ ๋””์ฝ”๋”ฉ
329
+ generated_ids = torch.cat(generated_tokens, dim=0)
330
+ response = tokenizer.decode(generated_ids, skip_special_tokens=True)
331
+ print(f" ์›๋ณธ ์‘๋‹ต: {response[:200]}...")
332
+
333
+ if full_message in response:
334
+ response = response.replace(full_message, "").strip()
335
+ print(f" ์ •๋ฆฌ๋œ ์‘๋‹ต: {response[:200]}...")
336
+
337
+ print("โœ… DEBUG: chat_with_model ์™„๋ฃŒ")
338
+ return response if response else "์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ์‘๋‹ต์„ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."
339
+ except Exception as e:
340
+ print(f"โŒ DEBUG: chat_with_model ์ „์ฒด ์˜ค๋ฅ˜: {e}")
341
+ print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(e).__name__}")
342
+ import traceback
343
+ traceback.print_exc()
344
+ return f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
345
+
346
+ def solve_math_problem(problem, file=None):
347
+ global tokenizer, model
348
+ print(f"๐Ÿ” DEBUG: solve_math_problem ์‹œ์ž‘")
349
+ print(f" ๋ฌธ์ œ: {problem}")
350
+ print(f" ํŒŒ์ผ: {file}")
351
+ print(f" MODEL_LOADED: {MODEL_LOADED}")
352
+
353
+ if not MODEL_LOADED:
354
+ print("โŒ DEBUG: ๋ชจ๋ธ์ด ๋กœ๋“œ๋˜์ง€ ์•Š์Œ")
355
+ return "โŒ ๋ชจ๋ธ์ด ๋กœ๋“œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."
356
+
357
+ try:
358
+ print("๐Ÿ“ DEBUG: ํŒŒ์ผ ์ฒ˜๋ฆฌ ์‹œ์ž‘")
359
+ # ํŒŒ์ผ ์ฒ˜๋ฆฌ
360
+ file_content = ""
361
+ image_file = None
362
+ if file is not None:
363
+ print(f" ํŒŒ์ผ๋ช…: {file.name}")
364
+ text_content, image_file = process_uploaded_file(file)
365
+ print(f" ํ…์ŠคํŠธ ๋‚ด์šฉ: {text_content[:100] if text_content else 'None'}...")
366
+ print(f" ์ด๋ฏธ์ง€ ํŒŒ์ผ: {image_file}")
367
+ if text_content:
368
+ file_content = f"\n[์—…๋กœ๋“œ๋œ ํŒŒ์ผ ๋‚ด์šฉ]\n{text_content}\n"
369
+
370
+ # ๋ฉ”์‹œ์ง€์— ํŒŒ์ผ ๋‚ด์šฉ ์ถ”๊ฐ€
371
+ full_prompt = f"๋‹ค์Œ ์ˆ˜ํ•™ ๋ฌธ์ œ๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ํ’€์–ด์ฃผ์„ธ์š”: {problem}{file_content}"
372
+ print(f"๐Ÿ“ DEBUG: ์ „์ฒด ํ”„๋กฌํ”„ํŠธ: {full_prompt[:200]}...")
373
+
374
+ print("๐Ÿ”ค DEBUG: ํ† ํฌ๋‚˜์ด์ € ์ฒ˜๋ฆฌ ์‹œ์ž‘")
375
+ print(f" tokenizer ํƒ€์ž…: {type(tokenizer)}")
376
+ print(f" tokenizer ๊ฐ’: {tokenizer}")
377
+
378
+ # tokenizer๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธ
379
+ if not hasattr(tokenizer, 'encode') or tokenizer is None or isinstance(tokenizer, bool):
380
+ print("โŒ DEBUG: tokenizer๊ฐ€ ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์Œ")
381
+ # tokenizer๋ฅผ ๋‹ค์‹œ ๋กœ๋“œ
382
+ print("๐Ÿ”„ DEBUG: tokenizer ์žฌ๋กœ๋“œ ์‹œ๋„")
383
+ try:
384
+ tokenizer = AutoTokenizer.from_pretrained(
385
+ MODEL_NAME,
386
+ token=HF_TOKEN,
387
+ trust_remote_code=True,
388
+ use_fast=False
389
+ )
390
+ print("โœ… DEBUG: tokenizer ์žฌ๋กœ๋“œ ์„ฑ๊ณต")
391
+ print(f" ์ƒˆ๋กœ์šด tokenizer ํƒ€์ž…: {type(tokenizer)}")
392
+ except Exception as reload_error:
393
+ print(f"โŒ DEBUG: tokenizer ์žฌ๋กœ๋“œ ์‹คํŒจ: {reload_error}")
394
+ return f"ํ† ํฌ๋‚˜์ด์ € ์˜ค๋ฅ˜: {str(reload_error)}"
395
+
396
+ inputs = tokenizer(full_prompt, return_tensors="pt")
397
+ print(f" ์ž…๋ ฅ shape: {inputs['input_ids'].shape}")
398
+ print(f" attention_mask shape: {inputs['attention_mask'].shape}")
399
+
400
+ print("๐Ÿค– DEBUG: ๋ชจ๋ธ ์ถ”๋ก  ์‹œ์ž‘")
401
+ with torch.no_grad():
402
+ if image_file is not None:
403
+ print("๐Ÿ–ผ๏ธ DEBUG: ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ๋ชจ๋“œ")
404
+ # ์ด๋ฏธ์ง€๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ์ƒ์„ฑ
405
+ import torchvision.transforms as transforms
406
+
407
+ # ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ
408
+ transform = transforms.Compose([
409
+ transforms.Resize((224, 224)),
410
+ transforms.ToTensor(),
411
+ transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
412
+ ])
413
+
414
+ pil_image = Image.open(image_file).convert('RGB')
415
+ pixel_values = transform(pil_image).unsqueeze(0)
416
+ image_metas = {"vision_grid_thw": torch.tensor([[1, 14, 14]])} # ๊ธฐ๋ณธ ๊ทธ๋ฆฌ๋“œ ํฌ๊ธฐ
417
+
418
+ print(f" ์ด๋ฏธ์ง€ shape: {pixel_values.shape}")
419
+ print(f" ์ด๋ฏธ์ง€ ๋ฉ”ํƒ€: {image_metas}")
420
+
421
+ # ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋ธ์˜ forward ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ
422
+ print("๐Ÿ”„ DEBUG: ๋ชจ๋ธ ํ˜ธ์ถœ (๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ)")
423
+ try:
424
+ outputs = model(
425
+ input_ids=inputs["input_ids"],
426
+ attention_mask=inputs["attention_mask"],
427
+ pixel_values=[pixel_values],
428
+ image_metas=image_metas,
429
+ max_new_tokens=300,
430
+ temperature=0.3,
431
+ do_sample=True,
432
+ pad_token_id=tokenizer.eos_token_id
433
+ )
434
+ print("โœ… DEBUG: ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋ธ ํ˜ธ์ถœ ์„ฑ๊ณต")
435
+ except Exception as model_error:
436
+ print(f"โŒ DEBUG: ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋ธ ํ˜ธ์ถœ ์‹คํŒจ: {model_error}")
437
+ print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(model_error).__name__}")
438
+ raise model_error
439
+ else:
440
+ print("๐Ÿ“„ DEBUG: ํ…์ŠคํŠธ๋งŒ ์ฒ˜๋ฆฌ ๋ชจ๋“œ")
441
+ # ํ…์ŠคํŠธ๋งŒ ์ƒ์„ฑ
442
+ print("๐Ÿ”„ DEBUG: ๋ชจ๋ธ ํ˜ธ์ถœ (ํ…์ŠคํŠธ๋งŒ)")
443
+ try:
444
+ outputs = model(
445
+ input_ids=inputs["input_ids"],
446
+ attention_mask=inputs["attention_mask"],
447
+ max_new_tokens=300,
448
+ temperature=0.3,
449
+ do_sample=True,
450
+ pad_token_id=tokenizer.eos_token_id
451
+ )
452
+ print("โœ… DEBUG: ํ…์ŠคํŠธ ๋ชจ๋ธ ํ˜ธ์ถœ ์„ฑ๊ณต")
453
+ except Exception as model_error:
454
+ print(f"โŒ DEBUG: ํ…์ŠคํŠธ ๋ชจ๋ธ ํ˜ธ์ถœ ์‹คํŒจ: {model_error}")
455
+ print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(model_error).__name__}")
456
+ raise model_error
457
+
458
+ print("๐Ÿ” DEBUG: ์ถœ๋ ฅ ์ฒ˜๋ฆฌ ์‹œ์ž‘")
459
+ print(f" outputs ํƒ€์ž…: {type(outputs)}")
460
+ print(f" outputs ๋‚ด์šฉ: {outputs}")
461
+
462
+ # outputs๊ฐ€ ํŠœํ”Œ์ธ ๊ฒฝ์šฐ ์ฒซ ๋ฒˆ์งธ ์š”์†Œ ์‚ฌ์šฉ
463
+ if isinstance(outputs, tuple):
464
+ print("๐Ÿ“ฆ DEBUG: outputs๊ฐ€ ํŠœํ”Œ์ž„")
465
+ logits = outputs[0]
466
+ print(f" logits shape: {logits.shape}")
467
+ else:
468
+ print("๐Ÿ“ฆ DEBUG: outputs๊ฐ€ ๊ฐ์ฒด์ž„")
469
+ if hasattr(outputs, 'logits'):
470
+ logits = outputs.logits
471
+ print(f" logits shape: {logits.shape}")
472
+ else:
473
+ logits = outputs
474
+ print(f" outputs shape: {logits.shape}")
475
+
476
+ print("๐ŸŽฏ DEBUG: ํ† ํฐ ์ƒ์„ฑ ์‹œ์ž‘")
477
+ # ๊ฐ€์žฅ ๋†’์€ ํ™•๋ฅ ์˜ ํ† ํฐ ์„ ํƒ
478
+ next_token = torch.argmax(logits[:, -1, :], dim=-1)
479
+ generated_tokens = [next_token]
480
+ print(f" ์ฒซ ๋ฒˆ์งธ ํ† ํฐ: {next_token.item()}")
481
+
482
+ # ์ถ”๊ฐ€ ํ† ํฐ ์ƒ์„ฑ
483
+ print("๐Ÿ”„ DEBUG: ๋ฐ˜๋ณต ํ† ํฐ ์ƒ์„ฑ ์‹œ์ž‘")
484
+ for i in range(299): # max_new_tokens - 1
485
+ if i % 50 == 0:
486
+ print(f" ์ง„ํ–‰๋ฅ : {i}/299")
487
+
488
+ inputs["input_ids"] = torch.cat([inputs["input_ids"], next_token.unsqueeze(-1)], dim=-1)
489
+ inputs["attention_mask"] = torch.cat([inputs["attention_mask"], torch.ones_like(next_token.unsqueeze(-1))], dim=-1)
490
+
491
+ with torch.no_grad():
492
+ try:
493
+ outputs = model(**inputs)
494
+ if isinstance(outputs, tuple):
495
+ logits = outputs[0]
496
+ else:
497
+ logits = outputs.logits if hasattr(outputs, 'logits') else outputs
498
+
499
+ next_token = torch.argmax(logits[:, -1, :], dim=-1)
500
+ generated_tokens.append(next_token)
501
+
502
+ if next_token.item() == tokenizer.eos_token_id:
503
+ print(f" EOS ํ† ํฐ ๋ฐœ๊ฒฌ: {i}๋ฒˆ์งธ")
504
+ break
505
+ except Exception as loop_error:
506
+ print(f"โŒ DEBUG: ํ† ํฐ ์ƒ์„ฑ ๋ฃจํ”„ ์˜ค๋ฅ˜ (i={i}): {loop_error}")
507
+ raise loop_error
508
+
509
+ print("๐Ÿ”ค DEBUG: ํ† ํฐ ๋””์ฝ”๋”ฉ ์‹œ์ž‘")
510
+ # ์ƒ์„ฑ๋œ ํ† ํฐ๋“ค์„ ๋””์ฝ”๋”ฉ
511
+ generated_ids = torch.cat(generated_tokens, dim=0)
512
+ response = tokenizer.decode(generated_ids, skip_special_tokens=True)
513
+ print(f" ์›๋ณธ ์‘๋‹ต: {response[:200]}...")
514
+
515
+ if full_prompt in response:
516
+ response = response.replace(full_prompt, "").strip()
517
+ print(f" ์ •๋ฆฌ๋œ ์‘๋‹ต: {response[:200]}...")
518
+
519
+ print("โœ… DEBUG: solve_math_problem ์™„๋ฃŒ")
520
+ return response if response else "์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜ํ•™ ๋ฌธ์ œ๋ฅผ ํ’€ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."
521
+ except Exception as e:
522
+ print(f"โŒ DEBUG: solve_math_problem ์ „์ฒด ์˜ค๋ฅ˜: {e}")
523
+ print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(e).__name__}")
524
+ import traceback
525
+ traceback.print_exc()
526
+ return f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
527
+
528
+ with gr.Blocks(title="Lily Math RAG System", theme=gr.themes.Soft()) as demo:
529
+ gr.Markdown("# ๐Ÿงฎ Lily Math RAG System")
530
+ gr.Markdown("์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ์„ ์œ„ํ•œ AI ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค.")
531
+ with gr.Tabs():
532
+ with gr.Tab("๐Ÿ’ฌ ์ฑ„ํŒ…"):
533
+ with gr.Row():
534
+ with gr.Column(scale=3):
535
+ chatbot = gr.Chatbot(height=400, type="messages")
536
+ msg = gr.Textbox(label="๋ฉ”์‹œ์ง€๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”", placeholder="์•ˆ๋…•ํ•˜์„ธ์š”! ์ˆ˜ํ•™ ๋ฌธ์ œ๋ฅผ ๋„์™€์ฃผ์„ธ์š”.", lines=2)
537
+ clear = gr.Button("๋Œ€ํ™” ์ดˆ๊ธฐํ™”")
538
+ with gr.Column(scale=1):
539
+ gr.Markdown("### ๐Ÿ“ ํŒŒ์ผ ์—…๋กœ๋“œ")
540
+ file_input = gr.File(label="PDF/์ด๋ฏธ์ง€ ํŒŒ์ผ (์„ ํƒ์‚ฌํ•ญ)", file_types=[".pdf", ".png", ".jpg", ".jpeg"])
541
+ gr.Markdown("PDF๋‚˜ ์ด๋ฏธ์ง€ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•˜๋ฉด ๋ฌธ์„œ๋ฅผ ํ•ด์„ํ•˜์—ฌ ๋‹ต๋ณ€ํ•ฉ๋‹ˆ๋‹ค.")
542
+
543
+ def respond(message, chat_history, file):
544
+ bot_message = chat_with_model(message, chat_history, file)
545
+ chat_history.append({"role": "user", "content": message})
546
+ chat_history.append({"role": "assistant", "content": bot_message})
547
+ return "", chat_history
548
+ msg.submit(respond, [msg, chatbot, file_input], [msg, chatbot])
549
+ clear.click(lambda: None, None, chatbot, queue=False)
550
+
551
+ with gr.Tab("๐Ÿงฎ ์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ"):
552
+ with gr.Row():
553
+ with gr.Column(scale=2):
554
+ math_input = gr.Textbox(label="์ˆ˜ํ•™ ๋ฌธ์ œ", placeholder="์˜ˆ: 2x + 5 = 13", lines=3)
555
+ solve_btn = gr.Button("๋ฌธ์ œ ํ’€๊ธฐ", variant="primary")
556
+ with gr.Column(scale=1):
557
+ gr.Markdown("### ๐Ÿ“ ํŒŒ์ผ ์—…๋กœ๋“œ")
558
+ math_file_input = gr.File(label="์ˆ˜ํ•™ ๋ฌธ์ œ ํŒŒ์ผ (์„ ํƒ์‚ฌํ•ญ)", file_types=[".pdf", ".png", ".jpg", ".jpeg"])
559
+ gr.Markdown("์ˆ˜ํ•™ ๋ฌธ์ œ PDF๋‚˜ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๋ฉด ๋” ์ •ํ™•ํ•œ ๋‹ต๋ณ€์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")
560
+ with gr.Column(scale=2):
561
+ math_output = gr.Textbox(label="ํ•ด๋‹ต", lines=8, interactive=False)
562
+ solve_btn.click(solve_math_problem, [math_input, math_file_input], math_output)
563
+
564
+ with gr.Tab("โš™๏ธ ์„ค์ •"):
565
+ gr.Markdown("## ๏ฟฝ๏ฟฝ์Šคํ…œ ์ •๋ณด")
566
+ gr.Markdown(f"**๋ชจ๋ธ**: {MODEL_NAME}")
567
+ gr.Markdown(f"**๋ชจ๋ธ ์ƒํƒœ**: {'โœ… ๋กœ๋“œ๋จ' if MODEL_LOADED else 'โŒ ๋กœ๋“œ ์‹คํŒจ'}")
568
+ gr.Markdown(f"**ํ† ํฐ ์ƒํƒœ**: {'โœ… ์„ค์ •๋จ' if HF_TOKEN else 'โŒ ์„ค์ •๋˜์ง€ ์•Š์Œ'}")
569
+ gr.Markdown("**๋ฒ„์ „**: 3.0.0 (๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ)")
570
+ gr.Markdown("**๊ธฐ๋Šฅ**: ํ…์ŠคํŠธ + ์ด๋ฏธ์ง€ ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋Œ€ํ™”")
571
+
572
+ if __name__ == "__main__":
573
+ demo.launch()
574
+
app_local.py ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import traceback
4
+ from transformers import AutoTokenizer, AutoModelForCausalLM
5
+ import torch
6
+ from typing import Optional
7
+ import fitz # PyMuPDF
8
+ from PIL import Image
9
+ import io
10
+
11
+ # --- 1. ์ „์—ญ ๋ณ€์ˆ˜ ๋ฐ ํ™˜๊ฒฝ ์„ค์ • ---
12
+
13
+ # ์ „์—ญ ๋ณ€์ˆ˜ ์„ ์–ธ
14
+ tokenizer = None
15
+ model = None
16
+ MODEL_LOADED = False
17
+
18
+ # ํ™˜๊ฒฝ ๊ฐ์ง€ (๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์ธ์ง€, ์„œ๋ฒ„์ธ์ง€ ํ™•์ธ)
19
+ IS_LOCAL = os.path.exists('../.env') or 'LOCAL_TEST' in os.environ
20
+ print(f"๐Ÿ” ํ™˜๊ฒฝ: {'๋กœ์ปฌ' if IS_LOCAL else '์„œ๋ฒ„'}")
21
+
22
+ # .env ํŒŒ์ผ์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋กœ๋“œ (์ฃผ๋กœ ๋กœ์ปฌ์—์„œ ์‚ฌ์šฉ)
23
+ try:
24
+ from dotenv import load_dotenv
25
+ if IS_LOCAL:
26
+ load_dotenv(dotenv_path='../.env')
27
+ print("โœ… .env ํŒŒ์ผ ๋กœ๋“œ๋จ")
28
+ except ImportError:
29
+ print("โš ๏ธ python-dotenv๊ฐ€ ์„ค์น˜๋˜์ง€ ์•Š์Œ")
30
+
31
+ # ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ ํ† ํฐ ๋ฐ ๋ชจ๋ธ ์ด๋ฆ„ ๊ฐ€์ ธ์˜ค๊ธฐ
32
+ HF_TOKEN = os.getenv("HF_TOKEN")
33
+ MODEL_NAME_SERVER = os.getenv("MODEL_NAME", "gbrabbit/lily-math-model")
34
+ MODEL_PATH_LOCAL = "../lily_llm_core/models/kanana-1.5-v-3b-instruct"
35
+
36
+ # ์ตœ์ข… ๋ชจ๋ธ ๊ฒฝ๋กœ ์„ค์ •
37
+ MODEL_PATH = MODEL_PATH_LOCAL if IS_LOCAL else MODEL_NAME_SERVER
38
+
39
+ print(f"๐Ÿ” ๋ชจ๋ธ ๊ฒฝ๋กœ: {MODEL_PATH}")
40
+ print(f"๐Ÿ” HF ํ† ํฐ: {'โœ… ์„ค์ •๋จ' if HF_TOKEN else 'โŒ ์„ค์ •๋˜์ง€ ์•Š์Œ'}")
41
+
42
+ # --- 2. ํ•ต์‹ฌ ๋กœ์ง: ๋ชจ๋ธ ๋ฐ ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ---
43
+
44
+ def load_model_and_tokenizer():
45
+ """ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ๋ชจ๋ธ๊ณผ ํ† ํฌ๋‚˜์ด์ €๋ฅผ ๋กœ๋“œํ•˜๋Š” ํ†ตํ•ฉ ํ•จ์ˆ˜"""
46
+ global tokenizer, model # ์ „์—ญ ๋ณ€์ˆ˜ ์‚ฌ์šฉ ์„ ์–ธ
47
+
48
+ print("๐Ÿ”ง ๋ชจ๋ธ ๋ฐ ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์‹œ์ž‘...")
49
+
50
+ # modeling.py๋Š” ์ปค์Šคํ…€ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•  ๋•Œ๋งŒ import
51
+ from modeling import KananaVForConditionalGeneration
52
+
53
+ if IS_LOCAL:
54
+ # ๋กœ์ปฌ ํŒŒ์ผ ์‹œ์Šคํ…œ์—์„œ ๋ชจ๋ธ ๋กœ๋“œ
55
+ if not os.path.exists(MODEL_PATH):
56
+ raise FileNotFoundError(f"๋กœ์ปฌ ๋ชจ๋ธ ๊ฒฝ๋กœ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {MODEL_PATH}")
57
+
58
+ tokenizer = AutoTokenizer.from_pretrained(
59
+ MODEL_PATH,
60
+ trust_remote_code=True,
61
+ local_files_only=True # ๋กœ์ปฌ ํŒŒ์ผ๋งŒ ์‚ฌ์šฉ
62
+ )
63
+ model = KananaVForConditionalGeneration.from_pretrained(
64
+ MODEL_PATH,
65
+ torch_dtype=torch.float16,
66
+ trust_remote_code=True,
67
+ local_files_only=True
68
+ )
69
+ else:
70
+ # Hugging Face Hub์—์„œ ๋ชจ๋ธ ๋กœ๋“œ
71
+ if not HF_TOKEN:
72
+ # ํ† ํฐ์ด ์—†์œผ๋ฉด ๊ณต๊ฐœ๋œ ๋ชจ๋ธ๋กœ ๋Œ€์ฒด (์„ ํƒ์ )
73
+ print("โš ๏ธ HF ํ† ํฐ์ด ์—†์–ด ๊ณต๊ฐœ ๋ชจ๋ธ(DialoGPT)๋กœ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค.")
74
+ public_model = "microsoft/DialoGPT-medium"
75
+ tokenizer = AutoTokenizer.from_pretrained(public_model)
76
+ model = AutoModelForCausalLM.from_pretrained(public_model, torch_dtype=torch.float16)
77
+ return
78
+
79
+ tokenizer = AutoTokenizer.from_pretrained(
80
+ MODEL_PATH,
81
+ token=HF_TOKEN,
82
+ trust_remote_code=True
83
+ )
84
+ model = KananaVForConditionalGeneration.from_pretrained(
85
+ MODEL_PATH,
86
+ token=HF_TOKEN,
87
+ torch_dtype=torch.float16,
88
+ trust_remote_code=True,
89
+ device_map="auto" # GPU ์ž๋™ ํ• ๋‹น
90
+ )
91
+
92
+ print("โœ… ๋ชจ๋ธ ๋ฐ ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์„ฑ๊ณต!")
93
+
94
+ # --- 3. ํŒŒ์ผ ์ฒ˜๋ฆฌ ์œ ํ‹ธ๋ฆฌํ‹ฐ ---
95
+
96
+ def extract_text_from_pdf(pdf_file):
97
+ """PDF์—์„œ ํ…์ŠคํŠธ ์ถ”์ถœ"""
98
+ try:
99
+ doc = fitz.open(stream=pdf_file.read(), filetype="pdf")
100
+ text = "".join(page.get_text() for page in doc)
101
+ doc.close()
102
+ return f"\n\n--- PDF ๋‚ด์šฉ ---\n{text}\n--- PDF ๋‚ด์šฉ ๋ ---"
103
+ except Exception as e:
104
+ return f"PDF ์ฝ๊ธฐ ์˜ค๋ฅ˜: {e}"
105
+
106
+ def extract_text_from_image(image_file):
107
+ """์ด๋ฏธ์ง€์—์„œ OCR๋กœ ํ…์ŠคํŠธ ์ถ”์ถœ (ํ˜„์žฌ๋Š” ํŒŒ์ผ ์ •๋ณด๋งŒ ๋ฐ˜ํ™˜)"""
108
+ try:
109
+ # ์‹ค์ œ OCR ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(์˜ˆ: Tesseract) ์—ฐ๋™ ํ•„์š”
110
+ image = Image.open(image_file)
111
+ return f"\n\n--- ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ •๋ณด: {image.format}, {image.size[0]}x{image.size[1]} ---"
112
+ except Exception as e:
113
+ return f"์ด๋ฏธ์ง€ ์ฝ๊ธฐ ์˜ค๋ฅ˜: {e}"
114
+
115
+ def process_uploaded_file(file):
116
+ """์—…๋กœ๋“œ๋œ ํŒŒ์ผ์„ ์ข…๋ฅ˜์— ๋งž๊ฒŒ ์ฒ˜๋ฆฌ"""
117
+ if file is None:
118
+ return ""
119
+
120
+ file_path = file.name
121
+ file_extension = os.path.splitext(file_path)[1].lower()
122
+
123
+ if file_extension == '.pdf':
124
+ return extract_text_from_pdf(file)
125
+ elif file_extension in ['.png', '.jpg', '.jpeg']:
126
+ # TODO: ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋ธ์„ ์œ„ํ•œ ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ ๋กœ์ง ์ถ”๊ฐ€
127
+ return extract_text_from_image(file)
128
+ else:
129
+ return f"์ง€์›ํ•˜์ง€ ์•Š๋Š” ํŒŒ์ผ ํ˜•์‹: {file_extension}"
130
+
131
+
132
+ # --- 4. ํ•ต์‹ฌ ๋กœ์ง: ์‘๋‹ต ์ƒ์„ฑ ํ•จ์ˆ˜ ---
133
+
134
+ def generate_response(prompt_template: str, message: str, file: Optional = None):
135
+ """ํ†ตํ•ฉ๋œ ์‘๋‹ต ์ƒ์„ฑ ํ•จ์ˆ˜"""
136
+ if not MODEL_LOADED:
137
+ return "โŒ ๋ชจ๋ธ์ด ๋กœ๋“œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์•ฑ์„ ์žฌ์‹œ์ž‘ํ•ด์ฃผ์„ธ์š”."
138
+
139
+ try:
140
+ print("โœ๏ธ ์‘๋‹ต ์ƒ์„ฑ ์‹œ์ž‘...")
141
+
142
+ # 1. ํŒŒ์ผ ๋‚ด์šฉ ์ฒ˜๋ฆฌ
143
+ file_content = process_uploaded_file(file)
144
+
145
+ # 2. ์ „์ฒด ํ”„๋กฌํ”„ํŠธ ๊ตฌ์„ฑ
146
+ full_message = message + file_content
147
+ full_prompt = prompt_template.format(message=full_message)
148
+
149
+ print(f"๐Ÿ“ ์ „์ฒด ํ”„๋กฌํ”„ํŠธ (์ผ๋ถ€): {full_prompt[:200]}...")
150
+
151
+ # 3. ํ† ํฌ๋‚˜์ด์ €๋กœ ์ž…๋ ฅ ๋ณ€ํ™˜
152
+ inputs = tokenizer(full_prompt, return_tensors="pt").to(model.device)
153
+
154
+ # 4. ๋ชจ๋ธ์„ ํ†ตํ•ด ์‘๋‹ต ์ƒ์„ฑ (๋‹จ ํ•œ ๋ฒˆ์˜ ํ˜ธ์ถœ)
155
+ with torch.no_grad():
156
+ outputs = model.generate(
157
+ **inputs,
158
+ max_new_tokens=512,
159
+ temperature=0.7,
160
+ top_p=0.9,
161
+ do_sample=True,
162
+ pad_token_id=tokenizer.eos_token_id,
163
+ eos_token_id=tokenizer.eos_token_id
164
+ )
165
+
166
+ # 5. ์ƒ์„ฑ๋œ ํ† ํฐ ID๋ฅผ ํ…์ŠคํŠธ๋กœ ๋””์ฝ”๋”ฉ
167
+ response_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
168
+
169
+ # 6. ์ž…๋ ฅ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์‘๋‹ต์—์„œ ์ œ๊ฑฐํ•˜์—ฌ ์ˆœ์ˆ˜ํ•œ ๋‹ต๋ณ€๋งŒ ์ถ”์ถœ
170
+ assistant_response = response_text.split("<|im_start|>assistant\n")[-1].strip()
171
+
172
+ print(f"๐Ÿ’ฌ ์ƒ์„ฑ๋œ ์‘๋‹ต (์ผ๋ถ€): {assistant_response[:200]}...")
173
+ print("โœ… ์‘๋‹ต ์ƒ์„ฑ ์™„๋ฃŒ")
174
+
175
+ return assistant_response if assistant_response else "์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."
176
+
177
+ except Exception as e:
178
+ print(f"โŒ ์‘๋‹ต ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {e}")
179
+ traceback.print_exc()
180
+ return f"์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}"
181
+
182
+
183
+ # --- 5. Gradio UI ์„ค์ • ---
184
+
185
+ with gr.Blocks(title="Lily Math RAG System", theme=gr.themes.Soft()) as demo:
186
+ gr.Markdown("# ๐Ÿงฎ Lily Math RAG System")
187
+ gr.Markdown("์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ ๋ฐ ์ผ๋ฐ˜ ๋Œ€ํ™”๋ฅผ ์œ„ํ•œ AI ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค.")
188
+
189
+ with gr.Tabs():
190
+ with gr.Tab("๐Ÿ’ฌ ์ผ๋ฐ˜ ์ฑ„ํŒ…"):
191
+ # ์ฑ„ํŒ… ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ
192
+ chat_prompt = "<|im_start|>user\n{message}<|im_end|>\n<|im_start|>assistant\n"
193
+
194
+ with gr.Row():
195
+ with gr.Column(scale=3):
196
+ chatbot = gr.Chatbot(height=500, label="๋Œ€ํ™”์ฐฝ", type="messages")
197
+ msg = gr.Textbox(label="๋ฉ”์‹œ์ง€", placeholder="์•ˆ๋…•ํ•˜์„ธ์š”! ๋ฌด์—‡์„ ๋„์™€๋“œ๋ฆด๊นŒ์š”?", lines=3)
198
+ clear = gr.Button("์ƒˆ ๋Œ€ํ™” ์‹œ์ž‘")
199
+ with gr.Column(scale=1):
200
+ gr.Markdown("### ๐Ÿ“ ํŒŒ์ผ ์—…๋กœ๋“œ (์„ ํƒ)")
201
+ chat_file = gr.File(label="PDF/์ด๋ฏธ์ง€ ํŒŒ์ผ", file_types=[".pdf", ".png", ".jpg", ".jpeg"])
202
+
203
+ def respond(message, chat_history, file):
204
+ bot_message = generate_response(chat_prompt, message, file)
205
+ chat_history.append({"role": "user", "content": message})
206
+ chat_history.append({"role": "assistant", "content": bot_message})
207
+ return "", chat_history
208
+
209
+ msg.submit(respond, [msg, chatbot, chat_file], [msg, chatbot])
210
+ clear.click(lambda: None, None, chatbot, queue=False)
211
+
212
+ with gr.Tab("๐Ÿงฎ ์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ"):
213
+ # ์ˆ˜ํ•™ ๋ฌธ์ œ ํ’€์ด์šฉ ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ
214
+ math_prompt = "๋‹ค์Œ ์ˆ˜ํ•™ ๋ฌธ์ œ๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ์ƒ์„ธํžˆ ํ’€์–ด์ฃผ์„ธ์š”:\n\n{message}\n\n<|im_start|>assistant\n"
215
+
216
+ with gr.Row():
217
+ with gr.Column(scale=2):
218
+ math_input = gr.Textbox(label="์ˆ˜ํ•™ ๋ฌธ์ œ", placeholder="์˜ˆ: 2x + 5 = 13", lines=5)
219
+ gr.Markdown("### ๐Ÿ“ ๋ฌธ์ œ ํŒŒ์ผ ์—…๋กœ๋“œ (์„ ํƒ)")
220
+ math_file = gr.File(label="PDF/์ด๋ฏธ์ง€ ํ˜•์‹์˜ ๋ฌธ์ œ", file_types=[".pdf", ".png", ".jpg", ".jpeg"])
221
+ solve_btn = gr.Button("๋ฌธ์ œ ํ’€๊ธฐ", variant="primary")
222
+ with gr.Column(scale=2):
223
+ math_output = gr.Textbox(label="ํ’€์ด ๊ณผ์ • ๋ฐ ์ •๋‹ต", lines=10, interactive=False)
224
+
225
+ solve_btn.click(lambda msg, file: generate_response(math_prompt, msg, file), [math_input, math_file], math_output)
226
+
227
+ with gr.Tab("โš™๏ธ ์‹œ์Šคํ…œ ์ •๋ณด"):
228
+ gr.Markdown(f"**๋ชจ๋ธ ๊ฒฝ๋กœ**: `{MODEL_PATH}`")
229
+ gr.Markdown(f"**๋ชจ๋ธ ์ƒํƒœ**: `{'โœ… ๋กœ๋“œ๋จ' if MODEL_LOADED else 'โŒ ๋กœ๋“œ ์‹คํŒจ'}`")
230
+ gr.Markdown(f"**์‹คํ–‰ ํ™˜๊ฒฝ**: `{'๋กœ์ปฌ' if IS_LOCAL else '์„œ๋ฒ„'}`")
231
+
232
+
233
+ # --- 6. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ---
234
+
235
+ if __name__ == "__main__":
236
+ try:
237
+ load_model_and_tokenizer()
238
+ MODEL_LOADED = True
239
+ except Exception as e:
240
+ print(f"โŒ ์ตœ์ข… ์‹คํ–‰ ์‹คํŒจ: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘์— ํ•„์š”ํ•œ ๋ชจ๋ธ์„ ๋กœ๋“œํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.")
241
+ print(f"์˜ค๋ฅ˜: {e}")
242
+ traceback.print_exc()
243
+ MODEL_LOADED = False
244
+
245
+ demo.launch(server_name="localhost", server_port=8006)
app_local_250807_0427.py ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import traceback
4
+ from transformers import AutoTokenizer, AutoModelForCausalLM
5
+ import torch
6
+ from typing import Optional
7
+ import fitz # PyMuPDF
8
+ from PIL import Image
9
+ import io
10
+
11
+ # --- 1. ์ „์—ญ ๋ณ€์ˆ˜ ๋ฐ ํ™˜๊ฒฝ ์„ค์ • ---
12
+
13
+ # ์ „์—ญ ๋ณ€์ˆ˜ ์„ ์–ธ
14
+ tokenizer = None
15
+ model = None
16
+ MODEL_LOADED = False
17
+
18
+ # ํ™˜๊ฒฝ ๊ฐ์ง€ (๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์ธ์ง€, ์„œ๋ฒ„์ธ์ง€ ํ™•์ธ)
19
+ IS_LOCAL = os.path.exists('../.env') or 'LOCAL_TEST' in os.environ
20
+ print(f"๐Ÿ” ํ™˜๊ฒฝ: {'๋กœ์ปฌ' if IS_LOCAL else '์„œ๋ฒ„'}")
21
+
22
+ # .env ํŒŒ์ผ์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋กœ๋“œ (์ฃผ๋กœ ๋กœ์ปฌ์—์„œ ์‚ฌ์šฉ)
23
+ try:
24
+ from dotenv import load_dotenv
25
+ if IS_LOCAL:
26
+ load_dotenv(dotenv_path='../.env')
27
+ print("โœ… .env ํŒŒ์ผ ๋กœ๋“œ๋จ")
28
+ except ImportError:
29
+ print("โš ๏ธ python-dotenv๊ฐ€ ์„ค์น˜๋˜์ง€ ์•Š์Œ")
30
+
31
+ # ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ ํ† ํฐ ๋ฐ ๋ชจ๋ธ ์ด๋ฆ„ ๊ฐ€์ ธ์˜ค๊ธฐ
32
+ HF_TOKEN = os.getenv("HF_TOKEN")
33
+ MODEL_NAME_SERVER = os.getenv("MODEL_NAME", "gbrabbit/lily-math-model")
34
+ MODEL_PATH_LOCAL = "../lily_llm_core/models/kanana-1.5-v-3b-instruct"
35
+
36
+ # ์ตœ์ข… ๋ชจ๋ธ ๊ฒฝ๋กœ ์„ค์ •
37
+ MODEL_PATH = MODEL_PATH_LOCAL if IS_LOCAL else MODEL_NAME_SERVER
38
+
39
+ print(f"๐Ÿ” ๋ชจ๋ธ ๊ฒฝ๋กœ: {MODEL_PATH}")
40
+ print(f"๐Ÿ” HF ํ† ํฐ: {'โœ… ์„ค์ •๋จ' if HF_TOKEN else 'โŒ ์„ค์ •๋˜์ง€ ์•Š์Œ'}")
41
+
42
+ # --- 2. ํ•ต์‹ฌ ๋กœ์ง: ๋ชจ๋ธ ๋ฐ ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ---
43
+
44
+ def load_model_and_tokenizer():
45
+ """ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ๋ชจ๋ธ๊ณผ ํ† ํฌ๋‚˜์ด์ €๋ฅผ ๋กœ๋“œํ•˜๋Š” ํ†ตํ•ฉ ํ•จ์ˆ˜"""
46
+ global tokenizer, model # ์ „์—ญ ๋ณ€์ˆ˜ ์‚ฌ์šฉ ์„ ์–ธ
47
+
48
+ print("๐Ÿ”ง ๋ชจ๋ธ ๋ฐ ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์‹œ์ž‘...")
49
+
50
+ # modeling.py๋Š” ์ปค์Šคํ…€ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•  ๋•Œ๋งŒ import
51
+ from modeling import KananaVForConditionalGeneration
52
+
53
+ if IS_LOCAL:
54
+ # ๋กœ์ปฌ ํŒŒ์ผ ์‹œ์Šคํ…œ์—์„œ ๋ชจ๋ธ ๋กœ๋“œ
55
+ if not os.path.exists(MODEL_PATH):
56
+ raise FileNotFoundError(f"๋กœ์ปฌ ๋ชจ๋ธ ๊ฒฝ๋กœ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {MODEL_PATH}")
57
+
58
+ tokenizer = AutoTokenizer.from_pretrained(
59
+ MODEL_PATH,
60
+ trust_remote_code=True,
61
+ local_files_only=True # ๋กœ์ปฌ ํŒŒ์ผ๋งŒ ์‚ฌ์šฉ
62
+ )
63
+ model = KananaVForConditionalGeneration.from_pretrained(
64
+ MODEL_PATH,
65
+ torch_dtype=torch.float16,
66
+ trust_remote_code=True,
67
+ local_files_only=True
68
+ )
69
+ else:
70
+ # Hugging Face Hub์—์„œ ๋ชจ๋ธ ๋กœ๋“œ
71
+ if not HF_TOKEN:
72
+ # ํ† ํฐ์ด ์—†์œผ๋ฉด ๊ณต๊ฐœ๋œ ๋ชจ๋ธ๋กœ ๋Œ€์ฒด (์„ ํƒ์ )
73
+ print("โš ๏ธ HF ํ† ํฐ์ด ์—†์–ด ๊ณต๊ฐœ ๋ชจ๋ธ(DialoGPT)๋กœ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค.")
74
+ public_model = "microsoft/DialoGPT-medium"
75
+ tokenizer = AutoTokenizer.from_pretrained(public_model)
76
+ model = AutoModelForCausalLM.from_pretrained(public_model, torch_dtype=torch.float16)
77
+ return
78
+
79
+ tokenizer = AutoTokenizer.from_pretrained(
80
+ MODEL_PATH,
81
+ token=HF_TOKEN,
82
+ trust_remote_code=True
83
+ )
84
+ model = KananaVForConditionalGeneration.from_pretrained(
85
+ MODEL_PATH,
86
+ token=HF_TOKEN,
87
+ torch_dtype=torch.float16,
88
+ trust_remote_code=True,
89
+ device_map="auto" # GPU ์ž๋™ ํ• ๋‹น
90
+ )
91
+
92
+ print("โœ… ๋ชจ๋ธ ๋ฐ ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์„ฑ๊ณต!")
93
+
94
+ # --- 3. ํŒŒ์ผ ์ฒ˜๋ฆฌ ์œ ํ‹ธ๋ฆฌํ‹ฐ ---
95
+
96
+ def extract_text_from_pdf(pdf_file):
97
+ """PDF์—์„œ ํ…์ŠคํŠธ ์ถ”์ถœ"""
98
+ try:
99
+ doc = fitz.open(stream=pdf_file.read(), filetype="pdf")
100
+ text = "".join(page.get_text() for page in doc)
101
+ doc.close()
102
+ return f"\n\n--- PDF ๋‚ด์šฉ ---\n{text}\n--- PDF ๋‚ด์šฉ ๋ ---"
103
+ except Exception as e:
104
+ return f"PDF ์ฝ๊ธฐ ์˜ค๋ฅ˜: {e}"
105
+
106
+ def extract_text_from_image(image_file):
107
+ """์ด๋ฏธ์ง€์—์„œ OCR๋กœ ํ…์ŠคํŠธ ์ถ”์ถœ (ํ˜„์žฌ๋Š” ํŒŒ์ผ ์ •๋ณด๋งŒ ๋ฐ˜ํ™˜)"""
108
+ try:
109
+ # ์‹ค์ œ OCR ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(์˜ˆ: Tesseract) ์—ฐ๋™ ํ•„์š”
110
+ image = Image.open(image_file)
111
+ return f"\n\n--- ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ •๋ณด: {image.format}, {image.size[0]}x{image.size[1]} ---"
112
+ except Exception as e:
113
+ return f"์ด๋ฏธ์ง€ ์ฝ๊ธฐ ์˜ค๋ฅ˜: {e}"
114
+
115
+ def process_uploaded_file(file):
116
+ """์—…๋กœ๋“œ๋œ ํŒŒ์ผ์„ ์ข…๋ฅ˜์— ๋งž๊ฒŒ ์ฒ˜๋ฆฌ"""
117
+ if file is None:
118
+ return ""
119
+
120
+ file_path = file.name
121
+ file_extension = os.path.splitext(file_path)[1].lower()
122
+
123
+ if file_extension == '.pdf':
124
+ return extract_text_from_pdf(file)
125
+ elif file_extension in ['.png', '.jpg', '.jpeg']:
126
+ # TODO: ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ชจ๋ธ์„ ์œ„ํ•œ ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ ๋กœ์ง ์ถ”๊ฐ€
127
+ return extract_text_from_image(file)
128
+ else:
129
+ return f"์ง€์›ํ•˜์ง€ ์•Š๋Š” ํŒŒ์ผ ํ˜•์‹: {file_extension}"
130
+
131
+
132
+ # --- 4. ํ•ต์‹ฌ ๋กœ์ง: ์‘๋‹ต ์ƒ์„ฑ ํ•จ์ˆ˜ ---
133
+
134
+ def generate_response(prompt_template: str, message: str, file: Optional = None):
135
+ """ํ†ตํ•ฉ๋œ ์‘๋‹ต ์ƒ์„ฑ ํ•จ์ˆ˜"""
136
+ if not MODEL_LOADED:
137
+ return "โŒ ๋ชจ๋ธ์ด ๋กœ๋“œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์•ฑ์„ ์žฌ์‹œ์ž‘ํ•ด์ฃผ์„ธ์š”."
138
+
139
+ try:
140
+ print("โœ๏ธ ์‘๋‹ต ์ƒ์„ฑ ์‹œ์ž‘...")
141
+
142
+ # 1. ํŒŒ์ผ ๋‚ด์šฉ ์ฒ˜๋ฆฌ
143
+ file_content = process_uploaded_file(file)
144
+
145
+ # 2. ์ „์ฒด ํ”„๋กฌํ”„ํŠธ ๊ตฌ์„ฑ
146
+ full_message = message + file_content
147
+ full_prompt = prompt_template.format(message=full_message)
148
+
149
+ print(f"๐Ÿ“ ์ „์ฒด ํ”„๋กฌํ”„ํŠธ (์ผ๋ถ€): {full_prompt[:200]}...")
150
+
151
+ # 3. ํ† ํฌ๋‚˜์ด์ €๋กœ ์ž…๋ ฅ ๋ณ€ํ™˜
152
+ inputs = tokenizer(full_prompt, return_tensors="pt").to(model.device)
153
+
154
+ # 4. ๋ชจ๋ธ์„ ํ†ตํ•ด ์‘๋‹ต ์ƒ์„ฑ (๋‹จ ํ•œ ๋ฒˆ์˜ ํ˜ธ์ถœ)
155
+ with torch.no_grad():
156
+ outputs = model.generate(
157
+ **inputs,
158
+ max_new_tokens=512,
159
+ temperature=0.7,
160
+ top_p=0.9,
161
+ do_sample=True,
162
+ pad_token_id=tokenizer.eos_token_id,
163
+ eos_token_id=tokenizer.eos_token_id
164
+ )
165
+
166
+ # 5. ์ƒ์„ฑ๋œ ํ† ํฐ ID๋ฅผ ํ…์ŠคํŠธ๋กœ ๋””์ฝ”๋”ฉ
167
+ response_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
168
+
169
+ # 6. ์ž…๋ ฅ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์‘๋‹ต์—์„œ ์ œ๊ฑฐํ•˜์—ฌ ์ˆœ์ˆ˜ํ•œ ๋‹ต๋ณ€๋งŒ ์ถ”์ถœ
170
+ assistant_response = response_text.split("<|im_start|>assistant\n")[-1].strip()
171
+
172
+ print(f"๐Ÿ’ฌ ์ƒ์„ฑ๋œ ์‘๋‹ต (์ผ๋ถ€): {assistant_response[:200]}...")
173
+ print("โœ… ์‘๋‹ต ์ƒ์„ฑ ์™„๋ฃŒ")
174
+
175
+ return assistant_response if assistant_response else "์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."
176
+
177
+ except Exception as e:
178
+ print(f"โŒ ์‘๋‹ต ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {e}")
179
+ traceback.print_exc()
180
+ return f"์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}"
181
+
182
+
183
+ # --- 5. Gradio UI ์„ค์ • ---
184
+
185
+ with gr.Blocks(title="Lily Math RAG System", theme=gr.themes.Soft()) as demo:
186
+ gr.Markdown("# ๐Ÿงฎ Lily Math RAG System")
187
+ gr.Markdown("์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ ๋ฐ ์ผ๋ฐ˜ ๋Œ€ํ™”๋ฅผ ์œ„ํ•œ AI ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค.")
188
+
189
+ with gr.Tabs():
190
+ with gr.Tab("๐Ÿ’ฌ ์ผ๋ฐ˜ ์ฑ„ํŒ…"):
191
+ # ์ฑ„ํŒ… ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ
192
+ chat_prompt = "<|im_start|>user\n{message}<|im_end|>\n<|im_start|>assistant\n"
193
+
194
+ with gr.Row():
195
+ with gr.Column(scale=3):
196
+ chatbot = gr.Chatbot(height=500, label="๋Œ€ํ™”์ฐฝ", type="messages")
197
+ msg = gr.Textbox(label="๋ฉ”์‹œ์ง€", placeholder="์•ˆ๋…•ํ•˜์„ธ์š”! ๋ฌด์—‡์„ ๋„์™€๋“œ๋ฆด๊นŒ์š”?", lines=3)
198
+ clear = gr.Button("์ƒˆ ๋Œ€ํ™” ์‹œ์ž‘")
199
+ with gr.Column(scale=1):
200
+ gr.Markdown("### ๐Ÿ“ ํŒŒ์ผ ์—…๋กœ๋“œ (์„ ํƒ)")
201
+ chat_file = gr.File(label="PDF/์ด๋ฏธ์ง€ ํŒŒ์ผ", file_types=[".pdf", ".png", ".jpg", ".jpeg"])
202
+
203
+ def respond(message, chat_history, file):
204
+ bot_message = generate_response(chat_prompt, message, file)
205
+ chat_history.append({"role": "user", "content": message})
206
+ chat_history.append({"role": "assistant", "content": bot_message})
207
+ return "", chat_history
208
+
209
+ msg.submit(respond, [msg, chatbot, chat_file], [msg, chatbot])
210
+ clear.click(lambda: None, None, chatbot, queue=False)
211
+
212
+ with gr.Tab("๐Ÿงฎ ์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ"):
213
+ # ์ˆ˜ํ•™ ๋ฌธ์ œ ํ’€์ด์šฉ ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ
214
+ math_prompt = "๋‹ค์Œ ์ˆ˜ํ•™ ๋ฌธ์ œ๋ฅผ ๋‹จ๊ณ„๋ณ„๋กœ ์ƒ์„ธํžˆ ํ’€์–ด์ฃผ์„ธ์š”:\n\n{message}\n\n<|im_start|>assistant\n"
215
+
216
+ with gr.Row():
217
+ with gr.Column(scale=2):
218
+ math_input = gr.Textbox(label="์ˆ˜ํ•™ ๋ฌธ์ œ", placeholder="์˜ˆ: 2x + 5 = 13", lines=5)
219
+ gr.Markdown("### ๐Ÿ“ ๋ฌธ์ œ ํŒŒ์ผ ์—…๋กœ๋“œ (์„ ํƒ)")
220
+ math_file = gr.File(label="PDF/์ด๋ฏธ์ง€ ํ˜•์‹์˜ ๋ฌธ์ œ", file_types=[".pdf", ".png", ".jpg", ".jpeg"])
221
+ solve_btn = gr.Button("๋ฌธ์ œ ํ’€๊ธฐ", variant="primary")
222
+ with gr.Column(scale=2):
223
+ math_output = gr.Textbox(label="ํ’€์ด ๊ณผ์ • ๋ฐ ์ •๋‹ต", lines=10, interactive=False)
224
+
225
+ solve_btn.click(lambda msg, file: generate_response(math_prompt, msg, file), [math_input, math_file], math_output)
226
+
227
+ with gr.Tab("โš™๏ธ ์‹œ์Šคํ…œ ์ •๋ณด"):
228
+ gr.Markdown(f"**๋ชจ๋ธ ๊ฒฝ๋กœ**: `{MODEL_PATH}`")
229
+ gr.Markdown(f"**๋ชจ๋ธ ์ƒํƒœ**: `{'โœ… ๋กœ๋“œ๋จ' if MODEL_LOADED else 'โŒ ๋กœ๋“œ ์‹คํŒจ'}`")
230
+ gr.Markdown(f"**์‹คํ–‰ ํ™˜๊ฒฝ**: `{'๋กœ์ปฌ' if IS_LOCAL else '์„œ๋ฒ„'}`")
231
+
232
+
233
+ # --- 6. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ---
234
+
235
+ if __name__ == "__main__":
236
+ try:
237
+ load_model_and_tokenizer()
238
+ MODEL_LOADED = True
239
+ except Exception as e:
240
+ print(f"โŒ ์ตœ์ข… ์‹คํ–‰ ์‹คํŒจ: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘์— ํ•„์š”ํ•œ ๋ชจ๋ธ์„ ๋กœ๋“œํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.")
241
+ print(f"์˜ค๋ฅ˜: {e}")
242
+ traceback.print_exc()
243
+ MODEL_LOADED = False
244
+
245
+ demo.launch(server_name="localhost", server_port=8006)
test_input.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from gradio_client import Client, file
3
+
4
+ # --- ์„ค์ • ---
5
+ # ๋กœ์ปฌ Gradio ์„œ๋ฒ„ ์ฃผ์†Œ (app.py ์‹คํ–‰ ์‹œ ํ„ฐ๋ฏธ๋„์— ํ‘œ์‹œ๋˜๋Š” ์ฃผ์†Œ)
6
+ SERVER_URL = "http://localhost:8006/"
7
+
8
+ def run_chat_test(client):
9
+ """์ผ๋ฐ˜ ์ฑ„ํŒ… ํƒญ์˜ ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค."""
10
+ print("\n--- ๐Ÿ’ฌ ์ผ๋ฐ˜ ์ฑ„ํŒ… ํ…Œ์ŠคํŠธ ์‹œ์ž‘ ---")
11
+
12
+ test_message = "์•ˆ๋…•ํ•˜์„ธ์š”! ์˜ค๋Š˜ ๋‚ ์”จ๋Š” ์–ด๋–ค๊ฐ€์š”?"
13
+ chat_history = [] # ์ดˆ๊ธฐ ๋Œ€ํ™” ๋‚ด์—ญ์€ ๋น„์–ด์žˆ์Œ
14
+
15
+ print(f"๋ณด๋‚ด๋Š” ๋ฉ”์‹œ์ง€: '{test_message}'")
16
+
17
+ # `respond` ํ•จ์ˆ˜ ํ˜ธ์ถœ (API ์—”๋“œํฌ์ธํŠธ ์ธ๋ฑ์Šค: 0)
18
+ # ์ž…๋ ฅ: (๋ฉ”์‹œ์ง€, ์ฑ„ํŒ… ๋‚ด์—ญ, ํŒŒ์ผ)
19
+ # ์ถœ๋ ฅ: (๋น„์›Œ์ง„ ํ…์ŠคํŠธ ๋ฐ•์Šค, ๊ฐฑ์‹ ๋œ ์ฑ„ํŒ… ๋‚ด์—ญ)
20
+ result = client.predict(
21
+ test_message,
22
+ chat_history,
23
+ None, # ํŒŒ์ผ ์—†์Œ
24
+ fn_index=0
25
+ )
26
+
27
+ # ๊ฐฑ์‹ ๋œ ์ฑ„ํŒ… ๋‚ด์—ญ์—์„œ ๋งˆ์ง€๋ง‰ ์‘๋‹ต(๋ด‡ ๋ฉ”์‹œ์ง€)์„ ์ถ”์ถœ
28
+ updated_history = result[1]
29
+ bot_response = updated_history[-1]['content']
30
+
31
+ print("โœ… ํ…Œ์ŠคํŠธ ์„ฑ๊ณต!")
32
+ print(f"๐Ÿค– ๋ฐ›์€ ์‘๋‹ต: '{bot_response}'")
33
+
34
+ def run_math_test(client):
35
+ """์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ ํƒญ์˜ ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค."""
36
+ print("\n--- ๐Ÿงฎ ์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ ํ…Œ์ŠคํŠธ ์‹œ์ž‘ ---")
37
+
38
+ test_problem = "๋‘ ๊ฐœ์˜ ์—ฐ์†๋œ ์ง์ˆ˜์˜ ํ•ฉ์ด 34์ผ ๋•Œ, ๋‘ ์ง์ˆ˜๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?"
39
+
40
+ print(f"๋ณด๋‚ด๋Š” ๋ฌธ์ œ: '{test_problem}'")
41
+
42
+ # ์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ ํ•จ์ˆ˜ ํ˜ธ์ถœ (API ์—”๋“œํฌ์ธํŠธ ์ธ๋ฑ์Šค: 1)
43
+ # ์ž…๋ ฅ: (์ˆ˜ํ•™ ๋ฌธ์ œ, ํŒŒ์ผ)
44
+ # ์ถœ๋ ฅ: (๊ฒฐ๊ณผ ํ…์ŠคํŠธ)
45
+ result = client.predict(
46
+ test_problem,
47
+ None, # ํŒŒ์ผ ์—†์Œ
48
+ fn_index=1
49
+ )
50
+
51
+ print("โœ… ํ…Œ์ŠคํŠธ ์„ฑ๊ณต!")
52
+ print(f"๐Ÿค– ๋ฐ›์€ ์‘๋‹ต (์ผ๋ถ€): '{result[:200]}...'")
53
+
54
+ def run_file_test(client):
55
+ """ํŒŒ์ผ ์—…๋กœ๋“œ ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค."""
56
+ print("\n--- ๐Ÿ“ ํŒŒ์ผ ์—…๋กœ๋“œ ์ฑ„ํŒ… ํ…Œ์ŠคํŠธ ์‹œ์ž‘ ---")
57
+
58
+ # ํ…Œ์ŠคํŠธ์šฉ ์ž„์‹œ ํ…์ŠคํŠธ ํŒŒ์ผ ์ƒ์„ฑ
59
+ temp_file_path = "test_document.txt"
60
+ with open(temp_file_path, "w", encoding="utf-8") as f:
61
+ f.write("์ด ํŒŒ์ผ์€ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.\n")
62
+ f.write("ํŒŒ์ผ์˜ ํ•ต์‹ฌ ๋‚ด์šฉ์€ '๋Œ€ํ•œ๋ฏผ๊ตญ์˜ ์ˆ˜๋„๋Š” ์„œ์šธ์ด๋‹ค' ์ž…๋‹ˆ๋‹ค.")
63
+
64
+ print(f"์—…๋กœ๋“œํ•  ํŒŒ์ผ: '{temp_file_path}'")
65
+ test_message = "์—…๋กœ๋“œํ•œ ํŒŒ์ผ์˜ ํ•ต์‹ฌ ๋‚ด์šฉ์ด ๋ญ์•ผ?"
66
+ print(f"๋ณด๋‚ด๋Š” ๋ฉ”์‹œ์ง€: '{test_message}'")
67
+
68
+ # `file()` ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŒŒ์ผ์„ ์„œ๋ฒ„์— ์—…๋กœ๋“œ ๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜
69
+ result = client.predict(
70
+ test_message,
71
+ [], # ์ฑ„ํŒ… ๋‚ด์—ญ ์—†์Œ
72
+ file(temp_file_path),
73
+ fn_index=0
74
+ )
75
+
76
+ # ์ž„์‹œ ํŒŒ์ผ ์‚ญ์ œ
77
+ os.remove(temp_file_path)
78
+
79
+ bot_response = result[1][-1]['content']
80
+ print("โœ… ํ…Œ์ŠคํŠธ ์„ฑ๊ณต!")
81
+ print(f"๐Ÿค– ๋ฐ›์€ ์‘๋‹ต: '{bot_response}'")
82
+
83
+
84
+ if __name__ == "__main__":
85
+ print(f"Gradio ์„œ๋ฒ„({SERVER_URL})์— ์—ฐ๊ฒฐ์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค...")
86
+
87
+ try:
88
+ # ์„œ๋ฒ„์— ํด๋ผ์ด์–ธํŠธ๋กœ ์—ฐ๊ฒฐ
89
+ client = Client(SERVER_URL, verbose=False)
90
+ print("โœ… ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์„ฑ๊ณต!")
91
+
92
+ # ํ…Œ์ŠคํŠธ ์‹คํ–‰
93
+ run_chat_test(client)
94
+ run_math_test(client)
95
+ # run_file_test(client) # ํŒŒ์ผ ํ…Œ์ŠคํŠธ๋Š” ํ•„์š”์‹œ ์ฃผ์„ ํ•ด์ œํ•˜์—ฌ ์‚ฌ์šฉ
96
+
97
+ except Exception as e:
98
+ print(f"\nโŒ ํ…Œ์ŠคํŠธ ์‹คํŒจ: ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์—†๊ฑฐ๋‚˜ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.")
99
+ print("๋จผ์ € ๋‹ค๋ฅธ ํ„ฐ๋ฏธ๋„์—์„œ 'python app.py'๋ฅผ ์‹คํ–‰ํ–ˆ๋Š”์ง€ ํ™•์ธํ•ด์ฃผ์„ธ์š”.")
100
+ print(f"์˜ค๋ฅ˜ ์ƒ์„ธ ์ •๋ณด: {e}")
test_text.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from gradio_client import Client, file
3
+
4
+ # --- ์„ค์ • ---
5
+ # ๋กœ์ปฌ Gradio ์„œ๋ฒ„ ์ฃผ์†Œ (app.py ์‹คํ–‰ ์‹œ ํ„ฐ๋ฏธ๋„์— ํ‘œ์‹œ๋˜๋Š” ์ฃผ์†Œ)
6
+ SERVER_URL = "http://localhost:8006/"
7
+
8
+ def run_chat_test(client):
9
+ """์ผ๋ฐ˜ ์ฑ„ํŒ… ํƒญ์˜ ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค."""
10
+ print("\n--- ๐Ÿ’ฌ ์ผ๋ฐ˜ ์ฑ„ํŒ… ํ…Œ์ŠคํŠธ ์‹œ์ž‘ ---")
11
+
12
+ test_message = "์•ˆ๋…•ํ•˜์„ธ์š”! ์˜ค๋Š˜ ๋‚ ์”จ๋Š” ์–ด๋–ค๊ฐ€์š”?"
13
+ chat_history = [] # ์ดˆ๊ธฐ ๋Œ€ํ™” ๋‚ด์—ญ์€ ๋น„์–ด์žˆ์Œ
14
+
15
+ print(f"๋ณด๋‚ด๋Š” ๋ฉ”์‹œ์ง€: '{test_message}'")
16
+
17
+ # `respond` ํ•จ์ˆ˜ ํ˜ธ์ถœ (API ์—”๋“œํฌ์ธํŠธ ์ธ๋ฑ์Šค: 0)
18
+ # ์ž…๋ ฅ: (๋ฉ”์‹œ์ง€, ์ฑ„ํŒ… ๋‚ด์—ญ, ํŒŒ์ผ)
19
+ # ์ถœ๋ ฅ: (๋น„์›Œ์ง„ ํ…์ŠคํŠธ ๋ฐ•์Šค, ๊ฐฑ์‹ ๋œ ์ฑ„ํŒ… ๋‚ด์—ญ)
20
+ result = client.predict(
21
+ test_message,
22
+ chat_history,
23
+ None, # ํŒŒ์ผ ์—†์Œ
24
+ fn_index=0
25
+ )
26
+
27
+ # ๊ฐฑ์‹ ๋œ ์ฑ„ํŒ… ๋‚ด์—ญ์—์„œ ๋งˆ์ง€๋ง‰ ์‘๋‹ต(๋ด‡ ๋ฉ”์‹œ์ง€)์„ ์ถ”์ถœ
28
+ updated_history = result[1]
29
+ bot_response = updated_history[-1]['content']
30
+
31
+ print("โœ… ํ…Œ์ŠคํŠธ ์„ฑ๊ณต!")
32
+ print(f"๐Ÿค– ๋ฐ›์€ ์‘๋‹ต: '{bot_response}'")
33
+
34
+ def run_math_test(client):
35
+ """์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ ํƒญ์˜ ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค."""
36
+ print("\n--- ๐Ÿงฎ ์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ ํ…Œ์ŠคํŠธ ์‹œ์ž‘ ---")
37
+
38
+ test_problem = "๋‘ ๊ฐœ์˜ ์—ฐ์†๋œ ์ง์ˆ˜์˜ ํ•ฉ์ด 34์ผ ๋•Œ, ๋‘ ์ง์ˆ˜๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?"
39
+
40
+ print(f"๋ณด๋‚ด๋Š” ๋ฌธ์ œ: '{test_problem}'")
41
+
42
+ # ์ˆ˜ํ•™ ๋ฌธ์ œ ํ•ด๊ฒฐ ํ•จ์ˆ˜ ํ˜ธ์ถœ (API ์—”๋“œํฌ์ธํŠธ ์ธ๋ฑ์Šค: 1)
43
+ # ์ž…๋ ฅ: (์ˆ˜ํ•™ ๋ฌธ์ œ, ํŒŒ์ผ)
44
+ # ์ถœ๋ ฅ: (๊ฒฐ๊ณผ ํ…์ŠคํŠธ)
45
+ result = client.predict(
46
+ test_problem,
47
+ None, # ํŒŒ์ผ ์—†์Œ
48
+ fn_index=1
49
+ )
50
+
51
+ print("โœ… ํ…Œ์ŠคํŠธ ์„ฑ๊ณต!")
52
+ print(f"๐Ÿค– ๋ฐ›์€ ์‘๋‹ต (์ผ๋ถ€): '{result[:200]}...'")
53
+
54
+ def run_file_test(client):
55
+ """ํŒŒ์ผ ์—…๋กœ๋“œ ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค."""
56
+ print("\n--- ๐Ÿ“ ํŒŒ์ผ ์—…๋กœ๋“œ ์ฑ„ํŒ… ํ…Œ์ŠคํŠธ ์‹œ์ž‘ ---")
57
+
58
+ # ํ…Œ์ŠคํŠธ์šฉ ์ž„์‹œ ํ…์ŠคํŠธ ํŒŒ์ผ ์ƒ์„ฑ
59
+ temp_file_path = "test_document.txt"
60
+ with open(temp_file_path, "w", encoding="utf-8") as f:
61
+ f.write("์ด ํŒŒ์ผ์€ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.\n")
62
+ f.write("ํŒŒ์ผ์˜ ํ•ต์‹ฌ ๋‚ด์šฉ์€ '๋Œ€ํ•œ๋ฏผ๊ตญ์˜ ์ˆ˜๋„๋Š” ์„œ์šธ์ด๋‹ค' ์ž…๋‹ˆ๋‹ค.")
63
+
64
+ print(f"์—…๋กœ๋“œํ•  ํŒŒ์ผ: '{temp_file_path}'")
65
+ test_message = "์—…๋กœ๋“œํ•œ ํŒŒ์ผ์˜ ํ•ต์‹ฌ ๋‚ด์šฉ์ด ๋ญ์•ผ?"
66
+ print(f"๋ณด๋‚ด๋Š” ๋ฉ”์‹œ์ง€: '{test_message}'")
67
+
68
+ # `file()` ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŒŒ์ผ์„ ์„œ๋ฒ„์— ์—…๋กœ๋“œ ๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜
69
+ result = client.predict(
70
+ test_message,
71
+ [], # ์ฑ„ํŒ… ๋‚ด์—ญ ์—†์Œ
72
+ file(temp_file_path),
73
+ fn_index=0
74
+ )
75
+
76
+ # ์ž„์‹œ ํŒŒ์ผ ์‚ญ์ œ
77
+ os.remove(temp_file_path)
78
+
79
+ bot_response = result[1][-1]['content']
80
+ print("โœ… ํ…Œ์ŠคํŠธ ์„ฑ๊ณต!")
81
+ print(f"๐Ÿค– ๋ฐ›์€ ์‘๋‹ต: '{bot_response}'")
82
+
83
+
84
+ if __name__ == "__main__":
85
+ print(f"Gradio ์„œ๋ฒ„({SERVER_URL})์— ์—ฐ๊ฒฐ์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค...")
86
+
87
+ try:
88
+ # ์„œ๋ฒ„์— ํด๋ผ์ด์–ธํŠธ๋กœ ์—ฐ๊ฒฐ
89
+ client = Client(SERVER_URL, verbose=False)
90
+ print("โœ… ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์„ฑ๊ณต!")
91
+
92
+ # ํ…Œ์ŠคํŠธ ์‹คํ–‰
93
+ run_chat_test(client)
94
+ run_math_test(client)
95
+ # run_file_test(client) # ํŒŒ์ผ ํ…Œ์ŠคํŠธ๋Š” ํ•„์š”์‹œ ์ฃผ์„ ํ•ด์ œํ•˜์—ฌ ์‚ฌ์šฉ
96
+
97
+ except Exception as e:
98
+ print(f"\nโŒ ํ…Œ์ŠคํŠธ ์‹คํŒจ: ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์—†๊ฑฐ๋‚˜ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.")
99
+ print("๋จผ์ € ๋‹ค๋ฅธ ํ„ฐ๋ฏธ๋„์—์„œ 'python app.py'๋ฅผ ์‹คํ–‰ํ–ˆ๋Š”์ง€ ํ™•์ธํ•ด์ฃผ์„ธ์š”.")
100
+ print(f"์˜ค๋ฅ˜ ์ƒ์„ธ ์ •๋ณด: {e}")
test_tokenizer.py ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import traceback
3
+ from typing import Optional
4
+ from transformers import AutoTokenizer
5
+ import torch
6
+
7
+ # ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋กœ๋“œ
8
+ try:
9
+ from dotenv import load_dotenv
10
+ load_dotenv()
11
+ print("โœ… .env ํŒŒ์ผ ๋กœ๋“œ๋จ")
12
+ except ImportError:
13
+ print("โš ๏ธ python-dotenv๊ฐ€ ์„ค์น˜๋˜์ง€ ์•Š์Œ")
14
+
15
+ HF_TOKEN = os.getenv("HF_TOKEN")
16
+
17
+ # ํ™˜๊ฒฝ ๊ฐ์ง€
18
+ IS_LOCAL = os.path.exists('../.env') or 'LOCAL_TEST' in os.environ
19
+ print(f"๐Ÿ” ํ™˜๊ฒฝ: {'๋กœ์ปฌ' if IS_LOCAL else '์„œ๋ฒ„'}")
20
+
21
+ # ํ™˜๊ฒฝ์— ๋”ฐ๋ฅธ ๋ชจ๋ธ ๊ฒฝ๋กœ ์„ค์ •
22
+ if IS_LOCAL:
23
+ # ๋กœ์ปฌ ๋ชจ๋ธ ๊ฒฝ๋กœ (hearth_llm_model ํด๋” ์‚ฌ์šฉ)
24
+ MODEL_PATH = "../lily_llm_core/models/kanana-1.5-v-3b-instruct"
25
+ print(f"๐Ÿ” ๋กœ์ปฌ ๋ชจ๋ธ ๊ฒฝ๋กœ: {MODEL_PATH}")
26
+ print(f"๐Ÿ” ๊ฒฝ๋กœ ์กด์žฌ: {os.path.exists(MODEL_PATH)}")
27
+ else:
28
+ # ์„œ๋ฒ„์—์„œ๋Š” Hugging Face ๋ชจ๋ธ ์‚ฌ์šฉ
29
+ MODEL_PATH = os.getenv("MODEL_NAME", "gbrabbit/lily-math-model")
30
+ print(f"๐Ÿ” ์„œ๋ฒ„ ๋ชจ๋ธ: {MODEL_PATH}")
31
+
32
+ print(f"๐Ÿ” ํ† ํฐ: {'โœ… ์„ค์ •๋จ' if HF_TOKEN else 'โŒ ์„ค์ •๋˜์ง€ ์•Š์Œ'}")
33
+
34
+ # ํ† ํฌ๋‚˜์ด์ € ํ…Œ์ŠคํŠธ
35
+ print("\n๐Ÿ”ง ํ† ํฌ๋‚˜์ด์ € ํ…Œ์ŠคํŠธ ์‹œ์ž‘...")
36
+
37
+ try:
38
+ print("๐Ÿ“ค ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์ค‘...")
39
+ print(f" MODEL_PATH: {MODEL_PATH}")
40
+ print(f" IS_LOCAL: {IS_LOCAL}")
41
+ print(f" trust_remote_code: True")
42
+ print(f" use_fast: False")
43
+
44
+ if IS_LOCAL:
45
+ tokenizer = AutoTokenizer.from_pretrained(
46
+ MODEL_PATH,
47
+ trust_remote_code=True,
48
+ )
49
+ else:
50
+ tokenizer = AutoTokenizer.from_pretrained(
51
+ MODEL_PATH,
52
+ token=HF_TOKEN,
53
+ trust_remote_code=True,
54
+ )
55
+
56
+ print(f"โœ… ํ† ํฌ๋‚˜์ด์ € ๋กœ๋”ฉ ์™„๋ฃŒ")
57
+ print(f" ํƒ€์ž…: {type(tokenizer)}")
58
+ print(f" ๊ฐ’: {tokenizer}")
59
+ print(f" hasattr('encode'): {hasattr(tokenizer, 'encode')}")
60
+ print(f" hasattr('__call__'): {hasattr(tokenizer, '__call__')}")
61
+
62
+ # ํ† ํฌ๋‚˜์ด์ € ํ…Œ์ŠคํŠธ
63
+ test_input = "์•ˆ๋…•ํ•˜์„ธ์š”"
64
+ print(f"\n๐Ÿ”ค ํ† ํฌ๋‚˜์ด์ € ํ…Œ์ŠคํŠธ: '{test_input}'")
65
+
66
+ test_tokens = tokenizer(test_input, return_tensors="pt")
67
+ print(f" โœ… ํ† ํฌ๋‚˜์ด์ € ํ˜ธ์ถœ ์„ฑ๊ณต")
68
+ print(f" input_ids shape: {test_tokens['input_ids'].shape}")
69
+ print(f" attention_mask shape: {test_tokens['attention_mask'].shape}")
70
+
71
+ # ๋””์ฝ”๋”ฉ ํ…Œ์ŠคํŠธ
72
+ decoded = tokenizer.decode(test_tokens['input_ids'][0], skip_special_tokens=True)
73
+ print(f" ๋””์ฝ”๋”ฉ ๊ฒฐ๊ณผ: '{decoded}'")
74
+
75
+ except Exception as e:
76
+ print(f"โŒ ํ† ํฌ๋‚˜์ด์ € ํ…Œ์ŠคํŠธ ์‹คํŒจ: {e}")
77
+ print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(e).__name__}")
78
+ traceback.print_exc()
79
+
80
+ # ๋ชจ๋ธ ํ…Œ์ŠคํŠธ
81
+ print("\n๐Ÿ”ง ๋ชจ๋ธ ํ…Œ์ŠคํŠธ ์‹œ์ž‘...")
82
+
83
+ try:
84
+ print("๐Ÿ“ค ๋ชจ๋ธ ๋กœ๋”ฉ ์ค‘...")
85
+ from modeling import KananaVForConditionalGeneration
86
+
87
+ if IS_LOCAL:
88
+ model = KananaVForConditionalGeneration.from_pretrained(
89
+ MODEL_PATH,
90
+ torch_dtype=torch.float16,
91
+ trust_remote_code=True,
92
+ device_map=None,
93
+ low_cpu_mem_usage=True
94
+ )
95
+ else:
96
+ model = KananaVForConditionalGeneration.from_pretrained(
97
+ MODEL_PATH,
98
+ token=HF_TOKEN,
99
+ torch_dtype=torch.float16,
100
+ trust_remote_code=True,
101
+ device_map=None,
102
+ low_cpu_mem_usage=True
103
+ )
104
+
105
+ print(f"โœ… ๋ชจ๋ธ ๋กœ๋”ฉ ์™„๋ฃŒ")
106
+ # print(f" ํƒ€์ž…: {type(model)}")
107
+ # print(f" ๋””๋ฐ”์ด์Šค: {next(model.parameters()).device}")
108
+
109
+ # ๋ชจ๋ธ ํ…Œ์ŠคํŠธ
110
+ test_input = "์•ˆ๋…•ํ•˜์„ธ์š”"
111
+ formatted_prompt = f"<|im_start|>user\n{test_input}<|im_end|>\n<|im_start|>assistant\n"
112
+ max_length: Optional[int] = None
113
+
114
+ inputs = tokenizer(
115
+ formatted_prompt,
116
+ return_tensors="pt",
117
+ padding=True,
118
+ truncation=True,
119
+ max_length=512
120
+ )
121
+
122
+ print(f"\n๐Ÿค– ๋ชจ๋ธ ์ถ”๋ก  ํ…Œ์ŠคํŠธ: '{test_input}'")
123
+
124
+ # Kanana์šฉ ์ƒ์„ฑ ์„ค์ •
125
+ max_new_tokens = max_length or 100
126
+
127
+ with torch.no_grad():
128
+ outputs = model.generate(
129
+ input_ids=inputs["input_ids"],
130
+ attention_mask=inputs["attention_mask"],
131
+ max_new_tokens=max_new_tokens,
132
+ repetition_penalty=1.1,
133
+ no_repeat_ngram_size=2,
134
+ pad_token_id=tokenizer.eos_token_id,
135
+ eos_token_id=tokenizer.eos_token_id,
136
+ use_cache=True
137
+ )
138
+
139
+ print(f" โœ… ๋ชจ๋ธ ํ˜ธ์ถœ ์„ฑ๊ณต")
140
+ print(f" outputs ํƒ€์ž…: {type(outputs)}")
141
+ print(f" outputs shape: {outputs.shape}")
142
+
143
+ # ๋””์ฝ”๋”ฉ ํ…Œ์ŠคํŠธ
144
+ # model.generate()์˜ ์ถœ๋ ฅ์€ ์ „์ฒด ์‹œํ€€์Šค์ด๋ฏ€๋กœ ๋ฐ”๋กœ ๋””์ฝ”๋”ฉํ•ฉ๋‹ˆ๋‹ค.
145
+ # outputs[0]์€ ๋ฐฐ์น˜ ์ค‘ ์ฒซ ๋ฒˆ์งธ ๊ฒฐ๊ณผ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
146
+ response = tokenizer.decode(outputs[0], skip_special_tokens=True)
147
+
148
+ # ์ž…๋ ฅ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์‘๋‹ต์—์„œ ์ œ๊ฑฐ (์„ ํƒ์‚ฌํ•ญ)
149
+ assistant_response = response.split("<|im_start|>assistant\n")[-1]
150
+
151
+ print(f" ์ƒ์„ฑ๋œ ์ „์ฒด ํ…์ŠคํŠธ: '{response}'")
152
+ print(f" ์–ด์‹œ์Šคํ„ดํŠธ ์‘๋‹ต: '{assistant_response.strip()}'")
153
+
154
+ except Exception as e:
155
+ print(f"โŒ ๋ชจ๋ธ ํ…Œ์ŠคํŠธ ์‹คํŒจ: {e}")
156
+ print(f" ์˜ค๋ฅ˜ ํƒ€์ž…: {type(e).__name__}")
157
+ traceback.print_exc()
158
+
159
+ print("\nโœ… ํ…Œ์ŠคํŠธ ์™„๋ฃŒ!")