@bitkyc08/opencodex 2.39.0 → 2.40.0
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/README.md +49 -21
- package/gui/dist/assets/index-BHe2rl_C.js +112 -0
- package/gui/dist/assets/index-CJSb3HPe.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/cursor/catalog.ts +140 -7
- package/src/adapters/cursor/discovery.ts +79 -87
- package/src/adapters/cursor/effort-map.ts +11 -0
- package/src/adapters/cursor/request-builder.ts +31 -4
- package/src/adapters/cursor.ts +17 -0
- package/src/adapters/openai-chat.ts +95 -9
- package/src/adapters/openai-responses.ts +73 -9
- package/src/bridge.ts +58 -13
- package/src/claude/auth-mode.ts +6 -9
- package/src/claude/gateway-cache.ts +23 -3
- package/src/claude/model-info.ts +13 -2
- package/src/cli/access.ts +30 -0
- package/src/cli/account-extended.ts +19 -10
- package/src/cli/capabilities.ts +40 -1
- package/src/cli/claude-agent-startup-sync.ts +3 -0
- package/src/cli/claude.ts +134 -36
- package/src/cli/connect.ts +226 -0
- package/src/cli/dispatch.ts +126 -21
- package/src/cli/gui-pair-client.ts +170 -0
- package/src/cli/gui.ts +87 -0
- package/src/cli/help.ts +4 -1
- package/src/cli/index.ts +76 -22
- package/src/cli/models-runtime-subcommands.ts +34 -0
- package/src/cli/models-runtime.ts +4 -0
- package/src/cli/models.ts +2 -1
- package/src/cli/opencode.ts +2 -1
- package/src/cli/provider-runtime.ts +31 -1
- package/src/cli/registry.ts +32 -2
- package/src/cli/runtime-api.ts +10 -1
- package/src/cli/status.ts +26 -0
- package/src/cli/system-command.ts +9 -3
- package/src/client/connect.ts +650 -0
- package/src/client/hub-client.ts +457 -0
- package/src/client/hub-relay.ts +288 -0
- package/src/client/machine-api.ts +139 -0
- package/src/client/machine-auth.ts +54 -0
- package/src/client/machine-listener.ts +143 -0
- package/src/client/runtime.ts +93 -0
- package/src/client/state.ts +175 -0
- package/src/clients/config-export.ts +16 -5
- package/src/codex/auth-api.ts +169 -15
- package/src/codex/auth-context.ts +61 -6
- package/src/codex/catalog/aggregation.ts +17 -13
- package/src/codex/catalog/metadata.ts +28 -0
- package/src/codex/catalog/provider-fetch.ts +38 -5
- package/src/codex/catalog.ts +1 -1
- package/src/codex/desired-state.ts +13 -3
- package/src/codex/inject.ts +212 -31
- package/src/codex/journal.ts +54 -6
- package/src/codex/main-account.ts +105 -33
- package/src/codex/native-main-claim.ts +23 -1
- package/src/codex/native-residue.ts +7 -0
- package/src/codex/reset-credit-auto-redeem.ts +237 -0
- package/src/combos/request.ts +15 -3
- package/src/combos/types.ts +12 -0
- package/src/config/paths.ts +15 -1
- package/src/config/provider-validation.ts +40 -0
- package/src/config.ts +447 -3
- package/src/generated/compatibility-version.json +224 -108
- package/src/images/artifacts.ts +73 -37
- package/src/images/fulfill.ts +6 -1
- package/src/images/index.ts +1 -1
- package/src/images/plan.ts +16 -2
- package/src/images/synthetic-tool.ts +5 -0
- package/src/images/xai-client.ts +34 -1
- package/src/integrations/cursor-detect.ts +133 -0
- package/src/integrations/cursor-seen.ts +31 -0
- package/src/lib/errors.ts +18 -0
- package/src/lib/gui-pair-capability.ts +104 -0
- package/src/lib/lab-live-route-production.ts +2 -1
- package/src/lib/process-control.ts +4 -3
- package/src/lib/service-secrets.ts +161 -1
- package/src/lib/windows-system-proxy.ts +115 -0
- package/src/oauth/index.ts +3 -2
- package/src/oauth/pool-settings-capability.ts +55 -0
- package/src/providers/api-keys.ts +3 -1
- package/src/providers/codex-capacity.ts +34 -4
- package/src/providers/derive.ts +10 -0
- package/src/providers/fastwire.ts +10 -2
- package/src/providers/key-store.ts +197 -0
- package/src/providers/model-rename-migration.ts +3 -0
- package/src/providers/openai-sidecar.ts +3 -3
- package/src/providers/quota.ts +91 -42
- package/src/providers/registry.ts +33 -3
- package/src/reasoning-effort.ts +32 -0
- package/src/remote/protocol.ts +109 -0
- package/src/responses/citation-markers.ts +101 -0
- package/src/responses/parser.ts +43 -3
- package/src/responses/spill-store.ts +9 -4
- package/src/router.ts +72 -8
- package/src/server/auth-cors.ts +37 -1
- package/src/server/gui-session.ts +449 -0
- package/src/server/gui-static.ts +24 -7
- package/src/server/images.ts +237 -10
- package/src/server/index.ts +356 -30
- package/src/server/management/agent-settings-routes.ts +7 -1
- package/src/server/management/api-key-rotation.ts +74 -0
- package/src/server/management/combo-routes.ts +18 -6
- package/src/server/management/config-routes.ts +22 -3
- package/src/server/management/context.ts +5 -1
- package/src/server/management/cursor-integration-routes.ts +98 -0
- package/src/server/management/logs-usage-routes.ts +2 -1
- package/src/server/management/model-routes.ts +79 -1
- package/src/server/management/model-rows.ts +23 -1
- package/src/server/management/oauth-account-routes.ts +135 -2
- package/src/server/management/provider-routes.ts +143 -9
- package/src/server/management/route-registry.ts +10 -0
- package/src/server/management/session-routes.ts +13 -0
- package/src/server/management/system-routes.ts +13 -1
- package/src/server/management-api.ts +8 -3
- package/src/server/management-auth.ts +124 -82
- package/src/server/models-capabilities.ts +124 -0
- package/src/server/ports.ts +7 -0
- package/src/server/proxy-liveness.ts +7 -0
- package/src/server/request-log.ts +11 -5
- package/src/server/responses/agent-task-recovery.ts +1 -0
- package/src/server/responses/codex-auth-error.ts +3 -1
- package/src/server/responses/collaboration.ts +26 -1
- package/src/server/responses/combo-stream-preflight.ts +25 -6
- package/src/server/responses/compact.ts +32 -12
- package/src/server/responses/context-overflow.ts +49 -0
- package/src/server/responses/core.ts +196 -12
- package/src/server/responses/fetch-helpers.ts +2 -1
- package/src/server/responses/outbound-body-guard.ts +110 -0
- package/src/server/responses/ws-upstream.ts +87 -6
- package/src/server/responses-self-named-namespace-scrub.ts +181 -0
- package/src/server/system-env.ts +78 -25
- package/src/service.ts +218 -42
- package/src/types/config.ts +134 -0
- package/src/types/provider.ts +54 -1
- package/src/types/request.ts +2 -0
- package/src/types.ts +5 -0
- package/src/update/job.ts +33 -5
- package/src/usage/cost.ts +32 -1
- package/src/usage/expected-prices.ts +16 -0
- package/src/usage/log.ts +6 -2
- package/src/usage/summary.ts +48 -6
- package/gui/dist/assets/index-D-lchsPw.js +0 -112
- package/gui/dist/assets/index-uvENYLin.css +0 -1
package/src/server/images.ts
CHANGED
|
@@ -37,7 +37,15 @@ import { getValidAccessToken, getOAuthCredentialProjectId } from "../oauth/index
|
|
|
37
37
|
import { safeAntigravityHttpErrorMessage } from "../adapters/google-errors";
|
|
38
38
|
import { sanitizeUpstreamErrorText } from "../adapters/upstream-http-error";
|
|
39
39
|
import { ANTIGRAVITY_REQUEST_UA } from "../adapters/google-antigravity-wire";
|
|
40
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
decodeValidatedImageBase64,
|
|
42
|
+
fetchPublicHttpsImage,
|
|
43
|
+
MAX_ENCODED_BYTES_PER_IMAGE,
|
|
44
|
+
sniffImageExtension,
|
|
45
|
+
type PinnedDownloadFn,
|
|
46
|
+
} from "../images/artifacts";
|
|
47
|
+
import { findXaiProvider, resolveXaiImageAuthToken } from "../images/plan";
|
|
48
|
+
import { callXaiImages } from "../images/xai-client";
|
|
41
49
|
import type { AdmissionLease } from "../lib/admission";
|
|
42
50
|
import { codexAccountSelectionForTurn } from "./lifecycle";
|
|
43
51
|
|
|
@@ -49,7 +57,8 @@ const IMAGES_UPSTREAM_TIMEOUT_MS = 300_000;
|
|
|
49
57
|
/**
|
|
50
58
|
* Cap for the buffered upstream response body (100 MiB). Images responses are JSON documents
|
|
51
59
|
* containing base64-encoded images — typically a few MB. This prevents an oversized or malicious
|
|
52
|
-
* response from exhausting process memory.
|
|
60
|
+
* response from exhausting process memory. The xAI `/v1/images` relay also uses this as the
|
|
61
|
+
* combined decoded-byte and base64-encoded output budget across the whole batch.
|
|
53
62
|
*/
|
|
54
63
|
export const IMAGES_RESPONSE_MAX_BYTES = 100 * 1024 * 1024;
|
|
55
64
|
|
|
@@ -118,6 +127,57 @@ const CCA_BLOCKING_FINISH_REASONS: ReadonlySet<string> = new Set([
|
|
|
118
127
|
"RECITATION",
|
|
119
128
|
]);
|
|
120
129
|
|
|
130
|
+
function decodedBytesFromBase64(encoded: string): number {
|
|
131
|
+
const padding = encoded.endsWith("==") ? 2 : encoded.endsWith("=") ? 1 : 0;
|
|
132
|
+
return Math.max(0, Math.floor((encoded.length * 3) / 4) - padding);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Largest decoded payload whose base64 form still fits in `remainingEncoded`. */
|
|
136
|
+
function remainingRelayDecodedBytes(spentDecoded: number, spentEncoded: number): number {
|
|
137
|
+
const remainingDecoded = IMAGES_RESPONSE_MAX_BYTES - spentDecoded;
|
|
138
|
+
const remainingEncoded = IMAGES_RESPONSE_MAX_BYTES - spentEncoded;
|
|
139
|
+
if (remainingDecoded <= 0 || remainingEncoded < 4) return 0;
|
|
140
|
+
return Math.max(0, Math.min(remainingDecoded, 3 * Math.floor(remainingEncoded / 4)));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function wouldExceedRelayBudget(
|
|
144
|
+
spentDecoded: number,
|
|
145
|
+
spentEncoded: number,
|
|
146
|
+
decodedBytes: number,
|
|
147
|
+
encodedBytes: number,
|
|
148
|
+
): boolean {
|
|
149
|
+
return spentDecoded + decodedBytes > IMAGES_RESPONSE_MAX_BYTES
|
|
150
|
+
|| spentEncoded + encodedBytes > IMAGES_RESPONSE_MAX_BYTES;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function xaiImageOutputTooLarge(): Response {
|
|
154
|
+
return formatErrorResponse(
|
|
155
|
+
502,
|
|
156
|
+
"upstream_error",
|
|
157
|
+
`xAI image generation output too large (exceeded ${IMAGES_RESPONSE_MAX_BYTES} bytes)`,
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function xaiImageDownloadFailed(): Response {
|
|
162
|
+
return formatErrorResponse(502, "upstream_error", "xAI image download failed");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function xaiImageAuthMissing(): Response {
|
|
166
|
+
return formatErrorResponse(
|
|
167
|
+
400,
|
|
168
|
+
"invalid_request_error",
|
|
169
|
+
"xAI Imagine relay is enabled but no usable Grok CLI OAuth token or xAI API key was found. "
|
|
170
|
+
+ "Run `ocx login xai` or set an xAI API key. The request was not forwarded to ChatGPT.",
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Test seam: inject a pinned HTTPS GET so relay tests never open a real socket. */
|
|
175
|
+
let xaiResultPinnedDownload: PinnedDownloadFn | undefined;
|
|
176
|
+
|
|
177
|
+
export function setXaiResultPinnedDownloadForTests(fn: PinnedDownloadFn | undefined): void {
|
|
178
|
+
xaiResultPinnedDownload = fn;
|
|
179
|
+
}
|
|
180
|
+
|
|
121
181
|
/**
|
|
122
182
|
* Race a promise against an abort signal. If the signal aborts first, reject
|
|
123
183
|
* immediately — our code stops awaiting the underlying operation even though
|
|
@@ -372,6 +432,166 @@ async function tryCcaImageGeneration(
|
|
|
372
432
|
}
|
|
373
433
|
}
|
|
374
434
|
|
|
435
|
+
/**
|
|
436
|
+
* Codex's client-side image_gen POSTs here. When the Grok Imagine bridge is
|
|
437
|
+
* opted in, send that request to api.x.ai instead of ChatGPT.
|
|
438
|
+
*/
|
|
439
|
+
async function tryXaiImageRelay(
|
|
440
|
+
body: unknown,
|
|
441
|
+
config: OcxConfig,
|
|
442
|
+
logCtx: RequestLogContext,
|
|
443
|
+
signal: AbortSignal | undefined,
|
|
444
|
+
endpoint: ImagesEndpoint,
|
|
445
|
+
): Promise<Response | undefined> {
|
|
446
|
+
if (config.images?.bridgeEnabled !== true) return undefined;
|
|
447
|
+
const found = findXaiProvider(config);
|
|
448
|
+
if (!found) return undefined;
|
|
449
|
+
const obj = body && typeof body === "object" && !Array.isArray(body)
|
|
450
|
+
? body as Record<string, unknown>
|
|
451
|
+
: {};
|
|
452
|
+
const prompt = typeof obj.prompt === "string" ? obj.prompt
|
|
453
|
+
: typeof obj.input === "string" ? obj.input
|
|
454
|
+
: "";
|
|
455
|
+
if (!prompt.trim()) {
|
|
456
|
+
return formatErrorResponse(400, "invalid_request_error", "image generation requires a prompt");
|
|
457
|
+
}
|
|
458
|
+
const n = typeof obj.n === "number" && Number.isFinite(obj.n) ? Math.max(1, Math.min(4, Math.floor(obj.n))) : 1;
|
|
459
|
+
const size = typeof obj.size === "string" ? obj.size : undefined;
|
|
460
|
+
const quality = typeof obj.quality === "string" ? obj.quality : undefined;
|
|
461
|
+
const aspectRatio = typeof obj.aspect_ratio === "string" ? obj.aspect_ratio : undefined;
|
|
462
|
+
let imageUrl: string | undefined;
|
|
463
|
+
if (endpoint === "edits") {
|
|
464
|
+
const images = obj.images;
|
|
465
|
+
const first = Array.isArray(images) ? images[0] : undefined;
|
|
466
|
+
if (typeof obj.image === "string") imageUrl = obj.image;
|
|
467
|
+
else if (typeof obj.image_url === "string") imageUrl = obj.image_url;
|
|
468
|
+
else if (first && typeof first === "object" && first !== null) {
|
|
469
|
+
const rec = first as Record<string, unknown>;
|
|
470
|
+
if (typeof rec.image_url === "string") imageUrl = rec.image_url;
|
|
471
|
+
else if (typeof rec.url === "string") imageUrl = rec.url;
|
|
472
|
+
}
|
|
473
|
+
if (!imageUrl?.trim()) {
|
|
474
|
+
return formatErrorResponse(400, "invalid_request_error", "image edits require an image URL");
|
|
475
|
+
}
|
|
476
|
+
imageUrl = imageUrl.trim();
|
|
477
|
+
}
|
|
478
|
+
const timeoutMs = config.images?.timeoutMs ?? IMAGES_UPSTREAM_TIMEOUT_MS;
|
|
479
|
+
const linkedSignal = signalWithTimeout(timeoutMs, signal);
|
|
480
|
+
try {
|
|
481
|
+
let token: string | undefined;
|
|
482
|
+
try {
|
|
483
|
+
token = await abortableRace(resolveXaiImageAuthToken(found.provider), linkedSignal.signal);
|
|
484
|
+
} catch {
|
|
485
|
+
if (signal?.aborted) {
|
|
486
|
+
return formatErrorResponse(499, "client_closed_request", `image ${endpoint} request canceled by client`);
|
|
487
|
+
}
|
|
488
|
+
if (linkedSignal.signal.aborted) {
|
|
489
|
+
return formatErrorResponse(504, "upstream_error", `xAI image ${endpoint} timed out during authentication`);
|
|
490
|
+
}
|
|
491
|
+
return xaiImageAuthMissing();
|
|
492
|
+
}
|
|
493
|
+
if (!token) return xaiImageAuthMissing();
|
|
494
|
+
logCtx.provider = "xai";
|
|
495
|
+
logCtx.model = config.images?.bridgeModel ?? "grok-imagine-image-quality";
|
|
496
|
+
const result = await callXaiImages(
|
|
497
|
+
{
|
|
498
|
+
prompt,
|
|
499
|
+
model: logCtx.model,
|
|
500
|
+
n,
|
|
501
|
+
size,
|
|
502
|
+
quality,
|
|
503
|
+
aspectRatio,
|
|
504
|
+
imageUrl,
|
|
505
|
+
},
|
|
506
|
+
{ baseUrl: "https://api.x.ai/v1", token },
|
|
507
|
+
linkedSignal.signal,
|
|
508
|
+
timeoutMs,
|
|
509
|
+
);
|
|
510
|
+
const data: Array<{ b64_json: string }> = [];
|
|
511
|
+
let spentDecoded = 0;
|
|
512
|
+
let spentEncoded = 0;
|
|
513
|
+
for (const img of result.images) {
|
|
514
|
+
if (typeof img.b64_json === "string" && img.b64_json) {
|
|
515
|
+
const encodedBytes = img.b64_json.length;
|
|
516
|
+
if (encodedBytes > MAX_ENCODED_BYTES_PER_IMAGE) {
|
|
517
|
+
return formatErrorResponse(502, "upstream_error", "xAI image payload exceeds per-image size cap");
|
|
518
|
+
}
|
|
519
|
+
try {
|
|
520
|
+
decodeValidatedImageBase64(img.b64_json);
|
|
521
|
+
} catch {
|
|
522
|
+
return formatErrorResponse(502, "upstream_error", "xAI image payload failed base64/magic validation");
|
|
523
|
+
}
|
|
524
|
+
const decodedBytes = decodedBytesFromBase64(img.b64_json);
|
|
525
|
+
if (wouldExceedRelayBudget(spentDecoded, spentEncoded, decodedBytes, encodedBytes)) {
|
|
526
|
+
return xaiImageOutputTooLarge();
|
|
527
|
+
}
|
|
528
|
+
data.push({ b64_json: img.b64_json });
|
|
529
|
+
spentDecoded += decodedBytes;
|
|
530
|
+
spentEncoded += encodedBytes;
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
if (typeof img.url !== "string" || !img.url) continue;
|
|
534
|
+
const remaining = remainingRelayDecodedBytes(spentDecoded, spentEncoded);
|
|
535
|
+
if (remaining <= 0) return xaiImageOutputTooLarge();
|
|
536
|
+
let fetched: Response;
|
|
537
|
+
try {
|
|
538
|
+
fetched = await fetchPublicHttpsImage(img.url, {
|
|
539
|
+
signal: linkedSignal.signal,
|
|
540
|
+
pinnedDownload: xaiResultPinnedDownload,
|
|
541
|
+
maxBytes: remaining,
|
|
542
|
+
});
|
|
543
|
+
} catch (err) {
|
|
544
|
+
if (signal?.aborted || linkedSignal.signal.aborted) throw err;
|
|
545
|
+
return xaiImageDownloadFailed();
|
|
546
|
+
}
|
|
547
|
+
const observed = await readImageResponseBytes(fetched, {
|
|
548
|
+
maxBytes: remaining,
|
|
549
|
+
signal: linkedSignal.signal,
|
|
550
|
+
});
|
|
551
|
+
if (observed.oversized) return xaiImageOutputTooLarge();
|
|
552
|
+
if (observed.bytes.byteLength === 0) continue;
|
|
553
|
+
if (!sniffImageExtension(observed.bytes)) return xaiImageDownloadFailed();
|
|
554
|
+
const decodedBytes = observed.bytes.byteLength;
|
|
555
|
+
const b64 = Buffer.from(observed.bytes).toString("base64");
|
|
556
|
+
if (wouldExceedRelayBudget(spentDecoded, spentEncoded, decodedBytes, b64.length)) {
|
|
557
|
+
return xaiImageOutputTooLarge();
|
|
558
|
+
}
|
|
559
|
+
data.push({ b64_json: b64 });
|
|
560
|
+
spentDecoded += decodedBytes;
|
|
561
|
+
spentEncoded += b64.length;
|
|
562
|
+
}
|
|
563
|
+
if (data.length === 0) {
|
|
564
|
+
return formatErrorResponse(502, "upstream_error", "xAI image generation returned no usable images");
|
|
565
|
+
}
|
|
566
|
+
return new Response(JSON.stringify({ created: Math.floor(Date.now() / 1000), data }), {
|
|
567
|
+
status: 200,
|
|
568
|
+
headers: { "content-type": "application/json" },
|
|
569
|
+
});
|
|
570
|
+
} catch (err) {
|
|
571
|
+
if (signal?.aborted) {
|
|
572
|
+
return formatErrorResponse(499, "client_closed_request", `image ${endpoint} request canceled by client`);
|
|
573
|
+
}
|
|
574
|
+
if (linkedSignal.signal.aborted || (err instanceof Error && err.name === "TimeoutError")) {
|
|
575
|
+
return formatErrorResponse(504, "upstream_error", `xAI image ${endpoint} timed out`);
|
|
576
|
+
}
|
|
577
|
+
const status = typeof err === "object" && err && "status" in err && typeof (err as { status: unknown }).status === "number"
|
|
578
|
+
? (err as { status: number }).status
|
|
579
|
+
: 502;
|
|
580
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
581
|
+
const safeMessage = sanitizeUpstreamErrorText(message).replace(
|
|
582
|
+
/https?:\/\/[^\s"'<>]+/gi,
|
|
583
|
+
"[upstream-url]",
|
|
584
|
+
);
|
|
585
|
+
return formatErrorResponse(
|
|
586
|
+
status >= 400 && status < 600 ? status : 502,
|
|
587
|
+
"upstream_error",
|
|
588
|
+
`xAI image ${endpoint} failed: ${safeMessage}`,
|
|
589
|
+
);
|
|
590
|
+
} finally {
|
|
591
|
+
linkedSignal.cleanup();
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
375
595
|
export async function handleImages(
|
|
376
596
|
req: Request,
|
|
377
597
|
config: OcxConfig,
|
|
@@ -379,7 +599,22 @@ export async function handleImages(
|
|
|
379
599
|
logCtx: RequestLogContext,
|
|
380
600
|
turnAdmissionLease?: AdmissionLease,
|
|
381
601
|
): Promise<Response> {
|
|
602
|
+
let body: unknown;
|
|
603
|
+
try {
|
|
604
|
+
body = await readJsonRequestBody(req);
|
|
605
|
+
} catch (err) {
|
|
606
|
+
return decodeRequestErrorResponse(err, "images");
|
|
607
|
+
}
|
|
608
|
+
const model = (body as { model?: unknown } | null)?.model;
|
|
609
|
+
if (typeof model === "string" && model) logCtx.model = model;
|
|
610
|
+
|
|
382
611
|
const candidates = selectImagesProvider(config);
|
|
612
|
+
// Explicit images.provider owns the route, including its validation errors.
|
|
613
|
+
// Do not divert that selection to the xAI Imagine relay.
|
|
614
|
+
if (config.images?.provider === undefined) {
|
|
615
|
+
const xaiRelay = await tryXaiImageRelay(body, config, logCtx, req.signal, endpoint);
|
|
616
|
+
if (xaiRelay) return xaiRelay;
|
|
617
|
+
}
|
|
383
618
|
if (candidates.error) {
|
|
384
619
|
return formatErrorResponse(400, "invalid_request_error", candidates.error);
|
|
385
620
|
}
|
|
@@ -398,14 +633,6 @@ export async function handleImages(
|
|
|
398
633
|
}
|
|
399
634
|
}
|
|
400
635
|
}
|
|
401
|
-
let body: unknown;
|
|
402
|
-
try {
|
|
403
|
-
body = await readJsonRequestBody(req);
|
|
404
|
-
} catch (err) {
|
|
405
|
-
return decodeRequestErrorResponse(err, "images");
|
|
406
|
-
}
|
|
407
|
-
const model = (body as { model?: unknown } | null)?.model;
|
|
408
|
-
if (typeof model === "string" && model) logCtx.model = model;
|
|
409
636
|
|
|
410
637
|
const canUseOpenAiForward = !skipOpenAiForwardForAdmissionBearer && candidates.forwardCandidates.length > 0;
|
|
411
638
|
|