@hebo-ai/gateway 0.5.2 → 0.6.0-rc0

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 (180) hide show
  1. package/README.md +32 -1
  2. package/package.json +17 -12
  3. package/src/endpoints/chat-completions/converters.test.ts +85 -1
  4. package/src/endpoints/chat-completions/converters.ts +139 -18
  5. package/src/endpoints/chat-completions/handler.test.ts +2 -0
  6. package/src/endpoints/chat-completions/index.ts +1 -0
  7. package/src/endpoints/chat-completions/otel.ts +1 -0
  8. package/src/endpoints/chat-completions/schema.ts +38 -4
  9. package/src/endpoints/embeddings/index.ts +1 -0
  10. package/src/lifecycle.ts +2 -2
  11. package/src/models/anthropic/middleware.test.ts +45 -1
  12. package/src/models/anthropic/middleware.ts +21 -1
  13. package/src/models/google/middleware.test.ts +30 -1
  14. package/src/models/google/middleware.ts +20 -1
  15. package/src/models/openai/middleware.test.ts +32 -1
  16. package/src/models/openai/middleware.ts +25 -1
  17. package/src/providers/bedrock/middleware.test.ts +121 -1
  18. package/src/providers/bedrock/middleware.ts +61 -1
  19. package/src/telemetry/fetch.ts +31 -4
  20. package/src/telemetry/index.ts +1 -0
  21. package/dist/config.d.ts +0 -2
  22. package/dist/config.js +0 -81
  23. package/dist/endpoints/chat-completions/converters.d.ts +0 -43
  24. package/dist/endpoints/chat-completions/converters.js +0 -551
  25. package/dist/endpoints/chat-completions/handler.d.ts +0 -2
  26. package/dist/endpoints/chat-completions/handler.js +0 -145
  27. package/dist/endpoints/chat-completions/index.d.ts +0 -3
  28. package/dist/endpoints/chat-completions/index.js +0 -3
  29. package/dist/endpoints/chat-completions/otel.d.ts +0 -6
  30. package/dist/endpoints/chat-completions/otel.js +0 -134
  31. package/dist/endpoints/chat-completions/schema.d.ts +0 -946
  32. package/dist/endpoints/chat-completions/schema.js +0 -257
  33. package/dist/endpoints/embeddings/converters.d.ts +0 -10
  34. package/dist/endpoints/embeddings/converters.js +0 -31
  35. package/dist/endpoints/embeddings/handler.d.ts +0 -2
  36. package/dist/endpoints/embeddings/handler.js +0 -101
  37. package/dist/endpoints/embeddings/index.d.ts +0 -3
  38. package/dist/endpoints/embeddings/index.js +0 -3
  39. package/dist/endpoints/embeddings/otel.d.ts +0 -6
  40. package/dist/endpoints/embeddings/otel.js +0 -35
  41. package/dist/endpoints/embeddings/schema.d.ts +0 -38
  42. package/dist/endpoints/embeddings/schema.js +0 -26
  43. package/dist/endpoints/models/converters.d.ts +0 -6
  44. package/dist/endpoints/models/converters.js +0 -42
  45. package/dist/endpoints/models/handler.d.ts +0 -2
  46. package/dist/endpoints/models/handler.js +0 -29
  47. package/dist/endpoints/models/index.d.ts +0 -3
  48. package/dist/endpoints/models/index.js +0 -3
  49. package/dist/endpoints/models/schema.d.ts +0 -42
  50. package/dist/endpoints/models/schema.js +0 -31
  51. package/dist/errors/ai-sdk.d.ts +0 -2
  52. package/dist/errors/ai-sdk.js +0 -52
  53. package/dist/errors/gateway.d.ts +0 -5
  54. package/dist/errors/gateway.js +0 -13
  55. package/dist/errors/openai.d.ts +0 -20
  56. package/dist/errors/openai.js +0 -40
  57. package/dist/errors/utils.d.ts +0 -22
  58. package/dist/errors/utils.js +0 -44
  59. package/dist/gateway.d.ts +0 -9
  60. package/dist/gateway.js +0 -34
  61. package/dist/index.d.ts +0 -14
  62. package/dist/index.js +0 -13
  63. package/dist/lifecycle.d.ts +0 -2
  64. package/dist/lifecycle.js +0 -94
  65. package/dist/logger/default.d.ts +0 -4
  66. package/dist/logger/default.js +0 -81
  67. package/dist/logger/index.d.ts +0 -14
  68. package/dist/logger/index.js +0 -25
  69. package/dist/middleware/common.d.ts +0 -12
  70. package/dist/middleware/common.js +0 -145
  71. package/dist/middleware/matcher.d.ts +0 -27
  72. package/dist/middleware/matcher.js +0 -112
  73. package/dist/middleware/utils.d.ts +0 -2
  74. package/dist/middleware/utils.js +0 -27
  75. package/dist/models/amazon/index.d.ts +0 -2
  76. package/dist/models/amazon/index.js +0 -2
  77. package/dist/models/amazon/middleware.d.ts +0 -3
  78. package/dist/models/amazon/middleware.js +0 -65
  79. package/dist/models/amazon/presets.d.ts +0 -2390
  80. package/dist/models/amazon/presets.js +0 -80
  81. package/dist/models/anthropic/index.d.ts +0 -2
  82. package/dist/models/anthropic/index.js +0 -2
  83. package/dist/models/anthropic/middleware.d.ts +0 -4
  84. package/dist/models/anthropic/middleware.js +0 -111
  85. package/dist/models/anthropic/presets.d.ts +0 -4802
  86. package/dist/models/anthropic/presets.js +0 -135
  87. package/dist/models/catalog.d.ts +0 -4
  88. package/dist/models/catalog.js +0 -4
  89. package/dist/models/cohere/index.d.ts +0 -2
  90. package/dist/models/cohere/index.js +0 -2
  91. package/dist/models/cohere/middleware.d.ts +0 -3
  92. package/dist/models/cohere/middleware.js +0 -60
  93. package/dist/models/cohere/presets.d.ts +0 -2918
  94. package/dist/models/cohere/presets.js +0 -134
  95. package/dist/models/google/index.d.ts +0 -2
  96. package/dist/models/google/index.js +0 -2
  97. package/dist/models/google/middleware.d.ts +0 -7
  98. package/dist/models/google/middleware.js +0 -103
  99. package/dist/models/google/presets.d.ts +0 -2553
  100. package/dist/models/google/presets.js +0 -83
  101. package/dist/models/meta/index.d.ts +0 -1
  102. package/dist/models/meta/index.js +0 -1
  103. package/dist/models/meta/presets.d.ts +0 -3254
  104. package/dist/models/meta/presets.js +0 -95
  105. package/dist/models/openai/index.d.ts +0 -2
  106. package/dist/models/openai/index.js +0 -2
  107. package/dist/models/openai/middleware.d.ts +0 -3
  108. package/dist/models/openai/middleware.js +0 -62
  109. package/dist/models/openai/presets.d.ts +0 -6634
  110. package/dist/models/openai/presets.js +0 -213
  111. package/dist/models/types.d.ts +0 -20
  112. package/dist/models/types.js +0 -84
  113. package/dist/models/voyage/index.d.ts +0 -2
  114. package/dist/models/voyage/index.js +0 -2
  115. package/dist/models/voyage/middleware.d.ts +0 -2
  116. package/dist/models/voyage/middleware.js +0 -18
  117. package/dist/models/voyage/presets.d.ts +0 -3471
  118. package/dist/models/voyage/presets.js +0 -85
  119. package/dist/providers/anthropic/canonical.d.ts +0 -3
  120. package/dist/providers/anthropic/canonical.js +0 -9
  121. package/dist/providers/anthropic/index.d.ts +0 -1
  122. package/dist/providers/anthropic/index.js +0 -1
  123. package/dist/providers/bedrock/canonical.d.ts +0 -17
  124. package/dist/providers/bedrock/canonical.js +0 -61
  125. package/dist/providers/bedrock/index.d.ts +0 -2
  126. package/dist/providers/bedrock/index.js +0 -2
  127. package/dist/providers/bedrock/middleware.d.ts +0 -3
  128. package/dist/providers/bedrock/middleware.js +0 -55
  129. package/dist/providers/cohere/canonical.d.ts +0 -3
  130. package/dist/providers/cohere/canonical.js +0 -17
  131. package/dist/providers/cohere/index.d.ts +0 -1
  132. package/dist/providers/cohere/index.js +0 -1
  133. package/dist/providers/groq/canonical.d.ts +0 -3
  134. package/dist/providers/groq/canonical.js +0 -12
  135. package/dist/providers/groq/index.d.ts +0 -1
  136. package/dist/providers/groq/index.js +0 -1
  137. package/dist/providers/openai/canonical.d.ts +0 -3
  138. package/dist/providers/openai/canonical.js +0 -8
  139. package/dist/providers/openai/index.d.ts +0 -1
  140. package/dist/providers/openai/index.js +0 -1
  141. package/dist/providers/registry.d.ts +0 -24
  142. package/dist/providers/registry.js +0 -100
  143. package/dist/providers/types.d.ts +0 -7
  144. package/dist/providers/types.js +0 -11
  145. package/dist/providers/vertex/canonical.d.ts +0 -3
  146. package/dist/providers/vertex/canonical.js +0 -8
  147. package/dist/providers/vertex/index.d.ts +0 -1
  148. package/dist/providers/vertex/index.js +0 -1
  149. package/dist/providers/voyage/canonical.d.ts +0 -3
  150. package/dist/providers/voyage/canonical.js +0 -7
  151. package/dist/providers/voyage/index.d.ts +0 -1
  152. package/dist/providers/voyage/index.js +0 -1
  153. package/dist/telemetry/ai-sdk.d.ts +0 -2
  154. package/dist/telemetry/ai-sdk.js +0 -31
  155. package/dist/telemetry/baggage.d.ts +0 -1
  156. package/dist/telemetry/baggage.js +0 -24
  157. package/dist/telemetry/fetch.d.ts +0 -2
  158. package/dist/telemetry/fetch.js +0 -24
  159. package/dist/telemetry/gen-ai.d.ts +0 -5
  160. package/dist/telemetry/gen-ai.js +0 -60
  161. package/dist/telemetry/http.d.ts +0 -3
  162. package/dist/telemetry/http.js +0 -54
  163. package/dist/telemetry/memory.d.ts +0 -2
  164. package/dist/telemetry/memory.js +0 -27
  165. package/dist/telemetry/span.d.ts +0 -13
  166. package/dist/telemetry/span.js +0 -60
  167. package/dist/telemetry/stream.d.ts +0 -3
  168. package/dist/telemetry/stream.js +0 -51
  169. package/dist/types.d.ts +0 -176
  170. package/dist/types.js +0 -1
  171. package/dist/utils/env.d.ts +0 -2
  172. package/dist/utils/env.js +0 -5
  173. package/dist/utils/headers.d.ts +0 -4
  174. package/dist/utils/headers.js +0 -22
  175. package/dist/utils/preset.d.ts +0 -9
  176. package/dist/utils/preset.js +0 -41
  177. package/dist/utils/request.d.ts +0 -2
  178. package/dist/utils/request.js +0 -14
  179. package/dist/utils/response.d.ts +0 -3
  180. package/dist/utils/response.js +0 -68
@@ -1,60 +0,0 @@
1
- import { metrics } from "@opentelemetry/api";
2
- const meter = metrics.getMeter("@hebo/gateway");
3
- const requestDurationHistogram = meter.createHistogram("gen_ai.server.request.duration", {
4
- description: "End-to-end gateway request duration",
5
- unit: "s",
6
- advice: {
7
- explicitBucketBoundaries: [
8
- 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 240,
9
- ],
10
- },
11
- });
12
- const timePerOutputTokenHistogram = meter.createHistogram("gen_ai.server.time_per_output_token", {
13
- description: "End-to-end gateway request duration per output token",
14
- unit: "s",
15
- advice: {
16
- explicitBucketBoundaries: [
17
- 0.01, 0.025, 0.05, 0.075, 0.1, 0.15, 0.2, 0.3, 0.4, 0.5, 0.75, 1.0, 2.5,
18
- ],
19
- },
20
- });
21
- const tokenUsageHistogram = meter.createHistogram("gen_ai.client.token.usage", {
22
- description: "Token usage reported by upstream model responses",
23
- unit: "{token}",
24
- advice: {
25
- explicitBucketBoundaries: [
26
- 1, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144,
27
- 524288, 1048576,
28
- ],
29
- },
30
- });
31
- // FUTURE: record unsuccessful calls
32
- export const recordRequestDuration = (start, attrs, signalLevel) => {
33
- if (!signalLevel || signalLevel === "off")
34
- return;
35
- requestDurationHistogram.record((performance.now() - start) / 1000, attrs);
36
- };
37
- // FUTURE: record unsuccessful calls
38
- export const recordTimePerOutputToken = (start, tokenAttrs, metricAttrs, signalLevel) => {
39
- if (!signalLevel || (signalLevel !== "recommended" && signalLevel !== "full"))
40
- return;
41
- const outputTokens = tokenAttrs["gen_ai.usage.output_tokens"];
42
- if (typeof outputTokens !== "number" || outputTokens <= 0)
43
- return;
44
- timePerOutputTokenHistogram.record((performance.now() - start) / 1000 / outputTokens, metricAttrs);
45
- };
46
- // FUTURE: record unsuccessful calls
47
- export const recordTokenUsage = (tokenAttrs, metricAttrs, signalLevel) => {
48
- if (!signalLevel || (signalLevel !== "recommended" && signalLevel !== "full"))
49
- return;
50
- const record = (value, tokenType) => {
51
- if (typeof value !== "number")
52
- return;
53
- tokenUsageHistogram.record(value, Object.assign({}, metricAttrs, { "gen_ai.token.type": tokenType }));
54
- };
55
- record(tokenAttrs["gen_ai.usage.input_tokens"], "input");
56
- record(tokenAttrs["gen_ai.usage.output_tokens"], "output");
57
- record(tokenAttrs["gen_ai.usage.total_tokens"], "total");
58
- record(tokenAttrs["gen_ai.usage.cached_tokens"], "cached");
59
- record(tokenAttrs["gen_ai.usage.reasoning_tokens"], "reasoning");
60
- };
@@ -1,3 +0,0 @@
1
- import { type TelemetrySignalLevel } from "../types";
2
- export declare const getRequestAttributes: (request: Request, signalLevel?: TelemetrySignalLevel) => {};
3
- export declare const getResponseAttributes: (response: Response, signalLevel?: TelemetrySignalLevel) => {};
@@ -1,54 +0,0 @@
1
- import {} from "../types";
2
- const headerArr = (h, k) => (h.has(k) ? [h.get(k)] : undefined);
3
- export const getRequestAttributes = (request, signalLevel) => {
4
- if (!signalLevel || signalLevel === "off")
5
- return {};
6
- let url;
7
- try {
8
- // FUTURE: reuse URL from lifecycle
9
- url = new URL(request.url);
10
- }
11
- catch { }
12
- const attrs = {
13
- "http.request.method": request.method,
14
- "url.full": request.url,
15
- "url.path": url?.pathname,
16
- "url.scheme": url?.protocol.replace(":", ""),
17
- "server.address": url?.hostname,
18
- "server.port": url
19
- ? url.port
20
- ? Number(url.port)
21
- : url.protocol === "https:"
22
- ? 443
23
- : 80
24
- : undefined,
25
- };
26
- if (signalLevel !== "required") {
27
- Object.assign(attrs, {
28
- "user_agent.original": request.headers.get("user-agent") ?? undefined,
29
- });
30
- }
31
- if (signalLevel === "full") {
32
- Object.assign(attrs, {
33
- // FUTURE: "url.query"
34
- "http.request.header.content-type": headerArr(request.headers, "content-type"),
35
- "http.request.header.content-length": headerArr(request.headers, "content-length"),
36
- // FUTURE: "client.address"
37
- });
38
- }
39
- return attrs;
40
- };
41
- export const getResponseAttributes = (response, signalLevel) => {
42
- if (!signalLevel || signalLevel === "off")
43
- return {};
44
- const attrs = {
45
- "http.response.status_code": response.status,
46
- };
47
- if (signalLevel === "full") {
48
- Object.assign(attrs, {
49
- "http.response.header.content-type": [headerArr(response.headers, "content-type")],
50
- "http.response.header.content-length": [headerArr(response.headers, "content-length")],
51
- });
52
- }
53
- return attrs;
54
- };
@@ -1,2 +0,0 @@
1
- import type { TelemetrySignalLevel } from "../types";
2
- export declare const recordV8jsMemory: (level?: TelemetrySignalLevel) => void;
@@ -1,27 +0,0 @@
1
- import { metrics } from "@opentelemetry/api";
2
- const meter = metrics.getMeter("@hebo/gateway");
3
- const defaultHeapSpaceAttrs = { "v8js.heap.space.name": "total" };
4
- const heapUsedCounter = meter.createUpDownCounter("v8js.memory.heap.used", {
5
- description: "Used bytes in the V8 heap",
6
- unit: "By",
7
- });
8
- const heapSpacePhysicalSizeCounter = meter.createUpDownCounter("v8js.memory.heap.space.physical_size", {
9
- description: "Physical bytes allocated for the V8 heap space",
10
- unit: "By",
11
- });
12
- const isEnabled = (level) => level === "recommended" || level === "full";
13
- export const recordV8jsMemory = (level) => {
14
- if (!isEnabled(level))
15
- return;
16
- let usage;
17
- try {
18
- usage = globalThis.process?.memoryUsage?.();
19
- }
20
- catch {
21
- return;
22
- }
23
- if (!usage)
24
- return;
25
- heapUsedCounter.add(usage.heapUsed, defaultHeapSpaceAttrs);
26
- heapSpacePhysicalSizeCounter.add(usage.rss, defaultHeapSpaceAttrs);
27
- };
@@ -1,13 +0,0 @@
1
- import type { Attributes, SpanOptions, Tracer } from "@opentelemetry/api";
2
- import type { TelemetrySignalLevel } from "../types";
3
- export declare const setSpanTracer: (tracer?: Tracer) => void;
4
- export declare const setSpanEventsEnabled: (level?: TelemetrySignalLevel) => void;
5
- export declare const startSpan: (name: string, options?: SpanOptions) => import("@opentelemetry/api").Span & {
6
- runWithContext: <T>(fn: () => Promise<T> | T) => T | Promise<T>;
7
- recordError: (_error: unknown) => void;
8
- finish: () => void;
9
- isExisting: boolean;
10
- };
11
- export declare const withSpan: <T>(name: string, run: () => Promise<T> | T, options?: SpanOptions) => Promise<T>;
12
- export declare const addSpanEvent: (name: string, attributes?: Attributes) => void;
13
- export declare const setSpanAttributes: (attributes?: Attributes) => void;
@@ -1,60 +0,0 @@
1
- import { INVALID_SPAN_CONTEXT, SpanKind, SpanStatusCode, context, trace } from "@opentelemetry/api";
2
- const DEFAULT_TRACER_NAME = "@hebo/gateway";
3
- let spanTracer;
4
- let spanEventsEnabled = false;
5
- const NOOP_SPAN = {
6
- runWithContext: (fn) => fn(),
7
- recordError: (_error) => { },
8
- finish: () => { },
9
- isExisting: true,
10
- };
11
- export const setSpanTracer = (tracer) => {
12
- spanTracer = tracer ?? trace.getTracer(DEFAULT_TRACER_NAME);
13
- };
14
- export const setSpanEventsEnabled = (level) => {
15
- spanEventsEnabled = level === "recommended" || level === "full";
16
- };
17
- export const startSpan = (name, options) => {
18
- if (!spanTracer) {
19
- return Object.assign(trace.wrapSpanContext(INVALID_SPAN_CONTEXT), NOOP_SPAN);
20
- }
21
- const parentContext = context.active();
22
- const activeSpan = trace.getActiveSpan();
23
- const span = spanTracer.startSpan(name, { kind: activeSpan ? SpanKind.INTERNAL : SpanKind.SERVER, ...options }, parentContext);
24
- const runWithContext = (fn) => context.with(trace.setSpan(parentContext, span), fn);
25
- const recordError = (error) => {
26
- const err = error instanceof Error ? error : new Error(String(error));
27
- span.recordException(err);
28
- span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
29
- };
30
- const finish = () => {
31
- span.end();
32
- };
33
- return Object.assign(span, { runWithContext, recordError, finish, isExisting: !!activeSpan });
34
- };
35
- export const withSpan = async (name, run, options) => {
36
- if (!spanTracer) {
37
- return await run();
38
- }
39
- const started = startSpan(name, options);
40
- try {
41
- return await started.runWithContext(run);
42
- }
43
- catch (error) {
44
- started.recordError(error);
45
- throw error;
46
- }
47
- finally {
48
- started.finish();
49
- }
50
- };
51
- export const addSpanEvent = (name, attributes) => {
52
- if (!spanEventsEnabled)
53
- return;
54
- trace.getActiveSpan()?.addEvent(name, attributes);
55
- };
56
- export const setSpanAttributes = (attributes) => {
57
- if (!attributes)
58
- return;
59
- trace.getActiveSpan()?.setAttributes(attributes);
60
- };
@@ -1,3 +0,0 @@
1
- export declare const wrapStream: (src: ReadableStream, hooks: {
2
- onDone?: (status: number, reason: unknown) => void;
3
- }) => ReadableStream;
@@ -1,51 +0,0 @@
1
- import { toOpenAIError } from "../errors/openai";
2
- const isErrorChunk = (v) => v instanceof Error || !!v?.error;
3
- export const wrapStream = (src, hooks) => {
4
- let finished = false;
5
- const done = (reader, controller, status, reason) => {
6
- if (!finished) {
7
- finished = true;
8
- hooks.onDone?.(status, reason);
9
- }
10
- reader.cancel(reason).catch(() => { });
11
- controller.close();
12
- };
13
- return new ReadableStream({
14
- async start(controller) {
15
- const reader = src.getReader();
16
- try {
17
- for (;;) {
18
- // eslint-disable-next-line no-await-in-loop
19
- const { value, done: eof } = await reader.read();
20
- if (eof)
21
- break;
22
- const out = isErrorChunk(value) ? toOpenAIError(value) : value;
23
- controller.enqueue(out);
24
- if (out !== value) {
25
- const status = out.error?.type === "invalid_request_error" ? 422 : 502;
26
- done(reader, controller, status, value);
27
- return;
28
- }
29
- }
30
- done(reader, controller, 200);
31
- }
32
- catch (err) {
33
- controller.enqueue(toOpenAIError(err));
34
- done(reader, controller, 502, err);
35
- }
36
- finally {
37
- try {
38
- reader.releaseLock();
39
- }
40
- catch { }
41
- }
42
- },
43
- cancel(reason) {
44
- if (!finished) {
45
- finished = true;
46
- hooks.onDone?.(499, reason);
47
- }
48
- src.cancel(reason).catch(() => { });
49
- },
50
- });
51
- };
package/dist/types.d.ts DELETED
@@ -1,176 +0,0 @@
1
- import type { ProviderV3 } from "@ai-sdk/provider";
2
- import type { Tracer } from "@opentelemetry/api";
3
- import type { ChatCompletions, ChatCompletionsBody, ChatCompletionsChunk } from "./endpoints/chat-completions/schema";
4
- import type { Embeddings, EmbeddingsBody } from "./endpoints/embeddings/schema";
5
- import type { Model, ModelList } from "./endpoints/models";
6
- import type { Logger, LoggerConfig } from "./logger";
7
- import type { ModelCatalog, ModelId } from "./models/types";
8
- import type { ProviderId, ProviderRegistry } from "./providers/types";
9
- /**
10
- * Per-request context shared across handlers and hooks.
11
- */
12
- export type GatewayContext = {
13
- /**
14
- * Mutable bag for passing data between hooks.
15
- */
16
- state: Record<string, unknown>;
17
- /**
18
- * Provider registry from config.
19
- */
20
- providers: ProviderRegistry;
21
- /**
22
- * Model catalog from config.
23
- */
24
- models: ModelCatalog;
25
- /**
26
- * Incoming request for the handler.
27
- */
28
- request: Request;
29
- /**
30
- * Resolved request ID for logging and telemetry.
31
- */
32
- requestId: string;
33
- /**
34
- * Parsed body from the request.
35
- */
36
- body?: ChatCompletionsBody | EmbeddingsBody;
37
- /**
38
- * Incoming model ID.
39
- */
40
- modelId?: ModelId;
41
- /**
42
- * Resolved model ID.
43
- */
44
- resolvedModelId?: ModelId;
45
- /**
46
- * Operation type.
47
- */
48
- operation?: "chat" | "embeddings" | "models";
49
- /**
50
- * Resolved provider instance.
51
- */
52
- provider?: ProviderV3;
53
- /**
54
- * Resolved provider ID.
55
- */
56
- resolvedProviderId?: ProviderId;
57
- /**
58
- * Result returned by the handler (pre-response).
59
- */
60
- result?: ChatCompletions | ReadableStream<ChatCompletionsChunk | Error> | Embeddings | Model | ModelList;
61
- /**
62
- * Response object returned by the handler.
63
- */
64
- response?: Response;
65
- };
66
- /**
67
- * Hook context: all fields readonly except `state`.
68
- */
69
- export type HookContext = Omit<Readonly<GatewayContext>, "state"> & {
70
- state: GatewayContext["state"];
71
- };
72
- type RequiredHookContext<K extends keyof GatewayContext> = Omit<HookContext, K> & Required<Pick<HookContext, K>>;
73
- export type OnRequestHookContext = RequiredHookContext<"request">;
74
- export type BeforeHookContext = RequiredHookContext<"request" | "operation" | "body">;
75
- export type ResolveModelHookContext = RequiredHookContext<"request" | "operation" | "body" | "modelId">;
76
- export type ResolveProviderHookContext = RequiredHookContext<"request" | "operation" | "body" | "modelId" | "resolvedModelId">;
77
- export type AfterHookContext = RequiredHookContext<"request" | "operation" | "body" | "modelId" | "resolvedModelId" | "provider" | "resolvedProviderId" | "result">;
78
- export type OnResponseHookContext = RequiredHookContext<"request" | "response">;
79
- /**
80
- * Hooks to plugin to the gateway lifecycle.
81
- */
82
- export type GatewayHooks = {
83
- /**
84
- * Runs before any endpoint handler logic.
85
- * @returns Optional Response to short-circuit the request.
86
- */
87
- onRequest?: (ctx: OnRequestHookContext) => void | Response | Promise<void | Response>;
88
- /**
89
- * Runs after request JSON is parsed and validated for chat completions / embeddings.
90
- * @returns Replacement parsed body, or undefined to keep original.
91
- */
92
- before?: (ctx: BeforeHookContext) => void | ChatCompletionsBody | EmbeddingsBody | Promise<void | ChatCompletionsBody | EmbeddingsBody>;
93
- /**
94
- * Maps a user-provided model ID or alias to a canonical ID.
95
- * @returns Canonical model ID or undefined to keep original.
96
- */
97
- resolveModelId?: (ctx: ResolveModelHookContext) => ModelId | void | Promise<ModelId | void>;
98
- /**
99
- * Picks a provider instance for the request.
100
- * @returns ProviderV3 to override, or undefined to use default.
101
- */
102
- resolveProvider?: (ctx: ResolveProviderHookContext) => ProviderV3 | void | Promise<ProviderV3 | void>;
103
- /**
104
- * Runs after the endpoint handler.
105
- * @returns Result to replace, or undefined to keep original.
106
- */
107
- after?: (ctx: AfterHookContext) => void | ChatCompletions | ReadableStream<ChatCompletionsChunk | Error> | Embeddings | Promise<void | ChatCompletions | ReadableStream<ChatCompletionsChunk | Error> | Embeddings>;
108
- /**
109
- * Runs after the lifecycle has produced the final Response.
110
- * @returns Replacement Response, or undefined to keep original.
111
- */
112
- onResponse?: (ctx: OnResponseHookContext) => void | Response | Promise<void | Response>;
113
- };
114
- export type TelemetrySignalLevel = "off" | "required" | "recommended" | "full";
115
- /**
116
- * Main configuration object for the gateway.
117
- */
118
- export type GatewayConfig = {
119
- /**
120
- * Optional base path the gateway is mounted under (e.g. "/v1/gateway").
121
- */
122
- basePath?: string;
123
- /**
124
- * Provider registry keyed by canonical provider IDs.
125
- */
126
- providers: ProviderRegistry;
127
- /**
128
- * Model catalog keyed by canonical model IDs.
129
- */
130
- models: ModelCatalog;
131
- /**
132
- * Optional lifecycle hooks for routing, auth, and response shaping.
133
- */
134
- hooks?: GatewayHooks;
135
- /**
136
- * Preferred logger configuration: custom logger or default logger settings.
137
- */
138
- logger?: Logger | LoggerConfig | null;
139
- /**
140
- * Optional AI SDK telemetry configuration.
141
- */
142
- telemetry?: {
143
- /**
144
- * Enable AI SDK OpenTelemetry instrumentation.
145
- * Disabled by default.
146
- */
147
- enabled?: boolean;
148
- /**
149
- * Optional custom OpenTelemetry tracer passed to AI SDK telemetry.
150
- */
151
- tracer?: Tracer;
152
- /**
153
- * Telemetry signal levels by namespace.
154
- * - off: disable the namespace
155
- * - required: minimal baseline
156
- * - recommended: practical defaults
157
- * - full: include all available details
158
- */
159
- signals?: {
160
- gen_ai?: TelemetrySignalLevel;
161
- http?: TelemetrySignalLevel;
162
- hebo?: TelemetrySignalLevel;
163
- };
164
- };
165
- };
166
- export declare const kParsed: unique symbol;
167
- export type GatewayConfigParsed = GatewayConfig & {
168
- [kParsed]: true;
169
- };
170
- export interface Endpoint {
171
- handler: (request: Request, state?: Record<string, unknown>) => Promise<Response>;
172
- }
173
- export interface HeboGateway<Routes extends Record<string, Endpoint>> extends Endpoint {
174
- routes: Routes;
175
- }
176
- export {};
package/dist/types.js DELETED
@@ -1 +0,0 @@
1
- export const kParsed = Symbol("hebo.gateway.parsed");
@@ -1,2 +0,0 @@
1
- export declare const isProduction: () => boolean;
2
- export declare const isTest: () => boolean;
package/dist/utils/env.js DELETED
@@ -1,5 +0,0 @@
1
- const NODE_ENV = typeof process === "undefined"
2
- ? (globalThis.NODE_ENV ?? globalThis.ENV?.NODE_ENV)
3
- : process.env?.NODE_ENV;
4
- export const isProduction = () => NODE_ENV === "production";
5
- export const isTest = () => NODE_ENV === "test";
@@ -1,4 +0,0 @@
1
- export declare const REQUEST_ID_HEADER = "x-request-id";
2
- type HeaderSource = Request | ResponseInit | undefined;
3
- export declare const resolveRequestId: (source: HeaderSource) => string | undefined;
4
- export {};
@@ -1,22 +0,0 @@
1
- export const REQUEST_ID_HEADER = "x-request-id";
2
- export const resolveRequestId = (source) => {
3
- if (!source)
4
- return undefined;
5
- if (source instanceof Request) {
6
- return source.headers.get(REQUEST_ID_HEADER) ?? undefined;
7
- }
8
- const headers = source.headers;
9
- if (!headers)
10
- return undefined;
11
- if (headers instanceof Headers) {
12
- return headers.get(REQUEST_ID_HEADER) ?? undefined;
13
- }
14
- if (Array.isArray(headers)) {
15
- for (const [key, value] of headers) {
16
- if (key.toLowerCase() === REQUEST_ID_HEADER)
17
- return value;
18
- }
19
- return undefined;
20
- }
21
- return headers[REQUEST_ID_HEADER];
22
- };
@@ -1,9 +0,0 @@
1
- export type DeepPartial<T> = T extends (...args: unknown[]) => unknown ? T : T extends readonly (infer U)[] ? readonly DeepPartial<U>[] : T extends object ? {
2
- [K in keyof T]?: DeepPartial<T[K]>;
3
- } : T;
4
- /**
5
- * Deep merge where overrides win.
6
- * Arrays are replaced.
7
- */
8
- export declare function deepMerge<A extends object, B extends object>(base: A, override?: B): A & B;
9
- export declare function presetFor<Ids extends string, T extends Record<string, unknown>>(): <const Id extends Ids, const Base extends DeepPartial<T>>(id: Id, base: Base) => <const O extends DeepPartial<T>>(override?: O) => Record<Id, Base & O>;
@@ -1,41 +0,0 @@
1
- function isPlainObject(v) {
2
- if (!v || typeof v !== "object" || Array.isArray(v))
3
- return false;
4
- const proto = Object.getPrototypeOf(v);
5
- return proto === Object.prototype || proto === null;
6
- }
7
- /**
8
- * Deep merge where overrides win.
9
- * Arrays are replaced.
10
- */
11
- export function deepMerge(base, override) {
12
- if (override === null || override === undefined)
13
- return base;
14
- if (!isPlainObject(base) || !isPlainObject(override)) {
15
- return override;
16
- }
17
- const out = { ...base };
18
- for (const [key, ov] of Object.entries(override)) {
19
- if (ov === undefined)
20
- continue;
21
- const bv = out[key];
22
- if (Array.isArray(ov)) {
23
- out[key] = ov;
24
- continue;
25
- }
26
- if (isPlainObject(bv) && isPlainObject(ov)) {
27
- out[key] = deepMerge(bv, ov);
28
- continue;
29
- }
30
- out[key] = ov;
31
- }
32
- return out;
33
- }
34
- export function presetFor() {
35
- return function preset(id, base) {
36
- return (override) => {
37
- const merged = deepMerge(base, override ?? {});
38
- return { [id]: merged };
39
- };
40
- };
41
- }
@@ -1,2 +0,0 @@
1
- export declare const resolveOrCreateRequestId: (request: Request) => string;
2
- export declare const prepareForwardHeaders: (request: Request) => Record<string, string>;
@@ -1,14 +0,0 @@
1
- import pkg from "../../package.json" with { type: "json" };
2
- import { resolveRequestId } from "./headers";
3
- const GATEWAY_VERSION = pkg.version;
4
- const createRequestId = () => "req_" + crypto.getRandomValues(new Uint32Array(2)).reduce((s, n) => s + n.toString(36), "");
5
- export const resolveOrCreateRequestId = (request) => resolveRequestId(request) ?? createRequestId();
6
- export const prepareForwardHeaders = (request) => {
7
- const userAgent = request.headers.get("user-agent");
8
- const appendedUserAgent = userAgent
9
- ? `${userAgent} @hebo-ai/gateway/${GATEWAY_VERSION}`
10
- : `@hebo-ai/gateway/${GATEWAY_VERSION}`;
11
- return {
12
- "user-agent": appendedUserAgent,
13
- };
14
- };
@@ -1,3 +0,0 @@
1
- export declare const prepareResponseInit: (requestId: string) => ResponseInit;
2
- export declare const mergeResponseInit: (defaultHeaders: HeadersInit, responseInit?: ResponseInit) => ResponseInit;
3
- export declare const toResponse: (result: ReadableStream | Uint8Array<ArrayBuffer> | object | string, responseInit?: ResponseInit) => Response;
@@ -1,68 +0,0 @@
1
- import { REQUEST_ID_HEADER } from "./headers";
2
- const TEXT_ENCODER = new TextEncoder();
3
- class JsonToSseTransformStream extends TransformStream {
4
- constructor() {
5
- super({
6
- transform(part, controller) {
7
- controller.enqueue(`data: ${JSON.stringify(part)}\n\n`);
8
- },
9
- flush(controller) {
10
- controller.enqueue("data: [DONE]\n\n");
11
- },
12
- });
13
- }
14
- }
15
- export const prepareResponseInit = (requestId) => ({
16
- headers: { [REQUEST_ID_HEADER]: requestId },
17
- });
18
- export const mergeResponseInit = (defaultHeaders, responseInit) => {
19
- const headers = new Headers(defaultHeaders);
20
- const override = responseInit?.headers;
21
- if (override) {
22
- new Headers(override).forEach((value, key) => headers.set(key, value));
23
- }
24
- if (!responseInit)
25
- return { headers };
26
- return {
27
- status: responseInit.status,
28
- statusText: responseInit.statusText,
29
- headers,
30
- };
31
- };
32
- export const toResponse = (result, responseInit) => {
33
- let body;
34
- const isStream = result instanceof ReadableStream;
35
- if (isStream) {
36
- body = result.pipeThrough(new JsonToSseTransformStream()).pipeThrough(new TextEncoderStream());
37
- }
38
- else if (result instanceof Uint8Array) {
39
- body = result;
40
- }
41
- else if (typeof result === "string") {
42
- body = TEXT_ENCODER.encode(result);
43
- }
44
- else if (result instanceof Error) {
45
- body = TEXT_ENCODER.encode(JSON.stringify({ message: result.message }));
46
- }
47
- else {
48
- body = TEXT_ENCODER.encode(JSON.stringify(result));
49
- }
50
- if (!responseInit?.statusText) {
51
- const isError = result instanceof Error;
52
- const status = responseInit?.status ?? (isError ? 500 : 200);
53
- const statusText = isError ? "REQUEST_FAILED" : "OK";
54
- const headers = responseInit?.headers;
55
- responseInit = headers ? { status, statusText, headers } : { status, statusText };
56
- }
57
- const init = mergeResponseInit(isStream
58
- ? {
59
- "content-type": "text/event-stream",
60
- "cache-control": "no-cache",
61
- connection: "keep-alive",
62
- }
63
- : {
64
- "content-type": "application/json",
65
- "content-length": String(body.byteLength),
66
- }, responseInit);
67
- return new Response(body, init);
68
- };