Madras1 commited on
Commit
5e1cc92
·
verified ·
1 Parent(s): 4bd1637

Upload 5 files

Browse files
Files changed (1) hide show
  1. app.py +143 -98
app.py CHANGED
@@ -1,6 +1,6 @@
1
  """
2
- Jade Code IDE - HuggingFace Space Backend v3.0
3
- Modo Agêntico + Multi-Provider (Groq, Cerebras, OpenRouter)
4
  """
5
 
6
  import os
@@ -18,11 +18,11 @@ from fastapi.middleware.cors import CORSMiddleware
18
  from pydantic import BaseModel
19
  import gradio as gr
20
 
21
- from providers import get_provider, list_all_providers, GroqProvider, CerebrasProvider, OpenRouterProvider
22
  from tools import AGENT_TOOLS, ToolExecutor, parse_tool_calls, get_response_content, has_tool_calls
23
 
24
  # ============== App Setup ==============
25
- app = FastAPI(title="Jade Code IDE API", version="3.0")
26
 
27
  app.add_middleware(
28
  CORSMiddleware,
@@ -40,7 +40,7 @@ class ChatRequest(BaseModel):
40
  model: str = "llama-3.3-70b-versatile"
41
  files: Optional[Dict[str, str]] = {} # {filename: content}
42
  current_file: Optional[str] = None
43
- agentic: bool = True # Modo agêntico ativado por padrão
44
  history: Optional[List[Dict]] = []
45
 
46
  class RunRequest(BaseModel):
@@ -54,29 +54,76 @@ class CompleteRequest(BaseModel):
54
  model: str = "llama-3.3-70b-versatile"
55
 
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  # ============== System Prompt ==============
58
- SYSTEM_PROMPT = """Você é CodeJade, um assistente de programação avançado integrado a uma IDE.
59
-
60
- CAPACIDADES:
61
- - Você pode explorar o projeto do usuário usando ferramentas
62
- - Você tem acesso aos arquivos do projeto
63
- - Você pode ajudar a escrever, debugar e explicar código
64
-
65
- FERRAMENTAS DISPONÍVEIS:
66
- 1. list_files - Lista todos os arquivos do projeto
67
- 2. read_file - o conteúdo de um arquivo específico
68
- 3. search_code - Busca texto nos arquivos do projeto
69
-
70
- REGRAS:
71
- - Use as ferramentas quando precisar entender o contexto do projeto
72
- - Seja direto e técnico
73
- - Quando sugerir código, use blocos ```python
74
- - Explique o que está fazendo
75
-
76
- EXEMPLO:
77
- Usuário: "Me explica o que esse projeto faz"
78
- Você: [usa list_files para ver estrutura, depois read_file nos arquivos importantes]
79
- Depois explica baseado no que leu.
80
  """
81
 
82
 
@@ -84,20 +131,17 @@ Depois explica baseado no que leu.
84
 
85
  @app.get("/")
86
  async def root():
87
- return {"status": "ok", "message": "Jade Code IDE API v3.0 - Agentic Mode", "version": "3.0"}
88
 
89
 
90
  @app.get("/providers")
91
  async def get_providers():
92
- """Lista providers disponíveis."""
93
  return {"providers": list_all_providers()}
94
 
95
 
96
  @app.get("/models/{provider}")
97
  async def get_models(provider: str):
98
- """Lista modelos de um provider."""
99
  try:
100
- # Cria provider com key dummy só pra pegar os modelos
101
  prov = get_provider(provider, "dummy")
102
  return {"models": prov.list_models()}
103
  except ValueError as e:
@@ -107,83 +151,86 @@ async def get_models(provider: str):
107
  @app.post("/chat")
108
  async def chat(req: ChatRequest, x_api_key: str = Header(..., alias="X-API-Key")):
109
  """
110
- Chat com modo agêntico.
111
- O LLM pode usar ferramentas para explorar o projeto.
112
  """
113
  if not x_api_key or len(x_api_key) < 10:
114
  raise HTTPException(status_code=401, detail="API Key inválida")
115
 
116
  try:
117
  provider = get_provider(req.provider, x_api_key)
118
- tool_executor = ToolExecutor(req.files)
119
 
120
- # Monta mensagens iniciais
121
- messages = [{"role": "system", "content": SYSTEM_PROMPT}]
122
 
123
- # Adiciona histórico
124
- for msg in (req.history or [])[-10:]:
125
- messages.append(msg)
 
126
 
127
- # Contexto do arquivo atual
128
- context_parts = []
129
- if req.files:
130
- context_parts.append(f"📁 Projeto tem {len(req.files)} arquivo(s): {', '.join(req.files.keys())}")
131
- if req.current_file and req.current_file in (req.files or {}):
132
- content = req.files[req.current_file][:2000]
133
- context_parts.append(f"📄 Arquivo aberto ({req.current_file}):\n```\n{content}\n```")
134
 
135
- user_msg = req.message
136
- if context_parts:
137
- user_msg = "\n".join(context_parts) + f"\n\n💬 Mensagem: {req.message}"
138
 
139
- messages.append({"role": "user", "content": user_msg})
 
140
 
141
- # Loop agêntico
142
- max_iterations = 5
143
  tool_results = []
144
 
145
- for i in range(max_iterations):
146
- # Chama LLM
147
- if req.agentic:
 
148
  response = provider.chat(messages, req.model, tools=AGENT_TOOLS)
149
- else:
150
- response = provider.chat(messages, req.model)
151
-
152
- # Verifica se tem tool calls
153
- if has_tool_calls(response):
154
- tool_calls = parse_tool_calls(response)
155
 
156
- # Adiciona resposta do assistente com tool calls
157
- assistant_msg = response["choices"][0]["message"]
158
- messages.append(assistant_msg)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
 
160
- # Executa cada tool
161
- for tc in tool_calls:
162
- result = tool_executor.execute(tc["name"], tc["args"])
163
- tool_results.append({"tool": tc["name"], "result": result[:500]})
164
-
165
- # Adiciona resultado como mensagem de tool
166
- messages.append({
167
- "role": "tool",
168
- "tool_call_id": tc["id"],
169
- "content": result
170
- })
171
-
172
- # Continua o loop
173
- continue
174
- else:
175
- # Resposta final
176
  content = get_response_content(response)
177
  return {
178
  "response": content,
179
  "tool_calls": tool_results if tool_results else None
180
  }
181
-
182
- # Se chegou aqui, atingiu limite
183
- return {
184
- "response": "⚠️ Limite de iterações atingido. Tente uma pergunta mais específica.",
185
- "tool_calls": tool_results
186
- }
 
 
 
 
 
 
 
 
 
 
187
 
188
  except Exception as e:
189
  traceback.print_exc()
@@ -259,22 +306,20 @@ async def autocomplete(req: CompleteRequest, x_api_key: str = Header(..., alias=
259
  try:
260
  provider = get_provider(req.provider, x_api_key)
261
 
262
- prompt = f"""Você é um autocomplete de código. Sugira completions.
263
 
264
- CÓDIGO ANTES DO CURSOR:
265
  ```{req.language}
266
- {req.prefix[-500:]}
267
  ```
268
 
269
- CÓDIGO DEPOIS DO CURSOR:
270
  ```{req.language}
271
- {req.suffix[:200] if req.suffix else ''}
272
  ```
273
 
274
- Responda APENAS com um JSON array:
275
- [{{"label": "nome", "insertText": "código", "detail": "descrição", "kind": "Function"}}]
276
-
277
- Máximo 5 sugestões. Tipos: Function, Variable, Class, Module, Keyword, Snippet"""
278
 
279
  response = provider.chat(
280
  messages=[
@@ -317,8 +362,8 @@ def gradio_chat(message, provider, model, api_key):
317
 
318
 
319
  with gr.Blocks(title="Jade Code IDE API") as demo:
320
- gr.Markdown("# 🟢 Jade Code IDE - API Backend v3.0")
321
- gr.Markdown("**Modo Agêntico** | Multi-Provider | API Docs: `/docs`")
322
 
323
  with gr.Row():
324
  provider_dd = gr.Dropdown(
@@ -327,7 +372,7 @@ with gr.Blocks(title="Jade Code IDE API") as demo:
327
  label="Provider"
328
  )
329
  model_dd = gr.Dropdown(
330
- choices=["llama-3.3-70b-versatile", "llama-3.1-8b-instant"],
331
  value="llama-3.3-70b-versatile",
332
  label="Model"
333
  )
 
1
  """
2
+ Jade Code IDE - HuggingFace Space Backend v3.1
3
+ Modo Agêntico Melhorado + Context-Aware + Multi-Provider
4
  """
5
 
6
  import os
 
18
  from pydantic import BaseModel
19
  import gradio as gr
20
 
21
+ from providers import get_provider, list_all_providers
22
  from tools import AGENT_TOOLS, ToolExecutor, parse_tool_calls, get_response_content, has_tool_calls
23
 
24
  # ============== App Setup ==============
25
+ app = FastAPI(title="Jade Code IDE API", version="3.1")
26
 
27
  app.add_middleware(
28
  CORSMiddleware,
 
40
  model: str = "llama-3.3-70b-versatile"
41
  files: Optional[Dict[str, str]] = {} # {filename: content}
42
  current_file: Optional[str] = None
43
+ agentic: bool = True
44
  history: Optional[List[Dict]] = []
45
 
46
  class RunRequest(BaseModel):
 
54
  model: str = "llama-3.3-70b-versatile"
55
 
56
 
57
+ # ============== Helper: Build Context ==============
58
+ def build_file_context(files: Dict[str, str], current_file: Optional[str], max_chars: int = 8000) -> str:
59
+ """
60
+ Constrói contexto dos arquivos para o LLM.
61
+ Inclui conteúdo resumido de todos os arquivos.
62
+ """
63
+ if not files:
64
+ return ""
65
+
66
+ context_parts = []
67
+ total_chars = 0
68
+
69
+ # Lista de arquivos
70
+ context_parts.append(f"📁 **PROJETO ({len(files)} arquivos)**")
71
+ context_parts.append("```")
72
+ for name in files.keys():
73
+ is_current = " ← [ABERTO]" if name == current_file else ""
74
+ context_parts.append(f" {name}{is_current}")
75
+ context_parts.append("```\n")
76
+
77
+ # Conteúdo do arquivo atual (prioridade máxima)
78
+ if current_file and current_file in files:
79
+ content = files[current_file]
80
+ if len(content) > 4000:
81
+ content = content[:4000] + f"\n... [truncado, {len(content)} chars total]"
82
+ context_parts.append(f"📄 **ARQUIVO ABERTO: {current_file}**")
83
+ context_parts.append(f"```python\n{content}\n```\n")
84
+ total_chars += len(content)
85
+
86
+ # Outros arquivos (resumido)
87
+ for name, content in files.items():
88
+ if name == current_file:
89
+ continue
90
+ if total_chars > max_chars:
91
+ context_parts.append(f"_... mais {len(files) - len(context_parts) + 5} arquivos não mostrados_")
92
+ break
93
+
94
+ # Mostra preview pequeno
95
+ preview = content[:500] if len(content) > 500 else content
96
+ if len(content) > 500:
97
+ preview += f"\n... [{len(content)} chars total]"
98
+
99
+ context_parts.append(f"📄 **{name}**")
100
+ ext = name.split('.')[-1] if '.' in name else 'text'
101
+ lang = {'py': 'python', 'js': 'javascript', 'md': 'markdown', 'json': 'json'}.get(ext, ext)
102
+ context_parts.append(f"```{lang}\n{preview}\n```\n")
103
+ total_chars += len(preview)
104
+
105
+ return "\n".join(context_parts)
106
+
107
+
108
  # ============== System Prompt ==============
109
+ SYSTEM_PROMPT = """Você é **CodeJade**, um assistente de programação integrado a uma IDE web.
110
+
111
+ ## Suas Capacidades:
112
+ - Você tem acesso COMPLETO ao projeto do usuário (arquivos listados abaixo)
113
+ - Você pode ver o arquivo que está aberto no editor
114
+ - Você ajuda a escrever código, debugar, explicar e melhorar
115
+
116
+ ## Regras:
117
+ 1. LEIA o contexto do projeto antes de responder
118
+ 2. Seja técnico e direto
119
+ 3. Use blocos ```python para código
120
+ 4. Se for modificar código, mostre apenas as partes que mudam
121
+ 5. Considere TODO o projeto, não só o arquivo aberto
122
+
123
+ ## Formato de Resposta:
124
+ - Para explicações: texto normal com formatação markdown
125
+ - Para código: blocos de código com linguagem especificada
126
+ - Para bugs: mostre o problema E a solução
 
 
 
 
127
  """
128
 
129
 
 
131
 
132
  @app.get("/")
133
  async def root():
134
+ return {"status": "ok", "message": "Jade Code IDE API v3.1 - Context-Aware Mode", "version": "3.1"}
135
 
136
 
137
  @app.get("/providers")
138
  async def get_providers():
 
139
  return {"providers": list_all_providers()}
140
 
141
 
142
  @app.get("/models/{provider}")
143
  async def get_models(provider: str):
 
144
  try:
 
145
  prov = get_provider(provider, "dummy")
146
  return {"models": prov.list_models()}
147
  except ValueError as e:
 
151
  @app.post("/chat")
152
  async def chat(req: ChatRequest, x_api_key: str = Header(..., alias="X-API-Key")):
153
  """
154
+ Chat com contexto completo do projeto.
155
+ Funciona com ou sem suporte a tool calls.
156
  """
157
  if not x_api_key or len(x_api_key) < 10:
158
  raise HTTPException(status_code=401, detail="API Key inválida")
159
 
160
  try:
161
  provider = get_provider(req.provider, x_api_key)
 
162
 
163
+ # Constrói contexto dos arquivos
164
+ file_context = build_file_context(req.files, req.current_file)
165
 
166
+ # System prompt com contexto
167
+ system_with_context = SYSTEM_PROMPT
168
+ if file_context:
169
+ system_with_context += f"\n\n---\n\n# Contexto do Projeto\n\n{file_context}"
170
 
171
+ # Monta mensagens
172
+ messages = [{"role": "system", "content": system_with_context}]
 
 
 
 
 
173
 
174
+ # Histórico
175
+ for msg in (req.history or [])[-6:]:
176
+ messages.append(msg)
177
 
178
+ # Mensagem do usuário
179
+ messages.append({"role": "user", "content": req.message})
180
 
181
+ # Tenta com tools primeiro (se agentic)
 
182
  tool_results = []
183
 
184
+ if req.agentic:
185
+ try:
186
+ # Tenta chamada com tools
187
+ tool_executor = ToolExecutor(req.files)
188
  response = provider.chat(messages, req.model, tools=AGENT_TOOLS)
 
 
 
 
 
 
189
 
190
+ # Se tiver tool calls, executa
191
+ if has_tool_calls(response):
192
+ for _ in range(3): # Max 3 iterações
193
+ tool_calls = parse_tool_calls(response)
194
+ if not tool_calls:
195
+ break
196
+
197
+ assistant_msg = response["choices"][0]["message"]
198
+ messages.append(assistant_msg)
199
+
200
+ for tc in tool_calls:
201
+ result = tool_executor.execute(tc["name"], tc["args"])
202
+ tool_results.append({"tool": tc["name"], "result": result[:300]})
203
+ messages.append({
204
+ "role": "tool",
205
+ "tool_call_id": tc["id"],
206
+ "content": result
207
+ })
208
+
209
+ response = provider.chat(messages, req.model, tools=AGENT_TOOLS)
210
+ if not has_tool_calls(response):
211
+ break
212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  content = get_response_content(response)
214
  return {
215
  "response": content,
216
  "tool_calls": tool_results if tool_results else None
217
  }
218
+
219
+ except Exception as tool_error:
220
+ # Fallback: chamada sem tools (modelo não suporta)
221
+ print(f"Tool call failed, falling back: {tool_error}")
222
+ response = provider.chat(messages, req.model)
223
+ content = get_response_content(response)
224
+ return {
225
+ "response": content,
226
+ "tool_calls": None,
227
+ "note": "Modelo não suporta tools, usando contexto direto"
228
+ }
229
+ else:
230
+ # Modo sem tools
231
+ response = provider.chat(messages, req.model)
232
+ content = get_response_content(response)
233
+ return {"response": content}
234
 
235
  except Exception as e:
236
  traceback.print_exc()
 
306
  try:
307
  provider = get_provider(req.provider, x_api_key)
308
 
309
+ prompt = f"""Complete o código. Responda APENAS JSON:
310
 
311
+ ANTES DO CURSOR:
312
  ```{req.language}
313
+ {req.prefix[-400:]}
314
  ```
315
 
316
+ DEPOIS:
317
  ```{req.language}
318
+ {req.suffix[:150] if req.suffix else ''}
319
  ```
320
 
321
+ JSON (max 5):
322
+ [{{"label": "nome", "insertText": "código", "detail": "desc", "kind": "Function"}}]"""
 
 
323
 
324
  response = provider.chat(
325
  messages=[
 
362
 
363
 
364
  with gr.Blocks(title="Jade Code IDE API") as demo:
365
+ gr.Markdown("# 🟢 Jade Code IDE - API Backend v3.1")
366
+ gr.Markdown("**Context-Aware Mode** | Multi-Provider | API Docs: `/docs`")
367
 
368
  with gr.Row():
369
  provider_dd = gr.Dropdown(
 
372
  label="Provider"
373
  )
374
  model_dd = gr.Dropdown(
375
+ choices=["llama-3.3-70b-versatile"],
376
  value="llama-3.3-70b-versatile",
377
  label="Model"
378
  )