test(proxy): cover proxy lifecycle and routing

This commit is contained in:
Tam Nhu Tran
2026-04-14 19:27:19 -04:00
parent 074e900557
commit 5c66c1f121
7 changed files with 745 additions and 0 deletions
@@ -0,0 +1,188 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it } from 'bun:test';
import * as fs from 'fs';
import * as http from 'http';
import * as os from 'os';
import * as path from 'path';
import { spawn, spawnSync } from 'child_process';
import getPort from 'get-port';
const DIST_ENTRY = path.join(process.cwd(), 'dist', 'ccs.js');
let originalCcsHome: string | undefined;
let tempDir: string;
let upstreamServer: http.Server;
let upstreamBody: unknown;
beforeEach(() => {
originalCcsHome = process.env.CCS_HOME;
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-routing-e2e-'));
upstreamBody = undefined;
});
afterEach(() => {
try {
upstreamServer?.close();
} catch {
// Best-effort cleanup.
}
spawnSync(process.execPath, [DIST_ENTRY, 'proxy', 'stop'], {
env: { ...process.env, CCS_HOME: tempDir },
});
if (originalCcsHome !== undefined) {
process.env.CCS_HOME = originalCcsHome;
} else {
delete process.env.CCS_HOME;
}
fs.rmSync(tempDir, { recursive: true, force: true });
});
function startMockUpstream(port: number): Promise<void> {
return new Promise((resolve) => {
upstreamServer = http.createServer(async (req, res) => {
let body = '';
for await (const chunk of req) {
body += chunk.toString();
}
upstreamBody = JSON.parse(body);
res.writeHead(200, { 'Content-Type': 'text/event-stream' });
res.write(
'data: {"id":"chatcmpl_1","model":"hf-model","choices":[{"index":0,"delta":{"role":"assistant","content":"Hello"}}]}\n\n'
);
res.write(
'data: {"id":"chatcmpl_1","model":"hf-model","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_1","type":"function","function":{"name":"search","arguments":"{\\"q\\":\\"docs\\"}"}}]}}]}\n\n'
);
res.write(
'data: {"id":"chatcmpl_1","model":"hf-model","choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":9,"completion_tokens":4}}\n\n'
);
res.end('data: [DONE]\n\n');
});
upstreamServer.listen(port, '127.0.0.1', () => resolve());
});
}
function runCli(args: string[], env: Record<string, string>): Promise<{ code: number | null; stdout: string; stderr: string }> {
return new Promise((resolve) => {
const child = spawn(process.execPath, [DIST_ENTRY, ...args], {
env: {
...process.env,
...env,
},
stdio: ['ignore', 'pipe', 'pipe'],
});
let stdout = '';
let stderr = '';
child.stdout.on('data', (chunk) => {
stdout += chunk.toString();
});
child.stderr.on('data', (chunk) => {
stderr += chunk.toString();
});
child.on('close', (code) => {
resolve({ code, stdout, stderr });
});
});
}
beforeAll(() => {
const result = spawnSync(process.execPath, ['run', 'build'], {
encoding: 'utf8',
env: process.env,
});
expect(result.status).toBe(0);
});
describe('openai provider routing e2e', () => {
it('routes a settings profile through the local proxy into an OpenAI-compatible upstream', async () => {
const upstreamPort = await getPort();
await startMockUpstream(upstreamPort);
const ccsDir = path.join(tempDir, '.ccs');
const binDir = path.join(tempDir, 'bin');
const outputPath = path.join(tempDir, 'claude-output.json');
fs.mkdirSync(ccsDir, { recursive: true });
fs.mkdirSync(binDir, { recursive: true });
const settingsPath = path.join(ccsDir, 'hf.settings.json');
fs.writeFileSync(
path.join(ccsDir, 'config.json'),
JSON.stringify({ profiles: { hf: settingsPath } }, null, 2),
'utf8'
);
fs.writeFileSync(
settingsPath,
JSON.stringify({
env: {
ANTHROPIC_BASE_URL: `http://127.0.0.1:${upstreamPort}`,
ANTHROPIC_AUTH_TOKEN: 'hf_token',
ANTHROPIC_MODEL: 'hf-model',
CCS_DROID_PROVIDER: 'generic-chat-completion-api',
},
}),
'utf8'
);
fs.writeFileSync(
path.join(binDir, 'claude'),
`#!/usr/bin/env node
const fs = require('fs');
(async () => {
const response = await fetch(\`\${process.env.ANTHROPIC_BASE_URL}/v1/messages\`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'anthropic-version': '2023-06-01',
'x-api-key': process.env.ANTHROPIC_AUTH_TOKEN,
},
body: JSON.stringify({
model: process.env.ANTHROPIC_MODEL,
stream: true,
tools: [{ name: 'search', description: 'Search docs', input_schema: { type: 'object' } }],
messages: [{ role: 'user', content: 'Find docs' }],
}),
});
const text = await response.text();
fs.writeFileSync(process.env.CCS_E2E_OUTPUT, JSON.stringify({
status: response.status,
baseUrl: process.env.ANTHROPIC_BASE_URL,
authToken: process.env.ANTHROPIC_AUTH_TOKEN,
text
}, null, 2));
})().catch((error) => {
console.error(error);
process.exit(1);
});
`,
{ mode: 0o755 }
);
const result = await runCli(['hf'], {
...process.env,
CCS_HOME: tempDir,
CCS_E2E_OUTPUT: outputPath,
PATH: `${binDir}:${process.env.PATH || ''}`,
});
expect(result.code).toBe(0);
const payload = JSON.parse(fs.readFileSync(outputPath, 'utf8')) as {
status: number;
baseUrl: string;
authToken: string;
text: string;
};
expect(payload.status).toBe(200);
expect(payload.baseUrl).toMatch(/^http:\/\/127\.0\.0\.1:\d+$/);
expect(payload.authToken).toMatch(/^[a-f0-9]{48}$/);
expect(payload.text).toContain('event: message_start');
expect(payload.text).toContain('tool_use');
expect(payload.text).toContain('message_stop');
const parsedUpstream = upstreamBody as {
messages?: Array<{ role: string; content: string }>;
tools?: Array<{ type: string }>;
};
expect(parsedUpstream.messages?.[0]).toEqual({ role: 'user', content: 'Find docs' });
expect(parsedUpstream.tools?.[0]?.type).toBe('function');
}, 35000);
});
+88
View File
@@ -0,0 +1,88 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it } from 'bun:test';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { spawnSync } from 'child_process';
import getPort from 'get-port';
const DIST_ENTRY = path.join(process.cwd(), 'dist', 'ccs.js');
let originalCcsHome: string | undefined;
let tempDir: string;
function runCli(args: string[], extraEnv: Record<string, string> = {}) {
return spawnSync(process.execPath, [DIST_ENTRY, ...args], {
encoding: 'utf8',
env: {
...process.env,
CCS_HOME: tempDir,
...extraEnv,
},
});
}
beforeAll(() => {
const result = spawnSync(process.execPath, ['run', 'build'], {
encoding: 'utf8',
env: process.env,
});
expect(result.status).toBe(0);
});
beforeEach(() => {
originalCcsHome = process.env.CCS_HOME;
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-proxy-e2e-'));
});
afterEach(() => {
runCli(['proxy', 'stop']);
if (originalCcsHome !== undefined) {
process.env.CCS_HOME = originalCcsHome;
} else {
delete process.env.CCS_HOME;
}
fs.rmSync(tempDir, { recursive: true, force: true });
});
describe('proxy command e2e', () => {
it('starts, reports status, activates, and stops via the built CLI', async () => {
const port = await getPort();
const ccsDir = path.join(tempDir, '.ccs');
fs.mkdirSync(ccsDir, { recursive: true });
const settingsPath = path.join(ccsDir, 'hf.settings.json');
fs.writeFileSync(
path.join(ccsDir, 'config.json'),
JSON.stringify({ profiles: { hf: settingsPath } }, null, 2),
'utf8'
);
fs.writeFileSync(
settingsPath,
JSON.stringify({
env: {
ANTHROPIC_BASE_URL: 'http://127.0.0.1:11434',
ANTHROPIC_AUTH_TOKEN: 'ollama',
ANTHROPIC_MODEL: 'qwen3-coder',
CCS_DROID_PROVIDER: 'generic-chat-completion-api',
},
}),
'utf8'
);
const started = runCli(['proxy', 'start', 'hf', '--port', String(port)]);
expect(started.status).toBe(0);
const status = runCli(['proxy', 'status']);
expect(status.stdout).toContain(`Proxy running on port ${port}`);
expect(status.stdout).toContain('Profile: hf');
const activate = runCli(['proxy', 'activate', '--shell', 'bash']);
expect(activate.stdout).toContain(`export ANTHROPIC_BASE_URL='http://127.0.0.1:${port}'`);
expect(activate.stdout).toMatch(/export ANTHROPIC_AUTH_TOKEN='[a-f0-9]{48}'/);
const health = await fetch(`http://127.0.0.1:${port}/health`);
expect(health.status).toBe(200);
const stopped = runCli(['proxy', 'stop']);
expect(stopped.status).toBe(0);
}, 35000);
});
@@ -0,0 +1,127 @@
import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import getPort from 'get-port';
import {
getOpenAICompatProxyStatus,
startOpenAICompatProxy,
stopOpenAICompatProxy,
} from '../../../src/proxy/proxy-daemon';
import { resolveOpenAICompatProfileConfig } from '../../../src/proxy/profile-router';
let originalCcsHome: string | undefined;
let tempDir: string;
beforeEach(() => {
originalCcsHome = process.env.CCS_HOME;
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-openai-proxy-'));
process.env.CCS_HOME = tempDir;
});
afterEach(async () => {
await stopOpenAICompatProxy();
if (originalCcsHome !== undefined) {
process.env.CCS_HOME = originalCcsHome;
} else {
delete process.env.CCS_HOME;
}
fs.rmSync(tempDir, { recursive: true, force: true });
});
describe('openai proxy daemon lifecycle', () => {
it('starts, reports status, serves health/models, and stops', async () => {
const port = await getPort();
const settingsPath = path.join(tempDir, 'hf.settings.json');
fs.writeFileSync(
settingsPath,
JSON.stringify({
env: {
ANTHROPIC_BASE_URL: 'http://127.0.0.1:11434',
ANTHROPIC_AUTH_TOKEN: 'ollama',
ANTHROPIC_MODEL: 'qwen3-coder',
CCS_DROID_PROVIDER: 'generic-chat-completion-api',
},
}),
'utf8'
);
const profile = resolveOpenAICompatProfileConfig('hf', settingsPath, {
ANTHROPIC_BASE_URL: 'http://127.0.0.1:11434',
ANTHROPIC_AUTH_TOKEN: 'ollama',
ANTHROPIC_MODEL: 'qwen3-coder',
CCS_DROID_PROVIDER: 'generic-chat-completion-api',
});
if (!profile) {
throw new Error('Expected an OpenAI-compatible profile');
}
const started = await startOpenAICompatProxy(profile, { port });
expect(started.success).toBe(true);
expect(started.authToken).toBeTruthy();
const status = await getOpenAICompatProxyStatus();
expect(status.running).toBe(true);
expect(status.profileName).toBe('hf');
expect(status.authToken).toBe(started.authToken);
const health = await fetch(`http://127.0.0.1:${port}/health`);
expect(health.status).toBe(200);
const models = (await (
await fetch(`http://127.0.0.1:${port}/v1/models`, {
headers: { 'x-api-key': started.authToken! },
})
).json()) as { data?: Array<{ id: string }> };
expect(models.data?.map((entry) => entry.id)).toEqual(['qwen3-coder']);
const stopped = await stopOpenAICompatProxy();
expect(stopped.success).toBe(true);
expect((await getOpenAICompatProxyStatus()).running).toBe(false);
}, 35000);
it('refuses to replace a running proxy for a different profile', async () => {
const firstPort = await getPort();
const firstSettingsPath = path.join(tempDir, 'hf.settings.json');
fs.writeFileSync(
firstSettingsPath,
JSON.stringify({
env: {
ANTHROPIC_BASE_URL: 'http://127.0.0.1:11434',
ANTHROPIC_AUTH_TOKEN: 'ollama',
ANTHROPIC_MODEL: 'qwen3-coder',
CCS_DROID_PROVIDER: 'generic-chat-completion-api',
},
}),
'utf8'
);
const firstProfile = resolveOpenAICompatProfileConfig('hf', firstSettingsPath, {
ANTHROPIC_BASE_URL: 'http://127.0.0.1:11434',
ANTHROPIC_AUTH_TOKEN: 'ollama',
ANTHROPIC_MODEL: 'qwen3-coder',
CCS_DROID_PROVIDER: 'generic-chat-completion-api',
});
if (!firstProfile) {
throw new Error('Expected first OpenAI-compatible profile');
}
const firstStart = await startOpenAICompatProxy(firstProfile, { port: firstPort });
expect(firstStart.success).toBe(true);
const secondProfile = resolveOpenAICompatProfileConfig('openai', path.join(tempDir, 'openai.settings.json'), {
ANTHROPIC_BASE_URL: 'https://api.openai.com/v1',
ANTHROPIC_AUTH_TOKEN: 'sk-openai',
ANTHROPIC_MODEL: 'gpt-4.1',
});
if (!secondProfile) {
throw new Error('Expected second OpenAI-compatible profile');
}
const secondStart = await startOpenAICompatProxy(secondProfile, { port: await getPort() });
expect(secondStart.success).toBe(false);
expect(secondStart.error).toContain('Proxy already running for profile "hf"');
const health = await fetch(`http://127.0.0.1:${firstPort}/health`);
expect(health.status).toBe(200);
});
});
@@ -0,0 +1,204 @@
import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
import getPort from 'get-port';
import * as http from 'http';
import { startOpenAICompatProxyServer } from '../../../src/proxy/server/proxy-server';
import type { OpenAICompatProfileConfig } from '../../../src/proxy/profile-router';
let upstreamServer: http.Server;
let proxyServer: http.Server;
let upstreamBody: unknown;
let upstreamPort: number;
let proxyPort: number;
function startMockUpstream(): Promise<void> {
return new Promise((resolve) => {
upstreamServer = http.createServer(async (req, res) => {
if (req.method !== 'POST' || req.url !== '/v1/chat/completions') {
res.writeHead(404).end();
return;
}
let body = '';
for await (const chunk of req) {
body += chunk.toString();
}
upstreamBody = JSON.parse(body);
const parsed = upstreamBody as { stream?: boolean };
if (parsed.stream) {
res.writeHead(200, { 'Content-Type': 'text/event-stream' });
res.write(
'data: {"id":"chatcmpl_1","model":"hf-model","choices":[{"index":0,"delta":{"role":"assistant","content":"Hello"}}]}\n\n'
);
res.write(
'data: {"id":"chatcmpl_1","model":"hf-model","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_1","type":"function","function":{"name":"search","arguments":"{\\"q\\":\\"docs\\"}"}}]}}]}\n\n'
);
res.write(
'data: {"id":"chatcmpl_1","model":"hf-model","choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":9,"completion_tokens":4}}\n\n'
);
res.end('data: [DONE]\n\n');
return;
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(
JSON.stringify({
id: 'chatcmpl_1',
model: 'hf-model',
choices: [
{
index: 0,
message: { role: 'assistant', content: 'Plain answer' },
finish_reason: 'stop',
},
],
usage: { prompt_tokens: 2, completion_tokens: 3 },
})
);
});
upstreamServer.listen(upstreamPort, '127.0.0.1', () => resolve());
});
}
async function requestProxy(payload: unknown): Promise<Response> {
return fetch(`http://127.0.0.1:${proxyPort}/v1/messages`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'anthropic-version': '2023-06-01',
'x-api-key': 'test-proxy-token',
},
body: JSON.stringify(payload),
});
}
beforeEach(async () => {
upstreamPort = await getPort();
proxyPort = await getPort();
upstreamBody = undefined;
await startMockUpstream();
const profile: OpenAICompatProfileConfig = {
profileName: 'hf',
settingsPath: '/tmp/hf.settings.json',
baseUrl: `http://127.0.0.1:${upstreamPort}`,
apiKey: 'hf_token',
provider: 'generic-chat-completion-api',
model: 'hf-model',
};
proxyServer = startOpenAICompatProxyServer({
profile,
port: proxyPort,
authToken: 'test-proxy-token',
});
});
afterEach(async () => {
await new Promise<void>((resolve) => proxyServer.close(() => resolve()));
await new Promise<void>((resolve) => upstreamServer.close(() => resolve()));
});
describe('openai proxy messages endpoint', () => {
it('translates Anthropic requests to OpenAI upstream and streams Anthropic SSE back', async () => {
const response = await requestProxy({
model: 'hf-model',
stream: true,
messages: [{ role: 'user', content: [{ type: 'text', text: 'Find docs' }] }],
tools: [{ name: 'search', description: 'Search docs', input_schema: { type: 'object' } }],
});
const body = await response.text();
expect(response.status).toBe(200);
expect(body).toContain('event: message_start');
expect(body).toContain('content_block_delta');
expect(body).toContain('tool_use');
expect(body).toContain('message_stop');
const parsedUpstream = upstreamBody as {
messages?: Array<{ role: string; content: string }>;
tools?: Array<{ type: string; function: { name: string } }>;
};
expect(parsedUpstream.messages?.[0]).toEqual({ role: 'user', content: 'Find docs' });
expect(parsedUpstream.tools?.[0]?.type).toBe('function');
expect(parsedUpstream.tools?.[0]?.function.name).toBe('search');
});
it('falls back to Anthropic JSON for non-streaming requests', async () => {
const response = await requestProxy({
model: 'hf-model',
messages: [{ role: 'user', content: 'hello' }],
});
const body = (await response.json()) as {
type?: string;
content?: Array<{ type?: string; text?: string }>;
};
expect(response.status).toBe(200);
expect(body.type).toBe('message');
expect(body.content?.[0]).toEqual({ type: 'text', text: 'Plain answer' });
});
it('returns invalid_request_error for malformed JSON', async () => {
const response = await fetch(`http://127.0.0.1:${proxyPort}/v1/messages`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'anthropic-version': '2023-06-01',
'x-api-key': 'test-proxy-token',
},
body: '{bad-json',
});
const body = (await response.json()) as { error?: { type?: string; message?: string } };
expect(response.status).toBe(400);
expect(body.error?.type).toBe('invalid_request_error');
expect(body.error?.message).toContain('Invalid JSON');
});
it('rejects requests without the local proxy auth token', async () => {
const response = await fetch(`http://127.0.0.1:${proxyPort}/v1/messages`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'anthropic-version': '2023-06-01',
},
body: JSON.stringify({
model: 'hf-model',
messages: [{ role: 'user', content: 'hello' }],
}),
});
const body = (await response.json()) as { error?: { type?: string } };
expect(response.status).toBe(401);
expect(body.error?.type).toBe('authentication_error');
});
it('returns invalid_request_error for unsupported Anthropic content blocks', async () => {
const response = await requestProxy({
model: 'hf-model',
messages: [{ role: 'user', content: [{ type: 'image' }] }],
});
const body = (await response.json()) as { error?: { type?: string; message?: string } };
expect(response.status).toBe(400);
expect(body.error?.type).toBe('invalid_request_error');
expect(body.error?.message).toContain('is not supported');
});
it('accepts query strings and trailing slashes on the messages route', async () => {
const response = await fetch(`http://127.0.0.1:${proxyPort}/v1/messages/?beta=test`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'anthropic-version': '2023-06-01',
authorization: 'Bearer test-proxy-token',
},
body: JSON.stringify({
model: 'hf-model',
messages: [{ role: 'user', content: 'hello' }],
}),
});
expect(response.status).toBe(200);
});
});
+35
View File
@@ -0,0 +1,35 @@
import { describe, expect, it } from 'bun:test';
import { SSEParser } from '../../../src/glmt/sse-parser';
describe('SSEParser', () => {
it('merges multi-line data fields into one JSON payload', () => {
const parser = new SSEParser({ throwOnMalformedJson: true });
const events = parser.parse(
[
'event: message',
'data: {"choices":[',
'data: {"delta":{"content":"Hello"}}',
'data: ]}',
'',
'',
].join('\n')
);
expect(events).toHaveLength(1);
expect(
(events[0]?.data as { choices?: Array<{ delta?: { content?: string } }> })?.choices?.[0]
?.delta?.content
).toBe('Hello');
});
it('keeps incomplete events buffered until the separator arrives', () => {
const parser = new SSEParser({ throwOnMalformedJson: true });
expect(parser.parse('data: {"choices":[{"delta":{"content":"Hi"}}]}')).toEqual([]);
const events = parser.parse('\n\n');
expect(events).toHaveLength(1);
expect((events[0]?.data as { choices?: Array<{ delta?: { content?: string } }> })?.choices?.[0]?.delta?.content).toBe(
'Hi'
);
});
});
+40
View File
@@ -0,0 +1,40 @@
import { describe, expect, it } from 'bun:test';
import { resolveOpenAICompatProfileConfig } from '../../../src/proxy/profile-router';
describe('resolveOpenAICompatProfileConfig', () => {
it('detects generic OpenAI-compatible profiles from base URL and provider hint', () => {
const result = resolveOpenAICompatProfileConfig('hf', '/tmp/hf.settings.json', {
ANTHROPIC_BASE_URL: 'https://router.huggingface.co/v1',
ANTHROPIC_AUTH_TOKEN: 'hf_token',
ANTHROPIC_MODEL: 'openai/gpt-oss-120b:fastest',
CCS_DROID_PROVIDER: 'generic-chat-completion-api',
});
expect(result).not.toBeNull();
expect(result?.provider).toBe('generic-chat-completion-api');
expect(result?.profileName).toBe('hf');
});
it('detects official OpenAI-style endpoints', () => {
const result = resolveOpenAICompatProfileConfig('openai', '/tmp/openai.settings.json', {
ANTHROPIC_BASE_URL: 'https://api.openai.com/v1',
ANTHROPIC_AUTH_TOKEN: 'sk-openai',
ANTHROPIC_MODEL: 'gpt-4.1',
CCS_OPENAI_PROXY_INSECURE: 'true',
});
expect(result?.provider).toBe('openai');
expect(result?.model).toBe('gpt-4.1');
expect(result?.insecure).toBe(true);
});
it('ignores Anthropic-compatible profiles', () => {
const result = resolveOpenAICompatProfileConfig('glm', '/tmp/glm.settings.json', {
ANTHROPIC_BASE_URL: 'https://api.z.ai/api/anthropic',
ANTHROPIC_AUTH_TOKEN: 'glm-key',
ANTHROPIC_MODEL: 'glm-5',
});
expect(result).toBeNull();
});
});
@@ -0,0 +1,63 @@
import { describe, expect, it } from 'bun:test';
import { ProxyRequestTransformer } from '../../../../src/proxy/transformers/request-transformer';
describe('ProxyRequestTransformer', () => {
it('translates Anthropic messages into OpenAI-compatible chat payloads', () => {
const transformer = new ProxyRequestTransformer();
const result = transformer.transform({
model: 'claude-sonnet-4.5',
stream: true,
messages: [
{ role: 'user', content: [{ type: 'text', text: 'Find release notes' }] },
{
role: 'assistant',
content: [{ type: 'tool_use', id: 'toolu_1', name: 'search', input: { q: 'release' } }],
},
{
role: 'user',
content: [{ type: 'tool_result', tool_use_id: 'toolu_1', content: 'v7.69.1' }],
},
],
thinking: { type: 'enabled', budget_tokens: 9000 },
max_tokens: 1024,
temperature: 0.2,
top_p: 0.9,
stop_sequences: ['STOP'],
metadata: { trace: 'abc' },
});
expect(result.stream).toBe(true);
expect(result.reasoning_effort).toBe('high');
expect(result.max_tokens).toBe(1024);
expect(result.temperature).toBe(0.2);
expect(result.top_p).toBe(0.9);
expect(result.stop).toEqual(['STOP']);
expect(result.metadata).toEqual({ trace: 'abc' });
expect(result.messages[0]).toEqual({ role: 'user', content: 'Find release notes' });
expect(result.messages[1]?.tool_calls?.[0]?.function.name).toBe('search');
expect(result.messages[2]).toEqual({
role: 'tool',
tool_call_id: 'toolu_1',
content: 'v7.69.1',
});
});
it('drops malformed optional fields but preserves the translated core request', () => {
const transformer = new ProxyRequestTransformer();
const result = transformer.transform({
messages: [{ role: 'user', content: 'hello' }],
max_tokens: 'bad',
temperature: 'bad',
top_p: 'bad',
stop_sequences: ['A', 1],
metadata: 'bad',
});
expect(result.messages).toEqual([{ role: 'user', content: 'hello' }]);
expect(result.max_tokens).toBeUndefined();
expect(result.temperature).toBeUndefined();
expect(result.top_p).toBeUndefined();
expect(result.stop).toEqual(['A']);
expect(result.metadata).toBeUndefined();
});
});