Madras1 commited on
Commit
ccbf17f
·
verified ·
1 Parent(s): 2a14228

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +46 -13
  2. app.py +289 -0
  3. requirements.txt +8 -0
README.md CHANGED
@@ -1,13 +1,46 @@
1
- ---
2
- title: Jade Code Api
3
- emoji: 🔥
4
- colorFrom: gray
5
- colorTo: yellow
6
- sdk: gradio
7
- sdk_version: 6.2.0
8
- app_file: app.py
9
- pinned: false
10
- license: apache-2.0
11
- ---
12
-
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Jade Code IDE API
3
+ emoji: 🟢
4
+ colorFrom: green
5
+ colorTo: gray
6
+ sdk: gradio
7
+ sdk_version: 4.44.0
8
+ app_file: app.py
9
+ pinned: false
10
+ license: mit
11
+ ---
12
+
13
+ # Jade Code IDE - Backend API
14
+
15
+ API backend para a Jade Code IDE, uma IDE web integrada com LLM.
16
+
17
+ ## Arquitetura BYOK (Bring Your Own Key)
18
+
19
+ - **Segurança**: Cada usuário fornece sua própria API key do Groq
20
+ - A key é enviada via header `X-API-Key` em cada request
21
+ - O backend **nunca armazena** keys - só repassa pro LLM
22
+
23
+ ## Endpoints
24
+
25
+ | Endpoint | Método | Descrição |
26
+ |----------|--------|-----------|
27
+ | `/` | GET | Health check |
28
+ | `/chat` | POST | Chat com o agente |
29
+ | `/run` | POST | Executa código Python |
30
+ | `/complete` | POST | Autocomplete via LLM |
31
+ | `/docs` | GET | Documentação OpenAPI |
32
+ | `/ui` | GET | Interface Gradio |
33
+
34
+ ## Como usar
35
+
36
+ 1. Obtenha uma API key do [Groq](https://console.groq.com/)
37
+ 2. Use a IDE web em: `https://seu-usuario.github.io/jade-coder/`
38
+ 3. Cole sua API key no campo indicado
39
+ 4. Comece a codar!
40
+
41
+ ## Desenvolvimento local
42
+
43
+ ```bash
44
+ pip install -r requirements.txt
45
+ python app.py
46
+ ```
app.py ADDED
@@ -0,0 +1,289 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Jade Code IDE - HuggingFace Space Backend
3
+ BYOK (Bring Your Own Key) Architecture
4
+
5
+ Cada usuário fornece sua própria API key via header.
6
+ O backend nunca armazena keys - só repassa pro LLM.
7
+ """
8
+
9
+ import os
10
+ import sys
11
+ import io
12
+ import json
13
+ import base64
14
+ import traceback
15
+ import re
16
+ from contextlib import redirect_stdout, redirect_stderr
17
+ from typing import Optional
18
+
19
+ from fastapi import FastAPI, HTTPException, Request, Header
20
+ from fastapi.middleware.cors import CORSMiddleware
21
+ from pydantic import BaseModel
22
+ import gradio as gr
23
+
24
+ # ============== App Setup ==============
25
+ app = FastAPI(title="Jade Code IDE API", version="2.0")
26
+
27
+ # CORS - permitir GitHub Pages e localhost para dev
28
+ app.add_middleware(
29
+ CORSMiddleware,
30
+ allow_origins=["*"], # Em produção, restringir para seu domínio
31
+ allow_credentials=True,
32
+ allow_methods=["*"],
33
+ allow_headers=["*"],
34
+ )
35
+
36
+
37
+ # ============== Request Models ==============
38
+ class ChatRequest(BaseModel):
39
+ message: str
40
+ current_file_content: Optional[str] = ""
41
+ history: Optional[list] = []
42
+
43
+ class RunRequest(BaseModel):
44
+ code: str
45
+
46
+ class CompleteRequest(BaseModel):
47
+ prefix: str
48
+ suffix: str = ""
49
+ language: str = "python"
50
+
51
+
52
+ # ============== Helper: Get Groq Client ==============
53
+ def get_groq_client(api_key: str):
54
+ """Cria cliente Groq com a key do usuário (não armazena)."""
55
+ from groq import Groq
56
+ return Groq(api_key=api_key)
57
+
58
+
59
+ # ============== System Prompt ==============
60
+ SYSTEM_PROMPT = """Você é CodeJade, um assistente de programação avançado.
61
+ Seu objetivo é ajudar o usuário a escrever código, corrigir bugs e explicar conceitos.
62
+
63
+ Regras:
64
+ - Seja direto e técnico
65
+ - Use código Python por padrão
66
+ - Explique o que está fazendo
67
+ - Se o usuário compartilhar código, analise e sugira melhorias
68
+ """
69
+
70
+
71
+ # ============== Endpoints ==============
72
+
73
+ @app.get("/")
74
+ async def root():
75
+ """Health check."""
76
+ return {"status": "ok", "message": "Jade Code IDE API v2.0 - BYOK Mode"}
77
+
78
+
79
+ @app.post("/chat")
80
+ async def chat(req: ChatRequest, x_api_key: str = Header(..., alias="X-API-Key")):
81
+ """
82
+ Chat com o agente usando a API key do usuário.
83
+ """
84
+ if not x_api_key or len(x_api_key) < 10:
85
+ raise HTTPException(status_code=401, detail="API Key inválida")
86
+
87
+ try:
88
+ client = get_groq_client(x_api_key)
89
+
90
+ # Monta mensagens
91
+ messages = [{"role": "system", "content": SYSTEM_PROMPT}]
92
+
93
+ # Adiciona histórico se existir
94
+ for msg in (req.history or [])[-10:]: # Últimas 10 mensagens
95
+ messages.append(msg)
96
+
97
+ # Adiciona contexto do arquivo se existir
98
+ user_msg = req.message
99
+ if req.current_file_content:
100
+ user_msg = f"[Código atual do usuário]\n```\n{req.current_file_content[:3000]}\n```\n\n{req.message}"
101
+
102
+ messages.append({"role": "user", "content": user_msg})
103
+
104
+ # Chama o modelo
105
+ response = client.chat.completions.create(
106
+ messages=messages,
107
+ model="llama-3.3-70b-versatile",
108
+ temperature=0.3,
109
+ max_tokens=2000
110
+ )
111
+
112
+ return {"response": response.choices[0].message.content}
113
+
114
+ except Exception as e:
115
+ error_msg = str(e)
116
+ if "authentication" in error_msg.lower() or "api key" in error_msg.lower():
117
+ raise HTTPException(status_code=401, detail="API Key inválida ou expirada")
118
+ raise HTTPException(status_code=500, detail=f"Erro: {error_msg}")
119
+
120
+
121
+ @app.post("/run")
122
+ async def run_code(req: RunRequest):
123
+ """
124
+ Executa código Python (sandbox limitado).
125
+ Não precisa de API key - é só execução local.
126
+ """
127
+ code = req.code
128
+
129
+ # Captura stdout/stderr
130
+ f_out = io.StringIO()
131
+ f_err = io.StringIO()
132
+
133
+ # Variável para capturar matplotlib
134
+ image_base64 = None
135
+
136
+ try:
137
+ # Prepara ambiente
138
+ import matplotlib
139
+ matplotlib.use('Agg')
140
+ import matplotlib.pyplot as plt
141
+
142
+ exec_globals = {
143
+ "__builtins__": __builtins__,
144
+ "__name__": "__main__",
145
+ "plt": plt,
146
+ }
147
+
148
+ # Importa libs comuns
149
+ try:
150
+ import numpy as np
151
+ exec_globals["np"] = np
152
+ exec_globals["numpy"] = np
153
+ except ImportError:
154
+ pass
155
+
156
+ try:
157
+ import pandas as pd
158
+ exec_globals["pd"] = pd
159
+ except ImportError:
160
+ pass
161
+
162
+ # Executa
163
+ with redirect_stdout(f_out), redirect_stderr(f_err):
164
+ exec(code, exec_globals)
165
+
166
+ # Captura figura matplotlib
167
+ if plt.get_fignums():
168
+ buf = io.BytesIO()
169
+ plt.savefig(buf, format='png', dpi=100, bbox_inches='tight',
170
+ facecolor='#09090b', edgecolor='none')
171
+ buf.seek(0)
172
+ image_base64 = base64.b64encode(buf.read()).decode('utf-8')
173
+ plt.close('all')
174
+
175
+ return {
176
+ "output": f_out.getvalue(),
177
+ "error": f_err.getvalue() or None,
178
+ "image": image_base64
179
+ }
180
+
181
+ except Exception as e:
182
+ return {
183
+ "output": f_out.getvalue(),
184
+ "error": f"❌ {type(e).__name__}: {str(e)}",
185
+ "image": None
186
+ }
187
+
188
+
189
+ @app.post("/complete")
190
+ async def autocomplete(req: CompleteRequest, x_api_key: str = Header(..., alias="X-API-Key")):
191
+ """
192
+ Autocomplete via LLM usando a key do usuário.
193
+ """
194
+ if not x_api_key or len(x_api_key) < 10:
195
+ raise HTTPException(status_code=401, detail="API Key inválida")
196
+
197
+ try:
198
+ client = get_groq_client(x_api_key)
199
+
200
+ prompt = f"""Você é um autocomplete de código. Sugira completions para o código abaixo.
201
+
202
+ CÓDIGO ANTES DO CURSOR:
203
+ ```{req.language}
204
+ {req.prefix[-500:]}
205
+ ```
206
+
207
+ CÓDIGO DEPOIS DO CURSOR:
208
+ ```{req.language}
209
+ {req.suffix[:200] if req.suffix else ''}
210
+ ```
211
+
212
+ Responda APENAS com um JSON array:
213
+ [{{"label": "nome", "insertText": "código", "detail": "descrição", "kind": "Function"}}]
214
+
215
+ Máximo 5 sugestões. Tipos: Function, Variable, Class, Module, Keyword, Snippet"""
216
+
217
+ response = client.chat.completions.create(
218
+ messages=[
219
+ {"role": "system", "content": "Responda APENAS JSON válido."},
220
+ {"role": "user", "content": prompt}
221
+ ],
222
+ model="llama-3.3-70b-versatile",
223
+ temperature=0.1,
224
+ max_tokens=500
225
+ )
226
+
227
+ response_text = response.choices[0].message.content
228
+
229
+ # Parse JSON
230
+ match = re.search(r'\[.*\]', response_text, re.DOTALL)
231
+ if match:
232
+ suggestions = json.loads(match.group(0))
233
+ return {"suggestions": suggestions}
234
+
235
+ return {"suggestions": []}
236
+
237
+ except Exception as e:
238
+ return {"suggestions": [], "error": str(e)}
239
+
240
+
241
+ # ============== Gradio Interface (for HF Spaces UI) ==============
242
+ def gradio_chat(message, api_key):
243
+ """Interface Gradio simples para testar."""
244
+ if not api_key:
245
+ return "❌ Coloque sua API Key do Groq"
246
+
247
+ try:
248
+ client = get_groq_client(api_key)
249
+ response = client.chat.completions.create(
250
+ messages=[
251
+ {"role": "system", "content": SYSTEM_PROMPT},
252
+ {"role": "user", "content": message}
253
+ ],
254
+ model="llama-3.3-70b-versatile",
255
+ temperature=0.3
256
+ )
257
+ return response.choices[0].message.content
258
+ except Exception as e:
259
+ return f"❌ Erro: {str(e)}"
260
+
261
+
262
+ # Gradio UI
263
+ with gr.Blocks(title="Jade Code IDE API") as demo:
264
+ gr.Markdown("# 🟢 Jade Code IDE - API Backend")
265
+ gr.Markdown("Esta é a API do Jade Code IDE. Use a IDE web para a experiência completa.")
266
+ gr.Markdown("**API Docs**: `/docs`")
267
+
268
+ with gr.Row():
269
+ api_key_input = gr.Textbox(label="Sua API Key do Groq", type="password")
270
+
271
+ with gr.Row():
272
+ msg_input = gr.Textbox(label="Mensagem de teste", lines=3)
273
+
274
+ with gr.Row():
275
+ send_btn = gr.Button("Testar Chat")
276
+
277
+ output = gr.Textbox(label="Resposta", lines=10)
278
+
279
+ send_btn.click(gradio_chat, inputs=[msg_input, api_key_input], outputs=output)
280
+
281
+
282
+ # Mount Gradio app
283
+ app = gr.mount_gradio_app(app, demo, path="/ui")
284
+
285
+
286
+ # ============== Run ==============
287
+ if __name__ == "__main__":
288
+ import uvicorn
289
+ uvicorn.run(app, host="0.0.0.0", port=7860)
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn[standard]
3
+ gradio>=4.0.0
4
+ groq
5
+ pydantic
6
+ matplotlib
7
+ numpy
8
+ pandas