@saber2pr/ai-agent 0.0.18 → 0.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cli-graph.d.ts +2 -0
- package/lib/cli-graph.js +9 -0
- package/lib/config/config.js +1 -1
- package/lib/core/agent-graph.d.ts +52 -0
- package/lib/core/agent-graph.js +220 -0
- package/lib/core/agent.js +1 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.js +5 -1
- package/lib/model/AgentChainModel.d.ts +4 -4
- package/lib/model/AgentChainModel.js +2 -2
- package/lib/model/AgentGraphModel.d.ts +21 -0
- package/lib/model/AgentGraphModel.js +96 -0
- package/lib/tools/filesystem/index.js +3 -3
- package/lib/tools/filesystem/lib.js +2 -2
- package/lib/tools/filesystem/path-utils.js +1 -1
- package/lib/tools/filesystem/roots-utils.js +1 -1
- package/lib/types/type.d.ts +13 -1
- package/lib/utils/convertToLangChainTool.d.ts +3 -0
- package/lib/utils/convertToLangChainTool.js +24 -0
- package/lib/utils/createTool.d.ts +1 -1
- package/lib/utils/jsonSchemaToZod.d.ts +1 -1
- package/package.json +10 -7
package/lib/cli-graph.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const agent_graph_1 = __importDefault(require("./core/agent-graph"));
|
|
8
|
+
const agent = new agent_graph_1.default();
|
|
9
|
+
agent.start();
|
package/lib/config/config.js
CHANGED
|
@@ -4,6 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.CONFIG_FILE = void 0;
|
|
7
|
-
const path_1 = __importDefault(require("path"));
|
|
8
7
|
const os_1 = __importDefault(require("os"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
9
|
exports.CONFIG_FILE = path_1.default.join(os_1.default.homedir(), ".saber2pr-agent.json");
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { BaseMessage } from "@langchain/core/messages";
|
|
2
|
+
import { AgentOptions } from "../types/type";
|
|
3
|
+
export declare const CONFIG_FILE: string;
|
|
4
|
+
declare const AgentState: import("@langchain/langgraph").AnnotationRoot<{
|
|
5
|
+
messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
6
|
+
auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
7
|
+
targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
8
|
+
mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
|
|
9
|
+
}>;
|
|
10
|
+
export default class McpGraphAgent {
|
|
11
|
+
private model;
|
|
12
|
+
private toolNode;
|
|
13
|
+
private targetDir;
|
|
14
|
+
private options;
|
|
15
|
+
private checkpointer;
|
|
16
|
+
private langchainTools;
|
|
17
|
+
private spinner;
|
|
18
|
+
constructor(options?: AgentOptions);
|
|
19
|
+
private askForConfig;
|
|
20
|
+
private getModel;
|
|
21
|
+
chat(query?: string): Promise<void>;
|
|
22
|
+
start(): Promise<void>;
|
|
23
|
+
private renderOutput;
|
|
24
|
+
callModel(state: typeof AgentState.State): Promise<{
|
|
25
|
+
messages: unknown[];
|
|
26
|
+
}>;
|
|
27
|
+
trackProgress(state: typeof AgentState.State): Promise<{
|
|
28
|
+
auditedFiles: string[];
|
|
29
|
+
}>;
|
|
30
|
+
createGraph(): Promise<import("@langchain/langgraph").CompiledStateGraph<import("@langchain/langgraph").StateType<{
|
|
31
|
+
messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
32
|
+
auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
33
|
+
targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
34
|
+
mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
|
|
35
|
+
}>, import("@langchain/langgraph").UpdateType<{
|
|
36
|
+
messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
37
|
+
auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
38
|
+
targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
39
|
+
mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
|
|
40
|
+
}>, "tools" | "agent" | "__start__" | "progress", {
|
|
41
|
+
messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
42
|
+
auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
43
|
+
targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
44
|
+
mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
|
|
45
|
+
}, {
|
|
46
|
+
messages: import("@langchain/langgraph").BinaryOperatorAggregate<BaseMessage[], BaseMessage[]>;
|
|
47
|
+
auditedFiles: import("@langchain/langgraph").BinaryOperatorAggregate<string[], string[]>;
|
|
48
|
+
targetCount: import("@langchain/langgraph").BinaryOperatorAggregate<number, number>;
|
|
49
|
+
mode: import("@langchain/langgraph").BinaryOperatorAggregate<"chat" | "auto", "chat" | "auto">;
|
|
50
|
+
}, import("@langchain/langgraph").StateDefinition>>;
|
|
51
|
+
}
|
|
52
|
+
export {};
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CONFIG_FILE = void 0;
|
|
7
|
+
const openai_1 = require("@langchain/openai");
|
|
8
|
+
const messages_1 = require("@langchain/core/messages");
|
|
9
|
+
const langgraph_1 = require("@langchain/langgraph");
|
|
10
|
+
const prebuilt_1 = require("@langchain/langgraph/prebuilt");
|
|
11
|
+
const prompts_1 = require("@langchain/core/prompts");
|
|
12
|
+
const readline_1 = __importDefault(require("readline"));
|
|
13
|
+
const fs_1 = __importDefault(require("fs"));
|
|
14
|
+
const path_1 = __importDefault(require("path"));
|
|
15
|
+
const os_1 = __importDefault(require("os"));
|
|
16
|
+
const ora_1 = __importDefault(require("ora")); // 用于显示 Loading 动画
|
|
17
|
+
const builtin_1 = require("../tools/builtin");
|
|
18
|
+
const convertToLangChainTool_1 = require("../utils/convertToLangChainTool");
|
|
19
|
+
exports.CONFIG_FILE = path_1.default.join(os_1.default.homedir(), ".saber2pr-agent.json");
|
|
20
|
+
// --- 1. 定义状态 (State) ---
|
|
21
|
+
const AgentState = langgraph_1.Annotation.Root({
|
|
22
|
+
messages: (0, langgraph_1.Annotation)({
|
|
23
|
+
reducer: (x, y) => x.concat(y),
|
|
24
|
+
default: () => [],
|
|
25
|
+
}),
|
|
26
|
+
auditedFiles: (0, langgraph_1.Annotation)({
|
|
27
|
+
reducer: (x, y) => Array.from(new Set([...x, ...y])),
|
|
28
|
+
default: () => [],
|
|
29
|
+
}),
|
|
30
|
+
targetCount: (0, langgraph_1.Annotation)({
|
|
31
|
+
reducer: (x, y) => y !== null && y !== void 0 ? y : x,
|
|
32
|
+
default: () => 4,
|
|
33
|
+
}),
|
|
34
|
+
mode: (0, langgraph_1.Annotation)({
|
|
35
|
+
reducer: (x, y) => y !== null && y !== void 0 ? y : x,
|
|
36
|
+
default: () => "chat",
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
class McpGraphAgent {
|
|
40
|
+
constructor(options = {}) {
|
|
41
|
+
this.checkpointer = new langgraph_1.MemorySaver();
|
|
42
|
+
this.langchainTools = [];
|
|
43
|
+
this.spinner = (0, ora_1.default)({ color: "cyan" });
|
|
44
|
+
this.options = options;
|
|
45
|
+
this.targetDir = options.targetDir || process.cwd();
|
|
46
|
+
process.setMaxListeners(50); // 防止 AbortSignal 监听器过多的警告
|
|
47
|
+
const builtinToolInfos = (0, builtin_1.createDefaultBuiltinTools)({ options });
|
|
48
|
+
const externalToolInfos = options.tools || [];
|
|
49
|
+
this.langchainTools = [...builtinToolInfos, ...externalToolInfos].map((t) => (0, convertToLangChainTool_1.convertToLangChainTool)(t));
|
|
50
|
+
this.toolNode = new prebuilt_1.ToolNode(this.langchainTools);
|
|
51
|
+
}
|
|
52
|
+
async askForConfig() {
|
|
53
|
+
let config = {};
|
|
54
|
+
if (fs_1.default.existsSync(exports.CONFIG_FILE)) {
|
|
55
|
+
try {
|
|
56
|
+
config = JSON.parse(fs_1.default.readFileSync(exports.CONFIG_FILE, "utf-8"));
|
|
57
|
+
}
|
|
58
|
+
catch (e) { }
|
|
59
|
+
}
|
|
60
|
+
if (!config.baseURL || !config.apiKey) {
|
|
61
|
+
const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
62
|
+
const question = (q) => new Promise((res) => rl.question(q, res));
|
|
63
|
+
console.log(`💡 首次运行请配置信息:`);
|
|
64
|
+
config.baseURL = config.baseURL || await question(`? API Base URL: `);
|
|
65
|
+
config.apiKey = config.apiKey || await question(`? API Key: `);
|
|
66
|
+
config.model = config.model || await question(`? Model Name: `) || "gpt-4o";
|
|
67
|
+
fs_1.default.writeFileSync(exports.CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
68
|
+
rl.close();
|
|
69
|
+
}
|
|
70
|
+
return config;
|
|
71
|
+
}
|
|
72
|
+
async getModel() {
|
|
73
|
+
if (this.model)
|
|
74
|
+
return this.model;
|
|
75
|
+
let modelInstance = this.options.apiModel;
|
|
76
|
+
if (!modelInstance) {
|
|
77
|
+
const config = await this.askForConfig();
|
|
78
|
+
modelInstance = new openai_1.ChatOpenAI({
|
|
79
|
+
openAIApiKey: config.apiKey,
|
|
80
|
+
configuration: { baseURL: config.baseURL },
|
|
81
|
+
modelName: config.model,
|
|
82
|
+
temperature: 0,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
this.model = modelInstance.bindTools(this.langchainTools);
|
|
86
|
+
return this.model;
|
|
87
|
+
}
|
|
88
|
+
async chat(query = "开始代码审计") {
|
|
89
|
+
await this.getModel();
|
|
90
|
+
const app = await this.createGraph();
|
|
91
|
+
const stream = await app.stream({
|
|
92
|
+
messages: [new messages_1.HumanMessage(query)],
|
|
93
|
+
mode: "auto",
|
|
94
|
+
targetCount: 4
|
|
95
|
+
}, { configurable: { thread_id: "auto_worker" }, recursionLimit: 100 });
|
|
96
|
+
for await (const output of stream)
|
|
97
|
+
this.renderOutput(output);
|
|
98
|
+
console.log("✅ 任务执行完毕。");
|
|
99
|
+
}
|
|
100
|
+
async start() {
|
|
101
|
+
await this.getModel();
|
|
102
|
+
const app = await this.createGraph();
|
|
103
|
+
const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
104
|
+
const threadId = `session_${Date.now()}`;
|
|
105
|
+
console.log(`\n💬 已进入交互审计模式 (Thread: ${threadId})`);
|
|
106
|
+
const ask = () => {
|
|
107
|
+
rl.question("> ", async (input) => {
|
|
108
|
+
if (input.toLowerCase() === "exit") {
|
|
109
|
+
rl.close();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const stream = await app.stream({ messages: [new messages_1.HumanMessage(input)], mode: "chat" }, { configurable: { thread_id: threadId }, recursionLimit: 50 });
|
|
113
|
+
for await (const output of stream)
|
|
114
|
+
this.renderOutput(output);
|
|
115
|
+
ask();
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
ask();
|
|
119
|
+
}
|
|
120
|
+
renderOutput(output) {
|
|
121
|
+
var _a, _b;
|
|
122
|
+
// 每次渲染输出前,确保停止 Spinner
|
|
123
|
+
if (this.spinner.isSpinning)
|
|
124
|
+
this.spinner.stop();
|
|
125
|
+
const agentNode = output.agent;
|
|
126
|
+
if (agentNode) {
|
|
127
|
+
const msg = agentNode.messages[0];
|
|
128
|
+
// 1. 打印思考过程
|
|
129
|
+
const reasoning = (_a = msg.additional_kwargs) === null || _a === void 0 ? void 0 : _a.reasoning;
|
|
130
|
+
if (reasoning) {
|
|
131
|
+
console.log("\n🧠 [思考过程]:\n" + "─".repeat(50) + "\n" + reasoning + "\n" + "─".repeat(50) + "\n");
|
|
132
|
+
}
|
|
133
|
+
// 2. 打印正式回答
|
|
134
|
+
if (msg.content)
|
|
135
|
+
console.log("🤖 [AI]:", msg.content);
|
|
136
|
+
// 3. 打印工具调用
|
|
137
|
+
if ((_b = msg.tool_calls) === null || _b === void 0 ? void 0 : _b.length) {
|
|
138
|
+
msg.tool_calls.forEach((call) => {
|
|
139
|
+
console.log(`🛠️ [调用工具]: ${call.name} 📦 参数: ${JSON.stringify(call.args)}`);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// --- 节点逻辑 ---
|
|
145
|
+
async callModel(state) {
|
|
146
|
+
// 处理变量序列化,防止 [object Object]
|
|
147
|
+
const auditedListStr = state.auditedFiles.length > 0
|
|
148
|
+
? state.auditedFiles.map(f => `\n - ${f}`).join("")
|
|
149
|
+
: "暂无";
|
|
150
|
+
const extraPromptStr = typeof this.options.extraSystemPrompt === 'object'
|
|
151
|
+
? JSON.stringify(this.options.extraSystemPrompt, null, 2)
|
|
152
|
+
: (this.options.extraSystemPrompt || "");
|
|
153
|
+
// 使用变量占位符 {extraPrompt} 避免内容中的 {} 引发模板解析错误
|
|
154
|
+
const prompt = prompts_1.ChatPromptTemplate.fromMessages([
|
|
155
|
+
["system", `你是一个代码审计专家。工作目录:${this.targetDir}。
|
|
156
|
+
当前模式:{mode}。
|
|
157
|
+
KPI进度:{doneCount}/{targetCount}。已审计文件:{auditedList}
|
|
158
|
+
|
|
159
|
+
# 附加指令
|
|
160
|
+
{extraPrompt}`],
|
|
161
|
+
new prompts_1.MessagesPlaceholder("messages"),
|
|
162
|
+
]);
|
|
163
|
+
// ✅ 显示 Loading
|
|
164
|
+
this.spinner.start("AI 正在思考并分析代码...");
|
|
165
|
+
try {
|
|
166
|
+
const chain = prompt.pipe(this.model);
|
|
167
|
+
const response = await chain.invoke({
|
|
168
|
+
messages: state.messages,
|
|
169
|
+
mode: state.mode,
|
|
170
|
+
targetCount: state.targetCount,
|
|
171
|
+
doneCount: state.auditedFiles.length,
|
|
172
|
+
auditedList: auditedListStr,
|
|
173
|
+
extraPrompt: extraPromptStr, // 变量方式传入更安全
|
|
174
|
+
});
|
|
175
|
+
this.spinner.stop(); // 得到响应即停止
|
|
176
|
+
return { messages: [response] };
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
this.spinner.fail("AI 响应异常");
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async trackProgress(state) {
|
|
184
|
+
const lastAiMsg = state.messages[state.messages.length - 1];
|
|
185
|
+
const newFiles = [];
|
|
186
|
+
if (lastAiMsg === null || lastAiMsg === void 0 ? void 0 : lastAiMsg.tool_calls) {
|
|
187
|
+
for (const tc of lastAiMsg.tool_calls) {
|
|
188
|
+
// 追踪涉及写、修复、生成审计报告的文件
|
|
189
|
+
const toolsToTrack = ["write_text_file", "apply_fix", "generate_review"];
|
|
190
|
+
if (toolsToTrack.includes(tc.name)) {
|
|
191
|
+
const file = tc.args.path || tc.args.filePath || tc.args.file;
|
|
192
|
+
if (file && typeof file === 'string')
|
|
193
|
+
newFiles.push(file);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return { auditedFiles: newFiles };
|
|
198
|
+
}
|
|
199
|
+
async createGraph() {
|
|
200
|
+
const workflow = new langgraph_1.StateGraph(AgentState)
|
|
201
|
+
.addNode("agent", (state) => this.callModel(state))
|
|
202
|
+
.addNode("tools", this.toolNode)
|
|
203
|
+
.addNode("progress", (state) => this.trackProgress(state))
|
|
204
|
+
.addEdge(langgraph_1.START, "agent")
|
|
205
|
+
.addConditionalEdges("agent", (state) => {
|
|
206
|
+
var _a;
|
|
207
|
+
const lastMsg = state.messages[state.messages.length - 1];
|
|
208
|
+
if ((_a = lastMsg.tool_calls) === null || _a === void 0 ? void 0 : _a.length)
|
|
209
|
+
return "tools";
|
|
210
|
+
// 自动模式下且未达标时继续
|
|
211
|
+
if (state.mode === "auto" && state.auditedFiles.length < state.targetCount)
|
|
212
|
+
return "agent";
|
|
213
|
+
return langgraph_1.END;
|
|
214
|
+
})
|
|
215
|
+
.addEdge("tools", "progress")
|
|
216
|
+
.addEdge("progress", "agent");
|
|
217
|
+
return workflow.compile({ checkpointer: this.checkpointer });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
exports.default = McpGraphAgent;
|
package/lib/core/agent.js
CHANGED
|
@@ -44,8 +44,8 @@ const path_1 = __importDefault(require("path"));
|
|
|
44
44
|
const readline = __importStar(require("readline"));
|
|
45
45
|
const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
46
46
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
47
|
-
const builtin_1 = require("../tools/builtin");
|
|
48
47
|
const config_1 = require("../config/config");
|
|
48
|
+
const builtin_1 = require("../tools/builtin");
|
|
49
49
|
class McpAgent {
|
|
50
50
|
constructor(options) {
|
|
51
51
|
this.modelName = "";
|
package/lib/index.d.ts
CHANGED
|
@@ -3,3 +3,5 @@ export { default as McpChainAgent } from './core/agent-chain';
|
|
|
3
3
|
export { default } from './core/agent';
|
|
4
4
|
export { createTool } from './utils/createTool';
|
|
5
5
|
export { AgentChainModel } from './model/AgentChainModel';
|
|
6
|
+
export { AgentGraphModel, AgentGraphLLMResponse } from './model/AgentGraphModel';
|
|
7
|
+
export { default as McpGraphAgent } from './core/agent-graph';
|
package/lib/index.js
CHANGED
|
@@ -17,7 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.AgentChainModel = exports.createTool = exports.default = exports.McpChainAgent = void 0;
|
|
20
|
+
exports.McpGraphAgent = exports.AgentGraphModel = exports.AgentChainModel = exports.createTool = exports.default = exports.McpChainAgent = void 0;
|
|
21
21
|
__exportStar(require("./core/agent"), exports);
|
|
22
22
|
var agent_chain_1 = require("./core/agent-chain");
|
|
23
23
|
Object.defineProperty(exports, "McpChainAgent", { enumerable: true, get: function () { return __importDefault(agent_chain_1).default; } });
|
|
@@ -27,3 +27,7 @@ var createTool_1 = require("./utils/createTool");
|
|
|
27
27
|
Object.defineProperty(exports, "createTool", { enumerable: true, get: function () { return createTool_1.createTool; } });
|
|
28
28
|
var AgentChainModel_1 = require("./model/AgentChainModel");
|
|
29
29
|
Object.defineProperty(exports, "AgentChainModel", { enumerable: true, get: function () { return AgentChainModel_1.AgentChainModel; } });
|
|
30
|
+
var AgentGraphModel_1 = require("./model/AgentGraphModel");
|
|
31
|
+
Object.defineProperty(exports, "AgentGraphModel", { enumerable: true, get: function () { return AgentGraphModel_1.AgentGraphModel; } });
|
|
32
|
+
var agent_graph_1 = require("./core/agent-graph");
|
|
33
|
+
Object.defineProperty(exports, "McpGraphAgent", { enumerable: true, get: function () { return __importDefault(agent_graph_1).default; } });
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { BaseChatModel } from
|
|
2
|
-
import { AIMessage, MessageFieldWithRole } from
|
|
1
|
+
import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
2
|
+
import { AIMessage, MessageFieldWithRole } from '@langchain/core/messages';
|
|
3
3
|
interface AgentChainModelImpl {
|
|
4
|
-
|
|
4
|
+
generateResponse: (messages: MessageFieldWithRole[]) => Promise<string>;
|
|
5
5
|
}
|
|
6
6
|
export declare abstract class AgentChainModel extends BaseChatModel implements AgentChainModelImpl {
|
|
7
7
|
bind(args: any): any;
|
|
8
8
|
constructor(fields?: any);
|
|
9
|
-
|
|
9
|
+
generateResponse(messages: MessageFieldWithRole[]): Promise<string>;
|
|
10
10
|
_generate(messages: any): Promise<{
|
|
11
11
|
generations: {
|
|
12
12
|
text: string;
|
|
@@ -11,11 +11,11 @@ class AgentChainModel extends chat_models_1.BaseChatModel {
|
|
|
11
11
|
return (super.bind ? super.bind(args) : this);
|
|
12
12
|
}
|
|
13
13
|
constructor(fields) { super(fields || {}); }
|
|
14
|
-
async
|
|
14
|
+
async generateResponse(messages) {
|
|
15
15
|
return '';
|
|
16
16
|
}
|
|
17
17
|
async _generate(messages) {
|
|
18
|
-
let text = await this.
|
|
18
|
+
let text = await this.generateResponse(messages);
|
|
19
19
|
return { generations: [{ text, message: new messages_1.AIMessage(text) }] };
|
|
20
20
|
}
|
|
21
21
|
_llmType() { return "my_private_llm"; }
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BaseChatModel, BaseChatModelParams } from '@langchain/core/language_models/chat_models';
|
|
2
|
+
import { BaseMessage } from '@langchain/core/messages';
|
|
3
|
+
import { ChatResult } from '@langchain/core/outputs';
|
|
4
|
+
export interface AgentGraphLLMResponse {
|
|
5
|
+
text: string;
|
|
6
|
+
reasoning?: string;
|
|
7
|
+
chatId?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare abstract class AgentGraphModel extends BaseChatModel {
|
|
10
|
+
protected boundTools?: any[];
|
|
11
|
+
protected chatId?: string;
|
|
12
|
+
constructor(fields?: BaseChatModelParams & {
|
|
13
|
+
chatId?: string;
|
|
14
|
+
});
|
|
15
|
+
bindTools(tools: any[]): any;
|
|
16
|
+
abstract callApi(prompt: string, chatId?: string): Promise<AgentGraphLLMResponse>;
|
|
17
|
+
_generate(messages: BaseMessage[]): Promise<ChatResult>;
|
|
18
|
+
private serializeMessages;
|
|
19
|
+
private parseToolCalls;
|
|
20
|
+
_llmType(): string;
|
|
21
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AgentGraphModel = void 0;
|
|
4
|
+
const chat_models_1 = require("@langchain/core/language_models/chat_models");
|
|
5
|
+
const messages_1 = require("@langchain/core/messages");
|
|
6
|
+
const function_calling_1 = require("@langchain/core/utils/function_calling");
|
|
7
|
+
class AgentGraphModel extends chat_models_1.BaseChatModel {
|
|
8
|
+
constructor(fields) {
|
|
9
|
+
super(fields || {});
|
|
10
|
+
this.chatId = fields === null || fields === void 0 ? void 0 : fields.chatId;
|
|
11
|
+
}
|
|
12
|
+
bindTools(tools) {
|
|
13
|
+
this.boundTools = tools.map(t => (0, function_calling_1.convertToOpenAITool)(t));
|
|
14
|
+
return this;
|
|
15
|
+
}
|
|
16
|
+
async _generate(messages) {
|
|
17
|
+
const fullPrompt = this.serializeMessages(messages);
|
|
18
|
+
const response = await this.callApi(fullPrompt, this.chatId);
|
|
19
|
+
if (response.chatId)
|
|
20
|
+
this.chatId = response.chatId;
|
|
21
|
+
let { text, reasoning } = response;
|
|
22
|
+
// ✅ 通用逻辑:解析 <think> 标签
|
|
23
|
+
if (!reasoning && text.includes("<think>")) {
|
|
24
|
+
const match = text.match(/<think>([\s\S]*?)<\/think>/);
|
|
25
|
+
if (match) {
|
|
26
|
+
reasoning = match[1].trim();
|
|
27
|
+
text = text.replace(/<think>[\s\S]*?<\/think>/, "").trim();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const toolCalls = this.parseToolCalls(text);
|
|
31
|
+
return {
|
|
32
|
+
generations: [{
|
|
33
|
+
text,
|
|
34
|
+
message: new messages_1.AIMessage({
|
|
35
|
+
content: text,
|
|
36
|
+
tool_calls: toolCalls,
|
|
37
|
+
additional_kwargs: { reasoning: reasoning || "" }
|
|
38
|
+
})
|
|
39
|
+
}]
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
serializeMessages(messages) {
|
|
43
|
+
const systemMsg = messages.find(m => m._getType() === 'system');
|
|
44
|
+
const lastMsg = messages[messages.length - 1];
|
|
45
|
+
const format = (m) => {
|
|
46
|
+
const content = typeof m.content === 'string' ? m.content : JSON.stringify(m.content, null, 2);
|
|
47
|
+
return `${m._getType().toUpperCase()}: ${content}`;
|
|
48
|
+
};
|
|
49
|
+
const toolsContext = this.boundTools ? `\n[Tools]\n${JSON.stringify(this.boundTools, null, 2)}` : "";
|
|
50
|
+
return `
|
|
51
|
+
${format(systemMsg)}
|
|
52
|
+
${toolsContext}
|
|
53
|
+
# Current Progress
|
|
54
|
+
${format(lastMsg)}
|
|
55
|
+
# Output Requirement
|
|
56
|
+
1. Reasoning in <think> tags.
|
|
57
|
+
2. Action: Name
|
|
58
|
+
3. Arguments: {JSON}
|
|
59
|
+
`.trim();
|
|
60
|
+
}
|
|
61
|
+
parseToolCalls(text) {
|
|
62
|
+
const actionMatch = text.match(/Action:\s*(\w+)/);
|
|
63
|
+
const argsMatch = text.match(/Arguments:\s*({[\s\S]*})/);
|
|
64
|
+
if (!actionMatch)
|
|
65
|
+
return [];
|
|
66
|
+
let args = {};
|
|
67
|
+
if (argsMatch) {
|
|
68
|
+
try {
|
|
69
|
+
// 强力解析逻辑,处理物理换行
|
|
70
|
+
const rawArgs = argsMatch[1].trim().replace(/\n/g, "\\n");
|
|
71
|
+
args = JSON.parse(rawArgs);
|
|
72
|
+
// 参数映射
|
|
73
|
+
const anyArgs = args;
|
|
74
|
+
if (anyArgs.file_path && !anyArgs.path)
|
|
75
|
+
anyArgs.path = anyArgs.file_path;
|
|
76
|
+
if (anyArgs.filePath && !anyArgs.path)
|
|
77
|
+
anyArgs.path = anyArgs.filePath;
|
|
78
|
+
if (anyArgs.path && !anyArgs.filePath)
|
|
79
|
+
anyArgs.filePath = anyArgs.path;
|
|
80
|
+
if (anyArgs.file && !anyArgs.filePath)
|
|
81
|
+
anyArgs.filePath = anyArgs.file;
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
console.warn("JSON Parse Error", e);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return [{
|
|
88
|
+
name: actionMatch[1],
|
|
89
|
+
args,
|
|
90
|
+
id: `call_${Date.now()}`,
|
|
91
|
+
type: "tool_call",
|
|
92
|
+
}];
|
|
93
|
+
}
|
|
94
|
+
_llmType() { return "agent_graph_model"; }
|
|
95
|
+
}
|
|
96
|
+
exports.AgentGraphModel = AgentGraphModel;
|
|
@@ -4,13 +4,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getFilesystemTools = void 0;
|
|
7
|
-
const zod_1 = require("zod");
|
|
8
7
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const minimatch_1 = require("minimatch");
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const zod_1 = require("zod");
|
|
10
11
|
const createTool_1 = require("../../utils/createTool");
|
|
11
|
-
const minimatch_1 = require("minimatch");
|
|
12
|
-
const lib_1 = require("./lib");
|
|
13
12
|
const jsonSchemaToZod_1 = require("../../utils/jsonSchemaToZod");
|
|
13
|
+
const lib_1 = require("./lib");
|
|
14
14
|
// Schema definitions
|
|
15
15
|
const ReadTextFileArgsSchema = zod_1.z.object({
|
|
16
16
|
path: zod_1.z.string(),
|
|
@@ -16,11 +16,11 @@ exports.applyFileEdits = applyFileEdits;
|
|
|
16
16
|
exports.tailFile = tailFile;
|
|
17
17
|
exports.headFile = headFile;
|
|
18
18
|
exports.searchFilesWithValidation = searchFilesWithValidation;
|
|
19
|
-
const promises_1 = __importDefault(require("fs/promises"));
|
|
20
|
-
const path_1 = __importDefault(require("path"));
|
|
21
19
|
const crypto_1 = require("crypto");
|
|
22
20
|
const diff_1 = require("diff");
|
|
21
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
23
22
|
const minimatch_1 = require("minimatch");
|
|
23
|
+
const path_1 = __importDefault(require("path"));
|
|
24
24
|
const path_utils_js_1 = require("./path-utils.js");
|
|
25
25
|
const path_validation_js_1 = require("./path-validation.js");
|
|
26
26
|
// Global allowed directories - set by the main module
|
|
@@ -6,8 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.convertToWindowsPath = convertToWindowsPath;
|
|
7
7
|
exports.normalizePath = normalizePath;
|
|
8
8
|
exports.expandHome = expandHome;
|
|
9
|
-
const path_1 = __importDefault(require("path"));
|
|
10
9
|
const os_1 = __importDefault(require("os"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
11
|
/**
|
|
12
12
|
* Converts WSL or Unix-style Windows paths to Windows format
|
|
13
13
|
* @param p The path to convert
|
|
@@ -5,8 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getValidRootDirectories = getValidRootDirectories;
|
|
7
7
|
const fs_1 = require("fs");
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
8
|
const os_1 = __importDefault(require("os"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const path_utils_js_1 = require("./path-utils.js");
|
|
11
11
|
/**
|
|
12
12
|
* Converts a root URI to a normalized directory path with basic security validation.
|
package/lib/types/type.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Client } from
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index';
|
|
2
2
|
export interface ApiConfig {
|
|
3
3
|
baseURL: string;
|
|
4
4
|
apiKey: string;
|
|
@@ -46,4 +46,16 @@ export interface AgentOptions {
|
|
|
46
46
|
* only for chain agent
|
|
47
47
|
*/
|
|
48
48
|
verbose?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* only for graph agent
|
|
51
|
+
*/
|
|
52
|
+
baseURL?: string;
|
|
53
|
+
/**
|
|
54
|
+
* only for graph agent
|
|
55
|
+
*/
|
|
56
|
+
apiKey?: string;
|
|
57
|
+
/**
|
|
58
|
+
* only for graph agent
|
|
59
|
+
*/
|
|
60
|
+
modelName?: string;
|
|
49
61
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.convertToLangChainTool = convertToLangChainTool;
|
|
4
|
+
const tools_1 = require("@langchain/core/tools");
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
function convertToLangChainTool(info) {
|
|
7
|
+
return new tools_1.DynamicStructuredTool({
|
|
8
|
+
name: info.function.name,
|
|
9
|
+
description: info.function.description || "",
|
|
10
|
+
schema: zod_1.z.record(zod_1.z.any()),
|
|
11
|
+
func: async (args) => {
|
|
12
|
+
if (info._handler)
|
|
13
|
+
return await info._handler(args);
|
|
14
|
+
if (info._client && info._originalName) {
|
|
15
|
+
const result = await info._client.callTool({
|
|
16
|
+
name: info._originalName,
|
|
17
|
+
arguments: args,
|
|
18
|
+
});
|
|
19
|
+
return JSON.stringify(result);
|
|
20
|
+
}
|
|
21
|
+
return "Error: No tool execution handler found.";
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saber2pr/ai-agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.20",
|
|
4
4
|
"description": "AI Assistant CLI.",
|
|
5
5
|
"author": "saber2pr",
|
|
6
6
|
"license": "ISC",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
],
|
|
10
10
|
"bin": {
|
|
11
11
|
"sagent": "./lib/cli.js",
|
|
12
|
-
"sagent-chain": "./lib/cli-chain.js"
|
|
12
|
+
"sagent-chain": "./lib/cli-chain.js",
|
|
13
|
+
"sagent-graph": "./lib/cli-graph.js"
|
|
13
14
|
},
|
|
14
15
|
"publishConfig": {
|
|
15
16
|
"access": "public",
|
|
@@ -22,18 +23,20 @@
|
|
|
22
23
|
"prepublishOnly": "tsc"
|
|
23
24
|
},
|
|
24
25
|
"dependencies": {
|
|
26
|
+
"@langchain/core": "0.3.39",
|
|
27
|
+
"@langchain/langgraph": "^0.2.39",
|
|
28
|
+
"@langchain/openai": "0.4.0",
|
|
25
29
|
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
26
30
|
"@saber2pr/ts-context-mcp": "^0.0.8",
|
|
27
31
|
"diff": "^8.0.3",
|
|
28
32
|
"glob": "^10.5.0",
|
|
29
33
|
"js-tiktoken": "^1.0.21",
|
|
34
|
+
"langchain": "0.3.15",
|
|
30
35
|
"minimatch": "^10.0.1",
|
|
31
36
|
"openai": "^6.16.0",
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"@langchain/openai": "0.4.0",
|
|
36
|
-
"zod": "3.23.8"
|
|
37
|
+
"ora": "^9.3.0",
|
|
38
|
+
"zod": "3.23.8",
|
|
39
|
+
"zod-to-json-schema": "3.23.2"
|
|
37
40
|
},
|
|
38
41
|
"resolutions": {
|
|
39
42
|
"@langchain/core": "0.3.39"
|