fix(cli-installer): serialize provider status checks by default
This commit is contained in:
parent
64c61ab7e4
commit
4548a01c97
2 changed files with 72 additions and 6 deletions
|
|
@ -5,7 +5,7 @@ import {
|
|||
CLI_INSTALLER_VERIFY_PROVIDER_MODELS,
|
||||
} from '@preload/constants/ipcChannels';
|
||||
import { createDefaultCliExtensionCapabilities } from '@shared/utils/providerExtensionCapabilities';
|
||||
import { describe, expect, test, vi } from 'vitest';
|
||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import { initializeCliInstallerHandlers, registerCliInstallerHandlers } from './cliInstaller';
|
||||
|
||||
|
|
@ -35,6 +35,12 @@ interface Deferred<T> {
|
|||
|
||||
type IpcHandler = (event: IpcMainInvokeEvent, ...args: unknown[]) => unknown;
|
||||
|
||||
const PARALLEL_PROVIDER_STATUS_ENV = 'CLAUDE_TEAM_PARALLEL_PROVIDER_STATUS';
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
function createDeferred<T>(): Deferred<T> {
|
||||
let resolve!: (value: T) => void;
|
||||
let reject!: (error: unknown) => void;
|
||||
|
|
@ -137,7 +143,48 @@ function setupHandlers(service: CliInstallerService): ReturnType<typeof createIp
|
|||
}
|
||||
|
||||
describe('cliInstaller IPC provider runtime scheduling', () => {
|
||||
test('runs different provider status requests concurrently up to the provider limit', async () => {
|
||||
test('runs provider status requests sequentially by default', async () => {
|
||||
const started: CliProviderId[] = [];
|
||||
const deferredByProvider = new Map<CliProviderId, Deferred<CliProviderStatus | null>>();
|
||||
const service = createInstallerService({
|
||||
getProviderStatus: vi.fn((providerId: CliProviderId) => {
|
||||
started.push(providerId);
|
||||
const deferred = createDeferred<CliProviderStatus | null>();
|
||||
deferredByProvider.set(providerId, deferred);
|
||||
return deferred.promise;
|
||||
}),
|
||||
});
|
||||
const { invoke } = setupHandlers(service);
|
||||
|
||||
const requests = (['anthropic', 'codex', 'opencode', 'gemini'] as CliProviderId[]).map(
|
||||
(providerId) =>
|
||||
invoke<IpcResult<CliProviderStatus | null>>(CLI_INSTALLER_GET_PROVIDER_STATUS, providerId)
|
||||
);
|
||||
|
||||
await flushMicrotasks();
|
||||
expect(started).toEqual(['anthropic']);
|
||||
|
||||
deferredByProvider.get('anthropic')?.resolve(createProviderStatus('anthropic'));
|
||||
await flushMicrotasks();
|
||||
expect(started).toEqual(['anthropic', 'codex']);
|
||||
|
||||
deferredByProvider.get('codex')?.resolve(createProviderStatus('codex'));
|
||||
await flushMicrotasks();
|
||||
expect(started).toEqual(['anthropic', 'codex', 'opencode']);
|
||||
|
||||
deferredByProvider.get('opencode')?.resolve(createProviderStatus('opencode'));
|
||||
await flushMicrotasks();
|
||||
expect(started).toEqual(['anthropic', 'codex', 'opencode', 'gemini']);
|
||||
|
||||
deferredByProvider.get('gemini')?.resolve(createProviderStatus('gemini'));
|
||||
|
||||
const results = await Promise.all(requests);
|
||||
expect(results.every((result) => result.success)).toBe(true);
|
||||
});
|
||||
|
||||
test('runs different provider status requests concurrently when the parallel flag is enabled', async () => {
|
||||
vi.stubEnv(PARALLEL_PROVIDER_STATUS_ENV, '1');
|
||||
|
||||
const started: CliProviderId[] = [];
|
||||
const deferredByProvider = new Map<CliProviderId, Deferred<CliProviderStatus | null>>();
|
||||
const service = createInstallerService({
|
||||
|
|
@ -258,12 +305,18 @@ describe('cliInstaller IPC provider runtime scheduling', () => {
|
|||
);
|
||||
|
||||
await flushMicrotasks();
|
||||
expect(started).toEqual(['anthropic', 'codex', 'opencode']);
|
||||
expect(started).toEqual(['anthropic']);
|
||||
|
||||
initializeCliInstallerHandlers(replacementService);
|
||||
|
||||
deferredByProvider.get('anthropic')?.resolve(createProviderStatus('anthropic'));
|
||||
await flushMicrotasks();
|
||||
expect(started).toEqual(['anthropic', 'codex']);
|
||||
|
||||
deferredByProvider.get('codex')?.resolve(createProviderStatus('codex'));
|
||||
await flushMicrotasks();
|
||||
expect(started).toEqual(['anthropic', 'codex', 'opencode']);
|
||||
|
||||
deferredByProvider.get('opencode')?.resolve(createProviderStatus('opencode'));
|
||||
await flushMicrotasks();
|
||||
|
||||
|
|
@ -294,14 +347,20 @@ describe('cliInstaller IPC provider runtime scheduling', () => {
|
|||
);
|
||||
|
||||
await flushMicrotasks();
|
||||
expect(started).toEqual(['anthropic', 'codex', 'opencode']);
|
||||
expect(started).toEqual(['anthropic']);
|
||||
|
||||
deferredByProvider.get('anthropic')?.reject(new Error('provider failed'));
|
||||
await flushMicrotasks();
|
||||
expect(started).toEqual(['anthropic', 'codex', 'opencode', 'gemini']);
|
||||
expect(started).toEqual(['anthropic', 'codex']);
|
||||
|
||||
deferredByProvider.get('codex')?.resolve(createProviderStatus('codex'));
|
||||
await flushMicrotasks();
|
||||
expect(started).toEqual(['anthropic', 'codex', 'opencode']);
|
||||
|
||||
deferredByProvider.get('opencode')?.resolve(createProviderStatus('opencode'));
|
||||
await flushMicrotasks();
|
||||
expect(started).toEqual(['anthropic', 'codex', 'opencode', 'gemini']);
|
||||
|
||||
deferredByProvider.get('gemini')?.resolve(createProviderStatus('gemini'));
|
||||
|
||||
const results = await Promise.all(requests);
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ const cachedStatus = new Map<
|
|||
let statusCacheGeneration = 0;
|
||||
const STATUS_CACHE_TTL_MS = 5_000;
|
||||
const MAX_PARALLEL_PROVIDER_RUNTIME_REQUESTS = 3;
|
||||
const PARALLEL_PROVIDER_STATUS_ENV = 'CLAUDE_TEAM_PARALLEL_PROVIDER_STATUS';
|
||||
const FRONTEND_MULTIMODEL_PROVIDER_IDS = new Set<CliProviderId>(['anthropic', 'codex', 'opencode']);
|
||||
|
||||
function isFrontendMultimodelProviderId(providerId: CliProviderId): boolean {
|
||||
|
|
@ -120,8 +121,14 @@ function resetProviderRuntimeRequestLimiter(): void {
|
|||
}
|
||||
}
|
||||
|
||||
function getProviderRuntimeRequestLimit(): number {
|
||||
return process.env[PARALLEL_PROVIDER_STATUS_ENV] === '1'
|
||||
? MAX_PARALLEL_PROVIDER_RUNTIME_REQUESTS
|
||||
: 1;
|
||||
}
|
||||
|
||||
function acquireProviderRuntimeRequestSlot(): Promise<void> {
|
||||
if (activeProviderRuntimeRequestCount < MAX_PARALLEL_PROVIDER_RUNTIME_REQUESTS) {
|
||||
if (activeProviderRuntimeRequestCount < getProviderRuntimeRequestLimit()) {
|
||||
activeProviderRuntimeRequestCount += 1;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue