@jeffreycao/copilot-api 2.7.28 → 2.7.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +1 -1
- package/dist/{server-C1Y_Wrlf.js → server-BE1P5ljq.js} +91 -434
- package/dist/server-BE1P5ljq.js.map +1 -0
- package/dist/{start-ChSjFGvj.js → start-CNMsFQIm.js} +2 -2
- package/dist/{start-ChSjFGvj.js.map → start-CNMsFQIm.js.map} +1 -1
- package/package.json +1 -2
- package/dist/server-C1Y_Wrlf.js.map +0 -1
|
@@ -6,10 +6,9 @@ import { n as isProviderAgentModelVisible, t as getProviderAgentModels } from ".
|
|
|
6
6
|
import { n as stopModelsRefreshLoop, t as setupCopilotRuntime } from "./copilot-runtime-Vxyt2nub.js";
|
|
7
7
|
import consola from "consola";
|
|
8
8
|
import { createHash, randomUUID } from "node:crypto";
|
|
9
|
-
import fs, {
|
|
10
|
-
import path
|
|
11
|
-
import fs$1
|
|
12
|
-
import { tmpdir } from "node:os";
|
|
9
|
+
import fs, { readFileSync } from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import fs$1 from "node:fs/promises";
|
|
13
12
|
import { fetch as fetch$1, getGlobalDispatcher } from "undici";
|
|
14
13
|
import { events } from "fetch-event-stream";
|
|
15
14
|
import { z } from "zod";
|
|
@@ -18,9 +17,6 @@ import { cors } from "hono/cors";
|
|
|
18
17
|
import { logger } from "hono/logger";
|
|
19
18
|
import util from "node:util";
|
|
20
19
|
import { streamSSE } from "hono/streaming";
|
|
21
|
-
import { Busboy } from "@fastify/busboy";
|
|
22
|
-
import { Readable, Transform } from "node:stream";
|
|
23
|
-
import { pipeline } from "node:stream/promises";
|
|
24
20
|
import { decompress } from "fzstd";
|
|
25
21
|
//#region src/lib/trace.ts
|
|
26
22
|
const traceIdMiddleware = async (c, next) => {
|
|
@@ -2193,7 +2189,7 @@ async function forwardProviderImages(providerConfig, request, operation) {
|
|
|
2193
2189
|
}
|
|
2194
2190
|
//#endregion
|
|
2195
2191
|
//#region src/routes/alpha-search/alpha-search-responses.ts
|
|
2196
|
-
const logger$
|
|
2192
|
+
const logger$19 = createHandlerLogger("alpha-search-responses-handler");
|
|
2197
2193
|
const SESSION_TTL_MS = 36e5;
|
|
2198
2194
|
const MAX_SESSIONS = 128;
|
|
2199
2195
|
const MAX_URL_REFERENCES = 256;
|
|
@@ -2551,14 +2547,14 @@ function createRemoteResponsesPayload(request, model, instruction) {
|
|
|
2551
2547
|
};
|
|
2552
2548
|
}
|
|
2553
2549
|
async function requestProviderSearch(c, providerConfig, payload, model, sessionId) {
|
|
2554
|
-
debugJson(logger$
|
|
2550
|
+
debugJson(logger$19, "Alpha search provider Responses request:", {
|
|
2555
2551
|
payload,
|
|
2556
2552
|
provider: providerConfig.name
|
|
2557
2553
|
});
|
|
2558
2554
|
const upstreamResponse = await forwardProviderResponses(providerConfig, payload, c.req.raw.headers, { clientSignal: c.req.raw.signal });
|
|
2559
2555
|
if (!upstreamResponse.ok) throw new HTTPError(`Failed to create ${providerConfig.name} responses for alpha search`, upstreamResponse);
|
|
2560
2556
|
const result = await upstreamResponse.json();
|
|
2561
|
-
debugJson(logger$
|
|
2557
|
+
debugJson(logger$19, "Alpha search provider Responses result:", {
|
|
2562
2558
|
provider: providerConfig.name,
|
|
2563
2559
|
result
|
|
2564
2560
|
});
|
|
@@ -2574,7 +2570,7 @@ async function requestProviderSearch(c, providerConfig, payload, model, sessionI
|
|
|
2574
2570
|
return result;
|
|
2575
2571
|
}
|
|
2576
2572
|
async function requestCopilotSearch(payload, model, requestId, sessionId, clientSignal) {
|
|
2577
|
-
debugJson(logger$
|
|
2573
|
+
debugJson(logger$19, "Alpha search Copilot Responses request:", payload);
|
|
2578
2574
|
const result = await alphaSearchResponsesDependencies.createResponses(payload, {
|
|
2579
2575
|
vision: false,
|
|
2580
2576
|
initiator: "agent",
|
|
@@ -2583,7 +2579,7 @@ async function requestCopilotSearch(payload, model, requestId, sessionId, client
|
|
|
2583
2579
|
sessionId,
|
|
2584
2580
|
clientSignal
|
|
2585
2581
|
});
|
|
2586
|
-
debugJson(logger$
|
|
2582
|
+
debugJson(logger$19, "Alpha search Copilot Responses result:", result);
|
|
2587
2583
|
alphaSearchResponsesDependencies.createUsageRecorder(model, sessionId)({
|
|
2588
2584
|
...normalizeResponsesUsage(result.usage),
|
|
2589
2585
|
total_nano_aiu: normalizeOptionalToken(result.copilot_usage?.total_nano_aiu)
|
|
@@ -2698,7 +2694,7 @@ async function forwardCodexAlphaSearch(request) {
|
|
|
2698
2694
|
}
|
|
2699
2695
|
//#endregion
|
|
2700
2696
|
//#region src/routes/alpha-search/route.ts
|
|
2701
|
-
const logger$
|
|
2697
|
+
const logger$18 = createHandlerLogger("alpha-search-handler");
|
|
2702
2698
|
const alphaSearchRoutes = new Hono();
|
|
2703
2699
|
const alphaSearchRouteDependencies = {
|
|
2704
2700
|
findEndpointModel,
|
|
@@ -2718,9 +2714,9 @@ function parseDebugBody(body) {
|
|
|
2718
2714
|
}
|
|
2719
2715
|
}
|
|
2720
2716
|
async function forwardCodexAlphaSearchRequest(request) {
|
|
2721
|
-
await debugJsonAsync(logger$
|
|
2717
|
+
await debugJsonAsync(logger$18, "alpha_search.codex.request", async () => ({ body: parseDebugBody(await request.clone().text()) }));
|
|
2722
2718
|
const upstreamResponse = await alphaSearchRouteDependencies.forwardCodexAlphaSearch(request);
|
|
2723
|
-
await debugJsonAsync(logger$
|
|
2719
|
+
await debugJsonAsync(logger$18, "alpha_search.codex.response", async () => ({
|
|
2724
2720
|
body: parseDebugBody(await upstreamResponse.clone().text()),
|
|
2725
2721
|
statusCode: upstreamResponse.status
|
|
2726
2722
|
}));
|
|
@@ -2831,7 +2827,7 @@ alphaSearchRoutes.post("/", async (c) => {
|
|
|
2831
2827
|
try {
|
|
2832
2828
|
return await handleAlphaSearchRequest(c);
|
|
2833
2829
|
} catch (error) {
|
|
2834
|
-
logger$
|
|
2830
|
+
logger$18.error("alpha_search.error", { error });
|
|
2835
2831
|
return await forwardError(c, error);
|
|
2836
2832
|
}
|
|
2837
2833
|
});
|
|
@@ -2978,7 +2974,7 @@ const normalizeProviderResponsesReasoningEffort = (payload, providerConfig) => {
|
|
|
2978
2974
|
};
|
|
2979
2975
|
//#endregion
|
|
2980
2976
|
//#region src/routes/provider/chat-completions/handler.ts
|
|
2981
|
-
const logger$
|
|
2977
|
+
const logger$17 = createHandlerLogger("provider-chat-completions-handler");
|
|
2982
2978
|
async function handleProviderChatCompletionsForProvider(c, options) {
|
|
2983
2979
|
const { payload, provider } = options;
|
|
2984
2980
|
const configuredProvider = await resolveProviderConfig(provider);
|
|
@@ -2993,13 +2989,13 @@ async function handleProviderChatCompletionsForProvider(c, options) {
|
|
|
2993
2989
|
applyProviderStreamOptions(payload);
|
|
2994
2990
|
applyDashScopePreserveThinkingDefault(payload, providerConfig);
|
|
2995
2991
|
applyProviderContextCache(payload, modelConfig, providerConfig);
|
|
2996
|
-
debugJson(logger$
|
|
2992
|
+
debugJson(logger$17, "provider.chat_completions.request", {
|
|
2997
2993
|
payload,
|
|
2998
2994
|
provider
|
|
2999
2995
|
});
|
|
3000
2996
|
const upstreamResponse = await forwardProviderChatCompletions(providerConfig, payload, c.req.raw.headers, { clientSignal: c.req.raw.signal });
|
|
3001
2997
|
if (!upstreamResponse.ok) {
|
|
3002
|
-
logger$
|
|
2998
|
+
logger$17.error("Failed to create provider chat completions", {
|
|
3003
2999
|
provider,
|
|
3004
3000
|
statusCode: upstreamResponse.status
|
|
3005
3001
|
});
|
|
@@ -3013,7 +3009,7 @@ async function handleProviderChatCompletionsForProvider(c, options) {
|
|
|
3013
3009
|
});
|
|
3014
3010
|
const responseBody = await upstreamResponse.clone().json();
|
|
3015
3011
|
recordUsage(normalizeOpenAIUsage(responseBody.usage));
|
|
3016
|
-
debugJson(logger$
|
|
3012
|
+
debugJson(logger$17, "provider.chat_completions.response", responseBody);
|
|
3017
3013
|
return createProviderProxyResponse(upstreamResponse);
|
|
3018
3014
|
}
|
|
3019
3015
|
const applyProviderStreamOptions = (payload) => {
|
|
@@ -3035,12 +3031,12 @@ const createProviderChatCompletionsUsageRecorder = (payload, provider, modelConf
|
|
|
3035
3031
|
providerName: provider
|
|
3036
3032
|
});
|
|
3037
3033
|
const streamProviderChatCompletions = (c, upstreamResponse, options) => {
|
|
3038
|
-
logger$
|
|
3034
|
+
logger$17.debug("provider.chat_completions.streaming", { provider: options.provider });
|
|
3039
3035
|
return streamSSE(c, async (stream) => {
|
|
3040
3036
|
let usage = {};
|
|
3041
3037
|
try {
|
|
3042
3038
|
for await (const chunk of events(upstreamResponse)) {
|
|
3043
|
-
debugJson(logger$
|
|
3039
|
+
debugJson(logger$17, "provider.chat_completions.stream_chunk", chunk);
|
|
3044
3040
|
if (chunk.data && chunk.data !== "[DONE]") {
|
|
3045
3041
|
const parsedChunk = parseChatCompletionChunkData(chunk.data);
|
|
3046
3042
|
if (parsedChunk?.usage) usage = normalizeOpenAIUsage(parsedChunk.usage);
|
|
@@ -3099,7 +3095,7 @@ const createChatCompletions = async (payload, options) => {
|
|
|
3099
3095
|
};
|
|
3100
3096
|
//#endregion
|
|
3101
3097
|
//#region src/routes/chat-completions/handler.ts
|
|
3102
|
-
const logger$
|
|
3098
|
+
const logger$16 = createHandlerLogger("chat-completions-handler");
|
|
3103
3099
|
async function handleCompletion$1(c) {
|
|
3104
3100
|
let payload = await c.req.json();
|
|
3105
3101
|
const requestedModel = payload.model;
|
|
@@ -3113,7 +3109,7 @@ async function handleCompletion$1(c) {
|
|
|
3113
3109
|
provider: providerModelAlias.provider
|
|
3114
3110
|
});
|
|
3115
3111
|
}
|
|
3116
|
-
debugJson(logger$
|
|
3112
|
+
debugJson(logger$16, "Request payload:", payload);
|
|
3117
3113
|
const selectedModel = findEndpointModel(payload.model);
|
|
3118
3114
|
payload.model = selectedModel?.id ?? payload.model;
|
|
3119
3115
|
if (isNullish(payload.max_tokens) && isNullish(payload.max_completion_tokens)) {
|
|
@@ -3121,16 +3117,16 @@ async function handleCompletion$1(c) {
|
|
|
3121
3117
|
...payload,
|
|
3122
3118
|
max_tokens: selectedModel?.capabilities.limits.max_output_tokens
|
|
3123
3119
|
};
|
|
3124
|
-
debugJson(logger$
|
|
3120
|
+
debugJson(logger$16, "Set max_tokens to:", payload.max_tokens);
|
|
3125
3121
|
}
|
|
3126
3122
|
if (payload.model.includes("gpt")) {
|
|
3127
3123
|
if (isNullish(payload.max_completion_tokens)) payload.max_completion_tokens = payload.max_tokens;
|
|
3128
3124
|
delete payload.max_tokens;
|
|
3129
3125
|
}
|
|
3130
3126
|
const requestId = generateRequestIdFromPayload(payload);
|
|
3131
|
-
logger$
|
|
3127
|
+
logger$16.debug("Generated request ID:", requestId);
|
|
3132
3128
|
const sessionId = getUUID(requestId);
|
|
3133
|
-
logger$
|
|
3129
|
+
logger$16.debug("Extracted session ID:", sessionId);
|
|
3134
3130
|
const recordUsage = createCopilotTokenUsageRecorder({
|
|
3135
3131
|
endpoint: "chat_completions",
|
|
3136
3132
|
fallbackSessionId: sessionId,
|
|
@@ -3142,18 +3138,18 @@ async function handleCompletion$1(c) {
|
|
|
3142
3138
|
sessionId
|
|
3143
3139
|
});
|
|
3144
3140
|
if (isNonStreaming$1(response)) {
|
|
3145
|
-
debugJson(logger$
|
|
3141
|
+
debugJson(logger$16, "Non-streaming response:", response);
|
|
3146
3142
|
recordUsage({
|
|
3147
3143
|
...normalizeOpenAIUsage(response.usage),
|
|
3148
3144
|
total_nano_aiu: normalizeOptionalToken(response.copilot_usage?.total_nano_aiu)
|
|
3149
3145
|
});
|
|
3150
3146
|
return c.json(response);
|
|
3151
3147
|
}
|
|
3152
|
-
logger$
|
|
3148
|
+
logger$16.debug("Streaming response");
|
|
3153
3149
|
return streamSSE(c, async (stream) => {
|
|
3154
3150
|
let usage = {};
|
|
3155
3151
|
for await (const chunk of response) {
|
|
3156
|
-
debugJson(logger$
|
|
3152
|
+
debugJson(logger$16, "Streaming chunk:", chunk);
|
|
3157
3153
|
const parsedChunk = parseChatCompletionChunk(chunk);
|
|
3158
3154
|
if (parsedChunk?.usage || parsedChunk?.copilot_usage) usage = {
|
|
3159
3155
|
...normalizeOpenAIUsage(parsedChunk.usage),
|
|
@@ -3383,7 +3379,7 @@ async function forwardCodexImages(request, operation) {
|
|
|
3383
3379
|
}
|
|
3384
3380
|
//#endregion
|
|
3385
3381
|
//#region src/routes/images/shared.ts
|
|
3386
|
-
const logger$
|
|
3382
|
+
const logger$15 = createHandlerLogger("images-handler");
|
|
3387
3383
|
const imageRouteDependencies = {
|
|
3388
3384
|
debugJsonAsync,
|
|
3389
3385
|
getImageEditModel,
|
|
@@ -3391,6 +3387,13 @@ const imageRouteDependencies = {
|
|
|
3391
3387
|
resolveMappedModel,
|
|
3392
3388
|
resolveProviderConfig
|
|
3393
3389
|
};
|
|
3390
|
+
function imageEditContentTypeErrorResponse(c) {
|
|
3391
|
+
if (c.req.header("content-type")?.split(";", 1)[0]?.trim().toLowerCase() === "application/json") return null;
|
|
3392
|
+
return c.json({ error: {
|
|
3393
|
+
message: "Image edits require application/json with images: [{ image_url }] or [{ file_id }]; multipart uploads are not supported",
|
|
3394
|
+
type: "invalid_request_error"
|
|
3395
|
+
} }, 415);
|
|
3396
|
+
}
|
|
3394
3397
|
function getContentMetadata(headers) {
|
|
3395
3398
|
return {
|
|
3396
3399
|
contentType: headers.get("content-type"),
|
|
@@ -3398,8 +3401,7 @@ function getContentMetadata(headers) {
|
|
|
3398
3401
|
};
|
|
3399
3402
|
}
|
|
3400
3403
|
/**
|
|
3401
|
-
* Snapshots request headers before the body
|
|
3402
|
-
* auto-generated body headers (like the multipart boundary) afterwards.
|
|
3404
|
+
* Snapshots generated request headers before consuming the body.
|
|
3403
3405
|
*/
|
|
3404
3406
|
function snapshotRequestHeaders(request) {
|
|
3405
3407
|
return new Headers(request.headers);
|
|
@@ -3420,6 +3422,37 @@ function createForwardRequest(request, requestHeaders, body) {
|
|
|
3420
3422
|
};
|
|
3421
3423
|
return new Request(request.url, init);
|
|
3422
3424
|
}
|
|
3425
|
+
function createJsonImagesRequest(request, requestHeaders, payload, model) {
|
|
3426
|
+
const headers = new Headers(requestHeaders);
|
|
3427
|
+
headers.delete("content-length");
|
|
3428
|
+
return createForwardRequest(request, headers, new TextEncoder().encode(JSON.stringify({
|
|
3429
|
+
...payload,
|
|
3430
|
+
model
|
|
3431
|
+
})));
|
|
3432
|
+
}
|
|
3433
|
+
/**
|
|
3434
|
+
* Parses an image request body as JSON, retaining the original bytes for
|
|
3435
|
+
* unchanged forwarding while allowing model mapping to rebuild the payload.
|
|
3436
|
+
* Rebuilt bodies stay binary so Node does not synthesize a text/plain content type.
|
|
3437
|
+
*/
|
|
3438
|
+
async function parseJsonImagesRequest(request) {
|
|
3439
|
+
const requestHeaders = snapshotRequestHeaders(request);
|
|
3440
|
+
const body = new Uint8Array(await request.arrayBuffer());
|
|
3441
|
+
const originalRequest = createForwardRequest(request, requestHeaders, body);
|
|
3442
|
+
let payload;
|
|
3443
|
+
try {
|
|
3444
|
+
const bodyText = new TextDecoder("utf-8", { fatal: true }).decode(body);
|
|
3445
|
+
payload = JSON.parse(bodyText);
|
|
3446
|
+
} catch {
|
|
3447
|
+
return originalRequest;
|
|
3448
|
+
}
|
|
3449
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload) || typeof payload.model !== "string") return originalRequest;
|
|
3450
|
+
return {
|
|
3451
|
+
createRequest: (mappedModel) => createJsonImagesRequest(request, requestHeaders, payload, mappedModel),
|
|
3452
|
+
model: payload.model,
|
|
3453
|
+
originalRequest
|
|
3454
|
+
};
|
|
3455
|
+
}
|
|
3423
3456
|
/**
|
|
3424
3457
|
* Handles Codex images proxying. Pass a resolvedProviderConfig when the
|
|
3425
3458
|
* caller already resolved the codex provider to avoid a second resolve, and
|
|
@@ -3431,16 +3464,16 @@ async function handleCodexImages(c, operation, resolvedProviderConfig, request =
|
|
|
3431
3464
|
message: "Provider 'codex' not found or disabled",
|
|
3432
3465
|
type: "invalid_request_error"
|
|
3433
3466
|
} }, 404);
|
|
3434
|
-
if (operation === "generations") await imageRouteDependencies.debugJsonAsync(logger$
|
|
3435
|
-
else debugJson(logger$
|
|
3467
|
+
if (operation === "generations") await imageRouteDependencies.debugJsonAsync(logger$15, "images.generations.codex.request", async () => ({ body: await request.clone().text() }));
|
|
3468
|
+
else debugJson(logger$15, "images.edits.codex.request", getContentMetadata(request.headers));
|
|
3436
3469
|
const upstreamResponse = await forwardCodexImages(request, operation);
|
|
3437
|
-
debugJson(logger$
|
|
3470
|
+
debugJson(logger$15, `images.${operation}.codex.response`, {
|
|
3438
3471
|
...getContentMetadata(upstreamResponse.headers),
|
|
3439
3472
|
statusCode: upstreamResponse.status
|
|
3440
3473
|
});
|
|
3441
3474
|
return createProviderProxyResponse(upstreamResponse);
|
|
3442
3475
|
} catch (error) {
|
|
3443
|
-
logger$
|
|
3476
|
+
logger$15.error(`images.${operation}.codex.error`, { error });
|
|
3444
3477
|
return await forwardError(c, error);
|
|
3445
3478
|
}
|
|
3446
3479
|
}
|
|
@@ -3459,7 +3492,7 @@ async function routeImagesRequest(c, operation, parsed) {
|
|
|
3459
3492
|
const request = parsed.createRequest(providerModelAlias.model);
|
|
3460
3493
|
if (providerConfig.name === "codex") return await handleCodexImages(c, operation, providerConfig, request);
|
|
3461
3494
|
return await forwardProviderImagesWithLogging(providerConfig, request, operation, {
|
|
3462
|
-
logger: logger$
|
|
3495
|
+
logger: logger$15,
|
|
3463
3496
|
provider: providerModelAlias.provider
|
|
3464
3497
|
});
|
|
3465
3498
|
}
|
|
@@ -3541,12 +3574,6 @@ const XAI_ASPECT_RATIOS = [
|
|
|
3541
3574
|
const XAI_ASPECT_RATIO_LABELS = /* @__PURE__ */ new Set([...XAI_ASPECT_RATIOS.map((ratio) => ratio.label), "auto"]);
|
|
3542
3575
|
const XAI_RESPONSE_FORMATS = /* @__PURE__ */ new Set(["url", "b64_json"]);
|
|
3543
3576
|
const XAI_RESOLUTIONS = /* @__PURE__ */ new Set(["1k", "2k"]);
|
|
3544
|
-
const IMAGE_FIELD_NAMES = /* @__PURE__ */ new Set([
|
|
3545
|
-
"image",
|
|
3546
|
-
"image[]",
|
|
3547
|
-
"images",
|
|
3548
|
-
"images[]"
|
|
3549
|
-
]);
|
|
3550
3577
|
const MAX_EDIT_IMAGES = 5;
|
|
3551
3578
|
const MAX_IMAGE_COUNT = 10;
|
|
3552
3579
|
const HIGH_RESOLUTION_EDGE = 1536;
|
|
@@ -3681,32 +3708,6 @@ function readImageReference(value) {
|
|
|
3681
3708
|
if (value.file_id !== void 0) throw new XaiImagesRequestError("file_id is not supported for xAI image edits; provide an image_url instead");
|
|
3682
3709
|
throw new XaiImagesRequestError("image must include a url, image_url, or b64_json");
|
|
3683
3710
|
}
|
|
3684
|
-
function detectImageMime(bytes, fileName) {
|
|
3685
|
-
if (bytes.length >= 8 && bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71) return "image/png";
|
|
3686
|
-
if (bytes.length >= 3 && bytes[0] === 255 && bytes[1] === 216) return "image/jpeg";
|
|
3687
|
-
if (bytes.length >= 12 && bytes[0] === 82 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 70 && bytes[8] === 87 && bytes[9] === 69 && bytes[10] === 66 && bytes[11] === 80) return "image/webp";
|
|
3688
|
-
if (bytes.length >= 6 && bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70) return "image/gif";
|
|
3689
|
-
const extension = fileName?.split(".").pop()?.toLowerCase();
|
|
3690
|
-
if (extension === "jpg" || extension === "jpeg") return "image/jpeg";
|
|
3691
|
-
if (extension === "webp") return "image/webp";
|
|
3692
|
-
if (extension === "gif") return "image/gif";
|
|
3693
|
-
return "image/png";
|
|
3694
|
-
}
|
|
3695
|
-
async function blobToImageReference(blob) {
|
|
3696
|
-
const bytes = new Uint8Array(await blob.arrayBuffer());
|
|
3697
|
-
if (bytes.byteLength === 0) throw new XaiImagesRequestError("image is empty");
|
|
3698
|
-
const fileName = blob instanceof File ? blob.name : void 0;
|
|
3699
|
-
const declared = blob.type.trim().toLowerCase();
|
|
3700
|
-
return {
|
|
3701
|
-
type: "image_url",
|
|
3702
|
-
url: `data:${declared.startsWith("image/") ? declared : detectImageMime(bytes, fileName)};base64,${Buffer.from(bytes).toString("base64")}`
|
|
3703
|
-
};
|
|
3704
|
-
}
|
|
3705
|
-
async function formValueToImageReference(value) {
|
|
3706
|
-
if (typeof value === "string") return readImageReference(value);
|
|
3707
|
-
if (!(value instanceof Blob)) throw new XaiImagesRequestError("image must be a file or a URL");
|
|
3708
|
-
return await blobToImageReference(value);
|
|
3709
|
-
}
|
|
3710
3711
|
function collectJsonImages(payload) {
|
|
3711
3712
|
const values = [];
|
|
3712
3713
|
if (isUnknownArray(payload.image)) values.push(...payload.image);
|
|
@@ -3731,24 +3732,6 @@ function buildEditPayload(model, prompt, images, source) {
|
|
|
3731
3732
|
else payload.images = images;
|
|
3732
3733
|
return payload;
|
|
3733
3734
|
}
|
|
3734
|
-
function formOptions(formData) {
|
|
3735
|
-
const source = {};
|
|
3736
|
-
for (const key of [
|
|
3737
|
-
"n",
|
|
3738
|
-
"quality",
|
|
3739
|
-
"response_format",
|
|
3740
|
-
"aspect_ratio",
|
|
3741
|
-
"resolution",
|
|
3742
|
-
"size",
|
|
3743
|
-
"stream",
|
|
3744
|
-
"mask"
|
|
3745
|
-
]) {
|
|
3746
|
-
const value = formData.get(key);
|
|
3747
|
-
if (typeof value === "string") source[key] = value;
|
|
3748
|
-
}
|
|
3749
|
-
if (formData.get("mask") !== null || formData.get("mask[]") !== null) source.mask = true;
|
|
3750
|
-
return source;
|
|
3751
|
-
}
|
|
3752
3735
|
/**
|
|
3753
3736
|
* Translates an OpenAI image generation body into an xAI Imagine generation
|
|
3754
3737
|
* request. The configured model replaces the client model.
|
|
@@ -3769,19 +3752,6 @@ function translateXaiImageEditJson(payload, model) {
|
|
|
3769
3752
|
if (!isRecord$3(payload)) throw new XaiImagesRequestError("Image edit body must be a JSON object");
|
|
3770
3753
|
return buildEditPayload(model, readPrompt(payload.prompt), collectJsonImages(payload).map((image) => readImageReference(image)), payload);
|
|
3771
3754
|
}
|
|
3772
|
-
/**
|
|
3773
|
-
* Translates an OpenAI multipart image edit into an xAI Imagine edit request.
|
|
3774
|
-
* File parts become base64 data URLs. A mask cannot be represented and is
|
|
3775
|
-
* rejected.
|
|
3776
|
-
*/
|
|
3777
|
-
async function translateXaiImageEditForm(formData, model) {
|
|
3778
|
-
const images = [];
|
|
3779
|
-
for (const [name, value] of formData.entries()) {
|
|
3780
|
-
if (!IMAGE_FIELD_NAMES.has(name)) continue;
|
|
3781
|
-
images.push(await formValueToImageReference(value));
|
|
3782
|
-
}
|
|
3783
|
-
return buildEditPayload(model, readPrompt(formData.get("prompt")), images, formOptions(formData));
|
|
3784
|
-
}
|
|
3785
3755
|
const MAX_RESPONSE_IMAGE_BYTES = 33554432;
|
|
3786
3756
|
function responseHasB64Json(value) {
|
|
3787
3757
|
return typeof value === "string" && value.trim() !== "";
|
|
@@ -3953,12 +3923,12 @@ async function forwardXaiImages(providerConfig, request, operation, payload) {
|
|
|
3953
3923
|
method: "POST",
|
|
3954
3924
|
signal: request.signal
|
|
3955
3925
|
}), operation, {
|
|
3956
|
-
logger: logger$
|
|
3926
|
+
logger: logger$15,
|
|
3957
3927
|
provider: "xai"
|
|
3958
3928
|
}), void 0, {
|
|
3959
3929
|
clientSignal: request.signal,
|
|
3960
3930
|
onImageUrl: (url) => {
|
|
3961
|
-
logger$
|
|
3931
|
+
logger$15.info("images.xai.output.url", JSON.stringify({
|
|
3962
3932
|
operation,
|
|
3963
3933
|
url
|
|
3964
3934
|
}));
|
|
@@ -3969,285 +3939,11 @@ async function forwardXaiImages(providerConfig, request, operation, payload) {
|
|
|
3969
3939
|
async function handleXaiImageGeneration(c, model) {
|
|
3970
3940
|
return await forwardTranslatedXaiImages(c, "generations", translateXaiImageGeneration(await readJsonBody(c.req.raw, "Image generation body must be JSON"), model));
|
|
3971
3941
|
}
|
|
3972
|
-
async function handleXaiImageEdit(c, model
|
|
3973
|
-
|
|
3974
|
-
const { staged } = parsed;
|
|
3975
|
-
let payload;
|
|
3976
|
-
try {
|
|
3977
|
-
payload = await translateXaiImageEditForm(staged.formData, model);
|
|
3978
|
-
} finally {
|
|
3979
|
-
await staged.cleanup();
|
|
3980
|
-
}
|
|
3981
|
-
return await forwardTranslatedXaiImages(c, "edits", payload);
|
|
3982
|
-
}
|
|
3983
|
-
//#endregion
|
|
3984
|
-
//#region src/routes/images/temp-form-data.ts
|
|
3985
|
-
const logger$15 = createHandlerLogger("images-temp-form-data");
|
|
3986
|
-
const multipartStagingDependencies = { createWriteStream };
|
|
3987
|
-
/**
|
|
3988
|
-
* Matches the images forwarding timeouts: an upstream upload may take that
|
|
3989
|
-
* long, so staged files must survive at least the whole forwarding window.
|
|
3990
|
-
*/
|
|
3991
|
-
const STAGED_FILES_TTL_MS = 9e5;
|
|
3992
|
-
const DEFAULT_MULTIPART_STAGING_LIMITS = {
|
|
3993
|
-
maxBodySizeBytes: 134217728,
|
|
3994
|
-
maxFieldSizeBytes: 10485760,
|
|
3995
|
-
maxFields: 64,
|
|
3996
|
-
maxFileSizeBytes: 67108864,
|
|
3997
|
-
maxFiles: 16,
|
|
3998
|
-
maxParts: 80
|
|
3999
|
-
};
|
|
4000
|
-
var InvalidMultipartBodyError = class extends Error {
|
|
4001
|
-
constructor(options) {
|
|
4002
|
-
super("Invalid multipart form data body", options);
|
|
4003
|
-
this.name = "InvalidMultipartBodyError";
|
|
4004
|
-
}
|
|
4005
|
-
};
|
|
4006
|
-
var MultipartBodyTooLargeError = class extends Error {
|
|
4007
|
-
constructor() {
|
|
4008
|
-
super("Multipart form data body exceeds the configured upload limits");
|
|
4009
|
-
this.name = "MultipartBodyTooLargeError";
|
|
4010
|
-
}
|
|
4011
|
-
};
|
|
4012
|
-
function sanitizeFileName(name) {
|
|
4013
|
-
return name.split(/[\\/]/).pop()?.replaceAll("\0", "") || "file";
|
|
4014
|
-
}
|
|
4015
|
-
async function createDiskBackedBlob(filePath, type) {
|
|
4016
|
-
if (typeof Bun !== "undefined") return Bun.file(filePath, { type });
|
|
4017
|
-
const { openAsBlob } = await import("node:fs");
|
|
4018
|
-
return await openAsBlob(filePath, { type });
|
|
4019
|
-
}
|
|
4020
|
-
function toError(error) {
|
|
4021
|
-
return error instanceof Error ? error : new Error(String(error));
|
|
4022
|
-
}
|
|
4023
|
-
function parseMultipartBody(body, contentType, directory, entries, limits) {
|
|
4024
|
-
return new Promise((resolve, reject) => {
|
|
4025
|
-
let busboy;
|
|
4026
|
-
try {
|
|
4027
|
-
busboy = new Busboy({
|
|
4028
|
-
headers: { "content-type": contentType },
|
|
4029
|
-
limits: {
|
|
4030
|
-
fieldSize: limits.maxFieldSizeBytes,
|
|
4031
|
-
fields: limits.maxFields,
|
|
4032
|
-
fileSize: limits.maxFileSizeBytes,
|
|
4033
|
-
files: limits.maxFiles,
|
|
4034
|
-
parts: limits.maxParts
|
|
4035
|
-
}
|
|
4036
|
-
});
|
|
4037
|
-
} catch (error) {
|
|
4038
|
-
reject(new InvalidMultipartBodyError({ cause: toError(error) }));
|
|
4039
|
-
return;
|
|
4040
|
-
}
|
|
4041
|
-
const pendingWrites = [];
|
|
4042
|
-
const activeFileStreams = /* @__PURE__ */ new Set();
|
|
4043
|
-
const source = body === null ? Readable.from([]) : Readable.fromWeb(body);
|
|
4044
|
-
let bodySizeBytes = 0;
|
|
4045
|
-
const bodyLimiter = new Transform({ transform(chunk, _encoding, callback) {
|
|
4046
|
-
bodySizeBytes += chunk.byteLength;
|
|
4047
|
-
if (bodySizeBytes > limits.maxBodySizeBytes) {
|
|
4048
|
-
callback(new MultipartBodyTooLargeError());
|
|
4049
|
-
return;
|
|
4050
|
-
}
|
|
4051
|
-
callback(null, chunk);
|
|
4052
|
-
} });
|
|
4053
|
-
let fileIndex = 0;
|
|
4054
|
-
let failure = null;
|
|
4055
|
-
let inputPipeline = null;
|
|
4056
|
-
let settled = false;
|
|
4057
|
-
const settleStreams = () => Promise.allSettled([...inputPipeline ? [inputPipeline] : [], ...pendingWrites]);
|
|
4058
|
-
const fail = (error) => {
|
|
4059
|
-
if (failure || settled) return;
|
|
4060
|
-
const failureError = toError(error);
|
|
4061
|
-
failure = failureError;
|
|
4062
|
-
source.destroy(failureError);
|
|
4063
|
-
bodyLimiter.destroy(failureError);
|
|
4064
|
-
busboy.destroy(failureError);
|
|
4065
|
-
for (const stream of activeFileStreams) stream.destroy(failureError);
|
|
4066
|
-
settleStreams().then(() => {
|
|
4067
|
-
if (settled) return;
|
|
4068
|
-
settled = true;
|
|
4069
|
-
reject(failureError);
|
|
4070
|
-
});
|
|
4071
|
-
};
|
|
4072
|
-
const failLimit = () => fail(new MultipartBodyTooLargeError());
|
|
4073
|
-
busboy.on("field", (name, value, nameTruncated, valueTruncated) => {
|
|
4074
|
-
if (nameTruncated || valueTruncated) {
|
|
4075
|
-
failLimit();
|
|
4076
|
-
return;
|
|
4077
|
-
}
|
|
4078
|
-
entries.push({
|
|
4079
|
-
kind: "field",
|
|
4080
|
-
name,
|
|
4081
|
-
value
|
|
4082
|
-
});
|
|
4083
|
-
});
|
|
4084
|
-
busboy.on("file", (name, stream, fileName, _transferEncoding, mimeType) => {
|
|
4085
|
-
if (failure) {
|
|
4086
|
-
stream.resume();
|
|
4087
|
-
return;
|
|
4088
|
-
}
|
|
4089
|
-
const filePath = join(directory, `${fileIndex}.upload`);
|
|
4090
|
-
fileIndex += 1;
|
|
4091
|
-
entries.push({
|
|
4092
|
-
fileName: sanitizeFileName(fileName),
|
|
4093
|
-
filePath,
|
|
4094
|
-
kind: "file",
|
|
4095
|
-
mimeType,
|
|
4096
|
-
name
|
|
4097
|
-
});
|
|
4098
|
-
activeFileStreams.add(stream);
|
|
4099
|
-
stream.once("close", () => activeFileStreams.delete(stream));
|
|
4100
|
-
stream.once("limit", failLimit);
|
|
4101
|
-
const write = pipeline(stream, multipartStagingDependencies.createWriteStream(filePath));
|
|
4102
|
-
pendingWrites.push(write);
|
|
4103
|
-
write.catch(fail);
|
|
4104
|
-
});
|
|
4105
|
-
busboy.on("fieldsLimit", failLimit);
|
|
4106
|
-
busboy.on("filesLimit", failLimit);
|
|
4107
|
-
busboy.on("partsLimit", failLimit);
|
|
4108
|
-
inputPipeline = pipeline(source, bodyLimiter, busboy);
|
|
4109
|
-
inputPipeline.then(() => {
|
|
4110
|
-
Promise.allSettled(pendingWrites).then((results) => {
|
|
4111
|
-
if (failure || settled) return;
|
|
4112
|
-
const failed = results.find((result) => result.status === "rejected");
|
|
4113
|
-
if (failed) fail(failed.reason);
|
|
4114
|
-
else {
|
|
4115
|
-
settled = true;
|
|
4116
|
-
resolve();
|
|
4117
|
-
}
|
|
4118
|
-
});
|
|
4119
|
-
}, (error) => {
|
|
4120
|
-
fail(error instanceof MultipartBodyTooLargeError ? error : new InvalidMultipartBodyError({ cause: toError(error) }));
|
|
4121
|
-
});
|
|
4122
|
-
});
|
|
4123
|
-
}
|
|
4124
|
-
function resolveStagingLimits(overrides) {
|
|
4125
|
-
const limits = {
|
|
4126
|
-
...DEFAULT_MULTIPART_STAGING_LIMITS,
|
|
4127
|
-
...overrides
|
|
4128
|
-
};
|
|
4129
|
-
for (const [name, value] of Object.entries(limits)) if (!Number.isSafeInteger(value) || value <= 0) throw new RangeError(`${name} must be a positive safe integer`);
|
|
4130
|
-
return limits;
|
|
4131
|
-
}
|
|
4132
|
-
/**
|
|
4133
|
-
* Streams a multipart request body through the parser, writing every file
|
|
4134
|
-
* part to a temporary directory as it arrives and rebuilding the form data
|
|
4135
|
-
* with disk-backed blobs, so forwarding streams payloads from disk instead
|
|
4136
|
-
* of pinning the whole upload in memory.
|
|
4137
|
-
*/
|
|
4138
|
-
async function stageMultipartBodyToDisk(body, contentType, limitOverrides = {}) {
|
|
4139
|
-
const limits = resolveStagingLimits(limitOverrides);
|
|
4140
|
-
const directory = await mkdtemp(join(tmpdir(), "copilot-api-images-"));
|
|
4141
|
-
const entries = [];
|
|
4142
|
-
try {
|
|
4143
|
-
await parseMultipartBody(body, contentType, directory, entries, limits);
|
|
4144
|
-
} catch (error) {
|
|
4145
|
-
await rm(directory, {
|
|
4146
|
-
force: true,
|
|
4147
|
-
recursive: true
|
|
4148
|
-
});
|
|
4149
|
-
throw error;
|
|
4150
|
-
}
|
|
4151
|
-
const stagedFormData = new FormData();
|
|
4152
|
-
try {
|
|
4153
|
-
for (const entry of entries) if (entry.kind === "field") stagedFormData.append(entry.name, entry.value);
|
|
4154
|
-
else stagedFormData.append(entry.name, await createDiskBackedBlob(entry.filePath, entry.mimeType), entry.fileName);
|
|
4155
|
-
} catch (error) {
|
|
4156
|
-
await rm(directory, {
|
|
4157
|
-
force: true,
|
|
4158
|
-
recursive: true
|
|
4159
|
-
});
|
|
4160
|
-
throw error;
|
|
4161
|
-
}
|
|
4162
|
-
let cleanupPromise = null;
|
|
4163
|
-
let cleanupTimer = null;
|
|
4164
|
-
let unregisterProcessCleanup = null;
|
|
4165
|
-
const cleanup = () => {
|
|
4166
|
-
cleanupPromise ??= (async () => {
|
|
4167
|
-
if (cleanupTimer) {
|
|
4168
|
-
clearTimeout(cleanupTimer);
|
|
4169
|
-
cleanupTimer = null;
|
|
4170
|
-
}
|
|
4171
|
-
try {
|
|
4172
|
-
await rm(directory, {
|
|
4173
|
-
force: true,
|
|
4174
|
-
recursive: true
|
|
4175
|
-
});
|
|
4176
|
-
} catch (error) {
|
|
4177
|
-
logger$15.warn("Failed to remove staged images temp directory", {
|
|
4178
|
-
directory,
|
|
4179
|
-
error
|
|
4180
|
-
});
|
|
4181
|
-
} finally {
|
|
4182
|
-
unregisterProcessCleanup?.();
|
|
4183
|
-
unregisterProcessCleanup = null;
|
|
4184
|
-
}
|
|
4185
|
-
})();
|
|
4186
|
-
return cleanupPromise;
|
|
4187
|
-
};
|
|
4188
|
-
unregisterProcessCleanup = registerProcessCleanup(() => {
|
|
4189
|
-
if (cleanupTimer) {
|
|
4190
|
-
clearTimeout(cleanupTimer);
|
|
4191
|
-
cleanupTimer = null;
|
|
4192
|
-
}
|
|
4193
|
-
try {
|
|
4194
|
-
rmSync(directory, {
|
|
4195
|
-
force: true,
|
|
4196
|
-
recursive: true
|
|
4197
|
-
});
|
|
4198
|
-
} catch (error) {
|
|
4199
|
-
logger$15.warn("Failed to remove staged images temp directory on exit", {
|
|
4200
|
-
directory,
|
|
4201
|
-
error
|
|
4202
|
-
});
|
|
4203
|
-
} finally {
|
|
4204
|
-
unregisterProcessCleanup?.();
|
|
4205
|
-
unregisterProcessCleanup = null;
|
|
4206
|
-
}
|
|
4207
|
-
});
|
|
4208
|
-
return {
|
|
4209
|
-
directory,
|
|
4210
|
-
formData: stagedFormData,
|
|
4211
|
-
cleanup,
|
|
4212
|
-
scheduleCleanup: () => {
|
|
4213
|
-
if (cleanupPromise || cleanupTimer) return;
|
|
4214
|
-
cleanupTimer = setTimeout(() => void cleanup(), STAGED_FILES_TTL_MS);
|
|
4215
|
-
cleanupTimer.unref();
|
|
4216
|
-
}
|
|
4217
|
-
};
|
|
3942
|
+
async function handleXaiImageEdit(c, model) {
|
|
3943
|
+
return await forwardTranslatedXaiImages(c, "edits", translateXaiImageEditJson(await readJsonBody(c.req.raw, "Image edit body must be JSON"), model));
|
|
4218
3944
|
}
|
|
4219
3945
|
//#endregion
|
|
4220
3946
|
//#region src/routes/images/edits-handler.ts
|
|
4221
|
-
const imageEditsRouteDependencies = { stageMultipartBodyToDisk };
|
|
4222
|
-
function createStagedFormDataRequest(request, requestHeaders, formData) {
|
|
4223
|
-
const headers = new Headers(requestHeaders);
|
|
4224
|
-
headers.delete("content-length");
|
|
4225
|
-
headers.delete("content-type");
|
|
4226
|
-
return createForwardRequest(request, headers, formData);
|
|
4227
|
-
}
|
|
4228
|
-
function createMultipartImagesRequest(request, requestHeaders, formData, model) {
|
|
4229
|
-
formData.set("model", model);
|
|
4230
|
-
return createStagedFormDataRequest(request, requestHeaders, formData);
|
|
4231
|
-
}
|
|
4232
|
-
/**
|
|
4233
|
-
* Streams an edits multipart body to disk, staging uploaded files so
|
|
4234
|
-
* forwarding streams them from the filesystem instead of pinning them in
|
|
4235
|
-
* memory. Non-multipart bodies come back as an untouched request stream;
|
|
4236
|
-
* malformed multipart bodies throw a typed error because the consumed stream
|
|
4237
|
-
* can no longer be forwarded.
|
|
4238
|
-
*/
|
|
4239
|
-
async function parseEditsRequest(request) {
|
|
4240
|
-
const requestHeaders = snapshotRequestHeaders(request);
|
|
4241
|
-
const contentType = requestHeaders.get("content-type");
|
|
4242
|
-
if (contentType?.split(";", 1)[0]?.trim().toLowerCase() !== "multipart/form-data" || !contentType) return createForwardRequest(request, requestHeaders, request.body);
|
|
4243
|
-
const staged = await imageEditsRouteDependencies.stageMultipartBodyToDisk(request.body, contentType);
|
|
4244
|
-
const model = staged.formData.get("model");
|
|
4245
|
-
return {
|
|
4246
|
-
model: typeof model === "string" ? model : void 0,
|
|
4247
|
-
requestHeaders,
|
|
4248
|
-
staged
|
|
4249
|
-
};
|
|
4250
|
-
}
|
|
4251
3947
|
async function handleImagesEdits(c) {
|
|
4252
3948
|
try {
|
|
4253
3949
|
const configuredModel = imageRouteDependencies.getImageEditModel();
|
|
@@ -4255,66 +3951,21 @@ async function handleImagesEdits(c) {
|
|
|
4255
3951
|
message: configuredModel.message,
|
|
4256
3952
|
type: "invalid_request_error"
|
|
4257
3953
|
} }, 400);
|
|
4258
|
-
const
|
|
4259
|
-
if (
|
|
3954
|
+
const contentTypeError = imageEditContentTypeErrorResponse(c);
|
|
3955
|
+
if (contentTypeError) return contentTypeError;
|
|
3956
|
+
if (configuredModel.status === "xai") return await handleXaiImageEdit(c, configuredModel.model);
|
|
3957
|
+
const parsed = await parseJsonImagesRequest(c.req.raw);
|
|
4260
3958
|
if (parsed instanceof Request) return await handleCodexImages(c, "edits", void 0, parsed);
|
|
4261
|
-
|
|
4262
|
-
try {
|
|
4263
|
-
const response = model === void 0 ? await handleCodexImages(c, "edits", void 0, createStagedFormDataRequest(c.req.raw, requestHeaders, staged.formData)) : await routeImagesRequest(c, "edits", {
|
|
4264
|
-
createRequest: (mappedModel) => createMultipartImagesRequest(c.req.raw, requestHeaders, staged.formData, mappedModel),
|
|
4265
|
-
model
|
|
4266
|
-
});
|
|
4267
|
-
staged.scheduleCleanup();
|
|
4268
|
-
return response;
|
|
4269
|
-
} catch (error) {
|
|
4270
|
-
await staged.cleanup();
|
|
4271
|
-
throw error;
|
|
4272
|
-
}
|
|
3959
|
+
return await routeImagesRequest(c, "edits", parsed);
|
|
4273
3960
|
} catch (error) {
|
|
4274
|
-
if (error instanceof InvalidMultipartBodyError || error instanceof MultipartBodyTooLargeError) return c.json({ error: {
|
|
4275
|
-
message: error.message,
|
|
4276
|
-
type: "invalid_request_error"
|
|
4277
|
-
} }, error instanceof MultipartBodyTooLargeError ? 413 : 400);
|
|
4278
3961
|
const invalidRequest = xaiImagesRequestErrorResponse(c, error);
|
|
4279
3962
|
if (invalidRequest) return invalidRequest;
|
|
4280
|
-
logger$
|
|
3963
|
+
logger$15.error("images.edits.error", { error });
|
|
4281
3964
|
return await forwardError(c, error);
|
|
4282
3965
|
}
|
|
4283
3966
|
}
|
|
4284
3967
|
//#endregion
|
|
4285
3968
|
//#region src/routes/images/generations-handler.ts
|
|
4286
|
-
function createJsonImagesRequest(request, requestHeaders, payload, model) {
|
|
4287
|
-
const headers = new Headers(requestHeaders);
|
|
4288
|
-
headers.delete("content-length");
|
|
4289
|
-
return createForwardRequest(request, headers, new TextEncoder().encode(JSON.stringify({
|
|
4290
|
-
...payload,
|
|
4291
|
-
model
|
|
4292
|
-
})));
|
|
4293
|
-
}
|
|
4294
|
-
/**
|
|
4295
|
-
* Parses a generations request body as JSON. Generation payloads are small
|
|
4296
|
-
* JSON documents, so one byte copy covers both routing on the model field
|
|
4297
|
-
* and forwarding an unchanged body to Codex. Rebuilt bodies stay binary so
|
|
4298
|
-
* Node does not synthesize a text/plain content type.
|
|
4299
|
-
*/
|
|
4300
|
-
async function parseGenerationsRequest(request) {
|
|
4301
|
-
const requestHeaders = snapshotRequestHeaders(request);
|
|
4302
|
-
const body = new Uint8Array(await request.arrayBuffer());
|
|
4303
|
-
const originalRequest = createForwardRequest(request, requestHeaders, body);
|
|
4304
|
-
let payload;
|
|
4305
|
-
try {
|
|
4306
|
-
const bodyText = new TextDecoder("utf-8", { fatal: true }).decode(body);
|
|
4307
|
-
payload = JSON.parse(bodyText);
|
|
4308
|
-
} catch {
|
|
4309
|
-
return originalRequest;
|
|
4310
|
-
}
|
|
4311
|
-
if (payload === null || typeof payload !== "object" || Array.isArray(payload) || typeof payload.model !== "string") return originalRequest;
|
|
4312
|
-
return {
|
|
4313
|
-
createRequest: (mappedModel) => createJsonImagesRequest(request, requestHeaders, payload, mappedModel),
|
|
4314
|
-
model: payload.model,
|
|
4315
|
-
originalRequest
|
|
4316
|
-
};
|
|
4317
|
-
}
|
|
4318
3969
|
async function handleImagesGenerations(c) {
|
|
4319
3970
|
try {
|
|
4320
3971
|
const configuredModel = imageRouteDependencies.getImageGenerationModel();
|
|
@@ -4323,13 +3974,13 @@ async function handleImagesGenerations(c) {
|
|
|
4323
3974
|
type: "invalid_request_error"
|
|
4324
3975
|
} }, 400);
|
|
4325
3976
|
if (configuredModel.status === "xai") return await handleXaiImageGeneration(c, configuredModel.model);
|
|
4326
|
-
const parsed = await
|
|
3977
|
+
const parsed = await parseJsonImagesRequest(c.req.raw);
|
|
4327
3978
|
if (parsed instanceof Request) return await handleCodexImages(c, "generations", void 0, parsed);
|
|
4328
3979
|
return await routeImagesRequest(c, "generations", parsed);
|
|
4329
3980
|
} catch (error) {
|
|
4330
3981
|
const invalidRequest = xaiImagesRequestErrorResponse(c, error);
|
|
4331
3982
|
if (invalidRequest) return invalidRequest;
|
|
4332
|
-
logger$
|
|
3983
|
+
logger$15.error("images.generations.error", { error });
|
|
4333
3984
|
return await forwardError(c, error);
|
|
4334
3985
|
}
|
|
4335
3986
|
}
|
|
@@ -12619,7 +12270,13 @@ async function handleProviderImages(c, operation) {
|
|
|
12619
12270
|
message: `Provider '${provider}' not found or disabled`,
|
|
12620
12271
|
type: "invalid_request_error"
|
|
12621
12272
|
} }, 404);
|
|
12622
|
-
if (providerConfig.name === "codex")
|
|
12273
|
+
if (providerConfig.name === "codex") {
|
|
12274
|
+
if (operation === "edits") {
|
|
12275
|
+
const contentTypeError = imageEditContentTypeErrorResponse(c);
|
|
12276
|
+
if (contentTypeError) return contentTypeError;
|
|
12277
|
+
}
|
|
12278
|
+
return await handleCodexImages(c, operation, providerConfig);
|
|
12279
|
+
}
|
|
12623
12280
|
return await forwardProviderImagesWithLogging(providerConfig, c.req.raw, operation, {
|
|
12624
12281
|
logger: logger$6,
|
|
12625
12282
|
provider
|
|
@@ -14305,4 +13962,4 @@ createServer();
|
|
|
14305
13962
|
//#endregion
|
|
14306
13963
|
export { createServer };
|
|
14307
13964
|
|
|
14308
|
-
//# sourceMappingURL=server-
|
|
13965
|
+
//# sourceMappingURL=server-BE1P5ljq.js.map
|