fix(chat): handle unavailable persisted models

This commit is contained in:
Bohdan Triapitsyn
2026-08-22 14:53:44 +03:00
parent 09f75b75e1
commit eed317f18d
2 changed files with 48 additions and 12 deletions
@@ -915,25 +915,29 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
? useSelectionStore.getState().getSessionAgentSelection(currentSessionId)
: null;
if (savedAgentName) {
if (currentAgentName !== savedAgentName) {
setAgent(savedAgentName);
}
const savedModel = getAgentModelForSession(currentSessionId, savedAgentName);
if (savedModel) {
const result = tryApplyModelSelection(savedModel.providerId, savedModel.modelId, savedAgentName);
if (result === 'applied') {
if (currentAgentName !== savedAgentName) {
setAgent(savedAgentName);
}
return 'resolved';
}
if (result === 'provider-missing') {
return 'waiting';
}
} else if (currentAgentName !== savedAgentName) {
setAgent(savedAgentName);
}
}
if (savedSessionModel) {
const result = tryApplyModelSelection(savedSessionModel.providerId, savedSessionModel.modelId, savedAgentName || currentAgentName || undefined);
if (result === 'applied') {
if (savedAgentName && currentAgentName !== savedAgentName) {
setAgent(savedAgentName);
}
return 'resolved';
}
if (result === 'provider-missing') {
@@ -947,16 +951,15 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
continue;
}
if (currentAgentName !== agent.name) {
setAgent(agent.name);
}
const existingSelection = useSelectionStore.getState().getSessionAgentSelection(currentSessionId) || stickySessionAgentRef.current;
if (!existingSelection) {
saveSessionAgentSelection(currentSessionId, agent.name);
}
const result = tryApplyModelSelection(selection.providerId, selection.modelId, agent.name);
if (result === 'applied') {
if (currentAgentName !== agent.name) {
setAgent(agent.name);
}
const existingSelection = useSelectionStore.getState().getSessionAgentSelection(currentSessionId) || stickySessionAgentRef.current;
if (!existingSelection) {
saveSessionAgentSelection(currentSessionId, agent.name);
}
return 'resolved';
}
if (result === 'provider-missing') {
@@ -0,0 +1,33 @@
/**
* Regression coverage for https://github.com/openchamber/openchamber/issues/3036.
*
* Restoring persisted agent/model pairs used to switch agents before checking
* whether each model still existed. Several stale pairs could therefore keep
* changing the active agent on every effect pass until React hit its nested
* update limit. The API error belongs in the assistant message; an invalid
* persisted pair must not mutate the current selection while it is rendered.
*/
import { describe, expect, test } from 'bun:test';
import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const modelControlsSource = readFileSync(join(__dirname, '..', 'ModelControls.tsx'), 'utf-8');
describe('issue #3036 stale persisted models', () => {
test('changes the agent only after its persisted model is accepted', () => {
const candidateLoop = modelControlsSource.slice(
modelControlsSource.indexOf('for (const agent of agents)'),
modelControlsSource.indexOf("return 'continue';"),
);
const applyIndex = candidateLoop.indexOf('const result = tryApplyModelSelection');
const acceptedIndex = candidateLoop.indexOf("if (result === 'applied')");
const setAgentIndex = candidateLoop.indexOf('setAgent(agent.name)');
expect(applyIndex).toBeGreaterThanOrEqual(0);
expect(acceptedIndex).toBeGreaterThan(applyIndex);
expect(setAgentIndex).toBeGreaterThan(acceptedIndex);
});
});