Madras1 commited on
Commit
6473abd
·
verified ·
1 Parent(s): 524cd09

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -73
app.py CHANGED
@@ -16,13 +16,13 @@ LOCAL_MODEL_ID = "Qwen/Qwen2.5-Coder-32B-Instruct"
16
  local_model = None
17
  local_tokenizer = None
18
 
19
- # 2. NUVEM
20
  groq_client = Groq(api_key=os.environ.get("GROQ_API_KEY")) if os.environ.get("GROQ_API_KEY") else None
21
  mistral_client = Mistral(api_key=os.environ.get("MISTRAL_API_KEY")) if os.environ.get("MISTRAL_API_KEY") else None
22
  if os.environ.get("GEMINI_API_KEY"):
23
  genai.configure(api_key=os.environ.get("GEMINI_API_KEY"))
24
 
25
- # --- HELPER: Imagem para Base64 (Mistral) ---
26
  def encode_image(image_path):
27
  with open(image_path, "rb") as image_file:
28
  return base64.b64encode(image_file.read()).decode('utf-8')
@@ -30,9 +30,10 @@ def encode_image(image_path):
30
  # --- FUNÇÃO 1: LOCAL H200 ---
31
  @spaces.GPU(duration=60)
32
  def run_local_h200(messages):
33
- # O Qwen local não suporta imagens via Gradio fácil, rejeitamos se tiver
34
- if isinstance(messages[-1]['content'], list) or (isinstance(messages[-1]['content'], str) and os.path.exists(messages[-1]['content'])):
35
- return "⚠️ O modelo Qwen Local H200 suporta apenas texto. Use Gemini ou Pixtral para imagens."
 
36
 
37
  global local_model, local_tokenizer
38
  if local_model is None:
@@ -42,71 +43,46 @@ def run_local_h200(messages):
42
  LOCAL_MODEL_ID, torch_dtype=torch.bfloat16, device_map="cuda"
43
  )
44
 
45
- # Extrai apenas o texto da última mensagem se for complexa
46
- text_content = messages[-1]['content']
47
- if isinstance(text_content, list): # Fallback defensivo
48
- text_content = next((item['text'] for item in text_content if item['type'] == 'text'), "")
49
-
50
- # Simplifica histórico para texto puro pro Qwen
51
- text_messages = []
52
- for m in messages[:-1]:
53
- content = m['content']
54
- if isinstance(content, list):
55
- content = next((item['text'] for item in content if item['type'] == 'text'), "")
56
- text_messages.append({"role": m['role'], "content": content})
57
- text_messages.append({"role": "user", "content": text_content})
58
-
59
- text = local_tokenizer.apply_chat_template(text_messages, tokenize=False, add_generation_prompt=True)
60
  inputs = local_tokenizer([text], return_tensors="pt").to(local_model.device)
61
  outputs = local_model.generate(**inputs, max_new_tokens=2048, temperature=0.6, do_sample=True)
62
  return local_tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=True)
63
 
64
  # --- FUNÇÃO 2: GROQ ---
65
  def run_groq(messages, model_id):
66
- # Groq (Llama) não vê imagem
67
- last_content = messages[-1]['content']
68
- if isinstance(last_content, list) or (isinstance(last_content, str) and os.path.exists(last_content)):
69
- return "⚠️ Modelos Llama (Groq) não veem imagens. Use Pixtral ou Gemini."
70
-
71
- if not groq_client: return "❌ Erro: Configure GROQ_API_KEY."
72
-
73
- # Converte formato Gradio novo (list of dicts) para OpenAI simples
74
- clean_messages = []
75
  for m in messages:
76
- content = m['content']
77
- if isinstance(content, list): # Remove imagem se tiver sobrado no histórico
78
- content = next((item['text'] for item in content if item['type'] == 'text'), "")
79
- clean_messages.append({"role": m['role'], "content": content})
80
 
 
81
  try:
82
  completion = groq_client.chat.completions.create(
83
- model=model_id, messages=clean_messages, temperature=0.7, max_tokens=4096
84
  )
85
  return completion.choices[0].message.content
86
  except Exception as e: return f"❌ Groq Error: {e}"
87
 
88
- # --- FUNÇÃO 3: MISTRAL (Com Pixtral) ---
89
  def run_mistral(messages, model_id):
90
  if not mistral_client: return "❌ Erro: Configure MISTRAL_API_KEY."
91
 
 
92
  mistral_msgs = []
93
  for msg in messages:
94
  role = msg['role']
95
  content = msg['content']
96
 
97
  if isinstance(content, str):
98
- # Se for caminho de arquivo (Gradio faz isso as vezes)
99
- if os.path.exists(content):
100
- base64_img = encode_image(content)
101
- mistral_msgs.append({"role": role, "content": [{"type": "image_url", "image_url": f"data:image/jpeg;base64,{base64_img}"}]})
102
- else:
103
- mistral_msgs.append({"role": role, "content": content})
104
- elif isinstance(content, list):
105
  new_content = []
106
  for item in content:
107
  if item['type'] == 'text':
108
  new_content.append({"type": "text", "text": item['text']})
109
- elif item['type'] == 'image': # Gradio 5 usa 'image' ou caminho
 
110
  image_path = item['image']['path'] if isinstance(item['image'], dict) else item['image']
111
  base64_img = encode_image(image_path)
112
  new_content.append({"type": "image_url", "image_url": f"data:image/jpeg;base64,{base64_img}"})
@@ -124,8 +100,8 @@ def run_gemini(messages, model_id):
124
  model = genai.GenerativeModel(model_id)
125
  chat_history = []
126
 
127
- # Converte histórico para formato Gemini
128
- for msg in messages[:-1]: # Tudo menos a última (que é o prompt atual)
129
  role = "user" if msg['role'] == "user" else "model"
130
  parts = []
131
  content = msg['content']
@@ -139,17 +115,13 @@ def run_gemini(messages, model_id):
139
  parts.append(Image.open(path))
140
  chat_history.append({"role": role, "parts": parts})
141
 
142
- # Processa a mensagem atual
143
- last_msg = messages[-1]
144
  current_parts = []
145
- content = last_msg['content']
146
- if isinstance(content, str):
147
- if os.path.exists(content): # É um upload direto sem texto
148
- current_parts.append(Image.open(content))
149
- else:
150
- current_parts.append(content)
151
- elif isinstance(content, list):
152
- for item in content:
153
  if item['type'] == 'text': current_parts.append(item['text'])
154
  elif item['type'] == 'image':
155
  path = item['image']['path'] if isinstance(item['image'], dict) else item['image']
@@ -162,18 +134,19 @@ def run_gemini(messages, model_id):
162
 
163
  # --- ROTEADOR ---
164
  def router(message, history, model_selector):
165
- # No Gradio 5, 'message' já vem formatado, e 'history' também.
166
- # Precisamos combinar para passar pro backend
 
167
 
168
- # Constrói a lista completa de mensagens
169
  full_history = history + [message]
170
 
 
171
  if "Gemini" in model_selector:
172
- # Extrai ID do Gemini (ex: "✨ Google: Gemini 3" -> "gemini-3.0-pro-preview")
173
- if "Gemini 3" in model_selector: tid = "gemini-3.0-pro-preview" #
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" # Hacker ID real
177
  else: tid = "gemini-1.5-flash"
178
  return run_gemini(full_history, tid)
179
 
@@ -193,21 +166,18 @@ def router(message, history, model_selector):
193
 
194
  return "Modelo desconhecido."
195
 
196
- # --- INTERFACE ---
197
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
198
- gr.Markdown("# 🔀 APIDOST V6: Vision Unleashed")
199
 
200
  with gr.Row():
201
  model_dropdown = gr.Dropdown(
202
  choices=[
203
  "✨ Google: Gemini 3.0 Pro (Experimental)",
204
- "✨ Google: Gemini 2.5 Pro",
205
  "✨ Google: Gemini 2.5 Flash",
206
- "✨ Google: Gemini 2.0 Flash (Exp)",
207
  "☁️ Groq: Llama 3.3 70B",
208
  "🇫🇷 Mistral: Pixtral Large (Vision) 🖼️",
209
  "🇫🇷 Mistral: Large 2512 (Dez/25)",
210
- "🇫🇷 Mistral: Magistral Medium (VIP)",
211
  "🔥 Local H200: Qwen 2.5 Coder"
212
  ],
213
  value="🇫🇷 Mistral: Pixtral Large (Vision) 🖼️",
@@ -215,14 +185,13 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
215
  interactive=True
216
  )
217
 
218
- # type="messages" é o padrão novo do Gradio 5 para chat multimodal
219
  chat = gr.ChatInterface(
220
  fn=router,
221
  type="messages",
222
  additional_inputs=[model_dropdown],
223
- multimodal=True
224
  )
225
 
226
  if __name__ == "__main__":
227
- # A SOLUÇÃO DO ERRO 'LOCALHOST' ESTÁ AQUI:
228
- demo.launch(server_name="0.0.0.0", server_port=7860, ssr_mode=False)
 
16
  local_model = None
17
  local_tokenizer = None
18
 
19
+ # 2. NUVEM (Carrega chaves do ambiente)
20
  groq_client = Groq(api_key=os.environ.get("GROQ_API_KEY")) if os.environ.get("GROQ_API_KEY") else None
21
  mistral_client = Mistral(api_key=os.environ.get("MISTRAL_API_KEY")) if os.environ.get("MISTRAL_API_KEY") else None
22
  if os.environ.get("GEMINI_API_KEY"):
23
  genai.configure(api_key=os.environ.get("GEMINI_API_KEY"))
24
 
25
+ # --- HELPER: Imagem para Base64 ---
26
  def encode_image(image_path):
27
  with open(image_path, "rb") as image_file:
28
  return base64.b64encode(image_file.read()).decode('utf-8')
 
30
  # --- FUNÇÃO 1: LOCAL H200 ---
31
  @spaces.GPU(duration=60)
32
  def run_local_h200(messages):
33
+ # Qwen local não suporta imagem fácil via API, bloqueia se tiver
34
+ for m in messages:
35
+ if isinstance(m['content'], list):
36
+ return "⚠️ O modelo Qwen Local H200 suporta apenas texto. Use Gemini ou Pixtral para imagens."
37
 
38
  global local_model, local_tokenizer
39
  if local_model is None:
 
43
  LOCAL_MODEL_ID, torch_dtype=torch.bfloat16, device_map="cuda"
44
  )
45
 
46
+ # Processa histórico simples para texto
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
  # --- FUNÇÃO 2: GROQ ---
53
  def run_groq(messages, model_id):
54
+ # Groq não vê imagem
 
 
 
 
 
 
 
 
55
  for m in messages:
56
+ if isinstance(m['content'], list):
57
+ return "⚠️ Modelos Groq não veem imagens. Use Pixtral ou Gemini."
 
 
58
 
59
+ if not groq_client: return "❌ Erro: Configure GROQ_API_KEY."
60
  try:
61
  completion = groq_client.chat.completions.create(
62
+ model=model_id, messages=messages, temperature=0.7, max_tokens=4096
63
  )
64
  return completion.choices[0].message.content
65
  except Exception as e: return f"❌ Groq Error: {e}"
66
 
67
+ # --- FUNÇÃO 3: MISTRAL (Pixtral) ---
68
  def run_mistral(messages, model_id):
69
  if not mistral_client: return "❌ Erro: Configure MISTRAL_API_KEY."
70
 
71
+ # Formata mensagens para Mistral (texto ou imagem url)
72
  mistral_msgs = []
73
  for msg in messages:
74
  role = msg['role']
75
  content = msg['content']
76
 
77
  if isinstance(content, str):
78
+ mistral_msgs.append({"role": role, "content": content})
79
+ elif isinstance(content, list): # Multimodal
 
 
 
 
 
80
  new_content = []
81
  for item in content:
82
  if item['type'] == 'text':
83
  new_content.append({"type": "text", "text": item['text']})
84
+ elif item['type'] == 'image':
85
+ # Gradio 5 passa o path da imagem
86
  image_path = item['image']['path'] if isinstance(item['image'], dict) else item['image']
87
  base64_img = encode_image(image_path)
88
  new_content.append({"type": "image_url", "image_url": f"data:image/jpeg;base64,{base64_img}"})
 
100
  model = genai.GenerativeModel(model_id)
101
  chat_history = []
102
 
103
+ # Histórico (Gradio 5 Messages -> Gemini Parts)
104
+ for msg in messages[:-1]:
105
  role = "user" if msg['role'] == "user" else "model"
106
  parts = []
107
  content = msg['content']
 
115
  parts.append(Image.open(path))
116
  chat_history.append({"role": role, "parts": parts})
117
 
118
+ # Mensagem Atual (Última)
119
+ current_content = messages[-1]['content']
120
  current_parts = []
121
+ if isinstance(current_content, str):
122
+ current_parts.append(current_content)
123
+ elif isinstance(current_content, list):
124
+ for item in current_content:
 
 
 
 
125
  if item['type'] == 'text': current_parts.append(item['text'])
126
  elif item['type'] == 'image':
127
  path = item['image']['path'] if isinstance(item['image'], dict) else item['image']
 
134
 
135
  # --- ROTEADOR ---
136
  def router(message, history, model_selector):
137
+ # No Gradio 5 com type="messages", history já vem atualizado e message é o payload novo
138
+ # Mas para o chat funcionar fluido, vamos reconstruir o array completo
139
+ # history é lista de dicts [{'role':'user', ...}, {'role':'assistant', ...}]
140
 
141
+ # 1. Constrói o histórico completo incluindo a mensagem atual
142
  full_history = history + [message]
143
 
144
+ # 2. Roteamento
145
  if "Gemini" in model_selector:
146
+ if "3.0" in model_selector: tid = "gemini-3.0-pro-preview" # Aposta no beta
147
+ elif "2.5 Pro" in model_selector: tid = "gemini-2.5-pro"
148
+ elif "2.5 Flash" in model_selector: tid = "gemini-2.5-flash"
149
+ elif "2.0 Flash" in model_selector: tid = "gemini-2.0-flash-exp"
 
150
  else: tid = "gemini-1.5-flash"
151
  return run_gemini(full_history, tid)
152
 
 
166
 
167
  return "Modelo desconhecido."
168
 
169
+ # --- INTERFACE GRADIO 5 ---
170
+ with gr.Blocks(fill_height=True) as demo:
171
+ gr.Markdown("# 🔀 APIDOST V7: The Vision Update")
172
 
173
  with gr.Row():
174
  model_dropdown = gr.Dropdown(
175
  choices=[
176
  "✨ Google: Gemini 3.0 Pro (Experimental)",
 
177
  "✨ Google: Gemini 2.5 Flash",
 
178
  "☁️ Groq: Llama 3.3 70B",
179
  "🇫🇷 Mistral: Pixtral Large (Vision) 🖼️",
180
  "🇫🇷 Mistral: Large 2512 (Dez/25)",
 
181
  "🔥 Local H200: Qwen 2.5 Coder"
182
  ],
183
  value="🇫🇷 Mistral: Pixtral Large (Vision) 🖼️",
 
185
  interactive=True
186
  )
187
 
188
+ # type="messages" é vital para o Gradio 5 entender o formato novo
189
  chat = gr.ChatInterface(
190
  fn=router,
191
  type="messages",
192
  additional_inputs=[model_dropdown],
193
+ multimodal=True,
194
  )
195
 
196
  if __name__ == "__main__":
197
+ demo.launch()