Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 36 additions & 7 deletions lib/request/helpers/tool-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ export interface Tool {
function: ToolFunction;
}

function isRecord(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === "object";
}

function cloneRecord(value: Record<string, unknown>): Record<string, unknown> {
return JSON.parse(JSON.stringify(value)) as Record<string, unknown>;
}

/**
* Cleans up tool definitions to ensure strict JSON Schema compliance.
*
Expand All @@ -31,17 +39,29 @@ export function cleanupToolDefinitions(tools: unknown): unknown {
if (!Array.isArray(tools)) return tools;

return tools.map((tool) => {
if (tool?.type !== "function" || !tool.function) {
if (!isRecord(tool) || tool.type !== "function") {
return tool;
}

// Clone to avoid mutating original
const cleanedTool = JSON.parse(JSON.stringify(tool));
if (cleanedTool.function.parameters) {
cleanupSchema(cleanedTool.function.parameters);
const functionDef = tool.function;
if (!isRecord(functionDef)) {
return tool;
}
const parameters = functionDef.parameters;
if (!isRecord(parameters)) {
return tool;
}

return cleanedTool;
// Clone only the schema tree we mutate to avoid heavy deep cloning of entire tools.
const cleanedParameters = cloneRecord(parameters);
cleanupSchema(cleanedParameters);

return {
...tool,
function: {
...functionDef,
parameters: cleanedParameters,
},
};
});
}

Expand All @@ -51,6 +71,15 @@ export function cleanupToolDefinitions(tools: unknown): unknown {
function cleanupSchema(schema: Record<string, unknown>): void {
if (!schema || typeof schema !== "object") return;

if (schema.properties && typeof schema.properties === "object") {
const properties = schema.properties as Record<string, unknown>;
for (const key of Object.keys(properties)) {
if (properties[key] === undefined) {
delete properties[key];
}
}
}

// 1. Flatten Unions (anyOf -> enum)
if (Array.isArray(schema.anyOf)) {
const anyOf = schema.anyOf as Record<string, unknown>[];
Expand Down
37 changes: 19 additions & 18 deletions lib/request/request-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -543,24 +543,25 @@ export function filterInput(
input: InputItem[] | undefined,
): InputItem[] | undefined {
if (!Array.isArray(input)) return input;

return input
.filter((item) => {
// Remove AI SDK constructs not supported by Codex API
if (item.type === "item_reference") {
return false; // AI SDK only - references server state
}
return true; // Keep all other items
})
.map((item) => {
// Strip IDs from all items (Codex API stateless mode)
if (item.id) {
const { id: _omit, ...itemWithoutId } = item;
void _omit;
return itemWithoutId as InputItem;
}
return item;
});
const filtered: InputItem[] = [];
for (const item of input) {
if (!item || typeof item !== "object") {
continue;
}
// Remove AI SDK constructs not supported by Codex API.
if (item.type === "item_reference") {
continue;
}
// Strip IDs from all items (Codex API stateless mode).
if (item.id) {
const { id: _omit, ...itemWithoutId } = item;
void _omit;
filtered.push(itemWithoutId as InputItem);
continue;
}
filtered.push(item);
}
return filtered;
}

/**
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
"bench:edit-formats": "node scripts/benchmark-edit-formats.mjs --preset=codex-core",
"bench:edit-formats:smoke": "node scripts/benchmark-edit-formats.mjs --smoke --preset=codex-core",
"bench:edit-formats:render": "node scripts/benchmark-render-dashboard.mjs",
"bench:runtime-path": "npm run build && node scripts/benchmark-runtime-path.mjs",
"bench:runtime-path:quick": "node scripts/benchmark-runtime-path.mjs",
"test:coverage": "vitest run --coverage",
"coverage": "vitest run --coverage",
"audit:prod": "npm audit --omit=dev --audit-level=high",
Expand Down
166 changes: 166 additions & 0 deletions scripts/benchmark-runtime-path.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#!/usr/bin/env node

import { mkdir, writeFile } from "node:fs/promises";
import { performance } from "node:perf_hooks";
import process from "node:process";
import { dirname, resolve } from "node:path";
import { filterInput } from "../dist/lib/request/request-transformer.js";
import { cleanupToolDefinitions } from "../dist/lib/request/helpers/tool-utils.js";
import { AccountManager } from "../dist/lib/accounts.js";

function argValue(args, name) {
const prefix = `${name}=`;
const match = args.find((arg) => arg.startsWith(prefix));
return match ? match.slice(prefix.length) : undefined;
}

function parsePositiveInt(value, fallback) {
if (!value) return fallback;
const parsed = Number.parseInt(value, 10);
if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
return parsed;
}

function benchmarkCase(name, iterations, fn) {
for (let i = 0; i < 5; i += 1) {
fn();
}
const start = performance.now();
for (let i = 0; i < iterations; i += 1) {
fn();
}
const end = performance.now();
return {
name,
iterations,
avgMs: Number(((end - start) / iterations).toFixed(6)),
};
}

function buildInputItems(size) {
const items = [];
for (let i = 0; i < size; i += 1) {
items.push({
type: "message",
role: i % 2 === 0 ? "user" : "assistant",
id: `msg_${i}`,
content: [{ type: "input_text", text: `payload-${i}` }],
});
if (i % 40 === 0) {
items.push({ type: "item_reference", id: `ref_${i}` });
}
}
return items;
}

function buildTools(toolCount, propertyCount) {
const tools = [];
for (let i = 0; i < toolCount; i += 1) {
const properties = {};
const required = [];
for (let j = 0; j < propertyCount; j += 1) {
const key = `field_${j}`;
properties[key] = { type: ["string", "null"], description: `property-${j}` };
required.push(key);
}
required.push("ghost_field");
tools.push({
type: "function",
function: {
name: `tool_${i}`,
parameters: {
type: "object",
properties,
required,
additionalProperties: false,
},
},
});
}
return tools;
}

function buildManager(accountCount) {
const now = Date.now();
const accounts = [];
for (let i = 0; i < accountCount; i += 1) {
accounts.push({
refreshToken: `rt_${i}`,
accessToken: `at_${i}`,
expiresAt: now + 3_600_000,
accountId: `acct_${i}`,
email: `user${i}@example.com`,
enabled: true,
addedAt: now,
lastUsed: 0,
rateLimitResetTimes: {},
});
}
return new AccountManager(undefined, {
version: 3,
accounts,
activeIndex: 0,
activeIndexByFamily: {},
});
}

function run() {
const args = process.argv.slice(2);
const iterations = parsePositiveInt(argValue(args, "--iterations"), 30);
const outputPath = argValue(args, "--output");

const inputSmall = buildInputItems(400);
const inputLarge = buildInputItems(2000);
const toolsMedium = buildTools(40, 12);
const toolsLarge = buildTools(140, 25);

const results = [
benchmarkCase("filterInput_small", iterations, () => {
const out = filterInput(inputSmall);
if (!Array.isArray(out)) throw new Error("filterInput_small failed");
}),
benchmarkCase("filterInput_large", iterations, () => {
const out = filterInput(inputLarge);
if (!Array.isArray(out)) throw new Error("filterInput_large failed");
}),
benchmarkCase("cleanupToolDefinitions_medium", iterations, () => {
const out = cleanupToolDefinitions(toolsMedium);
if (!Array.isArray(out)) throw new Error("cleanupToolDefinitions_medium failed");
}),
benchmarkCase("cleanupToolDefinitions_large", iterations, () => {
const out = cleanupToolDefinitions(toolsLarge);
if (!Array.isArray(out)) throw new Error("cleanupToolDefinitions_large failed");
}),
benchmarkCase("accountHybridSelection_200", iterations, () => {
const manager = buildManager(200);
for (let i = 0; i < 200; i += 1) {
manager.getCurrentOrNextForFamilyHybrid("codex", "gpt-5-codex", { pidOffsetEnabled: false });
}
}),
];

const payload = {
generatedAt: new Date().toISOString(),
node: process.version,
iterations,
results,
};

if (outputPath) {
const resolved = resolve(outputPath);
return mkdir(dirname(resolved), { recursive: true }).then(() =>
writeFile(resolved, `${JSON.stringify(payload, null, 2)}\n`, "utf8"),
).then(() => {
console.log(`Runtime benchmark written: ${resolved}`);
console.log(JSON.stringify(payload, null, 2));
});
}

console.log(JSON.stringify(payload, null, 2));
return Promise.resolve();
}

run().catch((error) => {
console.error(`Runtime benchmark failed: ${error instanceof Error ? error.message : String(error)}`);
process.exit(1);
});
38 changes: 25 additions & 13 deletions test/request-transformer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,21 +369,33 @@ describe('Request Transformer Module', () => {
expect(result![2].content).toBe('3');
});

it('should handle custom ID formats (future-proof)', async () => {
const input: InputItem[] = [
{ id: 'custom_id_format', type: 'message', role: 'user', content: 'test' },
{ id: 'another-format-123', type: 'message', role: 'user', content: 'test2' },
];
const result = filterInput(input);
it('should handle custom ID formats (future-proof)', async () => {
const input: InputItem[] = [
{ id: 'custom_id_format', type: 'message', role: 'user', content: 'test' },
{ id: 'another-format-123', type: 'message', role: 'user', content: 'test2' },
];
const result = filterInput(input);

expect(result).toHaveLength(2);
expect(result![0]).not.toHaveProperty('id');
expect(result![1]).not.toHaveProperty('id');
});

expect(result).toHaveLength(2);
expect(result![0]).not.toHaveProperty('id');
expect(result![1]).not.toHaveProperty('id');
});
it('should skip sparse entries without throwing', async () => {
const sparse = new Array<InputItem | undefined>(3);
sparse[0] = { id: 'msg_1', type: 'message', role: 'user', content: 'test' };
sparse[2] = { id: 'msg_2', type: 'message', role: 'assistant', content: 'reply' };

it('should return undefined for undefined input', async () => {
expect(filterInput(undefined)).toBeUndefined();
});
const result = filterInput(sparse as InputItem[]);

expect(result).toHaveLength(2);
expect(result![0]).not.toHaveProperty('id');
expect(result![1]).not.toHaveProperty('id');
});

it('should return undefined for undefined input', async () => {
expect(filterInput(undefined)).toBeUndefined();
});

it('should return non-array input as-is', async () => {
const notArray = { notAnArray: true };
Expand Down