Skip to content

Commit 2402271

Browse files
committed
refactor(auth-profiles): share stale runtime model selection
1 parent c7b40d7 commit 2402271

3 files changed

Lines changed: 117 additions & 127 deletions

File tree

src/gateway/server-methods/sessions.ts

Lines changed: 18 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,8 @@ import {
4444
waitForEmbeddedAgentRunEnd,
4545
} from "../../agents/embedded-agent-runner/runs.js";
4646
import { compactEmbeddedAgentSession } from "../../agents/embedded-agent.js";
47-
import { resolveModelRefFromString } from "../../agents/model-selection-shared.js";
4847
import { clearSessionQueues } from "../../auto-reply/reply/queue/cleanup.js";
4948
import { normalizeReasoningLevel, normalizeThinkLevel } from "../../auto-reply/thinking.js";
50-
import { resolveChannelModelOverride } from "../../channels/model-overrides.js";
5149
import {
5250
loadSessionStore,
5351
runSessionsCleanup,
@@ -109,6 +107,7 @@ import {
109107
readSessionMessageCountAsync,
110108
readSessionPreviewItemsFromTranscript,
111109
resolveDeletedAgentIdFromSessionKey,
110+
resolveSessionExpectedSelectedModelRef,
112111
resolveFreshestSessionEntryFromStoreKeys,
113112
resolveGatewaySessionStoreTarget,
114113
resolveGatewaySessionStoreTargetWithStore,
@@ -212,53 +211,28 @@ function resolveParentExpectedRuntimeSelection(
212211
agentId: string,
213212
store?: Record<string, SessionEntry>,
214213
): { provider: string; model: string; config: OpenClawConfig } {
215-
const defaultSelection = resolveSessionModelRef(cfg, undefined, agentId);
214+
const defaultSelection = resolveSessionExpectedSelectedModelRef({ cfg, agentId });
216215
const visited = new Set<string>();
217216
let currentEntry = parentEntry;
218217
let currentAgentId = agentId;
219218

220219
for (let depth = 0; currentEntry && depth < 8; depth += 1) {
221-
const explicitProviderOverride = normalizeOptionalString(currentEntry.providerOverride);
222-
const explicitModelOverride = normalizeOptionalString(currentEntry.modelOverride);
223-
if (explicitProviderOverride || explicitModelOverride) {
224-
return {
225-
...resolveSessionModelRef(
226-
cfg,
227-
{
228-
providerOverride: explicitProviderOverride,
229-
modelOverride: explicitModelOverride,
230-
},
231-
currentAgentId,
232-
),
233-
config: cfg,
234-
};
235-
}
236-
237-
const entryDefaultSelection = resolveSessionModelRef(cfg, undefined, currentAgentId);
238-
const channelModelOverride = cfg.channels?.modelByChannel
239-
? resolveChannelModelOverride({
240-
cfg,
241-
channel:
242-
currentEntry.channel ?? currentEntry.origin?.provider ?? currentEntry.lastChannel,
243-
groupId: currentEntry.groupId,
244-
groupChatType: currentEntry.chatType,
245-
groupChannel: currentEntry.groupChannel,
246-
groupSubject: currentEntry.subject,
247-
parentSessionKey: currentEntry.parentSessionKey,
248-
})
249-
: null;
250-
const channelSelection = channelModelOverride
251-
? resolveModelRefFromString({
252-
cfg,
253-
raw: channelModelOverride.model,
254-
defaultProvider: entryDefaultSelection.provider,
255-
})?.ref
256-
: null;
257-
if (channelSelection) {
258-
return {
259-
...channelSelection,
260-
config: cfg,
261-
};
220+
const currentDefaultSelection = resolveSessionExpectedSelectedModelRef({
221+
cfg,
222+
agentId: currentAgentId,
223+
});
224+
const currentSelection = resolveSessionExpectedSelectedModelRef({
225+
cfg,
226+
entry: currentEntry,
227+
agentId: currentAgentId,
228+
});
229+
if (
230+
normalizeOptionalString(currentEntry.providerOverride) ||
231+
normalizeOptionalString(currentEntry.modelOverride) ||
232+
currentSelection.provider !== currentDefaultSelection.provider ||
233+
currentSelection.model !== currentDefaultSelection.model
234+
) {
235+
return { ...currentSelection, config: cfg };
262236
}
263237

264238
const parentKey = normalizeOptionalString(currentEntry.parentSessionKey);

src/gateway/session-utils.ts

Lines changed: 61 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ import {
7676
normalizeMainKey,
7777
parseAgentSessionKey,
7878
} from "../routing/session-key.js";
79-
import { hasStaleAutoRuntimeAuthProfileSelection } from "../sessions/model-overrides.js";
79+
import {
80+
hasStaleAutoRuntimeAuthProfileSelection,
81+
type StaleAutoRuntimeAuthProfileEntry,
82+
} from "../sessions/model-overrides.js";
8083
import { isCronRunSessionKey } from "../sessions/session-key-utils.js";
8184
import {
8285
AVATAR_MAX_BYTES,
@@ -141,6 +144,26 @@ export type {
141144
SessionsPreviewResult,
142145
} from "./session-utils.types.js";
143146

147+
type SessionRuntimeModelEntry = Pick<
148+
SessionEntry,
149+
"model" | "modelProvider" | "modelOverride" | "providerOverride"
150+
>;
151+
152+
type SessionChannelModelSelectionEntry = Pick<
153+
SessionEntry,
154+
| "channel"
155+
| "origin"
156+
| "lastChannel"
157+
| "groupId"
158+
| "chatType"
159+
| "groupChannel"
160+
| "subject"
161+
| "parentSessionKey"
162+
>;
163+
164+
export type SessionExpectedModelEntry = StaleAutoRuntimeAuthProfileEntry &
165+
SessionChannelModelSelectionEntry;
166+
144167
const DERIVED_TITLE_MAX_LEN = 60;
145168

146169
function tryResolveExistingPath(value: string): string | null {
@@ -767,31 +790,14 @@ function resolveSessionSelectedModelRef(params: {
767790
return selected;
768791
}
769792

770-
function resolveStaleAutoRuntimeExpectedModelRef(params: {
793+
// Expected selection ignores transient runtime fields; callers use it to decide
794+
// whether auto-owned runtime/auth state should be repaired or inherited.
795+
export function resolveSessionExpectedSelectedModelRef(params: {
771796
cfg: OpenClawConfig;
772-
entry?:
773-
| SessionEntry
774-
| Pick<
775-
SessionEntry,
776-
| "authProfileOverride"
777-
| "authProfileOverrideCompactionCount"
778-
| "authProfileOverrideSource"
779-
| "providerOverride"
780-
| "modelOverride"
781-
| "modelProvider"
782-
| "model"
783-
| "channel"
784-
| "origin"
785-
| "lastChannel"
786-
| "groupId"
787-
| "chatType"
788-
| "groupChannel"
789-
| "subject"
790-
| "parentSessionKey"
791-
>;
797+
entry?: SessionExpectedModelEntry;
792798
agentId?: string;
793799
allowPluginNormalization?: boolean;
794-
}): ReturnType<typeof resolveSessionModelRef> | null {
800+
}): { provider: string; model: string } {
795801
const defaultSelection = params.agentId
796802
? resolveDefaultModelForAgent({
797803
cfg: params.cfg,
@@ -804,6 +810,23 @@ function resolveStaleAutoRuntimeExpectedModelRef(params: {
804810
defaultModel: DEFAULT_MODEL,
805811
allowPluginNormalization: params.allowPluginNormalization,
806812
});
813+
814+
const normalizedOverride = normalizeStoredOverrideModel({
815+
providerOverride: params.entry?.providerOverride,
816+
modelOverride: params.entry?.modelOverride,
817+
});
818+
if (normalizedOverride.modelOverride) {
819+
return resolveSessionModelRef(
820+
params.cfg,
821+
{
822+
providerOverride: normalizedOverride.providerOverride,
823+
modelOverride: normalizedOverride.modelOverride,
824+
},
825+
params.agentId,
826+
{ allowPluginNormalization: params.allowPluginNormalization },
827+
);
828+
}
829+
807830
const channelModelOverride = params.entry
808831
? resolveChannelModelOverride({
809832
cfg: params.cfg,
@@ -823,7 +846,16 @@ function resolveStaleAutoRuntimeExpectedModelRef(params: {
823846
allowPluginNormalization: params.allowPluginNormalization,
824847
})?.ref
825848
: null;
826-
const expected = channelSelection ?? defaultSelection;
849+
return channelSelection ?? defaultSelection;
850+
}
851+
852+
function resolveStaleAutoRuntimeExpectedModelRef(params: {
853+
cfg: OpenClawConfig;
854+
entry?: SessionExpectedModelEntry;
855+
agentId?: string;
856+
allowPluginNormalization?: boolean;
857+
}): ReturnType<typeof resolveSessionModelRef> | null {
858+
const expected = resolveSessionExpectedSelectedModelRef(params);
827859
return hasStaleAutoRuntimeAuthProfileSelection(params.entry, {
828860
...expected,
829861
config: params.cfg,
@@ -1674,9 +1706,7 @@ export function getSessionDefaults(
16741706

16751707
export function resolveSessionModelRef(
16761708
cfg: OpenClawConfig,
1677-
entry?:
1678-
| SessionEntry
1679-
| Pick<SessionEntry, "model" | "modelProvider" | "modelOverride" | "providerOverride">,
1709+
entry?: SessionRuntimeModelEntry,
16801710
agentId?: string,
16811711
options?: { allowPluginNormalization?: boolean },
16821712
): { provider: string; model: string } {
@@ -1726,29 +1756,12 @@ export function resolveSessionModelRef(
17261756

17271757
export function resolveSessionNextRunModelRef(
17281758
cfg: OpenClawConfig,
1729-
entry?:
1730-
| SessionEntry
1731-
| Pick<
1732-
SessionEntry,
1733-
| "authProfileOverride"
1734-
| "authProfileOverrideCompactionCount"
1735-
| "authProfileOverrideSource"
1736-
| "providerOverride"
1737-
| "modelOverride"
1738-
| "modelProvider"
1739-
| "model"
1740-
| "channel"
1741-
| "origin"
1742-
| "lastChannel"
1743-
| "groupId"
1744-
| "chatType"
1745-
| "groupChannel"
1746-
| "subject"
1747-
| "parentSessionKey"
1748-
>,
1759+
entry?: SessionExpectedModelEntry,
17491760
agentId?: string,
17501761
options?: { allowPluginNormalization?: boolean },
17511762
): { provider: string; model: string } {
1763+
// Dispatch/preflight wants the model the next run should use; generic row and
1764+
// history callers may still need the stored runtime model exactly as recorded.
17521765
const staleRuntimeExpected = resolveStaleAutoRuntimeExpectedModelRef({
17531766
cfg,
17541767
entry,
@@ -1838,18 +1851,7 @@ export async function resolveGatewayModelSupportsImages(params: {
18381851

18391852
export function resolveSessionModelIdentityRef(
18401853
cfg: OpenClawConfig,
1841-
entry?:
1842-
| SessionEntry
1843-
| Pick<
1844-
SessionEntry,
1845-
| "model"
1846-
| "modelProvider"
1847-
| "modelOverride"
1848-
| "providerOverride"
1849-
| "authProfileOverride"
1850-
| "authProfileOverrideSource"
1851-
| "authProfileOverrideCompactionCount"
1852-
>,
1854+
entry?: SessionExpectedModelEntry,
18531855
agentId?: string,
18541856
fallbackModelRef?: string,
18551857
options?: { allowPluginNormalization?: boolean },

src/sessions/model-overrides.ts

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,44 @@ export type ModelOverrideSelection = {
99
isDefault?: boolean;
1010
};
1111

12+
export type StaleAutoRuntimeAuthProfileEntry = Pick<
13+
SessionEntry,
14+
| "authProfileOverride"
15+
| "authProfileOverrideCompactionCount"
16+
| "authProfileOverrideSource"
17+
| "providerOverride"
18+
| "modelOverride"
19+
| "modelProvider"
20+
| "model"
21+
>;
22+
23+
type ExpectedModelSelection = {
24+
provider: string;
25+
model: string;
26+
config?: OpenClawConfig;
27+
};
28+
29+
const STALE_AUTO_RUNTIME_AUTH_PROFILE_FIELDS = [
30+
"modelProvider",
31+
"model",
32+
"contextTokens",
33+
"contextBudgetStatus",
34+
"authProfileOverride",
35+
"authProfileOverrideSource",
36+
"authProfileOverrideCompactionCount",
37+
"fallbackNoticeSelectedModel",
38+
"fallbackNoticeActiveModel",
39+
"fallbackNoticeReason",
40+
] as const satisfies readonly (keyof SessionEntry)[];
41+
1242
export function hasStaleAutoRuntimeAuthProfileSelection(
13-
entry:
14-
| Pick<
15-
SessionEntry,
16-
| "authProfileOverride"
17-
| "authProfileOverrideCompactionCount"
18-
| "authProfileOverrideSource"
19-
| "providerOverride"
20-
| "modelOverride"
21-
| "modelProvider"
22-
| "model"
23-
>
24-
| undefined,
25-
expectedSelection: { provider: string; model: string; config?: OpenClawConfig },
43+
entry: StaleAutoRuntimeAuthProfileEntry | undefined,
44+
expectedSelection: ExpectedModelSelection,
2645
): boolean {
2746
const hasAutoAuthProfileSelection =
2847
entry?.authProfileOverrideSource === "auto" ||
48+
// Older rows used the compaction counter as the only durable marker that
49+
// an auth-profile override came from automatic fallback/rotation.
2950
(entry?.authProfileOverrideSource === undefined &&
3051
typeof entry?.authProfileOverrideCompactionCount === "number");
3152
if (
@@ -59,7 +80,7 @@ export function hasStaleAutoRuntimeAuthProfileSelection(
5980

6081
export function clearStaleAutoRuntimeAuthProfileSelection(
6182
entry: SessionEntry,
62-
expectedSelection: { provider: string; model: string; config?: OpenClawConfig },
83+
expectedSelection: ExpectedModelSelection,
6384
): { updated: boolean } {
6485
if (!hasStaleAutoRuntimeAuthProfileSelection(entry, expectedSelection)) {
6586
return { updated: false };
@@ -73,16 +94,9 @@ export function clearStaleAutoRuntimeAuthProfileSelection(
7394
}
7495
};
7596

76-
clear("modelProvider");
77-
clear("model");
78-
clear("contextTokens");
79-
clear("contextBudgetStatus");
80-
clear("authProfileOverride");
81-
clear("authProfileOverrideSource");
82-
clear("authProfileOverrideCompactionCount");
83-
clear("fallbackNoticeSelectedModel");
84-
clear("fallbackNoticeActiveModel");
85-
clear("fallbackNoticeReason");
97+
for (const key of STALE_AUTO_RUNTIME_AUTH_PROFILE_FIELDS) {
98+
clear(key);
99+
}
86100

87101
if (updated) {
88102
entry.updatedAt = Date.now();

0 commit comments

Comments
 (0)