mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-11 16:18:07 -05:00
Compare commits
22 Commits
feat/execu
...
feat/agent
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee11623735 | ||
|
|
0bb160e930 | ||
|
|
81a09738dc | ||
|
|
6feedafd7d | ||
|
|
547da633c4 | ||
|
|
6d13dfc688 | ||
|
|
d0d498fa66 | ||
|
|
c843dee317 | ||
|
|
db969c1bf8 | ||
|
|
690fac91e4 | ||
|
|
5368fdc998 | ||
|
|
b9d293f181 | ||
|
|
acbcef77b2 | ||
|
|
e902848e04 | ||
|
|
cd917ec919 | ||
|
|
8ae37491e4 | ||
|
|
f45e5e0d59 | ||
|
|
1231236d87 | ||
|
|
4db0792ade | ||
|
|
81cb6fb1e6 | ||
|
|
c16598eed6 | ||
|
|
7706740308 |
@@ -75,6 +75,7 @@
|
||||
"moment": "2.30.1",
|
||||
"next": "15.3.5",
|
||||
"next-themes": "0.4.6",
|
||||
"nuqs": "2.4.3",
|
||||
"party-js": "2.2.0",
|
||||
"react": "18.3.1",
|
||||
"react-day-picker": "9.8.0",
|
||||
|
||||
33
autogpt_platform/frontend/pnpm-lock.yaml
generated
33
autogpt_platform/frontend/pnpm-lock.yaml
generated
@@ -155,6 +155,9 @@ importers:
|
||||
next-themes:
|
||||
specifier: 0.4.6
|
||||
version: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
nuqs:
|
||||
specifier: 2.4.3
|
||||
version: 2.4.3(next@15.3.5(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
|
||||
party-js:
|
||||
specifier: 2.2.0
|
||||
version: 2.2.0
|
||||
@@ -5329,6 +5332,9 @@ packages:
|
||||
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
||||
mitt@3.0.1:
|
||||
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
||||
|
||||
module-details-from-path@1.0.4:
|
||||
resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==}
|
||||
|
||||
@@ -5459,6 +5465,24 @@ packages:
|
||||
nth-check@2.1.1:
|
||||
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||
|
||||
nuqs@2.4.3:
|
||||
resolution: {integrity: sha512-BgtlYpvRwLYiJuWzxt34q2bXu/AIS66sLU1QePIMr2LWkb+XH0vKXdbLSgn9t6p7QKzwI7f38rX3Wl9llTXQ8Q==}
|
||||
peerDependencies:
|
||||
'@remix-run/react': '>=2'
|
||||
next: '>=14.2.0'
|
||||
react: '>=18.2.0 || ^19.0.0-0'
|
||||
react-router: ^6 || ^7
|
||||
react-router-dom: ^6 || ^7
|
||||
peerDependenciesMeta:
|
||||
'@remix-run/react':
|
||||
optional: true
|
||||
next:
|
||||
optional: true
|
||||
react-router:
|
||||
optional: true
|
||||
react-router-dom:
|
||||
optional: true
|
||||
|
||||
oas-kit-common@1.0.8:
|
||||
resolution: {integrity: sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==}
|
||||
|
||||
@@ -12998,6 +13022,8 @@ snapshots:
|
||||
|
||||
minipass@7.1.2: {}
|
||||
|
||||
mitt@3.0.1: {}
|
||||
|
||||
module-details-from-path@1.0.4: {}
|
||||
|
||||
moment@2.30.1: {}
|
||||
@@ -13160,6 +13186,13 @@ snapshots:
|
||||
dependencies:
|
||||
boolbase: 1.0.0
|
||||
|
||||
nuqs@2.4.3(next@15.3.5(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
mitt: 3.0.1
|
||||
react: 18.3.1
|
||||
optionalDependencies:
|
||||
next: 15.3.5(@babel/core@7.28.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
|
||||
oas-kit-common@1.0.8:
|
||||
dependencies:
|
||||
fast-safe-stringify: 2.1.1
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { useQueryState } from "nuqs";
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
@@ -45,6 +46,7 @@ import { useToast } from "@/components/molecules/Toast/use-toast";
|
||||
|
||||
export default function AgentRunsPage(): React.ReactElement {
|
||||
const { id: agentID }: { id: LibraryAgentID } = useParams();
|
||||
const [executionId, setExecutionId] = useQueryState("executionId");
|
||||
const { toast } = useToast();
|
||||
const router = useRouter();
|
||||
const api = useBackendAPI();
|
||||
@@ -202,6 +204,13 @@ export default function AgentRunsPage(): React.ReactElement {
|
||||
selectPreset,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (executionId) {
|
||||
selectRun(executionId as GraphExecutionID);
|
||||
setExecutionId(null);
|
||||
}
|
||||
}, [executionId, selectRun, setExecutionId]);
|
||||
|
||||
// Initial load
|
||||
useEffect(() => {
|
||||
refreshPageData();
|
||||
@@ -468,7 +477,7 @@ export default function AgentRunsPage(): React.ReactElement {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container justify-stretch p-0 lg:flex">
|
||||
<div className="container justify-stretch p-0 pt-16 lg:flex">
|
||||
{/* Sidebar w/ list of runs */}
|
||||
{/* TODO: render this below header in sm and md layouts */}
|
||||
<AgentRunsSelectorList
|
||||
|
||||
@@ -23,6 +23,7 @@ export async function login(
|
||||
return await Sentry.withServerActionInstrumentation("login", {}, async () => {
|
||||
const supabase = await getServerSupabase();
|
||||
const api = new BackendAPI();
|
||||
const isVercelPreview = process.env.VERCEL_ENV === "preview";
|
||||
|
||||
if (!supabase) {
|
||||
redirect("/error");
|
||||
@@ -30,7 +31,7 @@ export async function login(
|
||||
|
||||
// Verify Turnstile token if provided
|
||||
const success = await verifyTurnstileToken(turnstileToken, "login");
|
||||
if (!success) {
|
||||
if (!success && !isVercelPreview) {
|
||||
return "CAPTCHA verification failed. Please try again.";
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ export default function LoginPage() {
|
||||
isLoading,
|
||||
isLoggedIn,
|
||||
isCloudEnv,
|
||||
shouldNotRenderCaptcha,
|
||||
isUserLoading,
|
||||
isGoogleLoading,
|
||||
showNotAllowedModal,
|
||||
@@ -85,16 +86,18 @@ export default function LoginPage() {
|
||||
/>
|
||||
|
||||
{/* Turnstile CAPTCHA Component */}
|
||||
<Turnstile
|
||||
key={captchaKey}
|
||||
siteKey={turnstile.siteKey}
|
||||
onVerify={turnstile.handleVerify}
|
||||
onExpire={turnstile.handleExpire}
|
||||
onError={turnstile.handleError}
|
||||
setWidgetId={turnstile.setWidgetId}
|
||||
action="login"
|
||||
shouldRender={turnstile.shouldRender}
|
||||
/>
|
||||
{shouldNotRenderCaptcha ? null : (
|
||||
<Turnstile
|
||||
key={captchaKey}
|
||||
siteKey={turnstile.siteKey}
|
||||
onVerify={turnstile.handleVerify}
|
||||
onExpire={turnstile.handleExpire}
|
||||
onError={turnstile.handleError}
|
||||
setWidgetId={turnstile.setWidgetId}
|
||||
action="login"
|
||||
shouldRender={turnstile.shouldRender}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant="primary"
|
||||
|
||||
@@ -28,6 +28,8 @@ export function useLoginPage() {
|
||||
resetOnError: true,
|
||||
});
|
||||
|
||||
const shouldNotRenderCaptcha = isVercelPreview || turnstile.verified;
|
||||
|
||||
const form = useForm<z.infer<typeof loginFormSchema>>({
|
||||
resolver: zodResolver(loginFormSchema),
|
||||
defaultValues: {
|
||||
@@ -125,6 +127,7 @@ export function useLoginPage() {
|
||||
isLoading,
|
||||
isCloudEnv,
|
||||
isUserLoading,
|
||||
shouldNotRenderCaptcha,
|
||||
isGoogleLoading,
|
||||
showNotAllowedModal,
|
||||
isSupabaseAvailable: !!supabase,
|
||||
|
||||
@@ -18,6 +18,7 @@ export async function signup(
|
||||
{},
|
||||
async () => {
|
||||
const supabase = await getServerSupabase();
|
||||
const isVercelPreview = process.env.VERCEL_ENV === "preview";
|
||||
|
||||
if (!supabase) {
|
||||
redirect("/error");
|
||||
@@ -25,7 +26,7 @@ export async function signup(
|
||||
|
||||
// Verify Turnstile token if provided
|
||||
const success = await verifyTurnstileToken(turnstileToken, "signup");
|
||||
if (!success) {
|
||||
if (!success && !isVercelPreview) {
|
||||
return "CAPTCHA verification failed. Please try again.";
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ export default function SignupPage() {
|
||||
isLoading,
|
||||
isCloudEnv,
|
||||
isUserLoading,
|
||||
shouldNotRenderCaptcha,
|
||||
isGoogleLoading,
|
||||
showNotAllowedModal,
|
||||
isSupabaseAvailable,
|
||||
@@ -163,16 +164,18 @@ export default function SignupPage() {
|
||||
/>
|
||||
|
||||
{/* Turnstile CAPTCHA Component */}
|
||||
<Turnstile
|
||||
key={captchaKey}
|
||||
siteKey={turnstile.siteKey}
|
||||
onVerify={turnstile.handleVerify}
|
||||
onExpire={turnstile.handleExpire}
|
||||
onError={turnstile.handleError}
|
||||
setWidgetId={turnstile.setWidgetId}
|
||||
action="signup"
|
||||
shouldRender={turnstile.shouldRender}
|
||||
/>
|
||||
{shouldNotRenderCaptcha ? null : (
|
||||
<Turnstile
|
||||
key={captchaKey}
|
||||
siteKey={turnstile.siteKey}
|
||||
onVerify={turnstile.handleVerify}
|
||||
onExpire={turnstile.handleExpire}
|
||||
onError={turnstile.handleError}
|
||||
setWidgetId={turnstile.setWidgetId}
|
||||
action="signup"
|
||||
shouldRender={turnstile.shouldRender}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant="primary"
|
||||
|
||||
@@ -30,6 +30,8 @@ export function useSignupPage() {
|
||||
resetOnError: true,
|
||||
});
|
||||
|
||||
const shouldNotRenderCaptcha = isVercelPreview || turnstile.verified;
|
||||
|
||||
const resetCaptcha = useCallback(() => {
|
||||
setCaptchaKey((k) => k + 1);
|
||||
turnstile.reset();
|
||||
@@ -132,6 +134,7 @@ export function useSignupPage() {
|
||||
isCloudEnv,
|
||||
isUserLoading,
|
||||
isGoogleLoading,
|
||||
shouldNotRenderCaptcha,
|
||||
showNotAllowedModal,
|
||||
isSupabaseAvailable: !!supabase,
|
||||
handleSubmit: form.handleSubmit(handleSignup),
|
||||
|
||||
@@ -39,6 +39,8 @@ import type { Graph } from "../../models/graph";
|
||||
|
||||
import type { GraphExecutionMeta } from "../../models/graphExecutionMeta";
|
||||
|
||||
import type { GraphMeta } from "../../models/graphMeta";
|
||||
|
||||
import type { GraphModel } from "../../models/graphModel";
|
||||
|
||||
import type { HTTPValidationError } from "../../models/hTTPValidationError";
|
||||
@@ -47,8 +49,6 @@ import type { PostV1ExecuteGraphAgentParams } from "../../models/postV1ExecuteGr
|
||||
|
||||
import type { PostV1StopGraphExecution200 } from "../../models/postV1StopGraphExecution200";
|
||||
|
||||
import type { PostV1StopGraphExecutionsParams } from "../../models/postV1StopGraphExecutionsParams";
|
||||
|
||||
import type { SetGraphActiveVersion } from "../../models/setGraphActiveVersion";
|
||||
|
||||
import { customMutator } from "../../../mutators/custom-mutator";
|
||||
@@ -59,7 +59,7 @@ type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
|
||||
* @summary List user graphs
|
||||
*/
|
||||
export type getV1ListUserGraphsResponse200 = {
|
||||
data: GraphModel[];
|
||||
data: GraphMeta[];
|
||||
status: 200;
|
||||
};
|
||||
|
||||
@@ -1610,130 +1610,6 @@ export const usePostV1StopGraphExecution = <
|
||||
|
||||
return useMutation(mutationOptions, queryClient);
|
||||
};
|
||||
/**
|
||||
* @summary Stop graph executions
|
||||
*/
|
||||
export type postV1StopGraphExecutionsResponse200 = {
|
||||
data: GraphExecutionMeta[];
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type postV1StopGraphExecutionsResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type postV1StopGraphExecutionsResponseComposite =
|
||||
| postV1StopGraphExecutionsResponse200
|
||||
| postV1StopGraphExecutionsResponse422;
|
||||
|
||||
export type postV1StopGraphExecutionsResponse =
|
||||
postV1StopGraphExecutionsResponseComposite & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export const getPostV1StopGraphExecutionsUrl = (
|
||||
params: PostV1StopGraphExecutionsParams,
|
||||
) => {
|
||||
const normalizedParams = new URLSearchParams();
|
||||
|
||||
Object.entries(params || {}).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
normalizedParams.append(key, value === null ? "null" : value.toString());
|
||||
}
|
||||
});
|
||||
|
||||
const stringifiedParams = normalizedParams.toString();
|
||||
|
||||
return stringifiedParams.length > 0
|
||||
? `/api/executions?${stringifiedParams}`
|
||||
: `/api/executions`;
|
||||
};
|
||||
|
||||
export const postV1StopGraphExecutions = async (
|
||||
params: PostV1StopGraphExecutionsParams,
|
||||
options?: RequestInit,
|
||||
): Promise<postV1StopGraphExecutionsResponse> => {
|
||||
return customMutator<postV1StopGraphExecutionsResponse>(
|
||||
getPostV1StopGraphExecutionsUrl(params),
|
||||
{
|
||||
...options,
|
||||
method: "POST",
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
export const getPostV1StopGraphExecutionsMutationOptions = <
|
||||
TError = HTTPValidationError,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof postV1StopGraphExecutions>>,
|
||||
TError,
|
||||
{ params: PostV1StopGraphExecutionsParams },
|
||||
TContext
|
||||
>;
|
||||
request?: SecondParameter<typeof customMutator>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof postV1StopGraphExecutions>>,
|
||||
TError,
|
||||
{ params: PostV1StopGraphExecutionsParams },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ["postV1StopGraphExecutions"];
|
||||
const { mutation: mutationOptions, request: requestOptions } = options
|
||||
? options.mutation &&
|
||||
"mutationKey" in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey }, request: undefined };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof postV1StopGraphExecutions>>,
|
||||
{ params: PostV1StopGraphExecutionsParams }
|
||||
> = (props) => {
|
||||
const { params } = props ?? {};
|
||||
|
||||
return postV1StopGraphExecutions(params, requestOptions);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type PostV1StopGraphExecutionsMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof postV1StopGraphExecutions>>
|
||||
>;
|
||||
|
||||
export type PostV1StopGraphExecutionsMutationError = HTTPValidationError;
|
||||
|
||||
/**
|
||||
* @summary Stop graph executions
|
||||
*/
|
||||
export const usePostV1StopGraphExecutions = <
|
||||
TError = HTTPValidationError,
|
||||
TContext = unknown,
|
||||
>(
|
||||
options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof postV1StopGraphExecutions>>,
|
||||
TError,
|
||||
{ params: PostV1StopGraphExecutionsParams },
|
||||
TContext
|
||||
>;
|
||||
request?: SecondParameter<typeof customMutator>;
|
||||
},
|
||||
queryClient?: QueryClient,
|
||||
): UseMutationResult<
|
||||
Awaited<ReturnType<typeof postV1StopGraphExecutions>>,
|
||||
TError,
|
||||
{ params: PostV1StopGraphExecutionsParams },
|
||||
TContext
|
||||
> => {
|
||||
const mutationOptions = getPostV1StopGraphExecutionsMutationOptions(options);
|
||||
|
||||
return useMutation(mutationOptions, queryClient);
|
||||
};
|
||||
/**
|
||||
* @summary Get all executions
|
||||
*/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -37,14 +37,10 @@ import type { HTTPValidationError } from "../../models/hTTPValidationError";
|
||||
|
||||
import type { LibraryAgent } from "../../models/libraryAgent";
|
||||
|
||||
import type { LibraryAgentPreset } from "../../models/libraryAgentPreset";
|
||||
|
||||
import type { LibraryAgentResponse } from "../../models/libraryAgentResponse";
|
||||
|
||||
import type { LibraryAgentUpdateRequest } from "../../models/libraryAgentUpdateRequest";
|
||||
|
||||
import type { TriggeredPresetSetupParams } from "../../models/triggeredPresetSetupParams";
|
||||
|
||||
import { customMutator } from "../../../mutators/custom-mutator";
|
||||
|
||||
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
|
||||
@@ -1588,117 +1584,3 @@ export const usePostV2ForkLibraryAgent = <
|
||||
|
||||
return useMutation(mutationOptions, queryClient);
|
||||
};
|
||||
/**
|
||||
* Sets up a webhook-triggered `LibraryAgentPreset` for a `LibraryAgent`.
|
||||
Returns the correspondingly created `LibraryAgentPreset` with `webhook_id` set.
|
||||
* @summary Setup Trigger
|
||||
*/
|
||||
export type postV2SetupTriggerResponse200 = {
|
||||
data: LibraryAgentPreset;
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type postV2SetupTriggerResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type postV2SetupTriggerResponseComposite =
|
||||
| postV2SetupTriggerResponse200
|
||||
| postV2SetupTriggerResponse422;
|
||||
|
||||
export type postV2SetupTriggerResponse = postV2SetupTriggerResponseComposite & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export const getPostV2SetupTriggerUrl = (libraryAgentId: string) => {
|
||||
return `/api/library/agents/${libraryAgentId}/setup-trigger`;
|
||||
};
|
||||
|
||||
export const postV2SetupTrigger = async (
|
||||
libraryAgentId: string,
|
||||
triggeredPresetSetupParams: TriggeredPresetSetupParams,
|
||||
options?: RequestInit,
|
||||
): Promise<postV2SetupTriggerResponse> => {
|
||||
return customMutator<postV2SetupTriggerResponse>(
|
||||
getPostV2SetupTriggerUrl(libraryAgentId),
|
||||
{
|
||||
...options,
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", ...options?.headers },
|
||||
body: JSON.stringify(triggeredPresetSetupParams),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
export const getPostV2SetupTriggerMutationOptions = <
|
||||
TError = HTTPValidationError,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof postV2SetupTrigger>>,
|
||||
TError,
|
||||
{ libraryAgentId: string; data: TriggeredPresetSetupParams },
|
||||
TContext
|
||||
>;
|
||||
request?: SecondParameter<typeof customMutator>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof postV2SetupTrigger>>,
|
||||
TError,
|
||||
{ libraryAgentId: string; data: TriggeredPresetSetupParams },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ["postV2SetupTrigger"];
|
||||
const { mutation: mutationOptions, request: requestOptions } = options
|
||||
? options.mutation &&
|
||||
"mutationKey" in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey }, request: undefined };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof postV2SetupTrigger>>,
|
||||
{ libraryAgentId: string; data: TriggeredPresetSetupParams }
|
||||
> = (props) => {
|
||||
const { libraryAgentId, data } = props ?? {};
|
||||
|
||||
return postV2SetupTrigger(libraryAgentId, data, requestOptions);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type PostV2SetupTriggerMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof postV2SetupTrigger>>
|
||||
>;
|
||||
export type PostV2SetupTriggerMutationBody = TriggeredPresetSetupParams;
|
||||
export type PostV2SetupTriggerMutationError = HTTPValidationError;
|
||||
|
||||
/**
|
||||
* @summary Setup Trigger
|
||||
*/
|
||||
export const usePostV2SetupTrigger = <
|
||||
TError = HTTPValidationError,
|
||||
TContext = unknown,
|
||||
>(
|
||||
options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof postV2SetupTrigger>>,
|
||||
TError,
|
||||
{ libraryAgentId: string; data: TriggeredPresetSetupParams },
|
||||
TContext
|
||||
>;
|
||||
request?: SecondParameter<typeof customMutator>;
|
||||
},
|
||||
queryClient?: QueryClient,
|
||||
): UseMutationResult<
|
||||
Awaited<ReturnType<typeof postV2SetupTrigger>>,
|
||||
TError,
|
||||
{ libraryAgentId: string; data: TriggeredPresetSetupParams },
|
||||
TContext
|
||||
> => {
|
||||
const mutationOptions = getPostV2SetupTriggerMutationOptions(options);
|
||||
|
||||
return useMutation(mutationOptions, queryClient);
|
||||
};
|
||||
|
||||
@@ -37,6 +37,8 @@ import type { PostV2CreateANewPresetBody } from "../../models/postV2CreateANewPr
|
||||
|
||||
import type { PostV2ExecuteAPreset200 } from "../../models/postV2ExecuteAPreset200";
|
||||
|
||||
import type { TriggeredPresetSetupRequest } from "../../models/triggeredPresetSetupRequest";
|
||||
|
||||
import { customMutator } from "../../../mutators/custom-mutator";
|
||||
|
||||
type SecondParameter<T extends (...args: never) => unknown> = Parameters<T>[1];
|
||||
@@ -779,6 +781,116 @@ export const useDeleteV2DeleteAPreset = <
|
||||
|
||||
return useMutation(mutationOptions, queryClient);
|
||||
};
|
||||
/**
|
||||
* Sets up a webhook-triggered `LibraryAgentPreset` for a `LibraryAgent`.
|
||||
Returns the correspondingly created `LibraryAgentPreset` with `webhook_id` set.
|
||||
* @summary Setup Trigger
|
||||
*/
|
||||
export type postV2SetupTriggerResponse200 = {
|
||||
data: LibraryAgentPreset;
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type postV2SetupTriggerResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type postV2SetupTriggerResponseComposite =
|
||||
| postV2SetupTriggerResponse200
|
||||
| postV2SetupTriggerResponse422;
|
||||
|
||||
export type postV2SetupTriggerResponse = postV2SetupTriggerResponseComposite & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export const getPostV2SetupTriggerUrl = () => {
|
||||
return `/api/library/presets/setup-trigger`;
|
||||
};
|
||||
|
||||
export const postV2SetupTrigger = async (
|
||||
triggeredPresetSetupRequest: TriggeredPresetSetupRequest,
|
||||
options?: RequestInit,
|
||||
): Promise<postV2SetupTriggerResponse> => {
|
||||
return customMutator<postV2SetupTriggerResponse>(getPostV2SetupTriggerUrl(), {
|
||||
...options,
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", ...options?.headers },
|
||||
body: JSON.stringify(triggeredPresetSetupRequest),
|
||||
});
|
||||
};
|
||||
|
||||
export const getPostV2SetupTriggerMutationOptions = <
|
||||
TError = HTTPValidationError,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof postV2SetupTrigger>>,
|
||||
TError,
|
||||
{ data: TriggeredPresetSetupRequest },
|
||||
TContext
|
||||
>;
|
||||
request?: SecondParameter<typeof customMutator>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof postV2SetupTrigger>>,
|
||||
TError,
|
||||
{ data: TriggeredPresetSetupRequest },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ["postV2SetupTrigger"];
|
||||
const { mutation: mutationOptions, request: requestOptions } = options
|
||||
? options.mutation &&
|
||||
"mutationKey" in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey }, request: undefined };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof postV2SetupTrigger>>,
|
||||
{ data: TriggeredPresetSetupRequest }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
return postV2SetupTrigger(data, requestOptions);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type PostV2SetupTriggerMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof postV2SetupTrigger>>
|
||||
>;
|
||||
export type PostV2SetupTriggerMutationBody = TriggeredPresetSetupRequest;
|
||||
export type PostV2SetupTriggerMutationError = HTTPValidationError;
|
||||
|
||||
/**
|
||||
* @summary Setup Trigger
|
||||
*/
|
||||
export const usePostV2SetupTrigger = <
|
||||
TError = HTTPValidationError,
|
||||
TContext = unknown,
|
||||
>(
|
||||
options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof postV2SetupTrigger>>,
|
||||
TError,
|
||||
{ data: TriggeredPresetSetupRequest },
|
||||
TContext
|
||||
>;
|
||||
request?: SecondParameter<typeof customMutator>;
|
||||
},
|
||||
queryClient?: QueryClient,
|
||||
): UseMutationResult<
|
||||
Awaited<ReturnType<typeof postV2SetupTrigger>>,
|
||||
TError,
|
||||
{ data: TriggeredPresetSetupRequest },
|
||||
TContext
|
||||
> => {
|
||||
const mutationOptions = getPostV2SetupTriggerMutationOptions(options);
|
||||
|
||||
return useMutation(mutationOptions, queryClient);
|
||||
};
|
||||
/**
|
||||
* Execute a preset with the given graph and node input for the current user.
|
||||
* @summary Execute a preset
|
||||
|
||||
@@ -24,4 +24,5 @@ export interface BaseGraphOutput {
|
||||
forked_from_version?: BaseGraphOutputForkedFromVersion;
|
||||
readonly input_schema: BaseGraphOutputInputSchema;
|
||||
readonly output_schema: BaseGraphOutputOutputSchema;
|
||||
readonly has_external_trigger: boolean;
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* OpenAPI spec version: 0.1
|
||||
*/
|
||||
import type { CredentialsMetaInputTitle } from "./credentialsMetaInputTitle";
|
||||
import type { ProviderName } from "./providerName";
|
||||
import type { CredentialsMetaInputType } from "./credentialsMetaInputType";
|
||||
|
||||
export interface CredentialsMetaInput {
|
||||
id: string;
|
||||
title?: CredentialsMetaInputTitle;
|
||||
provider: ProviderName;
|
||||
/** Provider name for integrations. Can be any string value, including custom provider names. */
|
||||
provider: string;
|
||||
type: CredentialsMetaInputType;
|
||||
}
|
||||
|
||||
29
autogpt_platform/frontend/src/app/api/__generated__/models/graphMeta.ts
generated
Normal file
29
autogpt_platform/frontend/src/app/api/__generated__/models/graphMeta.ts
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Generated by orval v7.10.0 🍺
|
||||
* Do not edit manually.
|
||||
* AutoGPT Agent Server
|
||||
* This server is used to execute agents that are created by the AutoGPT system.
|
||||
* OpenAPI spec version: 0.1
|
||||
*/
|
||||
import type { GraphMetaForkedFromId } from "./graphMetaForkedFromId";
|
||||
import type { GraphMetaForkedFromVersion } from "./graphMetaForkedFromVersion";
|
||||
import type { BaseGraphOutput } from "./baseGraphOutput";
|
||||
import type { GraphMetaInputSchema } from "./graphMetaInputSchema";
|
||||
import type { GraphMetaOutputSchema } from "./graphMetaOutputSchema";
|
||||
import type { GraphMetaCredentialsInputSchema } from "./graphMetaCredentialsInputSchema";
|
||||
|
||||
export interface GraphMeta {
|
||||
id?: string;
|
||||
version?: number;
|
||||
is_active?: boolean;
|
||||
name: string;
|
||||
description: string;
|
||||
forked_from_id?: GraphMetaForkedFromId;
|
||||
forked_from_version?: GraphMetaForkedFromVersion;
|
||||
sub_graphs?: BaseGraphOutput[];
|
||||
user_id: string;
|
||||
readonly input_schema: GraphMetaInputSchema;
|
||||
readonly output_schema: GraphMetaOutputSchema;
|
||||
readonly has_external_trigger: boolean;
|
||||
readonly credentials_input_schema: GraphMetaCredentialsInputSchema;
|
||||
}
|
||||
9
autogpt_platform/frontend/src/app/api/__generated__/models/graphMetaCredentialsInputSchema.ts
generated
Normal file
9
autogpt_platform/frontend/src/app/api/__generated__/models/graphMetaCredentialsInputSchema.ts
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Generated by orval v7.10.0 🍺
|
||||
* Do not edit manually.
|
||||
* AutoGPT Agent Server
|
||||
* This server is used to execute agents that are created by the AutoGPT system.
|
||||
* OpenAPI spec version: 0.1
|
||||
*/
|
||||
|
||||
export type GraphMetaCredentialsInputSchema = { [key: string]: unknown };
|
||||
9
autogpt_platform/frontend/src/app/api/__generated__/models/graphMetaForkedFromId.ts
generated
Normal file
9
autogpt_platform/frontend/src/app/api/__generated__/models/graphMetaForkedFromId.ts
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Generated by orval v7.10.0 🍺
|
||||
* Do not edit manually.
|
||||
* AutoGPT Agent Server
|
||||
* This server is used to execute agents that are created by the AutoGPT system.
|
||||
* OpenAPI spec version: 0.1
|
||||
*/
|
||||
|
||||
export type GraphMetaForkedFromId = string | null;
|
||||
9
autogpt_platform/frontend/src/app/api/__generated__/models/graphMetaForkedFromVersion.ts
generated
Normal file
9
autogpt_platform/frontend/src/app/api/__generated__/models/graphMetaForkedFromVersion.ts
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Generated by orval v7.10.0 🍺
|
||||
* Do not edit manually.
|
||||
* AutoGPT Agent Server
|
||||
* This server is used to execute agents that are created by the AutoGPT system.
|
||||
* OpenAPI spec version: 0.1
|
||||
*/
|
||||
|
||||
export type GraphMetaForkedFromVersion = number | null;
|
||||
9
autogpt_platform/frontend/src/app/api/__generated__/models/graphMetaInputSchema.ts
generated
Normal file
9
autogpt_platform/frontend/src/app/api/__generated__/models/graphMetaInputSchema.ts
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Generated by orval v7.10.0 🍺
|
||||
* Do not edit manually.
|
||||
* AutoGPT Agent Server
|
||||
* This server is used to execute agents that are created by the AutoGPT system.
|
||||
* OpenAPI spec version: 0.1
|
||||
*/
|
||||
|
||||
export type GraphMetaInputSchema = { [key: string]: unknown };
|
||||
9
autogpt_platform/frontend/src/app/api/__generated__/models/graphMetaOutputSchema.ts
generated
Normal file
9
autogpt_platform/frontend/src/app/api/__generated__/models/graphMetaOutputSchema.ts
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Generated by orval v7.10.0 🍺
|
||||
* Do not edit manually.
|
||||
* AutoGPT Agent Server
|
||||
* This server is used to execute agents that are created by the AutoGPT system.
|
||||
* OpenAPI spec version: 0.1
|
||||
*/
|
||||
|
||||
export type GraphMetaOutputSchema = { [key: string]: unknown };
|
||||
@@ -28,6 +28,6 @@ export interface GraphModel {
|
||||
user_id: string;
|
||||
readonly input_schema: GraphModelInputSchema;
|
||||
readonly output_schema: GraphModelOutputSchema;
|
||||
readonly has_external_trigger: boolean;
|
||||
readonly credentials_input_schema: GraphModelCredentialsInputSchema;
|
||||
readonly has_webhook_trigger: boolean;
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
* This server is used to execute agents that are created by the AutoGPT system.
|
||||
* OpenAPI spec version: 0.1
|
||||
*/
|
||||
import type { ProviderName } from "./providerName";
|
||||
import type { LibraryAgentTriggerInfoConfigSchema } from "./libraryAgentTriggerInfoConfigSchema";
|
||||
import type { LibraryAgentTriggerInfoCredentialsInputName } from "./libraryAgentTriggerInfoCredentialsInputName";
|
||||
|
||||
export interface LibraryAgentTriggerInfo {
|
||||
provider: ProviderName;
|
||||
/** Provider name for integrations. Can be any string value, including custom provider names. */
|
||||
provider: string;
|
||||
/** Input schema for the trigger block */
|
||||
config_schema: LibraryAgentTriggerInfoConfigSchema;
|
||||
credentials_input_name: LibraryAgentTriggerInfoCredentialsInputName;
|
||||
|
||||
18
autogpt_platform/frontend/src/app/api/__generated__/models/triggeredPresetSetupRequest.ts
generated
Normal file
18
autogpt_platform/frontend/src/app/api/__generated__/models/triggeredPresetSetupRequest.ts
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Generated by orval v7.10.0 🍺
|
||||
* Do not edit manually.
|
||||
* AutoGPT Agent Server
|
||||
* This server is used to execute agents that are created by the AutoGPT system.
|
||||
* OpenAPI spec version: 0.1
|
||||
*/
|
||||
import type { TriggeredPresetSetupRequestTriggerConfig } from "./triggeredPresetSetupRequestTriggerConfig";
|
||||
import type { TriggeredPresetSetupRequestAgentCredentials } from "./triggeredPresetSetupRequestAgentCredentials";
|
||||
|
||||
export interface TriggeredPresetSetupRequest {
|
||||
name: string;
|
||||
description?: string;
|
||||
graph_id: string;
|
||||
graph_version: number;
|
||||
trigger_config: TriggeredPresetSetupRequestTriggerConfig;
|
||||
agent_credentials?: TriggeredPresetSetupRequestAgentCredentials;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Generated by orval v7.10.0 🍺
|
||||
* Do not edit manually.
|
||||
* AutoGPT Agent Server
|
||||
* This server is used to execute agents that are created by the AutoGPT system.
|
||||
* OpenAPI spec version: 0.1
|
||||
*/
|
||||
import type { CredentialsMetaInput } from "./credentialsMetaInput";
|
||||
|
||||
export type TriggeredPresetSetupRequestAgentCredentials = {
|
||||
[key: string]: CredentialsMetaInput;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Generated by orval v7.10.0 🍺
|
||||
* Do not edit manually.
|
||||
* AutoGPT Agent Server
|
||||
* This server is used to execute agents that are created by the AutoGPT system.
|
||||
* OpenAPI spec version: 0.1
|
||||
*/
|
||||
|
||||
export type TriggeredPresetSetupRequestTriggerConfig = {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
@@ -5,13 +5,13 @@
|
||||
* This server is used to execute agents that are created by the AutoGPT system.
|
||||
* OpenAPI spec version: 0.1
|
||||
*/
|
||||
import type { ProviderName } from "./providerName";
|
||||
import type { WebhookConfig } from "./webhookConfig";
|
||||
|
||||
export interface Webhook {
|
||||
id?: string;
|
||||
user_id: string;
|
||||
provider: ProviderName;
|
||||
/** Provider name for integrations. Can be any string value, including custom provider names. */
|
||||
provider: string;
|
||||
credentials_id: string;
|
||||
webhook_type: string;
|
||||
resource: string;
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProviderName",
|
||||
"title": "The provider to initiate an OAuth flow for"
|
||||
"type": "string",
|
||||
"title": "The provider to initiate an OAuth flow for",
|
||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -64,8 +65,9 @@
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProviderName",
|
||||
"title": "The target provider for this OAuth exchange"
|
||||
"type": "string",
|
||||
"title": "The target provider for this OAuth exchange",
|
||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -133,8 +135,9 @@
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProviderName",
|
||||
"title": "The provider to list credentials for"
|
||||
"type": "string",
|
||||
"title": "The provider to list credentials for",
|
||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -173,8 +176,9 @@
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProviderName",
|
||||
"title": "The provider to create credentials for"
|
||||
"type": "string",
|
||||
"title": "The provider to create credentials for",
|
||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -253,8 +257,9 @@
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProviderName",
|
||||
"title": "The provider to retrieve credentials for"
|
||||
"type": "string",
|
||||
"title": "The provider to retrieve credentials for",
|
||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -315,8 +320,9 @@
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProviderName",
|
||||
"title": "The provider to delete credentials for"
|
||||
"type": "string",
|
||||
"title": "The provider to delete credentials for",
|
||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -380,8 +386,9 @@
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProviderName",
|
||||
"title": "Provider where the webhook was registered"
|
||||
"type": "string",
|
||||
"title": "Provider where the webhook was registered",
|
||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -436,6 +443,86 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/integrations/providers": {
|
||||
"get": {
|
||||
"tags": ["v1", "integrations"],
|
||||
"summary": "List Providers",
|
||||
"description": "Get a list of all available provider names.\n\nReturns both statically defined providers (from ProviderName enum)\nand dynamically registered providers (from SDK decorators).\n\nNote: The complete list of provider names is also available as a constant\nin the generated TypeScript client via PROVIDER_NAMES.",
|
||||
"operationId": "getV1ListProviders",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": { "type": "string" },
|
||||
"type": "array",
|
||||
"title": "Response Getv1Listproviders"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/integrations/providers/names": {
|
||||
"get": {
|
||||
"tags": ["v1", "integrations"],
|
||||
"summary": "Get Provider Names",
|
||||
"description": "Get all provider names in a structured format.\n\nThis endpoint is specifically designed to expose the provider names\nin the OpenAPI schema so that code generators like Orval can create\nappropriate TypeScript constants.",
|
||||
"operationId": "getV1GetProviderNames",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProviderNamesResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/integrations/providers/constants": {
|
||||
"get": {
|
||||
"tags": ["v1", "integrations"],
|
||||
"summary": "Get Provider Constants",
|
||||
"description": "Get provider names as constants.\n\nThis endpoint returns a model with provider names as constants,\nspecifically designed for OpenAPI code generation tools to create\nTypeScript constants.",
|
||||
"operationId": "getV1GetProviderConstants",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ProviderConstants" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/integrations/providers/enum-example": {
|
||||
"get": {
|
||||
"tags": ["v1", "integrations"],
|
||||
"summary": "Get Provider Enum Example",
|
||||
"description": "Example endpoint that uses the CompleteProviderNames enum.\n\nThis endpoint exists to ensure that the CompleteProviderNames enum is included\nin the OpenAPI schema, which will cause Orval to generate it as a\nTypeScript enum/constant.",
|
||||
"operationId": "getV1GetProviderEnumExample",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProviderEnumResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/analytics/log_raw_metric": {
|
||||
"post": {
|
||||
"tags": ["v1", "analytics"],
|
||||
@@ -1018,7 +1105,7 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": { "$ref": "#/components/schemas/GraphModel" },
|
||||
"items": { "$ref": "#/components/schemas/GraphMeta" },
|
||||
"type": "array",
|
||||
"title": "Response Getv1List User Graphs"
|
||||
}
|
||||
@@ -1415,49 +1502,6 @@
|
||||
}
|
||||
},
|
||||
"/api/executions": {
|
||||
"post": {
|
||||
"tags": ["v1", "graphs"],
|
||||
"summary": "Stop graph executions",
|
||||
"operationId": "postV1Stop graph executions",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "graph_id",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"schema": { "type": "string", "title": "Graph Id" }
|
||||
},
|
||||
{
|
||||
"name": "graph_exec_id",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"schema": { "type": "string", "title": "Graph Exec Id" }
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/GraphExecutionMeta"
|
||||
},
|
||||
"title": "Response Postv1Stop Graph Executions"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"get": {
|
||||
"tags": ["v1", "graphs"],
|
||||
"summary": "Get all executions",
|
||||
@@ -1468,10 +1512,10 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/GraphExecutionMeta"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Response Getv1Get All Executions"
|
||||
}
|
||||
}
|
||||
@@ -3014,6 +3058,42 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/library/presets/setup-trigger": {
|
||||
"post": {
|
||||
"tags": ["v2", "presets"],
|
||||
"summary": "Setup Trigger",
|
||||
"description": "Sets up a webhook-triggered `LibraryAgentPreset` for a `LibraryAgent`.\nReturns the correspondingly created `LibraryAgentPreset` with `webhook_id` set.",
|
||||
"operationId": "postV2SetupTrigger",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TriggeredPresetSetupRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/LibraryAgentPreset" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/library/presets/{preset_id}/execute": {
|
||||
"post": {
|
||||
"tags": ["v2", "presets", "presets"],
|
||||
@@ -3401,55 +3481,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/library/agents/{library_agent_id}/setup-trigger": {
|
||||
"post": {
|
||||
"tags": ["v2", "library", "private"],
|
||||
"summary": "Setup Trigger",
|
||||
"description": "Sets up a webhook-triggered `LibraryAgentPreset` for a `LibraryAgent`.\nReturns the correspondingly created `LibraryAgentPreset` with `webhook_id` set.",
|
||||
"operationId": "postV2SetupTrigger",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "library_agent_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"description": "ID of the library agent",
|
||||
"title": "Library Agent Id"
|
||||
},
|
||||
"description": "ID of the library agent"
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TriggeredPresetSetupParams"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/LibraryAgentPreset" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/otto/ask": {
|
||||
"post": {
|
||||
"tags": ["v2", "otto"],
|
||||
@@ -3837,10 +3868,21 @@
|
||||
"type": "object",
|
||||
"title": "Output Schema",
|
||||
"readOnly": true
|
||||
},
|
||||
"has_external_trigger": {
|
||||
"type": "boolean",
|
||||
"title": "Has External Trigger",
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["name", "description", "input_schema", "output_schema"],
|
||||
"required": [
|
||||
"name",
|
||||
"description",
|
||||
"input_schema",
|
||||
"output_schema",
|
||||
"has_external_trigger"
|
||||
],
|
||||
"title": "BaseGraph"
|
||||
},
|
||||
"Body_postV1Callback": {
|
||||
@@ -4101,7 +4143,11 @@
|
||||
"anyOf": [{ "type": "string" }, { "type": "null" }],
|
||||
"title": "Title"
|
||||
},
|
||||
"provider": { "$ref": "#/components/schemas/ProviderName" },
|
||||
"provider": {
|
||||
"type": "string",
|
||||
"title": "Provider",
|
||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["api_key", "oauth2", "user_password", "host_scoped"],
|
||||
@@ -4405,6 +4451,68 @@
|
||||
],
|
||||
"title": "GraphExecutionWithNodes"
|
||||
},
|
||||
"GraphMeta": {
|
||||
"properties": {
|
||||
"id": { "type": "string", "title": "Id" },
|
||||
"version": { "type": "integer", "title": "Version", "default": 1 },
|
||||
"is_active": {
|
||||
"type": "boolean",
|
||||
"title": "Is Active",
|
||||
"default": true
|
||||
},
|
||||
"name": { "type": "string", "title": "Name" },
|
||||
"description": { "type": "string", "title": "Description" },
|
||||
"forked_from_id": {
|
||||
"anyOf": [{ "type": "string" }, { "type": "null" }],
|
||||
"title": "Forked From Id"
|
||||
},
|
||||
"forked_from_version": {
|
||||
"anyOf": [{ "type": "integer" }, { "type": "null" }],
|
||||
"title": "Forked From Version"
|
||||
},
|
||||
"sub_graphs": {
|
||||
"items": { "$ref": "#/components/schemas/BaseGraph-Output" },
|
||||
"type": "array",
|
||||
"title": "Sub Graphs",
|
||||
"default": []
|
||||
},
|
||||
"user_id": { "type": "string", "title": "User Id" },
|
||||
"input_schema": {
|
||||
"additionalProperties": true,
|
||||
"type": "object",
|
||||
"title": "Input Schema",
|
||||
"readOnly": true
|
||||
},
|
||||
"output_schema": {
|
||||
"additionalProperties": true,
|
||||
"type": "object",
|
||||
"title": "Output Schema",
|
||||
"readOnly": true
|
||||
},
|
||||
"has_external_trigger": {
|
||||
"type": "boolean",
|
||||
"title": "Has External Trigger",
|
||||
"readOnly": true
|
||||
},
|
||||
"credentials_input_schema": {
|
||||
"additionalProperties": true,
|
||||
"type": "object",
|
||||
"title": "Credentials Input Schema",
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"description",
|
||||
"user_id",
|
||||
"input_schema",
|
||||
"output_schema",
|
||||
"has_external_trigger",
|
||||
"credentials_input_schema"
|
||||
],
|
||||
"title": "GraphMeta"
|
||||
},
|
||||
"GraphModel": {
|
||||
"properties": {
|
||||
"id": { "type": "string", "title": "Id" },
|
||||
@@ -4455,16 +4563,16 @@
|
||||
"title": "Output Schema",
|
||||
"readOnly": true
|
||||
},
|
||||
"has_external_trigger": {
|
||||
"type": "boolean",
|
||||
"title": "Has External Trigger",
|
||||
"readOnly": true
|
||||
},
|
||||
"credentials_input_schema": {
|
||||
"additionalProperties": true,
|
||||
"type": "object",
|
||||
"title": "Credentials Input Schema",
|
||||
"readOnly": true
|
||||
},
|
||||
"has_webhook_trigger": {
|
||||
"type": "boolean",
|
||||
"title": "Has Webhook Trigger",
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
@@ -4474,8 +4582,8 @@
|
||||
"user_id",
|
||||
"input_schema",
|
||||
"output_schema",
|
||||
"credentials_input_schema",
|
||||
"has_webhook_trigger"
|
||||
"has_external_trigger",
|
||||
"credentials_input_schema"
|
||||
],
|
||||
"title": "GraphModel"
|
||||
},
|
||||
@@ -4820,7 +4928,11 @@
|
||||
},
|
||||
"LibraryAgentTriggerInfo": {
|
||||
"properties": {
|
||||
"provider": { "$ref": "#/components/schemas/ProviderName" },
|
||||
"provider": {
|
||||
"type": "string",
|
||||
"title": "Provider",
|
||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
||||
},
|
||||
"config_schema": {
|
||||
"additionalProperties": true,
|
||||
"type": "object",
|
||||
@@ -5623,51 +5735,44 @@
|
||||
"required": ["name", "username", "description", "links"],
|
||||
"title": "ProfileDetails"
|
||||
},
|
||||
"ProviderName": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"aiml_api",
|
||||
"anthropic",
|
||||
"apollo",
|
||||
"compass",
|
||||
"discord",
|
||||
"d_id",
|
||||
"e2b",
|
||||
"exa",
|
||||
"fal",
|
||||
"generic_webhook",
|
||||
"github",
|
||||
"google",
|
||||
"google_maps",
|
||||
"groq",
|
||||
"http",
|
||||
"hubspot",
|
||||
"ideogram",
|
||||
"jina",
|
||||
"linear",
|
||||
"llama_api",
|
||||
"medium",
|
||||
"mem0",
|
||||
"notion",
|
||||
"nvidia",
|
||||
"ollama",
|
||||
"openai",
|
||||
"openweathermap",
|
||||
"open_router",
|
||||
"pinecone",
|
||||
"reddit",
|
||||
"replicate",
|
||||
"revid",
|
||||
"screenshotone",
|
||||
"slant3d",
|
||||
"smartlead",
|
||||
"smtp",
|
||||
"twitter",
|
||||
"todoist",
|
||||
"unreal_speech",
|
||||
"zerobounce"
|
||||
],
|
||||
"title": "ProviderName"
|
||||
"ProviderConstants": {
|
||||
"properties": {
|
||||
"PROVIDER_NAMES": {
|
||||
"additionalProperties": { "type": "string" },
|
||||
"type": "object",
|
||||
"title": "Provider Names",
|
||||
"description": "All available provider names as a constant mapping"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "ProviderConstants",
|
||||
"description": "Model that exposes all provider names as a constant in the OpenAPI schema.\nThis is designed to be converted by Orval into a TypeScript constant."
|
||||
},
|
||||
"ProviderEnumResponse": {
|
||||
"properties": {
|
||||
"provider": {
|
||||
"type": "string",
|
||||
"title": "Provider",
|
||||
"description": "A provider name from the complete list of providers"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["provider"],
|
||||
"title": "ProviderEnumResponse",
|
||||
"description": "Response containing a provider from the enum."
|
||||
},
|
||||
"ProviderNamesResponse": {
|
||||
"properties": {
|
||||
"providers": {
|
||||
"items": { "type": "string" },
|
||||
"type": "array",
|
||||
"title": "Providers",
|
||||
"description": "List of all available provider names"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "ProviderNamesResponse",
|
||||
"description": "Response containing list of all provider names."
|
||||
},
|
||||
"RefundRequest": {
|
||||
"properties": {
|
||||
@@ -6153,7 +6258,7 @@
|
||||
"required": ["transactions", "next_transaction_time"],
|
||||
"title": "TransactionHistory"
|
||||
},
|
||||
"TriggeredPresetSetupParams": {
|
||||
"TriggeredPresetSetupRequest": {
|
||||
"properties": {
|
||||
"name": { "type": "string", "title": "Name" },
|
||||
"description": {
|
||||
@@ -6161,6 +6266,8 @@
|
||||
"title": "Description",
|
||||
"default": ""
|
||||
},
|
||||
"graph_id": { "type": "string", "title": "Graph Id" },
|
||||
"graph_version": { "type": "integer", "title": "Graph Version" },
|
||||
"trigger_config": {
|
||||
"additionalProperties": true,
|
||||
"type": "object",
|
||||
@@ -6175,8 +6282,8 @@
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": ["name", "trigger_config"],
|
||||
"title": "TriggeredPresetSetupParams"
|
||||
"required": ["name", "graph_id", "graph_version", "trigger_config"],
|
||||
"title": "TriggeredPresetSetupRequest"
|
||||
},
|
||||
"TurnstileVerifyRequest": {
|
||||
"properties": {
|
||||
@@ -6441,7 +6548,11 @@
|
||||
"properties": {
|
||||
"id": { "type": "string", "title": "Id" },
|
||||
"user_id": { "type": "string", "title": "User Id" },
|
||||
"provider": { "$ref": "#/components/schemas/ProviderName" },
|
||||
"provider": {
|
||||
"type": "string",
|
||||
"title": "Provider",
|
||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
||||
},
|
||||
"credentials_id": { "type": "string", "title": "Credentials Id" },
|
||||
"webhook_type": { "type": "string", "title": "Webhook Type" },
|
||||
"resource": { "type": "string", "title": "Resource" },
|
||||
|
||||
@@ -1,31 +1,35 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
||||
import { ThemeProviderProps } from "next-themes";
|
||||
import { BackendAPIProvider } from "@/lib/autogpt-server-api/context";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import CredentialsProvider from "@/components/integrations/credentials-provider";
|
||||
import { LaunchDarklyProvider } from "@/components/feature-flag/feature-flag-provider";
|
||||
import CredentialsProvider from "@/components/integrations/credentials-provider";
|
||||
import OnboardingProvider from "@/components/onboarding/onboarding-provider";
|
||||
import { QueryClientProvider } from "@tanstack/react-query";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import { BackendAPIProvider } from "@/lib/autogpt-server-api/context";
|
||||
import { getQueryClient } from "@/lib/react-query/queryClient";
|
||||
import { QueryClientProvider } from "@tanstack/react-query";
|
||||
import {
|
||||
ThemeProvider as NextThemesProvider,
|
||||
ThemeProviderProps,
|
||||
} from "next-themes";
|
||||
import { NuqsAdapter } from "nuqs/adapters/next/app";
|
||||
|
||||
export function Providers({ children, ...props }: ThemeProviderProps) {
|
||||
const queryClient = getQueryClient();
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<NextThemesProvider {...props}>
|
||||
<BackendAPIProvider>
|
||||
<CredentialsProvider>
|
||||
<LaunchDarklyProvider>
|
||||
<OnboardingProvider>
|
||||
<TooltipProvider>{children}</TooltipProvider>
|
||||
</OnboardingProvider>
|
||||
</LaunchDarklyProvider>
|
||||
</CredentialsProvider>
|
||||
</BackendAPIProvider>
|
||||
</NextThemesProvider>
|
||||
<NuqsAdapter>
|
||||
<NextThemesProvider {...props}>
|
||||
<BackendAPIProvider>
|
||||
<CredentialsProvider>
|
||||
<LaunchDarklyProvider>
|
||||
<OnboardingProvider>
|
||||
<TooltipProvider>{children}</TooltipProvider>
|
||||
</OnboardingProvider>
|
||||
</LaunchDarklyProvider>
|
||||
</CredentialsProvider>
|
||||
</BackendAPIProvider>
|
||||
</NextThemesProvider>
|
||||
</NuqsAdapter>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
"use client";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Plus } from "lucide-react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
GraphExecutionID,
|
||||
GraphExecutionMeta,
|
||||
@@ -12,14 +11,15 @@ import {
|
||||
Schedule,
|
||||
ScheduleID,
|
||||
} from "@/lib/autogpt-server-api";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Button } from "@/components/agptui/Button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
import { agentRunStatusMap } from "@/components/agents/agent-run-status-chip";
|
||||
import AgentRunSummaryCard from "@/components/agents/agent-run-summary-card";
|
||||
import { Button } from "../atoms/Button/Button";
|
||||
|
||||
interface AgentRunsSelectorListProps {
|
||||
agent: LibraryAgent;
|
||||
@@ -72,17 +72,11 @@ export default function AgentRunsSelectorList({
|
||||
<aside className={cn("flex flex-col gap-4", className)}>
|
||||
{allowDraftNewRun && (
|
||||
<Button
|
||||
size="card"
|
||||
className={
|
||||
"mb-4 hidden h-16 w-72 items-center gap-2 py-6 lg:flex xl:w-80 " +
|
||||
(selectedView.type == "run" && !selectedView.id
|
||||
? "agpt-card-selected text-accent"
|
||||
: "")
|
||||
}
|
||||
className={"mb-4 hidden lg:flex"}
|
||||
onClick={onSelectDraftNewRun}
|
||||
leftIcon={<Plus className="h-6 w-6" />}
|
||||
>
|
||||
<Plus className="h-6 w-6" />
|
||||
<span>New {agent.has_external_trigger ? "trigger" : "run"}</span>
|
||||
New {agent.has_external_trigger ? "trigger" : "run"}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -112,7 +106,7 @@ export default function AgentRunsSelectorList({
|
||||
{/* New Run button - only in small layouts */}
|
||||
{allowDraftNewRun && (
|
||||
<Button
|
||||
size="card"
|
||||
size="large"
|
||||
className={
|
||||
"flex h-28 w-40 items-center gap-2 py-6 lg:hidden " +
|
||||
(selectedView.type == "run" && !selectedView.id
|
||||
@@ -120,9 +114,9 @@ export default function AgentRunsSelectorList({
|
||||
: "")
|
||||
}
|
||||
onClick={onSelectDraftNewRun}
|
||||
leftIcon={<Plus className="h-6 w-6" />}
|
||||
>
|
||||
<Plus className="h-6 w-6" />
|
||||
<span>New {agent.has_external_trigger ? "trigger" : "run"}</span>
|
||||
New {agent.has_external_trigger ? "trigger" : "run"}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { IconAutoGPTLogo, IconType } from "@/components/ui/icons";
|
||||
import Wallet from "../../agptui/Wallet";
|
||||
import { AccountMenu } from "./components/AccountMenu/AccountMenu";
|
||||
import { AgentActivityDropdown } from "./components/AgentActivityDropdown/AgentActivityDropdown";
|
||||
import { LoginButton } from "./components/LoginButton";
|
||||
import { MobileNavBar } from "./components/MobileNavbar/MobileNavBar";
|
||||
import { NavbarLink } from "./components/NavbarLink";
|
||||
@@ -33,6 +34,7 @@ export async function Navbar() {
|
||||
<div className="flex flex-1 items-center justify-end gap-4">
|
||||
{isLoggedIn ? (
|
||||
<div className="flex items-center gap-4">
|
||||
<AgentActivityDropdown />
|
||||
{profile && <Wallet />}
|
||||
<AccountMenu
|
||||
userName={profile?.username}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
"use client";
|
||||
|
||||
import { Text } from "@/components/atoms/Text/Text";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Bell } from "@phosphor-icons/react";
|
||||
import { useState } from "react";
|
||||
import { ActivityDropdown } from "./components/ActivityDropdown";
|
||||
import { formatNotificationCount } from "./helpers";
|
||||
import { useAgentActivityDropdown } from "./useAgentActivityDropdown";
|
||||
|
||||
export function AgentActivityDropdown() {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const { activeExecutions, recentCompletions, recentFailures } =
|
||||
useAgentActivityDropdown();
|
||||
|
||||
return (
|
||||
<Popover open={isOpen} onOpenChange={setIsOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<button
|
||||
className={`group relative h-[2.5rem] w-[2.5rem] rounded-full p-2 transition-colors hover:bg-white ${isOpen ? "bg-white" : ""}`}
|
||||
title="Agent Activity"
|
||||
>
|
||||
<Bell size={22} className="text-black" />
|
||||
|
||||
{activeExecutions.length > 0 && (
|
||||
<>
|
||||
{/* Running Agents Rotating Badge */}
|
||||
<div className="absolute right-[1px] top-[0.5px] flex h-5 w-5 items-center justify-center rounded-full bg-purple-600 text-[10px] font-medium text-white">
|
||||
{formatNotificationCount(activeExecutions.length)}
|
||||
<div className="absolute -inset-0.5 animate-spin rounded-full border-[3px] border-transparent border-r-purple-200 border-t-purple-200" />
|
||||
</div>
|
||||
{/* Running Agent Hover Hint */}
|
||||
<div className="absolute bottom-[-2.5rem] left-1/2 z-50 hidden -translate-x-1/2 transform whitespace-nowrap rounded-small bg-white px-4 py-2 shadow-md group-hover:block">
|
||||
<Text variant="body-medium">
|
||||
{activeExecutions.length} running agent
|
||||
{activeExecutions.length > 1 ? "s" : ""}
|
||||
</Text>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</PopoverTrigger>
|
||||
|
||||
<PopoverContent className="w-80 p-0" align="center" sideOffset={8}>
|
||||
<ActivityDropdown
|
||||
activeExecutions={activeExecutions}
|
||||
recentCompletions={recentCompletions}
|
||||
recentFailures={recentFailures}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
"use client";
|
||||
|
||||
import { AgentExecutionStatus } from "@/app/api/__generated__/models/agentExecutionStatus";
|
||||
import { Text } from "@/components/atoms/Text/Text";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { Bell } from "@phosphor-icons/react";
|
||||
import { AgentExecutionWithInfo, EXECUTION_DISPLAY_LIMIT } from "../helpers";
|
||||
import { ActivityItem } from "./ActivityItem";
|
||||
|
||||
interface Props {
|
||||
activeExecutions: AgentExecutionWithInfo[];
|
||||
recentCompletions: AgentExecutionWithInfo[];
|
||||
recentFailures: AgentExecutionWithInfo[];
|
||||
}
|
||||
|
||||
export function ActivityDropdown({
|
||||
activeExecutions,
|
||||
recentCompletions,
|
||||
recentFailures,
|
||||
}: Props) {
|
||||
// Combine and sort all executions - running/queued at top, then by most recent
|
||||
function getSortedExecutions() {
|
||||
const allExecutions = [
|
||||
...activeExecutions.map((e) => ({ ...e, type: "running" as const })),
|
||||
...recentCompletions.map((e) => ({ ...e, type: "completed" as const })),
|
||||
...recentFailures.map((e) => ({ ...e, type: "failed" as const })),
|
||||
];
|
||||
|
||||
return allExecutions
|
||||
.sort((a, b) => {
|
||||
// Running/queued always at top
|
||||
const aIsActive =
|
||||
a.status === AgentExecutionStatus.RUNNING ||
|
||||
a.status === AgentExecutionStatus.QUEUED;
|
||||
const bIsActive =
|
||||
b.status === AgentExecutionStatus.RUNNING ||
|
||||
b.status === AgentExecutionStatus.QUEUED;
|
||||
|
||||
if (aIsActive && !bIsActive) return -1;
|
||||
if (!aIsActive && bIsActive) return 1;
|
||||
|
||||
// Within same category, sort by most recent
|
||||
const aTime = aIsActive ? a.started_at : a.ended_at;
|
||||
const bTime = bIsActive ? b.started_at : b.ended_at;
|
||||
|
||||
if (!aTime || !bTime) return 0;
|
||||
return new Date(bTime).getTime() - new Date(aTime).getTime();
|
||||
})
|
||||
.slice(0, EXECUTION_DISPLAY_LIMIT);
|
||||
}
|
||||
|
||||
const sortedExecutions = getSortedExecutions();
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Header */}
|
||||
<div className="sticky top-0 z-10 px-4 pb-1 pt-4">
|
||||
<Text variant="large-semibold" className="!text-black">
|
||||
Agent Activity
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<ScrollArea className="min-h-[10rem]">
|
||||
{sortedExecutions.length > 0 ? (
|
||||
<div className="p-2">
|
||||
{sortedExecutions.map((execution) => (
|
||||
<ActivityItem key={execution.id} execution={execution} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-full flex-col items-center justify-center gap-5 pb-8 pt-6">
|
||||
<div className="mx-auto inline-flex flex-col items-center justify-center rounded-full bg-lightGrey p-6">
|
||||
<Bell className="h-6 w-6 text-zinc-300" />
|
||||
</div>
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<Text variant="body-medium" className="!text-black">
|
||||
Nothing to show yet
|
||||
</Text>
|
||||
<Text variant="body" className="!text-zinc-500">
|
||||
Start an agent to get updates
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</ScrollArea>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
"use client";
|
||||
|
||||
import { AgentExecutionStatus } from "@/app/api/__generated__/models/agentExecutionStatus";
|
||||
import { Text } from "@/components/atoms/Text/Text";
|
||||
import {
|
||||
CheckCircle,
|
||||
CircleNotchIcon,
|
||||
Clock,
|
||||
WarningOctagonIcon,
|
||||
StopCircle,
|
||||
CircleDashed,
|
||||
} from "@phosphor-icons/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import type { AgentExecutionWithInfo } from "../helpers";
|
||||
import { formatTimeAgo, getExecutionDuration } from "../helpers";
|
||||
|
||||
interface Props {
|
||||
execution: AgentExecutionWithInfo;
|
||||
}
|
||||
|
||||
export function ActivityItem({ execution }: Props) {
|
||||
const router = useRouter();
|
||||
|
||||
function getStatusIcon() {
|
||||
switch (execution.status) {
|
||||
case AgentExecutionStatus.QUEUED:
|
||||
return <Clock size={18} className="text-purple-500" />;
|
||||
case AgentExecutionStatus.RUNNING:
|
||||
return (
|
||||
<CircleNotchIcon
|
||||
size={18}
|
||||
className="animate-spin text-purple-500"
|
||||
weight="bold"
|
||||
/>
|
||||
);
|
||||
case AgentExecutionStatus.COMPLETED:
|
||||
return (
|
||||
<CheckCircle size={18} weight="fill" className="text-purple-500" />
|
||||
);
|
||||
case AgentExecutionStatus.FAILED:
|
||||
return <WarningOctagonIcon size={18} className="text-purple-500" />;
|
||||
case AgentExecutionStatus.TERMINATED:
|
||||
return (
|
||||
<StopCircle size={18} className="text-purple-500" weight="fill" />
|
||||
);
|
||||
case AgentExecutionStatus.INCOMPLETE:
|
||||
return <CircleDashed size={18} className="text-purple-500" />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getTimeDisplay() {
|
||||
const isActiveStatus =
|
||||
execution.status === AgentExecutionStatus.RUNNING ||
|
||||
execution.status === AgentExecutionStatus.QUEUED;
|
||||
|
||||
if (isActiveStatus) {
|
||||
const timeAgo = formatTimeAgo(execution.started_at.toString());
|
||||
const statusText =
|
||||
execution.status === AgentExecutionStatus.QUEUED ? "queued" : "running";
|
||||
return `Started ${timeAgo}, ${getExecutionDuration(execution)} ${statusText}`;
|
||||
}
|
||||
|
||||
if (execution.ended_at) {
|
||||
const timeAgo = formatTimeAgo(execution.ended_at.toString());
|
||||
switch (execution.status) {
|
||||
case AgentExecutionStatus.COMPLETED:
|
||||
return `Completed ${timeAgo}`;
|
||||
case AgentExecutionStatus.FAILED:
|
||||
return `Failed ${timeAgo}`;
|
||||
case AgentExecutionStatus.TERMINATED:
|
||||
return `Stopped ${timeAgo}`;
|
||||
case AgentExecutionStatus.INCOMPLETE:
|
||||
return `Incomplete ${timeAgo}`;
|
||||
default:
|
||||
return `Ended ${timeAgo}`;
|
||||
}
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="cursor-pointer border-b border-slate-50 px-2 py-3 transition-colors last:border-b-0 hover:bg-lightGrey"
|
||||
onClick={() => {
|
||||
const agentId = execution.library_agent_id || execution.graph_id;
|
||||
router.push(`/library/agents/${agentId}?executionId=${execution.id}`);
|
||||
}}
|
||||
role="button"
|
||||
>
|
||||
{/* Icon + Agent Name */}
|
||||
<div className="flex items-center space-x-3">
|
||||
{getStatusIcon()}
|
||||
<Text variant="body-medium" className="truncate text-gray-900">
|
||||
{execution.agent_name}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
{/* Agent Message - Indented */}
|
||||
<div className="ml-7 pt-1">
|
||||
{/* Time - Indented */}
|
||||
<Text variant="small" className="!text-zinc-500">
|
||||
{getTimeDisplay()}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
import { AgentExecutionStatus } from "@/app/api/__generated__/models/agentExecutionStatus";
|
||||
import { GraphExecutionMeta as GeneratedGraphExecutionMeta } from "@/app/api/__generated__/models/graphExecutionMeta";
|
||||
import { MyAgent } from "@/app/api/__generated__/models/myAgent";
|
||||
import type { GraphExecution } from "@/lib/autogpt-server-api/types";
|
||||
|
||||
// Time constants
|
||||
const MILLISECONDS_PER_SECOND = 1000;
|
||||
const SECONDS_PER_MINUTE = 60;
|
||||
const MINUTES_PER_HOUR = 60;
|
||||
const HOURS_PER_DAY = 24;
|
||||
const MILLISECONDS_PER_MINUTE = SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND;
|
||||
const MILLISECONDS_PER_HOUR = MINUTES_PER_HOUR * MILLISECONDS_PER_MINUTE;
|
||||
const MILLISECONDS_PER_DAY = HOURS_PER_DAY * MILLISECONDS_PER_HOUR;
|
||||
|
||||
// Display constants
|
||||
export const EXECUTION_DISPLAY_LIMIT = 6;
|
||||
const SHORT_DURATION_THRESHOLD_SECONDS = 5;
|
||||
|
||||
export function formatTimeAgo(dateStr: string): string {
|
||||
const date = new Date(dateStr);
|
||||
const now = new Date();
|
||||
const diffMs = now.getTime() - date.getTime();
|
||||
const diffMins = Math.floor(diffMs / MILLISECONDS_PER_MINUTE);
|
||||
|
||||
if (diffMins < 1) return "just now";
|
||||
if (diffMins < SECONDS_PER_MINUTE) return `${diffMins}m ago`;
|
||||
const diffHours = Math.floor(diffMins / MINUTES_PER_HOUR);
|
||||
if (diffHours < HOURS_PER_DAY) return `${diffHours}h ago`;
|
||||
const diffDays = Math.floor(diffHours / HOURS_PER_DAY);
|
||||
return `${diffDays}d ago`;
|
||||
}
|
||||
|
||||
export function getStatusDisplayText(
|
||||
execution: GeneratedGraphExecutionMeta,
|
||||
): string {
|
||||
switch (execution.status) {
|
||||
case AgentExecutionStatus.QUEUED:
|
||||
return "Queued";
|
||||
case AgentExecutionStatus.RUNNING:
|
||||
return "Running";
|
||||
case AgentExecutionStatus.COMPLETED:
|
||||
return "Completed";
|
||||
case AgentExecutionStatus.FAILED:
|
||||
return "Failed";
|
||||
case AgentExecutionStatus.TERMINATED:
|
||||
return "Stopped";
|
||||
case AgentExecutionStatus.INCOMPLETE:
|
||||
return "Incomplete";
|
||||
default:
|
||||
return execution.status;
|
||||
}
|
||||
}
|
||||
|
||||
export function getStatusColorClass(
|
||||
execution: GeneratedGraphExecutionMeta,
|
||||
): string {
|
||||
switch (execution.status) {
|
||||
case AgentExecutionStatus.QUEUED:
|
||||
return "text-yellow-600";
|
||||
case AgentExecutionStatus.RUNNING:
|
||||
return "text-blue-600";
|
||||
case AgentExecutionStatus.COMPLETED:
|
||||
return "text-green-600";
|
||||
case AgentExecutionStatus.FAILED:
|
||||
case AgentExecutionStatus.TERMINATED:
|
||||
return "text-red-600";
|
||||
case AgentExecutionStatus.INCOMPLETE:
|
||||
return "text-gray-600";
|
||||
default:
|
||||
return "text-gray-600";
|
||||
}
|
||||
}
|
||||
|
||||
export function truncateGraphId(graphId: string, length: number = 8): string {
|
||||
return `${graphId.slice(0, length)}...`;
|
||||
}
|
||||
|
||||
export function getExecutionDuration(
|
||||
execution: GeneratedGraphExecutionMeta,
|
||||
): string {
|
||||
if (!execution.started_at) return "Unknown";
|
||||
|
||||
const start = new Date(execution.started_at);
|
||||
const end = execution.ended_at ? new Date(execution.ended_at) : new Date();
|
||||
|
||||
// Check if dates are valid
|
||||
if (isNaN(start.getTime()) || isNaN(end.getTime())) {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
const durationMs = end.getTime() - start.getTime();
|
||||
|
||||
// Handle negative durations (shouldn't happen but just in case)
|
||||
if (durationMs < 0) return "Unknown";
|
||||
|
||||
const durationSec = Math.floor(durationMs / MILLISECONDS_PER_SECOND);
|
||||
|
||||
// For short durations (< 5 seconds), show "a few seconds"
|
||||
if (durationSec < SHORT_DURATION_THRESHOLD_SECONDS) {
|
||||
return "a few seconds";
|
||||
}
|
||||
|
||||
if (durationSec < SECONDS_PER_MINUTE) return `${durationSec}s`;
|
||||
const durationMin = Math.floor(durationSec / SECONDS_PER_MINUTE);
|
||||
if (durationMin < MINUTES_PER_HOUR)
|
||||
return `${durationMin}m ${durationSec % SECONDS_PER_MINUTE}s`;
|
||||
const durationHr = Math.floor(durationMin / MINUTES_PER_HOUR);
|
||||
return `${durationHr}h ${durationMin % MINUTES_PER_HOUR}m`;
|
||||
}
|
||||
|
||||
export function shouldShowNotificationBadge(totalCount: number): boolean {
|
||||
return totalCount > 0;
|
||||
}
|
||||
|
||||
export function formatNotificationCount(count: number): string {
|
||||
if (count > 99) return "+99";
|
||||
return count.toString();
|
||||
}
|
||||
|
||||
export interface AgentExecutionWithInfo extends GeneratedGraphExecutionMeta {
|
||||
agent_name: string;
|
||||
agent_description: string;
|
||||
library_agent_id?: string;
|
||||
}
|
||||
|
||||
export interface NotificationState {
|
||||
activeExecutions: AgentExecutionWithInfo[];
|
||||
recentCompletions: AgentExecutionWithInfo[];
|
||||
recentFailures: AgentExecutionWithInfo[];
|
||||
totalCount: number;
|
||||
}
|
||||
|
||||
export function createAgentInfoMap(
|
||||
agents: MyAgent[],
|
||||
): Map<
|
||||
string,
|
||||
{ name: string; description: string; library_agent_id?: string }
|
||||
> {
|
||||
const agentMap = new Map<
|
||||
string,
|
||||
{ name: string; description: string; library_agent_id?: string }
|
||||
>();
|
||||
|
||||
agents.forEach((agent) => {
|
||||
agentMap.set(agent.agent_id, {
|
||||
name: agent.agent_name,
|
||||
description: agent.description,
|
||||
library_agent_id: undefined, // MyAgent doesn't have library_agent_id
|
||||
});
|
||||
});
|
||||
|
||||
return agentMap;
|
||||
}
|
||||
|
||||
export function convertLegacyExecutionToGenerated(
|
||||
execution: GraphExecution,
|
||||
): GeneratedGraphExecutionMeta {
|
||||
return {
|
||||
id: execution.id,
|
||||
user_id: execution.user_id,
|
||||
graph_id: execution.graph_id,
|
||||
graph_version: execution.graph_version,
|
||||
preset_id: execution.preset_id,
|
||||
status: execution.status as AgentExecutionStatus,
|
||||
started_at: execution.started_at.toISOString(),
|
||||
ended_at: execution.ended_at.toISOString(),
|
||||
stats: execution.stats || {
|
||||
cost: 0,
|
||||
duration: 0,
|
||||
duration_cpu_only: 0,
|
||||
node_exec_time: 0,
|
||||
node_exec_time_cpu_only: 0,
|
||||
node_exec_count: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function enrichExecutionWithAgentInfo(
|
||||
execution: GeneratedGraphExecutionMeta,
|
||||
agentInfoMap: Map<
|
||||
string,
|
||||
{ name: string; description: string; library_agent_id?: string }
|
||||
>,
|
||||
): AgentExecutionWithInfo {
|
||||
const agentInfo = agentInfoMap.get(execution.graph_id);
|
||||
return {
|
||||
...execution,
|
||||
agent_name: agentInfo?.name || `Graph ${execution.graph_id.slice(0, 8)}...`,
|
||||
agent_description: agentInfo?.description ?? "",
|
||||
library_agent_id: agentInfo?.library_agent_id,
|
||||
};
|
||||
}
|
||||
|
||||
export function isActiveExecution(
|
||||
execution: GeneratedGraphExecutionMeta,
|
||||
): boolean {
|
||||
const status = execution.status;
|
||||
return (
|
||||
status === AgentExecutionStatus.RUNNING ||
|
||||
status === AgentExecutionStatus.QUEUED
|
||||
);
|
||||
}
|
||||
|
||||
export function isRecentCompletion(
|
||||
execution: GeneratedGraphExecutionMeta,
|
||||
thirtyMinutesAgo: Date,
|
||||
): boolean {
|
||||
const status = execution.status;
|
||||
return (
|
||||
status === AgentExecutionStatus.COMPLETED &&
|
||||
!!execution.ended_at &&
|
||||
new Date(execution.ended_at) > thirtyMinutesAgo
|
||||
);
|
||||
}
|
||||
|
||||
export function isRecentFailure(
|
||||
execution: GeneratedGraphExecutionMeta,
|
||||
thirtyMinutesAgo: Date,
|
||||
): boolean {
|
||||
const status = execution.status;
|
||||
return (
|
||||
(status === AgentExecutionStatus.FAILED ||
|
||||
status === AgentExecutionStatus.TERMINATED) &&
|
||||
!!execution.ended_at &&
|
||||
new Date(execution.ended_at) > thirtyMinutesAgo
|
||||
);
|
||||
}
|
||||
|
||||
export function isRecentNotification(
|
||||
execution: AgentExecutionWithInfo,
|
||||
thirtyMinutesAgo: Date,
|
||||
): boolean {
|
||||
return execution.ended_at
|
||||
? new Date(execution.ended_at) > thirtyMinutesAgo
|
||||
: false;
|
||||
}
|
||||
|
||||
export function categorizeExecutions(
|
||||
executions: GeneratedGraphExecutionMeta[],
|
||||
agentInfoMap: Map<
|
||||
string,
|
||||
{ name: string; description: string; library_agent_id?: string }
|
||||
>,
|
||||
): NotificationState {
|
||||
const twentyFourHoursAgo = new Date(Date.now() - MILLISECONDS_PER_DAY);
|
||||
|
||||
const enrichedExecutions = executions.map((execution) =>
|
||||
enrichExecutionWithAgentInfo(execution, agentInfoMap),
|
||||
);
|
||||
|
||||
const activeExecutions = enrichedExecutions
|
||||
.filter(isActiveExecution)
|
||||
.slice(0, EXECUTION_DISPLAY_LIMIT);
|
||||
|
||||
const recentCompletions = enrichedExecutions
|
||||
.filter((execution) => isRecentCompletion(execution, twentyFourHoursAgo))
|
||||
.slice(0, EXECUTION_DISPLAY_LIMIT);
|
||||
|
||||
const recentFailures = enrichedExecutions
|
||||
.filter((execution) => isRecentFailure(execution, twentyFourHoursAgo))
|
||||
.slice(0, EXECUTION_DISPLAY_LIMIT);
|
||||
|
||||
return {
|
||||
activeExecutions,
|
||||
recentCompletions,
|
||||
recentFailures,
|
||||
totalCount:
|
||||
activeExecutions.length +
|
||||
recentCompletions.length +
|
||||
recentFailures.length,
|
||||
};
|
||||
}
|
||||
|
||||
export function removeExecutionFromAllCategories(
|
||||
state: NotificationState,
|
||||
executionId: string,
|
||||
): NotificationState {
|
||||
return {
|
||||
activeExecutions: state.activeExecutions.filter(
|
||||
(e) => e.id !== executionId,
|
||||
),
|
||||
recentCompletions: state.recentCompletions.filter(
|
||||
(e) => e.id !== executionId,
|
||||
),
|
||||
recentFailures: state.recentFailures.filter((e) => e.id !== executionId),
|
||||
totalCount: state.totalCount, // Will be recalculated later
|
||||
};
|
||||
}
|
||||
|
||||
export function addExecutionToCategory(
|
||||
state: NotificationState,
|
||||
execution: AgentExecutionWithInfo,
|
||||
): NotificationState {
|
||||
const twentyFourHoursAgo = new Date(Date.now() - MILLISECONDS_PER_DAY);
|
||||
const newState = { ...state };
|
||||
|
||||
if (isActiveExecution(execution)) {
|
||||
newState.activeExecutions = [execution, ...newState.activeExecutions].slice(
|
||||
0,
|
||||
EXECUTION_DISPLAY_LIMIT,
|
||||
);
|
||||
} else if (isRecentCompletion(execution, twentyFourHoursAgo)) {
|
||||
newState.recentCompletions = [
|
||||
execution,
|
||||
...newState.recentCompletions,
|
||||
].slice(0, EXECUTION_DISPLAY_LIMIT);
|
||||
} else if (isRecentFailure(execution, twentyFourHoursAgo)) {
|
||||
newState.recentFailures = [execution, ...newState.recentFailures].slice(
|
||||
0,
|
||||
EXECUTION_DISPLAY_LIMIT,
|
||||
);
|
||||
}
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
export function cleanupOldNotifications(
|
||||
state: NotificationState,
|
||||
): NotificationState {
|
||||
const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
|
||||
|
||||
return {
|
||||
...state,
|
||||
recentCompletions: state.recentCompletions.filter((e) =>
|
||||
isRecentNotification(e, twentyFourHoursAgo),
|
||||
),
|
||||
recentFailures: state.recentFailures.filter((e) =>
|
||||
isRecentNotification(e, twentyFourHoursAgo),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export function calculateTotalCount(
|
||||
state: NotificationState,
|
||||
): NotificationState {
|
||||
return {
|
||||
...state,
|
||||
totalCount:
|
||||
state.activeExecutions.length +
|
||||
state.recentCompletions.length +
|
||||
state.recentFailures.length,
|
||||
};
|
||||
}
|
||||
|
||||
export function handleExecutionUpdate(
|
||||
currentState: NotificationState,
|
||||
execution: GraphExecution,
|
||||
agentInfoMap: Map<
|
||||
string,
|
||||
{ name: string; description: string; library_agent_id?: string }
|
||||
>,
|
||||
): NotificationState {
|
||||
// Convert and enrich the execution
|
||||
const convertedExecution = convertLegacyExecutionToGenerated(execution);
|
||||
const enrichedExecution = enrichExecutionWithAgentInfo(
|
||||
convertedExecution,
|
||||
agentInfoMap,
|
||||
);
|
||||
|
||||
// Remove from all categories first
|
||||
let newState = removeExecutionFromAllCategories(currentState, execution.id);
|
||||
|
||||
// Add to appropriate category
|
||||
newState = addExecutionToCategory(newState, enrichedExecution);
|
||||
|
||||
// Clean up old notifications
|
||||
newState = cleanupOldNotifications(newState);
|
||||
|
||||
// Recalculate total count
|
||||
newState = calculateTotalCount(newState);
|
||||
|
||||
return newState;
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
import { useGetV1GetAllExecutions } from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import { useGetV2GetMyAgents } from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { useGetV2ListLibraryAgents } from "@/app/api/__generated__/endpoints/library/library";
|
||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
import BackendAPI from "@/lib/autogpt-server-api/client";
|
||||
import type { GraphExecution } from "@/lib/autogpt-server-api/types";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import {
|
||||
NotificationState,
|
||||
categorizeExecutions,
|
||||
createAgentInfoMap,
|
||||
handleExecutionUpdate,
|
||||
} from "./helpers";
|
||||
|
||||
type AgentInfoMap = Map<
|
||||
string,
|
||||
{ name: string; description: string; library_agent_id?: string }
|
||||
>;
|
||||
|
||||
export function useAgentActivityDropdown() {
|
||||
const [api] = useState(() => new BackendAPI());
|
||||
const [notifications, setNotifications] = useState<NotificationState>({
|
||||
activeExecutions: [],
|
||||
recentCompletions: [],
|
||||
recentFailures: [],
|
||||
totalCount: 0,
|
||||
});
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [agentInfoMap, setAgentInfoMap] = useState<AgentInfoMap>(new Map());
|
||||
|
||||
// Get library agents using generated hook
|
||||
const {
|
||||
data: myAgentsResponse,
|
||||
isLoading: isAgentsLoading,
|
||||
error: agentsError,
|
||||
} = useGetV2GetMyAgents({
|
||||
query: {
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Get library agents data to map graph_id to library_agent_id
|
||||
const {
|
||||
data: libraryAgentsResponse,
|
||||
isLoading: isLibraryAgentsLoading,
|
||||
error: libraryAgentsError,
|
||||
} = useGetV2ListLibraryAgents(
|
||||
{},
|
||||
{
|
||||
query: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// Get all executions using generated hook
|
||||
const {
|
||||
data: executionsResponse,
|
||||
isLoading: isExecutionsLoading,
|
||||
error: executionsError,
|
||||
} = useGetV1GetAllExecutions({
|
||||
query: {
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Update agent info map when both agent data sources change
|
||||
useEffect(() => {
|
||||
if (
|
||||
myAgentsResponse?.data?.agents &&
|
||||
libraryAgentsResponse?.data &&
|
||||
"agents" in libraryAgentsResponse.data
|
||||
) {
|
||||
const agentMap = createAgentInfoMap(myAgentsResponse.data.agents);
|
||||
|
||||
// Add library agent ID mapping
|
||||
libraryAgentsResponse.data.agents.forEach(
|
||||
(libraryAgent: LibraryAgent) => {
|
||||
const existingInfo = agentMap.get(libraryAgent.graph_id);
|
||||
if (existingInfo) {
|
||||
agentMap.set(libraryAgent.graph_id, {
|
||||
...existingInfo,
|
||||
library_agent_id: libraryAgent.id,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
setAgentInfoMap(agentMap);
|
||||
}
|
||||
}, [myAgentsResponse, libraryAgentsResponse]);
|
||||
|
||||
// Handle real-time execution updates
|
||||
const handleExecutionEvent = useCallback(
|
||||
(execution: GraphExecution) => {
|
||||
setNotifications((currentState) =>
|
||||
handleExecutionUpdate(currentState, execution, agentInfoMap),
|
||||
);
|
||||
},
|
||||
[agentInfoMap],
|
||||
);
|
||||
|
||||
// Process initial execution state when data loads
|
||||
useEffect(() => {
|
||||
if (
|
||||
executionsResponse?.data &&
|
||||
!isExecutionsLoading &&
|
||||
agentInfoMap.size > 0
|
||||
) {
|
||||
const newNotifications = categorizeExecutions(
|
||||
executionsResponse.data,
|
||||
agentInfoMap,
|
||||
);
|
||||
|
||||
setNotifications(newNotifications);
|
||||
}
|
||||
}, [executionsResponse, isExecutionsLoading, agentInfoMap]);
|
||||
|
||||
// Initialize WebSocket connection for real-time updates
|
||||
useEffect(() => {
|
||||
const connectHandler = api.onWebSocketConnect(() => {
|
||||
setIsConnected(true);
|
||||
|
||||
// Subscribe to graph executions for all user agents
|
||||
if (myAgentsResponse?.data?.agents) {
|
||||
myAgentsResponse.data.agents.forEach((agent) => {
|
||||
api
|
||||
.subscribeToGraphExecutions(agent.agent_id as any)
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
`[AgentNotifications] Failed to subscribe to graph ${agent.agent_id}:`,
|
||||
error,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const disconnectHandler = api.onWebSocketDisconnect(() => {
|
||||
setIsConnected(false);
|
||||
});
|
||||
|
||||
const messageHandler = api.onWebSocketMessage(
|
||||
"graph_execution_event",
|
||||
handleExecutionEvent,
|
||||
);
|
||||
|
||||
api.connectWebSocket();
|
||||
|
||||
return () => {
|
||||
connectHandler();
|
||||
disconnectHandler();
|
||||
messageHandler();
|
||||
api.disconnectWebSocket();
|
||||
};
|
||||
}, [api, handleExecutionEvent, myAgentsResponse]);
|
||||
|
||||
return {
|
||||
...notifications,
|
||||
isConnected,
|
||||
isLoading: isAgentsLoading || isExecutionsLoading || isLibraryAgentsLoading,
|
||||
error: agentsError || executionsError || libraryAgentsError,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { useGetV2GetUserProfile } from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
|
||||
|
||||
export function useNavbar() {
|
||||
const { isLoggedIn, isUserLoading } = useSupabase();
|
||||
|
||||
console.log("isLoggedIn", isLoggedIn);
|
||||
|
||||
const {
|
||||
data: profileResponse,
|
||||
isLoading: isProfileLoading,
|
||||
error: profileError,
|
||||
} = useGetV2GetUserProfile({
|
||||
query: {
|
||||
enabled: isLoggedIn === true,
|
||||
},
|
||||
});
|
||||
|
||||
const profile = profileResponse?.data || null;
|
||||
const isLoading = isUserLoading || (isLoggedIn && isProfileLoading);
|
||||
|
||||
return {
|
||||
isLoggedIn,
|
||||
profile,
|
||||
isLoading,
|
||||
profileError,
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
@@ -23,7 +23,7 @@ const PopoverContent = React.forwardRef<
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 w-72 rounded-md border border-neutral-200 bg-white p-4 text-neutral-950 shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50",
|
||||
"z-50 w-72 rounded-medium border border-neutral-200 bg-white p-4 text-neutral-950 shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -34,8 +34,8 @@ PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
||||
|
||||
export {
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
PopoverAnchor,
|
||||
PopoverContent,
|
||||
PopoverPortal,
|
||||
PopoverTrigger,
|
||||
};
|
||||
|
||||
261
autogpt_platform/frontend/src/tests/agent-notifications.spec.ts
Normal file
261
autogpt_platform/frontend/src/tests/agent-notifications.spec.ts
Normal file
@@ -0,0 +1,261 @@
|
||||
import { test } from "./fixtures";
|
||||
import { LibraryPage } from "./pages/library.page";
|
||||
|
||||
test.describe("Agent Notifications", () => {
|
||||
let libraryPage: LibraryPage;
|
||||
|
||||
test.beforeEach(async ({ page, loginPage, testUser }) => {
|
||||
libraryPage = new LibraryPage(page);
|
||||
|
||||
// Start each test with login using worker auth
|
||||
await page.goto("/login");
|
||||
await loginPage.login(testUser.email, testUser.password);
|
||||
await test.expect(page).toHaveURL("/marketplace");
|
||||
});
|
||||
|
||||
test("notification badge appears when agent is running", async ({ page }) => {
|
||||
// Navigate to library
|
||||
await libraryPage.navigateToLibrary();
|
||||
await test.expect(page).toHaveURL(new RegExp("/library"));
|
||||
|
||||
// Click on first available agent
|
||||
await libraryPage.clickFirstAgent();
|
||||
await libraryPage.waitForAgentPageLoad();
|
||||
|
||||
// Verify we're on agent page
|
||||
await test.expect(libraryPage.isLoaded()).resolves.toBeTruthy();
|
||||
|
||||
// Initially, no notification badge should be visible
|
||||
await test
|
||||
.expect(libraryPage.agentNotifications.isNotificationBadgeVisible())
|
||||
.resolves.toBeFalsy();
|
||||
|
||||
// Run the agent
|
||||
await libraryPage.runAgent();
|
||||
|
||||
// Wait for run to start and notification badge to appear
|
||||
await libraryPage.agentNotifications.waitForNotificationUpdate();
|
||||
|
||||
// Check that notification badge appears
|
||||
await test
|
||||
.expect(libraryPage.agentNotifications.isNotificationBadgeVisible())
|
||||
.resolves.toBeTruthy();
|
||||
|
||||
// Check that notification count is at least 1
|
||||
const notificationCount =
|
||||
await libraryPage.agentNotifications.getNotificationCount();
|
||||
test.expect(parseInt(notificationCount)).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test("notification dropdown shows running agent with correct status", async ({
|
||||
page: _page,
|
||||
}) => {
|
||||
// Navigate to library and run an agent
|
||||
await libraryPage.navigateToLibrary();
|
||||
await libraryPage.clickFirstAgent();
|
||||
await libraryPage.waitForAgentPageLoad();
|
||||
|
||||
const agentName = await libraryPage.getAgentName();
|
||||
|
||||
// Run the agent
|
||||
await libraryPage.runAgent();
|
||||
await libraryPage.agentNotifications.waitForNotificationUpdate();
|
||||
|
||||
// Click on notification button to open dropdown
|
||||
await libraryPage.agentNotifications.clickNotificationButton();
|
||||
|
||||
// Verify dropdown is visible
|
||||
await test
|
||||
.expect(libraryPage.agentNotifications.isNotificationDropdownVisible())
|
||||
.resolves.toBeTruthy();
|
||||
|
||||
// Check that running agent appears in dropdown
|
||||
await test
|
||||
.expect(
|
||||
libraryPage.agentNotifications.hasNotificationWithStatus("running"),
|
||||
)
|
||||
.resolves.toBeTruthy();
|
||||
|
||||
// Check that the agent name appears in notifications
|
||||
const notification =
|
||||
await libraryPage.agentNotifications.getNotificationByAgentName(
|
||||
agentName,
|
||||
);
|
||||
test.expect(notification).not.toBeNull();
|
||||
test.expect(notification?.status).toBe("running");
|
||||
});
|
||||
|
||||
test("notification dropdown shows completed agent after run finishes", async ({
|
||||
page: _page,
|
||||
}, testInfo) => {
|
||||
// Increase timeout for this test since we need to wait for completion
|
||||
await test.setTimeout(testInfo.timeout * 3);
|
||||
|
||||
// Navigate to library and run an agent
|
||||
await libraryPage.navigateToLibrary();
|
||||
await libraryPage.clickFirstAgent();
|
||||
await libraryPage.waitForAgentPageLoad();
|
||||
|
||||
const agentName = await libraryPage.getAgentName();
|
||||
|
||||
// Run the agent
|
||||
await libraryPage.runAgent();
|
||||
await libraryPage.agentNotifications.waitForNotificationUpdate();
|
||||
|
||||
// Wait for agent to complete (with longer timeout)
|
||||
await libraryPage.waitForRunToComplete(60000);
|
||||
await libraryPage.agentNotifications.waitForNotificationUpdate();
|
||||
|
||||
// Click on notification button to open dropdown
|
||||
await libraryPage.agentNotifications.clickNotificationButton();
|
||||
|
||||
// Verify dropdown is visible
|
||||
await test
|
||||
.expect(libraryPage.agentNotifications.isNotificationDropdownVisible())
|
||||
.resolves.toBeTruthy();
|
||||
|
||||
// Check that completed agent appears in dropdown
|
||||
const notification =
|
||||
await libraryPage.agentNotifications.getNotificationByAgentName(
|
||||
agentName,
|
||||
);
|
||||
test.expect(notification).not.toBeNull();
|
||||
test.expect(notification?.status).toMatch(/completed|failed|terminated/);
|
||||
});
|
||||
|
||||
test("notification dropdown shows correct time information", async ({
|
||||
page: _page,
|
||||
}) => {
|
||||
// Navigate to library and run an agent
|
||||
await libraryPage.navigateToLibrary();
|
||||
await libraryPage.clickFirstAgent();
|
||||
await libraryPage.waitForAgentPageLoad();
|
||||
|
||||
const agentName = await libraryPage.getAgentName();
|
||||
|
||||
// Run the agent
|
||||
await libraryPage.runAgent();
|
||||
await libraryPage.agentNotifications.waitForNotificationUpdate();
|
||||
|
||||
// Click on notification button to open dropdown
|
||||
await libraryPage.agentNotifications.clickNotificationButton();
|
||||
|
||||
// Get notification for this agent
|
||||
const notification =
|
||||
await libraryPage.agentNotifications.getNotificationByAgentName(
|
||||
agentName,
|
||||
);
|
||||
test.expect(notification).not.toBeNull();
|
||||
|
||||
// Check that time information is present and contains expected text
|
||||
test.expect(notification?.time).toContain("Started");
|
||||
test.expect(notification?.time).toMatch(/Started.*ago.*seconds/);
|
||||
});
|
||||
|
||||
test("notification dropdown shows multiple agents when multiple are running", async ({
|
||||
page: _page,
|
||||
}) => {
|
||||
// Navigate to library
|
||||
await libraryPage.navigateToLibrary();
|
||||
|
||||
// Run first agent
|
||||
await libraryPage.clickFirstAgent();
|
||||
await libraryPage.waitForAgentPageLoad();
|
||||
const firstAgentName = await libraryPage.getAgentName();
|
||||
await libraryPage.runAgent();
|
||||
await libraryPage.agentNotifications.waitForNotificationUpdate();
|
||||
|
||||
// Go back to library and run another agent (if available)
|
||||
await libraryPage.navigateToLibrary();
|
||||
const agentCards = await libraryPage.agentCards.count();
|
||||
|
||||
if (agentCards > 1) {
|
||||
await libraryPage.agentCards.nth(1).click();
|
||||
await libraryPage.waitForAgentPageLoad();
|
||||
const secondAgentName = await libraryPage.getAgentName();
|
||||
await libraryPage.runAgent();
|
||||
await libraryPage.agentNotifications.waitForNotificationUpdate();
|
||||
|
||||
// Click on notification button to open dropdown
|
||||
await libraryPage.agentNotifications.clickNotificationButton();
|
||||
|
||||
// Verify both agents appear in dropdown
|
||||
const firstNotification =
|
||||
await libraryPage.agentNotifications.getNotificationByAgentName(
|
||||
firstAgentName,
|
||||
);
|
||||
const secondNotification =
|
||||
await libraryPage.agentNotifications.getNotificationByAgentName(
|
||||
secondAgentName,
|
||||
);
|
||||
|
||||
test.expect(firstNotification).not.toBeNull();
|
||||
test.expect(secondNotification).not.toBeNull();
|
||||
|
||||
// Check that notification count reflects multiple running agents
|
||||
const notificationCount =
|
||||
await libraryPage.agentNotifications.getNotificationCount();
|
||||
test.expect(parseInt(notificationCount)).toBeGreaterThanOrEqual(2);
|
||||
} else {
|
||||
// Skip this part if only one agent is available
|
||||
console.log("Only one agent available, skipping multiple agent test");
|
||||
}
|
||||
});
|
||||
|
||||
test("notification dropdown closes when clicking outside", async ({
|
||||
page,
|
||||
}) => {
|
||||
// Navigate to library and run an agent
|
||||
await libraryPage.navigateToLibrary();
|
||||
await libraryPage.clickFirstAgent();
|
||||
await libraryPage.waitForAgentPageLoad();
|
||||
|
||||
// Run the agent
|
||||
await libraryPage.runAgent();
|
||||
await libraryPage.agentNotifications.waitForNotificationUpdate();
|
||||
|
||||
// Click on notification button to open dropdown
|
||||
await libraryPage.agentNotifications.clickNotificationButton();
|
||||
|
||||
// Verify dropdown is visible
|
||||
await test
|
||||
.expect(libraryPage.agentNotifications.isNotificationDropdownVisible())
|
||||
.resolves.toBeTruthy();
|
||||
|
||||
// Click outside the dropdown
|
||||
await page.click("body");
|
||||
|
||||
// Verify dropdown is no longer visible
|
||||
await test
|
||||
.expect(libraryPage.agentNotifications.isNotificationDropdownVisible())
|
||||
.resolves.toBeFalsy();
|
||||
});
|
||||
|
||||
test("notification badge count updates correctly", async ({
|
||||
page: _page,
|
||||
}) => {
|
||||
// Navigate to library and run an agent
|
||||
await libraryPage.navigateToLibrary();
|
||||
await libraryPage.clickFirstAgent();
|
||||
await libraryPage.waitForAgentPageLoad();
|
||||
|
||||
// Initially, no notification badge should be visible
|
||||
await test
|
||||
.expect(libraryPage.agentNotifications.isNotificationBadgeVisible())
|
||||
.resolves.toBeFalsy();
|
||||
|
||||
// Run the agent
|
||||
await libraryPage.runAgent();
|
||||
await libraryPage.agentNotifications.waitForNotificationUpdate();
|
||||
|
||||
// Check that notification count is 1
|
||||
const notificationCount =
|
||||
await libraryPage.agentNotifications.getNotificationCount();
|
||||
test.expect(notificationCount).toBe("1");
|
||||
|
||||
// Check that badge is visible and animating
|
||||
await test
|
||||
.expect(libraryPage.agentNotifications.isNotificationBadgeVisible())
|
||||
.resolves.toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,95 @@
|
||||
import { Locator, Page } from "@playwright/test";
|
||||
|
||||
export class AgentNotificationsPage {
|
||||
constructor(private page: Page) {}
|
||||
|
||||
get notificationButton(): Locator {
|
||||
return this.page.locator('button[title="Agent Activity"]');
|
||||
}
|
||||
|
||||
get notificationBadge(): Locator {
|
||||
return this.page
|
||||
.locator('button[title="Agent Activity"] .animate-spin')
|
||||
.first();
|
||||
}
|
||||
|
||||
get notificationDropdown(): Locator {
|
||||
return this.page.locator('[role="dialog"]:has-text("Agent Activity")');
|
||||
}
|
||||
|
||||
get notificationItems(): Locator {
|
||||
return this.notificationDropdown.locator('[role="button"]');
|
||||
}
|
||||
|
||||
async clickNotificationButton(): Promise<void> {
|
||||
await this.notificationButton.click();
|
||||
}
|
||||
|
||||
async isNotificationBadgeVisible(): Promise<boolean> {
|
||||
return await this.notificationBadge.isVisible();
|
||||
}
|
||||
|
||||
async isNotificationDropdownVisible(): Promise<boolean> {
|
||||
return await this.notificationDropdown.isVisible();
|
||||
}
|
||||
|
||||
async getNotificationCount(): Promise<string> {
|
||||
const badge = this.page.locator(
|
||||
'button[title="Agent Activity"] .bg-purple-600',
|
||||
);
|
||||
return (await badge.textContent()) || "0";
|
||||
}
|
||||
|
||||
async getNotificationItems(): Promise<
|
||||
{ name: string; status: string; time: string }[]
|
||||
> {
|
||||
const items = await this.notificationItems.all();
|
||||
const results = [];
|
||||
|
||||
for (const item of items) {
|
||||
const name = (await item.locator(".truncate").textContent()) || "";
|
||||
const time =
|
||||
(await item.locator(".\\!text-zinc-500").textContent()) || "";
|
||||
|
||||
// Determine status from icon classes and text content
|
||||
let status = "unknown";
|
||||
if (await item.locator(".animate-spin").isVisible()) {
|
||||
status = "running";
|
||||
} else if (await item.locator("svg").first().isVisible()) {
|
||||
// For non-animated icons, check the text content to determine status
|
||||
const timeText = time.toLowerCase();
|
||||
if (timeText.includes("completed")) {
|
||||
status = "completed";
|
||||
} else if (timeText.includes("failed")) {
|
||||
status = "failed";
|
||||
} else if (timeText.includes("stopped")) {
|
||||
status = "terminated";
|
||||
} else if (timeText.includes("incomplete")) {
|
||||
status = "incomplete";
|
||||
} else if (timeText.includes("queued")) {
|
||||
status = "queued";
|
||||
}
|
||||
}
|
||||
|
||||
results.push({ name, status, time });
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
async waitForNotificationUpdate(_timeout = 10000): Promise<void> {
|
||||
await this.page.waitForTimeout(1000); // Wait for potential updates
|
||||
}
|
||||
|
||||
async hasNotificationWithStatus(status: string): Promise<boolean> {
|
||||
const items = await this.getNotificationItems();
|
||||
return items.some((item) => item.status === status);
|
||||
}
|
||||
|
||||
async getNotificationByAgentName(
|
||||
agentName: string,
|
||||
): Promise<{ name: string; status: string; time: string } | null> {
|
||||
const items = await this.getNotificationItems();
|
||||
return items.find((item) => item.name.includes(agentName)) || null;
|
||||
}
|
||||
}
|
||||
107
autogpt_platform/frontend/src/tests/pages/library.page.ts
Normal file
107
autogpt_platform/frontend/src/tests/pages/library.page.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { Locator, Page } from "@playwright/test";
|
||||
import { AgentNotificationsPage } from "./agent-notifications.page";
|
||||
|
||||
export class LibraryPage {
|
||||
public agentNotifications: AgentNotificationsPage;
|
||||
|
||||
constructor(private page: Page) {
|
||||
this.agentNotifications = new AgentNotificationsPage(page);
|
||||
}
|
||||
|
||||
get libraryTab(): Locator {
|
||||
return this.page.locator('a[href="/library"]');
|
||||
}
|
||||
|
||||
get agentCards(): Locator {
|
||||
return this.page.locator(".agpt-div").filter({ hasText: /^test-agent-/ });
|
||||
}
|
||||
|
||||
get runButton(): Locator {
|
||||
return this.page.locator('button:has-text("Run")');
|
||||
}
|
||||
|
||||
get newRunButton(): Locator {
|
||||
return this.page.locator('button:has-text("New run")');
|
||||
}
|
||||
|
||||
get runDialogRunButton(): Locator {
|
||||
return this.page.locator('button:has-text("Run"):last-child');
|
||||
}
|
||||
|
||||
get agentTitle(): Locator {
|
||||
return this.page.locator("h1").first();
|
||||
}
|
||||
|
||||
async navigateToLibrary(): Promise<void> {
|
||||
await this.libraryTab.click();
|
||||
await this.page.waitForURL(/.*\/library/);
|
||||
}
|
||||
|
||||
async clickFirstAgent(): Promise<void> {
|
||||
const firstAgent = this.agentCards.first();
|
||||
await firstAgent.click();
|
||||
}
|
||||
|
||||
async navigateToAgentByName(agentName: string): Promise<void> {
|
||||
const agentCard = this.agentCards.filter({ hasText: agentName }).first();
|
||||
await agentCard.click();
|
||||
}
|
||||
|
||||
async clickRunButton(): Promise<void> {
|
||||
await this.runButton.click();
|
||||
}
|
||||
|
||||
async clickNewRunButton(): Promise<void> {
|
||||
await this.newRunButton.click();
|
||||
}
|
||||
|
||||
async runAgent(inputs: Record<string, string> = {}): Promise<void> {
|
||||
await this.clickRunButton();
|
||||
|
||||
// Fill in any required inputs
|
||||
for (const [key, value] of Object.entries(inputs)) {
|
||||
const input = this.page.locator(
|
||||
`input[placeholder*="${key}"], textarea[placeholder*="${key}"]`,
|
||||
);
|
||||
if (await input.isVisible()) {
|
||||
await input.fill(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Click the run button in the dialog
|
||||
await this.runDialogRunButton.click();
|
||||
}
|
||||
|
||||
async waitForAgentPageLoad(): Promise<void> {
|
||||
await this.page.waitForURL(/.*\/library\/agents\/[^/]+/);
|
||||
await this.page.waitForLoadState("networkidle");
|
||||
}
|
||||
|
||||
async getAgentName(): Promise<string> {
|
||||
return (await this.agentTitle.textContent()) || "";
|
||||
}
|
||||
|
||||
async isLoaded(): Promise<boolean> {
|
||||
return await this.page.locator("h1").isVisible();
|
||||
}
|
||||
|
||||
async waitForRunToComplete(timeout = 30000): Promise<void> {
|
||||
// Wait for completion badge or status change
|
||||
await this.page.waitForSelector(
|
||||
".bg-green-500, .bg-red-500, .bg-purple-500",
|
||||
{ timeout },
|
||||
);
|
||||
}
|
||||
|
||||
async getRunStatus(): Promise<string> {
|
||||
// Check for different status indicators
|
||||
if (await this.page.locator(".animate-spin").isVisible()) {
|
||||
return "running";
|
||||
} else if (await this.page.locator(".bg-green-500").isVisible()) {
|
||||
return "completed";
|
||||
} else if (await this.page.locator(".bg-red-500").isVisible()) {
|
||||
return "failed";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user