新增调用前端分区渲染功能,节点通过 ref 文件传输,并增加简单认证
This commit is contained in:
@@ -0,0 +1,16 @@
|
|||||||
|
import { tool } from "@opencode-ai/plugin";
|
||||||
|
|
||||||
|
export default tool({
|
||||||
|
description: "在前端地图上对 junctions 图层应用分区渲染。",
|
||||||
|
args: {
|
||||||
|
reason: tool.schema
|
||||||
|
.string()
|
||||||
|
.describe("Why this junction rendering action is needed for the user request."),
|
||||||
|
render_ref: tool.schema
|
||||||
|
.string()
|
||||||
|
.describe("Reference to a stored junction rendering payload resolved by the Agent service."),
|
||||||
|
},
|
||||||
|
async execute() {
|
||||||
|
return "已在地图上应用节点分区渲染。";
|
||||||
|
},
|
||||||
|
});
|
||||||
+25
-3
@@ -45,6 +45,7 @@ export type StoreResultInput = {
|
|||||||
|
|
||||||
export type RetrievalContext = {
|
export type RetrievalContext = {
|
||||||
actorKey: string;
|
actorKey: string;
|
||||||
|
clientSessionId?: string;
|
||||||
maxItems?: number;
|
maxItems?: number;
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
};
|
};
|
||||||
@@ -105,18 +106,33 @@ export class ResultReferenceStore {
|
|||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAuthorized(resultRef: string, context: RetrievalContext) {
|
async getAuthorized(resultRef: string, context: RetrievalContext) {
|
||||||
|
const record = await this.readAuthorizedRecord(resultRef, context);
|
||||||
|
if (!record) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const data = projectData(record.data, context.maxItems ?? config.RESULT_REF_MAX_RETRIEVAL_ITEMS);
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
result_ref: record.resultRef,
|
||||||
|
result_size_bytes: record.sizeBytes,
|
||||||
|
stored_at: record.createdAt,
|
||||||
|
data,
|
||||||
|
preview: record.preview,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFullAuthorized(resultRef: string, context: RetrievalContext) {
|
||||||
const record = await this.readAuthorizedRecord(resultRef, context);
|
const record = await this.readAuthorizedRecord(resultRef, context);
|
||||||
if (!record) {
|
if (!record) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const data = projectData(record.data, context.maxItems ?? config.RESULT_REF_MAX_RETRIEVAL_ITEMS);
|
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
result_ref: record.resultRef,
|
result_ref: record.resultRef,
|
||||||
result_size_bytes: record.sizeBytes,
|
result_size_bytes: record.sizeBytes,
|
||||||
stored_at: record.createdAt,
|
stored_at: record.createdAt,
|
||||||
data,
|
data: record.data,
|
||||||
preview: record.preview,
|
preview: record.preview,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -173,6 +189,12 @@ export class ResultReferenceStore {
|
|||||||
if ((record.projectId ?? "") !== (context.projectId ?? "")) {
|
if ((record.projectId ?? "") !== (context.projectId ?? "")) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
context.clientSessionId &&
|
||||||
|
record.clientSessionId !== context.clientSessionId
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ import { z } from "zod";
|
|||||||
import { type LearningOrchestrator } from "../learning/orchestrator.js";
|
import { type LearningOrchestrator } from "../learning/orchestrator.js";
|
||||||
import { logger } from "../logger.js";
|
import { logger } from "../logger.js";
|
||||||
import { MemoryStore } from "../memory/store.js";
|
import { MemoryStore } from "../memory/store.js";
|
||||||
|
import { type ResultReferenceStore } from "../results/store.js";
|
||||||
import { type OpencodeRuntimeAdapter } from "../runtime/opencode.js";
|
import { type OpencodeRuntimeAdapter } from "../runtime/opencode.js";
|
||||||
import { type ChatSessionBridge } from "../chat/sessionBridge.js";
|
import { type ChatSessionBridge } from "../chat/sessionBridge.js";
|
||||||
import { writeLlmRequestAuditLog } from "../audit/llmRequestAudit.js";
|
import { writeLlmRequestAuditLog } from "../audit/llmRequestAudit.js";
|
||||||
|
import { toActorKey } from "../utils/fileStore.js";
|
||||||
|
|
||||||
const supportedModels = [
|
const supportedModels = [
|
||||||
"deepseek/deepseek-v4-flash",
|
"deepseek/deepseek-v4-flash",
|
||||||
@@ -36,9 +38,47 @@ export const buildChatRouter = (
|
|||||||
runtime: OpencodeRuntimeAdapter,
|
runtime: OpencodeRuntimeAdapter,
|
||||||
memoryStore: MemoryStore,
|
memoryStore: MemoryStore,
|
||||||
learningOrchestrator: LearningOrchestrator,
|
learningOrchestrator: LearningOrchestrator,
|
||||||
|
resultReferenceStore: ResultReferenceStore,
|
||||||
) => {
|
) => {
|
||||||
const chatRouter = Router();
|
const chatRouter = Router();
|
||||||
|
|
||||||
|
chatRouter.get("/render-ref/:renderRef", async (req, res) => {
|
||||||
|
const renderRef = req.params.renderRef?.trim();
|
||||||
|
const userId = req.header("x-user-id")?.trim();
|
||||||
|
const projectId = req.header("x-project-id") ?? undefined;
|
||||||
|
const clientSessionId =
|
||||||
|
typeof req.query.session_id === "string"
|
||||||
|
? req.query.session_id.trim()
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
res.status(400).json({
|
||||||
|
message: "x-user-id is required",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!renderRef) {
|
||||||
|
res.status(400).json({
|
||||||
|
message: "render_ref is required",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await resultReferenceStore.getFullAuthorized(renderRef, {
|
||||||
|
actorKey: toActorKey(userId),
|
||||||
|
clientSessionId,
|
||||||
|
projectId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
res.status(404).json({ message: "render_ref not found" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(result);
|
||||||
|
});
|
||||||
|
|
||||||
chatRouter.post("/abort", async (req, res) => {
|
chatRouter.post("/abort", async (req, res) => {
|
||||||
const parsed = abortPayloadSchema.safeParse(req.body);
|
const parsed = abortPayloadSchema.safeParse(req.body);
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
@@ -1037,6 +1077,7 @@ const toolLabels: Record<string, string> = {
|
|||||||
view_history: "历史数据面板",
|
view_history: "历史数据面板",
|
||||||
view_scada: "SCADA 面板",
|
view_scada: "SCADA 面板",
|
||||||
show_chart: "图表渲染",
|
show_chart: "图表渲染",
|
||||||
|
render_junctions: "节点渲染",
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildPromptWithLearningContext = async (
|
const buildPromptWithLearningContext = async (
|
||||||
|
|||||||
+7
-1
@@ -172,7 +172,13 @@ app.post("/internal/tools/session-search", async (req, res) => {
|
|||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
"/api/v1/agent/chat",
|
"/api/v1/agent/chat",
|
||||||
buildChatRouter(sessionBridge, opencodeRuntime, memoryStore, learningOrchestrator),
|
buildChatRouter(
|
||||||
|
sessionBridge,
|
||||||
|
opencodeRuntime,
|
||||||
|
memoryStore,
|
||||||
|
learningOrchestrator,
|
||||||
|
resultReferenceStore,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const bootstrap = async () => {
|
const bootstrap = async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user