GgaammGo commited on
Commit
95fa14a
Β·
verified Β·
1 Parent(s): 6aa9afe

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +84 -12
app.py CHANGED
@@ -1,3 +1,5 @@
 
 
1
  import os
2
  import gradio as gr
3
  from openai import OpenAI, APIError
@@ -15,15 +17,48 @@ client = OpenAI(
15
  )
16
 
17
 
18
- # ── Gradio 버전에 따라 history ν˜•μ‹μ΄ λ‹€λ₯΄λ―€λ‘œ 두 ν˜•μ‹ λͺ¨λ‘ 지원 ────────────
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  def _normalize_history(history: list) -> list:
20
- """historyλŠ” Gradio 버전에 따라
21
  - μ‹ ν˜•(messages) ν˜•μ‹: [{"role": "user", "content": "..."}, ...]
22
  - κ΅¬ν˜•(tuples) ν˜•μ‹: [["user λ©”μ‹œμ§€", "bot λ©”μ‹œμ§€"], ...]
23
- λ‘˜ 쀑 ν•˜λ‚˜λ‘œ λ“€μ–΄μ˜¬ 수 μžˆμŠ΅λ‹ˆλ‹€. μ–΄λ–€ ν˜•μ‹μ΄ 와도 OpenAI ν˜Έν™˜
24
- messages λ°°μ—΄λ‘œ ν†΅μΌν•΄μ„œ λ°˜ν™˜ν•©λ‹ˆλ‹€. (νŠΉμ • Gradio 버전에 μ˜μ‘΄ν•˜μ§€
25
- μ•ŠκΈ° μœ„ν•΄, gr.Chatbot/gr.ChatInterface 생성 μ‹œ type 인자λ₯Ό μ§€μ •ν•˜μ§€
26
- μ•Šκ³  이 ν•¨μˆ˜μ—μ„œ ν‘μˆ˜ν•©λ‹ˆλ‹€.)
27
  """
28
  messages = []
29
  if not history:
@@ -46,15 +81,43 @@ def _normalize_history(history: list) -> list:
46
 
47
  if role == "model":
48
  role = "assistant"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  messages.append({"role": role, "content": content})
50
 
51
  return messages
52
 
53
 
54
  # ── λŒ€ν™” ν…μŠ€νŠΈ 생성 ν•¨μˆ˜ (OpenRouter chat.completions 슀트리밍 방식) ────────
55
- def predict_chat(message: str, history: list):
 
 
 
 
 
 
 
 
 
56
  messages = _normalize_history(history)
57
- messages.append({"role": "user", "content": message})
58
 
59
  try:
60
  stream = client.chat.completions.create(
@@ -94,8 +157,8 @@ def predict_chat(message: str, history: list):
94
  yield f"{partial_output}\n\n---\n_(μ‹€μ œ μ‚¬μš© λͺ¨λΈ: `{resolved_model}`)_"
95
 
96
  except APIError as e:
97
- # 무료 λͺ¨λΈμ˜ λΆ„λ‹Ή/일일 μš”μ²­ ν•œλ„ 초과, λͺ¨λΈ μΌμ‹œ 쀑단 λ“±
98
- # OpenRouter μͺ½ μ‚¬μœ λ‘œ 였λ₯˜κ°€ λ‚˜λŠ” κ²½μš°μž…λ‹ˆλ‹€.
99
  yield f"❌ OpenRouter API 였λ₯˜: {str(e)}"
100
  except Exception as e:
101
  yield f"❌ λ„€νŠΈμ›Œν¬ 톡신 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: {str(e)}"
@@ -103,15 +166,24 @@ def predict_chat(message: str, history: list):
103
 
104
  # ── UI λΉŒλ“œ 및 API κ°œμ„€ ──────────────────────────────────────────────────────
105
  with gr.Blocks() as demo:
106
- gr.Markdown(f"# πŸ€– GS-AI API Server (OpenRouter Mode)\nModel: `{MODEL_ID}`")
 
 
 
 
107
 
108
  chatbot_ui = gr.Chatbot(height=480)
109
- textbox_ui = gr.Textbox(placeholder="λ©”μ‹œμ§€λ₯Ό μž…λ ₯ν•˜μ„Έμš”...")
 
 
 
 
110
 
111
  gr.ChatInterface(
112
  fn=predict_chat,
113
  chatbot=chatbot_ui,
114
  textbox=textbox_ui,
 
115
  api_name="chat", # μ™ΈλΆ€ 연동을 μœ„ν•œ /chat 경둜 λͺ…μ‹œ ν™œμ„±ν™”
116
  )
117
 
 
1
+ import base64
2
+ import mimetypes
3
  import os
4
  import gradio as gr
5
  from openai import OpenAI, APIError
 
17
  )
18
 
19
 
20
+ def _encode_image_to_data_url(path: str) -> str:
21
+ """둜컬 이미지 파일 경둜λ₯Ό OpenRouterκ°€ μš”κ΅¬ν•˜λŠ” base64 data URL
22
+ ν˜•μ‹(data:image/png;base64,...)으둜 λ³€ν™˜ν•©λ‹ˆλ‹€.
23
+ (OpenRouter 곡식 λ¬Έμ„œ: Image Inputs κ°€μ΄λ“œ κΈ°μ€€)
24
+ """
25
+ mime_type, _ = mimetypes.guess_type(path)
26
+ mime_type = mime_type or "image/png"
27
+ with open(path, "rb") as f:
28
+ encoded = base64.b64encode(f.read()).decode("utf-8")
29
+ return f"data:{mime_type};base64,{encoded}"
30
+
31
+
32
+ def _build_content(text: str | None, files: list) -> list | str:
33
+ """ν…μŠ€νŠΈμ™€ 이미지 νŒŒμΌλ“€μ„ OpenAI/OpenRouter ν˜Έν™˜ content λ°°μ—΄λ‘œ
34
+ λ³€ν™˜ν•©λ‹ˆλ‹€. 이미지가 μ—†μœΌλ©΄ ν…μŠ€νŠΈλ§Œ λ‹΄κΈ΄ λ¬Έμžμ—΄μ„ κ·ΈλŒ€λ‘œ λ°˜ν™˜ν•©λ‹ˆλ‹€.
35
+ """
36
+ if not files:
37
+ return text or ""
38
+
39
+ content = []
40
+ if text:
41
+ content.append({"type": "text", "text": text})
42
+ for path in files:
43
+ try:
44
+ content.append({
45
+ "type": "image_url",
46
+ "image_url": {"url": _encode_image_to_data_url(path)},
47
+ })
48
+ except Exception:
49
+ # 이미지가 μ•„λ‹ˆκ±°λ‚˜ 읽을 수 μ—†λŠ” νŒŒμΌμ€ 쑰용히 κ±΄λ„ˆλœλ‹ˆλ‹€.
50
+ continue
51
+ return content
52
+
53
+
54
+ # ── Gradio 버전/λ©€ν‹°λͺ¨λ‹¬ 여뢀에 따라 history ν˜•μ‹μ΄ λ‹€λ₯΄λ―€λ‘œ λ‹€μ–‘ν•œ ν˜•μ‹ 지원 ──
55
  def _normalize_history(history: list) -> list:
56
+ """historyλŠ” Gradio 버전 및 λ©€ν‹°λͺ¨λ‹¬ 여뢀에 따라
57
  - μ‹ ν˜•(messages) ν˜•μ‹: [{"role": "user", "content": "..."}, ...]
58
  - κ΅¬ν˜•(tuples) ν˜•μ‹: [["user λ©”μ‹œμ§€", "bot λ©”μ‹œμ§€"], ...]
59
+ - 이미지 첨뢀 μ‹œ: contentκ°€ 파일 κ²½λ‘œκ°€ λ‹΄κΈ΄ νŠœν”Œ/리슀트인 κ²½μš°λ„ 있음
60
+ λ‹€μ–‘ν•œ ν˜•νƒœλ‘œ λ“€μ–΄μ˜¬ 수 μžˆμŠ΅λ‹ˆλ‹€. μ–΄λ–€ ν˜•μ‹μ΄ 와도 OpenAI/OpenRouter
61
+ ν˜Έν™˜ messages λ°°μ—΄λ‘œ ν†΅μΌν•΄μ„œ λ°˜ν™˜ν•©λ‹ˆλ‹€.
 
62
  """
63
  messages = []
64
  if not history:
 
81
 
82
  if role == "model":
83
  role = "assistant"
84
+
85
+ # contentκ°€ 이미지 파일 경둜(νŠœν”Œ/리슀트)인 경우, 이미지 λ©”μ‹œμ§€λ‘œ λ³€ν™˜ν•©λ‹ˆλ‹€.
86
+ if isinstance(content, (tuple, list)) and content:
87
+ path = content[0]
88
+ if isinstance(path, str) and os.path.isfile(path):
89
+ messages.append({
90
+ "role": role,
91
+ "content": _build_content(None, [path]),
92
+ })
93
+ continue
94
+
95
+ # contentκ°€ {"path": "..."} ν˜•νƒœλ‘œ λ“€μ–΄μ˜€λŠ” κ²½μš°λ„ μ²˜λ¦¬ν•©λ‹ˆλ‹€.
96
+ if isinstance(content, dict) and "path" in content:
97
+ messages.append({
98
+ "role": role,
99
+ "content": _build_content(None, [content["path"]]),
100
+ })
101
+ continue
102
+
103
  messages.append({"role": role, "content": content})
104
 
105
  return messages
106
 
107
 
108
  # ── λŒ€ν™” ν…μŠ€νŠΈ 생성 ν•¨μˆ˜ (OpenRouter chat.completions 슀트리밍 방식) ────────
109
+ def predict_chat(message, history: list):
110
+ # multimodal=True일 λ•Œ messageλŠ” {"text": "...", "files": ["경둜", ...]}
111
+ # ν˜•νƒœμ˜ λ”•μ…”λ„ˆλ¦¬λ‘œ λ“€μ–΄μ˜΅λ‹ˆλ‹€. (Gradio 곡식 λ¬Έμ„œ κΈ°μ€€)
112
+ if isinstance(message, dict):
113
+ text = message.get("text") or ""
114
+ files = message.get("files") or []
115
+ else:
116
+ text = message
117
+ files = []
118
+
119
  messages = _normalize_history(history)
120
+ messages.append({"role": "user", "content": _build_content(text, files)})
121
 
122
  try:
123
  stream = client.chat.completions.create(
 
157
  yield f"{partial_output}\n\n---\n_(μ‹€μ œ μ‚¬μš© λͺ¨λΈ: `{resolved_model}`)_"
158
 
159
  except APIError as e:
160
+ # 무료 λͺ¨λΈμ˜ λΆ„λ‹Ή/일일 μš”μ²­ ν•œλ„ 초과, 이미지 미지원 λͺ¨λΈ 선택,
161
+ # λͺ¨λΈ μΌμ‹œ 쀑단 λ“± OpenRouter μͺ½ μ‚¬μœ λ‘œ 였λ₯˜κ°€ λ‚˜λŠ” κ²½μš°μž…λ‹ˆλ‹€.
162
  yield f"❌ OpenRouter API 였λ₯˜: {str(e)}"
163
  except Exception as e:
164
  yield f"❌ λ„€νŠΈμ›Œν¬ 톡신 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: {str(e)}"
 
166
 
167
  # ── UI λΉŒλ“œ 및 API κ°œμ„€ ──────────────────────────────────────────────────────
168
  with gr.Blocks() as demo:
169
+ gr.Markdown(
170
+ f"# πŸ€– GS-AI API Server (OpenRouter Mode)\nModel: `{MODEL_ID}`\n\n"
171
+ "ν…μŠ€νŠΈμ™€ ν•¨κ»˜ 이미지λ₯Ό μ²¨λΆ€ν•΄μ„œ μ§ˆλ¬Έν•˜μ‹€ 수 μžˆμŠ΅λ‹ˆλ‹€. "
172
+ "(단, κ·Έ μ‹œμ μ— 이미지 인식 κ°€λŠ₯ν•œ 무료 λͺ¨λΈμ΄ μ—†μœΌλ©΄ 였λ₯˜κ°€ λ‚  수 μžˆμŠ΅λ‹ˆλ‹€.)"
173
+ )
174
 
175
  chatbot_ui = gr.Chatbot(height=480)
176
+ textbox_ui = gr.MultimodalTextbox(
177
+ placeholder="λ©”μ‹œμ§€λ₯Ό μž…λ ₯ν•˜κ±°λ‚˜ 이미지λ₯Ό μ²¨λΆ€ν•˜μ„Έμš”...",
178
+ file_types=["image"],
179
+ file_count="multiple",
180
+ )
181
 
182
  gr.ChatInterface(
183
  fn=predict_chat,
184
  chatbot=chatbot_ui,
185
  textbox=textbox_ui,
186
+ multimodal=True, # message μΈμžκ°€ {"text": ..., "files": [...]} λ”•μ…”λ„ˆλ¦¬λ‘œ μ „λ‹¬λ©λ‹ˆλ‹€.
187
  api_name="chat", # μ™ΈλΆ€ 연동을 μœ„ν•œ /chat 경둜 λͺ…μ‹œ ν™œμ„±ν™”
188
  )
189