Madras1 commited on
Commit
5e164ca
·
verified ·
1 Parent(s): d6f2775

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +58 -78
app.py CHANGED
@@ -20,13 +20,10 @@ if os.environ.get("GEMINI_API_KEY"):
20
  genai.configure(api_key=os.environ.get("GEMINI_API_KEY"))
21
 
22
  # --- HELPER ---
23
- def encode_image(image_source):
24
  try:
25
- # Se for string (caminho), abre. Se for objeto imagem, ignora.
26
- if isinstance(image_source, str):
27
- with open(image_source, "rb") as image_file:
28
- return base64.b64encode(image_file.read()).decode('utf-8')
29
- return None
30
  except Exception:
31
  return None
32
 
@@ -36,11 +33,8 @@ def encode_image(image_source):
36
  def run_local_h200(messages):
37
  # Qwen não suporta imagens
38
  for m in messages:
39
- content = m['content']
40
- if isinstance(content, list): # Verifica se tem imagem na lista
41
- for item in content:
42
- if isinstance(item, dict) and (item.get('type') == 'image' or item.get('type') == 'image_url'):
43
- return "⚠️ Qwen H200 não suporta imagens. Use Gemini ou Pixtral."
44
 
45
  global local_model, local_tokenizer
46
  if local_model is None:
@@ -50,42 +44,21 @@ def run_local_h200(messages):
50
  LOCAL_MODEL_ID, torch_dtype=torch.bfloat16, device_map="cuda"
51
  )
52
 
53
- # Simplifica input para texto
54
- text_only_msgs = []
55
- for m in messages:
56
- content = m['content']
57
- text = ""
58
- if isinstance(content, str): text = content
59
- elif isinstance(content, list):
60
- for item in content:
61
- if item.get('type') == 'text': text += item['text'] + " "
62
- text_only_msgs.append({"role": m['role'], "content": text.strip()})
63
-
64
- text = local_tokenizer.apply_chat_template(text_only_msgs, tokenize=False, add_generation_prompt=True)
65
  inputs = local_tokenizer([text], return_tensors="pt").to(local_model.device)
66
  outputs = local_model.generate(**inputs, max_new_tokens=2048, temperature=0.6, do_sample=True)
67
  return local_tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True)
68
 
69
  def run_groq(messages, model_id):
70
- # Groq não suporta imagens
71
  for m in messages:
72
- content = m['content']
73
- if isinstance(content, list):
74
- for item in content:
75
- if isinstance(item, dict) and (item.get('type') == 'image' or item.get('type') == 'image_url'):
76
- return "⚠️ Groq não suporta imagens. Use Gemini ou Pixtral."
77
 
78
  if not groq_client: return "❌ Erro: GROQ_API_KEY ausente."
79
 
80
  clean_msgs = []
81
  for m in messages:
82
- content = m['content']
83
- text = ""
84
- if isinstance(content, str): text = content
85
- elif isinstance(content, list):
86
- for item in content:
87
- if item.get('type') == 'text': text += item['text']
88
- clean_msgs.append({"role": m['role'], "content": text})
89
 
90
  try:
91
  completion = groq_client.chat.completions.create(
@@ -97,7 +70,6 @@ def run_groq(messages, model_id):
97
  def run_mistral(messages, model_id):
98
  if not mistral_client: return "❌ Erro: MISTRAL_API_KEY ausente."
99
 
100
- # Mistral espera formato específico para imagens
101
  formatted_msgs = []
102
  for m in messages:
103
  new_content = []
@@ -109,14 +81,14 @@ def run_mistral(messages, model_id):
109
  for item in content:
110
  if item.get('type') == 'text':
111
  new_content.append({"type": "text", "text": item['text']})
112
- elif item.get('type') == 'image' or item.get('type') == 'image_url':
113
- # Gradio 5 manda 'image' com caminho, ou 'image_url' com url
114
- path = item.get('image') or item.get('image_url')
115
- if isinstance(path, dict): path = path.get('url') # Caso venha aninhado
116
-
117
- if path and os.path.exists(path):
118
- b64 = encode_image(path)
119
  new_content.append({"type": "image_url", "image_url": f"data:image/jpeg;base64,{b64}"})
 
 
120
 
121
  formatted_msgs.append({"role": m['role'], "content": new_content})
122
 
@@ -131,7 +103,6 @@ def run_gemini(messages, model_id):
131
  model = genai.GenerativeModel(model_id)
132
  chat_history = []
133
 
134
- # Converte histórico
135
  for m in messages[:-1]:
136
  role = "user" if m['role'] == "user" else "model"
137
  parts = []
@@ -140,12 +111,11 @@ def run_gemini(messages, model_id):
140
  elif isinstance(content, list):
141
  for item in content:
142
  if item.get('type') == 'text': parts.append(item['text'])
143
- elif item.get('type') == 'image':
144
- path = item.get('image')
145
- if path: parts.append(Image.open(path))
146
  chat_history.append({"role": role, "parts": parts})
147
 
148
- # Mensagem atual
149
  last_msg = messages[-1]
150
  current_parts = []
151
  content = last_msg['content']
@@ -153,39 +123,49 @@ def run_gemini(messages, model_id):
153
  elif isinstance(content, list):
154
  for item in content:
155
  if item.get('type') == 'text': current_parts.append(item['text'])
156
- elif item.get('type') == 'image':
157
- path = item.get('image')
158
- if path: current_parts.append(Image.open(path))
159
 
160
  chat = model.start_chat(history=chat_history)
161
  response = chat.send_message(current_parts)
162
  return response.text
163
  except Exception as e: return f"❌ Gemini Error ({model_id}): {e}"
164
 
165
- # --- ROTEADOR (FIXED FOR GRADIO 5) ---
166
  def router(message, history, model_selector):
167
- # No Gradio 5 com type="messages", history vem formatado como lista de dicts!
168
- # message é um dict {'text': '...', 'files': [...]}
169
 
170
- # 1. Prepara a mensagem atual no formato OpenAI-like
171
- current_msg = {"role": "user", "content": []}
172
 
173
- # Texto
174
- if message.get("text"):
175
- current_msg["content"].append({"type": "text", "text": message["text"]})
176
-
177
- # Imagens (Arquivos)
178
- for file_path in message.get("files", []):
179
- # Gradio 5 passa o caminho local do arquivo
180
- current_msg["content"].append({"type": "image", "image": file_path})
181
 
182
- # Se não tiver imagem, simplifica (opcional, mas bom pra debug)
183
- if not message.get("files") and message.get("text"):
184
- # Se for só texto, mantém estrutura de lista pra compatibilidade com funções novas
185
- pass
 
 
 
 
 
 
 
 
 
 
186
 
187
- # 2. Junta tudo
188
- full_history = history + [current_msg]
 
 
 
189
 
190
  # 3. Roteamento
191
  if "Gemini" in model_selector:
@@ -194,7 +174,7 @@ def router(message, history, model_selector):
194
  elif "2.5 Pro" in model_selector: tid = "gemini-2.5-pro"
195
  elif "2.5 Flash" in model_selector: tid = "gemini-2.5-flash"
196
  elif "2.0 Flash" in model_selector: tid = "gemini-2.0-flash-exp"
197
- return run_gemini(full_history, tid)
198
 
199
  elif "Mistral" in model_selector:
200
  tid = "mistral-large-latest"
@@ -202,19 +182,19 @@ def router(message, history, model_selector):
202
  elif "2512" in model_selector: tid = "mistral-large-2512"
203
  elif "Magistral" in model_selector: tid = "magistral-medium-latest"
204
  elif "Codestral" in model_selector: tid = "codestral-2508"
205
- return run_mistral(full_history, tid)
206
 
207
  elif "Groq" in model_selector:
208
- return run_groq(full_history, "llama-3.3-70b-versatile")
209
 
210
  elif "H200" in model_selector:
211
- return run_local_h200(full_history)
212
 
213
  return "Modelo desconhecido."
214
 
215
  # --- UI ---
216
- with gr.Blocks(fill_height=True) as demo:
217
- gr.Markdown("# 🔀 APIDOST V7 (Gradio 5 Native)")
218
 
219
  with gr.Row():
220
  model_dropdown = gr.Dropdown(
@@ -231,9 +211,9 @@ with gr.Blocks(fill_height=True) as demo:
231
  interactive=True
232
  )
233
 
 
234
  chat = gr.ChatInterface(
235
  fn=router,
236
- type="messages", # O SEGREDO! Isso diz pro Gradio mandar a history como dict
237
  additional_inputs=[model_dropdown],
238
  multimodal=True,
239
  )
 
20
  genai.configure(api_key=os.environ.get("GEMINI_API_KEY"))
21
 
22
  # --- HELPER ---
23
+ def encode_image(image_path):
24
  try:
25
+ with open(image_path, "rb") as image_file:
26
+ return base64.b64encode(image_file.read()).decode('utf-8')
 
 
 
27
  except Exception:
28
  return None
29
 
 
33
  def run_local_h200(messages):
34
  # Qwen não suporta imagens
35
  for m in messages:
36
+ if isinstance(m['content'], list):
37
+ return "⚠️ Qwen H200 não suporta imagens. Use Gemini ou Pixtral."
 
 
 
38
 
39
  global local_model, local_tokenizer
40
  if local_model is None:
 
44
  LOCAL_MODEL_ID, torch_dtype=torch.bfloat16, device_map="cuda"
45
  )
46
 
47
+ text = local_tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
 
 
 
 
 
 
 
 
 
 
 
48
  inputs = local_tokenizer([text], return_tensors="pt").to(local_model.device)
49
  outputs = local_model.generate(**inputs, max_new_tokens=2048, temperature=0.6, do_sample=True)
50
  return local_tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True)
51
 
52
  def run_groq(messages, model_id):
 
53
  for m in messages:
54
+ if isinstance(m['content'], list):
55
+ return "⚠️ Groq não suporta imagens. Use Gemini ou Pixtral."
 
 
 
56
 
57
  if not groq_client: return "❌ Erro: GROQ_API_KEY ausente."
58
 
59
  clean_msgs = []
60
  for m in messages:
61
+ clean_msgs.append({"role": m['role'], "content": m['content']})
 
 
 
 
 
 
62
 
63
  try:
64
  completion = groq_client.chat.completions.create(
 
70
  def run_mistral(messages, model_id):
71
  if not mistral_client: return "❌ Erro: MISTRAL_API_KEY ausente."
72
 
 
73
  formatted_msgs = []
74
  for m in messages:
75
  new_content = []
 
81
  for item in content:
82
  if item.get('type') == 'text':
83
  new_content.append({"type": "text", "text": item['text']})
84
+ elif item.get('type') == 'image_url':
85
+ url = item['image_url']['url']
86
+ # Gradio 4 local path handling
87
+ if not url.startswith("data:") and os.path.exists(url):
88
+ b64 = encode_image(url)
 
 
89
  new_content.append({"type": "image_url", "image_url": f"data:image/jpeg;base64,{b64}"})
90
+ else:
91
+ new_content.append({"type": "image_url", "image_url": url})
92
 
93
  formatted_msgs.append({"role": m['role'], "content": new_content})
94
 
 
103
  model = genai.GenerativeModel(model_id)
104
  chat_history = []
105
 
 
106
  for m in messages[:-1]:
107
  role = "user" if m['role'] == "user" else "model"
108
  parts = []
 
111
  elif isinstance(content, list):
112
  for item in content:
113
  if item.get('type') == 'text': parts.append(item['text'])
114
+ elif item.get('type') == 'image_url':
115
+ path = item['image_url']['url']
116
+ if os.path.exists(path): parts.append(Image.open(path))
117
  chat_history.append({"role": role, "parts": parts})
118
 
 
119
  last_msg = messages[-1]
120
  current_parts = []
121
  content = last_msg['content']
 
123
  elif isinstance(content, list):
124
  for item in content:
125
  if item.get('type') == 'text': current_parts.append(item['text'])
126
+ elif item.get('type') == 'image_url':
127
+ path = item['image_url']['url']
128
+ if os.path.exists(path): current_parts.append(Image.open(path))
129
 
130
  chat = model.start_chat(history=chat_history)
131
  response = chat.send_message(current_parts)
132
  return response.text
133
  except Exception as e: return f"❌ Gemini Error ({model_id}): {e}"
134
 
135
+ # --- ROTEADOR (COMPATÍVEL GRADIO 4) ---
136
  def router(message, history, model_selector):
137
+ # Em Gradio 4, history é [[user, bot], [user, bot]]
138
+ # message é {'text': '...', 'files': ['path']}
139
 
140
+ formatted_history = []
 
141
 
142
+ # 1. Processa o histórico antigo
143
+ for user_turn, bot_turn in history:
144
+ # User turn (pode ser string ou tupla/lista com imagem no Gradio 4 antigo, mas vamos tratar como string safe)
145
+ u_text = str(user_turn)
146
+ # Tenta limpar se vier com sujeira de arquivo
147
+ if isinstance(user_turn, tuple): u_text = user_turn[0]
 
 
148
 
149
+ formatted_history.append({"role": "user", "content": u_text})
150
+ if bot_turn:
151
+ formatted_history.append({"role": "assistant", "content": str(bot_turn)})
152
+
153
+ # 2. Processa a mensagem ATUAL
154
+ current_content = []
155
+ text_input = message.get("text", "")
156
+ files = message.get("files", [])
157
+
158
+ if text_input:
159
+ current_content.append({"type": "text", "text": text_input})
160
+
161
+ for file_path in files:
162
+ current_content.append({"type": "image_url", "image_url": {"url": file_path}})
163
 
164
+ # Adiciona ao histórico final
165
+ if not files:
166
+ formatted_history.append({"role": "user", "content": text_input})
167
+ else:
168
+ formatted_history.append({"role": "user", "content": current_content})
169
 
170
  # 3. Roteamento
171
  if "Gemini" in model_selector:
 
174
  elif "2.5 Pro" in model_selector: tid = "gemini-2.5-pro"
175
  elif "2.5 Flash" in model_selector: tid = "gemini-2.5-flash"
176
  elif "2.0 Flash" in model_selector: tid = "gemini-2.0-flash-exp"
177
+ return run_gemini(formatted_history, tid)
178
 
179
  elif "Mistral" in model_selector:
180
  tid = "mistral-large-latest"
 
182
  elif "2512" in model_selector: tid = "mistral-large-2512"
183
  elif "Magistral" in model_selector: tid = "magistral-medium-latest"
184
  elif "Codestral" in model_selector: tid = "codestral-2508"
185
+ return run_mistral(formatted_history, tid)
186
 
187
  elif "Groq" in model_selector:
188
+ return run_groq(formatted_history, "llama-3.3-70b-versatile")
189
 
190
  elif "H200" in model_selector:
191
+ return run_local_h200(formatted_history)
192
 
193
  return "Modelo desconhecido."
194
 
195
  # --- UI ---
196
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
197
+ gr.Markdown("# 🔀 APIDOST V7 (Gradio 4 Fix)")
198
 
199
  with gr.Row():
200
  model_dropdown = gr.Dropdown(
 
211
  interactive=True
212
  )
213
 
214
+ # REMOVIDO type="messages"
215
  chat = gr.ChatInterface(
216
  fn=router,
 
217
  additional_inputs=[model_dropdown],
218
  multimodal=True,
219
  )