Spaces:
Runtime error
Runtime error
Uanderson Silva commited on
Commit ·
b6cd280
1
Parent(s): 38fb424
implement base agents
Browse files- package.json +1 -0
- src/agents/auditor/agent.ts +20 -0
- src/agents/auditor/index.ts +0 -62
- src/agents/auditor/model.ts +2 -2
- src/agents/auditor/state.ts +4 -4
- src/agents/auditor/tools/search-tool.ts +0 -18
- src/agents/auditor/tools/slither-tool.ts +15 -0
- src/agents/coder/agent.ts +20 -0
- src/agents/coder/state.ts +7 -0
- src/agents/tester/agent.ts +13 -0
- src/agents/tester/state.ts +8 -0
- src/index.ts +20 -1
- tsconfig.json +1 -0
package.json
CHANGED
|
@@ -3,6 +3,7 @@
|
|
| 3 |
"version": "0.0.1",
|
| 4 |
"description": "Projeto de TALP 1",
|
| 5 |
"main": "dist/index.js",
|
|
|
|
| 6 |
"scripts": {
|
| 7 |
"test": "vitest",
|
| 8 |
"coverage": "vitest run --coverage",
|
|
|
|
| 3 |
"version": "0.0.1",
|
| 4 |
"description": "Projeto de TALP 1",
|
| 5 |
"main": "dist/index.js",
|
| 6 |
+
"type": "module",
|
| 7 |
"scripts": {
|
| 8 |
"test": "vitest",
|
| 9 |
"coverage": "vitest run --coverage",
|
src/agents/auditor/agent.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { END, type GraphNode, START, StateGraph } from "@langchain/langgraph";
|
| 2 |
+
|
| 3 |
+
import { AuditorState } from "./state.ts";
|
| 4 |
+
import { slitherTool } from "./tools/slither-tool.ts";
|
| 5 |
+
|
| 6 |
+
const PLACEHOLDER_VULNERABILITIES = [
|
| 7 |
+
{ type: "reentrancy", severity: "high", description: "Unchecked external call allows reentrancy attack." },
|
| 8 |
+
{ type: "integer-overflow", severity: "medium", description: "Arithmetic operation may overflow." },
|
| 9 |
+
];
|
| 10 |
+
|
| 11 |
+
const auditContract: GraphNode<typeof AuditorState> = async (state) => {
|
| 12 |
+
await slitherTool.invoke({ solidityFile: state.solidityFile });
|
| 13 |
+
return { vulnerabilities: PLACEHOLDER_VULNERABILITIES };
|
| 14 |
+
};
|
| 15 |
+
|
| 16 |
+
export const auditorAgent = new StateGraph(AuditorState)
|
| 17 |
+
.addNode("auditContract", auditContract)
|
| 18 |
+
.addEdge(START, "auditContract")
|
| 19 |
+
.addEdge("auditContract", END)
|
| 20 |
+
.compile();
|
src/agents/auditor/index.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
| 1 |
-
import { AIMessage, SystemMessage, type ToolMessage } from "@langchain/core/messages";
|
| 2 |
-
import { type ConditionalEdgeRouter, END, type GraphNode, START, StateGraph } from "@langchain/langgraph";
|
| 3 |
-
import { model } from "./model";
|
| 4 |
-
import { MessagesState } from "./state";
|
| 5 |
-
import { searchTool } from "./tools/search-tool";
|
| 6 |
-
|
| 7 |
-
const modelWithTools = model.bindTools([searchTool]);
|
| 8 |
-
|
| 9 |
-
const llmCall: GraphNode<typeof MessagesState> = async (state) => {
|
| 10 |
-
return {
|
| 11 |
-
messages: [
|
| 12 |
-
await modelWithTools.invoke([
|
| 13 |
-
new SystemMessage("You are a helpful assistant tasked with performing arithmetic on a set of inputs."),
|
| 14 |
-
...state.messages,
|
| 15 |
-
]),
|
| 16 |
-
],
|
| 17 |
-
llmCalls: 1,
|
| 18 |
-
};
|
| 19 |
-
};
|
| 20 |
-
|
| 21 |
-
const toolNode: GraphNode<typeof MessagesState> = async (state) => {
|
| 22 |
-
const lastMessage = state.messages.at(-1);
|
| 23 |
-
|
| 24 |
-
if (lastMessage == null || !AIMessage.isInstance(lastMessage)) {
|
| 25 |
-
return { messages: [] };
|
| 26 |
-
}
|
| 27 |
-
|
| 28 |
-
const result: ToolMessage[] = [];
|
| 29 |
-
for (const toolCall of lastMessage.tool_calls ?? []) {
|
| 30 |
-
const _toolName = toolCall.name;
|
| 31 |
-
const tool = searchTool;
|
| 32 |
-
const observation = await tool.invoke(toolCall);
|
| 33 |
-
result.push(observation);
|
| 34 |
-
}
|
| 35 |
-
|
| 36 |
-
return { messages: result };
|
| 37 |
-
};
|
| 38 |
-
|
| 39 |
-
const shouldContinue: ConditionalEdgeRouter<typeof MessagesState, {}, "toolNode"> = (state) => {
|
| 40 |
-
const lastMessage = state.messages.at(-1);
|
| 41 |
-
|
| 42 |
-
// Check if it's an AIMessage before accessing tool_calls
|
| 43 |
-
if (!lastMessage || !AIMessage.isInstance(lastMessage)) {
|
| 44 |
-
return END;
|
| 45 |
-
}
|
| 46 |
-
|
| 47 |
-
// If the LLM makes a tool call, then perform an action
|
| 48 |
-
if (lastMessage.tool_calls?.length) {
|
| 49 |
-
return "toolNode";
|
| 50 |
-
}
|
| 51 |
-
|
| 52 |
-
// Otherwise, we stop (reply to the user)
|
| 53 |
-
return END;
|
| 54 |
-
};
|
| 55 |
-
|
| 56 |
-
export const agent = new StateGraph(MessagesState)
|
| 57 |
-
.addNode("llmCall", llmCall)
|
| 58 |
-
.addNode("toolNode", toolNode)
|
| 59 |
-
.addEdge(START, "llmCall")
|
| 60 |
-
.addConditionalEdges("llmCall", shouldContinue, ["toolNode", END])
|
| 61 |
-
.addEdge("toolNode", "llmCall")
|
| 62 |
-
.compile();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/agents/auditor/model.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
import { ChatOpenRouter } from "@langchain/openrouter";
|
| 2 |
|
| 3 |
-
export const
|
| 4 |
-
model: "
|
| 5 |
});
|
|
|
|
| 1 |
import { ChatOpenRouter } from "@langchain/openrouter";
|
| 2 |
|
| 3 |
+
export const auditorModel = new ChatOpenRouter({
|
| 4 |
+
model: "moonshotai/kimi-k2.6",
|
| 5 |
});
|
src/agents/auditor/state.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
-
import {
|
| 2 |
import { z } from "zod";
|
| 3 |
|
| 4 |
-
export const
|
| 5 |
-
|
| 6 |
-
|
| 7 |
});
|
|
|
|
| 1 |
+
import { StateSchema } from "@langchain/langgraph";
|
| 2 |
import { z } from "zod";
|
| 3 |
|
| 4 |
+
export const AuditorState = new StateSchema({
|
| 5 |
+
solidityFile: z.string().default(""),
|
| 6 |
+
vulnerabilities: z.array(z.record(z.string(), z.any())).default([]),
|
| 7 |
});
|
src/agents/auditor/tools/search-tool.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
| 1 |
-
import { tool } from "langchain";
|
| 2 |
-
import { z } from "zod";
|
| 3 |
-
|
| 4 |
-
export const searchTool = tool(
|
| 5 |
-
async ({ query }) => {
|
| 6 |
-
if (query.toLowerCase().includes("sf") || query.toLowerCase().includes("san francisco")) {
|
| 7 |
-
return "It's 60 degrees and foggy.";
|
| 8 |
-
}
|
| 9 |
-
return "It's 90 degrees and sunny.";
|
| 10 |
-
},
|
| 11 |
-
{
|
| 12 |
-
name: "search",
|
| 13 |
-
description: "Call to surf the web.",
|
| 14 |
-
schema: z.object({
|
| 15 |
-
query: z.string().describe("The query to use in your search."),
|
| 16 |
-
}),
|
| 17 |
-
},
|
| 18 |
-
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/agents/auditor/tools/slither-tool.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { tool } from "langchain";
|
| 2 |
+
import { z } from "zod";
|
| 3 |
+
|
| 4 |
+
export const slitherTool = tool(
|
| 5 |
+
async (_input) => {
|
| 6 |
+
return [];
|
| 7 |
+
},
|
| 8 |
+
{
|
| 9 |
+
name: "slither",
|
| 10 |
+
description: "Run slither static analysis on a Solidity contract.",
|
| 11 |
+
schema: z.object({
|
| 12 |
+
solidityFile: z.string().describe("The Solidity source code to analyze."),
|
| 13 |
+
}),
|
| 14 |
+
},
|
| 15 |
+
);
|
src/agents/coder/agent.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { END, type GraphNode, START, StateGraph } from "@langchain/langgraph";
|
| 2 |
+
|
| 3 |
+
import { CoderState } from "./state.ts";
|
| 4 |
+
|
| 5 |
+
const PLACEHOLDER_CONTRACT = `// SPDX-License-Identifier: MIT
|
| 6 |
+
pragma solidity ^0.8.0;
|
| 7 |
+
|
| 8 |
+
contract Placeholder {
|
| 9 |
+
// TODO: implement contract based on requirements
|
| 10 |
+
}`;
|
| 11 |
+
|
| 12 |
+
const generateContract: GraphNode<typeof CoderState> = async (_state) => {
|
| 13 |
+
return { contract: PLACEHOLDER_CONTRACT };
|
| 14 |
+
};
|
| 15 |
+
|
| 16 |
+
export const coderAgent = new StateGraph(CoderState)
|
| 17 |
+
.addNode("generateContract", generateContract)
|
| 18 |
+
.addEdge(START, "generateContract")
|
| 19 |
+
.addEdge("generateContract", END)
|
| 20 |
+
.compile();
|
src/agents/coder/state.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { StateSchema } from "@langchain/langgraph";
|
| 2 |
+
import { z } from "zod";
|
| 3 |
+
|
| 4 |
+
export const CoderState = new StateSchema({
|
| 5 |
+
requirements: z.array(z.string()).default([]),
|
| 6 |
+
contract: z.string().default(""),
|
| 7 |
+
});
|
src/agents/tester/agent.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { END, type GraphNode, START, StateGraph } from "@langchain/langgraph";
|
| 2 |
+
|
| 3 |
+
import { TesterState } from "./state.ts";
|
| 4 |
+
|
| 5 |
+
const runTests: GraphNode<typeof TesterState> = async (_state) => {
|
| 6 |
+
return { results: [] };
|
| 7 |
+
};
|
| 8 |
+
|
| 9 |
+
export const testerAgent = new StateGraph(TesterState)
|
| 10 |
+
.addNode("runTests", runTests)
|
| 11 |
+
.addEdge(START, "runTests")
|
| 12 |
+
.addEdge("runTests", END)
|
| 13 |
+
.compile();
|
src/agents/tester/state.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { StateSchema } from "@langchain/langgraph";
|
| 2 |
+
import { z } from "zod";
|
| 3 |
+
|
| 4 |
+
export const TesterState = new StateSchema({
|
| 5 |
+
solidityFiles: z.array(z.string()).default([]),
|
| 6 |
+
vulnerability: z.record(z.string(), z.any()).default({}),
|
| 7 |
+
results: z.array(z.any()).default([]),
|
| 8 |
+
});
|
src/index.ts
CHANGED
|
@@ -1,3 +1,22 @@
|
|
| 1 |
import "dotenv/config";
|
| 2 |
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import "dotenv/config";
|
| 2 |
|
| 3 |
+
import { auditorAgent } from "./agents/auditor/agent.ts";
|
| 4 |
+
import { coderAgent } from "./agents/coder/agent.ts";
|
| 5 |
+
import { testerAgent } from "./agents/tester/agent.ts";
|
| 6 |
+
|
| 7 |
+
const requirements = ["ERC20 token", "pausable", "ownable"];
|
| 8 |
+
|
| 9 |
+
const coderResult = await coderAgent.invoke({ requirements });
|
| 10 |
+
console.log("======= Coder =======");
|
| 11 |
+
console.log(coderResult.contract);
|
| 12 |
+
|
| 13 |
+
const auditorResult = await auditorAgent.invoke({ solidityFile: coderResult.contract });
|
| 14 |
+
console.log("\n======= Auditor =======");
|
| 15 |
+
console.log(auditorResult.vulnerabilities);
|
| 16 |
+
|
| 17 |
+
const testerResult = await testerAgent.invoke({
|
| 18 |
+
solidityFiles: [coderResult.contract],
|
| 19 |
+
vulnerability: auditorResult.vulnerabilities[0] ?? {},
|
| 20 |
+
});
|
| 21 |
+
console.log("\n======= Tester =======");
|
| 22 |
+
console.log(testerResult.results);
|
tsconfig.json
CHANGED
|
@@ -6,6 +6,7 @@
|
|
| 6 |
"compilerOptions": {
|
| 7 |
"rootDir": "./src",
|
| 8 |
"outDir": "./dist",
|
|
|
|
| 9 |
"module": "nodenext",
|
| 10 |
"target": "esnext",
|
| 11 |
"types": [
|
|
|
|
| 6 |
"compilerOptions": {
|
| 7 |
"rootDir": "./src",
|
| 8 |
"outDir": "./dist",
|
| 9 |
+
"moduleResolution": "nodenext",
|
| 10 |
"module": "nodenext",
|
| 11 |
"target": "esnext",
|
| 12 |
"types": [
|