feat: add unified OA event flow

This commit is contained in:
Codex
2026-05-14 09:41:55 +00:00
parent b36d7f94d7
commit 9f483b002c
37 changed files with 3417 additions and 502 deletions
+23 -7
View File
@@ -2775,6 +2775,7 @@ async function getMicroserviceAvailabilitySummary(): Promise<Record<string, Json
function microserviceCacheTtlMs(serviceId: string, targetPath: string): number {
if (targetPath === "/health") return 0;
if (targetPath.endsWith("/stream")) return 0;
if (serviceId === "pipeline" && targetPath === "/api/snapshot") return 6_000;
if (serviceId === "pipeline" && targetPath.startsWith("/api/oa-event-flow/")) return 20_000;
if (serviceId === "pipeline" && targetPath.startsWith("/api/model-quota/")) return 60_000;
@@ -2888,22 +2889,36 @@ async function directMicroserviceResponse(
proxyOptions: { query: string; jsonArrayLimits: Record<string, JsonValue> },
requestHeaders: Record<string, JsonValue>,
bodyText: string,
abortSignal?: AbortSignal,
): Promise<Response> {
const baseUrl = new URL(service.backend.nodeBaseUrl);
const upstreamUrl = new URL(targetPath, baseUrl);
upstreamUrl.search = proxyOptions.query;
const headers = headersFromMicroserviceRequest(requestHeaders);
const streamExpected = method === "GET" && (targetPath.endsWith("/stream") || String(headers.get("accept") || "").toLowerCase().includes("text/event-stream"));
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), Math.max(1000, service.backend.timeoutMs));
const timer = streamExpected ? null : setTimeout(() => controller.abort(), Math.max(1000, service.backend.timeoutMs));
try {
const upstream = await fetch(upstreamUrl, {
method,
headers,
body: method === "GET" || method === "HEAD" ? undefined : bodyText,
signal: controller.signal,
signal: streamExpected ? abortSignal : controller.signal,
});
const rawBodyText = await upstream.text();
const upstreamContentType = upstream.headers.get("content-type") ?? "text/plain; charset=utf-8";
if (upstreamContentType.toLowerCase().includes("text/event-stream")) {
const responseHeaders: Record<string, string> = {
"content-type": upstreamContentType,
"cache-control": upstream.headers.get("cache-control") || "no-store, no-transform",
"connection": "keep-alive",
"x-unidesk-proxy-mode": "direct",
"x-unidesk-response-truncated": "false",
};
const buffering = upstream.headers.get("x-accel-buffering");
if (buffering !== null) responseHeaders["x-accel-buffering"] = buffering;
return new Response(upstream.body, { status: upstream.status, headers: responseHeaders });
}
const rawBodyText = await upstream.text();
const limitedBodyText = applyJsonArrayLimits(rawBodyText, upstreamContentType, proxyOptions.jsonArrayLimits);
const bounded = boundedMicroserviceBodyText(limitedBodyText, upstreamContentType, {
serviceId: service.id,
@@ -2922,7 +2937,7 @@ async function directMicroserviceResponse(
} catch (error) {
return jsonResponse({ ok: false, error: "direct microservice proxy failed", serviceId: service.id, detail: errorToJson(error) }, 502);
} finally {
clearTimeout(timer);
if (timer !== null) clearTimeout(timer);
}
}
@@ -2933,9 +2948,10 @@ async function fetchMicroserviceUpstreamResponse(
proxyOptions: { query: string; jsonArrayLimits: Record<string, JsonValue> },
requestHeaders: Record<string, JsonValue>,
bodyText: string,
abortSignal?: AbortSignal,
): Promise<Response> {
if (canDirectProxyMicroservice(service)) {
return directMicroserviceResponse(service, method, targetPath, proxyOptions, requestHeaders, bodyText);
return directMicroserviceResponse(service, method, targetPath, proxyOptions, requestHeaders, bodyText, abortSignal);
}
if (!(await providerSupports(service.providerId, "microservice.http"))) {
return jsonResponse({ ok: false, error: `provider does not declare microservice.http capability: ${service.providerId}` }, 409);
@@ -3028,8 +3044,8 @@ async function microserviceRoute(req: Request, url: URL): Promise<Response> {
return stale;
}
}
const response = await fetchMicroserviceUpstreamResponse(service, method, targetPath, proxyOptions, requestHeaders, bodyText);
if (method === "GET" || method === "HEAD") rememberMicroserviceCache(cacheKey, cacheTtlMs, await cacheableResponseSnapshot(response));
const response = await fetchMicroserviceUpstreamResponse(service, method, targetPath, proxyOptions, requestHeaders, bodyText, req.signal);
if ((method === "GET" || method === "HEAD") && cacheTtlMs > 0) rememberMicroserviceCache(cacheKey, cacheTtlMs, await cacheableResponseSnapshot(response));
return response;
}