File size: 3,622 Bytes
b6cd280
 
 
5710d63
 
 
b6cd280
5710d63
b6cd280
5710d63
 
 
 
 
 
 
 
 
b6cd280
5710d63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b6cd280
 
5710d63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b6cd280
 
5710d63
 
 
b6cd280
5710d63
 
 
 
b6cd280
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import { END, type GraphNode, START, StateGraph } from "@langchain/langgraph";

import { CoderState } from "./state.ts";
import { solidityCoderPrompt, solidityFixPrompt, solidityReviewPrompt } from "./prompts.ts";
import { compileSolidityTool } from "./tools/compile-solidity.ts";
import { createLLM } from "../../config/llm.ts";

const MAX_FIX_ATTEMPTS = 3;

/**
 * Extrai apenas o bloco de código Solidity de uma resposta do LLM.
 */
function extractSolidityCode(text: string): string {
  const match = text.match(/```(?:solidity)?\s*\n([\s\S]*?)```/);
  if (match) return match[1].trim();
  // Se não tem bloco de código, assume que a resposta inteira é código
  return text.trim();
}

/**
 * Nó 1: Gera o smart contract a partir dos requisitos.
 */
const generateContract: GraphNode<typeof CoderState> = async (state) => {
  const llm = createLLM();
  const chain = solidityCoderPrompt.pipe(llm);

  const requirementsText = state.requirements
    .map((r, i) => `${i + 1}. ${r}`)
    .join("\n");

  const result = await chain.invoke({ requirements: requirementsText });
  const code = extractSolidityCode(
    typeof result.content === "string" ? result.content : JSON.stringify(result.content),
  );

  return { contract: code, compilationErrors: [] };
};

/**
 * Nó 2: Compila o contrato e armazena erros (se houver).
 */
const compileContract: GraphNode<typeof CoderState> = async (state) => {
  const result = await compileSolidityTool.invoke({
    sourceCode: state.contract,
    filename: "Contract.sol",
  });

  return { compilationErrors: result.errors };
};

/**
 * Nó 3: Corrige o contrato com base nos erros de compilação.
 */
const fixContract: GraphNode<typeof CoderState> = async (state) => {
  const llm = createLLM();
  const chain = solidityFixPrompt.pipe(llm);

  const errorsText = state.compilationErrors.join("\n\n");

  const result = await chain.invoke({
    contract: state.contract,
    errors: errorsText,
  });

  const code = extractSolidityCode(
    typeof result.content === "string" ? result.content : JSON.stringify(result.content),
  );

  return { contract: code, compilationErrors: [] };
};

/**
 * Nó 4: Revisa o contrato compilado quanto a segurança e boas práticas.
 */
const reviewContract: GraphNode<typeof CoderState> = async (state) => {
  const llm = createLLM();
  const chain = solidityReviewPrompt.pipe(llm);

  const requirementsText = state.requirements.join(", ");

  const result = await chain.invoke({
    requirements: requirementsText,
    contract: state.contract,
  });

  const summary =
    typeof result.content === "string" ? result.content : JSON.stringify(result.content);

  return { reviewSummary: summary };
};

/**
 * Roteador: decide se precisa corrigir ou se pode seguir para revisão.
 * Controla o número de tentativas de correção.
 */
let fixAttempts = 0;

function shouldFix(state: { compilationErrors: string[] }): "fixContract" | "reviewContract" {
  if (state.compilationErrors.length > 0 && fixAttempts < MAX_FIX_ATTEMPTS) {
    fixAttempts++;
    return "fixContract";
  }
  fixAttempts = 0; // reset para próxima execução
  return "reviewContract";
}

export const coderAgent = new StateGraph(CoderState)
  .addNode("generateContract", generateContract)
  .addNode("compileContract", compileContract)
  .addNode("fixContract", fixContract)
  .addNode("reviewContract", reviewContract)
  .addEdge(START, "generateContract")
  .addEdge("generateContract", "compileContract")
  .addConditionalEdges("compileContract", shouldFix)
  .addEdge("fixContract", "compileContract")
  .addEdge("reviewContract", END)
  .compile();