Skip to content

Commit db2cb9d

Browse files
creeep123tiann
authored andcommitted
fix(codex-models): cache model list + reduce RPC timeout to prevent intermittent 500s
Fixes #806 Problem: GET /api/sessions/{id}/codex-models intermittently returned 500 after 120s because listCodexModels() spawned a new codex app-server process on every call, and ~3% of those spawns exited early, causing the hub-side RPC to wait the full 120s timeout. Changes: - cli: Add module-level model list cache (TTL 5 min) with separate caches for visible and hidden models, plus clearCodexModelCache() export - cli: Preserve app-server exit details in error cause chain for faster failure detection - hub: Reduce MODEL_LIST_RPC_TIMEOUT_MS from 120s to 15s (normal requests complete in 1-5s; cached requests are instant)
1 parent f4c3513 commit db2cb9d

2 files changed

Lines changed: 35 additions & 2 deletions

File tree

cli/src/modules/common/codexModels.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@ export interface ListCodexModelsRequest {
88

99
export type ListCodexModelsResponse = CodexModelsResponse;
1010

11+
const CODEX_MODEL_CACHE_TTL_MS = 300_000;
12+
13+
type CodexModelCache = { models: CodexModelSummary[]; expiry: number };
14+
15+
let codexModelCache: CodexModelCache | null = null;
16+
let hiddenCodexModelCache: CodexModelCache | null = null;
17+
18+
export function clearCodexModelCache(): void {
19+
codexModelCache = null;
20+
hiddenCodexModelCache = null;
21+
}
22+
1123
function asNonEmptyString(value: unknown): string | null {
1224
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null;
1325
}
@@ -50,7 +62,18 @@ function normalizeModel(entry: unknown): CodexModelSummary | null {
5062
};
5163
}
5264

65+
function createCodexModelListError(error: unknown): Error {
66+
const message = getErrorMessage(error, 'Failed to list Codex models');
67+
return new Error(`Failed to list Codex models: ${message}`, { cause: error });
68+
}
69+
5370
export async function listCodexModels(includeHidden: boolean = false): Promise<CodexModelSummary[]> {
71+
const now = Date.now();
72+
const cache = includeHidden ? hiddenCodexModelCache : codexModelCache;
73+
if (cache && cache.expiry > now) {
74+
return cache.models;
75+
}
76+
5477
const client = new CodexAppServerClient();
5578

5679
try {
@@ -70,9 +93,19 @@ export async function listCodexModels(includeHidden: boolean = false): Promise<C
7093
? response.data.map(normalizeModel).filter((model): model is CodexModelSummary => model !== null)
7194
: [];
7295

96+
const nextCache = {
97+
models,
98+
expiry: Date.now() + CODEX_MODEL_CACHE_TTL_MS
99+
};
100+
if (includeHidden) {
101+
hiddenCodexModelCache = nextCache;
102+
} else {
103+
codexModelCache = nextCache;
104+
}
105+
73106
return models;
74107
} catch (error) {
75-
throw new Error(getErrorMessage(error, 'Failed to list Codex models'));
108+
throw createCodexModelListError(error);
76109
} finally {
77110
await client.disconnect().catch(() => undefined);
78111
}

hub/src/sync/rpcGateway.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import type { Server } from 'socket.io'
2121
import type { RpcRegistry } from '../socket/rpcRegistry'
2222

2323
const DEFAULT_RPC_TIMEOUT_MS = 30_000
24-
const MODEL_LIST_RPC_TIMEOUT_MS = 120_000
24+
const MODEL_LIST_RPC_TIMEOUT_MS = 15_000
2525

2626
export type RpcCommandResponse = CommandResponse
2727
export type RpcReadFileResponse = FileReadResponse

0 commit comments

Comments
 (0)