📛 feat: Chat Badges via Model Specs (#10272)

* refactor: remove `useChatContext` from `useSelectMention`, explicitly pass `conversation` object

* feat: ephemeral agents via model specs

* refactor: Sync Jotai state with ephemeral agent state, also when Ephemeral Agent has no MCP servers selected

* refactor: move `useUpdateEphemeralAgent` to store and clean up imports

* refactor: reorder imports and invalidate queries for mcpConnectionStatus in event handler

* refactor: replace useApplyModelSpecEffects with useApplyModelSpecAgents and update event handlers to use new agent template logic

* ci: update useMCPSelect test to verify mcpValues sync with empty ephemeralAgent.mcp
This commit is contained in:
Danny Avila 2025-10-27 19:46:30 -04:00 committed by GitHub
parent 64df54528d
commit 33d6b337bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 254 additions and 41 deletions

View file

@ -6,3 +6,4 @@ export { default as useAgentCapabilities } from './useAgentCapabilities';
export { default as useGetAgentsConfig } from './useGetAgentsConfig';
export { default as useAgentDefaultPermissionLevel } from './useAgentDefaultPermissionLevel';
export { default as useAgentToolPermissions } from './useAgentToolPermissions';
export * from './useApplyModelSpecAgents';

View file

@ -0,0 +1,95 @@
import { useCallback } from 'react';
import type { TStartupConfig, TSubmission } from 'librechat-data-provider';
import { useUpdateEphemeralAgent, useApplyNewAgentTemplate } from '~/store/agents';
import { getModelSpec, applyModelSpecEphemeralAgent } from '~/utils';
/**
* Hook that applies a model spec from a preset to an ephemeral agent.
* This is used when initializing a new conversation with a preset that has a spec.
*/
export function useApplyModelSpecEffects() {
const updateEphemeralAgent = useUpdateEphemeralAgent();
const applyPresetModelSpec = useCallback(
({
convoId,
specName,
startupConfig,
}: {
convoId: string | null;
specName?: string | null;
startupConfig?: TStartupConfig;
}) => {
if (specName == null || !specName) {
return;
}
const modelSpec = getModelSpec({
specName,
startupConfig,
});
applyModelSpecEphemeralAgent({
convoId,
modelSpec,
updateEphemeralAgent,
});
},
[updateEphemeralAgent],
);
return applyPresetModelSpec;
}
export function useApplyAgentTemplate() {
const applyAgentTemplate = useApplyNewAgentTemplate();
/**
* Helper function to apply agent template with model spec merged into ephemeral agent
*/
const applyAgentTemplateWithSpec = useCallback(
({
targetId,
sourceId,
ephemeralAgent,
specName,
startupConfig,
}: {
targetId: string;
sourceId?: TSubmission['conversation']['conversationId'] | null;
ephemeralAgent: TSubmission['ephemeralAgent'];
specName?: string | null;
startupConfig?: TStartupConfig;
}) => {
if (!specName) {
applyAgentTemplate(targetId, sourceId, ephemeralAgent);
return;
}
const modelSpec = getModelSpec({
specName,
startupConfig,
});
if (!modelSpec) {
applyAgentTemplate(targetId, sourceId, ephemeralAgent);
return;
}
// Merge model spec fields into ephemeral agent
const mergedAgent = {
...ephemeralAgent,
mcp: [...(ephemeralAgent?.mcp ?? []), ...(modelSpec.mcpServers ?? [])],
web_search: ephemeralAgent?.web_search ?? modelSpec.webSearch ?? false,
file_search: ephemeralAgent?.file_search ?? modelSpec.fileSearch ?? false,
execute_code: ephemeralAgent?.execute_code ?? modelSpec.executeCode ?? false,
};
// Deduplicate MCP servers
mergedAgent.mcp = [...new Set(mergedAgent.mcp)];
applyAgentTemplate(targetId, sourceId, mergedAgent);
},
[applyAgentTemplate],
);
return applyAgentTemplateWithSpec;
}