mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-08 22:58:01 -05:00
fix(frontend/library): Fix trigger UX flows in v3 library (#11589)
- Resolves #11586 - Follow-up to #11580 ### Changes 🏗️ - Fix logic to include manual triggers as a possibility - Fix input render logic to use trigger setup schema if applicable - Fix rendering payload input for externally triggered runs - Amend `RunAgentModal` to load preset inputs+credentials if selected - Amend `SelectedTemplateView` to use modified input for run (if applicable) - Hide non-applicable buttons in `SelectedRunView` for externally triggered runs - Implement auto-navigation to `SelectedTriggerView` on trigger setup ### Checklist 📋 #### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - [x] Can set up manual triggers - [x] Navigates to trigger view after setup - [x] Can set up automatic triggers - [x] Can create templates from runs - [x] Can run templates - [x] Can run templates with modified input
This commit is contained in:
committed by
GitHub
parent
979d7c3b74
commit
117bb05438
@@ -1,5 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
||||||
import { Button } from "@/components/atoms/Button/Button";
|
import { Button } from "@/components/atoms/Button/Button";
|
||||||
import { Breadcrumbs } from "@/components/molecules/Breadcrumbs/Breadcrumbs";
|
import { Breadcrumbs } from "@/components/molecules/Breadcrumbs/Breadcrumbs";
|
||||||
import { ErrorCard } from "@/components/molecules/ErrorCard/ErrorCard";
|
import { ErrorCard } from "@/components/molecules/ErrorCard/ErrorCard";
|
||||||
@@ -24,11 +25,13 @@ import { useNewAgentLibraryView } from "./useNewAgentLibraryView";
|
|||||||
|
|
||||||
export function NewAgentLibraryView() {
|
export function NewAgentLibraryView() {
|
||||||
const {
|
const {
|
||||||
agent,
|
|
||||||
hasAnyItems,
|
|
||||||
ready,
|
|
||||||
error,
|
|
||||||
agentId,
|
agentId,
|
||||||
|
agent,
|
||||||
|
ready,
|
||||||
|
activeTemplate,
|
||||||
|
isTemplateLoading,
|
||||||
|
error,
|
||||||
|
hasAnyItems,
|
||||||
activeItem,
|
activeItem,
|
||||||
sidebarLoading,
|
sidebarLoading,
|
||||||
activeTab,
|
activeTab,
|
||||||
@@ -38,6 +41,12 @@ export function NewAgentLibraryView() {
|
|||||||
handleClearSelectedRun,
|
handleClearSelectedRun,
|
||||||
} = useNewAgentLibraryView();
|
} = useNewAgentLibraryView();
|
||||||
|
|
||||||
|
function onTriggerSetup(newTrigger: LibraryAgentPreset) {
|
||||||
|
if (!agent) return;
|
||||||
|
|
||||||
|
handleSelectRun(newTrigger.id, "triggers");
|
||||||
|
}
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<ErrorCard
|
<ErrorCard
|
||||||
@@ -65,7 +74,7 @@ export function NewAgentLibraryView() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex min-h-0 flex-1">
|
<div className="flex min-h-0 flex-1">
|
||||||
<EmptyTasks agent={agent} />
|
<EmptyTasks agent={agent} onTriggerSetup={onTriggerSetup} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -82,16 +91,23 @@ export function NewAgentLibraryView() {
|
|||||||
>
|
>
|
||||||
<RunAgentModal
|
<RunAgentModal
|
||||||
triggerSlot={
|
triggerSlot={
|
||||||
<Button variant="primary" size="large" className="w-full">
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
size="large"
|
||||||
|
className="w-full"
|
||||||
|
disabled={isTemplateLoading && activeTab === "templates"}
|
||||||
|
>
|
||||||
<PlusIcon size={20} /> New task
|
<PlusIcon size={20} /> New task
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
agent={agent}
|
agent={agent}
|
||||||
agentId={agent.id.toString()}
|
|
||||||
onRunCreated={(execution) => handleSelectRun(execution.id, "runs")}
|
onRunCreated={(execution) => handleSelectRun(execution.id, "runs")}
|
||||||
onScheduleCreated={(schedule) =>
|
onScheduleCreated={(schedule) =>
|
||||||
handleSelectRun(schedule.id, "scheduled")
|
handleSelectRun(schedule.id, "scheduled")
|
||||||
}
|
}
|
||||||
|
onTriggerSetup={onTriggerSetup}
|
||||||
|
initialInputValues={activeTemplate?.inputs}
|
||||||
|
initialInputCredentials={activeTemplate?.credentials}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -151,7 +167,7 @@ export function NewAgentLibraryView() {
|
|||||||
</SelectedViewLayout>
|
</SelectedViewLayout>
|
||||||
) : (
|
) : (
|
||||||
<SelectedViewLayout agentName={agent.name} agentId={agent.id}>
|
<SelectedViewLayout agentName={agent.name} agentId={agent.id}>
|
||||||
<EmptyTasks agent={agent} />
|
<EmptyTasks agent={agent} onTriggerSetup={onTriggerSetup} />
|
||||||
</SelectedViewLayout>
|
</SelectedViewLayout>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import type { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
import type { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||||
import type { CredentialsMetaInput } from "@/lib/autogpt-server-api/types";
|
import type {
|
||||||
|
BlockIOSubSchema,
|
||||||
|
CredentialsMetaInput,
|
||||||
|
} from "@/lib/autogpt-server-api/types";
|
||||||
import { CredentialsInput } from "../CredentialsInputs/CredentialsInputs";
|
import { CredentialsInput } from "../CredentialsInputs/CredentialsInputs";
|
||||||
import {
|
import {
|
||||||
getAgentCredentialsFields,
|
getAgentCredentialsFields,
|
||||||
@@ -20,13 +23,21 @@ export function AgentInputsReadOnly({
|
|||||||
inputs,
|
inputs,
|
||||||
credentialInputs,
|
credentialInputs,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const fields = getAgentInputFields(agent);
|
const inputFields = getAgentInputFields(agent);
|
||||||
const credentialFields = getAgentCredentialsFields(agent);
|
const credentialFieldEntries = Object.entries(
|
||||||
const inputEntries = Object.entries(fields);
|
getAgentCredentialsFields(agent),
|
||||||
const credentialEntries = Object.entries(credentialFields);
|
);
|
||||||
|
|
||||||
const hasInputs = inputs && inputEntries.length > 0;
|
// Take actual input entries as leading; augment with schema from input fields.
|
||||||
const hasCredentials = credentialInputs && credentialEntries.length > 0;
|
// TODO: ensure consistent ordering.
|
||||||
|
const inputEntries =
|
||||||
|
inputs &&
|
||||||
|
Object.entries(inputs).map<[string, [BlockIOSubSchema | undefined, any]]>(
|
||||||
|
([k, v]) => [k, [inputFields[k], v]],
|
||||||
|
);
|
||||||
|
|
||||||
|
const hasInputs = inputEntries && inputEntries.length > 0;
|
||||||
|
const hasCredentials = credentialInputs && credentialFieldEntries.length > 0;
|
||||||
|
|
||||||
if (!hasInputs && !hasCredentials) {
|
if (!hasInputs && !hasCredentials) {
|
||||||
return <div className="text-neutral-600">No input for this run.</div>;
|
return <div className="text-neutral-600">No input for this run.</div>;
|
||||||
@@ -37,11 +48,13 @@ export function AgentInputsReadOnly({
|
|||||||
{/* Regular inputs */}
|
{/* Regular inputs */}
|
||||||
{hasInputs && (
|
{hasInputs && (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
{inputEntries.map(([key, sub]) => (
|
{inputEntries.map(([key, [schema, value]]) => (
|
||||||
<div key={key} className="flex flex-col gap-1.5">
|
<div key={key} className="flex flex-col gap-1.5">
|
||||||
<label className="text-sm font-medium">{sub?.title || key}</label>
|
<label className="text-sm font-medium">
|
||||||
|
{schema?.title || key}
|
||||||
|
</label>
|
||||||
<p className="whitespace-pre-wrap break-words text-sm text-neutral-700">
|
<p className="whitespace-pre-wrap break-words text-sm text-neutral-700">
|
||||||
{renderValue((inputs as Record<string, any>)[key])}
|
{renderValue(value)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -52,7 +65,7 @@ export function AgentInputsReadOnly({
|
|||||||
{hasCredentials && (
|
{hasCredentials && (
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
{hasInputs && <div className="border-t border-neutral-200 pt-4" />}
|
{hasInputs && <div className="border-t border-neutral-200 pt-4" />}
|
||||||
{credentialEntries.map(([key, inputSubSchema]) => {
|
{credentialFieldEntries.map(([key, inputSubSchema]) => {
|
||||||
const credential = credentialInputs![key];
|
const credential = credentialInputs![key];
|
||||||
if (!credential) return null;
|
if (!credential) return null;
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ export function getCredentialTypeDisplayName(type: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getAgentInputFields(agent: LibraryAgent): Record<string, any> {
|
export function getAgentInputFields(agent: LibraryAgent): Record<string, any> {
|
||||||
const schema = agent.input_schema as unknown as {
|
const schema = (agent.trigger_setup_info?.config_schema ??
|
||||||
|
agent.input_schema) as unknown as {
|
||||||
properties?: Record<string, any>;
|
properties?: Record<string, any>;
|
||||||
} | null;
|
} | null;
|
||||||
if (!schema || !schema.properties) return {};
|
if (!schema || !schema.properties) return {};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { GraphExecutionJobInfo } from "@/app/api/__generated__/models/graphExecutionJobInfo";
|
import { GraphExecutionJobInfo } from "@/app/api/__generated__/models/graphExecutionJobInfo";
|
||||||
import { GraphExecutionMeta } from "@/app/api/__generated__/models/graphExecutionMeta";
|
import { GraphExecutionMeta } from "@/app/api/__generated__/models/graphExecutionMeta";
|
||||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||||
|
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
||||||
import { Button } from "@/components/atoms/Button/Button";
|
import { Button } from "@/components/atoms/Button/Button";
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
@@ -22,16 +23,20 @@ import { useAgentRunModal } from "./useAgentRunModal";
|
|||||||
interface Props {
|
interface Props {
|
||||||
triggerSlot: React.ReactNode;
|
triggerSlot: React.ReactNode;
|
||||||
agent: LibraryAgent;
|
agent: LibraryAgent;
|
||||||
agentId: string;
|
initialInputValues?: Record<string, any>;
|
||||||
agentVersion?: number;
|
initialInputCredentials?: Record<string, any>;
|
||||||
onRunCreated?: (execution: GraphExecutionMeta) => void;
|
onRunCreated?: (execution: GraphExecutionMeta) => void;
|
||||||
|
onTriggerSetup?: (preset: LibraryAgentPreset) => void;
|
||||||
onScheduleCreated?: (schedule: GraphExecutionJobInfo) => void;
|
onScheduleCreated?: (schedule: GraphExecutionJobInfo) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RunAgentModal({
|
export function RunAgentModal({
|
||||||
triggerSlot,
|
triggerSlot,
|
||||||
agent,
|
agent,
|
||||||
|
initialInputValues,
|
||||||
|
initialInputCredentials,
|
||||||
onRunCreated,
|
onRunCreated,
|
||||||
|
onTriggerSetup,
|
||||||
onScheduleCreated,
|
onScheduleCreated,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const {
|
const {
|
||||||
@@ -71,6 +76,9 @@ export function RunAgentModal({
|
|||||||
handleRun,
|
handleRun,
|
||||||
} = useAgentRunModal(agent, {
|
} = useAgentRunModal(agent, {
|
||||||
onRun: onRunCreated,
|
onRun: onRunCreated,
|
||||||
|
onSetupTrigger: onTriggerSetup,
|
||||||
|
initialInputValues,
|
||||||
|
initialInputCredentials,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [isScheduleModalOpen, setIsScheduleModalOpen] = useState(false);
|
const [isScheduleModalOpen, setIsScheduleModalOpen] = useState(false);
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ export function ModalRunSection() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
{defaultRunType === "automatic-trigger" ? (
|
{defaultRunType === "automatic-trigger" ||
|
||||||
|
defaultRunType === "manual-trigger" ? (
|
||||||
<ModalSection
|
<ModalSection
|
||||||
title="Task Trigger"
|
title="Task Trigger"
|
||||||
subtitle="Set up a trigger for the agent to run this task automatically"
|
subtitle="Set up a trigger for the agent to run this task automatically"
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ export function RunActions({
|
|||||||
disabled={!isRunReady || isExecuting || isSettingUpTrigger}
|
disabled={!isRunReady || isExecuting || isSettingUpTrigger}
|
||||||
loading={isExecuting || isSettingUpTrigger}
|
loading={isExecuting || isSettingUpTrigger}
|
||||||
>
|
>
|
||||||
{defaultRunType === "automatic-trigger"
|
{defaultRunType === "automatic-trigger" ||
|
||||||
|
defaultRunType === "manual-trigger"
|
||||||
? "Set up Trigger"
|
? "Set up Trigger"
|
||||||
: "Start Task"}
|
: "Start Task"}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
getGetV2ListPresetsQueryKey,
|
getGetV2ListPresetsQueryKey,
|
||||||
usePostV2SetupTrigger,
|
usePostV2SetupTrigger,
|
||||||
} from "@/app/api/__generated__/endpoints/presets/presets";
|
} from "@/app/api/__generated__/endpoints/presets/presets";
|
||||||
import { GraphExecutionJobInfo } from "@/app/api/__generated__/models/graphExecutionJobInfo";
|
|
||||||
import { GraphExecutionMeta } from "@/app/api/__generated__/models/graphExecutionMeta";
|
import { GraphExecutionMeta } from "@/app/api/__generated__/models/graphExecutionMeta";
|
||||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||||
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
||||||
@@ -14,7 +13,7 @@ import { useToast } from "@/components/molecules/Toast/use-toast";
|
|||||||
import { isEmpty } from "@/lib/utils";
|
import { isEmpty } from "@/lib/utils";
|
||||||
import { analytics } from "@/services/analytics";
|
import { analytics } from "@/services/analytics";
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { showExecutionErrorToast } from "./errorHelpers";
|
import { showExecutionErrorToast } from "./errorHelpers";
|
||||||
|
|
||||||
export type RunVariant =
|
export type RunVariant =
|
||||||
@@ -25,8 +24,9 @@ export type RunVariant =
|
|||||||
|
|
||||||
interface UseAgentRunModalCallbacks {
|
interface UseAgentRunModalCallbacks {
|
||||||
onRun?: (execution: GraphExecutionMeta) => void;
|
onRun?: (execution: GraphExecutionMeta) => void;
|
||||||
onCreateSchedule?: (schedule: GraphExecutionJobInfo) => void;
|
|
||||||
onSetupTrigger?: (preset: LibraryAgentPreset) => void;
|
onSetupTrigger?: (preset: LibraryAgentPreset) => void;
|
||||||
|
initialInputValues?: Record<string, any>;
|
||||||
|
initialInputCredentials?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useAgentRunModal(
|
export function useAgentRunModal(
|
||||||
@@ -36,18 +36,28 @@ export function useAgentRunModal(
|
|||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [inputValues, setInputValues] = useState<Record<string, any>>({});
|
const [inputValues, setInputValues] = useState<Record<string, any>>(
|
||||||
|
callbacks?.initialInputValues || {},
|
||||||
|
);
|
||||||
const [inputCredentials, setInputCredentials] = useState<Record<string, any>>(
|
const [inputCredentials, setInputCredentials] = useState<Record<string, any>>(
|
||||||
{},
|
callbacks?.initialInputCredentials || {},
|
||||||
);
|
);
|
||||||
const [presetName, setPresetName] = useState<string>("");
|
const [presetName, setPresetName] = useState<string>("");
|
||||||
const [presetDescription, setPresetDescription] = useState<string>("");
|
const [presetDescription, setPresetDescription] = useState<string>("");
|
||||||
|
|
||||||
// Determine the default run type based on agent capabilities
|
// Determine the default run type based on agent capabilities
|
||||||
const defaultRunType: RunVariant = agent.has_external_trigger
|
const defaultRunType: RunVariant = agent.trigger_setup_info
|
||||||
? "automatic-trigger"
|
? agent.trigger_setup_info.credentials_input_name
|
||||||
|
? "automatic-trigger"
|
||||||
|
: "manual-trigger"
|
||||||
: "manual";
|
: "manual";
|
||||||
|
|
||||||
|
// Update input values/credentials if template is selected/unselected
|
||||||
|
useEffect(() => {
|
||||||
|
setInputValues(callbacks?.initialInputValues || {});
|
||||||
|
setInputCredentials(callbacks?.initialInputCredentials || {});
|
||||||
|
}, [callbacks?.initialInputValues, callbacks?.initialInputCredentials]);
|
||||||
|
|
||||||
// API mutations
|
// API mutations
|
||||||
const executeGraphMutation = usePostV1ExecuteGraphAgent({
|
const executeGraphMutation = usePostV1ExecuteGraphAgent({
|
||||||
mutation: {
|
mutation: {
|
||||||
@@ -105,11 +115,13 @@ export function useAgentRunModal(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Input schema validation
|
// Input schema validation (use trigger schema for triggered agents)
|
||||||
const agentInputSchema = useMemo(
|
const agentInputSchema = useMemo(() => {
|
||||||
() => agent.input_schema || { properties: {}, required: [] },
|
if (agent.trigger_setup_info?.config_schema) {
|
||||||
[agent.input_schema],
|
return agent.trigger_setup_info.config_schema;
|
||||||
);
|
}
|
||||||
|
return agent.input_schema || { properties: {}, required: [] };
|
||||||
|
}, [agent.input_schema, agent.trigger_setup_info]);
|
||||||
|
|
||||||
const agentInputFields = useMemo(() => {
|
const agentInputFields = useMemo(() => {
|
||||||
if (
|
if (
|
||||||
@@ -205,7 +217,10 @@ export function useAgentRunModal(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (defaultRunType === "automatic-trigger") {
|
if (
|
||||||
|
defaultRunType === "automatic-trigger" ||
|
||||||
|
defaultRunType === "manual-trigger"
|
||||||
|
) {
|
||||||
// Setup trigger
|
// Setup trigger
|
||||||
if (!presetName.trim()) {
|
if (!presetName.trim()) {
|
||||||
toast({
|
toast({
|
||||||
@@ -262,7 +277,7 @@ export function useAgentRunModal(
|
|||||||
setIsOpen,
|
setIsOpen,
|
||||||
|
|
||||||
// Run mode
|
// Run mode
|
||||||
defaultRunType,
|
defaultRunType: defaultRunType as RunVariant,
|
||||||
|
|
||||||
// Form: regular inputs
|
// Form: regular inputs
|
||||||
inputValues,
|
inputValues,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { getV1GetGraphVersion } from "@/app/api/__generated__/endpoints/graphs/graphs";
|
import { getV1GetGraphVersion } from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||||
|
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
||||||
import { Button } from "@/components/atoms/Button/Button";
|
import { Button } from "@/components/atoms/Button/Button";
|
||||||
import { Text } from "@/components/atoms/Text/Text";
|
import { Text } from "@/components/atoms/Text/Text";
|
||||||
import { ShowMoreText } from "@/components/molecules/ShowMoreText/ShowMoreText";
|
import { ShowMoreText } from "@/components/molecules/ShowMoreText/ShowMoreText";
|
||||||
@@ -15,9 +16,10 @@ import { EmptyTasksIllustration } from "./EmptyTasksIllustration";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
agent: LibraryAgent;
|
agent: LibraryAgent;
|
||||||
|
onTriggerSetup?: (preset: LibraryAgentPreset) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function EmptyTasks({ agent }: Props) {
|
export function EmptyTasks({ agent, onTriggerSetup }: Props) {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
async function handleExport() {
|
async function handleExport() {
|
||||||
@@ -75,7 +77,7 @@ export function EmptyTasks({ agent }: Props) {
|
|||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
agent={agent}
|
agent={agent}
|
||||||
agentId={agent.id.toString()}
|
onTriggerSetup={onTriggerSetup}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -198,8 +198,8 @@ export function SelectedRunView({
|
|||||||
<RunDetailCard title="Your input">
|
<RunDetailCard title="Your input">
|
||||||
<AgentInputsReadOnly
|
<AgentInputsReadOnly
|
||||||
agent={agent}
|
agent={agent}
|
||||||
inputs={(run as any)?.inputs}
|
inputs={run?.inputs}
|
||||||
credentialInputs={(run as any)?.credential_inputs}
|
credentialInputs={run?.credential_inputs}
|
||||||
/>
|
/>
|
||||||
</RunDetailCard>
|
</RunDetailCard>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,13 +20,18 @@ import { useSelectedRunActions } from "./useSelectedRunActions";
|
|||||||
type Props = {
|
type Props = {
|
||||||
agent: LibraryAgent;
|
agent: LibraryAgent;
|
||||||
run: GraphExecution | undefined;
|
run: GraphExecution | undefined;
|
||||||
scheduleRecurrence?: string;
|
|
||||||
onSelectRun?: (id: string) => void;
|
onSelectRun?: (id: string) => void;
|
||||||
onClearSelectedRun?: () => void;
|
onClearSelectedRun?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function SelectedRunActions(props: Props) {
|
export function SelectedRunActions({
|
||||||
|
agent,
|
||||||
|
run,
|
||||||
|
onSelectRun,
|
||||||
|
onClearSelectedRun,
|
||||||
|
}: Props) {
|
||||||
const {
|
const {
|
||||||
|
canRunManually,
|
||||||
handleRunAgain,
|
handleRunAgain,
|
||||||
handleStopRun,
|
handleStopRun,
|
||||||
isRunningAgain,
|
isRunningAgain,
|
||||||
@@ -37,21 +42,20 @@ export function SelectedRunActions(props: Props) {
|
|||||||
isCreateTemplateModalOpen,
|
isCreateTemplateModalOpen,
|
||||||
setIsCreateTemplateModalOpen,
|
setIsCreateTemplateModalOpen,
|
||||||
} = useSelectedRunActions({
|
} = useSelectedRunActions({
|
||||||
agentGraphId: props.agent.graph_id,
|
agentGraphId: agent.graph_id,
|
||||||
run: props.run,
|
run: run,
|
||||||
agent: props.agent,
|
agent: agent,
|
||||||
onSelectRun: props.onSelectRun,
|
onSelectRun: onSelectRun,
|
||||||
onClearSelectedRun: props.onClearSelectedRun,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const shareExecutionResultsEnabled = useGetFlag(Flag.SHARE_EXECUTION_RESULTS);
|
const shareExecutionResultsEnabled = useGetFlag(Flag.SHARE_EXECUTION_RESULTS);
|
||||||
const isRunning = props.run?.status === "RUNNING";
|
const isRunning = run?.status === "RUNNING";
|
||||||
|
|
||||||
if (!props.run || !props.agent) return null;
|
if (!run || !agent) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectedActionsWrap>
|
<SelectedActionsWrap>
|
||||||
{!isRunning ? (
|
{canRunManually && !isRunning ? (
|
||||||
<Button
|
<Button
|
||||||
variant="icon"
|
variant="icon"
|
||||||
size="icon"
|
size="icon"
|
||||||
@@ -103,37 +107,37 @@ export function SelectedRunActions(props: Props) {
|
|||||||
) : null}
|
) : null}
|
||||||
{shareExecutionResultsEnabled && (
|
{shareExecutionResultsEnabled && (
|
||||||
<ShareRunButton
|
<ShareRunButton
|
||||||
graphId={props.agent.graph_id}
|
graphId={agent.graph_id}
|
||||||
executionId={props.run.id}
|
executionId={run.id}
|
||||||
isShared={props.run.is_shared}
|
isShared={run.is_shared}
|
||||||
shareToken={props.run.share_token}
|
shareToken={run.share_token}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<FloatingSafeModeToggle
|
<FloatingSafeModeToggle graph={agent} variant="white" fullWidth={false} />
|
||||||
graph={props.agent}
|
{canRunManually && (
|
||||||
variant="white"
|
<>
|
||||||
fullWidth={false}
|
<Button
|
||||||
/>
|
variant="icon"
|
||||||
<Button
|
size="icon"
|
||||||
variant="icon"
|
aria-label="Save task as template"
|
||||||
size="icon"
|
onClick={() => setIsCreateTemplateModalOpen(true)}
|
||||||
aria-label="Save task as template"
|
title="Create template"
|
||||||
onClick={() => setIsCreateTemplateModalOpen(true)}
|
>
|
||||||
title="Create template"
|
<CardsThreeIcon weight="bold" size={18} className="text-zinc-700" />
|
||||||
>
|
</Button>
|
||||||
<CardsThreeIcon weight="bold" size={18} className="text-zinc-700" />
|
<CreateTemplateModal
|
||||||
</Button>
|
isOpen={isCreateTemplateModalOpen}
|
||||||
|
onClose={() => setIsCreateTemplateModalOpen(false)}
|
||||||
|
onCreate={handleCreateTemplate}
|
||||||
|
run={run}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<AgentActionsDropdown
|
<AgentActionsDropdown
|
||||||
agent={props.agent}
|
agent={agent}
|
||||||
run={props.run}
|
run={run}
|
||||||
agentGraphId={props.agent.graph_id}
|
agentGraphId={agent.graph_id}
|
||||||
onClearSelectedRun={props.onClearSelectedRun}
|
onClearSelectedRun={onClearSelectedRun}
|
||||||
/>
|
|
||||||
<CreateTemplateModal
|
|
||||||
isOpen={isCreateTemplateModalOpen}
|
|
||||||
onClose={() => setIsCreateTemplateModalOpen(false)}
|
|
||||||
onCreate={handleCreateTemplate}
|
|
||||||
run={props.run}
|
|
||||||
/>
|
/>
|
||||||
</SelectedActionsWrap>
|
</SelectedActionsWrap>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,15 +15,19 @@ import { useToast } from "@/components/molecules/Toast/use-toast";
|
|||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
interface Args {
|
interface Params {
|
||||||
agentGraphId: string;
|
agentGraphId: string;
|
||||||
run?: GraphExecution;
|
run?: GraphExecution;
|
||||||
agent?: LibraryAgent;
|
agent?: LibraryAgent;
|
||||||
onSelectRun?: (id: string) => void;
|
onSelectRun?: (id: string) => void;
|
||||||
onClearSelectedRun?: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSelectedRunActions(args: Args) {
|
export function useSelectedRunActions({
|
||||||
|
agentGraphId,
|
||||||
|
run,
|
||||||
|
agent,
|
||||||
|
onSelectRun,
|
||||||
|
}: Params) {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
@@ -31,8 +35,9 @@ export function useSelectedRunActions(args: Args) {
|
|||||||
const [isCreateTemplateModalOpen, setIsCreateTemplateModalOpen] =
|
const [isCreateTemplateModalOpen, setIsCreateTemplateModalOpen] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
|
||||||
const canStop =
|
const canStop = run?.status === "RUNNING" || run?.status === "QUEUED";
|
||||||
args.run?.status === "RUNNING" || args.run?.status === "QUEUED";
|
|
||||||
|
const canRunManually = !agent?.trigger_setup_info;
|
||||||
|
|
||||||
const { mutateAsync: stopRun, isPending: isStopping } =
|
const { mutateAsync: stopRun, isPending: isStopping } =
|
||||||
usePostV1StopGraphExecution();
|
usePostV1StopGraphExecution();
|
||||||
@@ -46,16 +51,16 @@ export function useSelectedRunActions(args: Args) {
|
|||||||
async function handleStopRun() {
|
async function handleStopRun() {
|
||||||
try {
|
try {
|
||||||
await stopRun({
|
await stopRun({
|
||||||
graphId: args.run?.graph_id ?? "",
|
graphId: run?.graph_id ?? "",
|
||||||
graphExecId: args.run?.id ?? "",
|
graphExecId: run?.id ?? "",
|
||||||
});
|
});
|
||||||
|
|
||||||
toast({ title: "Run stopped" });
|
toast({ title: "Run stopped" });
|
||||||
|
|
||||||
await queryClient.invalidateQueries({
|
await queryClient.invalidateQueries({
|
||||||
queryKey: getGetV1ListGraphExecutionsInfiniteQueryOptions(
|
queryKey:
|
||||||
args.agentGraphId,
|
getGetV1ListGraphExecutionsInfiniteQueryOptions(agentGraphId)
|
||||||
).queryKey,
|
.queryKey,
|
||||||
});
|
});
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
toast({
|
toast({
|
||||||
@@ -70,7 +75,7 @@ export function useSelectedRunActions(args: Args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleRunAgain() {
|
async function handleRunAgain() {
|
||||||
if (!args.run) {
|
if (!run) {
|
||||||
toast({
|
toast({
|
||||||
title: "Run not found",
|
title: "Run not found",
|
||||||
description: "Run not found",
|
description: "Run not found",
|
||||||
@@ -83,11 +88,11 @@ export function useSelectedRunActions(args: Args) {
|
|||||||
toast({ title: "Run started" });
|
toast({ title: "Run started" });
|
||||||
|
|
||||||
const res = await executeRun({
|
const res = await executeRun({
|
||||||
graphId: args.run.graph_id,
|
graphId: run.graph_id,
|
||||||
graphVersion: args.run.graph_version,
|
graphVersion: run.graph_version,
|
||||||
data: {
|
data: {
|
||||||
inputs: args.run.inputs || {},
|
inputs: run.inputs || {},
|
||||||
credentials_inputs: args.run.credential_inputs || {},
|
credentials_inputs: run.credential_inputs || {},
|
||||||
source: "library",
|
source: "library",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -95,12 +100,12 @@ export function useSelectedRunActions(args: Args) {
|
|||||||
const newRunId = res?.status === 200 ? (res?.data?.id ?? "") : "";
|
const newRunId = res?.status === 200 ? (res?.data?.id ?? "") : "";
|
||||||
|
|
||||||
await queryClient.invalidateQueries({
|
await queryClient.invalidateQueries({
|
||||||
queryKey: getGetV1ListGraphExecutionsInfiniteQueryOptions(
|
queryKey:
|
||||||
args.agentGraphId,
|
getGetV1ListGraphExecutionsInfiniteQueryOptions(agentGraphId)
|
||||||
).queryKey,
|
.queryKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (newRunId && args.onSelectRun) args.onSelectRun(newRunId);
|
if (newRunId && onSelectRun) onSelectRun(newRunId);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
toast({
|
toast({
|
||||||
title: "Failed to start run",
|
title: "Failed to start run",
|
||||||
@@ -118,7 +123,7 @@ export function useSelectedRunActions(args: Args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleCreateTemplate(name: string, description: string) {
|
async function handleCreateTemplate(name: string, description: string) {
|
||||||
if (!args.run) {
|
if (!run) {
|
||||||
toast({
|
toast({
|
||||||
title: "Run not found",
|
title: "Run not found",
|
||||||
description: "Cannot create template from missing run",
|
description: "Cannot create template from missing run",
|
||||||
@@ -132,7 +137,7 @@ export function useSelectedRunActions(args: Args) {
|
|||||||
data: {
|
data: {
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
graph_execution_id: args.run.id,
|
graph_execution_id: run.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -141,10 +146,10 @@ export function useSelectedRunActions(args: Args) {
|
|||||||
title: "Template created",
|
title: "Template created",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (args.agent) {
|
if (agent) {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: getGetV2ListPresetsQueryKey({
|
queryKey: getGetV2ListPresetsQueryKey({
|
||||||
graph_id: args.agent.graph_id,
|
graph_id: agent.graph_id,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -164,8 +169,8 @@ export function useSelectedRunActions(args: Args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Open in builder URL helper
|
// Open in builder URL helper
|
||||||
const openInBuilderHref = args.run
|
const openInBuilderHref = run
|
||||||
? `/build?flowID=${args.run.graph_id}&flowVersion=${args.run.graph_version}&flowExecutionID=${args.run.id}`
|
? `/build?flowID=${run.graph_id}&flowVersion=${run.graph_version}&flowExecutionID=${run.id}`
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -173,6 +178,7 @@ export function useSelectedRunActions(args: Args) {
|
|||||||
showDeleteDialog,
|
showDeleteDialog,
|
||||||
canStop,
|
canStop,
|
||||||
isStopping,
|
isStopping,
|
||||||
|
canRunManually,
|
||||||
isRunningAgain,
|
isRunningAgain,
|
||||||
handleShowDeleteDialog,
|
handleShowDeleteDialog,
|
||||||
handleStopRun,
|
handleStopRun,
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ export function SelectedTemplateView({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const templateOrTrigger = agent.trigger_setup_info ? "Trigger" : "Template";
|
||||||
const hasWebhook = !!template.webhook_id && template.webhook;
|
const hasWebhook = !!template.webhook_id && template.webhook;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -111,14 +112,14 @@ export function SelectedTemplateView({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<RunDetailCard title="Template Details">
|
<RunDetailCard title={`${templateOrTrigger} Details`}>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Input
|
<Input
|
||||||
id="template-name"
|
id="template-name"
|
||||||
label="Name"
|
label="Name"
|
||||||
value={name}
|
value={name}
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
placeholder="Enter template name"
|
placeholder={`Enter ${templateOrTrigger.toLowerCase()} name`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
@@ -128,7 +129,7 @@ export function SelectedTemplateView({
|
|||||||
rows={3}
|
rows={3}
|
||||||
value={description}
|
value={description}
|
||||||
onChange={(e) => setDescription(e.target.value)}
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
placeholder="Enter template description"
|
placeholder={`Enter ${templateOrTrigger.toLowerCase()} description`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</RunDetailCard>
|
</RunDetailCard>
|
||||||
|
|||||||
@@ -138,11 +138,21 @@ export function useSelectedTemplateView({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleStartTask() {
|
function handleStartTask() {
|
||||||
|
if (!query.data) return;
|
||||||
|
|
||||||
|
const inputsChanged =
|
||||||
|
JSON.stringify(inputs) !== JSON.stringify(query.data.inputs || {});
|
||||||
|
|
||||||
|
const credentialsChanged =
|
||||||
|
JSON.stringify(credentials) !==
|
||||||
|
JSON.stringify(query.data.credentials || {});
|
||||||
|
|
||||||
|
// Use changed unpersisted inputs if applicable
|
||||||
executeMutation.mutate({
|
executeMutation.mutate({
|
||||||
presetId: templateId,
|
presetId: templateId,
|
||||||
data: {
|
data: {
|
||||||
inputs: {},
|
inputs: inputsChanged ? inputs : undefined,
|
||||||
credential_inputs: {},
|
credential_inputs: credentialsChanged ? credentials : undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { useGetV2GetLibraryAgent } from "@/app/api/__generated__/endpoints/library/library";
|
import { useGetV2GetLibraryAgent } from "@/app/api/__generated__/endpoints/library/library";
|
||||||
|
import { useGetV2GetASpecificPreset } from "@/app/api/__generated__/endpoints/presets/presets";
|
||||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||||
|
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
||||||
import { okData } from "@/app/api/helpers";
|
import { okData } from "@/app/api/helpers";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { parseAsString, useQueryStates } from "nuqs";
|
import { parseAsString, useQueryStates } from "nuqs";
|
||||||
@@ -24,7 +26,7 @@ export function useNewAgentLibraryView() {
|
|||||||
const agentId = id as string;
|
const agentId = id as string;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: response,
|
data: agent,
|
||||||
isSuccess,
|
isSuccess,
|
||||||
error,
|
error,
|
||||||
} = useGetV2GetLibraryAgent(agentId, {
|
} = useGetV2GetLibraryAgent(agentId, {
|
||||||
@@ -41,6 +43,24 @@ export function useNewAgentLibraryView() {
|
|||||||
|
|
||||||
const activeTab = useMemo(() => parseTab(activeTabRaw), [activeTabRaw]);
|
const activeTab = useMemo(() => parseTab(activeTabRaw), [activeTabRaw]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: _template,
|
||||||
|
isSuccess: isTemplateLoaded,
|
||||||
|
isLoading: isTemplateLoading,
|
||||||
|
error: templateError,
|
||||||
|
} = useGetV2GetASpecificPreset(activeItem ?? "", {
|
||||||
|
query: {
|
||||||
|
enabled: Boolean(activeTab === "templates" && activeItem),
|
||||||
|
select: okData<LibraryAgentPreset>,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const activeTemplate =
|
||||||
|
isTemplateLoaded &&
|
||||||
|
activeTab === "templates" &&
|
||||||
|
_template?.id === activeItem
|
||||||
|
? _template
|
||||||
|
: null;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!activeTabRaw && !activeItem) {
|
if (!activeTabRaw && !activeItem) {
|
||||||
setQueryStates({
|
setQueryStates({
|
||||||
@@ -71,10 +91,10 @@ export function useNewAgentLibraryView() {
|
|||||||
const showSidebarLayout = sidebarLoading || hasAnyItems;
|
const showSidebarLayout = sidebarLoading || hasAnyItems;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (response) {
|
if (agent) {
|
||||||
document.title = `${response.name} - Library - AutoGPT Platform`;
|
document.title = `${agent.name} - Library - AutoGPT Platform`;
|
||||||
}
|
}
|
||||||
}, [response]);
|
}, [agent]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
@@ -135,9 +155,11 @@ export function useNewAgentLibraryView() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
agentId: id,
|
agentId: id,
|
||||||
|
agent,
|
||||||
ready: isSuccess,
|
ready: isSuccess,
|
||||||
error,
|
activeTemplate,
|
||||||
agent: response,
|
isTemplateLoading,
|
||||||
|
error: error || templateError,
|
||||||
hasAnyItems,
|
hasAnyItems,
|
||||||
showSidebarLayout,
|
showSidebarLayout,
|
||||||
activeItem,
|
activeItem,
|
||||||
|
|||||||
Reference in New Issue
Block a user