@linxiraos/pi-ai 1.2.1 → 1.3.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/CHANGELOG.md +83 -0
- package/README.md +4 -2
- package/THIRD-PARTY-NOTICES.txt +55 -5
- package/dist/types/auth/cascade.d.ts +5 -3
- package/dist/types/auth/pool.d.ts +3 -3
- package/dist/types/auth/select.d.ts +5 -1
- package/dist/types/auth/types.d.ts +15 -5
- package/dist/types/auth/usage-cache.d.ts +3 -1
- package/dist/types/auth-gateway/clients.d.ts +67 -0
- package/dist/types/auth-gateway/dispatch.d.ts +28 -10
- package/dist/types/auth-gateway/http.d.ts +2 -0
- package/dist/types/auth-gateway/index.d.ts +1 -0
- package/dist/types/auth-gateway/server.d.ts +9 -4
- package/dist/types/auth-gateway/session-state.d.ts +6 -0
- package/dist/types/auth-gateway/types.d.ts +16 -2
- package/dist/types/dialect/coercion.d.ts +6 -0
- package/dist/types/error/classes.d.ts +1 -1
- package/dist/types/error/flags.d.ts +15 -0
- package/dist/types/error/index.d.ts +1 -0
- package/dist/types/error/network-cause.d.ts +40 -0
- package/dist/types/images/shared.d.ts +26 -2
- package/dist/types/index.d.ts +1 -0
- package/dist/types/judgment/native.d.ts +51 -3
- package/dist/types/judgment/systemone.d.ts +9 -2
- package/dist/types/judgment/text.d.ts +11 -5
- package/dist/types/judgment/types.d.ts +5 -1
- package/dist/types/judgment/wire.d.ts +16 -3
- package/dist/types/provider-session-state.d.ts +2 -2
- package/dist/types/providers/anthropic-images.d.ts +7 -0
- package/dist/types/providers/anthropic-signature.d.ts +12 -2
- package/dist/types/providers/anthropic-state.d.ts +7 -0
- package/dist/types/providers/anthropic.d.ts +21 -6
- package/dist/types/providers/connection-prewarm.d.ts +15 -0
- package/dist/types/providers/cursor/exec-modern.d.ts +1 -1
- package/dist/types/providers/cursor-pi-args.d.ts +13 -0
- package/dist/types/providers/cursor.d.ts +7 -0
- package/dist/types/providers/openai-codex-responses.d.ts +6 -0
- package/dist/types/providers/openai-responses.d.ts +9 -0
- package/dist/types/providers/openai-shared.d.ts +30 -0
- package/dist/types/providers/systemone-server.d.ts +7 -3
- package/dist/types/providers/transform-messages.d.ts +26 -5
- package/dist/types/registry/oauth/google-antigravity.d.ts +1 -1
- package/dist/types/stream.d.ts +0 -2
- package/dist/types/types.d.ts +33 -9
- package/dist/types/utils/dsml-leak.d.ts +4 -2
- package/dist/types/utils/narration.d.ts +9 -0
- package/dist/types/utils/openai-http.d.ts +2 -0
- package/dist/types/utils/request-body.d.ts +11 -0
- package/dist/types/utils/request-compression.d.ts +21 -0
- package/dist/types/utils/tool-call-arguments.d.ts +2 -1
- package/dist/types/utils/transport-fetch.d.ts +8 -0
- package/dist/types/utils.d.ts +1 -1
- package/package.json +7 -7
- package/src/auth/cascade.ts +23 -13
- package/src/auth/pool.ts +3 -3
- package/src/auth/refresh.ts +39 -12
- package/src/auth/select.ts +8 -2
- package/src/auth/types.ts +16 -5
- package/src/auth/usage-cache.ts +23 -7
- package/src/auth/usage.ts +2 -4
- package/src/auth-broker/remote-store.ts +31 -6
- package/src/auth-gateway/clients.ts +318 -0
- package/src/auth-gateway/dispatch.ts +66 -35
- package/src/auth-gateway/http.ts +11 -5
- package/src/auth-gateway/index.ts +1 -0
- package/src/auth-gateway/routes/embeddings.ts +4 -10
- package/src/auth-gateway/routes/images.ts +6 -10
- package/src/auth-gateway/routes/rerank.ts +4 -10
- package/src/auth-gateway/routes/speech.ts +10 -10
- package/src/auth-gateway/routes/systemone.ts +22 -12
- package/src/auth-gateway/routes/transcriptions.ts +4 -2
- package/src/auth-gateway/routes/video.ts +12 -15
- package/src/auth-gateway/server.ts +136 -53
- package/src/auth-gateway/session-state.ts +14 -0
- package/src/auth-gateway/types.ts +16 -2
- package/src/dialect/coercion.ts +16 -0
- package/src/dialect/hermes.ts +7 -11
- package/src/dialect/qwen3.ts +3 -11
- package/src/error/classes.ts +3 -2
- package/src/error/flags.ts +24 -3
- package/src/error/index.ts +1 -0
- package/src/error/network-cause.ts +179 -0
- package/src/images/codex-format-directive.md +3 -0
- package/src/images/google-antigravity.ts +2 -1
- package/src/images/google-generative-ai.ts +4 -2
- package/src/images/openai-hosted.ts +25 -2
- package/src/images/openai-images.ts +11 -6
- package/src/images/shared.ts +77 -3
- package/src/index.ts +1 -0
- package/src/judgment/chat.ts +17 -1
- package/src/judgment/native.ts +182 -12
- package/src/judgment/openai-decisions.ts +27 -6
- package/src/judgment/systemone.ts +131 -10
- package/src/judgment/text.ts +20 -7
- package/src/judgment/types.ts +5 -1
- package/src/judgment/wire.ts +17 -3
- package/src/provider-session-state.ts +2 -2
- package/src/providers/amazon-bedrock.ts +4 -1
- package/src/providers/anthropic-client.ts +1 -1
- package/src/providers/anthropic-images.ts +263 -0
- package/src/providers/anthropic-messages-server.ts +84 -1
- package/src/providers/anthropic-signature.ts +35 -8
- package/src/providers/anthropic-state.ts +13 -0
- package/src/providers/anthropic.ts +479 -397
- package/src/providers/connection-prewarm.ts +78 -0
- package/src/providers/cowork-fetch.ts +5 -1
- package/src/providers/cursor/exec-modern.ts +1 -0
- package/src/providers/cursor-pi-args.ts +19 -0
- package/src/providers/cursor.ts +53 -13
- package/src/providers/devin.ts +10 -4
- package/src/providers/factory-droid/gemini.ts +9 -3
- package/src/providers/google-gemini-cli.ts +11 -1
- package/src/providers/google-shared.ts +2 -6
- package/src/providers/images-server.ts +6 -1
- package/src/providers/ollama.ts +15 -5
- package/src/providers/openai-anthropic-shim.ts +1 -0
- package/src/providers/openai-chat-server.ts +29 -0
- package/src/providers/openai-codex/access-programs.ts +2 -1
- package/src/providers/openai-codex-responses.ts +49 -82
- package/src/providers/openai-completions.ts +117 -19
- package/src/providers/openai-responses.ts +25 -2
- package/src/providers/openai-shared.ts +36 -5
- package/src/providers/pi-native-server.ts +1 -0
- package/src/providers/systemone-server.ts +72 -12
- package/src/providers/transform-messages.ts +95 -51
- package/src/registry/oauth/google-antigravity.ts +1 -1
- package/src/stream.ts +13 -73
- package/src/types.ts +33 -9
- package/src/usage/claude.ts +2 -9
- package/src/usage/github-copilot.ts +26 -4
- package/src/usage/openai-codex.ts +13 -7
- package/src/usage/opencode-go.ts +7 -8
- package/src/usage/xai-oauth.ts +2 -9
- package/src/utils/dsml-leak.ts +30 -14
- package/src/utils/leaked-thinking-stream.ts +10 -0
- package/src/utils/narration.ts +18 -0
- package/src/utils/openai-http.ts +27 -18
- package/src/utils/request-body.ts +27 -0
- package/src/utils/request-compression.ts +202 -0
- package/src/utils/request-debug.ts +23 -9
- package/src/utils/schema/dereference.ts +51 -18
- package/src/utils/schema/draft.ts +10 -2
- package/src/utils/schema/json-schema-validator.ts +4 -10
- package/src/utils/schema/normalize.ts +6 -9
- package/src/utils/tool-call-arguments.ts +2 -1
- package/src/utils/transport-fetch.ts +37 -0
- package/src/utils/validation.ts +26 -16
- package/src/utils.ts +38 -23
package/src/auth-gateway/http.ts
CHANGED
|
@@ -113,13 +113,19 @@ export function timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean {
|
|
|
113
113
|
|
|
114
114
|
const TOKEN_ENCODER = new TextEncoder();
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
/** The token of an `Authorization: Bearer <token>` header, trimmed; `undefined` without one. */
|
|
117
|
+
export function readBearerToken(req: Request): string | undefined {
|
|
118
118
|
const header = req.headers.get("authorization");
|
|
119
|
-
if (!header) return
|
|
119
|
+
if (!header) return undefined;
|
|
120
120
|
const match = header.match(/^Bearer\s+(.+)$/i);
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
return match ? match[1].trim() : undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function isAuthorized(req: Request, tokens: ReadonlySet<string>): boolean {
|
|
125
|
+
if (tokens.size === 0) return true;
|
|
126
|
+
const token = readBearerToken(req);
|
|
127
|
+
if (token === undefined) return false;
|
|
128
|
+
const presented = TOKEN_ENCODER.encode(token);
|
|
123
129
|
// Iterate every allowed token regardless of early hits so the result
|
|
124
130
|
// timing reflects the full set, not the position of the match.
|
|
125
131
|
let ok = false;
|
|
@@ -6,6 +6,7 @@ import { deterministicUuid } from "../../utils/deterministic-id";
|
|
|
6
6
|
import {
|
|
7
7
|
type AuthGatewayRouteOptions,
|
|
8
8
|
buildGatewayApiKeyResolver,
|
|
9
|
+
gatewayRequestLogFields,
|
|
9
10
|
mirrorRequestAbort,
|
|
10
11
|
recordGatewayUsage,
|
|
11
12
|
resolveGatewayApiKey,
|
|
@@ -49,7 +50,7 @@ export async function handleEmbeddings(
|
|
|
49
50
|
|
|
50
51
|
const client = resolveClientIdentity(req.headers);
|
|
51
52
|
const sessionId = deterministicUuid(`embeddings\u0000${model.provider}/${model.id}`);
|
|
52
|
-
const apiKey = await resolveGatewayApiKey(bootOpts
|
|
53
|
+
const apiKey = await resolveGatewayApiKey(bootOpts, model, sessionId, controller.signal, peer);
|
|
53
54
|
if (controller.signal.aborted) return aborted();
|
|
54
55
|
if ("status" in apiKey) {
|
|
55
56
|
return embeddings.formatError(apiKey.status, apiKey.type, apiKey.message);
|
|
@@ -63,19 +64,12 @@ export async function handleEmbeddings(
|
|
|
63
64
|
resolvedModel: model.id,
|
|
64
65
|
stream: false,
|
|
65
66
|
peer,
|
|
67
|
+
...gatewayRequestLogFields(bootOpts, apiKey),
|
|
66
68
|
});
|
|
67
69
|
|
|
68
70
|
try {
|
|
69
71
|
const result = await embed(model, parsed.request, {
|
|
70
|
-
apiKey: buildGatewayApiKeyResolver(
|
|
71
|
-
bootOpts.storage,
|
|
72
|
-
model,
|
|
73
|
-
sessionId,
|
|
74
|
-
apiKey,
|
|
75
|
-
controller.signal,
|
|
76
|
-
"embeddings",
|
|
77
|
-
peer,
|
|
78
|
-
),
|
|
72
|
+
apiKey: buildGatewayApiKeyResolver(bootOpts, model, sessionId, apiKey, controller.signal, "embeddings", peer),
|
|
79
73
|
fetch: bootOpts.fetch,
|
|
80
74
|
signal: controller.signal,
|
|
81
75
|
});
|
|
@@ -2,11 +2,13 @@ import { calculateCost } from "@linxiraos/pi-catalog/models";
|
|
|
2
2
|
import { logger } from "@linxiraos/pi-utils";
|
|
3
3
|
import { classifyGatewayError } from "../../error/gateway";
|
|
4
4
|
import { generateImage } from "../../images";
|
|
5
|
+
import { validateImageInput } from "../../images/shared";
|
|
5
6
|
import * as imagesServer from "../../providers/images-server";
|
|
6
7
|
import { deterministicUuid } from "../../utils/deterministic-id";
|
|
7
8
|
import {
|
|
8
9
|
type AuthGatewayRouteOptions,
|
|
9
10
|
buildGatewayApiKeyResolver,
|
|
11
|
+
gatewayRequestLogFields,
|
|
10
12
|
mirrorRequestAbort,
|
|
11
13
|
recordGatewayUsage,
|
|
12
14
|
resolveGatewayApiKey,
|
|
@@ -49,6 +51,7 @@ async function handleImages(
|
|
|
49
51
|
let parsed: imagesServer.ImagesParsedRequest;
|
|
50
52
|
try {
|
|
51
53
|
parsed = await imagesServer.parseRequest(body, kind);
|
|
54
|
+
for (const image of parsed.request.inputImages ?? []) validateImageInput(image);
|
|
52
55
|
} catch (error) {
|
|
53
56
|
const message = error instanceof Error ? error.message : String(error);
|
|
54
57
|
return imagesServer.formatError(400, "invalid_request_error", message);
|
|
@@ -75,7 +78,7 @@ async function handleImages(
|
|
|
75
78
|
|
|
76
79
|
const client = resolveClientIdentity(req.headers);
|
|
77
80
|
const sessionId = deterministicUuid(`images\u0000${model.provider}/${model.id}`);
|
|
78
|
-
const apiKey = await resolveGatewayApiKey(bootOpts
|
|
81
|
+
const apiKey = await resolveGatewayApiKey(bootOpts, model, sessionId, controller.signal, peer);
|
|
79
82
|
if (controller.signal.aborted) return aborted();
|
|
80
83
|
if ("status" in apiKey) return imagesServer.formatError(apiKey.status, apiKey.type, apiKey.message);
|
|
81
84
|
|
|
@@ -87,19 +90,12 @@ async function handleImages(
|
|
|
87
90
|
resolvedModel: model.id,
|
|
88
91
|
stream: false,
|
|
89
92
|
peer,
|
|
93
|
+
...gatewayRequestLogFields(bootOpts, apiKey),
|
|
90
94
|
});
|
|
91
95
|
|
|
92
96
|
try {
|
|
93
97
|
const result = await generateImage(model, parsed.request, {
|
|
94
|
-
apiKey: buildGatewayApiKeyResolver(
|
|
95
|
-
bootOpts.storage,
|
|
96
|
-
model,
|
|
97
|
-
sessionId,
|
|
98
|
-
apiKey,
|
|
99
|
-
controller.signal,
|
|
100
|
-
"images",
|
|
101
|
-
peer,
|
|
102
|
-
),
|
|
98
|
+
apiKey: buildGatewayApiKeyResolver(bootOpts, model, sessionId, apiKey, controller.signal, "images", peer),
|
|
103
99
|
fetch: bootOpts.fetch,
|
|
104
100
|
signal: controller.signal,
|
|
105
101
|
});
|
|
@@ -6,6 +6,7 @@ import { deterministicUuid } from "../../utils/deterministic-id";
|
|
|
6
6
|
import {
|
|
7
7
|
type AuthGatewayRouteOptions,
|
|
8
8
|
buildGatewayApiKeyResolver,
|
|
9
|
+
gatewayRequestLogFields,
|
|
9
10
|
mirrorRequestAbort,
|
|
10
11
|
recordGatewayUsage,
|
|
11
12
|
resolveGatewayApiKey,
|
|
@@ -39,7 +40,7 @@ export async function handleRerank(bootOpts: AuthGatewayRouteOptions, req: Reque
|
|
|
39
40
|
|
|
40
41
|
const client = resolveClientIdentity(req.headers);
|
|
41
42
|
const sessionId = deterministicUuid(`rerank\u0000${model.provider}/${model.id}`);
|
|
42
|
-
const apiKey = await resolveGatewayApiKey(bootOpts
|
|
43
|
+
const apiKey = await resolveGatewayApiKey(bootOpts, model, sessionId, controller.signal, peer);
|
|
43
44
|
if (controller.signal.aborted) return aborted();
|
|
44
45
|
if ("status" in apiKey) return rerankWire.formatError(apiKey.status, apiKey.type, apiKey.message);
|
|
45
46
|
|
|
@@ -51,19 +52,12 @@ export async function handleRerank(bootOpts: AuthGatewayRouteOptions, req: Reque
|
|
|
51
52
|
resolvedModel: model.id,
|
|
52
53
|
stream: false,
|
|
53
54
|
peer,
|
|
55
|
+
...gatewayRequestLogFields(bootOpts, apiKey),
|
|
54
56
|
});
|
|
55
57
|
|
|
56
58
|
try {
|
|
57
59
|
const result = await rerank(model, parsed.request, {
|
|
58
|
-
apiKey: buildGatewayApiKeyResolver(
|
|
59
|
-
bootOpts.storage,
|
|
60
|
-
model,
|
|
61
|
-
sessionId,
|
|
62
|
-
apiKey,
|
|
63
|
-
controller.signal,
|
|
64
|
-
"rerank",
|
|
65
|
-
peer,
|
|
66
|
-
),
|
|
60
|
+
apiKey: buildGatewayApiKeyResolver(bootOpts, model, sessionId, apiKey, controller.signal, "rerank", peer),
|
|
67
61
|
fetch: bootOpts.fetch,
|
|
68
62
|
signal: controller.signal,
|
|
69
63
|
});
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { logger } from "@linxiraos/pi-utils";
|
|
2
2
|
import { classifyGatewayError } from "../../error/gateway";
|
|
3
|
+
import { ValidationError } from "../../error/validation";
|
|
3
4
|
import * as speechWire from "../../providers/speech-server";
|
|
4
5
|
import { isSpeechApi, synthesizeSpeech } from "../../speech";
|
|
5
6
|
import { deterministicUuid } from "../../utils/deterministic-id";
|
|
6
7
|
import {
|
|
7
8
|
type AuthGatewayRouteOptions,
|
|
8
9
|
buildGatewayApiKeyResolver,
|
|
10
|
+
gatewayRequestLogFields,
|
|
9
11
|
mirrorRequestAbort,
|
|
10
12
|
recordGatewayUsage,
|
|
11
13
|
resolveGatewayApiKey,
|
|
@@ -55,7 +57,7 @@ export async function handleSpeech(bootOpts: AuthGatewayRouteOptions, req: Reque
|
|
|
55
57
|
|
|
56
58
|
const client = resolveClientIdentity(req.headers);
|
|
57
59
|
const sessionId = deterministicUuid(`speech\u0000${model.provider}/${model.id}`);
|
|
58
|
-
const apiKey = await resolveGatewayApiKey(bootOpts
|
|
60
|
+
const apiKey = await resolveGatewayApiKey(bootOpts, model, sessionId, controller.signal, peer);
|
|
59
61
|
if (controller.signal.aborted) return aborted();
|
|
60
62
|
if ("status" in apiKey) return speechWire.formatError(apiKey.status, apiKey.type, apiKey.message);
|
|
61
63
|
|
|
@@ -67,19 +69,12 @@ export async function handleSpeech(bootOpts: AuthGatewayRouteOptions, req: Reque
|
|
|
67
69
|
resolvedModel: model.id,
|
|
68
70
|
stream: false,
|
|
69
71
|
peer,
|
|
72
|
+
...gatewayRequestLogFields(bootOpts, apiKey),
|
|
70
73
|
});
|
|
71
74
|
|
|
72
75
|
try {
|
|
73
76
|
const result = await synthesizeSpeech(model, parsed.request, {
|
|
74
|
-
apiKey: buildGatewayApiKeyResolver(
|
|
75
|
-
bootOpts.storage,
|
|
76
|
-
model,
|
|
77
|
-
sessionId,
|
|
78
|
-
apiKey,
|
|
79
|
-
controller.signal,
|
|
80
|
-
"speech",
|
|
81
|
-
peer,
|
|
82
|
-
),
|
|
77
|
+
apiKey: buildGatewayApiKeyResolver(bootOpts, model, sessionId, apiKey, controller.signal, "speech", peer),
|
|
83
78
|
fetch: bootOpts.fetch,
|
|
84
79
|
signal: controller.signal,
|
|
85
80
|
});
|
|
@@ -94,6 +89,11 @@ export async function handleSpeech(bootOpts: AuthGatewayRouteOptions, req: Reque
|
|
|
94
89
|
return response;
|
|
95
90
|
} catch (error) {
|
|
96
91
|
if (controller.signal.aborted) return aborted();
|
|
92
|
+
// Adapter validation is a client error: the wire schema admits values the
|
|
93
|
+
// adapter cannot serve, and the classifier would bill them as a 502.
|
|
94
|
+
if (error instanceof ValidationError) {
|
|
95
|
+
return speechWire.formatError(400, "invalid_request_error", error.message);
|
|
96
|
+
}
|
|
97
97
|
const classified = classifyGatewayError(error);
|
|
98
98
|
logger.warn("auth-gateway speech failed", { format: "speech", error: classified.message, peer });
|
|
99
99
|
return speechWire.formatError(classified.status, classified.type, classified.message);
|
|
@@ -13,13 +13,15 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { calculateCost } from "@linxiraos/pi-catalog/models";
|
|
15
15
|
import { logger } from "@linxiraos/pi-utils";
|
|
16
|
+
import * as AIError from "../../error";
|
|
16
17
|
import { classifyGatewayError } from "../../error/gateway";
|
|
17
|
-
import { createNativeJudge, isJudgmentApi } from "../../judgment/native";
|
|
18
|
+
import { checkNativeJudgmentImages, createNativeJudge, isJudgmentApi } from "../../judgment/native";
|
|
18
19
|
import * as systemOne from "../../providers/systemone-server";
|
|
19
20
|
import { deterministicUuid } from "../../utils/deterministic-id";
|
|
20
21
|
import {
|
|
21
22
|
type AuthGatewayRouteOptions,
|
|
22
23
|
buildGatewayApiKeyResolver,
|
|
24
|
+
gatewayRequestLogFields,
|
|
23
25
|
mirrorRequestAbort,
|
|
24
26
|
recordGatewayUsage,
|
|
25
27
|
resolveGatewayApiKey,
|
|
@@ -66,26 +68,28 @@ export async function handleSystemOne(
|
|
|
66
68
|
`Model ${parsed.modelId} does not answer System One judgments`,
|
|
67
69
|
);
|
|
68
70
|
}
|
|
71
|
+
// Model capability and dialect caps are known locally: reject before credential lookup or refresh.
|
|
72
|
+
try {
|
|
73
|
+
checkNativeJudgmentImages(model, parsed.request, parsed.imageAssets);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
return systemOne.formatError(
|
|
76
|
+
422,
|
|
77
|
+
"invalid_request_error",
|
|
78
|
+
error instanceof Error ? error.message : String(error),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
69
81
|
const client = resolveClientIdentity(req.headers);
|
|
70
82
|
// Judgments carry no conversation to derive a session from. One sticky
|
|
71
83
|
// session per model keeps credential selection stable across calls and
|
|
72
84
|
// still lets `markUsageLimitReached` hand off to a sibling account.
|
|
73
85
|
const sessionId = deterministicUuid(`systemone\u0000${model.provider}/${model.id}`);
|
|
74
86
|
|
|
75
|
-
const apiKey = await resolveGatewayApiKey(bootOpts
|
|
87
|
+
const apiKey = await resolveGatewayApiKey(bootOpts, model, sessionId, controller.signal, peer);
|
|
76
88
|
if (controller.signal.aborted) return aborted();
|
|
77
89
|
if ("status" in apiKey) return systemOne.formatError(apiKey.status, apiKey.type, apiKey.message);
|
|
78
90
|
|
|
79
91
|
const judge = createNativeJudge(model, {
|
|
80
|
-
apiKey: buildGatewayApiKeyResolver(
|
|
81
|
-
bootOpts.storage,
|
|
82
|
-
model,
|
|
83
|
-
sessionId,
|
|
84
|
-
apiKey,
|
|
85
|
-
controller.signal,
|
|
86
|
-
"systemone",
|
|
87
|
-
peer,
|
|
88
|
-
),
|
|
92
|
+
apiKey: buildGatewayApiKeyResolver(bootOpts, model, sessionId, apiKey, controller.signal, "systemone", peer),
|
|
89
93
|
fetch: bootOpts.fetch,
|
|
90
94
|
});
|
|
91
95
|
|
|
@@ -97,11 +101,13 @@ export async function handleSystemOne(
|
|
|
97
101
|
resolvedModel: model.id,
|
|
98
102
|
stream: false,
|
|
99
103
|
peer,
|
|
104
|
+
...gatewayRequestLogFields(bootOpts, apiKey),
|
|
100
105
|
});
|
|
101
106
|
|
|
102
107
|
try {
|
|
103
108
|
const result = await judge.judge(parsed.request, {
|
|
104
109
|
signal: controller.signal,
|
|
110
|
+
imageAssets: parsed.imageAssets,
|
|
105
111
|
// A split judgment that fails still billed the chunks that succeeded.
|
|
106
112
|
onPartialUsage: usage => {
|
|
107
113
|
if (usage.cost.total === 0) calculateCost(model, usage);
|
|
@@ -117,7 +123,11 @@ export async function handleSystemOne(
|
|
|
117
123
|
return json(200, body, gatewayResponseHeaders(model, { requestId, costUsd: result.usage.cost.total, startedAt }));
|
|
118
124
|
} catch (error) {
|
|
119
125
|
if (controller.signal.aborted) return aborted();
|
|
120
|
-
const
|
|
126
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
127
|
+
const classified =
|
|
128
|
+
error instanceof AIError.ValidationError
|
|
129
|
+
? { status: 422, type: "invalid_request_error", message }
|
|
130
|
+
: classifyGatewayError(error);
|
|
121
131
|
logger.warn("auth-gateway judgment failed", { format: "systemone", error: classified.message, peer });
|
|
122
132
|
return systemOne.formatError(classified.status, classified.type, classified.message);
|
|
123
133
|
}
|
|
@@ -6,6 +6,7 @@ import { deterministicUuid } from "../../utils/deterministic-id";
|
|
|
6
6
|
import {
|
|
7
7
|
type AuthGatewayRouteOptions,
|
|
8
8
|
buildGatewayApiKeyResolver,
|
|
9
|
+
gatewayRequestLogFields,
|
|
9
10
|
mirrorRequestAbort,
|
|
10
11
|
recordGatewayUsage,
|
|
11
12
|
resolveGatewayApiKey,
|
|
@@ -49,7 +50,7 @@ export async function handleTranscriptions(
|
|
|
49
50
|
|
|
50
51
|
const client = resolveClientIdentity(req.headers);
|
|
51
52
|
const sessionId = deterministicUuid(`transcriptions\u0000${model.provider}/${model.id}`);
|
|
52
|
-
const apiKey = await resolveGatewayApiKey(bootOpts
|
|
53
|
+
const apiKey = await resolveGatewayApiKey(bootOpts, model, sessionId, controller.signal, peer);
|
|
53
54
|
if (controller.signal.aborted) return aborted();
|
|
54
55
|
if ("status" in apiKey) {
|
|
55
56
|
return transcriptions.formatError(apiKey.status, apiKey.type, apiKey.message);
|
|
@@ -63,12 +64,13 @@ export async function handleTranscriptions(
|
|
|
63
64
|
resolvedModel: model.id,
|
|
64
65
|
stream: false,
|
|
65
66
|
peer,
|
|
67
|
+
...gatewayRequestLogFields(bootOpts, apiKey),
|
|
66
68
|
});
|
|
67
69
|
|
|
68
70
|
try {
|
|
69
71
|
const result = await transcribeAudio(model, parsed.request, {
|
|
70
72
|
apiKey: buildGatewayApiKeyResolver(
|
|
71
|
-
bootOpts
|
|
73
|
+
bootOpts,
|
|
72
74
|
model,
|
|
73
75
|
sessionId,
|
|
74
76
|
apiKey,
|
|
@@ -9,6 +9,7 @@ import { deterministicUuid } from "../../utils/deterministic-id";
|
|
|
9
9
|
import {
|
|
10
10
|
type AuthGatewayRouteOptions,
|
|
11
11
|
buildGatewayApiKeyResolver,
|
|
12
|
+
gatewayRequestLogFields,
|
|
12
13
|
mirrorRequestAbort,
|
|
13
14
|
resolveGatewayApiKey,
|
|
14
15
|
} from "../dispatch";
|
|
@@ -57,7 +58,7 @@ async function resolveVideoJob(
|
|
|
57
58
|
);
|
|
58
59
|
}
|
|
59
60
|
const sessionId = deterministicUuid(`video\u0000${model.provider}/${model.id}`);
|
|
60
|
-
const apiKey = await resolveGatewayApiKey(bootOpts
|
|
61
|
+
const apiKey = await resolveGatewayApiKey(bootOpts, model, sessionId, controller.signal, peer);
|
|
61
62
|
if (controller.signal.aborted) return aborted();
|
|
62
63
|
if ("status" in apiKey) return videoServer.formatError(apiKey.status, apiKey.type, apiKey.message);
|
|
63
64
|
return { model, upstreamId: identity.upstreamId, sessionId, apiKey, controller };
|
|
@@ -66,7 +67,7 @@ async function resolveVideoJob(
|
|
|
66
67
|
function videoOptions(bootOpts: AuthGatewayRouteOptions, resolved: ResolvedVideoRequest, peer: string) {
|
|
67
68
|
return {
|
|
68
69
|
apiKey: buildGatewayApiKeyResolver(
|
|
69
|
-
bootOpts
|
|
70
|
+
bootOpts,
|
|
70
71
|
resolved.model,
|
|
71
72
|
resolved.sessionId,
|
|
72
73
|
resolved.apiKey,
|
|
@@ -80,11 +81,13 @@ function videoOptions(bootOpts: AuthGatewayRouteOptions, resolved: ResolvedVideo
|
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
function logVideoRequest(
|
|
84
|
+
bootOpts: AuthGatewayRouteOptions,
|
|
83
85
|
requestId: string,
|
|
84
86
|
operation: "submit" | "poll" | "content",
|
|
85
|
-
|
|
87
|
+
resolved: ResolvedVideoRequest,
|
|
86
88
|
peer: string,
|
|
87
89
|
): void {
|
|
90
|
+
const { model } = resolved;
|
|
88
91
|
logger.info("auth-gateway request", {
|
|
89
92
|
requestId,
|
|
90
93
|
format: `video-${operation}`,
|
|
@@ -93,6 +96,7 @@ function logVideoRequest(
|
|
|
93
96
|
resolvedModel: model.id,
|
|
94
97
|
stream: operation === "content",
|
|
95
98
|
peer,
|
|
99
|
+
...gatewayRequestLogFields(bootOpts, resolved.apiKey),
|
|
96
100
|
});
|
|
97
101
|
}
|
|
98
102
|
|
|
@@ -140,7 +144,7 @@ export async function handleVideoSubmit(
|
|
|
140
144
|
);
|
|
141
145
|
}
|
|
142
146
|
const sessionId = deterministicUuid(`video\u0000${model.provider}/${model.id}`);
|
|
143
|
-
const apiKey = await resolveGatewayApiKey(bootOpts
|
|
147
|
+
const apiKey = await resolveGatewayApiKey(bootOpts, model, sessionId, controller.signal, peer);
|
|
144
148
|
if (controller.signal.aborted) return aborted();
|
|
145
149
|
if ("status" in apiKey) return videoServer.formatError(apiKey.status, apiKey.type, apiKey.message);
|
|
146
150
|
logger.info("auth-gateway request", {
|
|
@@ -151,18 +155,11 @@ export async function handleVideoSubmit(
|
|
|
151
155
|
resolvedModel: model.id,
|
|
152
156
|
stream: false,
|
|
153
157
|
peer,
|
|
158
|
+
...gatewayRequestLogFields(bootOpts, apiKey),
|
|
154
159
|
});
|
|
155
160
|
try {
|
|
156
161
|
const job = await submitVideo(model, parsed.request, {
|
|
157
|
-
apiKey: buildGatewayApiKeyResolver(
|
|
158
|
-
bootOpts.storage,
|
|
159
|
-
model,
|
|
160
|
-
sessionId,
|
|
161
|
-
apiKey,
|
|
162
|
-
controller.signal,
|
|
163
|
-
"video",
|
|
164
|
-
peer,
|
|
165
|
-
),
|
|
162
|
+
apiKey: buildGatewayApiKeyResolver(bootOpts, model, sessionId, apiKey, controller.signal, "video", peer),
|
|
166
163
|
fetch: bootOpts.fetch,
|
|
167
164
|
signal: controller.signal,
|
|
168
165
|
});
|
|
@@ -195,7 +192,7 @@ export async function handleVideoPoll(
|
|
|
195
192
|
const requestId = crypto.randomUUID();
|
|
196
193
|
const resolved = await resolveVideoJob(bootOpts, req, peer, gatewayId);
|
|
197
194
|
if (resolved instanceof Response) return resolved;
|
|
198
|
-
logVideoRequest(requestId, "poll", resolved
|
|
195
|
+
logVideoRequest(bootOpts, requestId, "poll", resolved, peer);
|
|
199
196
|
try {
|
|
200
197
|
const job = await pollVideo(resolved.model, resolved.upstreamId, videoOptions(bootOpts, resolved, peer));
|
|
201
198
|
recordCompletedUsage(bootOpts, resolved, req, job);
|
|
@@ -227,7 +224,7 @@ export async function handleVideoContent(
|
|
|
227
224
|
const requestId = crypto.randomUUID();
|
|
228
225
|
const resolved = await resolveVideoJob(bootOpts, req, peer, gatewayId);
|
|
229
226
|
if (resolved instanceof Response) return resolved;
|
|
230
|
-
logVideoRequest(requestId, "content", resolved
|
|
227
|
+
logVideoRequest(bootOpts, requestId, "content", resolved, peer);
|
|
231
228
|
try {
|
|
232
229
|
const content = await downloadVideo(resolved.model, resolved.upstreamId, videoOptions(bootOpts, resolved, peer));
|
|
233
230
|
const headers = new Headers(gatewayResponseHeaders(resolved.model, { requestId, startedAt }));
|