Skip to content

Commit e0bbc85

Browse files
committed
Resolve PR body follow-up findings
1 parent 182b77e commit e0bbc85

7 files changed

Lines changed: 121 additions & 5 deletions

File tree

lib/runtime-rotation-proxy.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ const DEFAULT_QUOTA_REMAINING_THRESHOLD = 10;
109109
const DEFAULT_AUTH_FAILURE_COOLDOWN_MS = 30_000;
110110
const DEFAULT_MAX_RUNTIME_ACCOUNT_ATTEMPTS = 4;
111111
const MAX_REQUEST_BODY_BYTES = 64 * 1024 * 1024;
112+
const MAX_THREAD_GOAL_FALLBACKS = 512;
112113
const HOP_BY_HOP_HEADERS = new Set([
113114
"connection",
114115
"content-length",
@@ -345,6 +346,33 @@ function isThreadGoalFallbackStatus(status: number): boolean {
345346
return status === HTTP_STATUS.FORBIDDEN;
346347
}
347348

349+
function setThreadGoalFallback(
350+
fallbacks: Map<string, string | null>,
351+
key: string,
352+
goal: string | null,
353+
): void {
354+
if (fallbacks.has(key)) {
355+
fallbacks.delete(key);
356+
}
357+
fallbacks.set(key, goal);
358+
while (fallbacks.size > MAX_THREAD_GOAL_FALLBACKS) {
359+
const oldestKey = fallbacks.keys().next().value;
360+
if (typeof oldestKey !== "string") break;
361+
fallbacks.delete(oldestKey);
362+
}
363+
}
364+
365+
function getThreadGoalFallback(
366+
fallbacks: Map<string, string | null>,
367+
key: string,
368+
): string | null {
369+
if (!fallbacks.has(key)) return null;
370+
const goal = fallbacks.get(key) ?? null;
371+
fallbacks.delete(key);
372+
fallbacks.set(key, goal);
373+
return goal;
374+
}
375+
348376
function resolveSessionKey(headers: Headers, parsedBody: RequestBody | null): string | null {
349377
const headerKey =
350378
headers.get(OPENAI_HEADERS.SESSION_ID) ??
@@ -942,7 +970,7 @@ export async function startRuntimeRotationProxy(
942970
operation: isModelsRequest
943971
? "models"
944972
: isThreadGoalRequest
945-
? "responses"
973+
? "thread-goal"
946974
: "responses",
947975
model: context.model,
948976
projectKey,
@@ -1169,12 +1197,12 @@ export async function startRuntimeRotationProxy(
11691197
account: refreshed.account,
11701198
});
11711199
if (context.upstreamPath.endsWith("/set")) {
1172-
threadGoalFallbacks.set(fallbackKey, goal);
1200+
setThreadGoalFallback(threadGoalFallbacks, fallbackKey, goal);
11731201
writeJson(res, HTTP_STATUS.OK, { ok: true, goal });
11741202
return;
11751203
}
11761204
writeJson(res, HTTP_STATUS.OK, {
1177-
goal: threadGoalFallbacks.get(fallbackKey) ?? null,
1205+
goal: getThreadGoalFallback(threadGoalFallbacks, fallbackKey),
11781206
});
11791207
return;
11801208
}

lib/usage/ledger.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const VALID_SOURCES = new Set<UsageLedgerSource>([
4040
const VALID_OPERATIONS = new Set<UsageLedgerOperation>([
4141
"responses",
4242
"models",
43+
"thread-goal",
4344
"auth-refresh",
4445
"diagnostic",
4546
"unknown",

lib/usage/redaction.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const VALID_SOURCES = new Set<UsageLedgerSource>([
2020
const VALID_OPERATIONS = new Set<UsageLedgerOperation>([
2121
"responses",
2222
"models",
23+
"thread-goal",
2324
"auth-refresh",
2425
"diagnostic",
2526
"unknown",

lib/usage/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export type UsageLedgerSource =
88
export type UsageLedgerOperation =
99
| "responses"
1010
| "models"
11+
| "thread-goal"
1112
| "auth-refresh"
1213
| "diagnostic"
1314
| "unknown";

scripts/codex.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2886,8 +2886,7 @@ async function createRuntimeRotationAppHelperContext(
28862886

28872887
const cleanup = async ({ exitCode } = {}) => {
28882888
const livedMs = Date.now() - startedAt;
2889-
const exitedSuccessfully = exitCode === 0 || exitCode === null || exitCode === undefined;
2890-
if (exitedSuccessfully && (options.detachOnExit === true || livedMs < detachGraceMs)) {
2889+
if (exitCode === 0 && (options.detachOnExit === true || livedMs < detachGraceMs)) {
28912890
helper.stdout?.destroy();
28922891
helper.stderr?.destroy();
28932892
helper.unref();

test/runtime-policy.test.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,4 +165,54 @@ describe("runtime policy", () => {
165165
});
166166
expect(recorder.hasRecorded()).toBe(true);
167167
});
168+
169+
it("records thread goal usage as a distinct operation", async () => {
170+
const append = vi.fn<typeof appendUsageLedgerRow>().mockResolvedValue({
171+
version: 1,
172+
id: "row-1",
173+
createdAt: 100,
174+
source: "runtime-proxy",
175+
operation: "thread-goal",
176+
outcome: "failure",
177+
model: null,
178+
projectKey: "project-a",
179+
account: null,
180+
requestId: "thread-1",
181+
statusCode: 403,
182+
errorCode: "thread_goal_upstream_blocked",
183+
durationMs: 0,
184+
tokens: {
185+
inputTokens: 0,
186+
outputTokens: 0,
187+
cachedInputTokens: 0,
188+
reasoningTokens: 0,
189+
totalTokens: 0,
190+
},
191+
costUsd: 0,
192+
});
193+
const recorder = createRuntimeUsageRecorder({
194+
source: "runtime-proxy",
195+
operation: "thread-goal",
196+
model: null,
197+
projectKey: "project-a",
198+
requestId: "thread-1",
199+
startedAt: 100,
200+
append,
201+
});
202+
203+
await recorder.record({
204+
outcome: "failure",
205+
statusCode: 403,
206+
errorCode: "thread_goal_upstream_blocked",
207+
});
208+
209+
expect(append.mock.calls[0]?.[0]).toMatchObject({
210+
source: "runtime-proxy",
211+
operation: "thread-goal",
212+
outcome: "failure",
213+
requestId: "thread-1",
214+
statusCode: 403,
215+
errorCode: "thread_goal_upstream_blocked",
216+
});
217+
});
168218
});

test/runtime-rotation-proxy.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,42 @@ describe("runtime rotation proxy", () => {
765765
).toHaveLength(2);
766766
});
767767

768+
it("evicts oldest local thread goal fallbacks when capacity is exceeded", async () => {
769+
const now = Date.now();
770+
const accountManager = new AccountManager(undefined, createStorage(now, 600));
771+
const { fetchImpl } = createRecordingFetch(
772+
() =>
773+
new Response("<html>blocked</html>", {
774+
status: HTTP_STATUS.FORBIDDEN,
775+
headers: { "content-type": "text/html" },
776+
}),
777+
);
778+
const proxy = await startProxy({ accountManager, fetchImpl });
779+
780+
for (let index = 0; index < 513; index += 1) {
781+
const response = await postThreadGoal(
782+
proxy,
783+
{ threadId: `thread-${index}`, goal: `goal-${index}` },
784+
"/thread/goal/set",
785+
);
786+
expect(response.status).toBe(HTTP_STATUS.OK);
787+
}
788+
789+
const evicted = await postThreadGoal(
790+
proxy,
791+
{ threadId: "thread-0" },
792+
"/thread/goal/get",
793+
);
794+
const retained = await postThreadGoal(
795+
proxy,
796+
{ threadId: "thread-512" },
797+
"/thread/goal/get",
798+
);
799+
800+
expect(await evicted.json()).toEqual({ goal: null });
801+
expect(await retained.json()).toEqual({ goal: "goal-512" });
802+
});
803+
768804
it("rejects unauthenticated model discovery requests", async () => {
769805
const now = Date.now();
770806
const accountManager = new AccountManager(undefined, createStorage(now));

0 commit comments

Comments
 (0)