@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 +173 -0
- package/dist/client.d.ts +73 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +259 -0
- package/dist/client.js.map +1 -0
- package/dist/enums.d.ts +21 -0
- package/dist/enums.d.ts.map +1 -0
- package/dist/enums.js +56 -0
- package/dist/enums.js.map +1 -0
- package/dist/error.d.ts +23 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +59 -0
- package/dist/error.js.map +1 -0
- package/dist/gen/llmleaf/v1/llmleaf_pb.d.ts +1374 -0
- package/dist/gen/llmleaf/v1/llmleaf_pb.d.ts.map +1 -0
- package/dist/gen/llmleaf/v1/llmleaf_pb.js +361 -0
- package/dist/gen/llmleaf/v1/llmleaf_pb.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/stream.d.ts +14 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +85 -0
- package/dist/stream.js.map +1 -0
- package/dist/types.d.ts +301 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -0
- package/dist/wire.d.ts +19 -0
- package/dist/wire.d.ts.map +1 -0
- package/dist/wire.js +569 -0
- package/dist/wire.js.map +1 -0
- package/package.json +50 -0
- package/scripts/gen.sh +42 -0
- package/src/client.ts +353 -0
- package/src/enums.ts +73 -0
- package/src/error.ts +71 -0
- package/src/gen/llmleaf/v1/llmleaf_pb.ts +1675 -0
- package/src/index.ts +77 -0
- package/src/stream.ts +82 -0
- package/src/types.ts +384 -0
- package/src/wire.ts +605 -0
package/src/wire.ts
ADDED
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
// Serialization layer: map the public TypeScript model (src/types.ts) to/from the
|
|
2
|
+
// OpenAI/OpenRouter-shaped JSON the llmleaf core speaks (SPEC.md). This is the only
|
|
3
|
+
// place that knows the wire key names (snake_case), the enum<->token mapping, the
|
|
4
|
+
// "content is string-or-array" rule, the "stop is bare-string-or-array" rule, and the
|
|
5
|
+
// "free-form JSON fields are raw JSON strings spliced verbatim" rule.
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
roleToWire,
|
|
9
|
+
roleFromWire,
|
|
10
|
+
finishReasonToWire,
|
|
11
|
+
finishReasonFromWire,
|
|
12
|
+
batchStatusFromWire,
|
|
13
|
+
} from "./enums.js";
|
|
14
|
+
import type {
|
|
15
|
+
ChatRequest,
|
|
16
|
+
ChatMessage,
|
|
17
|
+
ContentPart,
|
|
18
|
+
MessageContent,
|
|
19
|
+
ToolCall,
|
|
20
|
+
ToolDef,
|
|
21
|
+
ToolChoice,
|
|
22
|
+
ResponseFormat,
|
|
23
|
+
ChatResponse,
|
|
24
|
+
Choice,
|
|
25
|
+
ChatCompletionChunk,
|
|
26
|
+
ChunkChoice,
|
|
27
|
+
Delta,
|
|
28
|
+
ToolCallDelta,
|
|
29
|
+
Usage,
|
|
30
|
+
EmbeddingRequest,
|
|
31
|
+
EmbeddingResponse,
|
|
32
|
+
Embedding,
|
|
33
|
+
SpeechRequest,
|
|
34
|
+
VoicesResponse,
|
|
35
|
+
Voice,
|
|
36
|
+
TranscriptionResponse,
|
|
37
|
+
ListModelsResponse,
|
|
38
|
+
ModelEntry,
|
|
39
|
+
Architecture,
|
|
40
|
+
Pricing,
|
|
41
|
+
TopProvider,
|
|
42
|
+
ModelEndpoint,
|
|
43
|
+
BatchCreateRequest,
|
|
44
|
+
BatchHandle,
|
|
45
|
+
BatchCounts,
|
|
46
|
+
BatchResultLine,
|
|
47
|
+
BatchResponse,
|
|
48
|
+
BatchError,
|
|
49
|
+
} from "./types.js";
|
|
50
|
+
|
|
51
|
+
// A plain JSON object on the wire.
|
|
52
|
+
type Json = Record<string, unknown>;
|
|
53
|
+
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Small read helpers (defensive: the server is authoritative, but be lenient).
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
function str(v: unknown, fallback = ""): string {
|
|
59
|
+
return typeof v === "string" ? v : fallback;
|
|
60
|
+
}
|
|
61
|
+
function num(v: unknown, fallback = 0): number {
|
|
62
|
+
return typeof v === "number" ? v : fallback;
|
|
63
|
+
}
|
|
64
|
+
function optStr(v: unknown): string | undefined {
|
|
65
|
+
return typeof v === "string" ? v : undefined;
|
|
66
|
+
}
|
|
67
|
+
function optNum(v: unknown): number | undefined {
|
|
68
|
+
return typeof v === "number" ? v : undefined;
|
|
69
|
+
}
|
|
70
|
+
function optBool(v: unknown): boolean | undefined {
|
|
71
|
+
return typeof v === "boolean" ? v : undefined;
|
|
72
|
+
}
|
|
73
|
+
function arr(v: unknown): unknown[] {
|
|
74
|
+
return Array.isArray(v) ? v : [];
|
|
75
|
+
}
|
|
76
|
+
function obj(v: unknown): Json | undefined {
|
|
77
|
+
return v && typeof v === "object" && !Array.isArray(v) ? (v as Json) : undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Parse a raw-JSON-string field; throws a clear error if the caller passed bad JSON. */
|
|
81
|
+
function parseRawJson(field: string, raw: string | undefined): unknown {
|
|
82
|
+
if (raw === undefined) return undefined;
|
|
83
|
+
try {
|
|
84
|
+
return JSON.parse(raw);
|
|
85
|
+
} catch (e) {
|
|
86
|
+
throw new TypeError(`${field} must be a valid JSON string: ${(e as Error).message}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Capture a wire sub-value back as a raw JSON string (free-form decode). */
|
|
91
|
+
function captureRawJson(v: unknown): string | undefined {
|
|
92
|
+
return v === undefined ? undefined : JSON.stringify(v);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Set key only when value is defined (keeps request bodies minimal/byte-clean). */
|
|
96
|
+
function put(o: Json, key: string, value: unknown): void {
|
|
97
|
+
if (value !== undefined) o[key] = value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ===========================================================================
|
|
101
|
+
// Chat — encode
|
|
102
|
+
// ===========================================================================
|
|
103
|
+
|
|
104
|
+
function encodeContent(content: MessageContent | undefined): unknown {
|
|
105
|
+
if (content === undefined) return undefined;
|
|
106
|
+
if (typeof content === "string") return content;
|
|
107
|
+
return content.map(encodeContentPart);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function encodeContentPart(p: ContentPart): Json {
|
|
111
|
+
if (p.type === "text") {
|
|
112
|
+
return { type: "text", text: p.text };
|
|
113
|
+
}
|
|
114
|
+
const imageUrl: Json = { url: p.imageUrl.url };
|
|
115
|
+
put(imageUrl, "detail", p.imageUrl.detail);
|
|
116
|
+
return { type: "image_url", image_url: imageUrl };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function encodeToolCall(tc: ToolCall): Json {
|
|
120
|
+
return {
|
|
121
|
+
id: tc.id,
|
|
122
|
+
type: tc.type,
|
|
123
|
+
function: { name: tc.function.name, arguments: tc.function.arguments },
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function encodeMessage(m: ChatMessage): Json {
|
|
128
|
+
const out: Json = { role: roleToWire(m.role) ?? "user" };
|
|
129
|
+
const content = encodeContent(m.content);
|
|
130
|
+
// content is required on the wire for most roles; emit it (possibly empty string)
|
|
131
|
+
// whenever it was provided, including the empty string.
|
|
132
|
+
if (content !== undefined) out["content"] = content;
|
|
133
|
+
put(out, "name", m.name);
|
|
134
|
+
if (m.toolCalls && m.toolCalls.length > 0) {
|
|
135
|
+
out["tool_calls"] = m.toolCalls.map(encodeToolCall);
|
|
136
|
+
}
|
|
137
|
+
put(out, "tool_call_id", m.toolCallId);
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function encodeToolDef(t: ToolDef): Json {
|
|
142
|
+
const fn: Json = { name: t.function.name };
|
|
143
|
+
put(fn, "description", t.function.description);
|
|
144
|
+
const params = parseRawJson("FunctionDef.parameters", t.function.parameters);
|
|
145
|
+
put(fn, "parameters", params);
|
|
146
|
+
return { type: t.type, function: fn };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function encodeToolChoice(tc: ToolChoice): unknown {
|
|
150
|
+
if (typeof tc === "string") return tc;
|
|
151
|
+
return { type: tc.type, function: { name: tc.function.name } };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function encodeResponseFormat(rf: ResponseFormat): Json {
|
|
155
|
+
const out: Json = { type: rf.type };
|
|
156
|
+
put(out, "json_schema", parseRawJson("ResponseFormat.json_schema", rf.jsonSchema));
|
|
157
|
+
return out;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** `stop`: bare string when single-element, else array (SPEC.md). */
|
|
161
|
+
function encodeStop(stop: string[] | undefined): unknown {
|
|
162
|
+
if (!stop || stop.length === 0) return undefined;
|
|
163
|
+
return stop.length === 1 ? stop[0] : stop;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Build the chat request body. `forceStream` overrides `stream` for the streaming call. */
|
|
167
|
+
export function encodeChatRequest(req: ChatRequest, forceStream?: boolean): Json {
|
|
168
|
+
const out: Json = {
|
|
169
|
+
model: req.model,
|
|
170
|
+
messages: req.messages.map(encodeMessage),
|
|
171
|
+
};
|
|
172
|
+
const stream = forceStream !== undefined ? forceStream : req.stream;
|
|
173
|
+
put(out, "stream", stream);
|
|
174
|
+
put(out, "temperature", req.temperature);
|
|
175
|
+
put(out, "top_p", req.topP);
|
|
176
|
+
// Prefer max_completion_tokens; still send max_tokens if only that was set (SPEC.md).
|
|
177
|
+
put(out, "max_completion_tokens", req.maxCompletionTokens);
|
|
178
|
+
if (req.maxTokens !== undefined) put(out, "max_tokens", req.maxTokens);
|
|
179
|
+
put(out, "stop", encodeStop(req.stop));
|
|
180
|
+
put(out, "n", req.n);
|
|
181
|
+
put(out, "seed", req.seed);
|
|
182
|
+
put(out, "frequency_penalty", req.frequencyPenalty);
|
|
183
|
+
put(out, "presence_penalty", req.presencePenalty);
|
|
184
|
+
if (req.tools && req.tools.length > 0) out["tools"] = req.tools.map(encodeToolDef);
|
|
185
|
+
put(out, "tool_choice", req.toolChoice && encodeToolChoice(req.toolChoice));
|
|
186
|
+
put(out, "response_format", req.responseFormat && encodeResponseFormat(req.responseFormat));
|
|
187
|
+
put(out, "reasoning_effort", req.reasoningEffort);
|
|
188
|
+
|
|
189
|
+
// `extra`: parsed and merged at the top level of the request object (SPEC.md).
|
|
190
|
+
const extra = parseRawJson("ChatRequest.extra", req.extra);
|
|
191
|
+
if (extra !== undefined) {
|
|
192
|
+
if (typeof extra !== "object" || extra === null || Array.isArray(extra)) {
|
|
193
|
+
throw new TypeError("ChatRequest.extra must be a JSON object");
|
|
194
|
+
}
|
|
195
|
+
for (const [k, v] of Object.entries(extra as Json)) out[k] = v;
|
|
196
|
+
}
|
|
197
|
+
return out;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ===========================================================================
|
|
201
|
+
// Chat — decode
|
|
202
|
+
// ===========================================================================
|
|
203
|
+
|
|
204
|
+
export function decodeUsage(v: unknown): Usage | undefined {
|
|
205
|
+
const o = obj(v);
|
|
206
|
+
if (!o) return undefined;
|
|
207
|
+
const usage: Usage = {
|
|
208
|
+
promptTokens: num(o["prompt_tokens"]),
|
|
209
|
+
completionTokens: num(o["completion_tokens"]),
|
|
210
|
+
totalTokens: num(o["total_tokens"]),
|
|
211
|
+
};
|
|
212
|
+
const cost = optNum(o["cost_usd"]);
|
|
213
|
+
if (cost !== undefined) usage.costUsd = cost;
|
|
214
|
+
return usage;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function decodeContent(v: unknown): MessageContent | undefined {
|
|
218
|
+
if (v === undefined || v === null) return undefined;
|
|
219
|
+
if (typeof v === "string") return v;
|
|
220
|
+
if (Array.isArray(v)) {
|
|
221
|
+
const parts: ContentPart[] = [];
|
|
222
|
+
for (const raw of v) {
|
|
223
|
+
const p = obj(raw);
|
|
224
|
+
if (!p) continue;
|
|
225
|
+
const type = str(p["type"]);
|
|
226
|
+
if (type === "image_url") {
|
|
227
|
+
const iu = obj(p["image_url"]) ?? {};
|
|
228
|
+
parts.push({
|
|
229
|
+
type: "image_url",
|
|
230
|
+
imageUrl: { url: str(iu["url"]), detail: optStr(iu["detail"]) },
|
|
231
|
+
});
|
|
232
|
+
} else {
|
|
233
|
+
parts.push({ type: "text", text: str(p["text"]) });
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return parts;
|
|
237
|
+
}
|
|
238
|
+
return undefined;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function decodeToolCall(v: unknown): ToolCall | undefined {
|
|
242
|
+
const o = obj(v);
|
|
243
|
+
if (!o) return undefined;
|
|
244
|
+
const fn = obj(o["function"]) ?? {};
|
|
245
|
+
return {
|
|
246
|
+
id: str(o["id"]),
|
|
247
|
+
type: str(o["type"], "function"),
|
|
248
|
+
function: { name: str(fn["name"]), arguments: str(fn["arguments"]) },
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function decodeMessage(v: unknown): ChatMessage {
|
|
253
|
+
const o = obj(v) ?? {};
|
|
254
|
+
const msg: ChatMessage = { role: roleFromWire(optStr(o["role"])) };
|
|
255
|
+
const content = decodeContent(o["content"]);
|
|
256
|
+
if (content !== undefined) msg.content = content;
|
|
257
|
+
const name = optStr(o["name"]);
|
|
258
|
+
if (name !== undefined) msg.name = name;
|
|
259
|
+
const tcs = arr(o["tool_calls"])
|
|
260
|
+
.map(decodeToolCall)
|
|
261
|
+
.filter((x): x is ToolCall => x !== undefined);
|
|
262
|
+
if (tcs.length > 0) msg.toolCalls = tcs;
|
|
263
|
+
const tcid = optStr(o["tool_call_id"]);
|
|
264
|
+
if (tcid !== undefined) msg.toolCallId = tcid;
|
|
265
|
+
return msg;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function decodeChoice(v: unknown): Choice {
|
|
269
|
+
const o = obj(v) ?? {};
|
|
270
|
+
const choice: Choice = {
|
|
271
|
+
index: num(o["index"]),
|
|
272
|
+
message: decodeMessage(o["message"]),
|
|
273
|
+
};
|
|
274
|
+
const fr = o["finish_reason"];
|
|
275
|
+
if (typeof fr === "string") choice.finishReason = finishReasonFromWire(fr);
|
|
276
|
+
return choice;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function decodeChatResponse(v: unknown): ChatResponse {
|
|
280
|
+
const o = obj(v) ?? {};
|
|
281
|
+
return {
|
|
282
|
+
id: str(o["id"]),
|
|
283
|
+
object: str(o["object"], "chat.completion"),
|
|
284
|
+
created: num(o["created"]),
|
|
285
|
+
model: str(o["model"]),
|
|
286
|
+
choices: arr(o["choices"]).map(decodeChoice),
|
|
287
|
+
usage: decodeUsage(o["usage"]),
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Streaming chunk decode
|
|
292
|
+
|
|
293
|
+
function decodeToolCallDelta(v: unknown): ToolCallDelta | undefined {
|
|
294
|
+
const o = obj(v);
|
|
295
|
+
if (!o) return undefined;
|
|
296
|
+
const d: ToolCallDelta = { index: num(o["index"]) };
|
|
297
|
+
const id = optStr(o["id"]);
|
|
298
|
+
if (id !== undefined) d.id = id;
|
|
299
|
+
const type = optStr(o["type"]);
|
|
300
|
+
if (type !== undefined) d.type = type;
|
|
301
|
+
const fn = obj(o["function"]);
|
|
302
|
+
if (fn) {
|
|
303
|
+
d.function = {};
|
|
304
|
+
const name = optStr(fn["name"]);
|
|
305
|
+
if (name !== undefined) d.function.name = name;
|
|
306
|
+
const args = optStr(fn["arguments"]);
|
|
307
|
+
if (args !== undefined) d.function.arguments = args;
|
|
308
|
+
}
|
|
309
|
+
return d;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function decodeDelta(v: unknown): Delta {
|
|
313
|
+
const o = obj(v) ?? {};
|
|
314
|
+
const delta: Delta = {};
|
|
315
|
+
const role = optStr(o["role"]);
|
|
316
|
+
if (role !== undefined) delta.role = roleFromWire(role);
|
|
317
|
+
const content = optStr(o["content"]);
|
|
318
|
+
if (content !== undefined) delta.content = content;
|
|
319
|
+
const tcs = arr(o["tool_calls"])
|
|
320
|
+
.map(decodeToolCallDelta)
|
|
321
|
+
.filter((x): x is ToolCallDelta => x !== undefined);
|
|
322
|
+
if (tcs.length > 0) delta.toolCalls = tcs;
|
|
323
|
+
return delta;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function decodeChunkChoice(v: unknown): ChunkChoice {
|
|
327
|
+
const o = obj(v) ?? {};
|
|
328
|
+
const cc: ChunkChoice = {
|
|
329
|
+
index: num(o["index"]),
|
|
330
|
+
delta: decodeDelta(o["delta"]),
|
|
331
|
+
};
|
|
332
|
+
const fr = o["finish_reason"];
|
|
333
|
+
if (typeof fr === "string") cc.finishReason = finishReasonFromWire(fr);
|
|
334
|
+
return cc;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export function decodeChatCompletionChunk(v: unknown): ChatCompletionChunk {
|
|
338
|
+
const o = obj(v) ?? {};
|
|
339
|
+
const chunk: ChatCompletionChunk = {
|
|
340
|
+
id: str(o["id"]),
|
|
341
|
+
object: str(o["object"], "chat.completion.chunk"),
|
|
342
|
+
created: num(o["created"]),
|
|
343
|
+
model: str(o["model"]),
|
|
344
|
+
choices: arr(o["choices"]).map(decodeChunkChoice),
|
|
345
|
+
};
|
|
346
|
+
const usage = decodeUsage(o["usage"]);
|
|
347
|
+
if (usage !== undefined) chunk.usage = usage;
|
|
348
|
+
return chunk;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ===========================================================================
|
|
352
|
+
// Embeddings
|
|
353
|
+
// ===========================================================================
|
|
354
|
+
|
|
355
|
+
export function encodeEmbeddingRequest(req: EmbeddingRequest): Json {
|
|
356
|
+
const out: Json = {
|
|
357
|
+
model: req.model,
|
|
358
|
+
// wire accepts string or array; emit a bare string for a single input.
|
|
359
|
+
input: req.input.length === 1 ? req.input[0] : req.input,
|
|
360
|
+
};
|
|
361
|
+
put(out, "dimensions", req.dimensions);
|
|
362
|
+
put(out, "encoding_format", req.encodingFormat);
|
|
363
|
+
const extra = parseRawJson("EmbeddingRequest.extra", req.extra);
|
|
364
|
+
if (extra !== undefined) {
|
|
365
|
+
if (typeof extra !== "object" || extra === null || Array.isArray(extra)) {
|
|
366
|
+
throw new TypeError("EmbeddingRequest.extra must be a JSON object");
|
|
367
|
+
}
|
|
368
|
+
for (const [k, v] of Object.entries(extra as Json)) out[k] = v;
|
|
369
|
+
}
|
|
370
|
+
return out;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/** Decode a base64 string of little-endian f32 bytes into a number[] (SPEC.md). */
|
|
374
|
+
function decodeBase64Floats(b64: string): number[] {
|
|
375
|
+
// atob is available in Node 16+, Deno, Bun and browsers.
|
|
376
|
+
const binary = atob(b64);
|
|
377
|
+
const len = binary.length;
|
|
378
|
+
const bytes = new Uint8Array(len);
|
|
379
|
+
for (let i = 0; i < len; i++) bytes[i] = binary.charCodeAt(i);
|
|
380
|
+
const floats = new Float32Array(
|
|
381
|
+
bytes.buffer,
|
|
382
|
+
bytes.byteOffset,
|
|
383
|
+
Math.floor(len / 4),
|
|
384
|
+
);
|
|
385
|
+
return Array.from(floats);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function decodeEmbedding(v: unknown): Embedding {
|
|
389
|
+
const o = obj(v) ?? {};
|
|
390
|
+
const raw = o["embedding"];
|
|
391
|
+
let embedding: number[];
|
|
392
|
+
if (typeof raw === "string") {
|
|
393
|
+
embedding = decodeBase64Floats(raw);
|
|
394
|
+
} else if (Array.isArray(raw)) {
|
|
395
|
+
embedding = raw.map((x) => num(x));
|
|
396
|
+
} else {
|
|
397
|
+
embedding = [];
|
|
398
|
+
}
|
|
399
|
+
return { object: str(o["object"], "embedding"), index: num(o["index"]), embedding };
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export function decodeEmbeddingResponse(v: unknown): EmbeddingResponse {
|
|
403
|
+
const o = obj(v) ?? {};
|
|
404
|
+
return {
|
|
405
|
+
object: str(o["object"], "list"),
|
|
406
|
+
data: arr(o["data"]).map(decodeEmbedding),
|
|
407
|
+
model: str(o["model"]),
|
|
408
|
+
usage: decodeUsage(o["usage"]),
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// ===========================================================================
|
|
413
|
+
// Audio
|
|
414
|
+
// ===========================================================================
|
|
415
|
+
|
|
416
|
+
export function encodeSpeechRequest(req: SpeechRequest): Json {
|
|
417
|
+
const out: Json = { model: req.model, input: req.input, voice: req.voice };
|
|
418
|
+
put(out, "response_format", req.responseFormat);
|
|
419
|
+
put(out, "speed", req.speed);
|
|
420
|
+
const extra = parseRawJson("SpeechRequest.extra", req.extra);
|
|
421
|
+
if (extra !== undefined) {
|
|
422
|
+
if (typeof extra !== "object" || extra === null || Array.isArray(extra)) {
|
|
423
|
+
throw new TypeError("SpeechRequest.extra must be a JSON object");
|
|
424
|
+
}
|
|
425
|
+
for (const [k, v] of Object.entries(extra as Json)) out[k] = v;
|
|
426
|
+
}
|
|
427
|
+
return out;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function decodeVoice(v: unknown): Voice {
|
|
431
|
+
const o = obj(v) ?? {};
|
|
432
|
+
const voice: Voice = {
|
|
433
|
+
id: str(o["id"]),
|
|
434
|
+
languages: arr(o["languages"]).map((x) => str(x)),
|
|
435
|
+
};
|
|
436
|
+
const name = optStr(o["name"]);
|
|
437
|
+
if (name !== undefined) voice.name = name;
|
|
438
|
+
return voice;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
export function decodeVoicesResponse(v: unknown): VoicesResponse {
|
|
442
|
+
const o = obj(v) ?? {};
|
|
443
|
+
return { model: str(o["model"]), voices: arr(o["voices"]).map(decodeVoice) };
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
export function decodeTranscriptionResponse(v: unknown): TranscriptionResponse {
|
|
447
|
+
const o = obj(v) ?? {};
|
|
448
|
+
const out: TranscriptionResponse = { text: str(o["text"]) };
|
|
449
|
+
const task = optStr(o["task"]);
|
|
450
|
+
if (task !== undefined) out.task = task;
|
|
451
|
+
const language = optStr(o["language"]);
|
|
452
|
+
if (language !== undefined) out.language = language;
|
|
453
|
+
const duration = optNum(o["duration"]);
|
|
454
|
+
if (duration !== undefined) out.duration = duration;
|
|
455
|
+
const usage = decodeUsage(o["usage"]);
|
|
456
|
+
if (usage !== undefined) out.usage = usage;
|
|
457
|
+
return out;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// ===========================================================================
|
|
461
|
+
// Model catalog
|
|
462
|
+
// ===========================================================================
|
|
463
|
+
|
|
464
|
+
function decodeArchitecture(v: unknown): Architecture | undefined {
|
|
465
|
+
const o = obj(v);
|
|
466
|
+
if (!o) return undefined;
|
|
467
|
+
const a: Architecture = {
|
|
468
|
+
inputModalities: arr(o["input_modalities"]).map((x) => str(x)),
|
|
469
|
+
outputModalities: arr(o["output_modalities"]).map((x) => str(x)),
|
|
470
|
+
tokenizer: str(o["tokenizer"]),
|
|
471
|
+
};
|
|
472
|
+
const modality = optStr(o["modality"]);
|
|
473
|
+
if (modality !== undefined) a.modality = modality;
|
|
474
|
+
const instruct = optStr(o["instruct_type"]);
|
|
475
|
+
if (instruct !== undefined) a.instructType = instruct;
|
|
476
|
+
return a;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function decodePricing(v: unknown): Pricing | undefined {
|
|
480
|
+
const o = obj(v);
|
|
481
|
+
if (!o) return undefined;
|
|
482
|
+
return { prompt: str(o["prompt"]), completion: str(o["completion"]) };
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function decodeTopProvider(v: unknown): TopProvider | undefined {
|
|
486
|
+
const o = obj(v);
|
|
487
|
+
if (!o) return undefined;
|
|
488
|
+
const tp: TopProvider = { isModerated: optBool(o["is_moderated"]) ?? false };
|
|
489
|
+
const cl = optNum(o["context_length"]);
|
|
490
|
+
if (cl !== undefined) tp.contextLength = cl;
|
|
491
|
+
const mct = optNum(o["max_completion_tokens"]);
|
|
492
|
+
if (mct !== undefined) tp.maxCompletionTokens = mct;
|
|
493
|
+
const mtt = optNum(o["max_thinking_tokens"]);
|
|
494
|
+
if (mtt !== undefined) tp.maxThinkingTokens = mtt;
|
|
495
|
+
return tp;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function decodeModelEndpoint(v: unknown): ModelEndpoint {
|
|
499
|
+
const o = obj(v) ?? {};
|
|
500
|
+
return {
|
|
501
|
+
provider: str(o["provider"]),
|
|
502
|
+
model: str(o["model"]),
|
|
503
|
+
down: optBool(o["down"]) ?? false,
|
|
504
|
+
source: str(o["source"]),
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function decodeModelEntry(v: unknown): ModelEntry {
|
|
509
|
+
const o = obj(v) ?? {};
|
|
510
|
+
const entry: ModelEntry = {
|
|
511
|
+
id: str(o["id"]),
|
|
512
|
+
canonicalSlug: str(o["canonical_slug"]),
|
|
513
|
+
name: str(o["name"]),
|
|
514
|
+
created: num(o["created"]),
|
|
515
|
+
description: str(o["description"]),
|
|
516
|
+
supportedParameters: arr(o["supported_parameters"]).map((x) => str(x)),
|
|
517
|
+
unsupportedParameters: arr(o["unsupported_parameters"]).map((x) => str(x)),
|
|
518
|
+
endpoints: arr(o["endpoints"]).map(decodeModelEndpoint),
|
|
519
|
+
};
|
|
520
|
+
const cl = optNum(o["context_length"]);
|
|
521
|
+
if (cl !== undefined) entry.contextLength = cl;
|
|
522
|
+
const arch = decodeArchitecture(o["architecture"]);
|
|
523
|
+
if (arch !== undefined) entry.architecture = arch;
|
|
524
|
+
const pricing = decodePricing(o["pricing"]);
|
|
525
|
+
if (pricing !== undefined) entry.pricing = pricing;
|
|
526
|
+
const tp = decodeTopProvider(o["top_provider"]);
|
|
527
|
+
if (tp !== undefined) entry.topProvider = tp;
|
|
528
|
+
const dp = captureRawJson(o["default_parameters"]);
|
|
529
|
+
if (dp !== undefined && o["default_parameters"] !== undefined) {
|
|
530
|
+
entry.defaultParameters = dp;
|
|
531
|
+
}
|
|
532
|
+
return entry;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
export function decodeListModelsResponse(v: unknown): ListModelsResponse {
|
|
536
|
+
const o = obj(v) ?? {};
|
|
537
|
+
return { data: arr(o["data"]).map(decodeModelEntry) };
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// ===========================================================================
|
|
541
|
+
// Batches
|
|
542
|
+
// ===========================================================================
|
|
543
|
+
|
|
544
|
+
export function encodeBatchCreateRequest(req: BatchCreateRequest): Json {
|
|
545
|
+
return {
|
|
546
|
+
requests: req.requests.map((item) => ({
|
|
547
|
+
custom_id: item.customId,
|
|
548
|
+
body: encodeChatRequest(item.body),
|
|
549
|
+
})),
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function decodeBatchCounts(v: unknown): BatchCounts | undefined {
|
|
554
|
+
const o = obj(v);
|
|
555
|
+
if (!o) return undefined;
|
|
556
|
+
return {
|
|
557
|
+
total: num(o["total"]),
|
|
558
|
+
processing: num(o["processing"]),
|
|
559
|
+
succeeded: num(o["succeeded"]),
|
|
560
|
+
errored: num(o["errored"]),
|
|
561
|
+
canceled: num(o["canceled"]),
|
|
562
|
+
expired: num(o["expired"]),
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
export function decodeBatchHandle(v: unknown): BatchHandle {
|
|
567
|
+
const o = obj(v) ?? {};
|
|
568
|
+
const handle: BatchHandle = {
|
|
569
|
+
id: str(o["id"]),
|
|
570
|
+
status: batchStatusFromWire(optStr(o["status"])),
|
|
571
|
+
};
|
|
572
|
+
const counts = decodeBatchCounts(o["counts"]);
|
|
573
|
+
if (counts !== undefined) handle.counts = counts;
|
|
574
|
+
const createdAt = optNum(o["created_at"]);
|
|
575
|
+
if (createdAt !== undefined) handle.createdAt = createdAt;
|
|
576
|
+
const expiresAt = optNum(o["expires_at"]);
|
|
577
|
+
if (expiresAt !== undefined) handle.expiresAt = expiresAt;
|
|
578
|
+
const endedAt = optNum(o["ended_at"]);
|
|
579
|
+
if (endedAt !== undefined) handle.endedAt = endedAt;
|
|
580
|
+
const endpoint = optStr(o["endpoint"]);
|
|
581
|
+
if (endpoint !== undefined) handle.endpoint = endpoint;
|
|
582
|
+
return handle;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function decodeBatchResponse(v: unknown): BatchResponse | undefined {
|
|
586
|
+
const o = obj(v);
|
|
587
|
+
if (!o) return undefined;
|
|
588
|
+
return { statusCode: num(o["status_code"]), body: decodeChatResponse(o["body"]) };
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function decodeBatchError(v: unknown): BatchError | undefined {
|
|
592
|
+
const o = obj(v);
|
|
593
|
+
if (!o) return undefined;
|
|
594
|
+
return { code: str(o["code"]), message: str(o["message"]) };
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
export function decodeBatchResultLine(v: unknown): BatchResultLine {
|
|
598
|
+
const o = obj(v) ?? {};
|
|
599
|
+
const line: BatchResultLine = { customId: str(o["custom_id"]) };
|
|
600
|
+
const response = decodeBatchResponse(o["response"]);
|
|
601
|
+
if (response !== undefined) line.response = response;
|
|
602
|
+
const error = decodeBatchError(o["error"]);
|
|
603
|
+
if (error !== undefined) line.error = error;
|
|
604
|
+
return line;
|
|
605
|
+
}
|