feat(compaction): support SubAPI native remote compaction

- support sub-api/openai-responses through the public Responses endpoint
- preserve explicit Authorization and support header-only authentication
- retain Codex JWT requirements and native compaction failure fallback
- restore SubAPI checkpoints and preserve portable model-switching summaries
- add provider, authentication and checkpoint lifecycle regression tests
- document compatibility requirements and the Sub2API protocol reference
This commit is contained in:
云服务部-叶林立
2026-09-07 16:25:48 +08:00
parent 6e7effa792
commit ff1e7400da
9 changed files with 431 additions and 12 deletions
+66
View File
@@ -0,0 +1,66 @@
import {
buildCodexHeaders,
isJsonObject,
isOpenAICodexModel,
mergeFeatureHeader,
resolveCodexResponsesUrl,
type Model,
} from "./native-compaction.js";
export function isNativeCompactionModel(model: unknown): model is Model<any> {
return isOpenAICodexModel(model) || (
isJsonObject(model) && model.provider === "sub-api" && model.api === "openai-responses"
);
}
export function buildNativeCompactionTransport(params: {
model: Pick<Model<any>, "provider" | "api" | "baseUrl">;
apiKey?: string;
headers?: Record<string, string>;
sessionId: string;
}): { url: string; headers: Headers } {
if (isOpenAICodexModel(params.model)) {
if (!params.apiKey) throw new Error("Codex native compaction authentication requires an API key.");
return {
url: resolveCodexResponsesUrl(params.model.baseUrl),
headers: buildCodexHeaders({ ...params, apiKey: params.apiKey }),
};
}
if (!isNativeCompactionModel(params.model)) {
throw new Error("Native compaction is not supported for this provider and API.");
}
if (!params.model.baseUrl?.trim()) {
throw new Error("SubAPI native compaction requires an explicit baseUrl.");
}
let url: URL;
try {
url = new URL(params.model.baseUrl.trim());
} catch {
throw new Error("SubAPI native compaction requires a valid HTTP(S) baseUrl.");
}
if (!["https:", "http:"].includes(url.protocol) || url.username || url.password || url.hash) {
throw new Error("SubAPI native compaction requires an HTTP(S) baseUrl without credentials or a fragment.");
}
const path = url.pathname.replace(/\/+$/, "");
url.pathname = path.endsWith("/responses") ? path : `${path}/responses`;
// Sub2API accepts native compaction v2 through its public Responses endpoint.
// The gateway owns upstream Codex credentials; honor the client's resolved authentication.
const headers = new Headers(params.headers);
if (!headers.get("authorization")?.trim()) {
if (params.apiKey?.trim()) {
headers.set("authorization", `Bearer ${params.apiKey}`);
} else if (!headers.get("cf-aig-authorization")?.trim()) {
throw new Error("SubAPI native compaction authentication requires an API key or a non-empty Authorization or cf-aig-authorization header.");
}
}
headers.set("content-type", "application/json");
headers.set("accept", "text/event-stream");
headers.set("OpenAI-Beta", "responses=experimental");
headers.set("originator", "pi");
headers.set("user-agent", "pi-compaction");
headers.set("session-id", params.sessionId);
headers.set("x-client-request-id", params.sessionId);
headers.set("x-codex-beta-features", mergeFeatureHeader(headers.get("x-codex-beta-features")));
return { url: url.toString(), headers };
}