Madras1 commited on
Commit
e58c12b
·
verified ·
1 Parent(s): 8387dd2

Delete code_jade

Browse files
code_jade/config.json DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "groq_model": "moonshotai/kimi-k2-instruct-0905",
3
- "max_context": 20,
4
- "safe_mode": false,
5
- "work_dir": "./workspace"
6
- }
 
 
 
 
 
 
 
code_jade/core.py DELETED
@@ -1,223 +0,0 @@
1
- import json
2
- import logging
3
- from groq import Groq
4
- from .tools import ToolManager
5
- from .reviewer import CodeReviewer
6
-
7
- class CodeJadeAgent:
8
- def __init__(self, config_path="code_jade/config.json"):
9
- # Carrega Configuração
10
- try:
11
- with open(config_path) as f:
12
- self.cfg = json.load(f)
13
- except FileNotFoundError:
14
- # Fallback se não achar, mas idealmente deve existir
15
- self.cfg = {"groq_model": "moonshotai/kimi-k2-instruct-0905", "safe_mode": True, "work_dir": "./workspace", "max_context": 20}
16
-
17
- self.client = Groq(api_key=self._get_api_key())
18
- self.tools = ToolManager(safe_mode=self.cfg.get("safe_mode", True), work_dir=self.cfg.get("work_dir", "."))
19
- self.reviewer = CodeReviewer(self.cfg)
20
-
21
- # System Prompt focado em Code Assistant
22
- self.system_prompt = """Você é CodeJade, um assistente de programação avançado (estilo Cursor AI).
23
- Seu objetivo é ajudar o usuário a escrever código, corrigir bugs e explorar o projeto.
24
-
25
- FERRAMENTAS DISPONÍVEIS:
26
- Você tem acesso a ferramentas. Para usá-las, você DEVE responder APENAS com um bloco JSON estrito no seguinte formato:
27
- {"tool": "nome_da_ferramenta", "args": {"arg1": "valor1"}}
28
-
29
- As ferramentas são:
30
- 1. execute_shell(command: str) -> Executa comandos bash (ls, pip, git, etc).
31
- 2. read_file(filepath: str) -> Lê o conteúdo de um arquivo.
32
- 3. write_file(filepath: str, content: str) -> Cria ou sobrescreve um arquivo.
33
- 4. list_files(path: str) -> Lista arquivos.
34
- 5. run_python(code: str) -> Executa script Python.
35
-
36
- REGRAS:
37
- - Se precisar de informações, use 'list_files' ou 'read_file'.
38
- - Se precisar rodar algo, use 'execute_shell' ou 'run_python'.
39
- - Se for apenas conversar ou explicar, responda em texto normal (sem JSON).
40
- - Mantenha respostas diretas e técnicas.
41
- - Sempre verifique se o código funciona rodando-o se possível.
42
-
43
- EXEMPLO DE USO:
44
- Usuário: "Crie um hello world em python"
45
- CodeJade: {"tool": "write_file", "args": {"filepath": "hello.py", "content": "print('Hello World')"}}
46
- (Sistema executa e retorna sucesso)
47
- CodeJade: "Arquivo criado. Quer que eu execute?"
48
- """
49
- self.history = [{"role": "system", "content": self.system_prompt}]
50
-
51
- logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
52
-
53
- def _get_api_key(self):
54
- import os
55
- key = os.getenv("GROQ_API_KEY")
56
- if not key:
57
- # Tenta pegar do colab se estiver lá
58
- try:
59
- from google.colab import userdata
60
- key = userdata.get('GROQ_API_KEY')
61
- except:
62
- pass
63
- if not key:
64
- print("⚠️ AVISO: GROQ_API_KEY não encontrada. O agente pode falhar.")
65
- return "dummy_key"
66
- return key
67
-
68
- def _chat(self, messages):
69
- """Chama o modelo Groq."""
70
- try:
71
- completion = self.client.chat.completions.create(
72
- messages=messages,
73
- model=self.cfg["groq_model"],
74
- temperature=0.3, # Baixa temperatura para código preciso
75
- stop=None
76
- )
77
- return completion.choices[0].message.content
78
- except Exception as e:
79
- return f"❌ Erro na API Groq: {e}"
80
-
81
- def process_tool_call(self, response_text):
82
- """Tenta parsear JSON para chamada de ferramenta usando Regex."""
83
- import json
84
- import re
85
-
86
- try:
87
- # Procura pelo padrão JSON: { ... }
88
- # O regex considera chaves aninhadas simples, mas foca no bloco principal
89
- # A opção re.DOTALL permite que o ponto (.) case com novas linhas
90
- match = re.search(r'\{.*\}', response_text, re.DOTALL)
91
- if not match:
92
- return None
93
-
94
- json_str = match.group(0)
95
-
96
- # Tenta carregar o JSON
97
- data = json.loads(json_str)
98
-
99
- if "tool" in data and "args" in data:
100
- return data
101
- return None
102
- except json.JSONDecodeError:
103
- # Fallback: tentar limpar crases de markdown se houver (```json ... ```)
104
- try:
105
- cleaned = re.sub(r'```json|```', '', response_text).strip()
106
- # Busca novamente na string limpa
107
- match = re.search(r'\{.*\}', cleaned, re.DOTALL)
108
- if match:
109
- data = json.loads(match.group(0))
110
- if "tool" in data: return data
111
- except:
112
- pass
113
- return None
114
- except Exception:
115
- return None
116
-
117
- def run_tool(self, tool_data):
118
- name = tool_data["tool"]
119
- args = tool_data["args"]
120
-
121
- print(f"⚙️ Executando ferramenta: {name}...")
122
-
123
- # Intercepta write_file para Review
124
- if name == "write_file":
125
- content = args.get("content")
126
- filepath = args.get("filepath")
127
-
128
- print(f"🕵️‍♂️ Solicitando revisão para '{filepath}'...")
129
- review_result = self.reviewer.review(content, context=f"User asked to create/edit {filepath}")
130
-
131
- if review_result.get("status") == "REJECTED":
132
- feedback = review_result.get("feedback", "Sem detalhes.")
133
- print(f"🛑 REJEITADO pelo Reviewer: {feedback}")
134
- # Retorna erro simulado para o modelo tentar de novo
135
- return f"❌ BLOQUEADO PELO REVIEWER. Motivo: {feedback}. Corrija o código e tente salvar novamente."
136
- else:
137
- print("✅ APROVADO pelo Reviewer.")
138
- # Prossegue para salvar
139
- return self.tools.write_file(filepath, content)
140
-
141
- if name == "execute_shell":
142
- return self.tools.execute_shell(args.get("command"))
143
- elif name == "read_file":
144
- return self.tools.read_file(args.get("filepath"))
145
- elif name == "list_files":
146
- return self.tools.list_files(args.get("path", "."))
147
- elif name == "run_python":
148
- return self.tools.run_python(args.get("code"))
149
- else:
150
- return f"❌ Ferramenta desconhecida: {name}"
151
-
152
- def _manage_memory(self):
153
- """Mantém o histórico limpo para não estourar tokens."""
154
- max_ctx = self.cfg.get("max_context", 20)
155
- if len(self.history) > max_ctx:
156
- # Mantém sempre o System Prompt (índice 0)
157
- # E pega as últimas (max_ctx - 1) mensagens
158
- self.history = [self.history[0]] + self.history[-(max_ctx-1):]
159
-
160
- def chat_loop(self, user_input):
161
- """Ciclo principal de raciocínio (ReAct simplificado)."""
162
-
163
- # 1. Adiciona input do usuário
164
- self.history.append({"role": "user", "content": user_input})
165
-
166
- # Limite de interações no loop para evitar loops infinitos
167
- max_turns = 5
168
- turn = 0
169
-
170
- final_response = ""
171
-
172
- last_tool_call = None
173
- consecutive_tool_failures = 0
174
-
175
- while turn < max_turns:
176
- # 2. Chama o modelo
177
- response = self._chat(self.history)
178
-
179
- # 3. Verifica se é tool call
180
- tool_data = self.process_tool_call(response)
181
-
182
- if tool_data:
183
- # Loop detection
184
- current_call_signature = f"{tool_data['tool']}:{json.dumps(tool_data['args'], sort_keys=True)}"
185
- if last_tool_call == current_call_signature:
186
- consecutive_tool_failures += 1
187
- if consecutive_tool_failures >= 2:
188
- error_msg = f"TOOL_ERROR: Detected repetitive loop with same arguments. Stopping tool execution."
189
- self.history.append({"role": "system", "content": error_msg})
190
- print(f"🛑 Loop detectado: {error_msg}")
191
- # Force model to explain
192
- continue
193
- else:
194
- consecutive_tool_failures = 0
195
-
196
- last_tool_call = current_call_signature
197
-
198
- # É uma ferramenta -> Executa
199
- tool_result = self.run_tool(tool_data)
200
-
201
- # Adiciona a "conversa" da ferramenta no histórico
202
- self.history.append({"role": "assistant", "content": response})
203
- self.history.append({"role": "system", "content": f"TOOL_RESULT ({tool_data['tool']}): {tool_result}"})
204
-
205
- print(f"🔍 Resultado: {str(tool_result)[:200]}...") # Preview
206
- turn += 1
207
- else:
208
- # Resposta final (texto)
209
- final_response = response
210
- self.history.append({"role": "assistant", "content": final_response})
211
- break
212
-
213
- # Se saiu do loop sem resposta final (max_turns reached)
214
- if not final_response and turn >= max_turns:
215
- print("⚠️ Max turns reached without final response. Forcing summary.")
216
- self.history.append({"role": "system", "content": "SYSTEM: Max tool turns reached. Please summarize what was done and what failed."})
217
- final_response = self._chat(self.history)
218
- self.history.append({"role": "assistant", "content": final_response})
219
-
220
- # Gerencia a memória ao fim do ciclo
221
- self._manage_memory()
222
-
223
- return final_response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
code_jade/main.py DELETED
@@ -1,40 +0,0 @@
1
- import sys
2
- import os
3
-
4
- # Adiciona o diretório raiz ao path para importar módulos corretamente
5
- sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
6
-
7
- from code_jade.core import CodeJadeAgent
8
-
9
- def main():
10
- print("==========================================")
11
- print("🚀 CodeJade - AI Developer Agent (v1.0)")
12
- print("==========================================")
13
-
14
- try:
15
- agent = CodeJadeAgent()
16
- print(f"🔧 Modelo: {agent.cfg.get('groq_model')}")
17
- print(f"📂 Work Dir: {agent.cfg.get('work_dir')}")
18
- print("💡 Digite 'sair' para encerrar.\n")
19
-
20
- while True:
21
- user_input = input("\n👨‍💻 Você: ").strip()
22
-
23
- if not user_input:
24
- continue
25
-
26
- if user_input.lower() in ["sair", "exit", "quit"]:
27
- print("👋 Até logo!")
28
- break
29
-
30
- print("🤖 CodeJade pensando...")
31
- response = agent.chat_loop(user_input)
32
- print(f"\n🤖 CodeJade: {response}")
33
-
34
- except KeyboardInterrupt:
35
- print("\n\n👋 Interrompido pelo usuário.")
36
- except Exception as e:
37
- print(f"\n❌ Erro fatal: {e}")
38
-
39
- if __name__ == "__main__":
40
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
code_jade/requirements.txt DELETED
@@ -1 +0,0 @@
1
- groq
 
 
code_jade/reviewer.py DELETED
@@ -1,63 +0,0 @@
1
- import json
2
- from groq import Groq
3
-
4
- class CodeReviewer:
5
- def __init__(self, config):
6
- self.client = Groq(api_key=self._get_api_key())
7
- self.model = config.get("groq_model", "llama3-70b-8192")
8
-
9
- self.system_prompt = """Você é um Code Reviewer Sênior, rigoroso e focado em Segurança e Qualidade.
10
- Sua função é analisar trechos de código e aprovar ou rejeitar baseando-se em:
11
- 1. Bugs lógicos óbvios.
12
- 2. Riscos de Segurança (ex: SQL Injection, exec sem validação, hardcoded credentials).
13
- 3. Boas práticas (variáveis legíveis, tratamento de erro básico).
14
-
15
- FORMATO DE RESPOSTA (JSON OBRIGATÓRIO):
16
- Você deve responder APENAS um JSON no seguinte formato:
17
- {
18
- "status": "APPROVED" | "REJECTED",
19
- "feedback": "Explicação curta do problema (se REJECTED) ou elogio (se APPROVED)."
20
- }
21
-
22
- Se o código for seguro e funcional, aprove. Não seja pedante com estilo (PEP8) a menos que torne o código ilegível.
23
- Se houver perigo real ou erro fatal, rejeite.
24
- """
25
-
26
- def _get_api_key(self):
27
- import os
28
- key = os.getenv("GROQ_API_KEY")
29
- if not key:
30
- try:
31
- from google.colab import userdata
32
- key = userdata.get('GROQ_API_KEY')
33
- except:
34
- pass
35
- return key or "dummy"
36
-
37
- def review(self, code, context=""):
38
- """Analisa o código e retorna status e feedback."""
39
- messages = [
40
- {"role": "system", "content": self.system_prompt},
41
- {"role": "user", "content": f"Contexto: {context}\n\nCódigo para revisão:\n```python\n{code}\n```"}
42
- ]
43
-
44
- try:
45
- chat = self.client.chat.completions.create(
46
- messages=messages,
47
- model=self.model,
48
- temperature=0.1, # Baixa temperatura para análise fria
49
- response_format={"type": "json_object"} # Força JSON mode no Llama 3 se disponível, ou ajuda
50
- )
51
- response_text = chat.choices[0].message.content
52
-
53
- # Tenta parsear
54
- try:
55
- return json.loads(response_text)
56
- except json.JSONDecodeError:
57
- # Fallback simples se não vier JSON limpo
58
- if "APPROVED" in response_text.upper():
59
- return {"status": "APPROVED", "feedback": "Parser failed but looks approved."}
60
- return {"status": "REJECTED", "feedback": f"Parser failed. Raw response: {response_text}"}
61
-
62
- except Exception as e:
63
- return {"status": "ERROR", "feedback": str(e)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
code_jade/tools.py DELETED
@@ -1,127 +0,0 @@
1
- import os
2
- import subprocess
3
- import sys
4
- import io
5
- from contextlib import redirect_stdout, redirect_stderr
6
-
7
- class ToolManager:
8
- def __init__(self, safe_mode=True, work_dir="."):
9
- self.safe_mode = safe_mode
10
- self.work_dir = work_dir
11
- if not os.path.exists(self.work_dir):
12
- os.makedirs(self.work_dir, exist_ok=True)
13
-
14
- def _confirm(self, action, content):
15
- if not self.safe_mode:
16
- return True
17
-
18
- # Check if running in an interactive terminal
19
- if sys.stdin.isatty():
20
- print(f"\n⚠️ [SOLICITAÇÃO DE EXECUÇÃO]")
21
- print(f"Ação: {action}")
22
- print(f"Detalhes: {content}")
23
- try:
24
- resp = input(">> Permitir? (s/n): ").strip().lower()
25
- return resp == "s"
26
- except EOFError:
27
- return False
28
- else:
29
- # Non-interactive mode (e.g. server): Auto-deny if safe_mode is True
30
- # Ideally, this should be configurable, but safe_mode=True implies user MUST confirm.
31
- # If user can't confirm, we can't proceed safely.
32
- print(f"⚠️ Blocked action '{action}' in safe_mode (non-interactive environment).")
33
- return False
34
-
35
- def execute_shell(self, command):
36
- """Executa comandos no terminal."""
37
- if not self._confirm("SHELL", command):
38
- return "❌ Ação negada pelo usuário."
39
-
40
- try:
41
- # Executa no diretório de trabalho
42
- result = subprocess.run(
43
- command,
44
- shell=True,
45
- cwd=self.work_dir,
46
- text=True,
47
- capture_output=True,
48
- timeout=30
49
- )
50
- output = result.stdout
51
- if result.stderr:
52
- output += f"\n[STDERR]\n{result.stderr}"
53
- return output.strip() or "[Comando executado sem saída]"
54
- except Exception as e:
55
- return f"❌ Erro de execução: {str(e)}"
56
-
57
- def write_file(self, filepath, content):
58
- """Cria ou sobrescreve um arquivo."""
59
- full_path = os.path.join(self.work_dir, filepath)
60
-
61
- if not self._confirm("WRITE_FILE", f"{filepath} ({len(content)} chars)"):
62
- return "❌ Ação negada pelo usuário."
63
-
64
- try:
65
- # Cria diretórios pai se não existirem
66
- os.makedirs(os.path.dirname(full_path), exist_ok=True)
67
- with open(full_path, "w", encoding="utf-8") as f:
68
- f.write(content)
69
- return f"✅ Arquivo '{filepath}' salvo com sucesso."
70
- except Exception as e:
71
- return f"❌ Erro ao salvar arquivo: {str(e)}"
72
-
73
- def read_file(self, filepath):
74
- """Lê o conteúdo de um arquivo."""
75
- full_path = os.path.join(self.work_dir, filepath)
76
- try:
77
- with open(full_path, "r", encoding="utf-8") as f:
78
- return f.read()
79
- except FileNotFoundError:
80
- return f"❌ Arquivo não encontrado: {filepath}"
81
- except Exception as e:
82
- return f"❌ Erro ao ler arquivo: {str(e)}"
83
-
84
- def list_files(self, path="."):
85
- """Lista arquivos no diretório."""
86
- # Se path for '.', usa o diretório atual (que já é o work_dir no execute_shell)
87
- # Se path for algo mais, concatena
88
- try:
89
- cmd = f"ls -R -F {path}"
90
- return self.execute_shell(cmd)
91
- except Exception as e:
92
- return f"❌ Erro ao listar arquivos: {str(e)}"
93
-
94
- def run_python(self, code):
95
- """Executa código Python e captura a saída."""
96
- if not self._confirm("PYTHON_EXEC", code[:200] + "..."):
97
- return "❌ Ação negada pelo usuário."
98
-
99
- # Captura stdout e stderr
100
- f_out = io.StringIO()
101
- f_err = io.StringIO()
102
-
103
- # Sandbox simples: define globais restritos
104
- # IMPORTANTE: exec() nunca é 100% seguro sem containers reais.
105
- safe_globals = {
106
- "__builtins__": __builtins__,
107
- "__name__": "__main__",
108
- "math": __import__("math"),
109
- "json": __import__("json"),
110
- "os": __import__("os"), # CodeJade precisa de OS muitas vezes, mas é perigoso.
111
- # Usuário confia no agente, mas evitamos acesso ao `self` do ToolManager
112
- }
113
-
114
- try:
115
- with redirect_stdout(f_out), redirect_stderr(f_err):
116
- exec(code, safe_globals)
117
-
118
- output = f_out.getvalue()
119
- errors = f_err.getvalue()
120
-
121
- res = ""
122
- if output: res += f"[STDOUT]\n{output}\n"
123
- if errors: res += f"[STDERR]\n{errors}\n"
124
-
125
- return res.strip() or "[Código executado sem saída]"
126
- except Exception as e:
127
- return f"❌ Erro de execução Python: {e}"