GuXSs commited on
Commit
ae13336
·
verified ·
1 Parent(s): 684060d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +78 -46
app.py CHANGED
@@ -5,7 +5,6 @@ import asyncio
5
  import html
6
 
7
  from dataclasses import dataclass
8
- from datetime import datetime
9
  from typing import Any, Optional, Tuple
10
 
11
  import gradio as gr
@@ -44,14 +43,10 @@ def setup_logger() -> logging.Logger:
44
  cfg = Config()
45
  log_level = getattr(logging, cfg.LOG_LEVEL.upper(), logging.INFO)
46
 
47
- # Avoid adding duplicate handlers if called multiple times
48
  logger = logging.getLogger("gemma_saas")
49
  if not logger.handlers:
50
  logger.setLevel(log_level)
51
-
52
- formatter = logging.Formatter(
53
- "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
54
- )
55
 
56
  file_handler = logging.FileHandler("gemma_saas.log")
57
  file_handler.setFormatter(formatter)
@@ -77,25 +72,22 @@ class ModelManager:
77
 
78
  async def initialize(self) -> None:
79
  if not self.config.HF_TOKEN:
80
- logger.error(
81
- "Token do Hugging Face não encontrado. O carregamento do modelo irá falhar."
82
- )
83
  return
84
 
85
  try:
86
  logger.info(f"A carregar o modelo: {self.config.MODEL_NAME}...")
 
87
 
88
  loop = asyncio.get_event_loop()
89
 
90
  def load_pipeline():
91
- # Nota: dependendo da versão do transformers/huggingface-hub,
92
- # o parâmetro pode ser use_auth_token ou token. Ajuste se necessário.
93
  return pipeline(
94
  "text-generation",
95
  model=self.config.MODEL_NAME,
96
- model_kwargs={"torch_dtype": "auto"},
 
97
  device_map="auto",
98
- use_auth_token=self.config.HF_TOKEN,
99
  )
100
 
101
  self.pipeline = await loop.run_in_executor(None, load_pipeline)
@@ -120,15 +112,13 @@ class ModelManager:
120
  messages = [{"role": "user", "content": request.prompt.strip()}]
121
 
122
  def do_generation():
123
- # A pipeline que usava apply_chat_template é específica de alguns tokenizers;
124
- # mantemos o uso mas com fallback simples se não existir.
125
  tokenizer = getattr(self.pipeline, "tokenizer", None)
 
126
  if tokenizer and hasattr(tokenizer, "apply_chat_template"):
127
  prompt_text = tokenizer.apply_chat_template(
128
  messages, tokenize=False, add_generation_prompt=True
129
  )
130
  else:
131
- # Fallback simples
132
  prompt_text = request.prompt.strip()
133
 
134
  outputs = self.pipeline(
@@ -141,7 +131,6 @@ class ModelManager:
141
  )
142
 
143
  generated_text = outputs[0].get("generated_text", "")
144
- # Se usamos prompt_text, retirar a parte inicial que corresponde ao prompt
145
  if generated_text.startswith(prompt_text):
146
  generated_text = generated_text[len(prompt_text) :]
147
 
@@ -178,9 +167,7 @@ class GemmaService:
178
  request = GenerationRequest(prompt=prompt, **kwargs)
179
  success, text, tokens_used = await self.model_manager.generate(request)
180
  if success:
181
- return APIResponse(
182
- success=True, data={"generated_text": text, "tokens_used": tokens_used}
183
- )
184
  else:
185
  return APIResponse(success=False, error=text)
186
  except Exception as e:
@@ -194,7 +181,11 @@ class GradioInterface:
194
  self.service = service
195
 
196
  def create_custom_css(self) -> str:
 
197
  return """
 
 
 
198
  :root {
199
  --dark-bg: #0a0a0a; --panel-bg: #1a1a1a; --border-color: #333;
200
  --text-color: #f0f0f0; --text-light: #a0a0a0; --accent-orange: #FF4500;
@@ -208,18 +199,76 @@ class GradioInterface:
208
  #output_display p { margin-bottom: 1rem; line-height: 1.7; }
209
  #input_area { margin-top: 1rem; }
210
  #api_key_input textarea, #prompt_input textarea { background-color: #2C2C2C !important; border-color: var(--border-color) !important; color: var(--text-color) !important; border-radius: 12px !important; }
211
- #send_button { background: var(--accent-orange); color: white; border: none; border-radius: 12px !important; transition: background-color 0.3s ease; }
212
  #send_button:hover { background-color: var(--accent-orange-hover); }
213
  #generate_button {
214
  background: linear-gradient(135deg, var(--accent-orange), var(--accent-orange-hover)); color: white !important;
215
  font-size: 1.1rem !important; font-weight: bold !important; border: none; border-radius: 12px !important;
216
- padding: 1rem !important; box-shadow: 0 4px 15px rgba(255, 69, 0, 0.4); transition: all 0.3s ease;
217
  }
218
  #generate_button:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(255, 69, 0, 0.6); }
219
  h2, h3 { color: white; border-bottom: 1px solid var(--border-color); padding-bottom: 0.75rem; margin-bottom: 1.5rem; font-weight: 600; }
220
  .code-snippet { background-color: var(--code-bg); color: #abb2bf; padding: 1.5rem; border-radius: 12px; font-family: 'Courier New', monospace; white-space: pre-wrap; word-wrap: break-word; border: 1px solid var(--border-color); }
221
  .code-snippet .keyword { color: #c678dd; } .code-snippet .string { color: #98c379; } .code-snippet .number { color: #d19a66; }
222
  .gr-slider { color: var(--text-light); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  """
224
 
225
  async def create_interface(self) -> gr.Blocks:
@@ -253,32 +302,21 @@ class GradioInterface:
253
  key_button = gr.Button("✨ Gerar Nova Chave", elem_id="generate_button")
254
 
255
  with gr.Accordion("Parâmetros Avançados", open=False):
256
- temp_slider = gr.Slider(
257
- minimum=0.1, maximum=2.0, value=0.7, step=0.1, label="Temperatura"
258
- )
259
- max_tokens_slider = gr.Slider(
260
- minimum=64, maximum=self.service.config.MAX_TOKENS, value=512, step=64, label="Max Tokens"
261
- )
262
- top_k_slider = gr.Slider(
263
- minimum=1, maximum=100, value=50, step=1, label="Top-K"
264
- )
265
- top_p_slider = gr.Slider(
266
- minimum=0.1, maximum=1.0, value=0.95, step=0.05, label="Top-P"
267
- )
268
 
269
  gr.Markdown("### Como Usar a API")
270
- api_example_display = gr.HTML(
271
- "<p style='color: #a0a0a0;'>Clique em 'Gerar Nova Chave' para ver um exemplo de código.</p>"
272
- )
273
 
274
  def handle_key_generation():
275
- # Gera chave e mostra exemplo de payload
276
  key = f"gsk-{secrets.token_urlsafe(24).replace('_', '').replace('-', '')}"
277
  code_html = f"""
278
  <div class="code-snippet">
279
  <div><span class="keyword">import</span> requests</div>
280
  <div>&nbsp;</div>
281
- <div>url = <span class="string">"https://SEU_SPACE.hf.space/run/generate"</span></div>
282
  <div>payload = {{</div>
283
  <div>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">"api_key"</span>: <span class="string">"{key}"</span>,</div>
284
  <div>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">"prompt"</span>: <span class="string">"Escreva um haikai sobre o universo"</span>,</div>
@@ -292,14 +330,12 @@ class GradioInterface:
292
  return key, gr.update(value=code_html)
293
 
294
  async def handle_generation(api_key, prompt, temp, max_tokens, top_k, top_p, btn):
295
- # Validações básicas
296
  if not api_key:
297
  yield (
298
  "<p style='color: #FFCC00;'>Por favor, insira a sua chave de API para começar.</p>",
299
  gr.update(value="➤ Enviar", interactive=True),
300
  )
301
  return
302
-
303
  if not prompt:
304
  yield (
305
  "<p style='color: #FFCC00;'>Por favor, digite um prompt.</p>",
@@ -307,7 +343,6 @@ class GradioInterface:
307
  )
308
  return
309
 
310
- # Indicador de carregamento
311
  yield "<p style='color: #a0a0a0;'>A gerar resposta...</p>", gr.update(value="A gerar...", interactive=False)
312
 
313
  response = await self.service.generate_text(
@@ -325,7 +360,6 @@ class GradioInterface:
325
  else:
326
  yield f"<p style='color: #FF4500;'>{response.error}</p>", gr.update(value="➤ Enviar", interactive=True)
327
 
328
- # Registar handlers
329
  send_button.click(
330
  handle_generation,
331
  inputs=[api_key_input, prompt_input, temp_slider, max_tokens_slider, top_k_slider, top_p_slider, send_button],
@@ -335,10 +369,9 @@ class GradioInterface:
335
 
336
  key_button.click(handle_key_generation, outputs=[api_key_input, api_example_display])
337
 
338
- # Carregar exemplo inicial
339
  app.load(
340
  lambda: gr.update(value="<p style='color: #a0a0a0;'>Clique em 'Gerar Nova Chave' para ver um exemplo de código.</p>"),
341
- [], # sem inputs
342
  [api_example_display],
343
  )
344
 
@@ -354,7 +387,6 @@ async def main():
354
  interface = GradioInterface(service)
355
  app = await interface.create_interface()
356
 
357
- # Ligar a interface Gradio
358
  app.launch(server_name="0.0.0.0", server_port=7860, share=False, debug=False)
359
  except Exception as e:
360
  logger.critical(f"Falha ao iniciar a aplicação: {e}", exc_info=True)
 
5
  import html
6
 
7
  from dataclasses import dataclass
 
8
  from typing import Any, Optional, Tuple
9
 
10
  import gradio as gr
 
43
  cfg = Config()
44
  log_level = getattr(logging, cfg.LOG_LEVEL.upper(), logging.INFO)
45
 
 
46
  logger = logging.getLogger("gemma_saas")
47
  if not logger.handlers:
48
  logger.setLevel(log_level)
49
+ formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
 
 
 
50
 
51
  file_handler = logging.FileHandler("gemma_saas.log")
52
  file_handler.setFormatter(formatter)
 
72
 
73
  async def initialize(self) -> None:
74
  if not self.config.HF_TOKEN:
75
+ logger.error("Token do Hugging Face não encontrado. O carregamento do modelo irá falhar.")
 
 
76
  return
77
 
78
  try:
79
  logger.info(f"A carregar o modelo: {self.config.MODEL_NAME}...")
80
+ os.environ.setdefault("HF_TOKEN", self.config.HF_TOKEN)
81
 
82
  loop = asyncio.get_event_loop()
83
 
84
  def load_pipeline():
 
 
85
  return pipeline(
86
  "text-generation",
87
  model=self.config.MODEL_NAME,
88
+ token=self.config.HF_TOKEN,
89
+ torch_dtype="auto",
90
  device_map="auto",
 
91
  )
92
 
93
  self.pipeline = await loop.run_in_executor(None, load_pipeline)
 
112
  messages = [{"role": "user", "content": request.prompt.strip()}]
113
 
114
  def do_generation():
 
 
115
  tokenizer = getattr(self.pipeline, "tokenizer", None)
116
+
117
  if tokenizer and hasattr(tokenizer, "apply_chat_template"):
118
  prompt_text = tokenizer.apply_chat_template(
119
  messages, tokenize=False, add_generation_prompt=True
120
  )
121
  else:
 
122
  prompt_text = request.prompt.strip()
123
 
124
  outputs = self.pipeline(
 
131
  )
132
 
133
  generated_text = outputs[0].get("generated_text", "")
 
134
  if generated_text.startswith(prompt_text):
135
  generated_text = generated_text[len(prompt_text) :]
136
 
 
167
  request = GenerationRequest(prompt=prompt, **kwargs)
168
  success, text, tokens_used = await self.model_manager.generate(request)
169
  if success:
170
+ return APIResponse(success=True, data={"generated_text": text, "tokens_used": tokens_used})
 
 
171
  else:
172
  return APIResponse(success=False, error=text)
173
  except Exception as e:
 
181
  self.service = service
182
 
183
  def create_custom_css(self) -> str:
184
+ # Importa Material Icons e adiciona ícones via pseudo-elementos nos botões
185
  return """
186
+ /* importar Material Icons */
187
+ @import url('https://fonts.googleapis.com/css2?family=Material+Icons&display=swap');
188
+
189
  :root {
190
  --dark-bg: #0a0a0a; --panel-bg: #1a1a1a; --border-color: #333;
191
  --text-color: #f0f0f0; --text-light: #a0a0a0; --accent-orange: #FF4500;
 
199
  #output_display p { margin-bottom: 1rem; line-height: 1.7; }
200
  #input_area { margin-top: 1rem; }
201
  #api_key_input textarea, #prompt_input textarea { background-color: #2C2C2C !important; border-color: var(--border-color) !important; color: var(--text-color) !important; border-radius: 12px !important; }
202
+ #send_button { background: var(--accent-orange); color: white; border: none; border-radius: 12px !important; transition: background-color 0.3s ease; position: relative; padding-left: 3rem; }
203
  #send_button:hover { background-color: var(--accent-orange-hover); }
204
  #generate_button {
205
  background: linear-gradient(135deg, var(--accent-orange), var(--accent-orange-hover)); color: white !important;
206
  font-size: 1.1rem !important; font-weight: bold !important; border: none; border-radius: 12px !important;
207
+ padding: 1rem 1.25rem !important; box-shadow: 0 4px 15px rgba(255, 69, 0, 0.4); transition: all 0.3s ease; position: relative; padding-left: 3rem;
208
  }
209
  #generate_button:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(255, 69, 0, 0.6); }
210
  h2, h3 { color: white; border-bottom: 1px solid var(--border-color); padding-bottom: 0.75rem; margin-bottom: 1.5rem; font-weight: 600; }
211
  .code-snippet { background-color: var(--code-bg); color: #abb2bf; padding: 1.5rem; border-radius: 12px; font-family: 'Courier New', monospace; white-space: pre-wrap; word-wrap: break-word; border: 1px solid var(--border-color); }
212
  .code-snippet .keyword { color: #c678dd; } .code-snippet .string { color: #98c379; } .code-snippet .number { color: #d19a66; }
213
  .gr-slider { color: var(--text-light); }
214
+
215
+ /* estilo para usar as Material Icons como ligatures */
216
+ .material-icon {
217
+ font-family: 'Material Icons', sans-serif;
218
+ font-weight: normal;
219
+ font-style: normal;
220
+ font-size: 20px;
221
+ line-height: 1;
222
+ letter-spacing: normal;
223
+ text-transform: none;
224
+ display: inline-block;
225
+ white-space: nowrap;
226
+ word-wrap: normal;
227
+ direction: ltr;
228
+ -webkit-font-feature-settings: 'liga';
229
+ -webkit-font-smoothing: antialiased;
230
+ }
231
+
232
+ /* adicionar ícones antes dos botões (usando ligatures) */
233
+ #send_button::before {
234
+ content: "send"; /* ligature do ícone */
235
+ font-family: 'Material Icons', sans-serif;
236
+ position: absolute;
237
+ left: 12px;
238
+ top: 50%;
239
+ transform: translateY(-50%);
240
+ font-size: 18px;
241
+ line-height: 1;
242
+ opacity: 0.95;
243
+ }
244
+
245
+ #generate_button::before {
246
+ content: "auto_awesome";
247
+ font-family: 'Material Icons', sans-serif;
248
+ position: absolute;
249
+ left: 12px;
250
+ top: 50%;
251
+ transform: translateY(-50%);
252
+ font-size: 18px;
253
+ line-height: 1;
254
+ opacity: 0.95;
255
+ }
256
+
257
+ /* ícone para o botão de gerar chave (se usar outro botão, adapte o id) */
258
+ #generate_button[aria-label], #generate_button[title] { /* fallback */
259
+ padding-left: 3rem;
260
+ }
261
+
262
+ /* ícone ao lado do exemplo de código (vpn_key) */
263
+ #right_panel .code-snippet::before {
264
+ content: "vpn_key";
265
+ font-family: 'Material Icons', sans-serif;
266
+ display: inline-block;
267
+ margin-right: 0.5rem;
268
+ vertical-align: middle;
269
+ font-size: 18px;
270
+ opacity: 0.9;
271
+ }
272
  """
273
 
274
  async def create_interface(self) -> gr.Blocks:
 
302
  key_button = gr.Button("✨ Gerar Nova Chave", elem_id="generate_button")
303
 
304
  with gr.Accordion("Parâmetros Avançados", open=False):
305
+ temp_slider = gr.Slider(minimum=0.1, maximum=2.0, value=0.7, step=0.1, label="Temperatura")
306
+ max_tokens_slider = gr.Slider(minimum=64, maximum=self.service.config.MAX_TOKENS, value=512, step=64, label="Max Tokens")
307
+ top_k_slider = gr.Slider(minimum=1, maximum=100, value=50, step=1, label="Top-K")
308
+ top_p_slider = gr.Slider(minimum=0.1, maximum=1.0, value=0.95, step=0.05, label="Top-P")
 
 
 
 
 
 
 
 
309
 
310
  gr.Markdown("### Como Usar a API")
311
+ api_example_display = gr.HTML("<p style='color: #a0a0a0;'>Clique em 'Gerar Nova Chave' para ver um exemplo de código.</p>")
 
 
312
 
313
  def handle_key_generation():
 
314
  key = f"gsk-{secrets.token_urlsafe(24).replace('_', '').replace('-', '')}"
315
  code_html = f"""
316
  <div class="code-snippet">
317
  <div><span class="keyword">import</span> requests</div>
318
  <div>&nbsp;</div>
319
+ <div>url = <span class="string">"https://Teste.hf.space/run/generate"</span></div>
320
  <div>payload = {{</div>
321
  <div>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">"api_key"</span>: <span class="string">"{key}"</span>,</div>
322
  <div>&nbsp;&nbsp;&nbsp;&nbsp;<span class="string">"prompt"</span>: <span class="string">"Escreva um haikai sobre o universo"</span>,</div>
 
330
  return key, gr.update(value=code_html)
331
 
332
  async def handle_generation(api_key, prompt, temp, max_tokens, top_k, top_p, btn):
 
333
  if not api_key:
334
  yield (
335
  "<p style='color: #FFCC00;'>Por favor, insira a sua chave de API para começar.</p>",
336
  gr.update(value="➤ Enviar", interactive=True),
337
  )
338
  return
 
339
  if not prompt:
340
  yield (
341
  "<p style='color: #FFCC00;'>Por favor, digite um prompt.</p>",
 
343
  )
344
  return
345
 
 
346
  yield "<p style='color: #a0a0a0;'>A gerar resposta...</p>", gr.update(value="A gerar...", interactive=False)
347
 
348
  response = await self.service.generate_text(
 
360
  else:
361
  yield f"<p style='color: #FF4500;'>{response.error}</p>", gr.update(value="➤ Enviar", interactive=True)
362
 
 
363
  send_button.click(
364
  handle_generation,
365
  inputs=[api_key_input, prompt_input, temp_slider, max_tokens_slider, top_k_slider, top_p_slider, send_button],
 
369
 
370
  key_button.click(handle_key_generation, outputs=[api_key_input, api_example_display])
371
 
 
372
  app.load(
373
  lambda: gr.update(value="<p style='color: #a0a0a0;'>Clique em 'Gerar Nova Chave' para ver um exemplo de código.</p>"),
374
+ [],
375
  [api_example_display],
376
  )
377
 
 
387
  interface = GradioInterface(service)
388
  app = await interface.create_interface()
389
 
 
390
  app.launch(server_name="0.0.0.0", server_port=7860, share=False, debug=False)
391
  except Exception as e:
392
  logger.critical(f"Falha ao iniciar a aplicação: {e}", exc_info=True)