mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-15 18:59:33 +01:00
fix: refuse an unlaunchable model id before quick-start asks to evict a model (#752)
The dedicated route got this order at merge and quick-start kept the old one, so a user could agree to a swap only to get the 400. It also runs on the pure build now, so a refused id writes no config dir to clean up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e68afdd2ac
commit
345c901e81
2 files changed
+37
-15
No files matched your search
@@ -4501,6 +4501,23 @@ export function registerSessionRoutes(
|
||||
const cmContextLength = resolveCustomModelContextLength(cmEndpoint, customModel.modelId);
|
||||
const cmSlotCount = cmEndpoint.modelSlotCounts?.[customModel.modelId];
|
||||
|
||||
// See the dedicated route: refused on the pure build, before either confirmation, so
|
||||
// nobody agrees to evict a model for an id that could never launch, and before anything
|
||||
// is written.
|
||||
const cmModelSpec = cmEntry.launch.params.model;
|
||||
const cmInjection = buildCustomModelInjection(cmEntry, cmEndpoint, customModel.modelId, cmContextLength);
|
||||
const cmForcedLaunchModel = cmInjection.kind === 'unsupported' ? undefined : cmInjection.launchModel;
|
||||
if (
|
||||
cmForcedLaunchModel !== undefined &&
|
||||
cmModelSpec?.type === 'token' &&
|
||||
!matchesPattern(cmModelSpec.pattern, cmForcedLaunchModel)
|
||||
) {
|
||||
return createErrorResponse(
|
||||
ApiErrorCode.INVALID_INPUT,
|
||||
`Model id ${JSON.stringify(customModel.modelId)} cannot be passed to ${mode} on its command line`
|
||||
);
|
||||
}
|
||||
|
||||
// See the dedicated route's own comment for the full reasoning: some CLIs' own fixed
|
||||
// overhead can exceed a small enough real context on the very first message,
|
||||
// regardless of contextLengthVar. Warn before creating a session that's certain to
|
||||
@@ -4571,19 +4588,6 @@ export function registerSessionRoutes(
|
||||
if (!cmApplied) {
|
||||
return createErrorResponse(ApiErrorCode.OPERATION_FAILED, `${mode} has no known custom-model mechanism`);
|
||||
}
|
||||
const cmModelSpec = cmEntry.launch.params.model;
|
||||
if (
|
||||
cmApplied.launchModel !== undefined &&
|
||||
cmModelSpec?.type === 'token' &&
|
||||
!matchesPattern(cmModelSpec.pattern, cmApplied.launchModel)
|
||||
) {
|
||||
removeConfigDir(cmApplied.configDir);
|
||||
return createErrorResponse(
|
||||
ApiErrorCode.INVALID_INPUT,
|
||||
`Model id ${JSON.stringify(customModel.modelId)} cannot be passed to ${mode} on its command line`
|
||||
);
|
||||
}
|
||||
|
||||
qsCustomModelEnvOverrides = { ...qsGatedEnvOverrides, ...cmApplied.envOverrides };
|
||||
qsCustomModelAppliedEnvOverrides = cmApplied.envOverrides;
|
||||
qsCustomModelLaunchModel = cmApplied.launchModel;
|
||||
|
||||
@@ -21,7 +21,7 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import Fastify, { type FastifyInstance } from 'fastify';
|
||||
import fastifyCookie from '@fastify/cookie';
|
||||
import { rm, readFile } from 'node:fs/promises';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { existsSync, readdirSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { createMockRouteContext, safeRmHomeTree, type MockRouteContext } from '../mocks/index.js';
|
||||
import { installRouteErrorHandler } from '../../src/web/route-error-handler.js';
|
||||
@@ -195,7 +195,9 @@ describe('POST /api/quick-start: customModel (one-shot custom-model launch)', ()
|
||||
expect(res.json().errorCode).toBe('OPERATION_FAILED');
|
||||
});
|
||||
|
||||
it('refuses a model id the CLI cannot carry on its command line, cleaning up any written config dir', async () => {
|
||||
it('refuses a model id the CLI cannot carry on its command line, before writing anything', async () => {
|
||||
const dirs = join(getDataDir(), 'custom-model-configs');
|
||||
const before = existsSync(dirs) ? readdirSync(dirs).length : 0;
|
||||
const res = await quickStart({
|
||||
caseName: 'cm-badmodel',
|
||||
mode: 'pi',
|
||||
@@ -203,6 +205,7 @@ describe('POST /api/quick-start: customModel (one-shot custom-model launch)', ()
|
||||
});
|
||||
expect(res.json().success).toBe(false);
|
||||
expect(res.json().errorCode).toBe('INVALID_INPUT');
|
||||
expect(existsSync(dirs) ? readdirSync(dirs).length : 0).toBe(before);
|
||||
});
|
||||
|
||||
it('refuses customModel for a remote case', async () => {
|
||||
@@ -229,6 +232,21 @@ describe('POST /api/quick-start: customModel (one-shot custom-model launch)', ()
|
||||
});
|
||||
}
|
||||
|
||||
it('refuses a model id the CLI cannot launch before asking anyone to evict a model', async () => {
|
||||
const other = ctx.sessions.get('test-session-1')!;
|
||||
(other as unknown as { customModel: unknown }).customModel = { endpointId: 'ep1', modelId: 'llama3' };
|
||||
mockRunning([{ model: 'llama3', state: 'ready' }]);
|
||||
|
||||
const res = await quickStart({
|
||||
caseName: 'cm-badmodel-swap',
|
||||
mode: 'codex',
|
||||
customModel: { endpointId: 'ep1', modelId: 'org/model@q4_k_m' },
|
||||
});
|
||||
|
||||
expect(res.json().requiresConfirmation).toBeUndefined();
|
||||
expect(res.json().errorCode).toBe('INVALID_INPUT');
|
||||
});
|
||||
|
||||
it('asks for confirmation instead of launching when another live session is using the currently loaded model', async () => {
|
||||
const other = ctx.sessions.get('test-session-1')!;
|
||||
(other as unknown as { customModel: unknown }).customModel = { endpointId: 'ep1', modelId: 'llama3' };
|
||||
|
||||
Reference in new issue
Block a user