Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
} from '@nemo/sdk/generated/agents/api';
import type { Agent } from '@nemo/sdk/generated/agents/schema/Agent';
import type { AgentDeployment } from '@nemo/sdk/generated/agents/schema/AgentDeployment';
import { Button, Divider, Flex, Text } from '@nvidia/foundations-react-core';
import { Button, Text } from '@nvidia/foundations-react-core';
import { getAgentModelNames } from '@studio/components/dataViews/AgentsDataView/utils';
import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal';
import { DocumentationButton } from '@studio/components/DocumentationButton';
Expand All @@ -34,7 +34,7 @@ import { LINK_DOCS_STUDIO } from '@studio/constants/links';
import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath';
import { getModelCompareRoute } from '@studio/routes/utils';
import { keepPreviousData, useQueryClient } from '@tanstack/react-query';
import { HatGlasses, Trash, X } from 'lucide-react';
import { HatGlasses, Trash } from 'lucide-react';
import { ComponentProps, FC, useEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router';

Expand Down Expand Up @@ -115,6 +115,14 @@ export const AgentsTable: FC<CombinedAgentsTableProps> = ({
},
});

// `keepPreviousData` keeps the previous workspace's rows on screen after a switch,
// so a selection made there would still resolve — and delete by name against the new
// workspace. Drop it as soon as the workspace changes.
const clearRowSelection = dataViewState.rowSelection.set;
useEffect(() => {
clearRowSelection({});
}, [workspace, clearRowSelection]);

Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
const page = dataViewState.pagination.state.pageIndex + 1;
const pageSize = dataViewState.pagination.state.pageSize;
const sortParam = getSortParamWithWhitelist(
Expand Down Expand Up @@ -177,12 +185,6 @@ export const AgentsTable: FC<CombinedAgentsTableProps> = ({
});
}, [agentsData, deploymentsData]);

const rowSelection = dataViewState.rowSelection.state;
const selectedAgents = useMemo(
() => tableData.filter((row) => rowSelection[row.id]),
[tableData, rowSelection]
);

const deleteAgentMutation = useAgentsDeleteAgent();
const deleteDeploymentMutation = useAgentsDeleteDeployment();

Expand Down Expand Up @@ -321,32 +323,18 @@ export const AgentsTable: FC<CombinedAgentsTableProps> = ({

return (
<>
{selectedAgents.length > 0 && (
<Flex align="center" gap="2">
<Text kind="label/regular/md">
{selectedAgents.length} {selectedAgents.length === 1 ? 'row' : 'rows'} selected
</Text>
<Flex align="center" gap="1">
<Button
kind="tertiary"
aria-label="Delete selected agents"
onClick={() => setDeleteState({ kind: 'bulk', items: selectedAgents })}
>
<Trash /> Delete
</Button>
<Divider orientation="vertical" />
<Button kind="tertiary" onClick={() => dataViewState.rowSelection.set({})}>
<span className="only-mobile">
<X variant="line" />
</span>
<span className="hide-mobile">Cancel</span>
</Button>
</Flex>
</Flex>
)}
<StudioDataView
dataViewState={dataViewState}
makeColumns={makeColumns}
renderBulkActions={({ selectedRows }) => (
<Button
kind="tertiary"
aria-label="Delete selected agents"
onClick={() => setDeleteState({ kind: 'bulk', items: selectedRows })}
>
<Trash /> Delete
</Button>
)}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
onRowClick={(row: AgentTableRow) => {
onAgentRowClick?.(row);
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import { Badge, Flex, Text } from '@nvidia/foundations-react-core';
import type { ComparisonMetricDelta } from '@studio/components/dataViews/EvalComparisonTable/types';
import { formatScore } from '@studio/routes/agents/AgentEvaluationsRoute/evalScores';
import { formatScore } from '@studio/components/evaluation/utils';
import { Equal, Minus, Plus } from 'lucide-react';
import type { FC } from 'react';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
metricNamesForComparisons,
scoreForMetric,
} from '@studio/components/dataViews/EvalComparisonTable/utils';
import { formatScore } from '@studio/routes/agents/AgentEvaluationsRoute/evalScores';
import { formatScore } from '@studio/components/evaluation/utils';
import { useMemo, type ComponentProps, type FC } from 'react';

const METRIC_COLUMN_ID = 'metric';
Expand Down
83 changes: 0 additions & 83 deletions web/packages/studio/src/constants/sampleAgents.test.ts

This file was deleted.

57 changes: 15 additions & 42 deletions web/packages/studio/src/constants/sampleAgents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,27 @@ import { z } from 'zod';

// Registry of canned example agents. Each entry references curated static assets
// under public/sample-agents/<dir>/ by path (fetched on demand, never bundled) —
// mirroring src/constants/sampleDatasets.ts. Used by both the Create Example Agent
// modal (fetch + parse agent.yml, inject model, POST). Samples with an
// evalConfigPath also appear in the Run Evaluation modal.
// mirroring src/constants/sampleDatasets.ts. Used by the Create Example Agent
// modal (fetch + parse agent.yml, inject model, POST).
//
// Eval configs are a SEPARATE registry (EVAL_CONFIG_SAMPLES) on purpose: either
// paradigm can target any agent, so a config is not owned by an agent.
//
// INVARIANT: an entry whose agent.yml uses a custom NAT `_type` requires that
// tool's Python package to be installed in the deploy venv, or the deployment
// fails at startup. Current mappings:
// _type: calculator -> plugins/nemo-agents/examples/calculator-agent
// _type: email_phishing_analyzer -> plugins/nemo-agents/examples/email-phishing-analyzer
// _type: analyze_email -> plugins/nemo-agents/examples/email-security-analyst
// _type: extract_iocs -> plugins/nemo-agents/examples/email-security-analyst
export interface SampleAgent {
/** Stable key; also the dropdown value and label. */
key: string;
label: string;
displayName: string;
description: string;
/** Prefix for generated agent names; drives onboarding detection. */
namePrefix: string;
/** Public path to the NAT workflow config (parsed + model-injected at create). */
agentConfigPath: string;
/** Public path to a reusable nemo-evaluator eval-config.json. Samples without
* one remain available for agent creation but not evaluation seeding. */
evalConfigPath?: string;
/** Config format identifier sent to the create API. Defaults to
* `nat-workflow-v1` server-side when omitted; set to `nemo-agents-spec-v1`
* for Fabric-backed samples so the API validates them as Fabric, not NAT. */
Expand All @@ -34,25 +34,15 @@ export interface SampleAgent {

export const SAMPLE_AGENTS: SampleAgent[] = [
{
key: 'calculator',
label: 'calculator',
description: 'A ReAct agent with a calculator and datetime tool.',
namePrefix: 'calculator-demo-agent',
agentConfigPath: 'sample-agents/calculator/agent.yml',
evalConfigPath: 'sample-agents/calculator/eval-config.json',
},
{
key: 'email_phishing_analyzer',
label: 'email_phishing_analyzer',
description: 'A ReAct agent that inspects an email body for phishing signals.',
namePrefix: 'email-phishing-demo-agent',
agentConfigPath: 'sample-agents/email-phishing-analyzer/agent.yml',
evalConfigPath: 'sample-agents/email-phishing-analyzer/eval-config.json',
key: 'email_security_analyst',
displayName: 'Email Security Analyst',
description:
'An analyst-facing email security assistant: select one or more messages, optionally ask a question, and it routes to the capability that answers it.',
namePrefix: 'email-security-analyst',
agentConfigPath: 'sample-agents/email-security-analyst/agent.yml',
Comment thread
nv-odrulea marked this conversation as resolved.
},
];

// Eval configs are a SEPARATE registry (EVAL_CONFIG_SAMPLES) on purpose: either
// paradigm can target any agent, so a config is not owned by an agent.
export interface EvalConfigSample {
key: string;
displayName: string;
Expand Down Expand Up @@ -92,27 +82,11 @@ export const DEFAULT_EVAL_CONFIG_KEY = EVAL_CONFIG_SAMPLES[0].key;
export const getEvalConfigSample = (key: string): EvalConfigSample =>
EVAL_CONFIG_SAMPLES.find((sample) => sample.key === key) ?? EVAL_CONFIG_SAMPLES[0];

export type EvaluationSampleAgent = SampleAgent & { evalConfigPath: string };

export const EVALUATION_SAMPLE_AGENTS = SAMPLE_AGENTS.filter(
(agent): agent is EvaluationSampleAgent => typeof agent.evalConfigPath === 'string'
);

export const DEFAULT_SAMPLE_AGENT_KEY = SAMPLE_AGENTS[0].key;

export const getSampleAgent = (key: string): SampleAgent =>
SAMPLE_AGENTS.find((agent) => agent.key === key) ?? SAMPLE_AGENTS[0];

export const getEvaluationSampleAgent = (key: string): EvaluationSampleAgent =>
EVALUATION_SAMPLE_AGENTS.find((agent) => agent.key === key) ?? EVALUATION_SAMPLE_AGENTS[0];

export const evaluationSampleAgentKeyForAgentName = (
name: string | undefined
): string | undefined => {
const key = sampleAgentKeyForAgentName(name);
return EVALUATION_SAMPLE_AGENTS.some((agent) => agent.key === key) ? key : undefined;
};

export const buildSampleAgentName = (namePrefix: string): string =>
`${namePrefix}-${Math.random().toString(36).slice(2, 8)}`;

Expand All @@ -122,8 +96,7 @@ export const isSampleAgentName = (name: string): boolean =>
/**
* Infer which sample-agent example a deployed agent came from by matching its
* generated name (`${namePrefix}-<suffix>`). Returns the example key, or
* undefined for agents not created from an example. Used to auto-select the
* matching eval config.
* undefined for agents not created from an example.
*
* Robustness: requires the `${namePrefix}-` separator (so a prefix only matches
* a real name boundary, not a partial token) and picks the LONGEST matching
Expand Down
2 changes: 0 additions & 2 deletions web/packages/studio/src/mocks/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { evaluatorHandlers } from '@studio/mocks/handlers/evaluator';
import { filesetsHandlers } from '@studio/mocks/handlers/filesets';
import { guardrailsHandlers } from '@studio/mocks/handlers/guardrails';
import { modelsHandlers } from '@studio/mocks/handlers/models';
import { sampleAgentsHandlers } from '@studio/mocks/handlers/sampleAgents';
import { sampleDatasetsHandlers } from '@studio/mocks/handlers/sampleDatasets';
import { secretsHandlers } from '@studio/mocks/handlers/secrets';
import { workspacesHandlers } from '@studio/mocks/handlers/workspaces';
Expand Down Expand Up @@ -73,7 +72,6 @@ export interface HypermodelParams {
* but tests can override these with `server.use`.
*/
export const handlers = [
...sampleAgentsHandlers,
...sampleDatasetsHandlers,

// Evaluator V2 — fixtures loaded on first use to keep initial handler graph smaller
Expand Down
77 changes: 0 additions & 77 deletions web/packages/studio/src/mocks/handlers/sampleAgents.ts

This file was deleted.

Loading
Loading