添加注释以说明工具和会话的上下文处理
This commit is contained in:
@@ -18,6 +18,7 @@ export default tool({
|
|||||||
.describe("Query arguments object."),
|
.describe("Query arguments object."),
|
||||||
},
|
},
|
||||||
async execute(args, context) {
|
async execute(args, context) {
|
||||||
|
// 工具本身不直接持有用户 token;通过 sessionID 回调 Agent 服务,由服务侧补齐用户上下文。
|
||||||
const response = await fetch(`${internalBaseUrl}/internal/tools/dynamic-http-call`, {
|
const response = await fetch(`${internalBaseUrl}/internal/tools/dynamic-http-call`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export default tool({
|
|||||||
.describe("Type of feature to locate."),
|
.describe("Type of feature to locate."),
|
||||||
},
|
},
|
||||||
async execute() {
|
async execute() {
|
||||||
|
// 前端工具只负责生成 tool part,真正的地图动作由 Agent SSE 适配层转发给浏览器执行。
|
||||||
return "已在地图上定位到指定要素。";
|
return "已在地图上定位到指定要素。";
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export default tool({
|
|||||||
y_axis_name: tool.schema.string().optional().describe("Y-axis display name."),
|
y_axis_name: tool.schema.string().optional().describe("Y-axis display name."),
|
||||||
},
|
},
|
||||||
async execute() {
|
async execute() {
|
||||||
|
// 图表数据已经在工具参数里,前端收到 tool_call 后直接渲染,不再二次请求后端。
|
||||||
return "图表将在对话中显示。";
|
return "图表将在对话中显示。";
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export default tool({
|
|||||||
end_time: tool.schema.string().optional().describe("Optional ISO8601 end time."),
|
end_time: tool.schema.string().optional().describe("Optional ISO8601 end time."),
|
||||||
},
|
},
|
||||||
async execute() {
|
async execute() {
|
||||||
|
// 返回短确认即可;面板打开动作由前端根据 tool_call 参数完成。
|
||||||
return "已打开计算结果面板。";
|
return "已打开计算结果面板。";
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export default tool({
|
|||||||
end_time: tool.schema.string().optional().describe("Optional ISO8601 end time."),
|
end_time: tool.schema.string().optional().describe("Optional ISO8601 end time."),
|
||||||
},
|
},
|
||||||
async execute() {
|
async execute() {
|
||||||
|
// SCADA 面板仍在浏览器侧执行,工具结果不承载实际监测数据。
|
||||||
return "已打开 SCADA 监测面板。";
|
return "已打开 SCADA 监测面板。";
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Vendored
+1
@@ -1,4 +1,5 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
// 统一在启动时解析环境变量,避免业务代码里散落字符串默认值。
|
||||||
const envSchema = z.object({
|
const envSchema = z.object({
|
||||||
NODE_ENV: z.string().default("development"),
|
NODE_ENV: z.string().default("development"),
|
||||||
PORT: z.coerce.number().int().positive().default(8788),
|
PORT: z.coerce.number().int().positive().default(8788),
|
||||||
|
|||||||
Vendored
+3
@@ -12,6 +12,7 @@ const app = express();
|
|||||||
const registry = new SessionRegistry(config.SESSION_TTL_SECONDS);
|
const registry = new SessionRegistry(config.SESSION_TTL_SECONDS);
|
||||||
const sessionBridge = new ChatSessionBridge(registry, opencodeRuntime);
|
const sessionBridge = new ChatSessionBridge(registry, opencodeRuntime);
|
||||||
const internalToken = config.AGENT_INTERNAL_TOKEN ?? randomUUID();
|
const internalToken = config.AGENT_INTERNAL_TOKEN ?? randomUUID();
|
||||||
|
// 这个 token 只用于 .opencode/tools 回调本服务,避免把 internal endpoint 暴露成无鉴权入口。
|
||||||
process.env.TJWATER_AGENT_INTERNAL_TOKEN = internalToken;
|
process.env.TJWATER_AGENT_INTERNAL_TOKEN = internalToken;
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(express.json({ limit: "1mb" }));
|
app.use(express.json({ limit: "1mb" }));
|
||||||
@@ -49,6 +50,7 @@ app.post("/internal/tools/dynamic-http-call", async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
// opencode 工具运行在 .opencode 侧,这里负责把工具调用重新绑定到当前用户/项目上下文。
|
||||||
const result = await dynamicHttpExecutor.execute({
|
const result = await dynamicHttpExecutor.execute({
|
||||||
path: req.body?.path,
|
path: req.body?.path,
|
||||||
method: req.body?.method,
|
method: req.body?.method,
|
||||||
@@ -71,6 +73,7 @@ const server = app.listen(config.PORT, config.HOST, () => {
|
|||||||
const shutdown = async () => {
|
const shutdown = async () => {
|
||||||
logger.info("shutting down TJWaterAgent");
|
logger.info("shutting down TJWaterAgent");
|
||||||
server.close();
|
server.close();
|
||||||
|
// 同步关闭 embedded opencode server,避免本服务退出后留下孤儿进程。
|
||||||
await opencodeRuntime.dispose();
|
await opencodeRuntime.dispose();
|
||||||
};
|
};
|
||||||
process.on("SIGINT", () => {
|
process.on("SIGINT", () => {
|
||||||
|
|||||||
Vendored
+1
@@ -43,6 +43,7 @@ export class SessionRegistry {
|
|||||||
return expired;
|
return expired;
|
||||||
}
|
}
|
||||||
makeKey(context) {
|
makeKey(context) {
|
||||||
|
// 会话隔离不能只看 conversationId;同一浏览器会话切换用户或项目时必须映射到不同 opencode session。
|
||||||
const digest = crypto
|
const digest = crypto
|
||||||
.createHash("sha256")
|
.createHash("sha256")
|
||||||
.update([
|
.update([
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// 统一在启动时解析环境变量,避免业务代码里散落字符串默认值。
|
||||||
const envSchema = z.object({
|
const envSchema = z.object({
|
||||||
NODE_ENV: z.string().default("development"),
|
NODE_ENV: z.string().default("development"),
|
||||||
PORT: z.coerce.number().int().positive().default(8788),
|
PORT: z.coerce.number().int().positive().default(8788),
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const registry = new SessionRegistry(config.SESSION_TTL_SECONDS);
|
|||||||
const sessionBridge = new ChatSessionBridge(registry, opencodeRuntime);
|
const sessionBridge = new ChatSessionBridge(registry, opencodeRuntime);
|
||||||
const internalToken = config.AGENT_INTERNAL_TOKEN ?? randomUUID();
|
const internalToken = config.AGENT_INTERNAL_TOKEN ?? randomUUID();
|
||||||
|
|
||||||
|
// 这个 token 只用于 .opencode/tools 回调本服务,避免把 internal endpoint 暴露成无鉴权入口。
|
||||||
process.env.TJWATER_AGENT_INTERNAL_TOKEN = internalToken;
|
process.env.TJWATER_AGENT_INTERNAL_TOKEN = internalToken;
|
||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
@@ -57,6 +58,7 @@ app.post("/internal/tools/dynamic-http-call", async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// opencode 工具运行在 .opencode 侧,这里负责把工具调用重新绑定到当前用户/项目上下文。
|
||||||
const result = await dynamicHttpExecutor.execute(
|
const result = await dynamicHttpExecutor.execute(
|
||||||
{
|
{
|
||||||
path: req.body?.path,
|
path: req.body?.path,
|
||||||
@@ -87,6 +89,7 @@ const server = app.listen(config.PORT, config.HOST, () => {
|
|||||||
const shutdown = async () => {
|
const shutdown = async () => {
|
||||||
logger.info("shutting down TJWaterAgent");
|
logger.info("shutting down TJWaterAgent");
|
||||||
server.close();
|
server.close();
|
||||||
|
// 同步关闭 embedded opencode server,避免本服务退出后留下孤儿进程。
|
||||||
await opencodeRuntime.dispose();
|
await opencodeRuntime.dispose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ export class SessionRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private makeKey(context: SessionContext): string {
|
private makeKey(context: SessionContext): string {
|
||||||
|
// 会话隔离不能只看 conversationId;同一浏览器会话切换用户或项目时必须映射到不同 opencode session。
|
||||||
const digest = crypto
|
const digest = crypto
|
||||||
.createHash("sha256")
|
.createHash("sha256")
|
||||||
.update(
|
.update(
|
||||||
|
|||||||
Reference in New Issue
Block a user