habulaj commited on
Commit
ad868d1
·
verified ·
1 Parent(s): 47a6536

Update routers/inference.py

Browse files
Files changed (1) hide show
  1. routers/inference.py +57 -119
routers/inference.py CHANGED
@@ -10,7 +10,7 @@ from datetime import datetime
10
  from zoneinfo import ZoneInfo
11
  import locale
12
  import re
13
- import time
14
 
15
  # Configurar logging
16
  logger = logging.getLogger(__name__)
@@ -19,7 +19,7 @@ router = APIRouter()
19
 
20
  class NewsRequest(BaseModel):
21
  content: str
22
- sources_url: str # URL do arquivo fontes.txt
23
 
24
  class NewsResponse(BaseModel):
25
  title: str
@@ -27,6 +27,9 @@ class NewsResponse(BaseModel):
27
  content: str
28
  sources: list[str] # Lista de URLs/links utilizados
29
 
 
 
 
30
  def get_brazilian_date_string():
31
  """
32
  Retorna a data atual formatada em português brasileiro.
@@ -104,120 +107,61 @@ def get_brazilian_date_string():
104
  date_string = now.strftime("%d de %B de %Y")
105
  return date_string
106
 
107
- def download_sources_file(url: str) -> str:
108
  """
109
- Baixa o arquivo fontes.txt da URL fornecida com retry e headers apropriados.
110
  """
111
- max_retries = 3
112
- base_timeout = 45
113
-
114
- # Headers que simulam um navegador real
115
- headers = {
116
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
117
- 'Accept': 'text/plain,text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
118
- 'Accept-Language': 'pt-BR,pt;q=0.9,en;q=0.8',
119
- 'Accept-Encoding': 'gzip, deflate, br',
120
- 'Connection': 'keep-alive',
121
- 'Upgrade-Insecure-Requests': '1',
122
- 'Sec-Fetch-Dest': 'document',
123
- 'Sec-Fetch-Mode': 'navigate',
124
- 'Sec-Fetch-Site': 'none',
125
- 'Cache-Control': 'max-age=0'
126
- }
127
-
128
- for attempt in range(max_retries):
129
- try:
130
- logger.info(f"Tentativa {attempt + 1} de download do arquivo: {url}")
131
-
132
- # Timeout progressivo: 45s, 60s, 90s
133
- timeout = base_timeout + (attempt * 15)
134
-
135
- # Configuração de sessão com retry automático
136
- session = requests.Session()
137
-
138
- # Adapter com retry automático
139
- from requests.adapters import HTTPAdapter
140
- from urllib3.util.retry import Retry
141
-
142
- retry_strategy = Retry(
143
- total=2,
144
- backoff_factor=1,
145
- status_forcelist=[429, 500, 502, 503, 504],
146
- )
147
-
148
- adapter = HTTPAdapter(max_retries=retry_strategy)
149
- session.mount("http://", adapter)
150
- session.mount("https://", adapter)
151
-
152
- # Fazer a requisição
153
- response = session.get(
154
- url,
155
- headers=headers,
156
- timeout=timeout,
157
- allow_redirects=True,
158
- stream=False # Não usar stream para arquivos pequenos
159
  )
160
-
161
- response.raise_for_status()
162
-
163
- content = response.text
164
- logger.info(f"Download bem-sucedido na tentativa {attempt + 1}. Tamanho: {len(content)} caracteres")
165
-
166
- # Validação básica do conteúdo
167
- if len(content.strip()) < 10:
168
- raise ValueError("Conteúdo do arquivo muito pequeno ou vazio")
169
-
170
- # Verifica se é um JSON válido (assumindo que o arquivo contém JSON)
171
- try:
172
- json.loads(content)
173
- logger.info("Arquivo JSON válido confirmado")
174
- except json.JSONDecodeError:
175
- logger.warning("Arquivo não é um JSON válido, mas continuando...")
176
-
177
- return content
178
-
179
- except requests.exceptions.Timeout as e:
180
- logger.warning(f"Timeout na tentativa {attempt + 1}: {e}")
181
- if attempt == max_retries - 1:
182
- raise HTTPException(
183
- status_code=408,
184
- detail=f"Timeout ao baixar arquivo após {max_retries} tentativas. O servidor pode estar sobrecarregado."
185
- )
186
-
187
- except requests.exceptions.ConnectionError as e:
188
- logger.warning(f"Erro de conexão na tentativa {attempt + 1}: {e}")
189
- if attempt == max_retries - 1:
190
- raise HTTPException(
191
- status_code=503,
192
- detail=f"Erro de conexão após {max_retries} tentativas. Verifique se a URL está correta: {url}"
193
- )
194
-
195
- except requests.exceptions.HTTPError as e:
196
- status_code = e.response.status_code if e.response else 500
197
- logger.error(f"Erro HTTP {status_code} na tentativa {attempt + 1}: {e}")
198
-
199
- if status_code == 404:
200
- raise HTTPException(status_code=404, detail="Arquivo não encontrado. Verifique se a URL está correta.")
201
- elif status_code in [500, 502, 503, 504]:
202
- if attempt == max_retries - 1:
203
- raise HTTPException(status_code=status_code, detail=f"Erro do servidor ({status_code}) após {max_retries} tentativas.")
204
  else:
205
- raise HTTPException(status_code=status_code, detail=f"Erro HTTP {status_code}: {str(e)}")
206
-
207
- except ValueError as e:
208
- logger.error(f"Erro de validação na tentativa {attempt + 1}: {e}")
209
- raise HTTPException(status_code=422, detail=f"Conteúdo do arquivo inválido: {str(e)}")
210
-
211
- except Exception as e:
212
- logger.error(f"Erro inesperado na tentativa {attempt + 1}: {e}")
213
- if attempt == max_retries - 1:
214
- raise HTTPException(status_code=500, detail=f"Erro inesperado ao baixar arquivo: {str(e)}")
215
 
216
- # Wait before retry (exponential backoff)
217
- if attempt < max_retries - 1:
218
- wait_time = (attempt + 1) * 2
219
- logger.info(f"Aguardando {wait_time}s antes da próxima tentativa...")
220
- time.sleep(wait_time)
 
 
 
 
 
 
 
 
 
 
 
221
 
222
  def extract_text_from_response(response):
223
  """
@@ -291,10 +235,8 @@ async def rewrite_news(news: NewsRequest):
291
  if not api_key:
292
  raise HTTPException(status_code=500, detail="API key não configurada")
293
 
294
- # Baixar arquivo de fontes com retry melhorado
295
- logger.info(f"Iniciando download do arquivo de fontes: {news.sources_url}")
296
- sources_content = download_sources_file(news.sources_url)
297
- logger.info("Download do arquivo de fontes concluído com sucesso")
298
 
299
  client = genai.Client(api_key=api_key)
300
  model = "gemini-2.5-pro"
@@ -393,7 +335,6 @@ News base: Ed Helms revealed in an interview that he was nervous about his paren
393
  ]
394
 
395
  # Gerar conteúdo
396
- logger.info("Iniciando geração de conteúdo com Gemini...")
397
  response = client.models.generate_content(
398
  model=model,
399
  contents=contents,
@@ -403,8 +344,6 @@ News base: Ed Helms revealed in an interview that he was nervous about his paren
403
  # Extrair texto e fontes
404
  response_text = extract_text_from_response(response)
405
  sources = extract_sources_from_response(response)
406
-
407
- logger.info("Conteúdo gerado com sucesso pelo Gemini")
408
 
409
  # Verificar se o texto está vazio
410
  if not response_text or response_text.strip() == "":
@@ -430,7 +369,6 @@ News base: Ed Helms revealed in an interview that he was nervous about his paren
430
  else:
431
  content = "Conteúdo não encontrado"
432
 
433
- logger.info("Processamento concluído com sucesso")
434
  return NewsResponse(title=title, subhead=subhead, content=content, sources=sources)
435
 
436
  except HTTPException:
 
10
  from zoneinfo import ZoneInfo
11
  import locale
12
  import re
13
+ from pathlib import Path
14
 
15
  # Configurar logging
16
  logger = logging.getLogger(__name__)
 
19
 
20
  class NewsRequest(BaseModel):
21
  content: str
22
+ file_id: str # ID do arquivo temporário ao invés da URL
23
 
24
  class NewsResponse(BaseModel):
25
  title: str
 
27
  content: str
28
  sources: list[str] # Lista de URLs/links utilizados
29
 
30
+ # Referência ao diretório de arquivos temporários (deve ser o mesmo do outro módulo)
31
+ TEMP_DIR = Path("/tmp")
32
+
33
  def get_brazilian_date_string():
34
  """
35
  Retorna a data atual formatada em português brasileiro.
 
107
  date_string = now.strftime("%d de %B de %Y")
108
  return date_string
109
 
110
+ def load_sources_file(file_id: str) -> str:
111
  """
112
+ Carrega o arquivo de fontes pelo ID do arquivo temporário.
113
  """
114
+ try:
115
+ # Constrói o caminho do arquivo
116
+ file_path = TEMP_DIR / f"fontes_{file_id}.txt"
117
+
118
+ # Verifica se o arquivo existe
119
+ if not file_path.exists():
120
+ raise HTTPException(
121
+ status_code=404,
122
+ detail=f"Arquivo temporário não encontrado ou expirado: {file_id}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  )
124
+
125
+ # Lê o conteúdo do arquivo
126
+ with open(file_path, 'r', encoding='utf-8') as f:
127
+ file_content = f.read()
128
+
129
+ # Se for um JSON, extrai os dados; caso contrário, retorna o conteúdo direto
130
+ try:
131
+ data = json.loads(file_content)
132
+ # Se contém 'results', formata os dados para o Gemini
133
+ if 'results' in data and isinstance(data['results'], list):
134
+ formatted_content = ""
135
+ for idx, result in enumerate(data['results'], 1):
136
+ formatted_content += f"\n--- FONTE {idx} ---\n"
137
+ formatted_content += f"Termo: {result.get('term', 'N/A')}\n"
138
+ formatted_content += f"URL: {result.get('url', 'N/A')}\n"
139
+ formatted_content += f"Idade: {result.get('age', 'N/A')}\n"
140
+ formatted_content += f"Conteúdo:\n{result.get('text', 'N/A')}\n"
141
+ formatted_content += "-" * 50 + "\n"
142
+ return formatted_content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  else:
144
+ return file_content
145
+ except json.JSONDecodeError:
146
+ # Se não for JSON válido, retorna o conteúdo como texto
147
+ return file_content
 
 
 
 
 
 
148
 
149
+ except FileNotFoundError:
150
+ raise HTTPException(
151
+ status_code=404,
152
+ detail=f"Arquivo temporário não encontrado: {file_id}"
153
+ )
154
+ except PermissionError:
155
+ raise HTTPException(
156
+ status_code=500,
157
+ detail=f"Erro de permissão ao acessar arquivo: {file_id}"
158
+ )
159
+ except Exception as e:
160
+ logger.error(f"Erro ao carregar arquivo de fontes {file_id}: {e}")
161
+ raise HTTPException(
162
+ status_code=500,
163
+ detail=f"Erro ao carregar arquivo de fontes: {str(e)}"
164
+ )
165
 
166
  def extract_text_from_response(response):
167
  """
 
235
  if not api_key:
236
  raise HTTPException(status_code=500, detail="API key não configurada")
237
 
238
+ # Carregar arquivo de fontes pelo ID
239
+ sources_content = load_sources_file(news.file_id)
 
 
240
 
241
  client = genai.Client(api_key=api_key)
242
  model = "gemini-2.5-pro"
 
335
  ]
336
 
337
  # Gerar conteúdo
 
338
  response = client.models.generate_content(
339
  model=model,
340
  contents=contents,
 
344
  # Extrair texto e fontes
345
  response_text = extract_text_from_response(response)
346
  sources = extract_sources_from_response(response)
 
 
347
 
348
  # Verificar se o texto está vazio
349
  if not response_text or response_text.strip() == "":
 
369
  else:
370
  content = "Conteúdo não encontrado"
371
 
 
372
  return NewsResponse(title=title, subhead=subhead, content=content, sources=sources)
373
 
374
  except HTTPException: