Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
39 changes: 37 additions & 2 deletions client/src/hooks/useNewConvo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,42 @@ const useNewConvo = (index = 0) => {

let preset = _preset;
const result = getDefaultModelSpec(startupConfig);
const defaultModelSpec = result?.default ?? result?.last;
if (

// Prefer last used spec over default spec for better user experience
const defaultModelSpec = result?.last ?? result?.default;

// Check for last used agent_id that might be outside of modelSpecs
const lastAgentId = localStorage.getItem(`${LocalStorageKeys.AGENT_ID_PREFIX}${index}`);

// Get the last conversation setup to determine what was most recently selected
// This is the most reliable way to know if a custom agent was selected after a modelSpec
const lastConvoSetup = JSON.parse(
localStorage.getItem(`${LocalStorageKeys.LAST_CONVO_SETUP}_${index}`) ?? '{}',
);
const lastConvoAgentId = lastConvoSetup?.agent_id;
const lastConvoSpec = lastConvoSetup?.spec;

// Use lastAgentId if:
// 1. No preset passed AND no template AND lastAgentId exists
// 2. AND the last conversation had this agent_id without a spec (custom agent selection)
// OR there's no defaultModelSpec at all
// This ensures:
// - Custom agent selection (no spec) is remembered
// - ModelSpec selections (has spec) take priority when they were the last action
const shouldUseLastAgent =
!_preset &&
lastAgentId &&
Object.keys(_template).length === 0 &&
((!lastConvoSpec && lastConvoAgentId === lastAgentId) || !defaultModelSpec);

if (shouldUseLastAgent) {
// Create a preset for the last used agent outside of modelSpecs
preset = {
endpoint: EModelEndpoint.agents,
agent_id: lastAgentId,
};
logger.log('conversation', 'Using last used agent_id outside modelSpecs', lastAgentId);
} else if (
!preset &&
startupConfig &&
(startupConfig.modelSpecs?.prioritize === true ||
Expand Down Expand Up @@ -352,6 +386,7 @@ const useNewConvo = (index = 0) => {
);
},
[
index,
files,
setFiles,
saveDrafts,
Expand Down
12 changes: 5 additions & 7 deletions client/src/routes/ChatRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useGetModelsQuery } from 'librechat-data-provider/react-query';
import type { TPreset } from 'librechat-data-provider';
import { useGetConvoIdQuery, useGetStartupConfig, useGetEndpointsQuery } from '~/data-provider';
import { useNewConvo, useAppStartup, useAssistantListMap, useIdChangeEffect } from '~/hooks';
import { getDefaultModelSpec, getModelSpecPreset, logger } from '~/utils';
import { logger } from '~/utils';
import { ToolCallsMapProvider } from '~/Providers';
import ChatView from '~/components/Chat/ChatView';
import useAuthRedirect from './useAuthRedirect';
Expand Down Expand Up @@ -72,13 +72,12 @@ export default function ChatRoute() {
}

if (conversationId === Constants.NEW_CONVO && endpointsQuery.data && modelsQuery.data) {
const result = getDefaultModelSpec(startupConfig);
const spec = result?.default ?? result?.last;
// Let useNewConvo handle the full logic for determining the preset
// (including last used agent_id that might be outside of modelSpecs)
logger.log('conversation', 'ChatRoute, new convo effect', conversation);
newConversation({
modelsData: modelsQuery.data,
template: conversation ? conversation : undefined,
...(spec ? { preset: getModelSpecPreset(spec) } : {}),
});

hasSetConversation.current = true;
Expand All @@ -97,13 +96,12 @@ export default function ChatRoute() {
assistantListMap[EModelEndpoint.assistants] &&
assistantListMap[EModelEndpoint.azureAssistants]
) {
const result = getDefaultModelSpec(startupConfig);
const spec = result?.default ?? result?.last;
// Let useNewConvo handle the full logic for determining the preset
// (including last used agent_id that might be outside of modelSpecs)
logger.log('conversation', 'ChatRoute new convo, assistants effect', conversation);
newConversation({
modelsData: modelsQuery.data,
template: conversation ? conversation : undefined,
...(spec ? { preset: getModelSpecPreset(spec) } : {}),
});
hasSetConversation.current = true;
} else if (
Expand Down
38 changes: 24 additions & 14 deletions client/src/utils/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,8 @@ export function applyModelSpecEphemeralAgent({

/**
* Gets default model spec from config and user preferences.
* Priority: admin default last selected → first spec (when prioritize=true or modelSelect disabled).
* Otherwise: admin default or last conversation spec.
* Returns both admin default and last selected spec when available.
* Priority for usage: last selected → admin default → first spec (when prioritize=true or modelSelect disabled).
*/
export function getDefaultModelSpec(startupConfig?: t.TStartupConfig):
| {
Expand All @@ -244,20 +244,30 @@ export function getDefaultModelSpec(startupConfig?: t.TStartupConfig):
return;
}
const defaultSpec = list?.find((spec) => spec.default);
if (prioritize === true || !interfaceConfig?.modelSelect) {
const lastSelectedSpecName = localStorage.getItem(LocalStorageKeys.LAST_SPEC);
const lastSelectedSpec = list?.find((spec) => spec.name === lastSelectedSpecName);
return { default: defaultSpec || lastSelectedSpec || list?.[0] };
} else if (defaultSpec) {
return { default: defaultSpec };

// Try to get last selected spec from localStorage
const lastSelectedSpecName = localStorage.getItem(LocalStorageKeys.LAST_SPEC);
let lastSelectedSpec = lastSelectedSpecName
? list?.find((spec) => spec.name === lastSelectedSpecName)
: undefined;

// Fallback: try to get spec from last conversation setup
if (!lastSelectedSpec) {
const lastConversationSetup = JSON.parse(
localStorage.getItem(LocalStorageKeys.LAST_CONVO_SETUP + '_0') ?? '{}',
);
if (lastConversationSetup.spec) {
lastSelectedSpec = list?.find((spec) => spec.name === lastConversationSetup.spec);
}
}
const lastConversationSetup = JSON.parse(
localStorage.getItem(LocalStorageKeys.LAST_CONVO_SETUP + '_0') ?? '{}',
);
if (!lastConversationSetup.spec) {
return;

if (prioritize === true || !interfaceConfig?.modelSelect) {
// When prioritize is true or modelSelect is disabled, return the effective spec as default
return { default: lastSelectedSpec || defaultSpec || list?.[0], last: lastSelectedSpec };
}
return { last: list?.find((spec) => spec.name === lastConversationSetup.spec) };

// Return both default and last, allowing consumer to choose priority
return { default: defaultSpec, last: lastSelectedSpec };
}

export function getModelSpecPreset(modelSpec?: t.TModelSpec) {
Expand Down