Skip to content

Commit 654e2d4

Browse files
committed
📌 feat: memorize agent/model selection and use from last choice
1 parent daed6d9 commit 654e2d4

File tree

3 files changed

+66
-23
lines changed

3 files changed

+66
-23
lines changed

client/src/hooks/useNewConvo.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,8 +297,42 @@ const useNewConvo = (index = 0) => {
297297

298298
let preset = _preset;
299299
const result = getDefaultModelSpec(startupConfig);
300-
const defaultModelSpec = result?.default ?? result?.last;
301-
if (
300+
301+
// Prefer last used spec over default spec for better user experience
302+
const defaultModelSpec = result?.last ?? result?.default;
303+
304+
// Check for last used agent_id that might be outside of modelSpecs
305+
const lastAgentId = localStorage.getItem(`${LocalStorageKeys.AGENT_ID_PREFIX}${index}`);
306+
307+
// Get the last conversation setup to determine what was most recently selected
308+
// This is the most reliable way to know if a custom agent was selected after a modelSpec
309+
const lastConvoSetup = JSON.parse(
310+
localStorage.getItem(`${LocalStorageKeys.LAST_CONVO_SETUP}_${index}`) ?? '{}',
311+
);
312+
const lastConvoAgentId = lastConvoSetup?.agent_id;
313+
const lastConvoSpec = lastConvoSetup?.spec;
314+
315+
// Use lastAgentId if:
316+
// 1. No preset passed AND no template AND lastAgentId exists
317+
// 2. AND the last conversation had this agent_id without a spec (custom agent selection)
318+
// OR there's no defaultModelSpec at all
319+
// This ensures:
320+
// - Custom agent selection (no spec) is remembered
321+
// - ModelSpec selections (has spec) take priority when they were the last action
322+
const shouldUseLastAgent =
323+
!_preset &&
324+
lastAgentId &&
325+
Object.keys(_template).length === 0 &&
326+
((!lastConvoSpec && lastConvoAgentId === lastAgentId) || !defaultModelSpec);
327+
328+
if (shouldUseLastAgent) {
329+
// Create a preset for the last used agent outside of modelSpecs
330+
preset = {
331+
endpoint: EModelEndpoint.agents,
332+
agent_id: lastAgentId,
333+
};
334+
logger.log('conversation', 'Using last used agent_id outside modelSpecs', lastAgentId);
335+
} else if (
302336
!preset &&
303337
startupConfig &&
304338
(startupConfig.modelSpecs?.prioritize === true ||

client/src/routes/ChatRoute.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { useGetModelsQuery } from 'librechat-data-provider/react-query';
77
import type { TPreset } from 'librechat-data-provider';
88
import { useGetConvoIdQuery, useGetStartupConfig, useGetEndpointsQuery } from '~/data-provider';
99
import { useNewConvo, useAppStartup, useAssistantListMap, useIdChangeEffect } from '~/hooks';
10-
import { getDefaultModelSpec, getModelSpecPreset, logger } from '~/utils';
10+
import { logger } from '~/utils';
1111
import { ToolCallsMapProvider } from '~/Providers';
1212
import ChatView from '~/components/Chat/ChatView';
1313
import useAuthRedirect from './useAuthRedirect';
@@ -72,13 +72,12 @@ export default function ChatRoute() {
7272
}
7373

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

8483
hasSetConversation.current = true;
@@ -97,13 +96,12 @@ export default function ChatRoute() {
9796
assistantListMap[EModelEndpoint.assistants] &&
9897
assistantListMap[EModelEndpoint.azureAssistants]
9998
) {
100-
const result = getDefaultModelSpec(startupConfig);
101-
const spec = result?.default ?? result?.last;
99+
// Let useNewConvo handle the full logic for determining the preset
100+
// (including last used agent_id that might be outside of modelSpecs)
102101
logger.log('conversation', 'ChatRoute new convo, assistants effect', conversation);
103102
newConversation({
104103
modelsData: modelsQuery.data,
105104
template: conversation ? conversation : undefined,
106-
...(spec ? { preset: getModelSpecPreset(spec) } : {}),
107105
});
108106
hasSetConversation.current = true;
109107
} else if (

client/src/utils/endpoints.ts

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,8 @@ export function applyModelSpecEphemeralAgent({
229229

230230
/**
231231
* Gets default model spec from config and user preferences.
232-
* Priority: admin default last selected → first spec (when prioritize=true or modelSelect disabled).
233-
* Otherwise: admin default or last conversation spec.
232+
* Returns both admin default and last selected spec when available.
233+
* Priority for usage: last selected → admin default → first spec (when prioritize=true or modelSelect disabled).
234234
*/
235235
export function getDefaultModelSpec(startupConfig?: t.TStartupConfig):
236236
| {
@@ -244,20 +244,31 @@ export function getDefaultModelSpec(startupConfig?: t.TStartupConfig):
244244
return;
245245
}
246246
const defaultSpec = list?.find((spec) => spec.default);
247-
if (prioritize === true || !interfaceConfig?.modelSelect) {
248-
const lastSelectedSpecName = localStorage.getItem(LocalStorageKeys.LAST_SPEC);
249-
const lastSelectedSpec = list?.find((spec) => spec.name === lastSelectedSpecName);
250-
return { default: defaultSpec || lastSelectedSpec || list?.[0] };
251-
} else if (defaultSpec) {
252-
return { default: defaultSpec };
247+
248+
// Try to get last selected spec from localStorage
249+
const lastSelectedSpecName = localStorage.getItem(LocalStorageKeys.LAST_SPEC);
250+
let lastSelectedSpec = lastSelectedSpecName
251+
? list?.find((spec) => spec.name === lastSelectedSpecName)
252+
: undefined;
253+
254+
// Fallback: try to get spec from last conversation setup
255+
if (!lastSelectedSpec) {
256+
const lastConversationSetup = JSON.parse(
257+
localStorage.getItem(LocalStorageKeys.LAST_CONVO_SETUP + '_0') ?? '{}',
258+
);
259+
if (lastConversationSetup.spec) {
260+
lastSelectedSpec = list?.find((spec) => spec.name === lastConversationSetup.spec);
261+
}
253262
}
254-
const lastConversationSetup = JSON.parse(
255-
localStorage.getItem(LocalStorageKeys.LAST_CONVO_SETUP + '_0') ?? '{}',
256-
);
257-
if (!lastConversationSetup.spec) {
258-
return;
263+
264+
if (prioritize === true || !interfaceConfig?.modelSelect) {
265+
// When prioritize is true or modelSelect is disabled, return the effective spec as default
266+
return { default: lastSelectedSpec || defaultSpec || list?.[0], last: lastSelectedSpec };
267+
259268
}
260-
return { last: list?.find((spec) => spec.name === lastConversationSetup.spec) };
269+
270+
// Return both default and last, allowing consumer to choose priority
271+
return { default: defaultSpec, last: lastSelectedSpec };
261272
}
262273

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

0 commit comments

Comments
 (0)