@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.
Files changed (148) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.md +4 -2
  3. package/THIRD-PARTY-NOTICES.txt +55 -5
  4. package/dist/types/auth/cascade.d.ts +5 -3
  5. package/dist/types/auth/pool.d.ts +3 -3
  6. package/dist/types/auth/select.d.ts +5 -1
  7. package/dist/types/auth/types.d.ts +15 -5
  8. package/dist/types/auth/usage-cache.d.ts +3 -1
  9. package/dist/types/auth-gateway/clients.d.ts +67 -0
  10. package/dist/types/auth-gateway/dispatch.d.ts +28 -10
  11. package/dist/types/auth-gateway/http.d.ts +2 -0
  12. package/dist/types/auth-gateway/index.d.ts +1 -0
  13. package/dist/types/auth-gateway/server.d.ts +9 -4
  14. package/dist/types/auth-gateway/session-state.d.ts +6 -0
  15. package/dist/types/auth-gateway/types.d.ts +16 -2
  16. package/dist/types/dialect/coercion.d.ts +6 -0
  17. package/dist/types/error/classes.d.ts +1 -1
  18. package/dist/types/error/flags.d.ts +15 -0
  19. package/dist/types/error/index.d.ts +1 -0
  20. package/dist/types/error/network-cause.d.ts +40 -0
  21. package/dist/types/images/shared.d.ts +26 -2
  22. package/dist/types/index.d.ts +1 -0
  23. package/dist/types/judgment/native.d.ts +51 -3
  24. package/dist/types/judgment/systemone.d.ts +9 -2
  25. package/dist/types/judgment/text.d.ts +11 -5
  26. package/dist/types/judgment/types.d.ts +5 -1
  27. package/dist/types/judgment/wire.d.ts +16 -3
  28. package/dist/types/provider-session-state.d.ts +2 -2
  29. package/dist/types/providers/anthropic-images.d.ts +7 -0
  30. package/dist/types/providers/anthropic-signature.d.ts +12 -2
  31. package/dist/types/providers/anthropic-state.d.ts +7 -0
  32. package/dist/types/providers/anthropic.d.ts +21 -6
  33. package/dist/types/providers/connection-prewarm.d.ts +15 -0
  34. package/dist/types/providers/cursor/exec-modern.d.ts +1 -1
  35. package/dist/types/providers/cursor-pi-args.d.ts +13 -0
  36. package/dist/types/providers/cursor.d.ts +7 -0
  37. package/dist/types/providers/openai-codex-responses.d.ts +6 -0
  38. package/dist/types/providers/openai-responses.d.ts +9 -0
  39. package/dist/types/providers/openai-shared.d.ts +30 -0
  40. package/dist/types/providers/systemone-server.d.ts +7 -3
  41. package/dist/types/providers/transform-messages.d.ts +26 -5
  42. package/dist/types/registry/oauth/google-antigravity.d.ts +1 -1
  43. package/dist/types/stream.d.ts +0 -2
  44. package/dist/types/types.d.ts +33 -9
  45. package/dist/types/utils/dsml-leak.d.ts +4 -2
  46. package/dist/types/utils/narration.d.ts +9 -0
  47. package/dist/types/utils/openai-http.d.ts +2 -0
  48. package/dist/types/utils/request-body.d.ts +11 -0
  49. package/dist/types/utils/request-compression.d.ts +21 -0
  50. package/dist/types/utils/tool-call-arguments.d.ts +2 -1
  51. package/dist/types/utils/transport-fetch.d.ts +8 -0
  52. package/dist/types/utils.d.ts +1 -1
  53. package/package.json +7 -7
  54. package/src/auth/cascade.ts +23 -13
  55. package/src/auth/pool.ts +3 -3
  56. package/src/auth/refresh.ts +39 -12
  57. package/src/auth/select.ts +8 -2
  58. package/src/auth/types.ts +16 -5
  59. package/src/auth/usage-cache.ts +23 -7
  60. package/src/auth/usage.ts +2 -4
  61. package/src/auth-broker/remote-store.ts +31 -6
  62. package/src/auth-gateway/clients.ts +318 -0
  63. package/src/auth-gateway/dispatch.ts +66 -35
  64. package/src/auth-gateway/http.ts +11 -5
  65. package/src/auth-gateway/index.ts +1 -0
  66. package/src/auth-gateway/routes/embeddings.ts +4 -10
  67. package/src/auth-gateway/routes/images.ts +6 -10
  68. package/src/auth-gateway/routes/rerank.ts +4 -10
  69. package/src/auth-gateway/routes/speech.ts +10 -10
  70. package/src/auth-gateway/routes/systemone.ts +22 -12
  71. package/src/auth-gateway/routes/transcriptions.ts +4 -2
  72. package/src/auth-gateway/routes/video.ts +12 -15
  73. package/src/auth-gateway/server.ts +136 -53
  74. package/src/auth-gateway/session-state.ts +14 -0
  75. package/src/auth-gateway/types.ts +16 -2
  76. package/src/dialect/coercion.ts +16 -0
  77. package/src/dialect/hermes.ts +7 -11
  78. package/src/dialect/qwen3.ts +3 -11
  79. package/src/error/classes.ts +3 -2
  80. package/src/error/flags.ts +24 -3
  81. package/src/error/index.ts +1 -0
  82. package/src/error/network-cause.ts +179 -0
  83. package/src/images/codex-format-directive.md +3 -0
  84. package/src/images/google-antigravity.ts +2 -1
  85. package/src/images/google-generative-ai.ts +4 -2
  86. package/src/images/openai-hosted.ts +25 -2
  87. package/src/images/openai-images.ts +11 -6
  88. package/src/images/shared.ts +77 -3
  89. package/src/index.ts +1 -0
  90. package/src/judgment/chat.ts +17 -1
  91. package/src/judgment/native.ts +182 -12
  92. package/src/judgment/openai-decisions.ts +27 -6
  93. package/src/judgment/systemone.ts +131 -10
  94. package/src/judgment/text.ts +20 -7
  95. package/src/judgment/types.ts +5 -1
  96. package/src/judgment/wire.ts +17 -3
  97. package/src/provider-session-state.ts +2 -2
  98. package/src/providers/amazon-bedrock.ts +4 -1
  99. package/src/providers/anthropic-client.ts +1 -1
  100. package/src/providers/anthropic-images.ts +263 -0
  101. package/src/providers/anthropic-messages-server.ts +84 -1
  102. package/src/providers/anthropic-signature.ts +35 -8
  103. package/src/providers/anthropic-state.ts +13 -0
  104. package/src/providers/anthropic.ts +479 -397
  105. package/src/providers/connection-prewarm.ts +78 -0
  106. package/src/providers/cowork-fetch.ts +5 -1
  107. package/src/providers/cursor/exec-modern.ts +1 -0
  108. package/src/providers/cursor-pi-args.ts +19 -0
  109. package/src/providers/cursor.ts +53 -13
  110. package/src/providers/devin.ts +10 -4
  111. package/src/providers/factory-droid/gemini.ts +9 -3
  112. package/src/providers/google-gemini-cli.ts +11 -1
  113. package/src/providers/google-shared.ts +2 -6
  114. package/src/providers/images-server.ts +6 -1
  115. package/src/providers/ollama.ts +15 -5
  116. package/src/providers/openai-anthropic-shim.ts +1 -0
  117. package/src/providers/openai-chat-server.ts +29 -0
  118. package/src/providers/openai-codex/access-programs.ts +2 -1
  119. package/src/providers/openai-codex-responses.ts +49 -82
  120. package/src/providers/openai-completions.ts +117 -19
  121. package/src/providers/openai-responses.ts +25 -2
  122. package/src/providers/openai-shared.ts +36 -5
  123. package/src/providers/pi-native-server.ts +1 -0
  124. package/src/providers/systemone-server.ts +72 -12
  125. package/src/providers/transform-messages.ts +95 -51
  126. package/src/registry/oauth/google-antigravity.ts +1 -1
  127. package/src/stream.ts +13 -73
  128. package/src/types.ts +33 -9
  129. package/src/usage/claude.ts +2 -9
  130. package/src/usage/github-copilot.ts +26 -4
  131. package/src/usage/openai-codex.ts +13 -7
  132. package/src/usage/opencode-go.ts +7 -8
  133. package/src/usage/xai-oauth.ts +2 -9
  134. package/src/utils/dsml-leak.ts +30 -14
  135. package/src/utils/leaked-thinking-stream.ts +10 -0
  136. package/src/utils/narration.ts +18 -0
  137. package/src/utils/openai-http.ts +27 -18
  138. package/src/utils/request-body.ts +27 -0
  139. package/src/utils/request-compression.ts +202 -0
  140. package/src/utils/request-debug.ts +23 -9
  141. package/src/utils/schema/dereference.ts +51 -18
  142. package/src/utils/schema/draft.ts +10 -2
  143. package/src/utils/schema/json-schema-validator.ts +4 -10
  144. package/src/utils/schema/normalize.ts +6 -9
  145. package/src/utils/tool-call-arguments.ts +2 -1
  146. package/src/utils/transport-fetch.ts +37 -0
  147. package/src/utils/validation.ts +26 -16
  148. package/src/utils.ts +38 -23
@@ -113,13 +113,19 @@ export function timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean {
113
113
 
114
114
  const TOKEN_ENCODER = new TextEncoder();
115
115
 
116
- export function isAuthorized(req: Request, tokens: ReadonlySet<string>): boolean {
117
- if (tokens.size === 0) return true;
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 false;
119
+ if (!header) return undefined;
120
120
  const match = header.match(/^Bearer\s+(.+)$/i);
121
- if (!match) return false;
122
- const presented = TOKEN_ENCODER.encode(match[1].trim());
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;
@@ -1,3 +1,4 @@
1
+ export * from "./clients";
1
2
  export * from "./dispatch";
2
3
  export * from "./http";
3
4
  export * from "./session-state";
@@ -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.storage, model, sessionId, controller.signal, peer);
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.storage, model, sessionId, controller.signal, peer);
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.storage, model, sessionId, controller.signal, peer);
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.storage, model, sessionId, controller.signal, peer);
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.storage, model, sessionId, controller.signal, peer);
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 classified = classifyGatewayError(error);
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.storage, model, sessionId, controller.signal, peer);
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.storage,
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.storage, model, sessionId, controller.signal, peer);
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.storage,
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
- model: ResolvedVideoRequest["model"],
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.storage, model, sessionId, controller.signal, peer);
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.model, peer);
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.model, peer);
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 }));