@codefionn/llmleaf-client 0.1.2

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/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # @codefionn/llmleaf-client
2
+
3
+ Official TypeScript / JavaScript client for the [llmleaf](../../) LLM proxy.
4
+
5
+ The wire is **OpenAI/OpenRouter-shaped JSON over HTTP** (see [`../SPEC.md`](../SPEC.md)),
6
+ not protobuf-binary. The [proto](../proto/llmleaf/v1/llmleaf.proto) is the typed source of
7
+ truth; the `protobuf-es` codegen artifact is committed under [`src/gen`](src/gen) and a
8
+ hand-written `fetch`-based transport maps the public model to/from the wire JSON.
9
+
10
+ Zero runtime dependencies beyond the `@bufbuild/protobuf` runtime (used for the generated
11
+ enum/descriptor types). Works in Node 20+, Deno, Bun and browsers — anywhere a global
12
+ `fetch` / `ReadableStream` / `FormData` / `Blob` exists.
13
+
14
+ ## Install
15
+
16
+ ```sh
17
+ npm install @codefionn/llmleaf-client
18
+ ```
19
+
20
+ ## Quickstart
21
+
22
+ ```ts
23
+ import { LlmleafClient, Role } from "@codefionn/llmleaf-client";
24
+
25
+ const client = new LlmleafClient({
26
+ baseUrl: "https://gateway.example.com",
27
+ apiKey: process.env.LLMLEAF_API_KEY!,
28
+ timeoutMs: 30_000, // optional per-request timeout (0/omitted = no timeout)
29
+ adminToken: "…", // optional; enables admin-only fields
30
+ // fetch: customFetch, // optional; defaults to the global fetch
31
+ });
32
+
33
+ // Non-streaming chat
34
+ const res = await client.chat({
35
+ model: "gpt-4o-mini",
36
+ messages: [{ role: Role.USER, content: "Say hi." }],
37
+ });
38
+ console.log(res.choices[0]?.message.content);
39
+
40
+ // Streaming chat — async iterable of ChatCompletionChunk, stops at `data: [DONE]`
41
+ for await (const chunk of client.chatStream({
42
+ model: "gpt-4o-mini",
43
+ messages: [{ role: Role.USER, content: "Count to 5." }],
44
+ })) {
45
+ process.stdout.write(chunk.choices[0]?.delta.content ?? "");
46
+ }
47
+ ```
48
+
49
+ ### Every endpoint
50
+
51
+ ```ts
52
+ await client.chat(req); // POST /v1/chat/completions
53
+ client.chatStream(req); // POST /v1/chat/completions (stream:true)
54
+ await client.embeddings(req); // POST /v1/embeddings (base64 decoded to floats)
55
+ await client.listModels({ type: "llm" }); // GET /v1/models (admin:true adds endpoints)
56
+ await client.speech(req); // POST /v1/audio/speech -> { bytes, contentType }
57
+ await client.voices("tts-1"); // GET /v1/audio/voices
58
+ await client.transcribe(file, req); // POST /v1/audio/transcriptions (multipart)
59
+ await client.createBatch(req); // POST /v1/batches
60
+ await client.getBatch(id); // GET /v1/batches/{id}
61
+ await client.cancelBatch(id); // POST /v1/batches/{id}/cancel
62
+ client.batchResults(id); // GET /v1/batches/{id}/results (async iterable)
63
+ ```
64
+
65
+ Speech returns the raw audio bytes plus the server's content type:
66
+
67
+ ```ts
68
+ const { bytes, contentType } = await client.speech({
69
+ model: "tts-1", input: "hello", voice: "alloy", responseFormat: "mp3",
70
+ });
71
+ ```
72
+
73
+ Transcription takes the audio as a `Blob`/`Uint8Array`/`ArrayBuffer` + filename:
74
+
75
+ ```ts
76
+ const tr = await client.transcribe(
77
+ { data: audioBytes, filename: "audio.mp3", contentType: "audio/mpeg" },
78
+ { model: "whisper-1", responseFormat: "json" },
79
+ );
80
+ // json / verbose_json -> structured TranscriptionResponse
81
+ // text / srt / vtt -> plain text in tr.text
82
+ ```
83
+
84
+ ### Free-form JSON fields
85
+
86
+ `ChatRequest.extra`, `FunctionDef.parameters`, `ResponseFormat.jsonSchema`,
87
+ `EmbeddingRequest.extra`, `SpeechRequest.extra` and `ModelEntry.defaultParameters` are
88
+ **raw JSON strings** — you pass a `JSON.stringify(...)`d object, the transport splices the
89
+ parsed value into the wire body (and `extra` keys are merged at the top level). On decode,
90
+ sub-objects are captured back as raw JSON strings.
91
+
92
+ ```ts
93
+ await client.chat({
94
+ model: "gpt-4o-mini",
95
+ messages: [{ role: Role.USER, content: "hi" }],
96
+ responseFormat: { type: "json_schema", jsonSchema: JSON.stringify({ type: "object" }) },
97
+ extra: JSON.stringify({ provider: { order: ["openai"] } }),
98
+ });
99
+ ```
100
+
101
+ ### Errors
102
+
103
+ Any non-2xx response throws a typed `ApiError`:
104
+
105
+ ```ts
106
+ import { ApiError } from "@codefionn/llmleaf-client";
107
+
108
+ try {
109
+ await client.chat(/* … */);
110
+ } catch (e) {
111
+ if (e instanceof ApiError) {
112
+ console.error(e.status, e.message, e.code); // 403 "model not allowed" "blocked"
113
+ }
114
+ }
115
+ ```
116
+
117
+ Status codes: 400 bad request · 401 missing/invalid key · 403 blocked or model-not-allowed
118
+ · 404 no route for model · 429 key suspended · 502 all upstreams failed.
119
+
120
+ ## Example
121
+
122
+ [`examples/basic.ts`](examples/basic.ts) runs non-streaming chat, streaming chat and a model
123
+ list. It reads `LLMLEAF_BASE_URL` / `LLMLEAF_API_KEY` (and optional `LLMLEAF_MODEL`):
124
+
125
+ ```sh
126
+ LLMLEAF_BASE_URL=https://gateway.example.com LLMLEAF_API_KEY=sk-… npx tsx examples/basic.ts
127
+ ```
128
+
129
+ ## Regenerate the typed model from the proto
130
+
131
+ The proto is the source of truth. The generated file (`src/gen/llmleaf/v1/llmleaf_pb.ts`)
132
+ is committed; regenerate it after the proto changes:
133
+
134
+ ```sh
135
+ npm install # fetch the @bufbuild/protoc-gen-es plugin into node_modules/.bin
136
+ npm run gen # runs scripts/gen.sh
137
+ ```
138
+
139
+ `scripts/gen.sh` invokes:
140
+
141
+ ```sh
142
+ protoc --plugin=protoc-gen-es=./node_modules/.bin/protoc-gen-es \
143
+ --es_out=src/gen --es_opt=target=ts \
144
+ --proto_path=../proto ../proto/llmleaf/v1/llmleaf.proto
145
+ ```
146
+
147
+ **Toolchain:** `protoc` (libprotoc 35) on `PATH`, plus `@bufbuild/protoc-gen-es`
148
+ (devDependency — run `npm install` first). From the repo root, `make gen-ts` delegates here.
149
+
150
+ ## Scripts
151
+
152
+ | Command | What it does |
153
+ |--------------------|----------------------------------------------------------|
154
+ | `npm run gen` | Regenerate `src/gen` from the proto. |
155
+ | `npm run typecheck`| `tsc --noEmit` over `src` + `examples`. |
156
+ | `npm run build` | Emit ESM + `.d.ts` to `dist/` (library only, Node-free). |
157
+ | `npm run example` | Run `examples/basic.ts` with `tsx`. |
158
+
159
+ ## Caveats (honest)
160
+
161
+ - **Wire mapping is hand-written.** protobuf-es's own JSON codec emits camelCase keys and
162
+ `SCREAMING_CASE` enum names, which do **not** match the OpenAI wire (snake_case keys +
163
+ lowercase tokens like `"assistant"`). So the public surface is plain TS interfaces and
164
+ [`src/wire.ts`](src/wire.ts) does the mapping; the generated descriptors stay available
165
+ under the `gen` export for advanced reflection use.
166
+ - **Decoders are lenient.** Responses are parsed defensively (missing/extra fields tolerated,
167
+ the server is authoritative); they do not strictly validate every field.
168
+ - **No retries / backoff.** One request per call. Wrap calls yourself if you need retries.
169
+ - **Streaming needs a `ReadableStream` body.** Some exotic `fetch` polyfills buffer the whole
170
+ response; use a streaming-capable fetch for `chatStream` / `batchResults`.
171
+ - **Realtime WebSocket is out of scope** for all llmleaf SDKs (per the repo README).
172
+ - **Node < 18** has no global `fetch`/`FormData`; pass a `fetch` implementation and run on
173
+ Node 20+ for `FormData`/`Blob` in transcriptions.
@@ -0,0 +1,73 @@
1
+ import type { ChatRequest, ChatResponse, ChatCompletionChunk, EmbeddingRequest, EmbeddingResponse, SpeechRequest, SpeechResult, VoicesResponse, TranscriptionRequest, TranscriptionResponse, ListModelsResponse, ListModelsOptions, BatchCreateRequest, BatchHandle, BatchResultLine } from "./types.js";
2
+ /** The subset of the WHATWG `fetch` signature this client uses. */
3
+ export type FetchLike = (input: string | URL, init?: RequestInit) => Promise<Response>;
4
+ export interface LlmleafClientOptions {
5
+ /** Gateway base URL, e.g. "https://gateway.example.com". */
6
+ baseUrl: string;
7
+ /** API key sent as `Authorization: Bearer <apiKey>`. */
8
+ apiKey: string;
9
+ /** Per-request timeout in milliseconds. 0 / omitted disables the timeout. */
10
+ timeoutMs?: number;
11
+ /** Optional admin token; sent as `x-admin-token` when an endpoint opts in. */
12
+ adminToken?: string;
13
+ /** Inject a custom fetch (defaults to the global `fetch`). */
14
+ fetch?: FetchLike;
15
+ }
16
+ /** Audio file input for transcriptions: bytes + a filename. */
17
+ export interface TranscriptionFile {
18
+ /** A Blob/File, or raw bytes. */
19
+ data: Blob | Uint8Array | ArrayBuffer;
20
+ /** Filename for the multipart part (e.g. "audio.mp3"). */
21
+ filename: string;
22
+ /** Optional content type when `data` is not already a Blob. */
23
+ contentType?: string;
24
+ }
25
+ export declare class LlmleafClient {
26
+ private readonly baseUrl;
27
+ private readonly apiKey;
28
+ private readonly timeoutMs;
29
+ private readonly adminToken?;
30
+ private readonly fetchImpl;
31
+ constructor(options: LlmleafClientOptions);
32
+ private url;
33
+ private headers;
34
+ /** Issue a request with the configured timeout; returns the raw Response. */
35
+ private send;
36
+ /** Send a JSON request and return the parsed JSON body, or throw ApiError. */
37
+ private sendJson;
38
+ /** POST /v1/chat/completions (non-streaming). */
39
+ chat(req: ChatRequest): Promise<ChatResponse>;
40
+ /**
41
+ * POST /v1/chat/completions with `stream:true`. Returns an async iterable of
42
+ * {@link ChatCompletionChunk}; stops at the `data: [DONE]` sentinel.
43
+ */
44
+ chatStream(req: ChatRequest): AsyncGenerator<ChatCompletionChunk, void, unknown>;
45
+ /** POST /v1/embeddings. Base64 embeddings are decoded to float arrays. */
46
+ embeddings(req: EmbeddingRequest): Promise<EmbeddingResponse>;
47
+ /** GET /v1/models. Pass `admin:true` to include per-model `endpoints`. */
48
+ listModels(opts?: ListModelsOptions): Promise<ListModelsResponse>;
49
+ /** POST /v1/audio/speech. Returns the raw audio bytes + Content-Type. */
50
+ speech(req: SpeechRequest): Promise<SpeechResult>;
51
+ /** GET /v1/audio/voices?model=<id>. */
52
+ voices(model: string): Promise<VoicesResponse>;
53
+ /**
54
+ * POST /v1/audio/transcriptions (multipart/form-data).
55
+ *
56
+ * For `responseFormat` json/verbose_json the structured
57
+ * {@link TranscriptionResponse} is returned; for text/srt/vtt the response is a
58
+ * plain-text body and the returned object carries it in `.text` (other fields empty).
59
+ */
60
+ transcribe(file: TranscriptionFile, req: TranscriptionRequest): Promise<TranscriptionResponse>;
61
+ /** POST /v1/batches. */
62
+ createBatch(req: BatchCreateRequest): Promise<BatchHandle>;
63
+ /** GET /v1/batches/{id}. */
64
+ getBatch(id: string): Promise<BatchHandle>;
65
+ /** POST /v1/batches/{id}/cancel. */
66
+ cancelBatch(id: string): Promise<BatchHandle>;
67
+ /**
68
+ * GET /v1/batches/{id}/results — an async iterable of {@link BatchResultLine}
69
+ * (one per NDJSON line).
70
+ */
71
+ batchResults(id: string): AsyncGenerator<BatchResultLine, void, unknown>;
72
+ }
73
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,cAAc,EACd,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB,mEAAmE;AACnE,MAAM,MAAM,SAAS,GAAG,CACtB,KAAK,EAAE,MAAM,GAAG,GAAG,EACnB,IAAI,CAAC,EAAE,WAAW,KACf,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEvB,MAAM,WAAW,oBAAoB;IACnC,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,+DAA+D;AAC/D,MAAM,WAAW,iBAAiB;IAChC,iCAAiC;IACjC,IAAI,EAAE,IAAI,GAAG,UAAU,GAAG,WAAW,CAAC;IACtC,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAWD,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;gBAE1B,OAAO,EAAE,oBAAoB;IAqBzC,OAAO,CAAC,GAAG;IAaX,OAAO,CAAC,OAAO;IAMf,6EAA6E;YAC/D,IAAI;IAalB,8EAA8E;YAChE,QAAQ;IAkBtB,iDAAiD;IAC3C,IAAI,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAMnD;;;OAGG;IACI,UAAU,CACf,GAAG,EAAE,WAAW,GACf,cAAc,CAAC,mBAAmB,EAAE,IAAI,EAAE,OAAO,CAAC;IAuBrD,0EAA0E;IACpE,UAAU,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAUnE,0EAA0E;IACpE,UAAU,CAAC,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAuB3E,yEAAyE;IACnE,MAAM,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAgBvD,uCAAuC;IACjC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAOpD;;;;;;OAMG;IACG,UAAU,CACd,IAAI,EAAE,iBAAiB,EACvB,GAAG,EAAE,oBAAoB,GACxB,OAAO,CAAC,qBAAqB,CAAC;IAoCjC,wBAAwB;IAClB,WAAW,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC;IAMhE,4BAA4B;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAShD,oCAAoC;IAC9B,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IASnD;;;OAGG;IACI,YAAY,CACjB,EAAE,EAAE,MAAM,GACT,cAAc,CAAC,eAAe,EAAE,IAAI,EAAE,OAAO,CAAC;CAWlD"}
package/dist/client.js ADDED
@@ -0,0 +1,259 @@
1
+ // LlmleafClient — the fetch-based HTTP transport. One method per SPEC.md endpoint.
2
+ //
3
+ // Runtime-agnostic: relies only on global fetch / Request / Response / FormData /
4
+ // Blob / AbortController, all present in Node 20+, Deno, Bun and browsers. A custom
5
+ // fetch can be injected (testing, proxies, custom agents).
6
+ import { ApiError, apiErrorFromResponse } from "./error.js";
7
+ import { parseSseData, parseNdjson } from "./stream.js";
8
+ import { encodeChatRequest, decodeChatResponse, decodeChatCompletionChunk, encodeEmbeddingRequest, decodeEmbeddingResponse, encodeSpeechRequest, decodeVoicesResponse, decodeTranscriptionResponse, decodeListModelsResponse, encodeBatchCreateRequest, decodeBatchHandle, decodeBatchResultLine, } from "./wire.js";
9
+ const SPEECH_CONTENT_TYPES = {
10
+ mp3: "audio/mpeg",
11
+ wav: "audio/wav",
12
+ opus: "audio/ogg",
13
+ aac: "audio/aac",
14
+ flac: "audio/flac",
15
+ pcm: "audio/pcm",
16
+ };
17
+ export class LlmleafClient {
18
+ baseUrl;
19
+ apiKey;
20
+ timeoutMs;
21
+ adminToken;
22
+ fetchImpl;
23
+ constructor(options) {
24
+ if (!options.baseUrl)
25
+ throw new TypeError("LlmleafClient: baseUrl is required");
26
+ if (!options.apiKey)
27
+ throw new TypeError("LlmleafClient: apiKey is required");
28
+ // Strip a single trailing slash so path joins are predictable.
29
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
30
+ this.apiKey = options.apiKey;
31
+ this.timeoutMs = options.timeoutMs ?? 0;
32
+ this.adminToken = options.adminToken;
33
+ const f = options.fetch ?? globalThis.fetch;
34
+ if (!f) {
35
+ throw new TypeError("LlmleafClient: no global fetch available; pass `fetch` in the options (Node <18, or a non-fetch runtime).");
36
+ }
37
+ this.fetchImpl = f;
38
+ }
39
+ // -------------------------------------------------------------------------
40
+ // Internal request plumbing
41
+ // -------------------------------------------------------------------------
42
+ url(path, query) {
43
+ let u = this.baseUrl + path;
44
+ if (query) {
45
+ const params = new URLSearchParams();
46
+ for (const [k, v] of Object.entries(query)) {
47
+ if (v !== undefined)
48
+ params.set(k, v);
49
+ }
50
+ const qs = params.toString();
51
+ if (qs)
52
+ u += "?" + qs;
53
+ }
54
+ return u;
55
+ }
56
+ headers(extra) {
57
+ const h = new Headers({ authorization: `Bearer ${this.apiKey}` });
58
+ if (extra)
59
+ for (const [k, v] of Object.entries(extra))
60
+ h.set(k, v);
61
+ return h;
62
+ }
63
+ /** Issue a request with the configured timeout; returns the raw Response. */
64
+ async send(url, init) {
65
+ if (this.timeoutMs <= 0) {
66
+ return this.fetchImpl(url, init);
67
+ }
68
+ const controller = new AbortController();
69
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
70
+ try {
71
+ return await this.fetchImpl(url, { ...init, signal: controller.signal });
72
+ }
73
+ finally {
74
+ clearTimeout(timer);
75
+ }
76
+ }
77
+ /** Send a JSON request and return the parsed JSON body, or throw ApiError. */
78
+ async sendJson(url, body, headers) {
79
+ const res = await this.send(url, {
80
+ method: "POST",
81
+ headers: this.headers({ "content-type": "application/json", ...headers }),
82
+ body: JSON.stringify(body),
83
+ });
84
+ if (!res.ok)
85
+ throw await apiErrorFromResponse(res);
86
+ return res.json();
87
+ }
88
+ // -------------------------------------------------------------------------
89
+ // Chat completions
90
+ // -------------------------------------------------------------------------
91
+ /** POST /v1/chat/completions (non-streaming). */
92
+ async chat(req) {
93
+ const body = encodeChatRequest(req, false);
94
+ const json = await this.sendJson(this.url("/v1/chat/completions"), body);
95
+ return decodeChatResponse(json);
96
+ }
97
+ /**
98
+ * POST /v1/chat/completions with `stream:true`. Returns an async iterable of
99
+ * {@link ChatCompletionChunk}; stops at the `data: [DONE]` sentinel.
100
+ */
101
+ async *chatStream(req) {
102
+ const body = encodeChatRequest(req, true);
103
+ const res = await this.send(this.url("/v1/chat/completions"), {
104
+ method: "POST",
105
+ headers: this.headers({
106
+ "content-type": "application/json",
107
+ accept: "text/event-stream",
108
+ }),
109
+ body: JSON.stringify(body),
110
+ });
111
+ if (!res.ok)
112
+ throw await apiErrorFromResponse(res);
113
+ if (!res.body) {
114
+ throw new ApiError(res.status, "streaming response had no body");
115
+ }
116
+ for await (const payload of parseSseData(res.body)) {
117
+ yield decodeChatCompletionChunk(JSON.parse(payload));
118
+ }
119
+ }
120
+ // -------------------------------------------------------------------------
121
+ // Embeddings
122
+ // -------------------------------------------------------------------------
123
+ /** POST /v1/embeddings. Base64 embeddings are decoded to float arrays. */
124
+ async embeddings(req) {
125
+ const body = encodeEmbeddingRequest(req);
126
+ const json = await this.sendJson(this.url("/v1/embeddings"), body);
127
+ return decodeEmbeddingResponse(json);
128
+ }
129
+ // -------------------------------------------------------------------------
130
+ // Model catalog
131
+ // -------------------------------------------------------------------------
132
+ /** GET /v1/models. Pass `admin:true` to include per-model `endpoints`. */
133
+ async listModels(opts = {}) {
134
+ const headers = {};
135
+ if (opts.admin) {
136
+ if (!this.adminToken) {
137
+ throw new TypeError("listModels({ admin:true }) requires an adminToken in the client options");
138
+ }
139
+ headers["x-admin-token"] = this.adminToken;
140
+ }
141
+ const url = this.url("/v1/models", { type: opts.type, search: opts.search });
142
+ const res = await this.send(url, {
143
+ method: "GET",
144
+ headers: this.headers(headers),
145
+ });
146
+ if (!res.ok)
147
+ throw await apiErrorFromResponse(res);
148
+ return decodeListModelsResponse(await res.json());
149
+ }
150
+ // -------------------------------------------------------------------------
151
+ // Audio
152
+ // -------------------------------------------------------------------------
153
+ /** POST /v1/audio/speech. Returns the raw audio bytes + Content-Type. */
154
+ async speech(req) {
155
+ const body = encodeSpeechRequest(req);
156
+ const res = await this.send(this.url("/v1/audio/speech"), {
157
+ method: "POST",
158
+ headers: this.headers({ "content-type": "application/json" }),
159
+ body: JSON.stringify(body),
160
+ });
161
+ if (!res.ok)
162
+ throw await apiErrorFromResponse(res);
163
+ const buf = await res.arrayBuffer();
164
+ const contentType = res.headers.get("content-type") ??
165
+ (req.responseFormat ? SPEECH_CONTENT_TYPES[req.responseFormat] : undefined) ??
166
+ "application/octet-stream";
167
+ return { bytes: new Uint8Array(buf), contentType };
168
+ }
169
+ /** GET /v1/audio/voices?model=<id>. */
170
+ async voices(model) {
171
+ const url = this.url("/v1/audio/voices", { model });
172
+ const res = await this.send(url, { method: "GET", headers: this.headers() });
173
+ if (!res.ok)
174
+ throw await apiErrorFromResponse(res);
175
+ return decodeVoicesResponse(await res.json());
176
+ }
177
+ /**
178
+ * POST /v1/audio/transcriptions (multipart/form-data).
179
+ *
180
+ * For `responseFormat` json/verbose_json the structured
181
+ * {@link TranscriptionResponse} is returned; for text/srt/vtt the response is a
182
+ * plain-text body and the returned object carries it in `.text` (other fields empty).
183
+ */
184
+ async transcribe(file, req) {
185
+ const form = new FormData();
186
+ const blob = file.data instanceof Blob
187
+ ? file.data
188
+ : new Blob([file.data instanceof Uint8Array ? bytesToArrayBuffer(file.data) : file.data], file.contentType ? { type: file.contentType } : undefined);
189
+ form.set("file", blob, file.filename);
190
+ form.set("model", req.model);
191
+ if (req.language !== undefined)
192
+ form.set("language", req.language);
193
+ if (req.prompt !== undefined)
194
+ form.set("prompt", req.prompt);
195
+ if (req.responseFormat !== undefined)
196
+ form.set("response_format", req.responseFormat);
197
+ if (req.temperature !== undefined)
198
+ form.set("temperature", String(req.temperature));
199
+ // Do NOT set content-type: the runtime sets it with the multipart boundary.
200
+ const res = await this.send(this.url("/v1/audio/transcriptions"), {
201
+ method: "POST",
202
+ headers: this.headers(),
203
+ body: form,
204
+ });
205
+ if (!res.ok)
206
+ throw await apiErrorFromResponse(res);
207
+ const ct = res.headers.get("content-type") ?? "";
208
+ if (ct.includes("application/json")) {
209
+ return decodeTranscriptionResponse(await res.json());
210
+ }
211
+ // text / srt / vtt: a plain-text body. Surface it directly in `.text`.
212
+ return { text: await res.text() };
213
+ }
214
+ // -------------------------------------------------------------------------
215
+ // Batches
216
+ // -------------------------------------------------------------------------
217
+ /** POST /v1/batches. */
218
+ async createBatch(req) {
219
+ const body = encodeBatchCreateRequest(req);
220
+ const json = await this.sendJson(this.url("/v1/batches"), body);
221
+ return decodeBatchHandle(json);
222
+ }
223
+ /** GET /v1/batches/{id}. */
224
+ async getBatch(id) {
225
+ const res = await this.send(this.url(`/v1/batches/${encodeURIComponent(id)}`), {
226
+ method: "GET",
227
+ headers: this.headers(),
228
+ });
229
+ if (!res.ok)
230
+ throw await apiErrorFromResponse(res);
231
+ return decodeBatchHandle(await res.json());
232
+ }
233
+ /** POST /v1/batches/{id}/cancel. */
234
+ async cancelBatch(id) {
235
+ const res = await this.send(this.url(`/v1/batches/${encodeURIComponent(id)}/cancel`), { method: "POST", headers: this.headers() });
236
+ if (!res.ok)
237
+ throw await apiErrorFromResponse(res);
238
+ return decodeBatchHandle(await res.json());
239
+ }
240
+ /**
241
+ * GET /v1/batches/{id}/results — an async iterable of {@link BatchResultLine}
242
+ * (one per NDJSON line).
243
+ */
244
+ async *batchResults(id) {
245
+ const res = await this.send(this.url(`/v1/batches/${encodeURIComponent(id)}/results`), { method: "GET", headers: this.headers({ accept: "application/x-ndjson" }) });
246
+ if (!res.ok)
247
+ throw await apiErrorFromResponse(res);
248
+ if (!res.body)
249
+ throw new ApiError(res.status, "batch results had no body");
250
+ for await (const line of parseNdjson(res.body)) {
251
+ yield decodeBatchResultLine(line);
252
+ }
253
+ }
254
+ }
255
+ /** Copy a Uint8Array's bytes into a fresh ArrayBuffer (handles non-zero byteOffset). */
256
+ function bytesToArrayBuffer(bytes) {
257
+ return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
258
+ }
259
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { Role, FinishReason, BatchStatus } from "./gen/llmleaf/v1/llmleaf_pb.js";
2
+ export { Role, FinishReason, BatchStatus };
3
+ /** A TS numeric enum object: name<->value reverse-mappable at runtime. */
4
+ type NumericEnum = Record<string, string | number>;
5
+ /**
6
+ * Encode an enum value to its wire token, or `undefined` for the unspecified zero
7
+ * value (which means "field absent on the wire").
8
+ */
9
+ export declare function enumToWire<E extends number>(enumObj: NumericEnum, value: E | undefined): string | undefined;
10
+ /**
11
+ * Decode a wire token back into the enum value. An absent/empty token, or a token
12
+ * that matches no member, maps to the unspecified zero value (0).
13
+ */
14
+ export declare function enumFromWire<E extends number>(enumObj: NumericEnum, token: string | null | undefined): E;
15
+ export declare const roleToWire: (v: Role | undefined) => string | undefined;
16
+ export declare const roleFromWire: (t: string | null | undefined) => Role;
17
+ export declare const finishReasonToWire: (v: FinishReason | undefined) => string | undefined;
18
+ export declare const finishReasonFromWire: (t: string | null | undefined) => FinishReason;
19
+ export declare const batchStatusToWire: (v: BatchStatus | undefined) => string | undefined;
20
+ export declare const batchStatusFromWire: (t: string | null | undefined) => BatchStatus;
21
+ //# sourceMappingURL=enums.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enums.d.ts","sourceRoot":"","sources":["../src/enums.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAEjF,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;AAE3C,0EAA0E;AAC1E,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;AAQnD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACzC,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,CAAC,GAAG,SAAS,GACnB,MAAM,GAAG,SAAS,CAMpB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,EAC3C,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,CAAC,CAQH;AAID,eAAO,MAAM,UAAU,GAAI,GAAG,IAAI,GAAG,SAAS,KAAG,MAAM,GAAG,SACL,CAAC;AACtD,eAAO,MAAM,YAAY,GAAI,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,IACL,CAAC;AAExD,eAAO,MAAM,kBAAkB,GAAI,GAAG,YAAY,GAAG,SAAS,KAAG,MAAM,GAAG,SACL,CAAC;AACtE,eAAO,MAAM,oBAAoB,GAAI,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,YACG,CAAC;AAExE,eAAO,MAAM,iBAAiB,GAAI,GAAG,WAAW,GAAG,SAAS,KAAG,MAAM,GAAG,SACL,CAAC;AACpE,eAAO,MAAM,mBAAmB,GAAI,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,WACE,CAAC"}
package/dist/enums.js ADDED
@@ -0,0 +1,56 @@
1
+ // Enum <-> wire-token mapping (SPEC.md "Enum ⇄ wire mapping").
2
+ //
3
+ // Every closed-set enum maps to its wire token by LOWERCASING the value name:
4
+ // TOOL_CALLS -> "tool_calls", ASSISTANT -> "assistant", IN_PROGRESS -> "in_progress".
5
+ // The `*_UNSPECIFIED` zero value <-> field absent on the wire (undefined here).
6
+ //
7
+ // The generated protobuf-es file emits these as TypeScript string-keyed numeric enums
8
+ // (e.g. `Role.ASSISTANT`). A TS numeric enum is bidirectional at runtime: indexing by
9
+ // the numeric value yields the member NAME, which is exactly the proto value name. We
10
+ // reuse that to derive the wire token mechanically — one helper pair for every enum,
11
+ // no per-enum hand mapping (SPEC.md).
12
+ import { Role, FinishReason, BatchStatus } from "./gen/llmleaf/v1/llmleaf_pb.js";
13
+ export { Role, FinishReason, BatchStatus };
14
+ function unspecifiedName(e) {
15
+ // The zero value is the `*_UNSPECIFIED` member; its name is what 0 maps to.
16
+ const name = e[0];
17
+ return typeof name === "string" ? name : undefined;
18
+ }
19
+ /**
20
+ * Encode an enum value to its wire token, or `undefined` for the unspecified zero
21
+ * value (which means "field absent on the wire").
22
+ */
23
+ export function enumToWire(enumObj, value) {
24
+ if (value === undefined)
25
+ return undefined;
26
+ const name = enumObj[value];
27
+ if (typeof name !== "string")
28
+ return undefined;
29
+ if (name === unspecifiedName(enumObj))
30
+ return undefined;
31
+ return name.toLowerCase();
32
+ }
33
+ /**
34
+ * Decode a wire token back into the enum value. An absent/empty token, or a token
35
+ * that matches no member, maps to the unspecified zero value (0).
36
+ */
37
+ export function enumFromWire(enumObj, token) {
38
+ if (token === null || token === undefined || token === "")
39
+ return 0;
40
+ const want = token.toLowerCase();
41
+ for (const [name, value] of Object.entries(enumObj)) {
42
+ if (typeof value !== "number")
43
+ continue;
44
+ if (name.toLowerCase() === want)
45
+ return value;
46
+ }
47
+ return 0;
48
+ }
49
+ // Convenience typed wrappers for the three enums on the surface.
50
+ export const roleToWire = (v) => enumToWire(Role, v);
51
+ export const roleFromWire = (t) => enumFromWire(Role, t);
52
+ export const finishReasonToWire = (v) => enumToWire(FinishReason, v);
53
+ export const finishReasonFromWire = (t) => enumFromWire(FinishReason, t);
54
+ export const batchStatusToWire = (v) => enumToWire(BatchStatus, v);
55
+ export const batchStatusFromWire = (t) => enumFromWire(BatchStatus, t);
56
+ //# sourceMappingURL=enums.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enums.js","sourceRoot":"","sources":["../src/enums.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,EAAE;AACF,8EAA8E;AAC9E,wFAAwF;AACxF,gFAAgF;AAChF,EAAE;AACF,sFAAsF;AACtF,sFAAsF;AACtF,sFAAsF;AACtF,qFAAqF;AACrF,sCAAsC;AAEtC,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAEjF,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;AAK3C,SAAS,eAAe,CAAC,CAAc;IACrC,4EAA4E;IAC5E,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAClB,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AACrD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,OAAoB,EACpB,KAAoB;IAEpB,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,IAAI,GAAG,OAAO,CAAC,KAAe,CAAC,CAAC;IACtC,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC/C,IAAI,IAAI,KAAK,eAAe,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IACxD,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,OAAoB,EACpB,KAAgC;IAEhC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,CAAM,CAAC;IACzE,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IACjC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACpD,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,SAAS;QACxC,IAAI,IAAI,CAAC,WAAW,EAAE,KAAK,IAAI;YAAE,OAAO,KAAU,CAAC;IACrD,CAAC;IACD,OAAO,CAAM,CAAC;AAChB,CAAC;AAED,iEAAiE;AAEjE,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAmB,EAAsB,EAAE,CACpE,UAAU,CAAO,IAA8B,EAAE,CAAC,CAAC,CAAC;AACtD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAA4B,EAAQ,EAAE,CACjE,YAAY,CAAO,IAA8B,EAAE,CAAC,CAAC,CAAC;AAExD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAA2B,EAAsB,EAAE,CACpF,UAAU,CAAe,YAAsC,EAAE,CAAC,CAAC,CAAC;AACtE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAA4B,EAAgB,EAAE,CACjF,YAAY,CAAe,YAAsC,EAAE,CAAC,CAAC,CAAC;AAExE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAA0B,EAAsB,EAAE,CAClF,UAAU,CAAc,WAAqC,EAAE,CAAC,CAAC,CAAC;AACpE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAA4B,EAAe,EAAE,CAC/E,YAAY,CAAc,WAAqC,EAAE,CAAC,CAAC,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Thrown for any non-2xx HTTP response from the gateway.
3
+ *
4
+ * Status codes (SPEC.md): 400 bad request · 401 missing/invalid key ·
5
+ * 403 blocked or model-not-allowed · 404 no route for model ·
6
+ * 429 key suspended (limiter) · 502 all upstreams failed.
7
+ */
8
+ export declare class ApiError extends Error {
9
+ readonly status: number;
10
+ /** present on some dialects; absent on the llmleaf core envelope. */
11
+ readonly type?: string;
12
+ readonly code?: string;
13
+ constructor(status: number, message: string, opts?: {
14
+ type?: string;
15
+ code?: string;
16
+ });
17
+ }
18
+ /**
19
+ * Build an {@link ApiError} from a non-2xx response. Reads the body once. Falls back
20
+ * to the HTTP status text when the body is missing or not the expected envelope.
21
+ */
22
+ export declare function apiErrorFromResponse(res: Response): Promise<ApiError>;
23
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;gBAGrB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE;CAU1C;AAQD;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CA4B3E"}