Refactor proxy-core surfaces and transformer boundaries (#163)

* refactor: add canonical request contracts

* test: harden transformer boundary contracts

* refactor: adapt protocol transformers to canonical contract

* refactor: add cli profile registry

* refactor: extract provider profiles

* refactor: add conductor and provider executors

* refactor: add models surface

* refactor: migrate responses surface

* refactor: migrate chat surface

* refactor: migrate gemini surface

* refactor: migrate file surfaces

* test: align runtime executor undici mock

* fix: preserve compact SSE closeout payloads

* feat: support all-api-hub v2 backup imports

* fix: aggregate codex oauth SSE final payloads

* fix: harden responses SSE failure handling
This commit is contained in:
Cita
2026-03-19 16:07:56 +08:00
committed by GitHub
parent c0c993f448
commit 24483c28eb
75 changed files with 8333 additions and 3574 deletions
@@ -11,6 +11,66 @@ function parseSsePayloads(lines: string[]): Array<Record<string, unknown>> {
}
describe('openAiChatTransformer.inbound', () => {
it('parses chat requests into canonical envelopes', () => {
const result = openAiChatTransformer.parseRequest({
model: 'gpt-5',
stream: true,
messages: [{ role: 'user', content: 'hello' }],
prompt_cache_key: 'cache-key',
reasoning_effort: 'high',
reasoning_budget: 1024,
});
expect(result.error).toBeUndefined();
expect(result.value).toMatchObject({
operation: 'generate',
surface: 'openai-chat',
cliProfile: 'generic',
requestedModel: 'gpt-5',
stream: true,
messages: [
{
role: 'user',
parts: [{ type: 'text', text: 'hello' }],
},
],
continuation: {
promptCacheKey: 'cache-key',
},
reasoning: {
effort: 'high',
budgetTokens: 1024,
},
});
});
it('builds chat requests from canonical envelopes', () => {
const body = openAiChatTransformer.buildProtocolRequest({
operation: 'generate',
surface: 'openai-chat',
cliProfile: 'codex',
requestedModel: 'gpt-5',
stream: true,
messages: [{ role: 'user', parts: [{ type: 'text', text: 'hello' }] }],
reasoning: {
effort: 'medium',
budgetTokens: 512,
},
continuation: {
promptCacheKey: 'cache-key',
},
});
expect(body).toMatchObject({
model: 'gpt-5',
stream: true,
messages: [{ role: 'user', content: 'hello' }],
prompt_cache_key: 'cache-key',
reasoning_effort: 'medium',
reasoning_budget: 512,
});
});
it('captures chat request metadata fields without changing upstream body', () => {
const result = openAiChatTransformer.transformRequest({
model: 'gpt-5',
@@ -1,3 +1,6 @@
import { canonicalRequestFromOpenAiBody, canonicalRequestToOpenAiChatBody } from '../../canonical/request.js';
import type { CanonicalRequestEnvelope } from '../../canonical/types.js';
import type { ProtocolBuildContext, ProtocolParseContext } from '../../contracts.js';
import { type NormalizedFinalResponse, type NormalizedStreamEvent, type StreamTransformContext } from '../../shared/normalized.js';
import { createChatEndpointStrategy } from '../../shared/chatEndpointStrategy.js';
import { openAiChatInbound } from './inbound.js';
@@ -28,6 +31,46 @@ export const openAiChatTransformer = {
proxyStream: {
createSession: createChatProxyStreamSession,
},
parseRequest(
body: unknown,
ctx?: ProtocolParseContext,
): { value?: CanonicalRequestEnvelope; error?: { statusCode: number; payload: unknown } } {
const parsed = openAiChatInbound.parse(body);
if (parsed.error) {
return { error: parsed.error };
}
if (!parsed.value) {
return {
error: {
statusCode: 400,
payload: {
error: {
message: 'invalid chat request',
type: 'invalid_request_error',
},
},
},
};
}
return {
value: canonicalRequestFromOpenAiBody({
body: parsed.value.parsed.upstreamBody,
surface: 'openai-chat',
cliProfile: ctx?.cliProfile,
operation: ctx?.operation,
metadata: ctx?.metadata,
passthrough: ctx?.passthrough,
continuation: ctx?.continuation,
}),
};
},
buildProtocolRequest(
request: CanonicalRequestEnvelope,
_ctx?: ProtocolBuildContext,
): Record<string, unknown> {
return canonicalRequestToOpenAiChatBody(request);
},
transformRequest(body: unknown): ReturnType<typeof openAiChatInbound.parse> {
return openAiChatInbound.parse(body);
},
@@ -3,6 +3,75 @@ import { describe, expect, it } from 'vitest';
import { openAiResponsesTransformer } from './index.js';
describe('openAiResponsesTransformer.inbound', () => {
it('parses responses requests into canonical envelopes', () => {
const result = openAiResponsesTransformer.parseRequest({
model: 'gpt-5',
input: 'hello',
previous_response_id: 'resp_prev_1',
prompt_cache_key: 'cache-key',
reasoning: {
effort: 'high',
},
});
expect(result.error).toBeUndefined();
expect(result.value).toMatchObject({
operation: 'generate',
surface: 'openai-responses',
cliProfile: 'generic',
requestedModel: 'gpt-5',
stream: false,
continuation: {
previousResponseId: 'resp_prev_1',
promptCacheKey: 'cache-key',
},
messages: [
{
role: 'user',
parts: [{ type: 'text', text: 'hello' }],
},
],
reasoning: {
effort: 'high',
},
});
});
it('builds responses requests from canonical envelopes', () => {
const body = openAiResponsesTransformer.buildProtocolRequest({
operation: 'generate',
surface: 'openai-responses',
cliProfile: 'codex',
requestedModel: 'gpt-5',
stream: true,
messages: [{ role: 'user', parts: [{ type: 'text', text: 'hello' }] }],
continuation: {
previousResponseId: 'resp_prev_1',
promptCacheKey: 'cache-key',
},
reasoning: {
effort: 'high',
},
});
expect(body).toMatchObject({
model: 'gpt-5',
stream: true,
previous_response_id: 'resp_prev_1',
prompt_cache_key: 'cache-key',
input: [
{
type: 'message',
role: 'user',
content: [{ type: 'input_text', text: 'hello' }],
},
],
reasoning: {
effort: 'high',
},
});
});
it('returns a protocol request envelope with a normalized responses body', () => {
const result = openAiResponsesTransformer.transformRequest({
model: 'gpt-5',
@@ -1,3 +1,6 @@
import { canonicalRequestFromOpenAiBody, canonicalRequestToOpenAiChatBody } from '../../canonical/request.js';
import type { CanonicalRequestEnvelope } from '../../canonical/types.js';
import type { ProtocolBuildContext, ProtocolParseContext } from '../../contracts.js';
import { type StreamTransformContext } from '../../shared/normalized.js';
import {
convertOpenAiBodyToResponsesBody,
@@ -61,6 +64,64 @@ export const openAiResponsesTransformer = {
proxyStream: {
createSession: createResponsesProxyStreamSession,
},
parseRequest(
body: unknown,
ctx?: ProtocolParseContext,
): { value?: CanonicalRequestEnvelope; error?: { statusCode: number; payload: unknown } } {
const parsed = openAiResponsesInbound.parse(body, {
defaultEncryptedReasoningInclude: ctx?.defaultEncryptedReasoningInclude,
});
if (parsed.error) {
return { error: parsed.error };
}
if (!parsed.value) {
return {
error: {
statusCode: 400,
payload: {
error: {
message: 'invalid responses request',
type: 'invalid_request_error',
},
},
},
};
}
const responsesBody = parsed.value.parsed.normalizedBody;
const openAiBody = convertResponsesBodyToOpenAiBody(
responsesBody,
typeof responsesBody.model === 'string' ? responsesBody.model : parsed.value.model,
responsesBody.stream === true,
{ defaultEncryptedReasoningInclude: ctx?.defaultEncryptedReasoningInclude },
);
return {
value: canonicalRequestFromOpenAiBody({
body: openAiBody,
surface: 'openai-responses',
cliProfile: ctx?.cliProfile,
operation: ctx?.operation,
metadata: ctx?.metadata,
passthrough: ctx?.passthrough,
continuation: ctx?.continuation,
}),
};
},
buildProtocolRequest(
request: CanonicalRequestEnvelope,
_ctx?: ProtocolBuildContext,
): Record<string, unknown> {
const openAiBody = canonicalRequestToOpenAiChatBody(request);
if (request.reasoning) {
openAiBody.reasoning = {
...(request.reasoning.effort ? { effort: request.reasoning.effort } : {}),
...(request.reasoning.budgetTokens !== undefined ? { budget_tokens: request.reasoning.budgetTokens } : {}),
...(request.reasoning.summary ? { summary: request.reasoning.summary } : {}),
};
}
return convertOpenAiBodyToResponsesBody(openAiBody, request.requestedModel, request.stream);
},
transformRequest(
body: unknown,
options?: { defaultEncryptedReasoningInclude?: boolean },