@hebo-ai/gateway 0.9.2 → 0.9.3

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 (213) hide show
  1. package/dist/config.d.ts +2 -0
  2. package/dist/config.js +125 -0
  3. package/dist/endpoints/chat-completions/converters.d.ts +26 -0
  4. package/dist/endpoints/chat-completions/converters.js +525 -0
  5. package/dist/endpoints/chat-completions/handler.d.ts +2 -0
  6. package/dist/endpoints/chat-completions/handler.js +152 -0
  7. package/dist/endpoints/chat-completions/index.d.ts +4 -0
  8. package/dist/endpoints/chat-completions/index.js +4 -0
  9. package/dist/endpoints/chat-completions/otel.d.ts +5 -0
  10. package/dist/endpoints/chat-completions/otel.js +178 -0
  11. package/dist/endpoints/chat-completions/schema.d.ts +1170 -0
  12. package/dist/endpoints/chat-completions/schema.js +252 -0
  13. package/dist/endpoints/conversations/converters.d.ts +8 -0
  14. package/dist/endpoints/conversations/converters.js +29 -0
  15. package/dist/endpoints/conversations/handler.d.ts +2 -0
  16. package/dist/endpoints/conversations/handler.js +259 -0
  17. package/dist/endpoints/conversations/index.d.ts +3 -0
  18. package/dist/endpoints/conversations/index.js +3 -0
  19. package/dist/endpoints/conversations/schema.d.ts +1511 -0
  20. package/dist/endpoints/conversations/schema.js +74 -0
  21. package/dist/endpoints/conversations/storage/dialects/greptime.d.ts +10 -0
  22. package/dist/endpoints/conversations/storage/dialects/greptime.js +87 -0
  23. package/dist/endpoints/conversations/storage/dialects/mysql.d.ts +12 -0
  24. package/dist/endpoints/conversations/storage/dialects/mysql.js +118 -0
  25. package/dist/endpoints/conversations/storage/dialects/postgres.d.ts +16 -0
  26. package/dist/endpoints/conversations/storage/dialects/postgres.js +185 -0
  27. package/dist/endpoints/conversations/storage/dialects/sqlite.d.ts +11 -0
  28. package/dist/endpoints/conversations/storage/dialects/sqlite.js +176 -0
  29. package/dist/endpoints/conversations/storage/dialects/types.d.ts +42 -0
  30. package/dist/endpoints/conversations/storage/dialects/types.js +0 -0
  31. package/dist/endpoints/conversations/storage/dialects/utils.d.ts +25 -0
  32. package/dist/endpoints/conversations/storage/dialects/utils.js +80 -0
  33. package/dist/endpoints/conversations/storage/memory.d.ts +25 -0
  34. package/dist/endpoints/conversations/storage/memory.js +200 -0
  35. package/dist/endpoints/conversations/storage/sql.d.ts +33 -0
  36. package/dist/endpoints/conversations/storage/sql.js +276 -0
  37. package/dist/endpoints/conversations/storage/types.d.ts +39 -0
  38. package/dist/endpoints/conversations/storage/types.js +0 -0
  39. package/dist/endpoints/embeddings/converters.d.ts +10 -0
  40. package/dist/endpoints/embeddings/converters.js +31 -0
  41. package/dist/endpoints/embeddings/handler.d.ts +2 -0
  42. package/dist/endpoints/embeddings/handler.js +99 -0
  43. package/dist/endpoints/embeddings/index.d.ts +4 -0
  44. package/dist/endpoints/embeddings/index.js +4 -0
  45. package/dist/endpoints/embeddings/otel.d.ts +5 -0
  46. package/dist/endpoints/embeddings/otel.js +29 -0
  47. package/dist/endpoints/embeddings/schema.d.ts +44 -0
  48. package/dist/endpoints/embeddings/schema.js +29 -0
  49. package/dist/endpoints/models/converters.d.ts +6 -0
  50. package/dist/endpoints/models/converters.js +42 -0
  51. package/dist/endpoints/models/handler.d.ts +2 -0
  52. package/dist/endpoints/models/handler.js +29 -0
  53. package/dist/endpoints/models/index.d.ts +3 -0
  54. package/dist/endpoints/models/index.js +3 -0
  55. package/dist/endpoints/models/schema.d.ts +42 -0
  56. package/dist/endpoints/models/schema.js +31 -0
  57. package/dist/endpoints/responses/converters.d.ts +17 -0
  58. package/dist/endpoints/responses/converters.js +1037 -0
  59. package/dist/endpoints/responses/handler.d.ts +2 -0
  60. package/dist/endpoints/responses/handler.js +141 -0
  61. package/dist/endpoints/responses/index.d.ts +4 -0
  62. package/dist/endpoints/responses/index.js +4 -0
  63. package/dist/endpoints/responses/otel.d.ts +6 -0
  64. package/dist/endpoints/responses/otel.js +226 -0
  65. package/dist/endpoints/responses/schema.d.ts +2109 -0
  66. package/dist/endpoints/responses/schema.js +314 -0
  67. package/dist/endpoints/shared/converters.d.ts +56 -0
  68. package/dist/endpoints/shared/converters.js +180 -0
  69. package/dist/endpoints/shared/schema.d.ts +70 -0
  70. package/dist/endpoints/shared/schema.js +46 -0
  71. package/dist/errors/ai-sdk.d.ts +2 -0
  72. package/dist/errors/ai-sdk.js +52 -0
  73. package/dist/errors/gateway.d.ts +5 -0
  74. package/dist/errors/gateway.js +13 -0
  75. package/dist/errors/openai.d.ts +15 -0
  76. package/dist/errors/openai.js +40 -0
  77. package/dist/errors/utils.d.ts +24 -0
  78. package/dist/errors/utils.js +46 -0
  79. package/dist/gateway.d.ts +11 -0
  80. package/dist/gateway.js +44 -0
  81. package/dist/index.d.ts +11 -0
  82. package/dist/index.js +10 -0
  83. package/dist/lifecycle.d.ts +3 -0
  84. package/dist/lifecycle.js +114 -0
  85. package/dist/logger/default.d.ts +4 -0
  86. package/dist/logger/default.js +81 -0
  87. package/dist/logger/index.d.ts +11 -0
  88. package/dist/logger/index.js +25 -0
  89. package/dist/middleware/common.d.ts +12 -0
  90. package/dist/middleware/common.js +146 -0
  91. package/dist/middleware/debug.d.ts +3 -0
  92. package/dist/middleware/debug.js +27 -0
  93. package/dist/middleware/matcher.d.ts +28 -0
  94. package/dist/middleware/matcher.js +118 -0
  95. package/dist/middleware/utils.d.ts +2 -0
  96. package/dist/middleware/utils.js +24 -0
  97. package/dist/models/amazon/index.d.ts +2 -0
  98. package/dist/models/amazon/index.js +2 -0
  99. package/dist/models/amazon/middleware.d.ts +3 -0
  100. package/dist/models/amazon/middleware.js +69 -0
  101. package/dist/models/amazon/presets.d.ts +345 -0
  102. package/dist/models/amazon/presets.js +80 -0
  103. package/dist/models/anthropic/index.d.ts +2 -0
  104. package/dist/models/anthropic/index.js +2 -0
  105. package/dist/models/anthropic/middleware.d.ts +5 -0
  106. package/dist/models/anthropic/middleware.js +128 -0
  107. package/dist/models/anthropic/presets.d.ts +711 -0
  108. package/dist/models/anthropic/presets.js +140 -0
  109. package/dist/models/catalog.d.ts +4 -0
  110. package/dist/models/catalog.js +8 -0
  111. package/dist/models/cohere/index.d.ts +2 -0
  112. package/dist/models/cohere/index.js +2 -0
  113. package/dist/models/cohere/middleware.d.ts +3 -0
  114. package/dist/models/cohere/middleware.js +62 -0
  115. package/dist/models/cohere/presets.d.ts +411 -0
  116. package/dist/models/cohere/presets.js +134 -0
  117. package/dist/models/google/index.d.ts +2 -0
  118. package/dist/models/google/index.js +2 -0
  119. package/dist/models/google/middleware.d.ts +8 -0
  120. package/dist/models/google/middleware.js +118 -0
  121. package/dist/models/google/presets.d.ts +815 -0
  122. package/dist/models/google/presets.js +184 -0
  123. package/dist/models/meta/index.d.ts +1 -0
  124. package/dist/models/meta/index.js +1 -0
  125. package/dist/models/meta/presets.d.ts +483 -0
  126. package/dist/models/meta/presets.js +105 -0
  127. package/dist/models/openai/index.d.ts +2 -0
  128. package/dist/models/openai/index.js +2 -0
  129. package/dist/models/openai/middleware.d.ts +4 -0
  130. package/dist/models/openai/middleware.js +89 -0
  131. package/dist/models/openai/presets.d.ts +1319 -0
  132. package/dist/models/openai/presets.js +277 -0
  133. package/dist/models/types.d.ts +20 -0
  134. package/dist/models/types.js +100 -0
  135. package/dist/models/voyage/index.d.ts +2 -0
  136. package/dist/models/voyage/index.js +2 -0
  137. package/dist/models/voyage/middleware.d.ts +2 -0
  138. package/dist/models/voyage/middleware.js +19 -0
  139. package/dist/models/voyage/presets.d.ts +436 -0
  140. package/dist/models/voyage/presets.js +85 -0
  141. package/dist/providers/anthropic/canonical.d.ts +3 -0
  142. package/dist/providers/anthropic/canonical.js +9 -0
  143. package/dist/providers/anthropic/index.d.ts +1 -0
  144. package/dist/providers/anthropic/index.js +1 -0
  145. package/dist/providers/bedrock/canonical.d.ts +17 -0
  146. package/dist/providers/bedrock/canonical.js +64 -0
  147. package/dist/providers/bedrock/index.d.ts +2 -0
  148. package/dist/providers/bedrock/index.js +2 -0
  149. package/dist/providers/bedrock/middleware.d.ts +5 -0
  150. package/dist/providers/bedrock/middleware.js +133 -0
  151. package/dist/providers/cohere/canonical.d.ts +3 -0
  152. package/dist/providers/cohere/canonical.js +17 -0
  153. package/dist/providers/cohere/index.d.ts +1 -0
  154. package/dist/providers/cohere/index.js +1 -0
  155. package/dist/providers/groq/canonical.d.ts +3 -0
  156. package/dist/providers/groq/canonical.js +12 -0
  157. package/dist/providers/groq/index.d.ts +2 -0
  158. package/dist/providers/groq/index.js +2 -0
  159. package/dist/providers/groq/middleware.d.ts +2 -0
  160. package/dist/providers/groq/middleware.js +30 -0
  161. package/dist/providers/openai/canonical.d.ts +3 -0
  162. package/dist/providers/openai/canonical.js +8 -0
  163. package/dist/providers/openai/index.d.ts +1 -0
  164. package/dist/providers/openai/index.js +1 -0
  165. package/dist/providers/registry.d.ts +24 -0
  166. package/dist/providers/registry.js +103 -0
  167. package/dist/providers/types.d.ts +7 -0
  168. package/dist/providers/types.js +11 -0
  169. package/dist/providers/vertex/canonical.d.ts +3 -0
  170. package/dist/providers/vertex/canonical.js +8 -0
  171. package/dist/providers/vertex/index.d.ts +2 -0
  172. package/dist/providers/vertex/index.js +2 -0
  173. package/dist/providers/vertex/middleware.d.ts +2 -0
  174. package/dist/providers/vertex/middleware.js +47 -0
  175. package/dist/providers/voyage/canonical.d.ts +3 -0
  176. package/dist/providers/voyage/canonical.js +7 -0
  177. package/dist/providers/voyage/index.d.ts +1 -0
  178. package/dist/providers/voyage/index.js +1 -0
  179. package/dist/telemetry/ai-sdk.d.ts +2 -0
  180. package/dist/telemetry/ai-sdk.js +31 -0
  181. package/dist/telemetry/baggage.d.ts +1 -0
  182. package/dist/telemetry/baggage.js +24 -0
  183. package/dist/telemetry/fetch.d.ts +2 -0
  184. package/dist/telemetry/fetch.js +49 -0
  185. package/dist/telemetry/gen-ai.d.ts +7 -0
  186. package/dist/telemetry/gen-ai.js +108 -0
  187. package/dist/telemetry/http.d.ts +3 -0
  188. package/dist/telemetry/http.js +54 -0
  189. package/dist/telemetry/index.d.ts +1 -0
  190. package/dist/telemetry/index.js +1 -0
  191. package/dist/telemetry/memory.d.ts +2 -0
  192. package/dist/telemetry/memory.js +43 -0
  193. package/dist/telemetry/span.d.ts +13 -0
  194. package/dist/telemetry/span.js +60 -0
  195. package/dist/types.d.ts +231 -0
  196. package/dist/types.js +2 -0
  197. package/dist/utils/body.d.ts +19 -0
  198. package/dist/utils/body.js +99 -0
  199. package/dist/utils/env.d.ts +2 -0
  200. package/dist/utils/env.js +7 -0
  201. package/dist/utils/headers.d.ts +4 -0
  202. package/dist/utils/headers.js +22 -0
  203. package/dist/utils/preset.d.ts +10 -0
  204. package/dist/utils/preset.js +41 -0
  205. package/dist/utils/request.d.ts +2 -0
  206. package/dist/utils/request.js +43 -0
  207. package/dist/utils/response.d.ts +6 -0
  208. package/dist/utils/response.js +55 -0
  209. package/dist/utils/stream.d.ts +9 -0
  210. package/dist/utils/stream.js +100 -0
  211. package/dist/utils/url.d.ts +4 -0
  212. package/dist/utils/url.js +21 -0
  213. package/package.json +1 -1
@@ -0,0 +1,52 @@
1
+ import { AISDKError, APICallError, DownloadError, EmptyResponseBodyError, InvalidArgumentError, InvalidDataContentError, InvalidMessageRoleError, InvalidPromptError, InvalidResponseDataError, InvalidStreamPartError, InvalidToolApprovalError, InvalidToolInputError, JSONParseError, LoadAPIKeyError, LoadSettingError, MessageConversionError, MissingToolResultsError, NoContentGeneratedError, NoImageGeneratedError, NoObjectGeneratedError, NoOutputGeneratedError, NoSpeechGeneratedError, NoSuchModelError, NoSuchProviderError, NoSuchToolError, NoTranscriptGeneratedError, NoVideoGeneratedError, RetryError, ToolCallNotFoundForApprovalError, ToolCallRepairError, TooManyEmbeddingValuesForCallError, TypeValidationError, UIMessageStreamError, UnsupportedModelVersionError, UnsupportedFunctionalityError, } from "ai";
2
+ import { GatewayError } from "./gateway";
3
+ import { STATUS_CODE } from "./utils";
4
+ export const normalizeAiSdkError = (error) => {
5
+ if (APICallError.isInstance(error)) {
6
+ const status = error.statusCode ?? (error.isRetryable ? 502 : 422);
7
+ const code = `UPSTREAM_${STATUS_CODE(status)}`;
8
+ return new GatewayError(error, status, code);
9
+ }
10
+ if (JSONParseError.isInstance(error) ||
11
+ InvalidResponseDataError.isInstance(error) ||
12
+ TypeValidationError.isInstance(error) ||
13
+ EmptyResponseBodyError.isInstance(error) ||
14
+ NoContentGeneratedError.isInstance(error) ||
15
+ NoOutputGeneratedError.isInstance(error) ||
16
+ InvalidStreamPartError.isInstance(error) ||
17
+ UIMessageStreamError.isInstance(error) ||
18
+ RetryError.isInstance(error) ||
19
+ DownloadError.isInstance(error) ||
20
+ ToolCallRepairError.isInstance(error) ||
21
+ NoImageGeneratedError.isInstance(error) ||
22
+ NoObjectGeneratedError.isInstance(error) ||
23
+ NoSpeechGeneratedError.isInstance(error) ||
24
+ NoTranscriptGeneratedError.isInstance(error) ||
25
+ NoVideoGeneratedError.isInstance(error)) {
26
+ return new GatewayError(error, 502, `UPSTREAM_${STATUS_CODE(502)}`);
27
+ }
28
+ if (InvalidArgumentError.isInstance(error) ||
29
+ InvalidPromptError.isInstance(error) ||
30
+ InvalidMessageRoleError.isInstance(error) ||
31
+ InvalidDataContentError.isInstance(error) ||
32
+ MessageConversionError.isInstance(error) ||
33
+ InvalidToolInputError.isInstance(error) ||
34
+ InvalidToolApprovalError.isInstance(error) ||
35
+ ToolCallNotFoundForApprovalError.isInstance(error) ||
36
+ MissingToolResultsError.isInstance(error) ||
37
+ NoSuchToolError.isInstance(error) ||
38
+ UnsupportedModelVersionError.isInstance(error) ||
39
+ UnsupportedFunctionalityError.isInstance(error) ||
40
+ TooManyEmbeddingValuesForCallError.isInstance(error) ||
41
+ NoSuchModelError.isInstance(error) ||
42
+ NoSuchProviderError.isInstance(error)) {
43
+ return new GatewayError(error, 422, `UPSTREAM_${STATUS_CODE(422)}`);
44
+ }
45
+ if (LoadSettingError.isInstance(error) || LoadAPIKeyError.isInstance(error)) {
46
+ return new GatewayError(error, 500);
47
+ }
48
+ if (AISDKError.isInstance(error)) {
49
+ return new GatewayError(error, 500);
50
+ }
51
+ return undefined;
52
+ };
@@ -0,0 +1,5 @@
1
+ export declare class GatewayError extends Error {
2
+ readonly status: number;
3
+ readonly code: string;
4
+ constructor(error: unknown, status: number, code?: string, cause?: unknown);
5
+ }
@@ -0,0 +1,13 @@
1
+ import { STATUS_CODE } from "./utils";
2
+ export class GatewayError extends Error {
3
+ status;
4
+ code;
5
+ constructor(error, status, code, cause) {
6
+ const isError = error instanceof Error;
7
+ super(isError ? error.message : String(error));
8
+ this.name = "GatewayError";
9
+ this.cause = cause ?? (isError ? error : undefined);
10
+ this.status = status;
11
+ this.code = code ?? STATUS_CODE(status);
12
+ }
13
+ }
@@ -0,0 +1,15 @@
1
+ import * as z from "zod";
2
+ export declare const OpenAIErrorSchema: z.ZodObject<{
3
+ error: z.ZodObject<{
4
+ message: z.ZodString;
5
+ type: z.ZodString;
6
+ code: z.ZodNullable<z.ZodOptional<z.ZodString>>;
7
+ param: z.ZodNullable<z.ZodOptional<z.ZodString>>;
8
+ }, z.core.$strip>;
9
+ }, z.core.$strip>;
10
+ export declare class OpenAIError {
11
+ readonly error: z.infer<typeof OpenAIErrorSchema>["error"];
12
+ constructor(message: string, type?: string, code?: string, param?: string);
13
+ }
14
+ export declare function toOpenAIError(error: unknown): OpenAIError;
15
+ export declare function toOpenAIErrorResponse(error: unknown, responseInit?: ResponseInit): Response;
@@ -0,0 +1,40 @@
1
+ import * as z from "zod";
2
+ import { isProduction } from "../utils/env";
3
+ import { resolveRequestId } from "../utils/headers";
4
+ import { toResponse } from "../utils/response";
5
+ import { getErrorMeta, STATUS_CODE } from "./utils";
6
+ export const OpenAIErrorSchema = z.object({
7
+ error: z.object({
8
+ message: z.string(),
9
+ type: z.string(),
10
+ code: z.string().optional().nullable(),
11
+ param: z.string().optional().nullable(),
12
+ }),
13
+ });
14
+ export class OpenAIError {
15
+ error;
16
+ constructor(message, type = "server_error", code, param = "") {
17
+ this.error = { message, type, code: code?.toLowerCase(), param };
18
+ }
19
+ }
20
+ const mapType = (status) => (status < 500 ? "invalid_request_error" : "server_error");
21
+ const maybeMaskMessage = (meta, requestId) => {
22
+ // FUTURE: consider masking all upstream errors, also 4xx
23
+ if (!(isProduction() && meta.status >= 500)) {
24
+ return meta.message;
25
+ }
26
+ // FUTURE: always attach requestId to errors (masked and unmasked)
27
+ return `${STATUS_CODE(meta.status)} (${requestId ?? "see requestId in response headers"})`;
28
+ };
29
+ export function toOpenAIError(error) {
30
+ const meta = getErrorMeta(error);
31
+ return new OpenAIError(maybeMaskMessage(meta), mapType(meta.status), meta.code);
32
+ }
33
+ export function toOpenAIErrorResponse(error, responseInit) {
34
+ const meta = getErrorMeta(error);
35
+ return toResponse(new OpenAIError(maybeMaskMessage(meta, resolveRequestId(responseInit)), mapType(meta.status), meta.code), {
36
+ ...responseInit,
37
+ status: meta.status,
38
+ statusText: meta.code,
39
+ });
40
+ }
@@ -0,0 +1,24 @@
1
+ export declare const STATUS_CODES: {
2
+ readonly 400: "BAD_REQUEST";
3
+ readonly 401: "UNAUTHORIZED";
4
+ readonly 402: "PAYMENT_REQUIRED";
5
+ readonly 403: "FORBIDDEN";
6
+ readonly 404: "NOT_FOUND";
7
+ readonly 405: "METHOD_NOT_ALLOWED";
8
+ readonly 409: "CONFLICT";
9
+ readonly 413: "PAYLOAD_TOO_LARGE";
10
+ readonly 415: "UNSUPPORTED_MEDIA_TYPE";
11
+ readonly 422: "UNPROCESSABLE_ENTITY";
12
+ readonly 429: "TOO_MANY_REQUESTS";
13
+ readonly 499: "CLIENT_CLOSED_REQUEST";
14
+ readonly 500: "INTERNAL_SERVER_ERROR";
15
+ readonly 502: "BAD_GATEWAY";
16
+ readonly 503: "SERVICE_UNAVAILABLE";
17
+ readonly 504: "GATEWAY_TIMEOUT";
18
+ };
19
+ export declare const STATUS_CODE: (status: number) => "BAD_REQUEST" | "UNAUTHORIZED" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "NOT_FOUND" | "METHOD_NOT_ALLOWED" | "CONFLICT" | "PAYLOAD_TOO_LARGE" | "UNSUPPORTED_MEDIA_TYPE" | "UNPROCESSABLE_ENTITY" | "TOO_MANY_REQUESTS" | "CLIENT_CLOSED_REQUEST" | "INTERNAL_SERVER_ERROR" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT";
20
+ export declare function getErrorMeta(error: unknown): {
21
+ status: number;
22
+ code: string;
23
+ message: string;
24
+ };
@@ -0,0 +1,46 @@
1
+ import { normalizeAiSdkError } from "./ai-sdk";
2
+ import { GatewayError } from "./gateway";
3
+ export const STATUS_CODES = {
4
+ 400: "BAD_REQUEST",
5
+ 401: "UNAUTHORIZED",
6
+ 402: "PAYMENT_REQUIRED",
7
+ 403: "FORBIDDEN",
8
+ 404: "NOT_FOUND",
9
+ 405: "METHOD_NOT_ALLOWED",
10
+ 409: "CONFLICT",
11
+ 413: "PAYLOAD_TOO_LARGE",
12
+ 415: "UNSUPPORTED_MEDIA_TYPE",
13
+ 422: "UNPROCESSABLE_ENTITY",
14
+ 429: "TOO_MANY_REQUESTS",
15
+ 499: "CLIENT_CLOSED_REQUEST",
16
+ 500: "INTERNAL_SERVER_ERROR",
17
+ 502: "BAD_GATEWAY",
18
+ 503: "SERVICE_UNAVAILABLE",
19
+ 504: "GATEWAY_TIMEOUT",
20
+ };
21
+ export const STATUS_CODE = (status) => {
22
+ const label = STATUS_CODES[status];
23
+ if (label)
24
+ return label;
25
+ return status >= 400 && status < 500 ? STATUS_CODES[400] : STATUS_CODES[500];
26
+ };
27
+ // FUTURE: always return a wrapped GatewayError?
28
+ export function getErrorMeta(error) {
29
+ const message = error instanceof Error ? error.message : String(error);
30
+ let status;
31
+ let code;
32
+ if (error instanceof GatewayError) {
33
+ ({ status, code } = error);
34
+ }
35
+ else {
36
+ const normalized = normalizeAiSdkError(error);
37
+ if (normalized) {
38
+ ({ status, code } = normalized);
39
+ }
40
+ else {
41
+ status = 500;
42
+ code = STATUS_CODE(status);
43
+ }
44
+ }
45
+ return { status, code, message };
46
+ }
@@ -0,0 +1,11 @@
1
+ import type { Endpoint, GatewayConfig } from "./types";
2
+ export declare function gateway(config: GatewayConfig): {
3
+ handler: (req: Request, state?: Record<string, unknown>) => Promise<Response>;
4
+ routes: {
5
+ readonly "/chat/completions": Endpoint;
6
+ readonly "/embeddings": Endpoint;
7
+ readonly "/models": Endpoint;
8
+ readonly "/conversations": Endpoint;
9
+ readonly "/responses": Endpoint;
10
+ };
11
+ };
@@ -0,0 +1,44 @@
1
+ import { parseConfig } from "./config";
2
+ import { chatCompletions } from "./endpoints/chat-completions/handler";
3
+ import { conversations } from "./endpoints/conversations/handler";
4
+ import { embeddings } from "./endpoints/embeddings/handler";
5
+ import { models } from "./endpoints/models/handler";
6
+ import { responses } from "./endpoints/responses/handler";
7
+ import { GatewayError } from "./errors/gateway";
8
+ import { winterCgHandler } from "./lifecycle";
9
+ import { logger } from "./logger";
10
+ let inflight = 0;
11
+ export function gateway(config) {
12
+ const basePath = (config.basePath ?? "").replace(/\/+$/, "");
13
+ const parsedConfig = parseConfig(config);
14
+ const notFoundHandler = winterCgHandler(() => {
15
+ throw new GatewayError("Not Found", 404);
16
+ }, parsedConfig);
17
+ const routes = {
18
+ ["/chat/completions"]: chatCompletions(parsedConfig),
19
+ ["/embeddings"]: embeddings(parsedConfig),
20
+ ["/models"]: models(parsedConfig),
21
+ ["/conversations"]: conversations(parsedConfig),
22
+ ["/responses"]: responses(parsedConfig),
23
+ };
24
+ const routeEntries = Object.entries(routes);
25
+ const handler = (req, state) => {
26
+ let pathname = new URL(req.url).pathname;
27
+ if (basePath && pathname.startsWith(basePath)) {
28
+ pathname = pathname.slice(basePath.length);
29
+ }
30
+ logger.info(`[gateway] ${req.method} ${pathname} (${++inflight})`);
31
+ for (const [route, endpoint] of routeEntries) {
32
+ if (pathname === route || pathname.startsWith(route + "/")) {
33
+ try {
34
+ return endpoint.handler(req, state);
35
+ }
36
+ finally {
37
+ inflight--;
38
+ }
39
+ }
40
+ }
41
+ return notFoundHandler(req, state);
42
+ };
43
+ return { handler, routes };
44
+ }
@@ -0,0 +1,11 @@
1
+ export * from "./gateway";
2
+ export type * from "./types";
3
+ export * from "./errors/gateway";
4
+ export * from "./errors/openai";
5
+ export * from "./logger";
6
+ export * from "./middleware/common";
7
+ export * from "./middleware/matcher";
8
+ export * from "./models/catalog";
9
+ export * from "./models/types";
10
+ export * from "./providers/registry";
11
+ export * from "./providers/types";
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ export * from "./gateway";
2
+ export * from "./errors/gateway";
3
+ export * from "./errors/openai";
4
+ export * from "./logger";
5
+ export * from "./middleware/common";
6
+ export * from "./middleware/matcher";
7
+ export * from "./models/catalog";
8
+ export * from "./models/types";
9
+ export * from "./providers/registry";
10
+ export * from "./providers/types";
@@ -0,0 +1,3 @@
1
+ import type { GatewayConfig, GatewayConfigParsed, GatewayContext } from "./types";
2
+ import type { SseFrame } from "./utils/stream";
3
+ export declare const winterCgHandler: (run: (ctx: GatewayContext, cfg: GatewayConfigParsed) => Promise<object | ReadableStream<SseFrame>>, config: GatewayConfig) => (request: Request, state?: Record<string, unknown>) => Promise<Response>;
@@ -0,0 +1,114 @@
1
+ import { parseConfig } from "./config";
2
+ import { GatewayError } from "./errors/gateway";
3
+ import { toOpenAIErrorResponse } from "./errors/openai";
4
+ import { logger } from "./logger";
5
+ import { getBaggageAttributes } from "./telemetry/baggage";
6
+ import { instrumentFetch } from "./telemetry/fetch";
7
+ import { recordRequestDuration } from "./telemetry/gen-ai";
8
+ import { getRequestAttributes, getResponseAttributes } from "./telemetry/http";
9
+ import { observeV8jsMemoryMetrics } from "./telemetry/memory";
10
+ import { addSpanEvent, setSpanEventsEnabled, setSpanTracer, startSpan } from "./telemetry/span";
11
+ import { resolveOrCreateRequestId } from "./utils/request";
12
+ import { prepareResponseInit, toResponse } from "./utils/response";
13
+ export const winterCgHandler = (run, config) => {
14
+ const parsedConfig = parseConfig(config);
15
+ if (parsedConfig.telemetry?.enabled) {
16
+ setSpanTracer(parsedConfig.telemetry?.tracer);
17
+ setSpanEventsEnabled(parsedConfig.telemetry?.signals?.hebo);
18
+ instrumentFetch(parsedConfig.telemetry?.signals?.hebo);
19
+ observeV8jsMemoryMetrics(parsedConfig.telemetry?.signals?.hebo);
20
+ }
21
+ return async (request, state) => {
22
+ const start = performance.now();
23
+ const ctx = {
24
+ request,
25
+ state: state ?? {},
26
+ otel: {},
27
+ providers: parsedConfig.providers,
28
+ models: parsedConfig.models,
29
+ requestId: resolveOrCreateRequestId(request),
30
+ };
31
+ const span = startSpan(ctx.request.url);
32
+ span.setAttributes(getBaggageAttributes(ctx.request));
33
+ if (!span.isExisting) {
34
+ span.setAttributes(getRequestAttributes(ctx.request, parsedConfig.telemetry?.signals?.http));
35
+ span.setAttributes({ "http.request.id": ctx.requestId });
36
+ }
37
+ const finalize = (status, reason) => {
38
+ if (ctx.operation) {
39
+ span.updateName(`${ctx.operation}${ctx.modelId ? ` ${ctx.modelId}` : ""}`);
40
+ }
41
+ if (!span.isExisting) {
42
+ // FUTURE add http.server.request.duration
43
+ span.setAttributes(getResponseAttributes(ctx.response, parsedConfig.telemetry?.signals?.http));
44
+ }
45
+ let realStatus = status;
46
+ if (ctx.request.signal.aborted)
47
+ realStatus = 499;
48
+ else if (status === 200 && ctx.response?.status)
49
+ realStatus = ctx.response.status;
50
+ if (realStatus !== 200) {
51
+ logger[realStatus >= 500 ? "error" : "warn"]({
52
+ requestId: ctx.requestId,
53
+ err: reason ?? ctx.request.signal.reason,
54
+ });
55
+ span.recordError(reason);
56
+ }
57
+ span.setAttributes({ "http.response.status_code_effective": realStatus });
58
+ if (ctx.operation === "chat" ||
59
+ ctx.operation === "embeddings" ||
60
+ ctx.operation === "responses") {
61
+ recordRequestDuration(performance.now() - start, realStatus, ctx, parsedConfig.telemetry?.signals?.gen_ai);
62
+ }
63
+ span.finish();
64
+ };
65
+ await span.runWithContext(async () => {
66
+ try {
67
+ if (parsedConfig.hooks?.onRequest) {
68
+ const onRequest = await parsedConfig.hooks.onRequest(ctx);
69
+ addSpanEvent("hebo.hooks.on_request.completed");
70
+ if (onRequest instanceof Response) {
71
+ ctx.response = onRequest;
72
+ }
73
+ }
74
+ if (!ctx.response) {
75
+ ctx.result = (await run(ctx, parsedConfig));
76
+ ctx.response = toResponse(ctx.result, prepareResponseInit(ctx.requestId), {
77
+ onDone: finalize,
78
+ });
79
+ }
80
+ if (parsedConfig.hooks?.onResponse) {
81
+ const onResponse = await parsedConfig.hooks.onResponse(ctx);
82
+ addSpanEvent("hebo.hooks.on_response.completed");
83
+ if (onResponse) {
84
+ ctx.response = onResponse;
85
+ }
86
+ }
87
+ // FUTURE: this can leak if onResponse removed wrapper from response.body
88
+ if (!(ctx.result instanceof ReadableStream)) {
89
+ finalize(ctx.response.status);
90
+ }
91
+ }
92
+ catch (error) {
93
+ if (parsedConfig.hooks?.onError) {
94
+ try {
95
+ ctx.error = error;
96
+ const onError = await parsedConfig.hooks.onError(ctx);
97
+ addSpanEvent("hebo.hooks.on_error.completed");
98
+ if (onError) {
99
+ ctx.response = onError;
100
+ }
101
+ }
102
+ catch {
103
+ logger.debug("[lifecycle] onError hook threw");
104
+ }
105
+ }
106
+ ctx.response ??= toOpenAIErrorResponse(ctx.request.signal.aborted
107
+ ? new GatewayError(error ?? ctx.request.signal.reason, 499)
108
+ : error, prepareResponseInit(ctx.requestId));
109
+ finalize(ctx.response.status, error);
110
+ }
111
+ });
112
+ return ctx.response ?? new Response("Internal Server Error", { status: 500 });
113
+ };
114
+ };
@@ -0,0 +1,4 @@
1
+ import type { LogLevel, Logger } from "./index";
2
+ export declare const createDefaultLogger: (config: {
3
+ level?: LogLevel;
4
+ }) => Logger;
@@ -0,0 +1,81 @@
1
+ import { isProduction, isTest } from "../utils/env";
2
+ const getDefaultLogLevel = () => isTest() ? "silent" : isProduction() ? "info" : "debug";
3
+ const noop = () => { };
4
+ const LEVEL = {
5
+ trace: 5,
6
+ debug: 10,
7
+ info: 20,
8
+ warn: 30,
9
+ error: 40,
10
+ silent: 50,
11
+ };
12
+ const LEVELS = Object.keys(LEVEL);
13
+ const isRecord = (value) => typeof value === "object" && value !== null && !(value instanceof Error);
14
+ function serializeError(err, _seen) {
15
+ if (!(err instanceof Error))
16
+ return { message: String(err) };
17
+ const seen = _seen ?? new WeakSet();
18
+ if (seen.has(err))
19
+ return { name: err.name, message: err.message, circular: true };
20
+ seen.add(err);
21
+ const out = {};
22
+ for (const k of Object.getOwnPropertyNames(err)) {
23
+ if (k.startsWith("_"))
24
+ continue;
25
+ let val;
26
+ try {
27
+ val = err[k];
28
+ }
29
+ catch {
30
+ val = "[Unreadable]";
31
+ }
32
+ if (typeof val === "bigint")
33
+ val = `${val}n`;
34
+ // FUTURE: check for circular references within val
35
+ out[k] = val instanceof Error ? serializeError(val, seen) : val;
36
+ }
37
+ return out;
38
+ }
39
+ const buildLogObject = (level, args) => {
40
+ const [first, second] = args;
41
+ let obj;
42
+ let err;
43
+ let msg;
44
+ if (first instanceof Error) {
45
+ err = serializeError(first);
46
+ }
47
+ else if (isRecord(first)) {
48
+ if (first["err"] !== undefined) {
49
+ err = serializeError(first["err"]);
50
+ delete first["err"];
51
+ }
52
+ obj = first;
53
+ }
54
+ else {
55
+ msg = first;
56
+ }
57
+ if (second !== undefined) {
58
+ msg = second;
59
+ }
60
+ if (err && msg === undefined) {
61
+ msg = err["message"];
62
+ }
63
+ return {
64
+ level,
65
+ time: Date.now(),
66
+ ...(msg ? { msg } : {}),
67
+ ...(err ? { err } : {}),
68
+ ...obj,
69
+ };
70
+ };
71
+ const makeLogFn = (level, write) => (...args) => {
72
+ write(JSON.stringify(buildLogObject(level, args)));
73
+ };
74
+ export const createDefaultLogger = (config) => {
75
+ if (config.level === "silent" || getDefaultLogLevel() === "silent") {
76
+ return { trace: noop, debug: noop, info: noop, warn: noop, error: noop };
77
+ }
78
+ const threshold = LEVEL[config.level ?? getDefaultLogLevel()];
79
+ const enabled = (lvl) => LEVEL[lvl] >= threshold;
80
+ return Object.fromEntries(LEVELS.map((lvl) => [lvl, enabled(lvl) ? makeLogFn(lvl, console.log) : noop]));
81
+ };
@@ -0,0 +1,11 @@
1
+ export type LogArgs = [msg: string] | [obj: Record<string, unknown>, msg?: string] | [err: Error, msg?: string];
2
+ export type LogFn = (...args: LogArgs) => void;
3
+ export type Logger = Record<"trace" | "debug" | "info" | "warn" | "error", LogFn>;
4
+ export type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "silent";
5
+ export type LoggerConfig = {
6
+ level?: LogLevel;
7
+ };
8
+ export declare let logger: Logger;
9
+ export declare const isLogger: (input: Logger | LoggerConfig) => input is Logger;
10
+ export declare function isLoggerDisabled(input?: Logger | LoggerConfig | null): boolean;
11
+ export declare function setLoggerInstance(next: Logger): void;
@@ -0,0 +1,25 @@
1
+ import { isTest } from "../utils/env";
2
+ const KEY = Symbol.for("@hebo/logger");
3
+ const g = globalThis;
4
+ g[KEY] ??= {
5
+ trace: () => { },
6
+ debug: () => { },
7
+ info: () => { },
8
+ warn: () => { },
9
+ error: () => { },
10
+ };
11
+ export let logger = g[KEY];
12
+ export const isLogger = (input) => typeof input === "object" && input !== null && "info" in input;
13
+ export function isLoggerDisabled(input) {
14
+ if (isTest())
15
+ return true;
16
+ if (input === null)
17
+ return true;
18
+ if (!input || typeof input !== "object" || "info" in input)
19
+ return false;
20
+ return input.level === "silent";
21
+ }
22
+ export function setLoggerInstance(next) {
23
+ g[KEY] = next;
24
+ logger = g[KEY];
25
+ }
@@ -0,0 +1,12 @@
1
+ import type { EmbeddingModelMiddleware, LanguageModelMiddleware } from "ai";
2
+ import type { ProviderId } from "../providers/types";
3
+ /**
4
+ * Converts snake_case params in providerOptions to camelCase
5
+ * and moves all of them into providerOptions[providerName].
6
+ * Also snakizes values in providerMetadata for OpenAI compatibility.
7
+ */
8
+ export declare function forwardLanguageParams(providerName: ProviderId): LanguageModelMiddleware;
9
+ export declare function forwardEmbeddingParams(providerName: ProviderId): EmbeddingModelMiddleware;
10
+ export declare function extractProviderNamespace(id: string): string;
11
+ export declare function forwardParamsMiddleware(provider: string): LanguageModelMiddleware;
12
+ export declare function forwardParamsEmbeddingMiddleware(provider: string): EmbeddingModelMiddleware;