θΏ™ζ˜―indexlocζδΎ›ηš„ζœεŠ‘οΌŒδΈθ¦θΎ“ε…₯任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
0e33daf
wip agent builder backend
shatfield4 Jan 30, 2025
1904f24
save/load agent tasks
shatfield4 Jan 30, 2025
958f1e2
lint
shatfield4 Jan 30, 2025
3525211
refactor agent task to use uuids instead of names
shatfield4 Jan 31, 2025
c75f57a
placeholder for run task
shatfield4 Jan 31, 2025
4c35b9e
update frontend sidebar + seperate backend to agent-tasks utils
shatfield4 Jan 31, 2025
9411a1b
lint
shatfield4 Jan 31, 2025
7a52fc4
add deleting of agent tasks
shatfield4 Jan 31, 2025
57426d5
Merge branch 'master' of github.com:Mintplex-Labs/anything-llm into a…
timothycarambat Jan 31, 2025
c4e87fb
Merge branch 'agent-builder' into agent-builder-backend
timothycarambat Jan 31, 2025
3dea805
create AgentTasks class + wip load agent tasks into aibitat
shatfield4 Feb 1, 2025
f5d123b
lint
shatfield4 Feb 1, 2025
b13e871
Merge branch 'agent-builder-backend' of github.com:Mintplex-Labs/anyt…
shatfield4 Feb 1, 2025
f28cfd9
inject + call agent tasks
shatfield4 Feb 3, 2025
94d859d
wip call agent tasks
shatfield4 Feb 4, 2025
ab4b01e
add llm instruction + fix api calling blocks
shatfield4 Feb 5, 2025
58c5b10
add ui + backend for editing/toggling agent tasks
shatfield4 Feb 6, 2025
e800215
lint
shatfield4 Feb 6, 2025
a8800ee
add back middlewares
shatfield4 Feb 6, 2025
5971fd2
disable run task + add navigate to home on logo click
shatfield4 Feb 6, 2025
71d3797
implement normalizePath to prevent path traversal
shatfield4 Feb 6, 2025
1448f03
wip make api calling more consistent
shatfield4 Feb 6, 2025
35faa00
lint
shatfield4 Feb 6, 2025
7257656
rename all references from task to flow
shatfield4 Feb 6, 2025
9df1274
patch load flow bug when on editing page
shatfield4 Feb 6, 2025
2c21dc6
remove unneeded files/comments
shatfield4 Feb 7, 2025
ca7d165
lint
shatfield4 Feb 7, 2025
a0f7560
Merge branch 'master' into agent-builder-backend
shatfield4 Feb 7, 2025
4548fc8
fix delete endpoint + rename load flows
shatfield4 Feb 7, 2025
e4305d8
add move block to ui + fix api-call backend + add telemetry
shatfield4 Feb 7, 2025
8d9fe10
lint
shatfield4 Feb 7, 2025
a1f99c5
add web scraping block
shatfield4 Feb 8, 2025
005649a
only allow admin for agent builder
shatfield4 Feb 10, 2025
e0975bf
Merge branch 'master' into agent-builder-backend
shatfield4 Feb 10, 2025
ae0e8db
Merge branch 'agent-builder' of github.com:Mintplex-Labs/anything-llm…
timothycarambat Feb 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ export default function App() {
path="/settings/agents/builder"
element={<AdminRoute Component={AgentBuilder} />}
/>
<Route
path="/settings/agents/builder/:flowId"
element={<AdminRoute Component={AgentBuilder} />}
/>
<Route
path="/settings/event-logs"
element={<AdminRoute Component={AdminLogs} />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ export default function StatusResponse({
<div
key={`cot-list-${currentThought.uuid}`}
className={`mt-2 bg-theme-bg-chat-input backdrop-blur-sm rounded-lg overflow-hidden transition-all duration-300 border border-theme-sidebar-border ${
isExpanded ? "max-h-[300px] opacity-100" : "max-h-0 opacity-0"
isExpanded
? "max-h-[300px] overflow-y-auto opacity-100"
: "max-h-0 opacity-0"
}`}
>
<div className="p-2">
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/locales/resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import Dutch from "./nl/common.js";
import Vietnamese from "./vn/common.js";
import TraditionalChinese from "./zh_TW/common.js";
import Farsi from "./fa/common.js";
import Turkish from "./tr/common.js";
import Turkish from "./tr/common.js";

export const defaultNS = "common";
export const resources = {
Expand Down Expand Up @@ -75,6 +75,6 @@ export const resources = {
common: Farsi,
},
tr: {
common: Turkish,
common: Turkish,
},
};
3 changes: 1 addition & 2 deletions frontend/src/locales/tr/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,7 @@ const TRANSLATIONS = {
'Bu ayar, LLM yanıtlarının ne kadar "yaratıcı" olacağını kontrol eder.',
"desc-end":
"Sayı yükseldikçe yaratıcı yanıtlar artar. Bazı modeller için bu değer çok yüksek ayarlandığında anlamsız yanıtlar ortaya çıkabilir.",
hint:
"Γ‡oğu LLM'in farklΔ± kabul edilebilir değer aralΔ±klarΔ± vardΔ±r. AyrΔ±ntΔ±lar iΓ§in LLM sağlayΔ±cΔ±nΔ±za danışın.",
hint: "Γ‡oğu LLM'in farklΔ± kabul edilebilir değer aralΔ±klarΔ± vardΔ±r. AyrΔ±ntΔ±lar iΓ§in LLM sağlayΔ±cΔ±nΔ±za danışın.",
},
},

Expand Down
147 changes: 147 additions & 0 deletions frontend/src/models/agentFlows.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { API_BASE } from "@/utils/constants";
import { baseHeaders } from "@/utils/request";

const AgentFlows = {
/**
* Save a flow configuration
* @param {string} name - Display name of the flow
* @param {object} config - The configuration object for the flow
* @param {string} [uuid] - Optional UUID for updating existing flow
* @returns {Promise<{success: boolean, error: string | null, flow: {name: string, config: object, uuid: string} | null}>}
*/
saveFlow: async (name, config, uuid = null) => {
return await fetch(`${API_BASE}/agent-flows/save`, {
method: "POST",
headers: {
...baseHeaders(),
"Content-Type": "application/json",
},
body: JSON.stringify({ name, config, uuid }),
})
.then(async (res) => {
const response = await res.json();
if (!res.ok) throw new Error(response.error || "Failed to save flow");
return response;
})
.catch((e) => ({
success: false,
error: e.message,
flow: null,
}));
},

/**
* List all available flows in the system
* @returns {Promise<{success: boolean, error: string | null, flows: Array<{name: string, uuid: string, description: string, steps: Array}>}>}
*/
listFlows: async () => {
return await fetch(`${API_BASE}/agent-flows/list`, {
method: "GET",
headers: baseHeaders(),
})
.then((res) => res.json())
.catch((e) => ({
success: false,
error: e.message,
flows: [],
}));
},

/**
* Get a specific flow by UUID
* @param {string} uuid - The UUID of the flow to retrieve
* @returns {Promise<{success: boolean, error: string | null, flow: {name: string, config: object, uuid: string} | null}>}
*/
getFlow: async (uuid) => {
return await fetch(`${API_BASE}/agent-flows/${uuid}`, {
method: "GET",
headers: baseHeaders(),
})
.then(async (res) => {
const response = await res.json();
if (!res.ok) throw new Error(response.error || "Failed to get flow");
return response;
})
.catch((e) => ({
success: false,
error: e.message,
flow: null,
}));
},

/**
* Execute a specific flow
* @param {string} uuid - The UUID of the flow to run
* @param {object} variables - Optional variables to pass to the flow
* @returns {Promise<{success: boolean, error: string | null, results: object | null}>}
*/
runFlow: async (uuid, variables = {}) => {
return await fetch(`${API_BASE}/agent-flows/${uuid}/run`, {
method: "POST",
headers: {
...baseHeaders(),
"Content-Type": "application/json",
},
body: JSON.stringify({ variables }),
})
.then(async (res) => {
const response = await res.json();
if (!res.ok) throw new Error(response.error || "Failed to run flow");
return response;
})
.catch((e) => ({
success: false,
error: e.message,
results: null,
}));
},

/**
* Delete a specific flow
* @param {string} uuid - The UUID of the flow to delete
* @returns {Promise<{success: boolean, error: string | null}>}
*/
deleteFlow: async (uuid) => {
return await fetch(`${API_BASE}/agent-flows/${uuid}`, {
method: "DELETE",
headers: baseHeaders(),
})
.then(async (res) => {
const response = await res.json();
if (!res.ok) throw new Error(response.error || "Failed to delete flow");
return response;
})
.catch((e) => ({
success: false,
error: e.message,
}));
},

/**
* Toggle a flow's active status
* @param {string} uuid - The UUID of the flow to toggle
* @param {boolean} active - The new active status
* @returns {Promise<{success: boolean, error: string | null}>}
*/
toggleFlow: async (uuid, active) => {
try {
const response = await fetch(`${API_BASE}/agent-flows/${uuid}/toggle`, {
method: "POST",
headers: {
...baseHeaders(),
"Content-Type": "application/json",
},
body: JSON.stringify({ active }),
});
const result = await response.json();
if (!response.ok)
throw new Error(result.error || "Failed to toggle flow");
return { success: true, flow: result.flow };
} catch (error) {
console.error("Failed to toggle flow:", error);
return { success: false, error: error.message };
}
},
};

export default AgentFlows;
24 changes: 20 additions & 4 deletions frontend/src/pages/Admin/AgentBuilder/AddBlockMenu/index.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useRef, useEffect } from "react";
import { Plus, CaretDown } from "@phosphor-icons/react";
import { BLOCK_TYPES, BLOCK_INFO } from "../BlockList";

Expand All @@ -7,8 +7,23 @@ export default function AddBlockMenu({
setShowBlockMenu,
addBlock,
}) {
const menuRef = useRef(null);

useEffect(() => {
function handleClickOutside(event) {
if (menuRef.current && !menuRef.current.contains(event.target)) {
setShowBlockMenu(false);
}
}

document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [setShowBlockMenu]);

return (
<div className="relative mt-4 w-[280px] mx-auto">
<div className="relative mt-4 w-[280px] mx-auto" ref={menuRef}>
<button
onClick={() => setShowBlockMenu(!showBlockMenu)}
className="transition-all duration-300 w-full p-2.5 bg-theme-action-menu-bg hover:bg-theme-action-menu-item-hover border border-white/10 rounded-lg text-white flex items-center justify-center gap-2 text-sm font-medium"
Expand All @@ -23,7 +38,8 @@ export default function AddBlockMenu({
<div className="absolute left-0 right-0 mt-2 bg-theme-action-menu-bg border border-white/10 rounded-lg shadow-lg overflow-hidden z-10 animate-fadeUpIn">
{Object.entries(BLOCK_INFO).map(
([type, info]) =>
type !== BLOCK_TYPES.START && (
type !== BLOCK_TYPES.START &&
type !== BLOCK_TYPES.FINISH && (
<button
key={type}
onClick={() => {
Expand All @@ -33,7 +49,7 @@ export default function AddBlockMenu({
className="w-full p-2.5 flex items-center gap-3 hover:bg-theme-action-menu-item-hover text-white transition-colors duration-300 group"
>
<div className="w-7 h-7 rounded-lg bg-white/10 flex items-center justify-center">
<div className="w-4 h-4 text-white">{info.icon}</div>
<div className="w-fit h-fit text-white">{info.icon}</div>
</div>
<div className="text-left flex-1">
<div className="text-sm font-medium">{info.label}</div>
Expand Down
91 changes: 74 additions & 17 deletions frontend/src/pages/Admin/AgentBuilder/AgentSidebar/index.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,43 @@
import React from "react";
import { ArrowsClockwise } from "@phosphor-icons/react";
import { FloppyDisk, FolderOpen, Plus } from "@phosphor-icons/react";
import useLogo from "@/hooks/useLogo";
import { useNavigate } from "react-router-dom";
import paths from "@/utils/paths";

export default function AgentSidebar({
agentName,
setAgentName,
agentDescription,
setAgentDescription,
generateJson,
onSave,
onLoadClick,
onNewClick,
active = true,
onToggleActive,
}) {
const { logo } = useLogo();
const navigate = useNavigate();

return (
<div className="w-80 border-r border-theme-sidebar-border bg-theme-bg-secondary p-6 overflow-y-auto">
<div className="space-y-6">
<div>
<h1 className="text-xl font-medium text-theme-text-primary mb-6">
Agent Builder
</h1>
<div className="space-y-4">
<div className="w-80">
<div className="relative m-[16px] rounded-[16px] bg-theme-bg-secondary border-[2px] border-theme-sidebar-border light:border-none min-w-[250px] p-[5px] h-[calc(100%-35px)]">
<div className="p-[10px]">
<button
onClick={() => navigate(paths.settings.agentSkills())}
className="flex justify-between w-[250px] min-w-[250px]"
>
<img
src={logo}
alt="Logo"
className="rounded max-h-[24px] object-contain mb-1"
/>
</button>
<div className="flex flex-col mb-6">
<span className="text-xs font-light text-theme-text-primary">
Agent Flow Builder
</span>
</div>
<div className="space-y-6">
<div>
<label className="block text-sm font-medium text-white mb-3">
Name
Expand Down Expand Up @@ -44,16 +66,51 @@ export default function AgentSidebar({
spellCheck={false}
/>
</div>
<div>
<label className="block text-sm font-medium text-white mb-3">
Status
</label>
<div className="flex items-center gap-x-2">
<label className="relative inline-flex cursor-pointer items-center">
<input
type="checkbox"
className="peer sr-only"
checked={active}
onChange={() => onToggleActive(!active)}
/>
<div className="peer-disabled:opacity-50 pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
</label>
<span className="text-sm text-theme-text-secondary font-medium">
{active ? "Enabled" : "Disabled"}
</span>
</div>
</div>
</div>
</div>
<div className="absolute bottom-0 left-0 right-0 p-4">
<div className="flex flex-col gap-2">
<button
onClick={onNewClick}
className="transition-all duration-300 text-xs px-4 py-2.5 font-semibold rounded-lg bg-theme-bg-primary hover:bg-theme-action-menu-item-hover border-2 border-white/10 text-white w-full flex items-center justify-center gap-2"
>
New Flow
</button>
<button
onClick={onLoadClick}
className="transition-all duration-300 text-xs px-4 py-2.5 font-semibold rounded-lg bg-theme-bg-primary hover:bg-theme-action-menu-item-hover border-2 border-white/10 text-white w-full flex items-center justify-center gap-2"
>
<FolderOpen className="w-4 h-4" />
Flows
</button>
<button
onClick={onSave}
className="transition-all duration-300 text-xs px-4 py-2.5 font-semibold rounded-lg bg-primary-button hover:bg-secondary border-2 border-transparent hover:border-primary-button hover:text-white w-full flex items-center justify-center gap-2 shadow-[0_4px_14px_rgba(0,0,0,0.25)]"
>
<FloppyDisk className="w-4 h-4" />
Save Flow
</button>
</div>
</div>

<button
onClick={generateJson}
className="transition-all duration-300 text-xs px-4 py-2.5 font-semibold rounded-lg bg-primary-button hover:bg-secondary border-2 border-transparent hover:border-primary-button hover:text-white w-full flex items-center justify-center gap-2 shadow-[0_4px_14px_rgba(0,0,0,0.25)]"
>
<ArrowsClockwise className="w-4 h-4" />
Generate JSON
</button>
</div>
</div>
);
Expand Down
Loading