IacodeBACKUP / app.py
Lucasidcloned's picture
Update app.py
f8e7a08 verified
Raw
History Blame Contribute Delete
6.47 kB
from fastapi import FastAPI, HTTPException
from fastapi.responses import FileResponse, JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import requests
import zipfile
import io
import os
from typing import Dict, List
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# MEMÓRIA TEMPORÁRIA DO PROJETO
projeto_atual = {
"url": "",
"arquivos": {}, # {"lib/main.dart": "conteúdo..."}
"estrutura": []
}
# Cliente de IA - MODELO LEVE QUE FUNCIONA EM CPU GRATUITA
from huggingface_hub import InferenceClient
client = InferenceClient()
class LinkGitHub(BaseModel):
url: str
class Mensagem(BaseModel):
mensagem: str
class EditarArquivo(BaseModel):
caminho: str
novo_conteudo: str
def baixar_github(url: str) -> tuple[Dict[str, str], List[str]]:
"""Baixa TODOS os arquivos de um repo GitHub público"""
# Extrai owner/repo
url = url.rstrip('/')
if url.endswith('.git'):
url = url[:-4]
parts = url.replace('https://github.com/', '').split('/')
if len(parts) < 2:
raise ValueError("URL inválida")
owner, repo = parts[0], parts[1]
# Tenta main primeiro, depois master
for branch in ['main', 'master']:
api_url = f"https://api.github.com/repos/{owner}/{repo}/git/trees/{branch}?recursive=1"
resp = requests.get(api_url)
if resp.status_code == 200:
break
else:
raise ValueError("Repositório não encontrado ou privado")
tree = resp.json().get('tree', [])
arquivos = {}
estrutura = []
for item in tree:
path = item['path']
estrutura.append(path)
# Ignora node_modules, .git, build, etc
if any(x in path for x in ['node_modules', '.git/', 'build/', '.gradle', '.idea']):
continue
if item['type'] == 'blob':
# Baixa arquivo
raw_url = f"https://raw.githubusercontent.com/{owner}/{repo}/{branch}/{path}"
try:
file_resp = requests.get(raw_url, timeout=5)
if file_resp.status_code == 200:
# Tenta decodificar como texto
try:
arquivos[path] = file_resp.content.decode('utf-8')
except:
# Binário - ignora
pass
except:
pass
return arquivos, estrutura
@app.post("/carregar-projeto")
async def carregar_projeto(data: LinkGitHub):
"""Carrega projeto do GitHub"""
try:
arquivos, estrutura = baixar_github(data.url)
projeto_atual['url'] = data.url
projeto_atual['arquivos'] = arquivos
projeto_atual['estrutura'] = estrutura
return {
"status": "sucesso",
"total_arquivos": len(arquivos),
"estrutura": estrutura
}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/estrutura")
def get_estrutura():
"""Retorna estrutura de pastas/arquivos"""
return {"estrutura": projeto_atual['estrutura']}
@app.get("/arquivo/{caminho:path}")
def get_arquivo(caminho: str):
"""Pega conteúdo de um arquivo"""
if caminho in projeto_atual['arquivos']:
return {"conteudo": projeto_atual['arquivos'][caminho]}
raise HTTPException(status_code=404, detail="Arquivo não encontrado")
@app.post("/editar-arquivo")
def editar_arquivo(data: EditarArquivo):
"""Salva edição de arquivo"""
projeto_atual['arquivos'][data.caminho] = data.novo_conteudo
return {"status": "salvo"}
@app.post("/chat")
async def chat(data: Mensagem):
"""Chat com IA que conhece o projeto"""
if not projeto_atual['arquivos']:
return {"resposta": "Carregue um projeto primeiro!"}
# Monta contexto compacto com arquivos principais
contexto = f"PROJETO: {projeto_atual['url']}\n\n"
contexto += f"ESTRUTURA:\n{chr(10).join(projeto_atual['estrutura'][:30])}\n\n"
contexto += "ARQUIVOS PRINCIPAIS:\n\n"
# Adiciona apenas trechos dos arquivos mais relevantes
count = 0
for caminho, conteudo in projeto_atual['arquivos'].items():
if caminho.endswith(('.dart', '.yaml', '.json')):
if count < 8:
contexto += f"--- {caminho} ---\n{conteudo[:1500]}\n\n"
count += 1
# Limita tamanho total
if len(contexto) > 12000:
contexto = contexto[:12000]
prompt = f"""{contexto}
PERGUNTA: {data.mensagem}
Responda de forma clara sobre o código Flutter/Dart."""
try:
# Usa modelo pequeno e rápido que REALMENTE funciona em CPU gratuita
resposta = client.text_generation(
prompt,
model="bigcode/starcoder2-3b",
max_new_tokens=600,
temperature=0.5,
)
return {"resposta": resposta}
except Exception as e:
# Fallback para modelo ainda menor
try:
resposta = client.text_generation(
prompt,
model="Salesforce/codegen-350M-mono",
max_new_tokens=400,
)
return {"resposta": resposta}
except:
return {"resposta": f"IA indisponível no momento. Erro: {str(e)}"}
@app.get("/baixar-projeto")
def baixar_projeto():
"""Gera ZIP do projeto modificado"""
if not projeto_atual['arquivos']:
raise HTTPException(status_code=400, detail="Nenhum projeto carregado")
# Cria ZIP em memória
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
for caminho, conteudo in projeto_atual['arquivos'].items():
zip_file.writestr(caminho, conteudo)
zip_buffer.seek(0)
# Salva temporariamente
with open('/tmp/projeto.zip', 'wb') as f:
f.write(zip_buffer.getvalue())
return FileResponse(
'/tmp/projeto.zip',
media_type='application/zip',
filename='projeto_modificado.zip'
)
@app.get("/")
def root():
return {"status": "IA Code Online", "projeto_carregado": bool(projeto_atual['arquivos'])}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)