@hebo-ai/gateway 0.9.2 → 0.9.4
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.
- package/dist/config.d.ts +2 -0
- package/dist/config.js +125 -0
- package/dist/endpoints/chat-completions/converters.d.ts +26 -0
- package/dist/endpoints/chat-completions/converters.js +525 -0
- package/dist/endpoints/chat-completions/handler.d.ts +2 -0
- package/dist/endpoints/chat-completions/handler.js +152 -0
- package/dist/endpoints/chat-completions/index.d.ts +4 -0
- package/dist/endpoints/chat-completions/index.js +4 -0
- package/dist/endpoints/chat-completions/otel.d.ts +5 -0
- package/dist/endpoints/chat-completions/otel.js +177 -0
- package/dist/endpoints/chat-completions/schema.d.ts +1170 -0
- package/dist/endpoints/chat-completions/schema.js +252 -0
- package/dist/endpoints/conversations/converters.d.ts +8 -0
- package/dist/endpoints/conversations/converters.js +29 -0
- package/dist/endpoints/conversations/handler.d.ts +2 -0
- package/dist/endpoints/conversations/handler.js +259 -0
- package/dist/endpoints/conversations/index.d.ts +3 -0
- package/dist/endpoints/conversations/index.js +3 -0
- package/dist/endpoints/conversations/schema.d.ts +1511 -0
- package/dist/endpoints/conversations/schema.js +74 -0
- package/dist/endpoints/conversations/storage/dialects/greptime.d.ts +10 -0
- package/dist/endpoints/conversations/storage/dialects/greptime.js +87 -0
- package/dist/endpoints/conversations/storage/dialects/mysql.d.ts +12 -0
- package/dist/endpoints/conversations/storage/dialects/mysql.js +118 -0
- package/dist/endpoints/conversations/storage/dialects/postgres.d.ts +16 -0
- package/dist/endpoints/conversations/storage/dialects/postgres.js +185 -0
- package/dist/endpoints/conversations/storage/dialects/sqlite.d.ts +11 -0
- package/dist/endpoints/conversations/storage/dialects/sqlite.js +176 -0
- package/dist/endpoints/conversations/storage/dialects/types.d.ts +42 -0
- package/dist/endpoints/conversations/storage/dialects/types.js +0 -0
- package/dist/endpoints/conversations/storage/dialects/utils.d.ts +25 -0
- package/dist/endpoints/conversations/storage/dialects/utils.js +80 -0
- package/dist/endpoints/conversations/storage/memory.d.ts +25 -0
- package/dist/endpoints/conversations/storage/memory.js +200 -0
- package/dist/endpoints/conversations/storage/sql.d.ts +33 -0
- package/dist/endpoints/conversations/storage/sql.js +276 -0
- package/dist/endpoints/conversations/storage/types.d.ts +39 -0
- package/dist/endpoints/conversations/storage/types.js +0 -0
- package/dist/endpoints/embeddings/converters.d.ts +10 -0
- package/dist/endpoints/embeddings/converters.js +31 -0
- package/dist/endpoints/embeddings/handler.d.ts +2 -0
- package/dist/endpoints/embeddings/handler.js +99 -0
- package/dist/endpoints/embeddings/index.d.ts +4 -0
- package/dist/endpoints/embeddings/index.js +4 -0
- package/dist/endpoints/embeddings/otel.d.ts +5 -0
- package/dist/endpoints/embeddings/otel.js +29 -0
- package/dist/endpoints/embeddings/schema.d.ts +44 -0
- package/dist/endpoints/embeddings/schema.js +29 -0
- package/dist/endpoints/models/converters.d.ts +6 -0
- package/dist/endpoints/models/converters.js +42 -0
- package/dist/endpoints/models/handler.d.ts +2 -0
- package/dist/endpoints/models/handler.js +29 -0
- package/dist/endpoints/models/index.d.ts +3 -0
- package/dist/endpoints/models/index.js +3 -0
- package/dist/endpoints/models/schema.d.ts +42 -0
- package/dist/endpoints/models/schema.js +31 -0
- package/dist/endpoints/responses/converters.d.ts +17 -0
- package/dist/endpoints/responses/converters.js +1037 -0
- package/dist/endpoints/responses/handler.d.ts +2 -0
- package/dist/endpoints/responses/handler.js +141 -0
- package/dist/endpoints/responses/index.d.ts +4 -0
- package/dist/endpoints/responses/index.js +4 -0
- package/dist/endpoints/responses/otel.d.ts +6 -0
- package/dist/endpoints/responses/otel.js +225 -0
- package/dist/endpoints/responses/schema.d.ts +2109 -0
- package/dist/endpoints/responses/schema.js +314 -0
- package/dist/endpoints/shared/converters.d.ts +56 -0
- package/dist/endpoints/shared/converters.js +180 -0
- package/dist/endpoints/shared/schema.d.ts +70 -0
- package/dist/endpoints/shared/schema.js +46 -0
- package/dist/errors/ai-sdk.d.ts +2 -0
- package/dist/errors/ai-sdk.js +52 -0
- package/dist/errors/gateway.d.ts +5 -0
- package/dist/errors/gateway.js +13 -0
- package/dist/errors/openai.d.ts +15 -0
- package/dist/errors/openai.js +40 -0
- package/dist/errors/utils.d.ts +24 -0
- package/dist/errors/utils.js +46 -0
- package/dist/gateway.d.ts +11 -0
- package/dist/gateway.js +44 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +10 -0
- package/dist/lifecycle.d.ts +3 -0
- package/dist/lifecycle.js +114 -0
- package/dist/logger/default.d.ts +4 -0
- package/dist/logger/default.js +81 -0
- package/dist/logger/index.d.ts +11 -0
- package/dist/logger/index.js +25 -0
- package/dist/middleware/common.d.ts +12 -0
- package/dist/middleware/common.js +146 -0
- package/dist/middleware/debug.d.ts +3 -0
- package/dist/middleware/debug.js +27 -0
- package/dist/middleware/matcher.d.ts +28 -0
- package/dist/middleware/matcher.js +118 -0
- package/dist/middleware/utils.d.ts +2 -0
- package/dist/middleware/utils.js +24 -0
- package/dist/models/amazon/index.d.ts +2 -0
- package/dist/models/amazon/index.js +2 -0
- package/dist/models/amazon/middleware.d.ts +3 -0
- package/dist/models/amazon/middleware.js +69 -0
- package/dist/models/amazon/presets.d.ts +345 -0
- package/dist/models/amazon/presets.js +80 -0
- package/dist/models/anthropic/index.d.ts +2 -0
- package/dist/models/anthropic/index.js +2 -0
- package/dist/models/anthropic/middleware.d.ts +5 -0
- package/dist/models/anthropic/middleware.js +128 -0
- package/dist/models/anthropic/presets.d.ts +711 -0
- package/dist/models/anthropic/presets.js +140 -0
- package/dist/models/catalog.d.ts +4 -0
- package/dist/models/catalog.js +8 -0
- package/dist/models/cohere/index.d.ts +2 -0
- package/dist/models/cohere/index.js +2 -0
- package/dist/models/cohere/middleware.d.ts +3 -0
- package/dist/models/cohere/middleware.js +62 -0
- package/dist/models/cohere/presets.d.ts +411 -0
- package/dist/models/cohere/presets.js +134 -0
- package/dist/models/google/index.d.ts +2 -0
- package/dist/models/google/index.js +2 -0
- package/dist/models/google/middleware.d.ts +8 -0
- package/dist/models/google/middleware.js +118 -0
- package/dist/models/google/presets.d.ts +815 -0
- package/dist/models/google/presets.js +184 -0
- package/dist/models/meta/index.d.ts +1 -0
- package/dist/models/meta/index.js +1 -0
- package/dist/models/meta/presets.d.ts +483 -0
- package/dist/models/meta/presets.js +105 -0
- package/dist/models/openai/index.d.ts +2 -0
- package/dist/models/openai/index.js +2 -0
- package/dist/models/openai/middleware.d.ts +4 -0
- package/dist/models/openai/middleware.js +89 -0
- package/dist/models/openai/presets.d.ts +1319 -0
- package/dist/models/openai/presets.js +277 -0
- package/dist/models/types.d.ts +20 -0
- package/dist/models/types.js +100 -0
- package/dist/models/voyage/index.d.ts +2 -0
- package/dist/models/voyage/index.js +2 -0
- package/dist/models/voyage/middleware.d.ts +2 -0
- package/dist/models/voyage/middleware.js +19 -0
- package/dist/models/voyage/presets.d.ts +436 -0
- package/dist/models/voyage/presets.js +85 -0
- package/dist/providers/anthropic/canonical.d.ts +3 -0
- package/dist/providers/anthropic/canonical.js +9 -0
- package/dist/providers/anthropic/index.d.ts +1 -0
- package/dist/providers/anthropic/index.js +1 -0
- package/dist/providers/bedrock/canonical.d.ts +17 -0
- package/dist/providers/bedrock/canonical.js +64 -0
- package/dist/providers/bedrock/index.d.ts +2 -0
- package/dist/providers/bedrock/index.js +2 -0
- package/dist/providers/bedrock/middleware.d.ts +5 -0
- package/dist/providers/bedrock/middleware.js +133 -0
- package/dist/providers/cohere/canonical.d.ts +3 -0
- package/dist/providers/cohere/canonical.js +17 -0
- package/dist/providers/cohere/index.d.ts +1 -0
- package/dist/providers/cohere/index.js +1 -0
- package/dist/providers/groq/canonical.d.ts +3 -0
- package/dist/providers/groq/canonical.js +12 -0
- package/dist/providers/groq/index.d.ts +2 -0
- package/dist/providers/groq/index.js +2 -0
- package/dist/providers/groq/middleware.d.ts +2 -0
- package/dist/providers/groq/middleware.js +30 -0
- package/dist/providers/openai/canonical.d.ts +3 -0
- package/dist/providers/openai/canonical.js +8 -0
- package/dist/providers/openai/index.d.ts +1 -0
- package/dist/providers/openai/index.js +1 -0
- package/dist/providers/registry.d.ts +24 -0
- package/dist/providers/registry.js +103 -0
- package/dist/providers/types.d.ts +7 -0
- package/dist/providers/types.js +11 -0
- package/dist/providers/vertex/canonical.d.ts +3 -0
- package/dist/providers/vertex/canonical.js +8 -0
- package/dist/providers/vertex/index.d.ts +2 -0
- package/dist/providers/vertex/index.js +2 -0
- package/dist/providers/vertex/middleware.d.ts +2 -0
- package/dist/providers/vertex/middleware.js +47 -0
- package/dist/providers/voyage/canonical.d.ts +3 -0
- package/dist/providers/voyage/canonical.js +7 -0
- package/dist/providers/voyage/index.d.ts +1 -0
- package/dist/providers/voyage/index.js +1 -0
- package/dist/telemetry/ai-sdk.d.ts +2 -0
- package/dist/telemetry/ai-sdk.js +31 -0
- package/dist/telemetry/baggage.d.ts +1 -0
- package/dist/telemetry/baggage.js +24 -0
- package/dist/telemetry/fetch.d.ts +2 -0
- package/dist/telemetry/fetch.js +49 -0
- package/dist/telemetry/gen-ai.d.ts +7 -0
- package/dist/telemetry/gen-ai.js +112 -0
- package/dist/telemetry/http.d.ts +3 -0
- package/dist/telemetry/http.js +54 -0
- package/dist/telemetry/index.d.ts +1 -0
- package/dist/telemetry/index.js +1 -0
- package/dist/telemetry/memory.d.ts +2 -0
- package/dist/telemetry/memory.js +43 -0
- package/dist/telemetry/span.d.ts +13 -0
- package/dist/telemetry/span.js +60 -0
- package/dist/types.d.ts +231 -0
- package/dist/types.js +2 -0
- package/dist/utils/body.d.ts +19 -0
- package/dist/utils/body.js +99 -0
- package/dist/utils/env.d.ts +2 -0
- package/dist/utils/env.js +7 -0
- package/dist/utils/headers.d.ts +4 -0
- package/dist/utils/headers.js +22 -0
- package/dist/utils/preset.d.ts +10 -0
- package/dist/utils/preset.js +41 -0
- package/dist/utils/request.d.ts +2 -0
- package/dist/utils/request.js +43 -0
- package/dist/utils/response.d.ts +6 -0
- package/dist/utils/response.js +55 -0
- package/dist/utils/stream.d.ts +9 -0
- package/dist/utils/stream.js +100 -0
- package/dist/utils/url.d.ts +4 -0
- package/dist/utils/url.js +21 -0
- package/package.json +1 -1
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
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 type Preset<Id extends string, T extends Record<string, unknown>, Base extends DeepPartial<T>> = <O extends DeepPartial<T>>(override?: O) => Record<Id, Base & O>;
|
|
10
|
+
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) => Preset<Id, T, Base>;
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import pkg from "../../package.json" with { type: "json" };
|
|
2
|
+
import { resolveRequestId } from "./headers";
|
|
3
|
+
const GATEWAY_VERSION = pkg.version;
|
|
4
|
+
const FORWARD_HEADER_ALLOWLIST = [
|
|
5
|
+
// OpenAI + OpenAI-compatible providers (Azure, Groq, Together, Fireworks, etc.)
|
|
6
|
+
"openai-beta",
|
|
7
|
+
"openai-organization",
|
|
8
|
+
"openai-project",
|
|
9
|
+
// OpenRouter
|
|
10
|
+
"x-openrouter-categories",
|
|
11
|
+
"x-openrouter-title",
|
|
12
|
+
"x-title",
|
|
13
|
+
// Anthropic
|
|
14
|
+
"anthropic-beta",
|
|
15
|
+
// AWS Bedrock
|
|
16
|
+
"x-amzn-bedrock-guardrailidentifier",
|
|
17
|
+
"x-amzn-bedrock-guardrailversion",
|
|
18
|
+
"x-amzn-bedrock-performanceconfig-latency",
|
|
19
|
+
"x-amzn-bedrock-trace",
|
|
20
|
+
// Cohere
|
|
21
|
+
"x-client-name",
|
|
22
|
+
// Vertex provisioned throughput / endpoint routing
|
|
23
|
+
"x-vertex-ai-endpoint-id",
|
|
24
|
+
"x-vertex-ai-llm-request-type",
|
|
25
|
+
"x-vertex-ai-llm-shared-request-type",
|
|
26
|
+
];
|
|
27
|
+
const createRequestId = () => "req_" + crypto.getRandomValues(new Uint32Array(2)).reduce((s, n) => s + n.toString(36), "");
|
|
28
|
+
export const resolveOrCreateRequestId = (request) => resolveRequestId(request) ?? createRequestId();
|
|
29
|
+
export const prepareForwardHeaders = (request) => {
|
|
30
|
+
const userAgent = request.headers.get("user-agent");
|
|
31
|
+
const appendedUserAgent = userAgent
|
|
32
|
+
? `${userAgent} @hebo-ai/gateway/${GATEWAY_VERSION}`
|
|
33
|
+
: `@hebo-ai/gateway/${GATEWAY_VERSION}`;
|
|
34
|
+
const headers = {
|
|
35
|
+
"user-agent": appendedUserAgent,
|
|
36
|
+
};
|
|
37
|
+
for (const key of FORWARD_HEADER_ALLOWLIST) {
|
|
38
|
+
const value = request.headers.get(key);
|
|
39
|
+
if (value !== null)
|
|
40
|
+
headers[key] = value;
|
|
41
|
+
}
|
|
42
|
+
return headers;
|
|
43
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SseFrame } from "./stream";
|
|
2
|
+
export declare const prepareResponseInit: (requestId: string) => ResponseInit;
|
|
3
|
+
export declare const mergeResponseInit: (defaultHeaders: HeadersInit, responseInit?: ResponseInit) => ResponseInit;
|
|
4
|
+
export declare const toResponse: (result: ReadableStream<SseFrame> | Uint8Array<ArrayBuffer> | object | string, responseInit?: ResponseInit, streamOptions?: {
|
|
5
|
+
onDone?: (status: number, reason?: unknown) => void;
|
|
6
|
+
}) => Response;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { REQUEST_ID_HEADER } from "./headers";
|
|
2
|
+
import { toSseStream } from "./stream";
|
|
3
|
+
const TEXT_ENCODER = new TextEncoder();
|
|
4
|
+
export const prepareResponseInit = (requestId) => ({
|
|
5
|
+
headers: { [REQUEST_ID_HEADER]: requestId },
|
|
6
|
+
});
|
|
7
|
+
export const mergeResponseInit = (defaultHeaders, responseInit) => {
|
|
8
|
+
const headers = new Headers(defaultHeaders);
|
|
9
|
+
const override = responseInit?.headers;
|
|
10
|
+
if (override) {
|
|
11
|
+
new Headers(override).forEach((value, key) => {
|
|
12
|
+
headers.set(key, value);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
if (!responseInit)
|
|
16
|
+
return { headers };
|
|
17
|
+
return {
|
|
18
|
+
status: responseInit.status,
|
|
19
|
+
statusText: responseInit.statusText,
|
|
20
|
+
headers,
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export const toResponse = (result, responseInit, streamOptions) => {
|
|
24
|
+
let body;
|
|
25
|
+
const isStream = result instanceof ReadableStream;
|
|
26
|
+
if (isStream) {
|
|
27
|
+
body = toSseStream(result, streamOptions);
|
|
28
|
+
}
|
|
29
|
+
else if (result instanceof Uint8Array) {
|
|
30
|
+
body = result;
|
|
31
|
+
}
|
|
32
|
+
else if (typeof result === "string") {
|
|
33
|
+
body = TEXT_ENCODER.encode(result);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
body = TEXT_ENCODER.encode(JSON.stringify(result));
|
|
37
|
+
}
|
|
38
|
+
if (!responseInit?.statusText) {
|
|
39
|
+
const status = responseInit?.status ?? 200;
|
|
40
|
+
const statusText = "OK";
|
|
41
|
+
const headers = responseInit?.headers;
|
|
42
|
+
responseInit = headers ? { status, statusText, headers } : { status, statusText };
|
|
43
|
+
}
|
|
44
|
+
const init = mergeResponseInit(isStream
|
|
45
|
+
? {
|
|
46
|
+
"content-type": "text/event-stream",
|
|
47
|
+
"cache-control": "no-cache",
|
|
48
|
+
connection: "keep-alive",
|
|
49
|
+
}
|
|
50
|
+
: {
|
|
51
|
+
"content-type": "application/json",
|
|
52
|
+
"content-length": String(body.byteLength),
|
|
53
|
+
}, responseInit);
|
|
54
|
+
return new Response(body, init);
|
|
55
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type SseFrame<T = unknown, E extends string | undefined = string | undefined> = {
|
|
2
|
+
data: T;
|
|
3
|
+
event?: E;
|
|
4
|
+
};
|
|
5
|
+
export type SseErrorFrame = SseFrame<Error, "error" | undefined>;
|
|
6
|
+
export declare function toSseStream(src: ReadableStream<SseFrame>, options?: {
|
|
7
|
+
onDone?: (status: number, reason?: unknown) => void;
|
|
8
|
+
keepAliveMs?: number;
|
|
9
|
+
}): ReadableStream<Uint8Array>;
|
|
@@ -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
|
+
void 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
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const parseDataUrl = (url) => {
|
|
2
|
+
if (url.slice(0, 5).toLowerCase() !== "data:") {
|
|
3
|
+
return { mimeType: "", dataStart: 0 };
|
|
4
|
+
}
|
|
5
|
+
const MAX_HEADER_LENGTH = 1024;
|
|
6
|
+
const headerEnd = Math.min(url.length, 5 + MAX_HEADER_LENGTH);
|
|
7
|
+
const comma = url.indexOf(",", 5);
|
|
8
|
+
if (comma <= 5 || comma > headerEnd) {
|
|
9
|
+
return { mimeType: "", dataStart: 0 };
|
|
10
|
+
}
|
|
11
|
+
const semi = url.indexOf(";", 5);
|
|
12
|
+
const mimeEnd = semi !== -1 && semi < comma ? semi : comma;
|
|
13
|
+
const mimeType = url.slice(5, mimeEnd).trim();
|
|
14
|
+
if (!mimeType) {
|
|
15
|
+
return { mimeType: "", dataStart: 0 };
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
mimeType,
|
|
19
|
+
dataStart: comma + 1,
|
|
20
|
+
};
|
|
21
|
+
};
|
package/package.json
CHANGED