Skip to content
This repository has been archived by the owner on Oct 14, 2024. It is now read-only.

Commit

Permalink
fix(rnd): Fix execution error on non-saved agent (Significant-Gravita…
Browse files Browse the repository at this point in the history
  • Loading branch information
majdyz authored Sep 16, 2024
1 parent 22ce8e0 commit b1347a9
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 55 deletions.
7 changes: 6 additions & 1 deletion rnd/autogpt_builder/src/components/Flow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -557,11 +557,16 @@ const FlowEditor: React.FC<{
onClick: handleRedo,
},
{
label: !isRunning ? "Run" : "Stop",
label: !savedAgent
? "Please save the agent to run"
: !isRunning
? "Run"
: "Stop",
icon: !isRunning ? <IconPlay /> : <IconSquare />,
onClick: !isRunning
? () => runnerUIRef.current?.runOrOpenInput()
: requestStopRun,
disabled: !savedAgent,
},
{
label: "Runner Output",
Expand Down
22 changes: 13 additions & 9 deletions rnd/autogpt_builder/src/components/edit/control/ControlPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import React from "react";
export type Control = {
icon: React.ReactNode;
label: string;
disabled?: boolean;
onClick: () => void;
};

Expand Down Expand Up @@ -50,15 +51,18 @@ export const ControlPanel = ({
{controls.map((control, index) => (
<Tooltip key={index} delayDuration={500}>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => control.onClick()}
data-id={`control-button-${index}`}
>
{control.icon}
<span className="sr-only">{control.label}</span>
</Button>
<div>
<Button
variant="ghost"
size="icon"
onClick={() => control.onClick()}
data-id={`control-button-${index}`}
disabled={control.disabled || false}
>
{control.icon}
<span className="sr-only">{control.label}</span>
</Button>
</div>
</TooltipTrigger>
<TooltipContent side="right">{control.label}</TooltipContent>
</Tooltip>
Expand Down
6 changes: 3 additions & 3 deletions rnd/autogpt_builder/src/components/node-input-components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ const NodeKeyValueInput: FC<{
<Input
type="text"
placeholder="Value"
value={value ?? ""}
defaultValue={value ?? ""}
onBlur={(e) =>
updateKeyValuePairs(
keyValuePairs.toSpliced(index, 1, {
Expand Down Expand Up @@ -563,7 +563,7 @@ const NodeStringInput: FC<{
<Input
type="text"
id={selfKey}
value={schema.secret && value ? "********" : value}
defaultValue={schema.secret && value ? "********" : value}
readOnly={schema.secret}
placeholder={
schema?.placeholder || `Enter ${beautifyString(displayName)}`
Expand Down Expand Up @@ -658,7 +658,7 @@ const NodeNumberInput: FC<{
<Input
type="number"
id={selfKey}
value={value}
defaultValue={value}
onBlur={(e) => handleInputChange(selfKey, parseFloat(e.target.value))}
placeholder={
schema.placeholder || `Enter ${beautifyString(displayName)}`
Expand Down
16 changes: 1 addition & 15 deletions rnd/autogpt_builder/src/components/ui/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,7 @@ export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}

const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, value, ...props }, ref) => {
// This ref allows the `Input` component to be both controlled and uncontrolled.
// The HTMLvalue will only be updated if the value prop changes, but the user can still type in the input.
ref = ref || React.createRef<HTMLInputElement>();
React.useEffect(() => {
if (
ref &&
ref.current &&
ref.current.value !== value &&
type !== "file"
) {
ref.current.value = value;
}
}, [value, type, ref]);
({ className, type, ...props }, ref) => {
return (
<input
type={type}
Expand All @@ -29,7 +16,6 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
className,
)}
ref={ref}
defaultValue={type !== "file" ? value : undefined}
{...props}
/>
);
Expand Down
62 changes: 43 additions & 19 deletions rnd/autogpt_builder/src/hooks/useAgentGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ export default function useAgentGraph(
id: node.id,
type: "custom",
position: {
x: node.metadata.position.x,
y: node.metadata.position.y,
x: node?.metadata?.position?.x || 0,
y: node?.metadata?.position?.y || 0,
},
data: {
block_id: block.id,
Expand Down Expand Up @@ -614,7 +614,7 @@ export default function useAgentGraph(
}));

return {
id: node.data.backend_id,
id: node.id,
block_id: node.data.block_id,
input_default: inputDefault,
input_nodes: inputNodes,
Expand Down Expand Up @@ -643,35 +643,59 @@ export default function useAgentGraph(
nodes: formattedNodes,
links: links,
};
if (savedAgent && deepEquals(payload, savedAgent, true)) {

// To avoid saving the same graph, we compare the payload with the saved agent.
// Differences in IDs are ignored.
const comparedPayload = {
...(({ id, ...rest }) => rest)(payload),
nodes: payload.nodes.map(
({ id, data, input_nodes, output_nodes, ...rest }) => rest,
),
links: payload.links.map(({ source_id, sink_id, ...rest }) => rest),
};
const comparedSavedAgent = {
name: savedAgent?.name,
description: savedAgent?.description,
nodes: savedAgent?.nodes.map((v) => ({
block_id: v.block_id,
input_default: v.input_default,
metadata: v.metadata,
})),
links: savedAgent?.links.map((v) => ({
sink_name: v.sink_name,
source_name: v.source_name,
})),
};

let newSavedAgent = null;
if (savedAgent && deepEquals(comparedPayload, comparedSavedAgent)) {
console.warn("No need to save: Graph is the same as version on server");
// Trigger state change
setSavedAgent(savedAgent);
return;
newSavedAgent = savedAgent;
} else {
console.debug(
"Saving new Graph version; old vs new:",
savedAgent,
comparedPayload,
payload,
);
}
setNodesSyncedWithSavedAgent(false);

setNodesSyncedWithSavedAgent(false);
newSavedAgent = savedAgent
? await (savedAgent.is_template
? api.updateTemplate(savedAgent.id, payload)
: api.updateGraph(savedAgent.id, payload))
: await (asTemplate
? api.createTemplate(payload)
: api.createGraph(payload));

const newSavedAgent = savedAgent
? await (savedAgent.is_template
? api.updateTemplate(savedAgent.id, payload)
: api.updateGraph(savedAgent.id, payload))
: await (asTemplate
? api.createTemplate(payload)
: api.createGraph(payload));
console.debug("Response from the API:", newSavedAgent);
console.debug("Response from the API:", newSavedAgent);
}

// Route the URL to the new flow ID if it's a new agent.
if (!savedAgent) {
const path = new URLSearchParams(searchParams);
path.set("flowID", newSavedAgent.id);
router.replace(`${pathname}?${path.toString()}`);
router.push(`${pathname}?${path.toString()}`);
return;
}

// Update the node IDs on the frontend
Expand Down
11 changes: 4 additions & 7 deletions rnd/autogpt_builder/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,18 @@ export function hashString(str: string): number {
}

/** Derived from https://stackoverflow.com/a/32922084 */
export function deepEquals(x: any, y: any, allowMissingKeys = false): boolean {
export function deepEquals(x: any, y: any): boolean {
const ok = Object.keys,
tx = typeof x,
ty = typeof y;

const sk = (a: object, b: object) => ok(a).filter((k) => k in b);
const skipLengthCheck = allowMissingKeys && !Array.isArray(x);

const res =
x &&
y &&
tx === ty &&
(tx === "object"
? (skipLengthCheck || ok(x).length === ok(y).length) &&
sk(x, y).every((key) => deepEquals(x[key], y[key], allowMissingKeys))
? ok(x).length === ok(y).length &&
ok(x).every((key) => deepEquals(x[key], y[key]))
: x === y);
return res;
}
Expand Down Expand Up @@ -188,7 +185,7 @@ export const categoryColorMap: Record<string, string> = {
SEARCH: "bg-blue-300/[.7]",
BASIC: "bg-purple-300/[.7]",
INPUT: "bg-cyan-300/[.7]",
OUTPUT: "bg-brown-300/[.7]",
OUTPUT: "bg-red-300/[.7]",
LOGIC: "bg-teal-300/[.7]",
};

Expand Down
2 changes: 1 addition & 1 deletion rnd/autogpt_server/autogpt_server/executor/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def update_execution(status: ExecutionStatus) -> ExecutionResult:
try:
credit = wait(user_credit.get_or_refill_credit(user_id))
if credit < 0:
raise ValueError("Insufficient credit: {credit}")
raise ValueError(f"Insufficient credit: {credit}")

for output_name, output_data in node_block.execute(input_data):
output_size += len(json.dumps(output_data))
Expand Down

0 comments on commit b1347a9

Please sign in to comment.