caarleexx commited on
Commit
9374a4b
·
verified ·
1 Parent(s): 2663ee4

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile.txt +19 -0
  2. entrypoint.sh +359 -0
  3. git_manager (6).py +152 -0
  4. requirements.txt +16 -0
Dockerfile.txt ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ RUN apt-get update && apt-get install -y git
4
+
5
+ RUN useradd -m -u 1000 user
6
+ USER user
7
+ ENV HOME=/home/user \
8
+ PATH=/home/user/.local/bin:$PATH
9
+ WORKDIR $HOME/app
10
+
11
+ COPY --chown=user requirements.txt requirements.txt
12
+ RUN pip install --no-cache-dir -r requirements.txt
13
+
14
+ COPY --chown=user . .
15
+
16
+ RUN chmod +x entrypoint.sh
17
+ EXPOSE 7860
18
+
19
+ CMD ["./entrypoint.sh"]
entrypoint.sh ADDED
@@ -0,0 +1,359 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ echo "╔════════════════════════════════════════════════════════════════════╗"
6
+ echo "║ ParaAi v5.0 - Sistema Híbrido (Flex/Batch/Gemini/OpenRouter) ║"
7
+ echo "╚════════════════════════════════════════════════════════════════════╝"
8
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🚀 Iniciando ParaAi v5.0..."
9
+ echo ""
10
+
11
+ # ============================================================================
12
+ # VALIDAÇÃO DE AMBIENTE
13
+ # ============================================================================
14
+
15
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🔍 Validando ambiente..."
16
+
17
+ # GIT_TOKEN obrigatório
18
+ if [[ -z "${GIT_TOKEN}" ]]; then
19
+ echo "❌ GIT_TOKEN não definido"
20
+ exit 1
21
+ fi
22
+
23
+ # PROCESSING_MODE (padrão: flex)
24
+ export PROCESSING_MODE="${PROCESSING_MODE:-flex}"
25
+
26
+ # Valida modo
27
+ if [[ ! "$PROCESSING_MODE" =~ ^(flex|batch|gemini|openrouter)$ ]]; then
28
+ echo "❌ PROCESSING_MODE inválido: $PROCESSING_MODE"
29
+ echo " Valores aceitos: flex, batch, gemini, openrouter"
30
+ exit 1
31
+ fi
32
+
33
+ # Valida API Keys por modo
34
+ if [[ "$PROCESSING_MODE" == "gemini" ]]; then
35
+ if [[ -z "${GEMINI_API_KEY}" ]]; then
36
+ echo "❌ GEMINI_API_KEY não definida (modo: gemini)"
37
+ exit 1
38
+ fi
39
+
40
+ elif [[ "$PROCESSING_MODE" == "flex" ]] || [[ "$PROCESSING_MODE" == "batch" ]]; then
41
+ if [[ -z "${GROQ_API_KEY}" ]]; then
42
+ echo "❌ GROQ_API_KEY não definida (modo: $PROCESSING_MODE)"
43
+ exit 1
44
+ fi
45
+
46
+ elif [[ "$PROCESSING_MODE" == "openrouter" ]]; then
47
+ if [[ -z "${OPENROUTER_API_KEY}" ]]; then
48
+ echo "❌ OPENROUTER_API_KEY não definida (modo: openrouter)"
49
+ exit 1
50
+ fi
51
+ fi
52
+
53
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✅ Validações concluídas"
54
+ echo ""
55
+
56
+ # ============================================================================
57
+ # CONFIGURAÇÃO GIT
58
+ # ============================================================================
59
+
60
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🔧 Configurando Git..."
61
+
62
+ git config --global user.email "paraai-worker@bot.com"
63
+ git config --global user.name "ParaAi Worker v5.0"
64
+ git config --global pull.rebase true
65
+
66
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✅ Git configurado"
67
+
68
+ # ============================================================================
69
+ # CRIAÇÃO DE DIRETÓRIOS
70
+ # ============================================================================
71
+
72
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 📁 Criando diretórios..."
73
+
74
+ mkdir -p trabalho/dados_llm
75
+ mkdir -p trabalho/batches_temp
76
+ mkdir -p repo_git_temp
77
+
78
+ # Garante permissões
79
+ chmod -R 755 trabalho/ 2>/dev/null || true
80
+ chmod -R 755 repo_git_temp/ 2>/dev/null || true
81
+
82
+ # Limpa repo_git_temp se não for repo válido
83
+ if [ -d "repo_git_temp" ] && [ "$(ls -A repo_git_temp)" ]; then
84
+ if [ ! -d "repo_git_temp/.git" ]; then
85
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ⚠️ repo_git_temp não é repo válido, limpando..."
86
+ rm -rf repo_git_temp/* 2>/dev/null || true
87
+ rm -rf repo_git_temp/.* 2>/dev/null || true
88
+ fi
89
+ fi
90
+
91
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✅ Diretórios criados"
92
+ echo ""
93
+
94
+ # ============================================================================
95
+ # INFORMAÇÕES DA CONFIGURAÇÃO
96
+ # ============================================================================
97
+
98
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
99
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 📊 CONFIGURAÇÃO DO SISTEMA"
100
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
101
+
102
+ # Repositório
103
+ export GITHUB_REPO="${GITHUB_REPO:-carlex22/ParaAi}"
104
+ export GITHUB_BRANCH="${GITHUB_BRANCH:-main}"
105
+
106
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 📦 Repositório: $GITHUB_REPO"
107
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🌿 Branch: $GITHUB_BRANCH"
108
+
109
+ # Modo de processamento
110
+ echo ""
111
+
112
+ if [[ "$PROCESSING_MODE" == "flex" ]]; then
113
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ⚡ MODO: FLEX Processing"
114
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Tipo: Síncrono"
115
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Rate Limit: 500 req/min (10x on-demand)"
116
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Preço: Pay-as-you-go"
117
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Workers: ${NUM_WORKERS:-30}"
118
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Ideal para: Produção rápida"
119
+
120
+ elif [[ "$PROCESSING_MODE" == "batch" ]]; then
121
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 📦 MODO: BATCH API"
122
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Tipo: Assíncrono"
123
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Rate Limit: Sem limite"
124
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Preço: 50% desconto 💰"
125
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Completion Window: ${BATCH_COMPLETION_WINDOW:-24h}"
126
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Poll Interval: ${BATCH_POLL_INTERVAL:-30}s"
127
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Ideal para: Volume grande (>100k reqs)"
128
+
129
+ elif [[ "$PROCESSING_MODE" == "gemini" ]]; then
130
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🤖 MODO: GEMINI"
131
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Tipo: Síncrono"
132
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Rate Limit: 15 req/min"
133
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Preço: Grátis (free tier)"
134
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Workers: ${NUM_WORKERS:-10}"
135
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • 🔥 Resiliência: RetryDelay automático"
136
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Ideal para: Teste/desenvolvimento"
137
+
138
+ elif [[ "$PROCESSING_MODE" == "openrouter" ]]; then
139
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🌐 MODO: OPENROUTER"
140
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Tipo: Síncrono"
141
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Rate Limit: 200 req/min (adaptativo)"
142
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Preço: Varia por modelo"
143
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Workers: ${NUM_WORKERS:-20}"
144
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Modelos: 200+ disponíveis"
145
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Ideal para: Qualidade e variedade"
146
+ fi
147
+
148
+ # Modelos
149
+ echo ""
150
+
151
+ if [[ "$PROCESSING_MODE" == "gemini" ]]; then
152
+ export GEMINI_MODEL="${GEMINI_MODEL:-gemini-2.0-flash-exp}"
153
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🎯 Modelo: $GEMINI_MODEL"
154
+
155
+ elif [[ "$PROCESSING_MODE" == "openrouter" ]]; then
156
+ export OPENROUTER_MODEL="${OPENROUTER_MODEL:-openai/gpt-4o-mini}"
157
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🎯 Modelo: $OPENROUTER_MODEL"
158
+ export OPENROUTER_SITE_URL="${OPENROUTER_SITE_URL:-https://github.com/caarleexx/ParaAi}"
159
+ export OPENROUTER_SITE_NAME="${OPENROUTER_SITE_NAME:-ParaAi}"
160
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Site: $OPENROUTER_SITE_NAME"
161
+
162
+ else
163
+ export GROQ_MODEL="${GROQ_MODEL:-llama-3.3-70b-versatile}"
164
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🎯 Modelo: $GROQ_MODEL"
165
+ fi
166
+
167
+ # Chunks
168
+ echo ""
169
+ export REGISTROS_POR_CHUNK="${REGISTROS_POR_CHUNK:-100}"
170
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 📦 Registros por chunk: $REGISTROS_POR_CHUNK"
171
+
172
+ # Retry (apenas para modos síncronos)
173
+ if [[ "$PROCESSING_MODE" != "batch" ]]; then
174
+ export MAX_RETRIES="${MAX_RETRIES:-500000}"
175
+ export REQUEST_TIMEOUT="${REQUEST_TIMEOUT:-30000}"
176
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🔄 Max retries: $MAX_RETRIES"
177
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ⏱️ Timeout: ${REQUEST_TIMEOUT}s"
178
+ fi
179
+
180
+ # Batch específico
181
+ if [[ "$PROCESSING_MODE" == "batch" ]]; then
182
+ export MAX_BATCH_SIZE="${MAX_BATCH_SIZE:-1000}"
183
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 📦 Max batch size: $MAX_BATCH_SIZE registros"
184
+ fi
185
+
186
+ # Estatísticas rastreadas
187
+ echo ""
188
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 📊 Estatísticas rastreadas:"
189
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Registros processados"
190
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Tokens consumidos"
191
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Retries 429 (Rate Limit)"
192
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Retries 498 (Capacity)"
193
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Retries 503 (Service Unavailable) 🆕"
194
+
195
+ if [[ "$PROCESSING_MODE" == "gemini" ]]; then
196
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] • Retries Quota (Gemini RetryDelay) 🆕"
197
+ fi
198
+
199
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
200
+ echo ""
201
+
202
+ # ============================================================================
203
+ # VALIDAÇÃO DE ARQUIVOS
204
+ # ============================================================================
205
+
206
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🔎 Verificando arquivos necessários..."
207
+
208
+ REQUIRED_FILES=(
209
+ "config.py"
210
+ "worker_orquestrador_hybrid.py"
211
+ "llm_worker_hybrid.py"
212
+ "batch_client.py"
213
+ "queue_manager.py"
214
+ "git_manager.py"
215
+ "result_collector.py"
216
+ "app.py"
217
+ )
218
+
219
+ MISSING_FILES=()
220
+ for file in "${REQUIRED_FILES[@]}"; do
221
+ if [ ! -f "$file" ]; then
222
+ MISSING_FILES+=("$file")
223
+ fi
224
+ done
225
+
226
+ if [ ${#MISSING_FILES[@]} -gt 0 ]; then
227
+ echo "❌ Arquivos ausentes:"
228
+ for file in "${MISSING_FILES[@]}"; do
229
+ echo " - $file"
230
+ done
231
+ exit 1
232
+ fi
233
+
234
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✅ Todos os arquivos necessários presentes"
235
+ echo ""
236
+
237
+ # ============================================================================
238
+ # TESTE DE IMPORTAÇÕES
239
+ # ============================================================================
240
+
241
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🧪 Testando importações Python..."
242
+
243
+ python3 << 'PYEOF'
244
+ import sys
245
+
246
+ try:
247
+ from config import PROCESSING_MODE, validate_config
248
+ print(f" ✅ config.py OK")
249
+
250
+ validate_config()
251
+ print(f" ✅ Configuração validada")
252
+ print(f" ℹ️ Modo: {PROCESSING_MODE.value}")
253
+
254
+ except Exception as e:
255
+ print(f" ❌ Erro: {e}")
256
+ sys.exit(1)
257
+ PYEOF
258
+
259
+ if [ $? -ne 0 ]; then
260
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ❌ Falha na validação de configuração"
261
+ exit 1
262
+ fi
263
+
264
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✅ Importações OK"
265
+ echo ""
266
+
267
+ # ============================================================================
268
+ # MENSAGEM DE BOAS-VINDAS v5.0
269
+ # ============================================================================
270
+
271
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
272
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🎉 NOVIDADES v5.0"
273
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
274
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✨ OpenRouter - 200+ modelos LLM"
275
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🔥 Resiliência Gemini com RetryDelay"
276
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🛡️ Retry inteligente por tipo de erro"
277
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 📊 Estatísticas aprimoradas"
278
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
279
+ echo ""
280
+
281
+ # ============================================================================
282
+ # INICIAR WORKER ORQUESTRADOR
283
+ # ============================================================================
284
+
285
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🤖 Iniciando Worker Orquestrador..."
286
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
287
+ echo ""
288
+
289
+ # Função para iniciar worker com retry
290
+ start_worker() {
291
+ local max_retries=3
292
+ local retry=0
293
+
294
+ while [ $retry -lt $max_retries ]; do
295
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🔄 Tentativa $((retry + 1))/$max_retries de iniciar worker..."
296
+
297
+ python3 -u worker_orquestrador_hybrid.py &
298
+ WORKER_PID=$!
299
+
300
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ℹ️ Worker PID: $WORKER_PID"
301
+
302
+ # Aguarda 10s para verificar se iniciou
303
+ sleep 10
304
+
305
+ if kill -0 $WORKER_PID 2>/dev/null; then
306
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✅ Worker iniciado com sucesso!"
307
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 📊 Monitorando PID: $WORKER_PID"
308
+ return 0
309
+ else
310
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ⚠️ Worker falhou ao iniciar"
311
+ retry=$((retry + 1))
312
+
313
+ if [ $retry -lt $max_retries ]; then
314
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🗑️ Limpando estado..."
315
+
316
+ # Reseta repo git
317
+ if [ -d "repo_git_temp/.git" ]; then
318
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🔄 Resetando repo Git..."
319
+ (cd repo_git_temp && git reset --hard HEAD 2>/dev/null) || true
320
+ else
321
+ rm -rf repo_git_temp/* 2>/dev/null || true
322
+ rm -rf repo_git_temp/.* 2>/dev/null || true
323
+ fi
324
+
325
+ sleep 3
326
+ fi
327
+ fi
328
+ done
329
+
330
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ❌ Worker não iniciou após $max_retries tentativas"
331
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ℹ️ Dashboard continuará disponível (modo read-only)"
332
+ return 1
333
+ }
334
+
335
+ start_worker
336
+ sleep 3
337
+ echo ""
338
+
339
+ # ============================================================================
340
+ # INICIAR DASHBOARD
341
+ # ============================================================================
342
+
343
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 🌐 Iniciando Dashboard FastAPI..."
344
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
345
+ echo ""
346
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 📊 Dashboard: http://0.0.0.0:7860"
347
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 📈 API Stats: http://0.0.0.0:7860/api/stats"
348
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] 💚 Health Check: http://0.0.0.0:7860/health"
349
+ echo ""
350
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
351
+ echo ""
352
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✅ Sistema v5.0 inicializado!"
353
+ echo ""
354
+
355
+ # ============================================================================
356
+ # EXEC UVICORN
357
+ # ============================================================================
358
+
359
+ exec uvicorn app:app --host 0.0.0.0 --port 7860 --log-level info
git_manager (6).py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ ParaAi - Git Manager (v3.0 - Baseado no TJ-PR v8.7)
4
+ Lógica simplificada e robusta: Reset -> Pull -> Copy -> Push
5
+ """
6
+
7
+ import os
8
+ import shutil
9
+ import logging
10
+ from pathlib import Path
11
+ from threading import Lock
12
+ from git import Repo, GitCommandError
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ class GitManager:
17
+ """
18
+ Gerencia Git usando a estratégia 'Force Sync' do TJ-PR Extractor.
19
+ Encapsula autenticação, clonagem, sincronização e publicação.
20
+ """
21
+
22
+ def __init__(self, token: str, repo_slug: str, branch: str, repo_path: Path, work_path: Path):
23
+ self.token = token
24
+ self.repo_slug = repo_slug
25
+ self.branch = branch
26
+ self.repo_path = Path(repo_path)
27
+ self.work_path = Path(work_path)
28
+ self.lock = Lock()
29
+
30
+ # URL com autenticação embutida
31
+ self.remote_url = f"https://oauth2:{token}@github.com/{repo_slug}.git"
32
+
33
+ logger.info(f"🔧 Git Manager: Repo={repo_slug} | Branch={branch}")
34
+ self.repo = self._setup_repo()
35
+
36
+ def _setup_repo(self) -> Repo:
37
+ """Clona o repositório se não existir, ou abre o existente."""
38
+ try:
39
+ # Se não existe ou está vazio, clona
40
+ if not self.repo_path.exists() or not any(self.repo_path.iterdir()):
41
+ logger.info(f"📥 Clonando {self.repo_slug} para {self.repo_path}...")
42
+ self.repo_path.mkdir(parents=True, exist_ok=True)
43
+ repo = Repo.clone_from(self.remote_url, self.repo_path, branch=self.branch)
44
+ else:
45
+ logger.info(f"📂 Repositório local encontrado em {self.repo_path}. Abrindo...")
46
+ repo = Repo(self.repo_path)
47
+ # Atualiza URL remota caso o token tenha mudado
48
+ repo.remotes.origin.set_url(self.remote_url)
49
+
50
+ # Configurações essenciais para evitar travamentos e conflitos
51
+ with repo.config_writer() as config:
52
+ config.set_value("pull", "rebase", "true")
53
+ config.set_value("user", "email", "paraai-bot@automacao.com")
54
+ config.set_value("user", "name", "ParaAi Worker")
55
+ # Previne travamento pedindo senha no terminal
56
+ config.set_value("core", "askPass", "echo")
57
+ config.set_value("credential", "helper", "")
58
+
59
+ return repo
60
+
61
+ except GitCommandError as e:
62
+ logger.critical(f"❌ Falha crítica na configuração do Git: {e}", exc_info=True)
63
+ # Se falhar setup, tentamos limpar e clonar de novo na próxima chamada, ou levantamos erro
64
+ if self.repo_path.exists():
65
+ shutil.rmtree(self.repo_path, ignore_errors=True)
66
+ raise
67
+
68
+ def sincronizar_com_remoto(self) -> bool:
69
+ """
70
+ Estratégia 'Force Sync': Limpa tudo localmente e puxa do remoto.
71
+ Garante que não haverá conflitos de merge.
72
+ """
73
+ with self.lock:
74
+ try:
75
+ logger.info("🔄 [Git] Resetando e limpando repositório local...")
76
+ self.repo.git.reset("--hard", "HEAD")
77
+ self.repo.git.clean("-fd")
78
+
79
+ logger.info("⬇️ [Git] Executando Pull (Rebase)...")
80
+ self.repo.remotes.origin.pull()
81
+
82
+ logger.info("✅ [Git] Sincronização concluída.")
83
+ return True
84
+ except GitCommandError as e:
85
+ logger.error(f"❌ [Git] Falha ao sincronizar: {e}")
86
+ return False
87
+
88
+ def copiar_arquivos_de_trabalho(self, arquivos_relativos: list) -> bool:
89
+ """Copia arquivos da pasta de trabalho para a pasta do repo."""
90
+ try:
91
+ count = 0
92
+ for arquivo_rel in arquivos_relativos:
93
+ origem = self.work_path / arquivo_rel
94
+ destino = self.repo_path / arquivo_rel
95
+
96
+ if origem.exists():
97
+ destino.parent.mkdir(parents=True, exist_ok=True)
98
+ shutil.copy2(origem, destino)
99
+ count += 1
100
+
101
+ logger.info(f"📋 [Git] {count} arquivos copiados para área de stage.")
102
+ return True
103
+ except Exception as e:
104
+ logger.error(f"❌ [Git] Erro na cópia de arquivos: {e}")
105
+ return False
106
+
107
+ def publicar_alteracoes(self, arquivos_relativos: list, commit_msg: str) -> bool:
108
+ """
109
+ Fluxo completo: Sync -> Copy -> Add -> Commit -> Push
110
+ """
111
+ with self.lock:
112
+ try:
113
+ logger.info("🚀 [Git] Iniciando publicação...")
114
+
115
+ # 1. Sincroniza (Garante que estamos na última versão)
116
+ logger.info("1️⃣ [Git] Sincronizando...")
117
+ self.repo.git.reset("--hard", "HEAD")
118
+ self.repo.git.clean("-fd")
119
+ self.repo.remotes.origin.pull()
120
+
121
+ # 2. Copia arquivos (Do WorkDir para RepoDir)
122
+ logger.info("2️⃣ [Git] Copiando arquivos...")
123
+ if not self.copiar_arquivos_de_trabalho(arquivos_relativos):
124
+ return False
125
+
126
+ # 3. Add
127
+ logger.info(f"3️⃣ [Git] Adicionando arquivos...")
128
+ # Adiciona tudo que mudou (já que limpamos antes, só o que copiamos mudou)
129
+ self.repo.git.add(A=True)
130
+
131
+ # Verifica se tem algo para commitar
132
+ if not self.repo.is_dirty(untracked_files=True):
133
+ logger.warning("⚠️ [Git] Nenhuma alteração detectada para commit.")
134
+ return True
135
+
136
+ # 4. Commit
137
+ logger.info(f"4️⃣ [Git] Commit: {commit_msg}")
138
+ self.repo.index.commit(commit_msg)
139
+
140
+ # 5. Push
141
+ logger.info("5️⃣ [Git] Push...")
142
+ self.repo.remotes.origin.push()
143
+
144
+ logger.info("✅ [Git] SUCESSO! Dados enviados.")
145
+ return True
146
+
147
+ except GitCommandError as e:
148
+ logger.error(f"❌ [Git] Falha no processo Git: {e}", exc_info=True)
149
+ return False
150
+ except Exception as e:
151
+ logger.error(f"❌ [Git] Erro inesperado: {e}", exc_info=True)
152
+ return False
requirements.txt ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ParaAi v2.1.2 - Requirements
2
+ # FastAPI (startup rápido)
3
+ fastapi==0.109.0
4
+ uvicorn[standard]==0.27.0
5
+ python-multipart==0.0.6
6
+
7
+ # Git
8
+ GitPython==3.1.41
9
+
10
+ # LLM APIs
11
+ google-generativeai==0.3.2
12
+ groq==0.4.2
13
+
14
+ # Utilidades
15
+ requests==2.31.0
16
+ python-dotenv==1.0.0