feat(tools): add search and map tools
This commit is contained in:
@@ -61,9 +61,12 @@ const isDevelopmentDebugLoggingEnabled = process.env.NODE_ENV === "development";
|
||||
|
||||
const toolLabels: Record<string, string> = {
|
||||
memory_manager: "记忆写入",
|
||||
geocode: "地理编码",
|
||||
session_search: "历史会话检索",
|
||||
skill_manager: "流程沉淀",
|
||||
web_search: "网页搜索",
|
||||
locate_features: "地图定位",
|
||||
zoom_to_map: "地图缩放",
|
||||
view_history: "历史数据面板",
|
||||
view_scada: "SCADA 面板",
|
||||
show_chart: "图表渲染",
|
||||
|
||||
+137
@@ -250,6 +250,143 @@ app.post("/internal/tools/session-search", async (req, res) => {
|
||||
});
|
||||
});
|
||||
|
||||
const callBackendJson = async (
|
||||
path: string,
|
||||
accessToken: string | undefined,
|
||||
payload: unknown,
|
||||
) => {
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), config.TJWATER_API_TIMEOUT_MS);
|
||||
try {
|
||||
const headers: Record<string, string> = {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
if (accessToken) {
|
||||
headers.Authorization = `Bearer ${accessToken}`;
|
||||
}
|
||||
const response = await fetch(new URL(path, config.TJWATER_API_BASE_URL), {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify(payload),
|
||||
signal: controller.signal,
|
||||
});
|
||||
const text = await response.text();
|
||||
return {
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
text,
|
||||
};
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
};
|
||||
|
||||
const parseStringArray = (value: unknown) =>
|
||||
Array.isArray(value)
|
||||
? value.filter((item): item is string => typeof item === "string")
|
||||
: undefined;
|
||||
|
||||
app.post("/internal/tools/web-search", async (req, res) => {
|
||||
if (req.header("x-agent-internal-token") !== internalToken) {
|
||||
res.status(403).json({ message: "forbidden" });
|
||||
return;
|
||||
}
|
||||
|
||||
const sessionId =
|
||||
typeof req.body?.session_id === "string" ? req.body.session_id.trim() : "";
|
||||
const context = sessionId ? getRuntimeSessionContext(sessionId) : null;
|
||||
if (!context) {
|
||||
res.status(404).json({
|
||||
message: "session context not found",
|
||||
detail: sessionId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const query = typeof req.body?.query === "string" ? req.body.query.trim() : "";
|
||||
if (!query) {
|
||||
res.status(400).json({ message: "query is required" });
|
||||
return;
|
||||
}
|
||||
|
||||
const count =
|
||||
typeof req.body?.count === "number" && Number.isFinite(req.body.count)
|
||||
? Math.trunc(req.body.count)
|
||||
: undefined;
|
||||
const payload = {
|
||||
query,
|
||||
freshness:
|
||||
typeof req.body?.freshness === "string" ? req.body.freshness : undefined,
|
||||
summary:
|
||||
typeof req.body?.summary === "boolean" ? req.body.summary : undefined,
|
||||
count,
|
||||
include: parseStringArray(req.body?.include),
|
||||
exclude: parseStringArray(req.body?.exclude),
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await callBackendJson(
|
||||
"/api/v1/web-search",
|
||||
context.accessToken,
|
||||
payload,
|
||||
);
|
||||
res
|
||||
.status(response.ok ? 200 : response.status)
|
||||
.type("application/json")
|
||||
.send(response.text);
|
||||
} catch (error) {
|
||||
const detail = error instanceof Error ? error.message : String(error);
|
||||
res.status(503).json({
|
||||
message: "web search service unavailable",
|
||||
detail,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/internal/tools/geocode", async (req, res) => {
|
||||
if (req.header("x-agent-internal-token") !== internalToken) {
|
||||
res.status(403).json({ message: "forbidden" });
|
||||
return;
|
||||
}
|
||||
|
||||
const sessionId =
|
||||
typeof req.body?.session_id === "string" ? req.body.session_id.trim() : "";
|
||||
const context = sessionId ? getRuntimeSessionContext(sessionId) : null;
|
||||
if (!context) {
|
||||
res.status(404).json({
|
||||
message: "session context not found",
|
||||
detail: sessionId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const keyword =
|
||||
typeof req.body?.keyword === "string" ? req.body.keyword.trim() : "";
|
||||
if (!keyword) {
|
||||
res.status(400).json({ message: "keyword is required" });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await callBackendJson(
|
||||
"/api/v1/tianditu/geocode",
|
||||
context.accessToken,
|
||||
{ keyword },
|
||||
);
|
||||
res
|
||||
.status(response.ok ? 200 : response.status)
|
||||
.type("application/json")
|
||||
.send(response.text);
|
||||
} catch (error) {
|
||||
const detail = error instanceof Error ? error.message : String(error);
|
||||
res.status(503).json({
|
||||
message: "geocoding service unavailable",
|
||||
detail,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.use(
|
||||
"/api/v1/agent/chat",
|
||||
buildChatRouter(
|
||||
|
||||
Reference in New Issue
Block a user