merlyn/server/endpoints/agentFlows.js
Sean Hatfield e5f3fb0892
Agent flow builder (#3077)
* wip agent builder

* refactor structure for agent builder

* improve ui for add block menu and sidebar

* lint

* node ui improvement

* handle deleting variable in all nodes

* add headers and body to apiCall node

* lint

* Agent flow builder backend (#3078)

* wip agent builder backend

* save/load agent tasks

* lint

* refactor agent task to use uuids instead of names

* placeholder for run task

* update frontend sidebar + seperate backend to agent-tasks utils

* lint

* add deleting of agent tasks

* create AgentTasks class + wip load agent tasks into aibitat

* lint

* inject + call agent tasks

* wip call agent tasks

* add llm instruction + fix api calling blocks

* add ui + backend for editing/toggling agent tasks

* lint

* add back middlewares

* disable run task + add navigate to home on logo click

* implement normalizePath to prevent path traversal

* wip make api calling more consistent

* lint

* rename all references from task to flow

* patch load flow bug when on editing page

* remove unneeded files/comments

* lint

* fix delete endpoint + rename load flows

* add move block to ui + fix api-call backend + add telemetry

* lint

* add web scraping block

* only allow admin for agent builder

---------

Co-authored-by: timothycarambat <rambat1010@gmail.com>

* Move AgentFlowManager flows to static
simplify UI states
Handle LLM prompt flow when provided non-string

* delete/edit menu for agent flow panel + update flow icon

* lint

* fix open builder button hidden bug

* add tooltips to move up/down block buttons

* add tooltip to delete block

* truncate block description to fit on blocklist component

* light mode agent builder sidebar

* light mode api call block

* fix light mode styles for agent builder blocks

* agent flow fetch in UI

* sync delete flow

* agent flow ui/ux improvements

* remove unused AgentSidebar component

* comment out /run

* UI changes and updates for flow builder

* format flow panel info

* update link handling

* ui tweaks to header menu

* remove unused import

* update doc links
update block icons

* bump readme

* Patch code block header oddity
resolves #3117

* bump dev image

---------

Co-authored-by: Timothy Carambat <rambat1010@gmail.com>
2025-02-12 16:50:43 -08:00

203 lines
5.3 KiB
JavaScript

const { AgentFlows } = require("../utils/agentFlows");
const {
flexUserRoleValid,
ROLES,
} = require("../utils/middleware/multiUserProtected");
const { validatedRequest } = require("../utils/middleware/validatedRequest");
const { Telemetry } = require("../models/telemetry");
function agentFlowEndpoints(app) {
if (!app) return;
// Save a flow configuration
app.post(
"/agent-flows/save",
[validatedRequest, flexUserRoleValid([ROLES.admin])],
async (request, response) => {
try {
const { name, config, uuid } = request.body;
if (!name || !config) {
return response.status(400).json({
success: false,
error: "Name and config are required",
});
}
const flow = AgentFlows.saveFlow(name, config, uuid);
if (!flow) {
return response.status(500).json({
success: false,
error: "Failed to save flow",
});
}
if (!uuid) {
await Telemetry.sendTelemetry("agent_flow_created", {
blockCount: config.blocks?.length || 0,
});
}
return response.status(200).json({
success: true,
flow,
});
} catch (error) {
console.error("Error saving flow:", error);
return response.status(500).json({
success: false,
error: error.message,
});
}
}
);
// List all available flows
app.get(
"/agent-flows/list",
[validatedRequest, flexUserRoleValid([ROLES.admin])],
async (_request, response) => {
try {
const flows = AgentFlows.listFlows();
return response.status(200).json({
success: true,
flows,
});
} catch (error) {
console.error("Error listing flows:", error);
return response.status(500).json({
success: false,
error: error.message,
});
}
}
);
// Get a specific flow by UUID
app.get(
"/agent-flows/:uuid",
[validatedRequest, flexUserRoleValid([ROLES.admin])],
async (request, response) => {
try {
const { uuid } = request.params;
const flow = AgentFlows.loadFlow(uuid);
if (!flow) {
return response.status(404).json({
success: false,
error: "Flow not found",
});
}
return response.status(200).json({
success: true,
flow,
});
} catch (error) {
console.error("Error getting flow:", error);
return response.status(500).json({
success: false,
error: error.message,
});
}
}
);
// Run a specific flow
// app.post(
// "/agent-flows/:uuid/run",
// [validatedRequest, flexUserRoleValid([ROLES.admin])],
// async (request, response) => {
// try {
// const { uuid } = request.params;
// const { variables = {} } = request.body;
// // TODO: Implement flow execution
// console.log("Running flow with UUID:", uuid);
// await Telemetry.sendTelemetry("agent_flow_executed", {
// variableCount: Object.keys(variables).length,
// });
// return response.status(200).json({
// success: true,
// results: {
// success: true,
// results: "test",
// variables: variables,
// },
// });
// } catch (error) {
// console.error("Error running flow:", error);
// return response.status(500).json({
// success: false,
// error: error.message,
// });
// }
// }
// );
// Delete a specific flow
app.delete(
"/agent-flows/:uuid",
[validatedRequest, flexUserRoleValid([ROLES.admin])],
async (request, response) => {
try {
const { uuid } = request.params;
const { success } = AgentFlows.deleteFlow(uuid);
if (!success) {
return response.status(500).json({
success: false,
error: "Failed to delete flow",
});
}
return response.status(200).json({
success,
});
} catch (error) {
console.error("Error deleting flow:", error);
return response.status(500).json({
success: false,
error: error.message,
});
}
}
);
// Toggle flow active status
app.post(
"/agent-flows/:uuid/toggle",
[validatedRequest, flexUserRoleValid([ROLES.admin])],
async (request, response) => {
try {
const { uuid } = request.params;
const { active } = request.body;
const flow = AgentFlows.loadFlow(uuid);
if (!flow) {
return response
.status(404)
.json({ success: false, error: "Flow not found" });
}
flow.config.active = active;
const { success } = AgentFlows.saveFlow(flow.name, flow.config, uuid);
if (!success) {
return response
.status(500)
.json({ success: false, error: "Failed to update flow" });
}
return response.json({ success: true, flow });
} catch (error) {
console.error("Error toggling flow:", error);
response.status(500).json({ success: false, error: error.message });
}
}
);
}
module.exports = { agentFlowEndpoints };