@hebo-ai/gateway 0.6.2-rc1 → 0.7.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 (49) hide show
  1. package/README.md +58 -8
  2. package/dist/config.js +28 -1
  3. package/dist/endpoints/chat-completions/converters.d.ts +5 -5
  4. package/dist/endpoints/chat-completions/converters.js +86 -49
  5. package/dist/endpoints/chat-completions/handler.js +4 -4
  6. package/dist/endpoints/chat-completions/otel.d.ts +1 -1
  7. package/dist/endpoints/chat-completions/otel.js +20 -18
  8. package/dist/endpoints/chat-completions/schema.d.ts +47 -23
  9. package/dist/endpoints/chat-completions/schema.js +24 -17
  10. package/dist/endpoints/embeddings/handler.js +2 -2
  11. package/dist/endpoints/embeddings/otel.d.ts +2 -2
  12. package/dist/endpoints/embeddings/otel.js +7 -2
  13. package/dist/endpoints/embeddings/schema.d.ts +6 -0
  14. package/dist/endpoints/embeddings/schema.js +4 -1
  15. package/dist/endpoints/models/handler.js +2 -2
  16. package/dist/errors/openai.d.ts +1 -6
  17. package/dist/lifecycle.d.ts +3 -2
  18. package/dist/lifecycle.js +4 -6
  19. package/dist/middleware/utils.js +0 -1
  20. package/dist/models/amazon/middleware.js +6 -5
  21. package/dist/models/anthropic/middleware.js +13 -13
  22. package/dist/models/cohere/middleware.js +7 -5
  23. package/dist/models/google/middleware.d.ts +1 -1
  24. package/dist/models/google/middleware.js +29 -25
  25. package/dist/models/google/presets.d.ts +28 -0
  26. package/dist/models/google/presets.js +7 -1
  27. package/dist/models/openai/middleware.js +7 -7
  28. package/dist/models/types.d.ts +1 -1
  29. package/dist/models/types.js +1 -0
  30. package/dist/models/voyage/middleware.js +2 -1
  31. package/dist/providers/bedrock/middleware.d.ts +1 -0
  32. package/dist/providers/bedrock/middleware.js +54 -23
  33. package/dist/providers/groq/index.d.ts +1 -0
  34. package/dist/providers/groq/index.js +1 -0
  35. package/dist/providers/groq/middleware.d.ts +2 -0
  36. package/dist/providers/groq/middleware.js +31 -0
  37. package/dist/providers/vertex/index.d.ts +1 -0
  38. package/dist/providers/vertex/index.js +1 -0
  39. package/dist/providers/vertex/middleware.d.ts +2 -0
  40. package/dist/providers/vertex/middleware.js +47 -0
  41. package/dist/types.d.ts +25 -4
  42. package/dist/types.js +1 -0
  43. package/dist/utils/response.d.ts +4 -1
  44. package/dist/utils/response.js +5 -20
  45. package/dist/utils/stream.d.ts +9 -0
  46. package/dist/utils/stream.js +100 -0
  47. package/package.json +5 -1
  48. package/dist/telemetry/stream.d.ts +0 -3
  49. package/dist/telemetry/stream.js +0 -58
@@ -0,0 +1,100 @@
1
+ import { toOpenAIError } from "../errors/openai";
2
+ const TEXT_ENCODER = new TextEncoder();
3
+ const SSE_DONE_CHUNK = TEXT_ENCODER.encode("data: [DONE]\n\n");
4
+ const SSE_KEEP_ALIVE_CHUNK = TEXT_ENCODER.encode(": keep-alive\n\n");
5
+ const SSE_DEFAULT_KEEP_ALIVE_MS = 20_000;
6
+ export function toSseStream(src, options = {}) {
7
+ const keepAliveMs = options.keepAliveMs ?? SSE_DEFAULT_KEEP_ALIVE_MS;
8
+ let reader;
9
+ let timer;
10
+ let finished = false;
11
+ const done = (controller, status, reason) => {
12
+ if (finished)
13
+ return;
14
+ finished = true;
15
+ if (timer)
16
+ clearTimeout(timer);
17
+ try {
18
+ options.onDone?.(status, reason);
19
+ }
20
+ catch { }
21
+ try {
22
+ controller.enqueue(SSE_DONE_CHUNK);
23
+ }
24
+ catch { }
25
+ try {
26
+ controller.close();
27
+ }
28
+ catch { }
29
+ };
30
+ const heartbeat = (controller) => {
31
+ if (timer)
32
+ clearTimeout(timer);
33
+ if (!keepAliveMs || keepAliveMs <= 0 || finished)
34
+ return;
35
+ timer = setTimeout(() => {
36
+ if (finished)
37
+ return;
38
+ try {
39
+ controller.enqueue(SSE_KEEP_ALIVE_CHUNK);
40
+ heartbeat(controller);
41
+ }
42
+ catch { }
43
+ }, keepAliveMs);
44
+ };
45
+ return new ReadableStream({
46
+ start(controller) {
47
+ reader = src.getReader();
48
+ heartbeat(controller);
49
+ },
50
+ async pull(controller) {
51
+ if (finished)
52
+ return;
53
+ try {
54
+ // oxlint-disable-next-line no-await-in-loop
55
+ const result = await reader.read();
56
+ if (result.done) {
57
+ done(controller, 200);
58
+ return;
59
+ }
60
+ const value = result.value;
61
+ if (value.event === "error" || value.data instanceof Error) {
62
+ const error = toOpenAIError(value.data);
63
+ controller.enqueue(TEXT_ENCODER.encode(serializeSseFrame({ event: value.event, data: error })));
64
+ done(controller, error.error.type === "invalid_request_error" ? 422 : 502, value.data);
65
+ reader.cancel(value.data).catch(() => { });
66
+ return;
67
+ }
68
+ controller.enqueue(TEXT_ENCODER.encode(serializeSseFrame(value)));
69
+ heartbeat(controller);
70
+ }
71
+ catch (error) {
72
+ try {
73
+ controller.enqueue(TEXT_ENCODER.encode(serializeSseFrame({
74
+ event: "error",
75
+ data: toOpenAIError(error),
76
+ })));
77
+ }
78
+ catch { }
79
+ done(controller, 502, error);
80
+ }
81
+ },
82
+ cancel(reason) {
83
+ if (finished)
84
+ return;
85
+ finished = true;
86
+ if (timer)
87
+ clearTimeout(timer);
88
+ options.onDone?.(499, reason);
89
+ return reader?.cancel(reason).catch(() => { });
90
+ },
91
+ });
92
+ }
93
+ function serializeSseFrame(frame) {
94
+ let out = "";
95
+ if (frame.event) {
96
+ out += `event: ${frame.event}\n`;
97
+ }
98
+ out += `data: ${JSON.stringify(frame.data)}\n\n`;
99
+ return out;
100
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hebo-ai/gateway",
3
- "version": "0.6.2-rc1",
3
+ "version": "0.7.0",
4
4
  "description": "AI gateway as a framework. For full control over models, routing & lifecycle. OpenAI-compatible /chat/completions, /embeddings & /models.",
5
5
  "keywords": [
6
6
  "ai",
@@ -179,6 +179,7 @@
179
179
  "@ai-sdk/amazon-bedrock": "^4.0.77",
180
180
  "@ai-sdk/anthropic": "^3.0.58",
181
181
  "@ai-sdk/cohere": "^3.0.25",
182
+ "@ai-sdk/google": "^3.0.43",
182
183
  "@ai-sdk/google-vertex": "^4.0.80",
183
184
  "@ai-sdk/groq": "^3.0.29",
184
185
  "@ai-sdk/openai": "^3.0.41",
@@ -196,6 +197,9 @@
196
197
  "@ai-sdk/cohere": {
197
198
  "optional": true
198
199
  },
200
+ "@ai-sdk/google": {
201
+ "optional": true
202
+ },
199
203
  "@ai-sdk/google-vertex": {
200
204
  "optional": true
201
205
  },
@@ -1,3 +0,0 @@
1
- export declare const wrapStream: (src: ReadableStream, hooks: {
2
- onDone?: (status: number, reason: unknown) => void;
3
- }) => ReadableStream;
@@ -1,58 +0,0 @@
1
- import { toOpenAIError } from "../errors/openai";
2
- const isErrorChunk = (v) => v instanceof Error || (typeof v === "object" && v !== null && "error" in v);
3
- export const wrapStream = (src, hooks) => {
4
- let finished = false;
5
- let reader;
6
- const done = (controller, status, reason) => {
7
- if (finished)
8
- return;
9
- finished = true;
10
- hooks.onDone?.(status, reason);
11
- if (status !== 200) {
12
- reader?.cancel(reason).catch(() => { });
13
- }
14
- try {
15
- controller.close();
16
- }
17
- catch { }
18
- };
19
- return new ReadableStream({
20
- async start(controller) {
21
- reader = src.getReader();
22
- try {
23
- for (;;) {
24
- // oxlint-disable-next-line no-await-in-loop, no-unsafe-assignment
25
- const { value, done: eof } = await reader.read();
26
- if (eof)
27
- break;
28
- controller.enqueue(value);
29
- if (isErrorChunk(value)) {
30
- done(controller, toOpenAIError(value).error.type === "invalid_request_error" ? 422 : 502, value);
31
- return;
32
- }
33
- }
34
- done(controller, 200);
35
- }
36
- catch (err) {
37
- try {
38
- controller.enqueue(toOpenAIError(err));
39
- }
40
- catch { }
41
- done(controller, 502, err);
42
- }
43
- finally {
44
- try {
45
- reader?.releaseLock();
46
- }
47
- catch { }
48
- }
49
- },
50
- cancel(reason) {
51
- if (finished)
52
- return;
53
- finished = true;
54
- hooks.onDone?.(499, reason);
55
- reader?.cancel(reason).catch(() => { });
56
- },
57
- });
58
- };