Skip to content

Commit 598805f

Browse files
authored
Merge pull request #8644 from ferenci84/per-tab-model-persistence
Add per-tab model persistence and selection functionality
2 parents 4fdb919 + 4b330de commit 598805f

File tree

4 files changed

+66
-3
lines changed

4 files changed

+66
-3
lines changed

core/index.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,10 @@ export interface Session {
272272
title: string;
273273
workspaceDirectory: string;
274274
history: ChatHistoryItem[];
275+
/** Optional: per-session UI mode (chat/agent/plan/background) */
276+
mode?: MessageModes;
277+
/** Optional: title of the selected chat model for this session */
278+
chatModelTitle?: string | null;
275279
}
276280

277281
export interface BaseSessionMetadata {

core/util/history.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as fs from "fs";
22

3-
import { Session, BaseSessionMetadata } from "../index.js";
3+
import { BaseSessionMetadata, Session } from "../index.js";
44
import { ListHistoryOptions } from "../protocol/core.js";
55

66
import { NEW_SESSION_TITLE } from "./constants.js";
@@ -108,6 +108,13 @@ export class HistoryManager {
108108
workspaceDirectory: session.workspaceDirectory,
109109
history: session.history,
110110
};
111+
if (session.mode) {
112+
orderedSession.mode = session.mode;
113+
}
114+
if (session.chatModelTitle !== undefined) {
115+
orderedSession.chatModelTitle = session.chatModelTitle;
116+
}
117+
111118
fs.writeFileSync(
112119
getSessionFilePath(session.sessionId),
113120
JSON.stringify(orderedSession, undefined, 2),

gui/src/redux/slices/sessionSlice.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,9 @@ export const sessionSlice = createSlice({
693693
state.history = payload.history as any;
694694
state.title = payload.title;
695695
state.id = payload.sessionId;
696+
if (payload.mode) {
697+
state.mode = payload.mode;
698+
}
696699
} else {
697700
state.history = [];
698701
state.title = NEW_SESSION_TITLE;

gui/src/redux/thunks/session.ts

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { NEW_SESSION_TITLE } from "core/util/constants";
55
import { renderChatMessage } from "core/util/messageContent";
66
import { IIdeMessenger } from "../../context/IdeMessenger";
77
import { selectSelectedChatModel } from "../slices/configSlice";
8+
import { selectSelectedProfile } from "../slices/profilesSlice";
89
import {
910
deleteSessionMetadata,
1011
newSession,
@@ -13,6 +14,7 @@ import {
1314
updateSessionMetadata,
1415
} from "../slices/sessionSlice";
1516
import { ThunkApiType } from "../store";
17+
import { updateSelectedModelByRole } from "../thunks/updateSelectedModelByRole";
1618

1719
const MAX_TITLE_LENGTH = 100;
1820

@@ -103,7 +105,10 @@ export const loadSession = createAsyncThunk<
103105
ThunkApiType
104106
>(
105107
"session/load",
106-
async ({ sessionId, saveCurrentSession: save }, { extra, dispatch }) => {
108+
async (
109+
{ sessionId, saveCurrentSession: save },
110+
{ extra, dispatch, getState },
111+
) => {
107112
if (save) {
108113
const result = await dispatch(
109114
saveCurrentSession({
@@ -115,6 +120,11 @@ export const loadSession = createAsyncThunk<
115120
}
116121
const session = await getSession(extra.ideMessenger, sessionId);
117122
dispatch(newSession(session));
123+
124+
// Restore selected chat model from session, if present
125+
if (session.chatModelTitle) {
126+
dispatch(selectChatModelForProfile(session.chatModelTitle));
127+
}
118128
},
119129
);
120130

@@ -127,7 +137,10 @@ export const loadRemoteSession = createAsyncThunk<
127137
ThunkApiType
128138
>(
129139
"session/loadRemote",
130-
async ({ remoteId, saveCurrentSession: save }, { extra, dispatch }) => {
140+
async (
141+
{ remoteId, saveCurrentSession: save },
142+
{ extra, dispatch, getState },
143+
) => {
131144
if (save) {
132145
const result = await dispatch(
133146
saveCurrentSession({
@@ -139,6 +152,35 @@ export const loadRemoteSession = createAsyncThunk<
139152
}
140153
const session = await getRemoteSession(extra.ideMessenger, remoteId);
141154
dispatch(newSession(session));
155+
156+
// Restore selected chat model from session, if present
157+
if (session.chatModelTitle) {
158+
dispatch(selectChatModelForProfile(session.chatModelTitle));
159+
}
160+
},
161+
);
162+
163+
export const selectChatModelForProfile = createAsyncThunk<
164+
void,
165+
string,
166+
ThunkApiType
167+
>(
168+
"session/selectModelForCurrentProfile",
169+
async (modelTitle, { extra, dispatch, getState }) => {
170+
const state = getState();
171+
const modelMatch = state.config.config?.modelsByRole?.chat?.find(
172+
(m) => m.title === modelTitle,
173+
);
174+
const selectedProfile = selectSelectedProfile(state);
175+
if (selectedProfile && modelMatch) {
176+
await dispatch(
177+
updateSelectedModelByRole({
178+
role: "chat",
179+
modelTitle: modelTitle,
180+
selectedProfile,
181+
}),
182+
);
183+
}
142184
},
143185
);
144186

@@ -168,6 +210,9 @@ export const loadLastSession = createAsyncThunk<void, void, ThunkApiType>(
168210
session = await getSession(extra.ideMessenger, lastSessionId);
169211
}
170212
dispatch(newSession(session));
213+
if (session.chatModelTitle) {
214+
dispatch(selectChatModelForProfile(session.chatModelTitle));
215+
}
171216
},
172217
);
173218

@@ -246,11 +291,15 @@ export const saveCurrentSession = createAsyncThunk<
246291
title = NEW_SESSION_TITLE;
247292
}
248293

294+
const selectedChatModel = selectSelectedChatModel(state);
295+
249296
const session: Session = {
250297
sessionId: state.session.id,
251298
title,
252299
workspaceDirectory: window.workspacePaths?.[0] || "",
253300
history: state.session.history,
301+
mode: state.session.mode,
302+
chatModelTitle: selectedChatModel?.title ?? null,
254303
};
255304

256305
const result = await dispatch(updateSession(session));

0 commit comments

Comments
 (0)