|
7 | 7 | import type { OpenClawConfig } from "openclaw/plugin-sdk/core"; |
8 | 8 | import { parseStrictInteger } from "openclaw/plugin-sdk/number-runtime"; |
9 | 9 | import { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared"; |
10 | | -import { loadSessionStore, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime"; |
| 10 | +import { parseThreadSessionSuffix } from "openclaw/plugin-sdk/routing"; |
| 11 | +import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime"; |
11 | 12 | import { |
12 | 13 | normalizeOptionalString, |
13 | 14 | normalizeStringifiedOptionalString, |
@@ -39,6 +40,8 @@ type MattermostModelPickerRenderedView = { |
39 | 40 | buttons: MattermostInteractiveButtonInput[][]; |
40 | 41 | }; |
41 | 42 |
|
| 43 | +type MattermostModelPickerSessionEntry = ReturnType<typeof getSessionEntry>; |
| 44 | + |
42 | 45 | function splitModelRef(modelRef?: string | null): { provider: string; model: string } | null { |
43 | 46 | const trimmed = normalizeOptionalString(modelRef); |
44 | 47 | const match = trimmed?.match(/^([^/]+)\/(.+)$/u); |
@@ -77,6 +80,24 @@ function normalizePage(value: number | undefined): number { |
77 | 80 | return Math.max(1, Math.floor(value as number)); |
78 | 81 | } |
79 | 82 |
|
| 83 | +function resolveMattermostModelPickerParentSessionKey(params: { |
| 84 | + sessionEntry: MattermostModelPickerSessionEntry; |
| 85 | + sessionKey: string; |
| 86 | +}): string | undefined { |
| 87 | + // Preserve inherited model overrides without exposing whole-store reads to the UI path. |
| 88 | + const persistedParent = |
| 89 | + typeof params.sessionEntry?.parentSessionKey === "string" |
| 90 | + ? params.sessionEntry.parentSessionKey.trim() |
| 91 | + : ""; |
| 92 | + if (persistedParent && persistedParent !== params.sessionKey) { |
| 93 | + return persistedParent; |
| 94 | + } |
| 95 | + const parsed = parseThreadSessionSuffix(params.sessionKey); |
| 96 | + return parsed.threadId && parsed.baseSessionKey && parsed.baseSessionKey !== params.sessionKey |
| 97 | + ? parsed.baseSessionKey |
| 98 | + : undefined; |
| 99 | +} |
| 100 | + |
80 | 101 | function paginateItems<T>(items: T[], page?: number, pageSize = MODELS_PAGE_SIZE) { |
81 | 102 | const totalPages = Math.max(1, Math.ceil(items.length / pageSize)); |
82 | 103 | const safePage = Math.max(1, Math.min(normalizePage(page), totalPages)); |
@@ -244,14 +265,31 @@ export function resolveMattermostModelPickerCurrentModel(params: { |
244 | 265 | const storePath = resolveStorePath(params.cfg.session?.store, { |
245 | 266 | agentId: params.route.agentId, |
246 | 267 | }); |
247 | | - const sessionStore = params.skipCache |
248 | | - ? loadSessionStore(storePath, { skipCache: true }) |
249 | | - : loadSessionStore(storePath); |
250 | | - const sessionEntry = sessionStore[params.route.sessionKey]; |
| 268 | + const readOptions = { |
| 269 | + storePath, |
| 270 | + ...(params.skipCache ? { readConsistency: "latest" as const } : {}), |
| 271 | + }; |
| 272 | + const sessionEntry = getSessionEntry({ |
| 273 | + ...readOptions, |
| 274 | + sessionKey: params.route.sessionKey, |
| 275 | + }); |
| 276 | + const parentSessionKey = resolveMattermostModelPickerParentSessionKey({ |
| 277 | + sessionEntry, |
| 278 | + sessionKey: params.route.sessionKey, |
| 279 | + }); |
| 280 | + const parentEntry = parentSessionKey |
| 281 | + ? getSessionEntry({ |
| 282 | + ...readOptions, |
| 283 | + sessionKey: parentSessionKey, |
| 284 | + }) |
| 285 | + : undefined; |
251 | 286 | const override = resolveStoredModelOverride({ |
252 | 287 | sessionEntry, |
253 | | - sessionStore, |
| 288 | + ...(parentEntry && parentSessionKey |
| 289 | + ? { sessionStore: { [parentSessionKey]: parentEntry } } |
| 290 | + : {}), |
254 | 291 | sessionKey: params.route.sessionKey, |
| 292 | + parentSessionKey, |
255 | 293 | defaultProvider: params.data.resolvedDefault.provider, |
256 | 294 | }); |
257 | 295 | if (!override?.model) { |
|
0 commit comments