From 005290727550c134aee55546382fdcbdf50db28f Mon Sep 17 00:00:00 2001 From: mschwab Date: Fri, 12 Jun 2026 15:05:20 -0700 Subject: [PATCH] fix(studio): refresh agents table immediately after creating an agent The agents table only refetches on its 15s poll interval, and create onSuccess navigated without invalidating the list, so a new agent took several seconds to appear. Invalidate getAgentsListAgentsQueryKey on create success (matching the delete flow) so it shows up right away. ASTD-247 Signed-off-by: mschwab --- .../agents/AgentsListRoute/index.spec.tsx | 39 +++++++++++++++++++ .../routes/agents/AgentsListRoute/index.tsx | 6 ++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/web/packages/studio/src/routes/agents/AgentsListRoute/index.spec.tsx b/web/packages/studio/src/routes/agents/AgentsListRoute/index.spec.tsx index 3742c35301..fa410571b0 100644 --- a/web/packages/studio/src/routes/agents/AgentsListRoute/index.spec.tsx +++ b/web/packages/studio/src/routes/agents/AgentsListRoute/index.spec.tsx @@ -104,6 +104,45 @@ describe('AgentsListRoute', () => { expect(config.llms.llm.model_name).toBe('nvidia-nemotron-nano-9b-v2'); }); + it('refetches the agents list after creating so the new agent appears immediately', async () => { + const user = userEvent.setup(); + mockModels(['nvidia-nemotron-nano-9b-v2']); + // Returning user → stays on the list, so the table query is still mounted. + markExampleAgentIntroShown(); + + let agentListFetches = 0; + server.use( + http.get(CREATE_AGENT_URL, () => { + agentListFetches += 1; + return HttpResponse.json({ + data: [], + pagination: { + page: 1, + page_size: 50, + current_page_size: 0, + total_pages: 1, + total_results: 0, + }, + sort: '-created_at', + filter: null, + }); + }), + http.post(CREATE_AGENT_URL, async ({ request, params }) => { + const body = (await request.json()) as { name?: string }; + return HttpResponse.json({ ...body, workspace: params['workspace'] }); + }) + ); + + renderList(); + await waitFor(() => expect(agentListFetches).toBeGreaterThan(0)); + const before = agentListFetches; + + await clickCreateOnceReady(user); + + // Create invalidates the list, triggering an immediate refetch (not the 15s poll). + await waitFor(() => expect(agentListFetches).toBeGreaterThan(before)); + }); + it('does not reopen the sidepanel/intro for a later example agent in the same session', async () => { const user = userEvent.setup(); mockModels(['nvidia-nemotron-nano-9b-v2']); diff --git a/web/packages/studio/src/routes/agents/AgentsListRoute/index.tsx b/web/packages/studio/src/routes/agents/AgentsListRoute/index.tsx index 672b606049..7903325f01 100644 --- a/web/packages/studio/src/routes/agents/AgentsListRoute/index.tsx +++ b/web/packages/studio/src/routes/agents/AgentsListRoute/index.tsx @@ -3,7 +3,7 @@ import { LoadingButton } from '@nemo/common/src/components/LoadingButton'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; -import { useAgentsCreateAgent } from '@nemo/sdk/generated/agents/api'; +import { getAgentsListAgentsQueryKey, useAgentsCreateAgent } from '@nemo/sdk/generated/agents/api'; import type { Agent } from '@nemo/sdk/generated/agents/schema/Agent'; import { useModelsListModels } from '@nemo/sdk/generated/platform/api'; import { PageHeader, Stack } from '@nvidia/foundations-react-core'; @@ -26,6 +26,7 @@ import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; import { CreateDeploymentModal } from '@studio/routes/agents/AgentDeploymentsListRoute/CreateDeploymentModal'; import { getAgentDetailRoute, getAgentsListRoute } from '@studio/routes/utils'; import { pickDefaultModelName } from '@studio/util/buildSuggestedModelOptions'; +import { useQueryClient } from '@tanstack/react-query'; import { type FC, useCallback, useEffect, useState } from 'react'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; @@ -77,6 +78,7 @@ export const AgentsListRoute: FC = () => { const workspace = useWorkspaceFromPath(); const navigate = useNavigate(); const toast = useToast(); + const queryClient = useQueryClient(); const [searchParams, setSearchParams] = useSearchParams(); const [createDeploymentAgent, setCreateDeploymentAgent] = useState(null); const { [ROUTE_PARAMS.agentName]: agentNameParam } = useParams<{ agentName?: string }>(); @@ -99,6 +101,8 @@ export const AgentsListRoute: FC = () => { mutation: { onSuccess: (agent) => { toast.success(`Agent "${agent.name}" created`); + // Refresh the table immediately instead of waiting for its poll interval. + void queryClient.invalidateQueries({ queryKey: getAgentsListAgentsQueryKey(workspace) }); const priorExampleAgentExists = loadedAgents.some( (existing) => !!existing.name && existing.name !== agent.name && isExampleAgentName(existing.name)