diff --git a/packages/api/session-controller/README.i18n.yaml b/packages/api/session-controller/README.i18n.yaml index bfca49ba57..67261d6093 100644 --- a/packages/api/session-controller/README.i18n.yaml +++ b/packages/api/session-controller/README.i18n.yaml @@ -21,8 +21,8 @@ en: 41bdda1ac32df558 zh: aa5d0e325924066b /session-controller/session-media-references: - en: 9449c5c3a53031c6 - zh: 68d8ad0d3632983a + en: 49a3d0e8e92017d3 + zh: 91302aa3dd6e9378 /session-controller/configuration: en: 5ae2b340c630897f zh: 1ffbd61ecf302ec2 diff --git a/packages/api/session-controller/README.md b/packages/api/session-controller/README.md index 1ac2148792..092727dd59 100644 --- a/packages/api/session-controller/README.md +++ b/packages/api/session-controller/README.md @@ -90,7 +90,7 @@ References keep local Session data, scoped Contexts, and history streams alive, GUI model selection requires the exact provider/model pair in the available catalog; unavailable choices reject with `session/model-unavailable`. Prompt admission retains the saved route without a catalog availability gate, so request execution reports missing credentials or unavailable models. `initializeDefaultModel()` saves the first available account model after login when no other provider has a configured API key; credential checks use configured references independently of model availability. A provider with no available models rejects initialization with `session/provider-models-unavailable`. Availability never substitutes another model or rewrites a Session selection. -A successful `selectModel` response acknowledges the recorded Session selection without waiting for the default profile setting to save. The loop registers its projection; a change applies to the next step while a prepared step retains its captured choice. Default saves run in the background in submission order; a failure logs a warning and leaves the Session selection intact. A Session with no recorded selection or request reads the last successfully saved default through live `AgentOptions` accessors. An explicit choice is recorded even when it equals that default. +A successful `selectModel` response acknowledges the recorded Session selection without waiting for the default profile setting to save. The loop registers its projection; a change applies to the next step while a prepared step retains its captured choice. Default saves run in the background in submission order; a failure logs a warning and leaves the Session selection intact. A Session with no recorded selection or request reads the last successfully saved default through live `AgentOptions` accessors. An explicit choice is recorded even when it equals that default. Omitting reasoning effort clears the explicit effort and lets each request use the adapter default. ----- diff --git a/packages/api/session-controller/README.zh.md b/packages/api/session-controller/README.zh.md index 1a89a27cb5..83da9b21bf 100644 --- a/packages/api/session-controller/README.zh.md +++ b/packages/api/session-controller/README.zh.md @@ -90,7 +90,7 @@ Client 的首次 `follow`、重连首屏与 `loadOlder()` 至少请求 50 条以 GUI 模型选择要求确切提供方/模型对出现在可用目录中;不可用的选择以 `session/model-unavailable` 拒绝。提示词准入保留已保存的路由,不按目录可用性阻断发送,由请求执行报告凭据缺失或模型不可用。`initializeDefaultModel()` 在账号登录后、其他提供方均未配置 API key 时,将第一个可用账号模型保存为默认模型;凭据检查使用已配置的引用,不依赖模型是否可用。提供方没有可用模型时,初始化以 `session/provider-models-unavailable` 拒绝。可用性变化不会替换模型或改写会话选择。 -`selectModel` 成功返回表示会话模型选择已记录,不等待默认 profile 设置保存。循环注册该投影;变更从下一个步骤开始生效,已准备的步骤保留捕获的选择。默认设置在后台按提交顺序保存;保存失败会记录警告,并保留会话选择。尚未记录选择或请求的会话通过 `AgentOptions` 的动态访问器读取最近一次成功保存的默认值。显式选择即使与默认值相同也会记录。 +`selectModel` 成功返回表示会话模型选择已记录,不等待默认 profile 设置保存。循环注册该投影;变更从下一个步骤开始生效,已准备的步骤保留捕获的选择。默认设置在后台按提交顺序保存;保存失败会记录警告,并保留会话选择。尚未记录选择或请求的会话通过 `AgentOptions` 的动态访问器读取最近一次成功保存的默认值。显式选择即使与默认值相同也会记录。省略推理强度会清除显式强度,让每次请求使用适配器默认值。 ----- diff --git a/packages/api/session-controller/src/commands.ts b/packages/api/session-controller/src/commands.ts index 8f55c2f609..59e32cd431 100644 --- a/packages/api/session-controller/src/commands.ts +++ b/packages/api/session-controller/src/commands.ts @@ -159,20 +159,14 @@ export class SessionCommandController { return this.agents.serializeImageAdmission(agent, async () => { try { await this.requireModel(request) - const resolved = await this.ctx.llm.resolveCallConfig({ + const selected: AgentModelSelection = { provider: request.provider, model: request.model, ...(request.reasoningEffort === undefined ? {} : { reasoningEffort: ReasoningEffortId(request.reasoningEffort) }), - }) - const selected: AgentModelSelection = { - provider: resolved.provider, - model: resolved.model, - ...(resolved.reasoningEffort === undefined - ? {} - : { reasoningEffort: resolved.reasoningEffort }), } + await this.ctx.llm.resolveCallConfig(selected) agent.model.select(selected) void this.ctx.agentDefaultModel.saveSelection(selected).catch((error: unknown) => { this.ctx.logger.warn( diff --git a/packages/api/session-controller/tests/session-models.host.spec.ts b/packages/api/session-controller/tests/session-models.host.spec.ts index 017e3a5d55..cdb6f9e1d9 100644 --- a/packages/api/session-controller/tests/session-models.host.spec.ts +++ b/packages/api/session-controller/tests/session-models.host.spec.ts @@ -706,9 +706,10 @@ describe('Web session model selection', () => { const stillAccepted = expectValue(await remote.selectModel(request({ sessionId, provider: 'deepseek-official', model: 'deepseek-chat', }))) - expect(stillAccepted.selected).toEqual({ provider: 'deepseek-official', model: 'deepseek-chat', reasoningEffort: 'high' }) + expect(stillAccepted.selected).toEqual({ provider: 'deepseek-official', model: 'deepseek-chat' }) expect(currentSelection(ctx, sessionId)) - .toEqual({ provider: 'deepseek-official', model: 'deepseek-chat', reasoningEffort: 'high' }) + .toEqual({ provider: 'deepseek-official', model: 'deepseek-chat' }) + expect(saved[1]).toEqual({ provider: 'deepseek-official', model: 'deepseek-chat' }) await expect.poll(() => warn.mock.calls).toContainEqual([ 'session-controller: model selection changed for the Session but the default was not saved: Error: read-only document', ])