mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
fix(frontend): agent activity links (2) (#10488)
## Changes 🏗️ My previous PR, https://github.com/Significant-Gravitas/AutoGPT/pull/10480, didn't fully resolve the issue of broken links sometimes appearing for some runs in the Agent Activity dropdown. - Fixed the logic ( verified with a deployment in dev... ) - Simplified logic, making less API calls - If we have an execution without a clear agent ID, we display it but don't link to it - Re-generated API types ( _had to update call in dashboard agents because of it_ ) ## Checklist 📋 ### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - [x] Run agents - [x] Runs appear correctly in the activity dropdown without broken links ### For configuration changes: None
This commit is contained in:
796
autogpt_platform/frontend/pnpm-lock.yaml
generated
796
autogpt_platform/frontend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -27,8 +27,6 @@ import type { CreatorDetails } from "../../models/creatorDetails";
|
||||
|
||||
import type { CreatorsResponse } from "../../models/creatorsResponse";
|
||||
|
||||
import type { GetV2GetMyAgentsParams } from "../../models/getV2GetMyAgentsParams";
|
||||
|
||||
import type { GetV2ListMySubmissionsParams } from "../../models/getV2ListMySubmissionsParams";
|
||||
|
||||
import type { GetV2ListStoreAgentsParams } from "../../models/getV2ListStoreAgentsParams";
|
||||
@@ -1952,78 +1950,45 @@ export type getV2GetMyAgentsResponse200 = {
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type getV2GetMyAgentsResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type getV2GetMyAgentsResponseComposite =
|
||||
| getV2GetMyAgentsResponse200
|
||||
| getV2GetMyAgentsResponse422;
|
||||
export type getV2GetMyAgentsResponseComposite = getV2GetMyAgentsResponse200;
|
||||
|
||||
export type getV2GetMyAgentsResponse = getV2GetMyAgentsResponseComposite & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export const getGetV2GetMyAgentsUrl = (params?: GetV2GetMyAgentsParams) => {
|
||||
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/store/myagents?${stringifiedParams}`
|
||||
: `/api/store/myagents`;
|
||||
export const getGetV2GetMyAgentsUrl = () => {
|
||||
return `/api/store/myagents`;
|
||||
};
|
||||
|
||||
export const getV2GetMyAgents = async (
|
||||
params?: GetV2GetMyAgentsParams,
|
||||
options?: RequestInit,
|
||||
): Promise<getV2GetMyAgentsResponse> => {
|
||||
return customMutator<getV2GetMyAgentsResponse>(
|
||||
getGetV2GetMyAgentsUrl(params),
|
||||
{
|
||||
...options,
|
||||
method: "GET",
|
||||
},
|
||||
);
|
||||
return customMutator<getV2GetMyAgentsResponse>(getGetV2GetMyAgentsUrl(), {
|
||||
...options,
|
||||
method: "GET",
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetV2GetMyAgentsQueryKey = (
|
||||
params?: GetV2GetMyAgentsParams,
|
||||
) => {
|
||||
return [`/api/store/myagents`, ...(params ? [params] : [])] as const;
|
||||
export const getGetV2GetMyAgentsQueryKey = () => {
|
||||
return [`/api/store/myagents`] as const;
|
||||
};
|
||||
|
||||
export const getGetV2GetMyAgentsQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getV2GetMyAgents>>,
|
||||
TError = HTTPValidationError,
|
||||
>(
|
||||
params?: GetV2GetMyAgentsParams,
|
||||
options?: {
|
||||
query?: Partial<
|
||||
UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getV2GetMyAgents>>,
|
||||
TError,
|
||||
TData
|
||||
>
|
||||
>;
|
||||
request?: SecondParameter<typeof customMutator>;
|
||||
},
|
||||
) => {
|
||||
TError = unknown,
|
||||
>(options?: {
|
||||
query?: Partial<
|
||||
UseQueryOptions<Awaited<ReturnType<typeof getV2GetMyAgents>>, TError, TData>
|
||||
>;
|
||||
request?: SecondParameter<typeof customMutator>;
|
||||
}) => {
|
||||
const { query: queryOptions, request: requestOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetV2GetMyAgentsQueryKey(params);
|
||||
const queryKey = queryOptions?.queryKey ?? getGetV2GetMyAgentsQueryKey();
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getV2GetMyAgents>>
|
||||
> = ({ signal }) => getV2GetMyAgents(params, { signal, ...requestOptions });
|
||||
> = ({ signal }) => getV2GetMyAgents({ signal, ...requestOptions });
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getV2GetMyAgents>>,
|
||||
@@ -2035,13 +2000,12 @@ export const getGetV2GetMyAgentsQueryOptions = <
|
||||
export type GetV2GetMyAgentsQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getV2GetMyAgents>>
|
||||
>;
|
||||
export type GetV2GetMyAgentsQueryError = HTTPValidationError;
|
||||
export type GetV2GetMyAgentsQueryError = unknown;
|
||||
|
||||
export function useGetV2GetMyAgents<
|
||||
TData = Awaited<ReturnType<typeof getV2GetMyAgents>>,
|
||||
TError = HTTPValidationError,
|
||||
TError = unknown,
|
||||
>(
|
||||
params: undefined | GetV2GetMyAgentsParams,
|
||||
options: {
|
||||
query: Partial<
|
||||
UseQueryOptions<
|
||||
@@ -2066,9 +2030,8 @@ export function useGetV2GetMyAgents<
|
||||
};
|
||||
export function useGetV2GetMyAgents<
|
||||
TData = Awaited<ReturnType<typeof getV2GetMyAgents>>,
|
||||
TError = HTTPValidationError,
|
||||
TError = unknown,
|
||||
>(
|
||||
params?: GetV2GetMyAgentsParams,
|
||||
options?: {
|
||||
query?: Partial<
|
||||
UseQueryOptions<
|
||||
@@ -2093,9 +2056,8 @@ export function useGetV2GetMyAgents<
|
||||
};
|
||||
export function useGetV2GetMyAgents<
|
||||
TData = Awaited<ReturnType<typeof getV2GetMyAgents>>,
|
||||
TError = HTTPValidationError,
|
||||
TError = unknown,
|
||||
>(
|
||||
params?: GetV2GetMyAgentsParams,
|
||||
options?: {
|
||||
query?: Partial<
|
||||
UseQueryOptions<
|
||||
@@ -2116,9 +2078,8 @@ export function useGetV2GetMyAgents<
|
||||
|
||||
export function useGetV2GetMyAgents<
|
||||
TData = Awaited<ReturnType<typeof getV2GetMyAgents>>,
|
||||
TError = HTTPValidationError,
|
||||
TError = unknown,
|
||||
>(
|
||||
params?: GetV2GetMyAgentsParams,
|
||||
options?: {
|
||||
query?: Partial<
|
||||
UseQueryOptions<
|
||||
@@ -2133,7 +2094,7 @@ export function useGetV2GetMyAgents<
|
||||
): UseQueryResult<TData, TError> & {
|
||||
queryKey: DataTag<QueryKey, TData, TError>;
|
||||
} {
|
||||
const queryOptions = getGetV2GetMyAgentsQueryOptions(params, options);
|
||||
const queryOptions = getGetV2GetMyAgentsQueryOptions(options);
|
||||
|
||||
const query = useQuery(queryOptions, queryClient) as UseQueryResult<
|
||||
TData,
|
||||
@@ -2150,10 +2111,9 @@ export function useGetV2GetMyAgents<
|
||||
*/
|
||||
export const prefetchGetV2GetMyAgentsQuery = async <
|
||||
TData = Awaited<ReturnType<typeof getV2GetMyAgents>>,
|
||||
TError = HTTPValidationError,
|
||||
TError = unknown,
|
||||
>(
|
||||
queryClient: QueryClient,
|
||||
params?: GetV2GetMyAgentsParams,
|
||||
options?: {
|
||||
query?: Partial<
|
||||
UseQueryOptions<
|
||||
@@ -2165,7 +2125,7 @@ export const prefetchGetV2GetMyAgentsQuery = async <
|
||||
request?: SecondParameter<typeof customMutator>;
|
||||
},
|
||||
): Promise<QueryClient> => {
|
||||
const queryOptions = getGetV2GetMyAgentsQueryOptions(params, options);
|
||||
const queryOptions = getGetV2GetMyAgentsQueryOptions(options);
|
||||
|
||||
await queryClient.prefetchQuery(queryOptions);
|
||||
|
||||
|
||||
@@ -2467,30 +2467,6 @@
|
||||
"tags": ["v2", "store", "private"],
|
||||
"summary": "Get my agents",
|
||||
"operationId": "getV2Get my agents",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "page",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"default": 1,
|
||||
"title": "Page"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "page_size",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"default": 20,
|
||||
"title": "Page Size"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
@@ -2499,14 +2475,6 @@
|
||||
"schema": { "$ref": "#/components/schemas/MyAgentsResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,17 +75,17 @@ export const PublishAgentPopout: React.FC<PublishAgentPopoutProps> = ({
|
||||
const api = useBackendAPI();
|
||||
|
||||
// Use the auto-generated API hook
|
||||
const { data, error, isLoading, refetch } = useGetV2GetMyAgents(
|
||||
{
|
||||
page: currentPage,
|
||||
page_size: 20,
|
||||
},
|
||||
{
|
||||
query: {
|
||||
enabled: open, // Only fetch when the popout is open
|
||||
const { data, error, isLoading, refetch } = useGetV2GetMyAgents({
|
||||
request: {
|
||||
params: {
|
||||
page: currentPage,
|
||||
page_size: 20,
|
||||
},
|
||||
},
|
||||
);
|
||||
query: {
|
||||
enabled: open, // Only fetch when the popout is open
|
||||
},
|
||||
});
|
||||
|
||||
// Update allAgents when new data arrives
|
||||
React.useEffect(() => {
|
||||
|
||||
@@ -79,18 +79,11 @@ export function ActivityItem({ execution }: Props) {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
const agentId = execution.library_agent_id || execution.graph_id;
|
||||
const linkUrl = `/library/agents/${agentId}?executionId=${execution.id}`;
|
||||
const withLibraryId = !!execution.library_agent_id;
|
||||
const linkUrl = `/library/agents/${execution.library_agent_id}?executionId=${execution.id}`;
|
||||
const withExecutionLink = execution.library_agent_id && execution.id;
|
||||
|
||||
if (!withLibraryId) return null;
|
||||
|
||||
return (
|
||||
<Link
|
||||
className="block cursor-pointer border-b border-slate-50 px-2 py-3 transition-colors last:border-b-0 hover:bg-lightGrey"
|
||||
href={linkUrl}
|
||||
role="button"
|
||||
>
|
||||
const content = (
|
||||
<>
|
||||
{/* Icon + Agent Name */}
|
||||
<div className="flex items-center space-x-2">
|
||||
{getStatusIcon()}
|
||||
@@ -109,6 +102,20 @@ export function ActivityItem({ execution }: Props) {
|
||||
{getTimeDisplay()}
|
||||
</Text>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
return withExecutionLink ? (
|
||||
<Link
|
||||
className="block cursor-pointer border-b border-slate-50 px-2 py-3 transition-colors last:border-b-0 hover:bg-lightGrey"
|
||||
href={linkUrl}
|
||||
role="button"
|
||||
>
|
||||
{content}
|
||||
</Link>
|
||||
) : (
|
||||
<div className="block border-b border-slate-50 px-2 py-3 last:border-b-0">
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
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 { LibraryAgentResponse } from "@/app/api/__generated__/models/libraryAgentResponse";
|
||||
import { MyAgentsResponse } from "@/app/api/__generated__/models/myAgentsResponse";
|
||||
import { MyAgent } from "@/app/api/__generated__/models/myAgent";
|
||||
import BackendAPI from "@/lib/autogpt-server-api/client";
|
||||
import type { GraphExecution, GraphID } from "@/lib/autogpt-server-api/types";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import {
|
||||
NotificationState,
|
||||
categorizeExecutions,
|
||||
createAgentInfoMap,
|
||||
handleExecutionUpdate,
|
||||
} from "./helpers";
|
||||
|
||||
@@ -22,80 +19,55 @@ type AgentInfoMap = Map<
|
||||
|
||||
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,
|
||||
data: agents,
|
||||
isSuccess: agentsSuccess,
|
||||
error: agentsError,
|
||||
} = useGetV2GetMyAgents(
|
||||
{},
|
||||
{
|
||||
// Enable query by default
|
||||
query: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
} = useGetV2ListLibraryAgents();
|
||||
|
||||
// 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,
|
||||
data: executions,
|
||||
isSuccess: executionsSuccess,
|
||||
error: executionsError,
|
||||
} = useGetV1GetAllExecutions({
|
||||
query: {
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
} = useGetV1GetAllExecutions();
|
||||
|
||||
// Update agent info map when both agent data sources change
|
||||
// Create a map of library agents
|
||||
useEffect(() => {
|
||||
if (myAgentsResponse?.data && libraryAgentsResponse?.data) {
|
||||
const myAgents = myAgentsResponse.data as MyAgentsResponse;
|
||||
const libraryAgents = libraryAgentsResponse.data as LibraryAgentResponse;
|
||||
if (agents && agentsSuccess) {
|
||||
// SafeCast: library agents loaded
|
||||
const libraryAgents = agents.data as LibraryAgentResponse;
|
||||
|
||||
if (myAgents?.agents && libraryAgents?.agents) {
|
||||
const agentMap = createAgentInfoMap(myAgents.agents);
|
||||
if (!libraryAgents.agents || !libraryAgents.agents.length) return;
|
||||
|
||||
libraryAgents.agents.forEach((libraryAgent: LibraryAgent) => {
|
||||
if (libraryAgent.graph_id && libraryAgent.id) {
|
||||
const existingInfo = agentMap.get(libraryAgent.graph_id);
|
||||
if (existingInfo) {
|
||||
agentMap.set(libraryAgent.graph_id, {
|
||||
...existingInfo,
|
||||
library_agent_id: libraryAgent.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
const agentMap = new Map<
|
||||
string,
|
||||
{ name: string; description: string; library_agent_id?: string }
|
||||
>();
|
||||
|
||||
setAgentInfoMap(agentMap);
|
||||
}
|
||||
libraryAgents.agents.forEach((agent) => {
|
||||
if (agent.graph_id && agent.id) {
|
||||
agentMap.set(agent.graph_id, {
|
||||
name: agent.name || `Agent ${agent.graph_id.slice(0, 8)}`,
|
||||
description: agent.description || "",
|
||||
library_agent_id: agent.id,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
setAgentInfoMap(agentMap);
|
||||
}
|
||||
}, [myAgentsResponse, libraryAgentsResponse]);
|
||||
}, [agents, agentsSuccess]);
|
||||
|
||||
// Handle real-time execution updates
|
||||
const handleExecutionEvent = useCallback(
|
||||
@@ -109,41 +81,27 @@ export function useAgentActivityDropdown() {
|
||||
|
||||
// Process initial execution state when data loads
|
||||
useEffect(() => {
|
||||
if (
|
||||
executionsResponse?.data &&
|
||||
!isExecutionsLoading &&
|
||||
agentInfoMap.size > 0
|
||||
) {
|
||||
const newNotifications = categorizeExecutions(
|
||||
executionsResponse.data,
|
||||
agentInfoMap,
|
||||
);
|
||||
|
||||
setNotifications(newNotifications);
|
||||
if (executions && executionsSuccess && agentInfoMap.size > 0) {
|
||||
const notifications = categorizeExecutions(executions.data, agentInfoMap);
|
||||
setNotifications(notifications);
|
||||
}
|
||||
}, [executionsResponse, isExecutionsLoading, agentInfoMap]);
|
||||
}, [executions, executionsSuccess, agentInfoMap]);
|
||||
|
||||
// Initialize WebSocket connection for real-time updates
|
||||
useEffect(() => {
|
||||
if (!agentInfoMap.size) return;
|
||||
|
||||
const connectHandler = api.onWebSocketConnect(() => {
|
||||
setIsConnected(true);
|
||||
|
||||
if (myAgentsResponse?.data) {
|
||||
const myAgents = myAgentsResponse.data as MyAgentsResponse;
|
||||
|
||||
if (myAgents?.agents) {
|
||||
myAgents.agents.forEach((agent: MyAgent) => {
|
||||
api
|
||||
.subscribeToGraphExecutions(agent.agent_id as GraphID)
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
`[AgentNotifications] Failed to subscribe to graph ${agent.agent_id}:`,
|
||||
error,
|
||||
);
|
||||
});
|
||||
agentInfoMap.forEach((_, graphId) => {
|
||||
api.subscribeToGraphExecutions(graphId as GraphID).catch((error) => {
|
||||
Sentry.captureException(error, {
|
||||
tags: {
|
||||
graphId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const disconnectHandler = api.onWebSocketDisconnect(() => {
|
||||
@@ -163,12 +121,12 @@ export function useAgentActivityDropdown() {
|
||||
messageHandler();
|
||||
api.disconnectWebSocket();
|
||||
};
|
||||
}, [api, handleExecutionEvent, myAgentsResponse]);
|
||||
}, [api, handleExecutionEvent, agentInfoMap]);
|
||||
|
||||
return {
|
||||
...notifications,
|
||||
isConnected,
|
||||
isLoading: isAgentsLoading || isExecutionsLoading || isLibraryAgentsLoading,
|
||||
error: agentsError || executionsError || libraryAgentsError,
|
||||
isReady: executionsSuccess && agentsSuccess,
|
||||
error: executionsError || agentsError,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user