mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-30 01:52:18 +00:00
fix(ui): extract overlayDiscoveredCardParams + debounce parent-driven discovery
Two findings from greptile review: 1. `overlayDiscoveredCardParams` was copy-pasted between `add_agent_form.tsx` and `agent_info.tsx`. Move it to `agent_discovery_utils.ts` so the create and edit flows share the same overlay logic and there's only one place to update when discovered fields change. 2. `agent_card_discovery.tsx` used a zero-debounce path for parent-driven mode, which fires one discovery HTTP request per keystroke when an admin types into the parent form's URL / api_base / assistant_id fields (the parent rebuilds the plan from watched form values every render). Apply the same 400ms debounce uniformly.
This commit is contained in:
parent
8145ece4ca
commit
1a9bce33be
4 changed files with 64 additions and 84 deletions
|
|
@ -22,7 +22,10 @@ import AgentFormFields from "./agent_form_fields";
|
|||
import AgentCardDiscovery, {
|
||||
DiscoveredAgentCardSelection,
|
||||
} from "./agent_card_discovery";
|
||||
import { buildDiscoveryRequest } from "./agent_discovery_utils";
|
||||
import {
|
||||
buildDiscoveryRequest,
|
||||
overlayDiscoveredCardParams,
|
||||
} from "./agent_discovery_utils";
|
||||
import DynamicAgentFormFields, { buildDynamicAgentData } from "./dynamic_agent_form_fields";
|
||||
import { getDefaultFormValues, buildAgentDataFromForm } from "./agent_config";
|
||||
import MCPServerSelector from "../mcp_server_management/MCPServerSelector";
|
||||
|
|
@ -210,47 +213,6 @@ const AddAgentForm: React.FC<AddAgentFormProps> = ({
|
|||
setCurrentStep((s) => Math.max(0, s - 1));
|
||||
};
|
||||
|
||||
// Overlay the user's discovery selections onto the agent_card_params built
|
||||
// from the form. Name and description are overlaid (falling back to the
|
||||
// form-built value) along with skills, capabilities, input/output modes,
|
||||
// provider, and icon/doc URLs because dynamic agent forms (e.g. LangGraph)
|
||||
// don't register Form.Items for them, so AntD's setFieldsValue silently
|
||||
// drops those keys and the values never make it back through buildAgentData.
|
||||
const overlayDiscoveredCardParams = (
|
||||
agentData: Record<string, any>,
|
||||
): Record<string, any> => {
|
||||
if (!appliedDiscoveredSelection) return agentData;
|
||||
const discovered = appliedDiscoveredSelection.selected_card;
|
||||
return {
|
||||
...agentData,
|
||||
agent_card_params: {
|
||||
...agentData.agent_card_params,
|
||||
name: discovered.name ?? agentData.agent_card_params?.name,
|
||||
description:
|
||||
discovered.description ?? agentData.agent_card_params?.description,
|
||||
...(Array.isArray(discovered.skills) && {
|
||||
skills: discovered.skills,
|
||||
}),
|
||||
...(discovered.capabilities && {
|
||||
capabilities: discovered.capabilities,
|
||||
}),
|
||||
...(Array.isArray(discovered.defaultInputModes) &&
|
||||
discovered.defaultInputModes.length > 0 && {
|
||||
defaultInputModes: discovered.defaultInputModes,
|
||||
}),
|
||||
...(Array.isArray(discovered.defaultOutputModes) &&
|
||||
discovered.defaultOutputModes.length > 0 && {
|
||||
defaultOutputModes: discovered.defaultOutputModes,
|
||||
}),
|
||||
...(discovered.provider && { provider: discovered.provider }),
|
||||
...(discovered.iconUrl && { iconUrl: discovered.iconUrl }),
|
||||
...(discovered.documentationUrl && {
|
||||
documentationUrl: discovered.documentationUrl,
|
||||
}),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const buildAgentData = (values: any) => {
|
||||
if (agentType === CUSTOM_AGENT_TYPE) {
|
||||
return {
|
||||
|
|
@ -292,7 +254,10 @@ const AddAgentForm: React.FC<AddAgentFormProps> = ({
|
|||
return null;
|
||||
}
|
||||
|
||||
return overlayDiscoveredCardParams(agentData);
|
||||
return overlayDiscoveredCardParams(
|
||||
agentData,
|
||||
appliedDiscoveredSelection?.selected_card,
|
||||
);
|
||||
};
|
||||
|
||||
const handleCreateAgent = async () => {
|
||||
|
|
|
|||
|
|
@ -199,7 +199,10 @@ const AgentCardDiscovery: React.FC<AgentCardDiscoveryProps> = ({
|
|||
discoveryParamsKey,
|
||||
]);
|
||||
|
||||
// Auto-discover when the URL (or parent plan) becomes available.
|
||||
// Auto-discover when the URL (or parent plan) becomes available. Debounce
|
||||
// is applied uniformly so rapid changes from a watched parent form (e.g.
|
||||
// typing into a LangGraph api_base / assistant_id field) don't fire one
|
||||
// HTTP request per keystroke.
|
||||
useEffect(() => {
|
||||
if (!accessToken) return;
|
||||
const trimmed = effectiveUrl.trim();
|
||||
|
|
@ -211,12 +214,11 @@ const AgentCardDiscovery: React.FC<AgentCardDiscoveryProps> = ({
|
|||
return;
|
||||
}
|
||||
|
||||
const debounceMs = isParentDriven ? 0 : 400;
|
||||
const timer = window.setTimeout(() => {
|
||||
void handleDiscover();
|
||||
}, debounceMs);
|
||||
}, 400);
|
||||
return () => window.clearTimeout(timer);
|
||||
}, [accessToken, effectiveUrl, isParentDriven, handleDiscover]);
|
||||
}, [accessToken, effectiveUrl, handleDiscover]);
|
||||
|
||||
const toggleSkill = (id: string, checked: boolean) => {
|
||||
setSelectedSkillIds((prev) => {
|
||||
|
|
|
|||
|
|
@ -94,6 +94,48 @@ export const selectionsFromUpstreamCard = (
|
|||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Overlay the admin's discovery selections onto the ``agent_card_params``
|
||||
* built from the form. Dynamic agent forms (e.g. LangGraph) don't register
|
||||
* Form.Items for name / description / skills / capabilities, so AntD's
|
||||
* setFieldsValue silently drops those keys and the values never make it back
|
||||
* through buildAgentData — we re-apply them here from the selection.
|
||||
*/
|
||||
export const overlayDiscoveredCardParams = (
|
||||
agentData: Record<string, any>,
|
||||
discovered: DiscoveredAgentCard | null | undefined,
|
||||
): Record<string, any> => {
|
||||
if (!discovered) return agentData;
|
||||
return {
|
||||
...agentData,
|
||||
agent_card_params: {
|
||||
...agentData.agent_card_params,
|
||||
name: discovered.name ?? agentData.agent_card_params?.name,
|
||||
description:
|
||||
discovered.description ?? agentData.agent_card_params?.description,
|
||||
...(Array.isArray(discovered.skills) && {
|
||||
skills: discovered.skills,
|
||||
}),
|
||||
...(discovered.capabilities && {
|
||||
capabilities: discovered.capabilities,
|
||||
}),
|
||||
...(Array.isArray(discovered.defaultInputModes) &&
|
||||
discovered.defaultInputModes.length > 0 && {
|
||||
defaultInputModes: discovered.defaultInputModes,
|
||||
}),
|
||||
...(Array.isArray(discovered.defaultOutputModes) &&
|
||||
discovered.defaultOutputModes.length > 0 && {
|
||||
defaultOutputModes: discovered.defaultOutputModes,
|
||||
}),
|
||||
...(discovered.provider && { provider: discovered.provider }),
|
||||
...(discovered.iconUrl && { iconUrl: discovered.iconUrl }),
|
||||
...(discovered.documentationUrl && {
|
||||
documentationUrl: discovered.documentationUrl,
|
||||
}),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const buildDiscoveryRequest = (
|
||||
agentType: string,
|
||||
values: Record<string, any>,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,10 @@ import { detectAgentType, parseDynamicAgentForForm } from "./agent_type_utils";
|
|||
import AgentCardDiscovery, {
|
||||
DiscoveredAgentCardSelection,
|
||||
} from "./agent_card_discovery";
|
||||
import { buildDiscoveryRequest } from "./agent_discovery_utils";
|
||||
import {
|
||||
buildDiscoveryRequest,
|
||||
overlayDiscoveredCardParams,
|
||||
} from "./agent_discovery_utils";
|
||||
|
||||
interface AgentInfoViewProps {
|
||||
agentId: string;
|
||||
|
|
@ -111,41 +114,6 @@ const AgentInfoView: React.FC<AgentInfoViewProps> = ({
|
|||
[watchedFormValues, selectedAgentTypeInfo, detectedAgentType],
|
||||
);
|
||||
|
||||
const overlayDiscoveredCardParams = (
|
||||
agentData: Record<string, any>,
|
||||
): Record<string, any> => {
|
||||
if (!appliedDiscoveredSelection) return agentData;
|
||||
const discovered = appliedDiscoveredSelection.selected_card;
|
||||
return {
|
||||
...agentData,
|
||||
agent_card_params: {
|
||||
...agentData.agent_card_params,
|
||||
name: discovered.name ?? agentData.agent_card_params?.name,
|
||||
description:
|
||||
discovered.description ?? agentData.agent_card_params?.description,
|
||||
...(Array.isArray(discovered.skills) && {
|
||||
skills: discovered.skills,
|
||||
}),
|
||||
...(discovered.capabilities && {
|
||||
capabilities: discovered.capabilities,
|
||||
}),
|
||||
...(Array.isArray(discovered.defaultInputModes) &&
|
||||
discovered.defaultInputModes.length > 0 && {
|
||||
defaultInputModes: discovered.defaultInputModes,
|
||||
}),
|
||||
...(Array.isArray(discovered.defaultOutputModes) &&
|
||||
discovered.defaultOutputModes.length > 0 && {
|
||||
defaultOutputModes: discovered.defaultOutputModes,
|
||||
}),
|
||||
...(discovered.provider && { provider: discovered.provider }),
|
||||
...(discovered.iconUrl && { iconUrl: discovered.iconUrl }),
|
||||
...(discovered.documentationUrl && {
|
||||
documentationUrl: discovered.documentationUrl,
|
||||
}),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const handleApplyDiscoveredCard = (
|
||||
selection: DiscoveredAgentCardSelection | null,
|
||||
) => {
|
||||
|
|
@ -196,7 +164,10 @@ const AgentInfoView: React.FC<AgentInfoViewProps> = ({
|
|||
}
|
||||
|
||||
if (appliedDiscoveredSelection) {
|
||||
updateData = overlayDiscoveredCardParams(updateData);
|
||||
updateData = overlayDiscoveredCardParams(
|
||||
updateData,
|
||||
appliedDiscoveredSelection.selected_card,
|
||||
);
|
||||
}
|
||||
|
||||
await patchAgentCall(accessToken, agentId, updateData);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue