Skip to content

Commit

Permalink
Merge branch 'dev' into dependabot/pip/autogpt_platform/market/dev/pr…
Browse files Browse the repository at this point in the history
…oduction-dependencies-38eee1723f
  • Loading branch information
Swiftyos authored Dec 17, 2024
2 parents cdf765e + cd339b0 commit ed1f5a8
Show file tree
Hide file tree
Showing 9 changed files with 683 additions and 62 deletions.
1 change: 1 addition & 0 deletions autogpt_platform/backend/backend/data/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ def ref_to_dict(obj):
}
elif isinstance(obj, list):
return [ref_to_dict(item) for item in obj]

return obj

cls.cached_jsonschema = cast(dict[str, Any], ref_to_dict(model))
Expand Down
44 changes: 42 additions & 2 deletions autogpt_platform/backend/backend/data/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ def sanitize(name):
for link in self.links:
input_links[link.sink_id].append(link)

# Nodes: required fields are filled or connected
# Nodes: required fields are filled or connected and dependencies are satisfied
for node in self.nodes:
block = get_block(node.block_id)
if block is None:
Expand All @@ -278,6 +278,38 @@ def sanitize(name):
f"Node {block.name} #{node.id} required input missing: `{name}`"
)

# Get input schema properties and check dependencies
input_schema = block.input_schema.model_fields
required_fields = block.input_schema.get_required_fields()

def has_value(name):
return (
node is not None
and name in node.input_default
and node.input_default[name] is not None
and str(node.input_default[name]).strip() != ""
) or (name in input_schema and input_schema[name].default is not None)

# Validate dependencies between fields
for field_name, field_info in input_schema.items():

# Apply input dependency validation only on run & field with depends_on
json_schema_extra = field_info.json_schema_extra or {}
dependencies = json_schema_extra.get("depends_on", [])
if not for_run or not dependencies:
continue

# Check if dependent field has value in input_default
field_has_value = has_value(field_name)
field_is_required = field_name in required_fields

# Check for missing dependencies when dependent field is present
missing_deps = [dep for dep in dependencies if not has_value(dep)]
if missing_deps and (field_has_value or field_is_required):
raise ValueError(
f"Node {block.name} #{node.id}: Field `{field_name}` requires [{', '.join(missing_deps)}] to be set"
)

node_map = {v.id: v for v in self.nodes}

def is_static_output_block(nid: str) -> bool:
Expand Down Expand Up @@ -432,7 +464,15 @@ async def get_graphs(
include=AGENT_GRAPH_INCLUDE,
)

return [GraphModel.from_db(graph) for graph in graphs]
graph_models = []
for graph in graphs:
try:
graph_models.append(GraphModel.from_db(graph))
except Exception as e:
logger.error(f"Error processing graph {graph.id}: {e}")
continue

return graph_models


async def get_executions(user_id: str) -> list[GraphExecution]:
Expand Down
2 changes: 2 additions & 0 deletions autogpt_platform/backend/backend/data/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ def SchemaField(
secret: bool = False,
exclude: bool = False,
hidden: Optional[bool] = None,
depends_on: list[str] | None = None,
**kwargs,
) -> T:
json_extra = {
Expand All @@ -147,6 +148,7 @@ def SchemaField(
"secret": secret,
"advanced": advanced,
"hidden": hidden,
"depends_on": depends_on,
}.items()
if v is not None
}
Expand Down
77 changes: 27 additions & 50 deletions autogpt_platform/frontend/src/app/monitoring/page.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
"use client";

import { useEffect, useState, useMemo, useCallback } from "react";
import React, { useCallback, useEffect, useMemo, useState } from "react";

import AutoGPTServerAPI, {
GraphMetaWithRuns,
ExecutionMeta,
GraphExecution,
Schedule,
GraphMeta,
} from "@/lib/autogpt-server-api";

import { Card } from "@/components/ui/card";
import { FlowRun } from "@/lib/types";
import {
AgentFlowList,
FlowInfo,
Expand All @@ -20,13 +18,11 @@ import {
import { SchedulesTable } from "@/components/monitor/scheduleTable";

const Monitor = () => {
const [flows, setFlows] = useState<GraphMetaWithRuns[]>([]);
const [flowRuns, setFlowRuns] = useState<FlowRun[]>([]);
const [flows, setFlows] = useState<GraphMeta[]>([]);
const [executions, setExecutions] = useState<GraphExecution[]>([]);
const [schedules, setSchedules] = useState<Schedule[]>([]);
const [selectedFlow, setSelectedFlow] = useState<GraphMetaWithRuns | null>(
null,
);
const [selectedRun, setSelectedRun] = useState<FlowRun | null>(null);
const [selectedFlow, setSelectedFlow] = useState<GraphMeta | null>(null);
const [selectedRun, setSelectedRun] = useState<GraphExecution | null>(null);
const [sortColumn, setSortColumn] = useState<keyof Schedule>("id");
const [sortDirection, setSortDirection] = useState<"asc" | "desc">("asc");

Expand All @@ -45,16 +41,11 @@ const Monitor = () => {
);

const fetchAgents = useCallback(() => {
api.listGraphsWithRuns().then((agent) => {
api.listGraphs().then((agent) => {
setFlows(agent);
const flowRuns = agent.flatMap((graph) =>
graph.executions != null
? graph.executions.map((execution) =>
flowRunFromExecutionMeta(graph, execution),
)
: [],
);
setFlowRuns(flowRuns);
});
api.getExecutions().then((executions) => {
setExecutions(executions);
});
}, [api]);

Expand Down Expand Up @@ -86,43 +77,45 @@ const Monitor = () => {

return (
<div
className="grid h-full w-screen grid-cols-1 gap-4 px-8 md:grid-cols-5 lg:grid-cols-4 xl:grid-cols-10"
className="grid grid-cols-1 gap-4 md:grid-cols-5 lg:grid-cols-4 xl:grid-cols-10"
data-testid="monitor-page"
>
<AgentFlowList
className={column1}
flows={flows}
flowRuns={flowRuns}
executions={executions}
selectedFlow={selectedFlow}
onSelectFlow={(f) => {
setSelectedRun(null);
setSelectedFlow(
f.id == selectedFlow?.id ? null : (f as GraphMetaWithRuns),
);
setSelectedFlow(f.id == selectedFlow?.id ? null : (f as GraphMeta));
}}
/>
<FlowRunsList
className={column2}
flows={flows}
runs={[
executions={[
...(selectedFlow
? flowRuns.filter((v) => v.graphID == selectedFlow.id)
: flowRuns),
].sort((a, b) => Number(a.startTime) - Number(b.startTime))}
? executions.filter((v) => v.graph_id == selectedFlow.id)
: executions),
].sort((a, b) => Number(b.started_at) - Number(a.started_at))}
selectedRun={selectedRun}
onSelectRun={(r) => setSelectedRun(r.id == selectedRun?.id ? null : r)}
onSelectRun={(r) =>
setSelectedRun(r.execution_id == selectedRun?.execution_id ? null : r)
}
/>
{(selectedRun && (
<FlowRunInfo
flow={selectedFlow || flows.find((f) => f.id == selectedRun.graphID)!}
flowRun={selectedRun}
flow={
selectedFlow || flows.find((f) => f.id == selectedRun.graph_id)!
}
execution={selectedRun}
className={column3}
/>
)) ||
(selectedFlow && (
<FlowInfo
flow={selectedFlow}
flowRuns={flowRuns.filter((r) => r.graphID == selectedFlow.id)}
executions={executions.filter((e) => e.graph_id == selectedFlow.id)}
className={column3}
refresh={() => {
fetchAgents();
Expand All @@ -132,7 +125,7 @@ const Monitor = () => {
/>
)) || (
<Card className={`p-6 ${column3}`}>
<FlowRunsStats flows={flows} flowRuns={flowRuns} />
<FlowRunsStats flows={flows} executions={executions} />
</Card>
)}
<div className="col-span-full xl:col-span-6">
Expand All @@ -149,20 +142,4 @@ const Monitor = () => {
);
};

function flowRunFromExecutionMeta(
graphMeta: GraphMetaWithRuns,
executionMeta: ExecutionMeta,
): FlowRun {
return {
id: executionMeta.execution_id,
graphID: graphMeta.id,
graphVersion: graphMeta.version,
status: executionMeta.status,
startTime: executionMeta.started_at,
endTime: executionMeta.ended_at,
duration: executionMeta.duration,
totalRunTime: executionMeta.total_run_time,
} as FlowRun;
}

export default Monitor;
Loading

0 comments on commit ed1f5a8

Please sign in to comment.