θΏ™ζ˜―indexlocζδΎ›ηš„ζœεŠ‘οΌŒδΈθ¦θΎ“ε…₯任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
d5e5f01
wip agent builder
shatfield4 Jan 28, 2025
a38f062
refactor structure for agent builder
shatfield4 Jan 28, 2025
60c6fb0
Merge branch 'master' into agent-builder
shatfield4 Jan 28, 2025
a4fb36c
improve ui for add block menu and sidebar
shatfield4 Jan 29, 2025
31236fc
lint
shatfield4 Jan 29, 2025
eeaefc2
node ui improvement
shatfield4 Jan 29, 2025
d5d3a45
handle deleting variable in all nodes
shatfield4 Jan 29, 2025
bad9248
add headers and body to apiCall node
shatfield4 Jan 29, 2025
63caded
lint
shatfield4 Jan 29, 2025
4c50141
Merge branch 'master' into agent-builder
timothycarambat Jan 31, 2025
09e69c2
Merge branch 'master' into agent-builder
shatfield4 Feb 10, 2025
3adf9d8
Agent flow builder backend (#3078)
shatfield4 Feb 10, 2025
f12f33b
Merge branch 'master' of github.com:Mintplex-Labs/anything-llm into a…
timothycarambat Feb 10, 2025
60ecff5
Move AgentFlowManager flows to static
timothycarambat Feb 10, 2025
5188572
delete/edit menu for agent flow panel + update flow icon
shatfield4 Feb 10, 2025
8e115db
lint
shatfield4 Feb 10, 2025
b0c8ca6
fix open builder button hidden bug
shatfield4 Feb 10, 2025
5cea5be
add tooltips to move up/down block buttons
shatfield4 Feb 11, 2025
b8cf704
add tooltip to delete block
shatfield4 Feb 11, 2025
b13d09f
truncate block description to fit on blocklist component
shatfield4 Feb 11, 2025
5c4aa5a
light mode agent builder sidebar
shatfield4 Feb 11, 2025
388d9e9
light mode api call block
shatfield4 Feb 11, 2025
251e33a
fix light mode styles for agent builder blocks
shatfield4 Feb 11, 2025
583ce84
agent flow fetch in UI
timothycarambat Feb 11, 2025
a1590bb
sync delete flow
timothycarambat Feb 11, 2025
a6e19c2
agent flow ui/ux improvements
shatfield4 Feb 12, 2025
dc2387e
Merge branch 'agent-builder' of github.com:Mintplex-Labs/anything-llm…
shatfield4 Feb 12, 2025
0489bde
remove unused AgentSidebar component
shatfield4 Feb 12, 2025
2364f66
Merge branch 'master' of github.com:Mintplex-Labs/anything-llm into a…
timothycarambat Feb 12, 2025
b2f972c
comment out /run
timothycarambat Feb 12, 2025
fa00277
UI changes and updates for flow builder
timothycarambat Feb 12, 2025
657657f
format flow panel info
timothycarambat Feb 12, 2025
21bb044
update link handling
timothycarambat Feb 12, 2025
6b62fec
ui tweaks to header menu
shatfield4 Feb 12, 2025
663ba89
Merge branch 'agent-builder' of github.com:Mintplex-Labs/anything-llm…
shatfield4 Feb 12, 2025
835b406
remove unused import
shatfield4 Feb 12, 2025
e3bd716
update doc links
timothycarambat Feb 12, 2025
381cbe4
doc link
timothycarambat Feb 13, 2025
a0a98b0
bump readme
timothycarambat Feb 13, 2025
53efb6c
Patch code block header oddity
timothycarambat Feb 13, 2025
e992d26
Merge branch 'master' of github.com:Mintplex-Labs/anything-llm into a…
timothycarambat Feb 13, 2025
e2e3f89
bump dev image
timothycarambat Feb 13, 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
2 changes: 1 addition & 1 deletion .github/workflows/dev-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ concurrency:

on:
push:
branches: ['3069-tokenizer-collector-improvements'] # put your current branch to create a build. Core team only.
branches: ['agent-builder'] # put your current branch to create a build. Core team only.
paths-ignore:
- '**.md'
- 'cloud-deployments/*'
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,10 @@ AnythingLLM divides your documents into objects called `workspaces`. A Workspace
## Cool features of AnythingLLM

- πŸ†• [**Custom AI Agents**](https://docs.anythingllm.com/agent/custom/introduction)
- πŸ†• [**No-code AI Agent builder**](https://docs.anythingllm.com/agent-flows/overview)
- πŸ–ΌοΈ **Multi-modal support (both closed and open-source LLMs!)**
- πŸ‘€ Multi-user instance support and permissioning _Docker version only_
- 🦾 Agents inside your workspace (browse the web, run code, etc)
- 🦾 Agents inside your workspace (browse the web, etc)
- πŸ’¬ [Custom Embeddable Chat widget for your website](./embed/README.md) _Docker version only_
- πŸ“– Multiple document type support (PDF, TXT, DOCX, etc)
- Simple chat UI with Drag-n-Drop funcitonality and clear citations.
Expand Down
19 changes: 19 additions & 0 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const ExperimentalFeatures = lazy(
const LiveDocumentSyncManage = lazy(
() => import("@/pages/Admin/ExperimentalFeatures/Features/LiveSync/manage")
);
const AgentBuilder = lazy(() => import("@/pages/Admin/AgentBuilder"));

const CommunityHubTrending = lazy(
() => import("@/pages/GeneralSettings/CommunityHub/Trending")
Expand Down Expand Up @@ -143,6 +144,24 @@ export default function App() {
path="/settings/agents"
element={<AdminRoute Component={AdminAgents} />}
/>
<Route
path="/settings/agents/builder"
element={
<AdminRoute
Component={AgentBuilder}
hideUserMenu={true}
/>
}
/>
<Route
path="/settings/agents/builder/:flowId"
element={
<AdminRoute
Component={AgentBuilder}
hideUserMenu={true}
/>
}
/>
<Route
path="/settings/event-logs"
element={<AdminRoute Component={AdminLogs} />}
Expand Down
10 changes: 7 additions & 3 deletions frontend/src/components/PrivateRoute/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ function useIsAuthenticated() {

// Allows only admin to access the route and if in single user mode,
// allows all users to access the route
export function AdminRoute({ Component }) {
export function AdminRoute({ Component, hideUserMenu = false }) {
const { isAuthd, shouldRedirectToOnboarding, multiUserMode } =
useIsAuthenticated();
if (isAuthd === null) return <FullScreenLoader />;
Expand All @@ -94,9 +94,13 @@ export function AdminRoute({ Component }) {

const user = userFromStorage();
return isAuthd && (user?.role === "admin" || !multiUserMode) ? (
<UserMenu>
hideUserMenu ? (
<Component />
</UserMenu>
) : (
<UserMenu>
<Component />
</UserMenu>
)
) : (
<Navigate to={paths.home()} />
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ import {
} from "../ThoughtContainer";

const DOMPurify = createDOMPurify(window);
DOMPurify.setConfig({
ADD_ATTR: ["target", "rel"],
});

const HistoricalMessage = ({
uuid = v4(),
message,
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
9 changes: 9 additions & 0 deletions frontend/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
--theme-button-primary: #46c8ff;
--theme-button-primary-hover: #434343;

--theme-button-cta: #7cd4fd;

--theme-file-row-even: #0e0f0f;
--theme-file-row-odd: #1b1b1e;
--theme-file-row-selected-even: rgba(14, 165, 233, 0.2);
Expand Down Expand Up @@ -92,6 +94,8 @@
--theme-button-primary: #0ba5ec;
--theme-button-primary-hover: #dedede;

--theme-button-cta: #7cd4fd;

--theme-file-row-even: #f5f5f5;
--theme-file-row-odd: #e9e9e9;
--theme-file-row-selected-even: #0ba5ec;
Expand Down Expand Up @@ -664,6 +668,11 @@ dialog::backdrop {
padding: 14px 15px;
}

.markdown > * a {
color: var(--theme-button-cta);
text-decoration: underline;
}

@media (max-width: 600px) {
.markdown table th,
.markdown table td {
Expand Down
Binary file added frontend/src/media/logo/anything-llm-infinity.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
149 changes: 149 additions & 0 deletions frontend/src/models/agentFlows.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
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((res) => {
if (!res.ok) throw new Error(response.error || "Failed to save flow");
return res;
})
.then((res) => res.json())
.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((res) => {
if (!res.ok) throw new Error(response.error || "Failed to get flow");
return res;
})
.then((res) => res.json())
.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((res) => {
// if (!res.ok) throw new Error(response.error || "Failed to run flow");
// return res;
// })
// .then((res) => res.json())
// .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((res) => {
if (!res.ok) throw new Error(response.error || "Failed to delete flow");
return res;
})
.then((res) => res.json())
.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 result = await fetch(`${API_BASE}/agent-flows/${uuid}/toggle`, {
method: "POST",
headers: {
...baseHeaders(),
"Content-Type": "application/json",
},
body: JSON.stringify({ active }),
})
.then((res) => {
if (!res.ok) throw new Error(res.error || "Failed to toggle flow");
return res;
})
.then((res) => res.json());
return { success: true, flow: result.flow };
} catch (error) {
console.error("Failed to toggle flow:", error);
return { success: false, error: error.message };
}
},
};

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

export default function AddBlockMenu({
showBlockMenu,
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 pb-[50%]" 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"
>
<Plus className="w-4 h-4" />
Add Block
<CaretDown
className={`w-3.5 h-3.5 transition-transform duration-300 ${showBlockMenu ? "rotate-180" : ""}`}
/>
</button>
{showBlockMenu && (
<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.FINISH &&
type !== BLOCK_TYPES.FLOW_INFO && (
<button
key={type}
onClick={() => {
addBlock(type);
setShowBlockMenu(false);
}}
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-fit h-fit text-white">{info.icon}</div>
</div>
<div className="text-left flex-1">
<div className="text-sm font-medium">{info.label}</div>
<div className="text-xs text-white/60">
{info.description}
</div>
</div>
</button>
)
)}
</div>
)}
</div>
);
}
Loading