Spaces:
Runtime error
Runtime error
Uanderson Silva commited on
Commit ·
f27a135
1
Parent(s): 0b3cd21
use different llms for each phase
Browse files- src/agents/auditor/agent.ts +7 -5
- src/config/llm.ts +18 -11
src/agents/auditor/agent.ts
CHANGED
|
@@ -29,7 +29,9 @@ import { buildRepoTree } from "./tools/repo-tree/tool.ts";
|
|
| 29 |
import { analyzeSolidityFile } from "./tools/solidity-analyzer/tool.ts";
|
| 30 |
import { matchLines } from "./utils.ts";
|
| 31 |
|
| 32 |
-
const
|
|
|
|
|
|
|
| 33 |
|
| 34 |
const walkDirectory = (dir: string, depth: number, solFiles: string[], docFiles: string[]) => {
|
| 35 |
if (depth > MAX_DEPTH) return;
|
|
@@ -79,7 +81,7 @@ const defineScope: GraphNode<typeof AuditorState> = async (state) => {
|
|
| 79 |
logger.info("defineScope: ranking files by importance");
|
| 80 |
|
| 81 |
const RankFilesSchema = z.object({ rankings: z.array(FileRankingSchema) });
|
| 82 |
-
const rankingModel =
|
| 83 |
|
| 84 |
const { rankings } = await rankingModel.invoke([
|
| 85 |
new SystemMessage(RANK_FILES_PROMPT),
|
|
@@ -138,7 +140,7 @@ const gatherContext: GraphNode<typeof AuditorState> = async (state) => {
|
|
| 138 |
parts.push(analysis);
|
| 139 |
}
|
| 140 |
|
| 141 |
-
const model =
|
| 142 |
const result = await model.invoke([new SystemMessage(GATHER_CONTEXT_PROMPT), new HumanMessage(parts.join("\n\n"))]);
|
| 143 |
|
| 144 |
logger.debug(`gatherContext: full context:\n${parts.join("\n\n")}`);
|
|
@@ -149,7 +151,7 @@ const gatherContext: GraphNode<typeof AuditorState> = async (state) => {
|
|
| 149 |
};
|
| 150 |
|
| 151 |
const findVulnerabilities: GraphNode<typeof AuditorState> = async (state) => {
|
| 152 |
-
const model =
|
| 153 |
|
| 154 |
const previousFeedback =
|
| 155 |
state.judgeReviews.length > 0
|
|
@@ -212,7 +214,7 @@ const judgeFindings: GraphNode<typeof AuditorState> = async (state) => {
|
|
| 212 |
};
|
| 213 |
}
|
| 214 |
|
| 215 |
-
const model =
|
| 216 |
|
| 217 |
logger.info(`judgeFindings: reviewing ${state.candidateFindings.length} candidate finding(s) in parallel`);
|
| 218 |
|
|
|
|
| 29 |
import { analyzeSolidityFile } from "./tools/solidity-analyzer/tool.ts";
|
| 30 |
import { matchLines } from "./utils.ts";
|
| 31 |
|
| 32 |
+
const llmHaiku = createLLM("anthropic", { model: "claude-haiku-4-5", maxTokens: 20000 });
|
| 33 |
+
const llmOpus = createLLM("anthropic", { model: "claude-opus-4-8", maxTokens: 20000 });
|
| 34 |
+
const llmSonnet = createLLM("anthropic", { model: "claude-sonnet-4-6", maxTokens: 20000 });
|
| 35 |
|
| 36 |
const walkDirectory = (dir: string, depth: number, solFiles: string[], docFiles: string[]) => {
|
| 37 |
if (depth > MAX_DEPTH) return;
|
|
|
|
| 81 |
logger.info("defineScope: ranking files by importance");
|
| 82 |
|
| 83 |
const RankFilesSchema = z.object({ rankings: z.array(FileRankingSchema) });
|
| 84 |
+
const rankingModel = llmHaiku.withStructuredOutput(RankFilesSchema);
|
| 85 |
|
| 86 |
const { rankings } = await rankingModel.invoke([
|
| 87 |
new SystemMessage(RANK_FILES_PROMPT),
|
|
|
|
| 140 |
parts.push(analysis);
|
| 141 |
}
|
| 142 |
|
| 143 |
+
const model = llmHaiku.withStructuredOutput(z.object({ context: z.string() }));
|
| 144 |
const result = await model.invoke([new SystemMessage(GATHER_CONTEXT_PROMPT), new HumanMessage(parts.join("\n\n"))]);
|
| 145 |
|
| 146 |
logger.debug(`gatherContext: full context:\n${parts.join("\n\n")}`);
|
|
|
|
| 151 |
};
|
| 152 |
|
| 153 |
const findVulnerabilities: GraphNode<typeof AuditorState> = async (state) => {
|
| 154 |
+
const model = llmOpus.withStructuredOutput(z.object({ findings: z.array(CandidateFindingSchema) }));
|
| 155 |
|
| 156 |
const previousFeedback =
|
| 157 |
state.judgeReviews.length > 0
|
|
|
|
| 214 |
};
|
| 215 |
}
|
| 216 |
|
| 217 |
+
const model = llmSonnet.withStructuredOutput(JudgeReviewSchema);
|
| 218 |
|
| 219 |
logger.info(`judgeFindings: reviewing ${state.candidateFindings.length} candidate finding(s) in parallel`);
|
| 220 |
|
src/config/llm.ts
CHANGED
|
@@ -5,32 +5,39 @@ import type { BaseChatModel } from "@langchain/core/language_models/chat_models"
|
|
| 5 |
|
| 6 |
export type LLMProvider = "google" | "openrouter" | "anthropic";
|
| 7 |
|
| 8 |
-
export
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
const provider = overrideProvider || (process.env.LLM_PROVIDER as LLMProvider) || "openrouter";
|
| 10 |
|
| 11 |
switch (provider) {
|
| 12 |
case "openrouter":
|
| 13 |
return new ChatOpenRouter({
|
| 14 |
-
model: process.env.OPENROUTER_MODEL || "google/gemini-3.1-flash-lite",
|
| 15 |
-
temperature: 0.2,
|
| 16 |
apiKey: process.env.OPENROUTER_API_KEY,
|
| 17 |
-
maxTokens: 4096,
|
| 18 |
});
|
| 19 |
|
| 20 |
-
|
| 21 |
case "anthropic":
|
| 22 |
return new ChatAnthropic({
|
| 23 |
-
model: process.env.ANTHROPIC_MODEL || "claude-sonnet-4-6",
|
| 24 |
-
temperature: 0.2,
|
| 25 |
-
|
|
|
|
| 26 |
});
|
|
|
|
| 27 |
case "google":
|
| 28 |
default:
|
| 29 |
return new ChatGoogleGenerativeAI({
|
| 30 |
apiKey: process.env.GOOGLE_API_KEY || "",
|
| 31 |
-
model: process.env.MODEL_NAME || "gemini-2.5-flash",
|
| 32 |
-
temperature: 0.2,
|
| 33 |
-
maxOutputTokens: 4096,
|
| 34 |
});
|
| 35 |
}
|
| 36 |
}
|
|
|
|
| 5 |
|
| 6 |
export type LLMProvider = "google" | "openrouter" | "anthropic";
|
| 7 |
|
| 8 |
+
export interface LLMOptions {
|
| 9 |
+
model?: string;
|
| 10 |
+
temperature?: number;
|
| 11 |
+
maxTokens?: number;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
export function createLLM(overrideProvider?: LLMProvider, options?: LLMOptions): BaseChatModel {
|
| 15 |
const provider = overrideProvider || (process.env.LLM_PROVIDER as LLMProvider) || "openrouter";
|
| 16 |
|
| 17 |
switch (provider) {
|
| 18 |
case "openrouter":
|
| 19 |
return new ChatOpenRouter({
|
| 20 |
+
model: options?.model || process.env.OPENROUTER_MODEL || "google/gemini-3.1-flash-lite",
|
| 21 |
+
temperature: options?.temperature ?? 0.2,
|
| 22 |
apiKey: process.env.OPENROUTER_API_KEY,
|
| 23 |
+
maxTokens: options?.maxTokens ?? 4096,
|
| 24 |
});
|
| 25 |
|
|
|
|
| 26 |
case "anthropic":
|
| 27 |
return new ChatAnthropic({
|
| 28 |
+
model: options?.model || process.env.ANTHROPIC_MODEL || "claude-sonnet-4-6",
|
| 29 |
+
temperature: options?.temperature ?? 0.2,
|
| 30 |
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
| 31 |
+
maxTokens: options?.maxTokens ?? 4096,
|
| 32 |
});
|
| 33 |
+
|
| 34 |
case "google":
|
| 35 |
default:
|
| 36 |
return new ChatGoogleGenerativeAI({
|
| 37 |
apiKey: process.env.GOOGLE_API_KEY || "",
|
| 38 |
+
model: options?.model || process.env.MODEL_NAME || "gemini-2.5-flash",
|
| 39 |
+
temperature: options?.temperature ?? 0.2,
|
| 40 |
+
maxOutputTokens: options?.maxTokens ?? 4096,
|
| 41 |
});
|
| 42 |
}
|
| 43 |
}
|