mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-11 16:18:07 -05:00
Compare commits
1 Commits
master
...
fix/flag-r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e34a055ed4 |
@@ -65,3 +65,125 @@ export function parseCronDescription(cron: string): string {
|
||||
|
||||
return cron; // Fallback to showing the raw cron
|
||||
}
|
||||
|
||||
export function getMissingRequiredInputs(
|
||||
inputSchema: any,
|
||||
values: Record<string, any>,
|
||||
): string[] {
|
||||
if (!inputSchema || typeof inputSchema !== "object") return [];
|
||||
const required: string[] = Array.isArray(inputSchema.required)
|
||||
? inputSchema.required
|
||||
: [];
|
||||
const properties: Record<string, any> = inputSchema.properties || {};
|
||||
return required.filter((key) => {
|
||||
const field = properties[key];
|
||||
if (field?.hidden) return false;
|
||||
return isEmpty(values[key]);
|
||||
});
|
||||
}
|
||||
|
||||
export function getMissingCredentials(
|
||||
credentialsProperties: Record<string, any> | undefined,
|
||||
values: Record<string, any>,
|
||||
): string[] {
|
||||
const props = credentialsProperties || {};
|
||||
return Object.keys(props).filter((key) => !(key in values));
|
||||
}
|
||||
|
||||
type DeriveReadinessParams = {
|
||||
inputSchema: any;
|
||||
credentialsProperties?: Record<string, any>;
|
||||
values: Record<string, any>;
|
||||
credentialsValues: Record<string, any>;
|
||||
};
|
||||
|
||||
export function deriveReadiness(params: DeriveReadinessParams): {
|
||||
missingInputs: string[];
|
||||
missingCredentials: string[];
|
||||
credentialsRequired: boolean;
|
||||
allRequiredInputsAreSet: boolean;
|
||||
} {
|
||||
const missingInputs = getMissingRequiredInputs(
|
||||
params.inputSchema,
|
||||
params.values,
|
||||
);
|
||||
const credentialsRequired =
|
||||
Object.keys(params.credentialsProperties || {}).length > 0;
|
||||
const missingCredentials = getMissingCredentials(
|
||||
params.credentialsProperties,
|
||||
params.credentialsValues,
|
||||
);
|
||||
const allRequiredInputsAreSet =
|
||||
missingInputs.length === 0 &&
|
||||
(!credentialsRequired || missingCredentials.length === 0);
|
||||
return {
|
||||
missingInputs,
|
||||
missingCredentials,
|
||||
credentialsRequired,
|
||||
allRequiredInputsAreSet,
|
||||
};
|
||||
}
|
||||
|
||||
export function getVisibleInputFields(inputSchema: any): Record<string, any> {
|
||||
if (
|
||||
!inputSchema ||
|
||||
typeof inputSchema !== "object" ||
|
||||
!("properties" in inputSchema) ||
|
||||
!inputSchema.properties
|
||||
) {
|
||||
return {} as Record<string, any>;
|
||||
}
|
||||
const properties = inputSchema.properties as Record<string, any>;
|
||||
return Object.fromEntries(
|
||||
Object.entries(properties).filter(([, subSchema]) => !subSchema?.hidden),
|
||||
);
|
||||
}
|
||||
|
||||
export function getCredentialFields(
|
||||
credentialsInputSchema: any,
|
||||
): Record<string, any> {
|
||||
if (
|
||||
!credentialsInputSchema ||
|
||||
typeof credentialsInputSchema !== "object" ||
|
||||
!("properties" in credentialsInputSchema) ||
|
||||
!credentialsInputSchema.properties
|
||||
) {
|
||||
return {} as Record<string, any>;
|
||||
}
|
||||
return credentialsInputSchema.properties as Record<string, any>;
|
||||
}
|
||||
|
||||
type CollectMissingFieldsOptions = {
|
||||
needScheduleName?: boolean;
|
||||
scheduleName: string;
|
||||
missingInputs: string[];
|
||||
credentialsRequired: boolean;
|
||||
allCredentialsAreSet: boolean;
|
||||
missingCredentials: string[];
|
||||
};
|
||||
|
||||
export function collectMissingFields(
|
||||
options: CollectMissingFieldsOptions,
|
||||
): string[] {
|
||||
const scheduleMissing =
|
||||
options.needScheduleName && !options.scheduleName ? ["schedule_name"] : [];
|
||||
|
||||
const missingCreds =
|
||||
options.credentialsRequired && !options.allCredentialsAreSet
|
||||
? options.missingCredentials.map((k) => `credentials:${k}`)
|
||||
: [];
|
||||
|
||||
return ([] as string[])
|
||||
.concat(scheduleMissing)
|
||||
.concat(options.missingInputs)
|
||||
.concat(missingCreds);
|
||||
}
|
||||
|
||||
export function getErrorMessage(error: unknown): string {
|
||||
if (typeof error === "string") return error;
|
||||
if (error && typeof error === "object" && "message" in error) {
|
||||
const msg = (error as any).message;
|
||||
if (typeof msg === "string" && msg.trim().length > 0) return msg;
|
||||
}
|
||||
return "An unexpected error occurred.";
|
||||
}
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import { useState, useCallback, useMemo } from "react";
|
||||
import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
import { isEmpty } from "@/lib/utils";
|
||||
import { usePostV1ExecuteGraphAgent } from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import { usePostV1CreateExecutionSchedule as useCreateSchedule } from "@/app/api/__generated__/endpoints/schedules/schedules";
|
||||
import { usePostV2SetupTrigger } from "@/app/api/__generated__/endpoints/presets/presets";
|
||||
import { ExecuteGraphResponse } from "@/app/api/__generated__/models/executeGraphResponse";
|
||||
import { GraphExecutionJobInfo } from "@/app/api/__generated__/models/graphExecutionJobInfo";
|
||||
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
|
||||
import {
|
||||
collectMissingFields,
|
||||
getErrorMessage,
|
||||
deriveReadiness,
|
||||
getVisibleInputFields,
|
||||
getCredentialFields,
|
||||
} from "./helpers";
|
||||
|
||||
export type RunVariant =
|
||||
| "manual"
|
||||
@@ -44,68 +50,9 @@ export function useAgentRunModal(
|
||||
: "manual";
|
||||
|
||||
// API mutations
|
||||
const executeGraphMutation = usePostV1ExecuteGraphAgent({
|
||||
mutation: {
|
||||
onSuccess: (response) => {
|
||||
if (response.status === 200) {
|
||||
toast({
|
||||
title: "Agent execution started",
|
||||
});
|
||||
callbacks?.onRun?.(response.data);
|
||||
setIsOpen(false);
|
||||
}
|
||||
},
|
||||
onError: (error: any) => {
|
||||
toast({
|
||||
title: "❌ Failed to execute agent",
|
||||
description: error.message || "An unexpected error occurred.",
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const createScheduleMutation = useCreateSchedule({
|
||||
mutation: {
|
||||
onSuccess: (response) => {
|
||||
if (response.status === 200) {
|
||||
toast({
|
||||
title: "Schedule created",
|
||||
});
|
||||
callbacks?.onCreateSchedule?.(response.data);
|
||||
setIsOpen(false);
|
||||
}
|
||||
},
|
||||
onError: (error: any) => {
|
||||
toast({
|
||||
title: "❌ Failed to create schedule",
|
||||
description: error.message || "An unexpected error occurred.",
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const setupTriggerMutation = usePostV2SetupTrigger({
|
||||
mutation: {
|
||||
onSuccess: (response: any) => {
|
||||
if (response.status === 200) {
|
||||
toast({
|
||||
title: "Trigger setup complete",
|
||||
});
|
||||
callbacks?.onSetupTrigger?.(response.data);
|
||||
setIsOpen(false);
|
||||
}
|
||||
},
|
||||
onError: (error: any) => {
|
||||
toast({
|
||||
title: "❌ Failed to setup trigger",
|
||||
description: error.message || "An unexpected error occurred.",
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
const executeGraphMutation = usePostV1ExecuteGraphAgent();
|
||||
const createScheduleMutation = useCreateSchedule();
|
||||
const setupTriggerMutation = usePostV2SetupTrigger();
|
||||
|
||||
// Input schema validation
|
||||
const agentInputSchema = useMemo(
|
||||
@@ -113,84 +60,48 @@ export function useAgentRunModal(
|
||||
[agent.input_schema],
|
||||
);
|
||||
|
||||
const agentInputFields = useMemo(() => {
|
||||
if (
|
||||
!agentInputSchema ||
|
||||
typeof agentInputSchema !== "object" ||
|
||||
!("properties" in agentInputSchema) ||
|
||||
!agentInputSchema.properties
|
||||
) {
|
||||
return {};
|
||||
}
|
||||
const properties = agentInputSchema.properties as Record<string, any>;
|
||||
return Object.fromEntries(
|
||||
Object.entries(properties).filter(
|
||||
([_, subSchema]: [string, any]) => !subSchema.hidden,
|
||||
),
|
||||
);
|
||||
}, [agentInputSchema]);
|
||||
|
||||
const agentCredentialsInputFields = useMemo(() => {
|
||||
if (
|
||||
!agent.credentials_input_schema ||
|
||||
typeof agent.credentials_input_schema !== "object" ||
|
||||
!("properties" in agent.credentials_input_schema) ||
|
||||
!agent.credentials_input_schema.properties
|
||||
) {
|
||||
return {} as Record<string, any>;
|
||||
}
|
||||
return agent.credentials_input_schema.properties as Record<string, any>;
|
||||
}, [agent.credentials_input_schema]);
|
||||
|
||||
// Validation logic
|
||||
const [allRequiredInputsAreSetRaw, missingInputs] = useMemo(() => {
|
||||
const nonEmptyInputs = new Set(
|
||||
Object.keys(inputValues).filter((k) => !isEmpty(inputValues[k])),
|
||||
);
|
||||
const requiredInputs = new Set(
|
||||
(agentInputSchema.required as string[]) || [],
|
||||
);
|
||||
const missing = [...requiredInputs].filter(
|
||||
(input) => !nonEmptyInputs.has(input),
|
||||
);
|
||||
return [missing.length === 0, missing];
|
||||
}, [agentInputSchema.required, inputValues]);
|
||||
|
||||
const [allCredentialsAreSet, missingCredentials] = useMemo(() => {
|
||||
const availableCredentials = new Set(Object.keys(inputCredentials));
|
||||
const allCredentials = new Set(
|
||||
Object.keys(agentCredentialsInputFields || {}) ?? [],
|
||||
);
|
||||
const missing = [...allCredentials].filter(
|
||||
(key) => !availableCredentials.has(key),
|
||||
);
|
||||
return [missing.length === 0, missing];
|
||||
}, [agentCredentialsInputFields, inputCredentials]);
|
||||
|
||||
const credentialsRequired = useMemo(
|
||||
() => Object.keys(agentCredentialsInputFields || {}).length > 0,
|
||||
[agentCredentialsInputFields],
|
||||
const agentInputFields = useMemo(
|
||||
() => getVisibleInputFields(agentInputSchema),
|
||||
[agentInputSchema],
|
||||
);
|
||||
|
||||
// Final readiness flag combining inputs + credentials when credentials are shown
|
||||
const allRequiredInputsAreSet = useMemo(
|
||||
const agentCredentialsInputFields = useMemo(
|
||||
() => getCredentialFields(agent.credentials_input_schema),
|
||||
[agent.credentials_input_schema],
|
||||
);
|
||||
|
||||
// Validation logic (presence checks derived from schemas)
|
||||
const {
|
||||
missingInputs,
|
||||
missingCredentials,
|
||||
credentialsRequired,
|
||||
allRequiredInputsAreSet,
|
||||
} = useMemo(
|
||||
() =>
|
||||
allRequiredInputsAreSetRaw &&
|
||||
(!credentialsRequired || allCredentialsAreSet),
|
||||
[allRequiredInputsAreSetRaw, credentialsRequired, allCredentialsAreSet],
|
||||
deriveReadiness({
|
||||
inputSchema: agentInputSchema,
|
||||
credentialsProperties: agentCredentialsInputFields,
|
||||
values: inputValues,
|
||||
credentialsValues: inputCredentials,
|
||||
}),
|
||||
[
|
||||
agentInputSchema,
|
||||
agentCredentialsInputFields,
|
||||
inputValues,
|
||||
inputCredentials,
|
||||
],
|
||||
);
|
||||
|
||||
const notifyMissingRequirements = useCallback(
|
||||
(needScheduleName: boolean = false) => {
|
||||
const allMissingFields = (
|
||||
needScheduleName && !scheduleName ? ["schedule_name"] : []
|
||||
)
|
||||
.concat(missingInputs)
|
||||
.concat(
|
||||
credentialsRequired && !allCredentialsAreSet
|
||||
? missingCredentials.map((k) => `credentials:${k}`)
|
||||
: [],
|
||||
);
|
||||
const allMissingFields = collectMissingFields({
|
||||
needScheduleName,
|
||||
scheduleName,
|
||||
missingInputs,
|
||||
credentialsRequired,
|
||||
allCredentialsAreSet: missingCredentials.length === 0,
|
||||
missingCredentials,
|
||||
});
|
||||
|
||||
toast({
|
||||
title: "⚠️ Missing required inputs",
|
||||
@@ -203,21 +114,30 @@ export function useAgentRunModal(
|
||||
scheduleName,
|
||||
toast,
|
||||
credentialsRequired,
|
||||
allCredentialsAreSet,
|
||||
missingCredentials,
|
||||
],
|
||||
);
|
||||
|
||||
// Action handlers
|
||||
const handleRun = useCallback(() => {
|
||||
function showError(title: string, error: unknown) {
|
||||
toast({
|
||||
title,
|
||||
description: getErrorMessage(error),
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
|
||||
async function handleRun() {
|
||||
if (!allRequiredInputsAreSet) {
|
||||
notifyMissingRequirements();
|
||||
return;
|
||||
}
|
||||
|
||||
if (defaultRunType === "automatic-trigger") {
|
||||
const shouldUseTrigger = defaultRunType === "automatic-trigger";
|
||||
|
||||
if (shouldUseTrigger) {
|
||||
// Setup trigger
|
||||
if (!scheduleName.trim()) {
|
||||
const hasScheduleName = scheduleName.trim().length > 0;
|
||||
if (!hasScheduleName) {
|
||||
toast({
|
||||
title: "⚠️ Trigger name required",
|
||||
description: "Please provide a name for your trigger.",
|
||||
@@ -225,50 +145,63 @@ export function useAgentRunModal(
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setupTriggerMutation.mutate({
|
||||
data: {
|
||||
name: presetName || scheduleName,
|
||||
description: presetDescription || `Trigger for ${agent.name}`,
|
||||
graph_id: agent.graph_id,
|
||||
graph_version: agent.graph_version,
|
||||
trigger_config: inputValues,
|
||||
agent_credentials: inputCredentials,
|
||||
},
|
||||
});
|
||||
try {
|
||||
const nameToUse = presetName || scheduleName;
|
||||
const descriptionToUse =
|
||||
presetDescription || `Trigger for ${agent.name}`;
|
||||
const response = await setupTriggerMutation.mutateAsync({
|
||||
data: {
|
||||
name: nameToUse,
|
||||
description: descriptionToUse,
|
||||
graph_id: agent.graph_id,
|
||||
graph_version: agent.graph_version,
|
||||
trigger_config: inputValues,
|
||||
agent_credentials: inputCredentials,
|
||||
},
|
||||
});
|
||||
if (response.status === 200) {
|
||||
toast({ title: "Trigger setup complete" });
|
||||
callbacks?.onSetupTrigger?.(response.data);
|
||||
setIsOpen(false);
|
||||
} else {
|
||||
throw new Error(JSON.stringify(response?.data?.detail));
|
||||
}
|
||||
} catch (error: any) {
|
||||
showError("❌ Failed to setup trigger", error);
|
||||
}
|
||||
} else {
|
||||
// Manual execution
|
||||
executeGraphMutation.mutate({
|
||||
graphId: agent.graph_id,
|
||||
graphVersion: agent.graph_version,
|
||||
data: {
|
||||
inputs: inputValues,
|
||||
credentials_inputs: inputCredentials,
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [
|
||||
allRequiredInputsAreSet,
|
||||
defaultRunType,
|
||||
scheduleName,
|
||||
inputValues,
|
||||
inputCredentials,
|
||||
agent,
|
||||
presetName,
|
||||
presetDescription,
|
||||
notifyMissingRequirements,
|
||||
setupTriggerMutation,
|
||||
executeGraphMutation,
|
||||
toast,
|
||||
]);
|
||||
try {
|
||||
const response = await executeGraphMutation.mutateAsync({
|
||||
graphId: agent.graph_id,
|
||||
graphVersion: agent.graph_version,
|
||||
data: {
|
||||
inputs: inputValues,
|
||||
credentials_inputs: inputCredentials,
|
||||
},
|
||||
});
|
||||
|
||||
const handleSchedule = useCallback(() => {
|
||||
if (response.status === 200) {
|
||||
toast({ title: "Agent execution started" });
|
||||
callbacks?.onRun?.(response.data);
|
||||
setIsOpen(false);
|
||||
} else {
|
||||
throw new Error(JSON.stringify(response?.data?.detail));
|
||||
}
|
||||
} catch (error: any) {
|
||||
showError("Failed to execute agent", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSchedule() {
|
||||
if (!allRequiredInputsAreSet) {
|
||||
notifyMissingRequirements(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!scheduleName.trim()) {
|
||||
const hasScheduleName = scheduleName.trim().length > 0;
|
||||
if (!hasScheduleName) {
|
||||
toast({
|
||||
title: "⚠️ Schedule name required",
|
||||
description: "Please provide a name for your schedule.",
|
||||
@@ -276,28 +209,27 @@ export function useAgentRunModal(
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
createScheduleMutation.mutate({
|
||||
graphId: agent.graph_id,
|
||||
data: {
|
||||
name: presetName || scheduleName,
|
||||
cron: cronExpression,
|
||||
inputs: inputValues,
|
||||
graph_version: agent.graph_version,
|
||||
credentials: inputCredentials,
|
||||
},
|
||||
});
|
||||
}, [
|
||||
allRequiredInputsAreSet,
|
||||
scheduleName,
|
||||
cronExpression,
|
||||
inputValues,
|
||||
inputCredentials,
|
||||
agent,
|
||||
notifyMissingRequirements,
|
||||
createScheduleMutation,
|
||||
toast,
|
||||
]);
|
||||
try {
|
||||
const nameToUse = presetName || scheduleName;
|
||||
const response = await createScheduleMutation.mutateAsync({
|
||||
graphId: agent.graph_id,
|
||||
data: {
|
||||
name: nameToUse,
|
||||
cron: cronExpression,
|
||||
inputs: inputValues,
|
||||
graph_version: agent.graph_version,
|
||||
credentials: inputCredentials,
|
||||
},
|
||||
});
|
||||
if (response.status === 200) {
|
||||
toast({ title: "Schedule created" });
|
||||
callbacks?.onCreateSchedule?.(response.data);
|
||||
setIsOpen(false);
|
||||
}
|
||||
} catch (error: any) {
|
||||
showError("❌ Failed to create schedule", error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleShowSchedule() {
|
||||
// Initialize with sensible defaults when entering schedule view
|
||||
@@ -342,10 +274,6 @@ export function useAgentRunModal(
|
||||
cronExpression,
|
||||
allRequiredInputsAreSet,
|
||||
missingInputs,
|
||||
// Expose credential readiness for any UI hints if needed
|
||||
// but enforcement is already applied in allRequiredInputsAreSet
|
||||
// allCredentialsAreSet,
|
||||
// missingCredentials,
|
||||
agentInputFields,
|
||||
agentCredentialsInputFields,
|
||||
hasInputFields,
|
||||
|
||||
@@ -11,9 +11,16 @@ const envEnabled = process.env.NEXT_PUBLIC_LAUNCHDARKLY_ENABLED === "true";
|
||||
|
||||
export function LaunchDarklyProvider({ children }: { children: ReactNode }) {
|
||||
const { user, isUserLoading } = useSupabase();
|
||||
const isCloud = getBehaveAs() === BehaveAs.CLOUD;
|
||||
const isCloud = true;
|
||||
const isLaunchDarklyConfigured = isCloud && envEnabled && clientId;
|
||||
|
||||
console.log({
|
||||
clientId,
|
||||
envEnabled,
|
||||
isCloud,
|
||||
isLaunchDarklyConfigured,
|
||||
});
|
||||
|
||||
const context = useMemo(() => {
|
||||
if (isUserLoading || !user) {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user