fix(chat): handle unavailable persisted models
This commit is contained in:
@@ -915,25 +915,29 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
|
|||||||
? useSelectionStore.getState().getSessionAgentSelection(currentSessionId)
|
? useSelectionStore.getState().getSessionAgentSelection(currentSessionId)
|
||||||
: null;
|
: null;
|
||||||
if (savedAgentName) {
|
if (savedAgentName) {
|
||||||
if (currentAgentName !== savedAgentName) {
|
|
||||||
setAgent(savedAgentName);
|
|
||||||
}
|
|
||||||
|
|
||||||
const savedModel = getAgentModelForSession(currentSessionId, savedAgentName);
|
const savedModel = getAgentModelForSession(currentSessionId, savedAgentName);
|
||||||
if (savedModel) {
|
if (savedModel) {
|
||||||
const result = tryApplyModelSelection(savedModel.providerId, savedModel.modelId, savedAgentName);
|
const result = tryApplyModelSelection(savedModel.providerId, savedModel.modelId, savedAgentName);
|
||||||
if (result === 'applied') {
|
if (result === 'applied') {
|
||||||
|
if (currentAgentName !== savedAgentName) {
|
||||||
|
setAgent(savedAgentName);
|
||||||
|
}
|
||||||
return 'resolved';
|
return 'resolved';
|
||||||
}
|
}
|
||||||
if (result === 'provider-missing') {
|
if (result === 'provider-missing') {
|
||||||
return 'waiting';
|
return 'waiting';
|
||||||
}
|
}
|
||||||
|
} else if (currentAgentName !== savedAgentName) {
|
||||||
|
setAgent(savedAgentName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (savedSessionModel) {
|
if (savedSessionModel) {
|
||||||
const result = tryApplyModelSelection(savedSessionModel.providerId, savedSessionModel.modelId, savedAgentName || currentAgentName || undefined);
|
const result = tryApplyModelSelection(savedSessionModel.providerId, savedSessionModel.modelId, savedAgentName || currentAgentName || undefined);
|
||||||
if (result === 'applied') {
|
if (result === 'applied') {
|
||||||
|
if (savedAgentName && currentAgentName !== savedAgentName) {
|
||||||
|
setAgent(savedAgentName);
|
||||||
|
}
|
||||||
return 'resolved';
|
return 'resolved';
|
||||||
}
|
}
|
||||||
if (result === 'provider-missing') {
|
if (result === 'provider-missing') {
|
||||||
@@ -947,16 +951,15 @@ export const ModelControls: React.FC<ModelControlsProps> = ({
|
|||||||
continue;
|
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);
|
const result = tryApplyModelSelection(selection.providerId, selection.modelId, agent.name);
|
||||||
if (result === 'applied') {
|
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';
|
return 'resolved';
|
||||||
}
|
}
|
||||||
if (result === 'provider-missing') {
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user