@hebo-ai/gateway 0.4.1 → 0.5.0-beta.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 (62) hide show
  1. package/README.md +2 -3
  2. package/dist/endpoints/chat-completions/converters.d.ts +3 -1
  3. package/dist/endpoints/chat-completions/converters.js +121 -90
  4. package/dist/endpoints/chat-completions/handler.js +2 -4
  5. package/dist/endpoints/chat-completions/otel.js +7 -0
  6. package/dist/endpoints/chat-completions/schema.d.ts +400 -76
  7. package/dist/endpoints/chat-completions/schema.js +80 -36
  8. package/dist/endpoints/embeddings/handler.js +2 -4
  9. package/dist/endpoints/embeddings/schema.d.ts +1 -1
  10. package/dist/endpoints/embeddings/schema.js +1 -1
  11. package/dist/errors/gateway.js +1 -0
  12. package/dist/lifecycle.js +7 -12
  13. package/dist/logger/default.d.ts +0 -1
  14. package/dist/logger/default.js +30 -6
  15. package/dist/middleware/utils.js +1 -0
  16. package/dist/models/amazon/middleware.js +1 -0
  17. package/dist/models/anthropic/middleware.d.ts +2 -0
  18. package/dist/models/anthropic/middleware.js +77 -16
  19. package/dist/models/google/middleware.js +17 -0
  20. package/dist/models/google/presets.d.ts +387 -0
  21. package/dist/models/google/presets.js +9 -2
  22. package/dist/models/openai/middleware.js +1 -0
  23. package/dist/models/types.d.ts +1 -1
  24. package/dist/models/types.js +1 -0
  25. package/dist/providers/bedrock/index.d.ts +1 -0
  26. package/dist/providers/bedrock/index.js +1 -0
  27. package/dist/providers/bedrock/middleware.d.ts +2 -0
  28. package/dist/providers/bedrock/middleware.js +35 -0
  29. package/dist/telemetry/http.js +0 -3
  30. package/dist/types.d.ts +10 -20
  31. package/dist/utils/request.d.ts +1 -3
  32. package/dist/utils/request.js +3 -26
  33. package/dist/utils/response.d.ts +1 -1
  34. package/dist/utils/response.js +3 -3
  35. package/package.json +19 -21
  36. package/src/endpoints/chat-completions/converters.test.ts +219 -0
  37. package/src/endpoints/chat-completions/converters.ts +144 -104
  38. package/src/endpoints/chat-completions/handler.test.ts +87 -0
  39. package/src/endpoints/chat-completions/handler.ts +2 -5
  40. package/src/endpoints/chat-completions/otel.ts +6 -0
  41. package/src/endpoints/chat-completions/schema.ts +85 -43
  42. package/src/endpoints/embeddings/handler.ts +5 -5
  43. package/src/endpoints/embeddings/schema.ts +1 -1
  44. package/src/errors/gateway.ts +2 -0
  45. package/src/lifecycle.ts +7 -11
  46. package/src/logger/default.ts +34 -8
  47. package/src/middleware/utils.ts +1 -0
  48. package/src/models/amazon/middleware.ts +1 -0
  49. package/src/models/anthropic/middleware.test.ts +332 -1
  50. package/src/models/anthropic/middleware.ts +83 -19
  51. package/src/models/google/middleware.test.ts +31 -0
  52. package/src/models/google/middleware.ts +18 -0
  53. package/src/models/google/presets.ts +13 -2
  54. package/src/models/openai/middleware.ts +1 -0
  55. package/src/models/types.ts +1 -0
  56. package/src/providers/bedrock/index.ts +1 -0
  57. package/src/providers/bedrock/middleware.test.ts +73 -0
  58. package/src/providers/bedrock/middleware.ts +43 -0
  59. package/src/telemetry/http.ts +0 -3
  60. package/src/types.ts +19 -23
  61. package/src/utils/request.ts +5 -33
  62. package/src/utils/response.ts +3 -3
@@ -0,0 +1,43 @@
1
+ import type { LanguageModelMiddleware } from "ai";
2
+
3
+ import { modelMiddlewareMatcher } from "../../middleware/matcher";
4
+
5
+ export const bedrockAnthropicReasoningMiddleware: LanguageModelMiddleware = {
6
+ specificationVersion: "v3",
7
+ // eslint-disable-next-line require-await
8
+ transformParams: async ({ params, model }) => {
9
+ if (!model.modelId.includes("claude")) return params;
10
+
11
+ const bedrock = params.providerOptions?.["bedrock"];
12
+ if (!bedrock || typeof bedrock !== "object") return params;
13
+
14
+ const bedrockOptions = bedrock as Record<string, unknown>;
15
+ const thinking = bedrockOptions["thinking"];
16
+ const effort = bedrockOptions["effort"];
17
+
18
+ if (!thinking && effort === undefined) return params;
19
+
20
+ const target = (bedrockOptions["reasoningConfig"] ??= {}) as Record<string, unknown>;
21
+
22
+ if (thinking && typeof thinking === "object") {
23
+ const thinkingOptions = thinking as Record<string, unknown>;
24
+ if (thinkingOptions["type"] !== undefined) {
25
+ target["type"] = thinkingOptions["type"];
26
+ }
27
+ if (thinkingOptions["budgetTokens"] !== undefined) {
28
+ target["budgetTokens"] = thinkingOptions["budgetTokens"];
29
+ }
30
+ }
31
+
32
+ if (effort !== undefined) target["maxReasoningEffort"] = effort;
33
+
34
+ delete bedrockOptions["thinking"];
35
+ delete bedrockOptions["effort"];
36
+
37
+ return params;
38
+ },
39
+ };
40
+
41
+ modelMiddlewareMatcher.useForProvider("amazon-bedrock", {
42
+ language: [bedrockAnthropicReasoningMiddleware],
43
+ });
@@ -1,5 +1,4 @@
1
1
  import { type TelemetrySignalLevel } from "../types";
2
- import { resolveRequestId } from "../utils/headers";
3
2
 
4
3
  const headerArr = (h: Headers, k: string) => (h.has(k) ? [h.get(k)!] : undefined);
5
4
 
@@ -29,8 +28,6 @@ export const getRequestAttributes = (request: Request, signalLevel?: TelemetrySi
29
28
 
30
29
  if (signalLevel !== "required") {
31
30
  Object.assign(attrs, {
32
- // FUTURE: does ElysiaJS and other frameworks attach request id?
33
- "http.request.id": resolveRequestId(request),
34
31
  "user_agent.original": request.headers.get("user-agent") ?? undefined,
35
32
  });
36
33
  }
package/src/types.ts CHANGED
@@ -12,20 +12,6 @@ import type { Logger, LoggerConfig } from "./logger";
12
12
  import type { ModelCatalog, ModelId } from "./models/types";
13
13
  import type { ProviderId, ProviderRegistry } from "./providers/types";
14
14
 
15
- /**
16
- * Request overrides returned from the `onRequest` hook.
17
- */
18
- export type RequestPatch = {
19
- /**
20
- * Headers to merge into the incoming request.
21
- */
22
- headers?: HeadersInit;
23
- /**
24
- * Body to replace on the incoming request.
25
- */
26
- body?: BodyInit;
27
- };
28
-
29
15
  /**
30
16
  * Per-request context shared across handlers and hooks.
31
17
  */
@@ -46,6 +32,10 @@ export type GatewayContext = {
46
32
  * Incoming request for the handler.
47
33
  */
48
34
  request: Request;
35
+ /**
36
+ * Resolved request ID for logging and telemetry.
37
+ */
38
+ requestId: string;
49
39
  /**
50
40
  * Parsed body from the request.
51
41
  */
@@ -95,13 +85,22 @@ export type HookContext = Omit<Readonly<GatewayContext>, "state"> & {
95
85
  type RequiredHookContext<K extends keyof GatewayContext> = Omit<HookContext, K> &
96
86
  Required<Pick<HookContext, K>>;
97
87
  export type OnRequestHookContext = RequiredHookContext<"request">;
98
- export type BeforeHookContext = RequiredHookContext<"request" | "body" | "operation">;
99
- export type ResolveModelHookContext = RequiredHookContext<"request" | "body" | "modelId">;
88
+ export type BeforeHookContext = RequiredHookContext<"request" | "operation" | "body">;
89
+ export type ResolveModelHookContext = RequiredHookContext<
90
+ "request" | "operation" | "body" | "modelId"
91
+ >;
100
92
  export type ResolveProviderHookContext = RequiredHookContext<
101
- "request" | "body" | "modelId" | "resolvedModelId" | "operation"
93
+ "request" | "operation" | "body" | "modelId" | "resolvedModelId"
102
94
  >;
103
95
  export type AfterHookContext = RequiredHookContext<
104
- "request" | "result" | "provider" | "resolvedModelId" | "operation"
96
+ | "request"
97
+ | "operation"
98
+ | "body"
99
+ | "modelId"
100
+ | "resolvedModelId"
101
+ | "provider"
102
+ | "resolvedProviderId"
103
+ | "result"
105
104
  >;
106
105
  export type OnResponseHookContext = RequiredHookContext<"request" | "response">;
107
106
 
@@ -111,12 +110,9 @@ export type OnResponseHookContext = RequiredHookContext<"request" | "response">;
111
110
  export type GatewayHooks = {
112
111
  /**
113
112
  * Runs before any endpoint handler logic.
114
- * @returns Optional RequestPatch to merge into headers / override body,
115
- * or Response to short-circuit the request.
113
+ * @returns Optional Response to short-circuit the request.
116
114
  */
117
- onRequest?: (
118
- ctx: OnRequestHookContext,
119
- ) => void | RequestPatch | Response | Promise<void | RequestPatch | Response>;
115
+ onRequest?: (ctx: OnRequestHookContext) => void | Response | Promise<void | Response>;
120
116
  /**
121
117
  * Runs after request JSON is parsed and validated for chat completions / embeddings.
122
118
  * @returns Replacement parsed body, or undefined to keep original.
@@ -1,22 +1,13 @@
1
- import type { RequestPatch } from "../types";
2
-
3
1
  import pkg from "../../package.json" with { type: "json" };
4
- import { REQUEST_ID_HEADER } from "./headers";
2
+ import { resolveRequestId } from "./headers";
5
3
 
6
4
  const GATEWAY_VERSION = pkg.version;
7
5
 
8
- export const prepareRequestHeaders = (request: Request) => {
9
- const existingRequestId = request.headers.get(REQUEST_ID_HEADER);
10
- if (existingRequestId) return;
11
-
12
- const requestId =
13
- "req_" + crypto.getRandomValues(new Uint32Array(2)).reduce((s, n) => s + n.toString(36), "");
6
+ const createRequestId = () =>
7
+ "req_" + crypto.getRandomValues(new Uint32Array(2)).reduce((s, n) => s + n.toString(36), "");
14
8
 
15
- const headers = new Headers(request.headers);
16
- headers.set(REQUEST_ID_HEADER, requestId);
17
-
18
- return headers;
19
- };
9
+ export const resolveOrCreateRequestId = (request: Request) =>
10
+ resolveRequestId(request) ?? createRequestId();
20
11
 
21
12
  export const prepareForwardHeaders = (request: Request): Record<string, string> => {
22
13
  const userAgent = request.headers.get("user-agent");
@@ -28,22 +19,3 @@ export const prepareForwardHeaders = (request: Request): Record<string, string>
28
19
  "user-agent": appendedUserAgent,
29
20
  };
30
21
  };
31
-
32
- export const maybeApplyRequestPatch = (request: Request, patch: RequestPatch) => {
33
- if (!patch.headers && patch.body === undefined) return request;
34
-
35
- if (!patch.headers) {
36
- // eslint-disable-next-line no-invalid-fetch-options
37
- return new Request(request, { body: patch.body });
38
- }
39
-
40
- const headers = new Headers(request.headers);
41
- for (const [key, value] of new Headers(patch.headers)) {
42
- headers.set(key, value);
43
- }
44
-
45
- const init: RequestInit = { headers };
46
- if (patch.body !== undefined) init.body = patch.body;
47
-
48
- return new Request(request, init);
49
- };
@@ -1,4 +1,4 @@
1
- import { REQUEST_ID_HEADER, resolveRequestId } from "./headers";
1
+ import { REQUEST_ID_HEADER } from "./headers";
2
2
 
3
3
  const TEXT_ENCODER = new TextEncoder();
4
4
 
@@ -15,8 +15,8 @@ class JsonToSseTransformStream extends TransformStream<unknown, string> {
15
15
  }
16
16
  }
17
17
 
18
- export const prepareResponseInit = (request: Request): ResponseInit => ({
19
- headers: { [REQUEST_ID_HEADER]: resolveRequestId(request)! },
18
+ export const prepareResponseInit = (requestId: string): ResponseInit => ({
19
+ headers: { [REQUEST_ID_HEADER]: requestId },
20
20
  });
21
21
 
22
22
  export const mergeResponseInit = (