fix: allow replacing unavailable conversation models
Persist the selected model before opening dormant sessions, and reconfigure crashed workers on the same account. Preserve session history and report model removal through the model-unavailable contract instead of a generic runtime failure.
This commit is contained in:
@@ -34,6 +34,8 @@ import type {
|
||||
PromptConversationInput,
|
||||
} from '../../electron/coding-runtime/contracts';
|
||||
import { archivePiConversationSession } from '../../electron/coding-runtime/pi/resource-loader';
|
||||
import { PiProviderConfigError } from '../../electron/coding-runtime/pi/provider-config';
|
||||
import type { ProductModelRef } from '../../shared/coding-conversation-contracts';
|
||||
|
||||
const roots: string[] = [];
|
||||
const servers: Server[] = [];
|
||||
@@ -273,6 +275,44 @@ describe('PI-100 coding core Host contract', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('switches a removed saved model before preparing the Conversation', async () => {
|
||||
class RemovedModelRuntime extends InMemoryConversationRuntime {
|
||||
override async validateModel(model: ProductModelRef) {
|
||||
if (model.modelId === MODEL.modelId) {
|
||||
throw new PiProviderConfigError('MODEL_UNAVAILABLE', 'Model is no longer available');
|
||||
}
|
||||
return await super.validateModel(model);
|
||||
}
|
||||
|
||||
override async prepare(input: PrepareConversationInput) {
|
||||
if (input.model.model) await this.validateModel(input.model.model);
|
||||
return await super.prepare(input);
|
||||
}
|
||||
}
|
||||
const result = await setup(new RemovedModelRuntime());
|
||||
const conversation = await createConversation(result.conversations);
|
||||
const nextModel = { ...MODEL, modelId: 'available-model' };
|
||||
const prepare = vi.spyOn(result.runtime, 'prepare');
|
||||
|
||||
await expect(result.conversations.getSnapshot(conversation.id)).rejects.toMatchObject({
|
||||
code: 'CODING_MODEL_UNAVAILABLE', status: 409,
|
||||
});
|
||||
await expect(result.conversations.setModel(conversation.id, MODEL)).rejects.toMatchObject({
|
||||
code: 'CODING_MODEL_UNAVAILABLE', status: 409,
|
||||
});
|
||||
expect((await result.conversations.getConversation(conversation.id)).model).toEqual(MODEL);
|
||||
await expect(result.conversations.setModel(conversation.id, nextModel)).resolves.toMatchObject({
|
||||
model: nextModel,
|
||||
modelResolution: 'resolved',
|
||||
});
|
||||
expect(prepare).toHaveBeenLastCalledWith(expect.objectContaining({
|
||||
model: { model: nextModel, modelResolution: 'resolved' },
|
||||
}));
|
||||
expect(await result.projects.conversationStore(result.root).get(conversation.id)).toMatchObject({
|
||||
id: conversation.id, model: nextModel, modelResolution: 'resolved',
|
||||
});
|
||||
});
|
||||
|
||||
it('switches a resolved active Conversation model through the target runtime without disposing it', async () => {
|
||||
const result = await setup();
|
||||
const conversation = await createConversation(result.conversations);
|
||||
|
||||
Reference in New Issue
Block a user