@compose-market/sdk 0.1.1 → 0.2.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.
- package/CHANGELOG.md +43 -0
- package/LICENSE +21 -0
- package/README.md +158 -35
- package/dist/errors.d.ts +145 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +152 -0
- package/dist/errors.js.map +1 -0
- package/dist/http.d.ts +98 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +350 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +104 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +137 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/inference.d.ts +161 -0
- package/dist/resources/inference.d.ts.map +1 -0
- package/dist/resources/inference.js +710 -0
- package/dist/resources/inference.js.map +1 -0
- package/dist/resources/keys.d.ts +50 -0
- package/dist/resources/keys.d.ts.map +1 -0
- package/dist/resources/keys.js +154 -0
- package/dist/resources/keys.js.map +1 -0
- package/dist/resources/models.d.ts +26 -0
- package/dist/resources/models.d.ts.map +1 -0
- package/dist/resources/models.js +52 -0
- package/dist/resources/models.js.map +1 -0
- package/dist/resources/webhooks.d.ts +27 -0
- package/dist/resources/webhooks.d.ts.map +1 -0
- package/dist/resources/webhooks.js +78 -0
- package/dist/resources/webhooks.js.map +1 -0
- package/dist/resources/x402.d.ts +37 -0
- package/dist/resources/x402.d.ts.map +1 -0
- package/dist/resources/x402.js +72 -0
- package/dist/resources/x402.js.map +1 -0
- package/dist/streaming/receipt.d.ts +25 -0
- package/dist/streaming/receipt.d.ts.map +1 -0
- package/dist/streaming/receipt.js +92 -0
- package/dist/streaming/receipt.js.map +1 -0
- package/dist/streaming/sse.d.ts +29 -0
- package/dist/streaming/sse.d.ts.map +1 -0
- package/dist/streaming/sse.js +125 -0
- package/dist/streaming/sse.js.map +1 -0
- package/dist/types/index.d.ts +497 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +10 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +30 -20
- package/index.d.ts +0 -244
- package/index.js +0 -397
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
import { BadRequestError, ComposeError } from "../errors.js";
|
|
2
|
+
import { parseSSEStream } from "../streaming/sse.js";
|
|
3
|
+
import { extractReceiptFromResponse, parseReceiptEvent } from "../streaming/receipt.js";
|
|
4
|
+
function buildCallHeaders(options, ctxWallet, ctxToken) {
|
|
5
|
+
// When the caller explicitly passes `composeKey: null`, force the raw x402
|
|
6
|
+
// path by suppressing the instance-level token. Any other value (string or
|
|
7
|
+
// undefined) falls back to the instance token.
|
|
8
|
+
const tokenResolved = options && "composeKey" in options
|
|
9
|
+
? options.composeKey
|
|
10
|
+
: ctxToken;
|
|
11
|
+
return {
|
|
12
|
+
walletAddress: options?.walletAddress ?? ctxWallet.address ?? undefined,
|
|
13
|
+
chainId: options?.chainId ?? ctxWallet.chainId ?? undefined,
|
|
14
|
+
composeKey: tokenResolved ?? undefined,
|
|
15
|
+
paymentSignature: options?.paymentSignature,
|
|
16
|
+
x402MaxAmountWei: options?.x402MaxAmountWei,
|
|
17
|
+
idempotencyKey: options?.idempotencyKey,
|
|
18
|
+
composeRunId: options?.composeRunId,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Async iterable returned by `inference.chat.completions.stream(...)` and
|
|
23
|
+
* `inference.responses.stream(...)`. Exposes `.final()` for consumers that
|
|
24
|
+
* want the reassembled final object + receipt without iterating manually.
|
|
25
|
+
*
|
|
26
|
+
* `for await (const chunk of stream) { ... }` consumes chunks; the generator's
|
|
27
|
+
* return value is captured internally so a subsequent `await stream.final()`
|
|
28
|
+
* resolves to the typed final object even when the caller drove iteration.
|
|
29
|
+
*/
|
|
30
|
+
export class ComposeStreamIterator {
|
|
31
|
+
iterator;
|
|
32
|
+
finalResult = null;
|
|
33
|
+
finalSettled = false;
|
|
34
|
+
finalPromise = null;
|
|
35
|
+
constructor(iterator) {
|
|
36
|
+
this.iterator = iterator;
|
|
37
|
+
}
|
|
38
|
+
[Symbol.asyncIterator]() {
|
|
39
|
+
const self = this;
|
|
40
|
+
return {
|
|
41
|
+
next: async () => {
|
|
42
|
+
const r = await self.iterator.next();
|
|
43
|
+
if (r.done) {
|
|
44
|
+
self.finalResult = r.value;
|
|
45
|
+
self.finalSettled = true;
|
|
46
|
+
return { done: true, value: undefined };
|
|
47
|
+
}
|
|
48
|
+
return { done: false, value: r.value };
|
|
49
|
+
},
|
|
50
|
+
return: async () => {
|
|
51
|
+
const r = await self.iterator.return(undefined);
|
|
52
|
+
if (r.done) {
|
|
53
|
+
self.finalResult = r.value;
|
|
54
|
+
self.finalSettled = true;
|
|
55
|
+
}
|
|
56
|
+
return { done: true, value: undefined };
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async final() {
|
|
61
|
+
if (this.finalSettled)
|
|
62
|
+
return this.finalResult;
|
|
63
|
+
if (this.finalPromise)
|
|
64
|
+
return this.finalPromise;
|
|
65
|
+
this.finalPromise = (async () => {
|
|
66
|
+
let last;
|
|
67
|
+
do {
|
|
68
|
+
last = await this.iterator.next();
|
|
69
|
+
} while (!last.done);
|
|
70
|
+
this.finalResult = last.value;
|
|
71
|
+
this.finalSettled = true;
|
|
72
|
+
return last.value;
|
|
73
|
+
})();
|
|
74
|
+
return this.finalPromise;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Chat completions
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
class ChatCompletionsNamespace {
|
|
81
|
+
client;
|
|
82
|
+
ctx;
|
|
83
|
+
constructor(client, ctx) {
|
|
84
|
+
this.client = client;
|
|
85
|
+
this.ctx = ctx;
|
|
86
|
+
}
|
|
87
|
+
async create(params, options) {
|
|
88
|
+
if (params.stream === true) {
|
|
89
|
+
throw new BadRequestError({
|
|
90
|
+
message: "Pass stream: true only to chat.completions.stream(). Use create() for non-streaming calls.",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
const { data, response } = await this.client.request({
|
|
94
|
+
method: "POST",
|
|
95
|
+
path: "/v1/chat/completions",
|
|
96
|
+
body: { ...params, stream: false },
|
|
97
|
+
headers: buildCallHeaders(options, this.ctx.getWalletMaybe(), this.ctx.getTokenMaybe()),
|
|
98
|
+
signal: options?.signal,
|
|
99
|
+
timeoutMs: options?.timeoutMs,
|
|
100
|
+
}).withResponse();
|
|
101
|
+
return {
|
|
102
|
+
data,
|
|
103
|
+
receipt: extractReceiptFromResponse(response, data),
|
|
104
|
+
requestId: response.headers.get("x-request-id") ?? response.headers.get("X-Request-Id"),
|
|
105
|
+
response,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
stream(params, options) {
|
|
109
|
+
const iterator = streamChatCompletions(this.client, this.ctx, params, options);
|
|
110
|
+
return new ComposeStreamIterator(iterator);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function* streamChatCompletions(client, ctx, params, options) {
|
|
114
|
+
const response = await client.request({
|
|
115
|
+
method: "POST",
|
|
116
|
+
path: "/v1/chat/completions",
|
|
117
|
+
body: { ...params, stream: true },
|
|
118
|
+
headers: buildCallHeaders(options, ctx.getWalletMaybe(), ctx.getTokenMaybe()),
|
|
119
|
+
signal: options?.signal,
|
|
120
|
+
timeoutMs: options?.timeoutMs,
|
|
121
|
+
expectStream: true,
|
|
122
|
+
}).asResponse();
|
|
123
|
+
if (!response.body) {
|
|
124
|
+
throw new ComposeError({
|
|
125
|
+
code: "upstream_error",
|
|
126
|
+
message: "Streaming response had no body",
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
let receipt = null;
|
|
130
|
+
const aggregator = buildChatCompletionAggregator(params.model);
|
|
131
|
+
let streamError = null;
|
|
132
|
+
try {
|
|
133
|
+
for await (const frame of parseSSEStream(response.body, { signal: options?.signal })) {
|
|
134
|
+
if (frame.event === "compose.receipt") {
|
|
135
|
+
try {
|
|
136
|
+
receipt = parseReceiptEvent(frame.data);
|
|
137
|
+
}
|
|
138
|
+
catch { /* ignore */ }
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (frame.event === "compose.error") {
|
|
142
|
+
try {
|
|
143
|
+
const parsed = JSON.parse(frame.data);
|
|
144
|
+
streamError = new ComposeError({
|
|
145
|
+
code: parsed.code ?? "upstream_error",
|
|
146
|
+
message: parsed.message ?? "Stream error",
|
|
147
|
+
details: parsed.details,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
catch { /* ignore */ }
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (frame.data === "[DONE]") {
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
if (frame.event !== "message") {
|
|
157
|
+
// Unknown Compose-specific frames: surface nothing to the chunk stream.
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
let parsed;
|
|
161
|
+
try {
|
|
162
|
+
parsed = JSON.parse(frame.data);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
aggregator.absorb(parsed);
|
|
168
|
+
yield parsed;
|
|
169
|
+
}
|
|
170
|
+
if (streamError)
|
|
171
|
+
throw streamError;
|
|
172
|
+
return {
|
|
173
|
+
chatCompletion: aggregator.finalize(),
|
|
174
|
+
receipt,
|
|
175
|
+
requestId: response.headers.get("x-request-id") ?? response.headers.get("X-Request-Id"),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
try {
|
|
180
|
+
response.body?.cancel();
|
|
181
|
+
}
|
|
182
|
+
catch { /* best-effort */ }
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function buildChatCompletionAggregator(modelHint) {
|
|
186
|
+
let id = "";
|
|
187
|
+
let created = 0;
|
|
188
|
+
let model = modelHint;
|
|
189
|
+
let role = undefined;
|
|
190
|
+
let content = "";
|
|
191
|
+
let reasoningContent = "";
|
|
192
|
+
let finishReason = "stop";
|
|
193
|
+
const toolCalls = new Map();
|
|
194
|
+
let usage;
|
|
195
|
+
return {
|
|
196
|
+
absorb(chunk) {
|
|
197
|
+
id = id || chunk.id;
|
|
198
|
+
created = created || chunk.created;
|
|
199
|
+
model = model || chunk.model;
|
|
200
|
+
if (chunk.usage)
|
|
201
|
+
usage = chunk.usage;
|
|
202
|
+
const choice = chunk.choices?.[0];
|
|
203
|
+
if (!choice)
|
|
204
|
+
return;
|
|
205
|
+
if (choice.finish_reason)
|
|
206
|
+
finishReason = choice.finish_reason;
|
|
207
|
+
const delta = choice.delta;
|
|
208
|
+
if (delta?.role)
|
|
209
|
+
role = delta.role;
|
|
210
|
+
if (typeof delta?.content === "string")
|
|
211
|
+
content += delta.content;
|
|
212
|
+
if (typeof delta?.reasoning_content === "string")
|
|
213
|
+
reasoningContent += delta.reasoning_content;
|
|
214
|
+
if (Array.isArray(delta?.tool_calls)) {
|
|
215
|
+
for (const toolCall of delta.tool_calls) {
|
|
216
|
+
const idx = toolCall.index ?? 0;
|
|
217
|
+
const existing = toolCalls.get(idx) ?? {
|
|
218
|
+
id: toolCall.id ?? `call_${idx}`,
|
|
219
|
+
type: "function",
|
|
220
|
+
function: { name: "", arguments: "" },
|
|
221
|
+
};
|
|
222
|
+
if (toolCall.id)
|
|
223
|
+
existing.id = toolCall.id;
|
|
224
|
+
if (toolCall.function?.name)
|
|
225
|
+
existing.function.name += toolCall.function.name;
|
|
226
|
+
if (toolCall.function?.arguments)
|
|
227
|
+
existing.function.arguments += toolCall.function.arguments;
|
|
228
|
+
toolCalls.set(idx, existing);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
finalize() {
|
|
233
|
+
return {
|
|
234
|
+
id,
|
|
235
|
+
object: "chat.completion",
|
|
236
|
+
created,
|
|
237
|
+
model,
|
|
238
|
+
choices: [
|
|
239
|
+
{
|
|
240
|
+
index: 0,
|
|
241
|
+
message: {
|
|
242
|
+
role: role ?? "assistant",
|
|
243
|
+
content,
|
|
244
|
+
...(toolCalls.size > 0 ? { tool_calls: Array.from(toolCalls.values()) } : {}),
|
|
245
|
+
...(reasoningContent.length > 0 ? { reasoning_content: reasoningContent } : {}),
|
|
246
|
+
},
|
|
247
|
+
finish_reason: finishReason,
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
usage: usage ?? { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
|
|
251
|
+
};
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
// ---------------------------------------------------------------------------
|
|
256
|
+
// Responses API
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
class ResponsesNamespace {
|
|
259
|
+
client;
|
|
260
|
+
ctx;
|
|
261
|
+
constructor(client, ctx) {
|
|
262
|
+
this.client = client;
|
|
263
|
+
this.ctx = ctx;
|
|
264
|
+
}
|
|
265
|
+
async create(params, options) {
|
|
266
|
+
if (params.stream === true) {
|
|
267
|
+
throw new BadRequestError({
|
|
268
|
+
message: "Pass stream: true only to responses.stream(). Use create() for non-streaming calls.",
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
const { data, response } = await this.client.request({
|
|
272
|
+
method: "POST",
|
|
273
|
+
path: "/v1/responses",
|
|
274
|
+
body: { ...params, stream: false },
|
|
275
|
+
headers: buildCallHeaders(options, this.ctx.getWalletMaybe(), this.ctx.getTokenMaybe()),
|
|
276
|
+
signal: options?.signal,
|
|
277
|
+
timeoutMs: options?.timeoutMs,
|
|
278
|
+
}).withResponse();
|
|
279
|
+
return {
|
|
280
|
+
data,
|
|
281
|
+
receipt: extractReceiptFromResponse(response, data),
|
|
282
|
+
requestId: response.headers.get("x-request-id") ?? response.headers.get("X-Request-Id"),
|
|
283
|
+
response,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
stream(params, options) {
|
|
287
|
+
const iterator = streamResponses(this.client, this.ctx, params, options);
|
|
288
|
+
return new ComposeStreamIterator(iterator);
|
|
289
|
+
}
|
|
290
|
+
async get(responseId, options) {
|
|
291
|
+
return this.client.request({
|
|
292
|
+
method: "GET",
|
|
293
|
+
path: `/v1/responses/${encodeURIComponent(responseId)}`,
|
|
294
|
+
headers: buildCallHeaders(options, this.ctx.getWalletMaybe(), this.ctx.getTokenMaybe()),
|
|
295
|
+
signal: options?.signal,
|
|
296
|
+
timeoutMs: options?.timeoutMs,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
async inputItems(responseId, options) {
|
|
300
|
+
return this.client.request({
|
|
301
|
+
method: "GET",
|
|
302
|
+
path: `/v1/responses/${encodeURIComponent(responseId)}/input_items`,
|
|
303
|
+
headers: buildCallHeaders(options, this.ctx.getWalletMaybe(), this.ctx.getTokenMaybe()),
|
|
304
|
+
signal: options?.signal,
|
|
305
|
+
timeoutMs: options?.timeoutMs,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
async cancel(responseId, options) {
|
|
309
|
+
return this.client.request({
|
|
310
|
+
method: "POST",
|
|
311
|
+
path: `/v1/responses/${encodeURIComponent(responseId)}/cancel`,
|
|
312
|
+
headers: buildCallHeaders(options, this.ctx.getWalletMaybe(), this.ctx.getTokenMaybe()),
|
|
313
|
+
signal: options?.signal,
|
|
314
|
+
timeoutMs: options?.timeoutMs,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
async function* streamResponses(client, ctx, params, options) {
|
|
319
|
+
const response = await client.request({
|
|
320
|
+
method: "POST",
|
|
321
|
+
path: "/v1/responses",
|
|
322
|
+
body: { ...params, stream: true },
|
|
323
|
+
headers: buildCallHeaders(options, ctx.getWalletMaybe(), ctx.getTokenMaybe()),
|
|
324
|
+
signal: options?.signal,
|
|
325
|
+
timeoutMs: options?.timeoutMs,
|
|
326
|
+
expectStream: true,
|
|
327
|
+
}).asResponse();
|
|
328
|
+
if (!response.body) {
|
|
329
|
+
throw new ComposeError({ code: "upstream_error", message: "Streaming response had no body" });
|
|
330
|
+
}
|
|
331
|
+
let receipt = null;
|
|
332
|
+
let lastCompleted = null;
|
|
333
|
+
let streamError = null;
|
|
334
|
+
try {
|
|
335
|
+
for await (const frame of parseSSEStream(response.body, { signal: options?.signal })) {
|
|
336
|
+
if (frame.event === "compose.receipt") {
|
|
337
|
+
try {
|
|
338
|
+
receipt = parseReceiptEvent(frame.data);
|
|
339
|
+
}
|
|
340
|
+
catch { /* ignore */ }
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (frame.event === "compose.error") {
|
|
344
|
+
try {
|
|
345
|
+
const parsed = JSON.parse(frame.data);
|
|
346
|
+
streamError = new ComposeError({
|
|
347
|
+
code: parsed.code ?? "upstream_error",
|
|
348
|
+
message: parsed.message ?? "Stream error",
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
catch { /* ignore */ }
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
if (frame.data === "[DONE]") {
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
if (frame.event !== "message")
|
|
358
|
+
continue;
|
|
359
|
+
let parsed;
|
|
360
|
+
try {
|
|
361
|
+
parsed = JSON.parse(frame.data);
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
if (parsed.type === "response.completed") {
|
|
367
|
+
lastCompleted = parsed;
|
|
368
|
+
}
|
|
369
|
+
yield parsed;
|
|
370
|
+
}
|
|
371
|
+
if (streamError)
|
|
372
|
+
throw streamError;
|
|
373
|
+
const finalResponse = lastCompleted
|
|
374
|
+
? {
|
|
375
|
+
id: lastCompleted.response_id,
|
|
376
|
+
object: "response",
|
|
377
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
378
|
+
status: "completed",
|
|
379
|
+
model: lastCompleted.model,
|
|
380
|
+
output: [],
|
|
381
|
+
...(lastCompleted.usage
|
|
382
|
+
? {
|
|
383
|
+
usage: {
|
|
384
|
+
input_tokens: lastCompleted.usage.input_tokens,
|
|
385
|
+
output_tokens: lastCompleted.usage.output_tokens,
|
|
386
|
+
total_tokens: lastCompleted.usage.total_tokens,
|
|
387
|
+
},
|
|
388
|
+
}
|
|
389
|
+
: {}),
|
|
390
|
+
}
|
|
391
|
+
: null;
|
|
392
|
+
return {
|
|
393
|
+
response: finalResponse,
|
|
394
|
+
receipt,
|
|
395
|
+
requestId: response.headers.get("x-request-id") ?? response.headers.get("X-Request-Id"),
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
finally {
|
|
399
|
+
try {
|
|
400
|
+
response.body?.cancel();
|
|
401
|
+
}
|
|
402
|
+
catch { /* best-effort */ }
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
// ---------------------------------------------------------------------------
|
|
406
|
+
// Embeddings
|
|
407
|
+
// ---------------------------------------------------------------------------
|
|
408
|
+
class EmbeddingsNamespace {
|
|
409
|
+
client;
|
|
410
|
+
ctx;
|
|
411
|
+
constructor(client, ctx) {
|
|
412
|
+
this.client = client;
|
|
413
|
+
this.ctx = ctx;
|
|
414
|
+
}
|
|
415
|
+
async create(params, options) {
|
|
416
|
+
const { data, response } = await this.client.request({
|
|
417
|
+
method: "POST",
|
|
418
|
+
path: "/v1/embeddings",
|
|
419
|
+
body: params,
|
|
420
|
+
headers: buildCallHeaders(options, this.ctx.getWalletMaybe(), this.ctx.getTokenMaybe()),
|
|
421
|
+
signal: options?.signal,
|
|
422
|
+
timeoutMs: options?.timeoutMs,
|
|
423
|
+
}).withResponse();
|
|
424
|
+
return {
|
|
425
|
+
data,
|
|
426
|
+
receipt: extractReceiptFromResponse(response, data),
|
|
427
|
+
requestId: response.headers.get("x-request-id") ?? response.headers.get("X-Request-Id"),
|
|
428
|
+
response,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
// ---------------------------------------------------------------------------
|
|
433
|
+
// Images
|
|
434
|
+
// ---------------------------------------------------------------------------
|
|
435
|
+
class ImagesNamespace {
|
|
436
|
+
client;
|
|
437
|
+
ctx;
|
|
438
|
+
constructor(client, ctx) {
|
|
439
|
+
this.client = client;
|
|
440
|
+
this.ctx = ctx;
|
|
441
|
+
}
|
|
442
|
+
async generate(params, options) {
|
|
443
|
+
const { data, response } = await this.client.request({
|
|
444
|
+
method: "POST",
|
|
445
|
+
path: "/v1/images/generations",
|
|
446
|
+
body: params,
|
|
447
|
+
headers: buildCallHeaders(options, this.ctx.getWalletMaybe(), this.ctx.getTokenMaybe()),
|
|
448
|
+
signal: options?.signal,
|
|
449
|
+
timeoutMs: options?.timeoutMs,
|
|
450
|
+
}).withResponse();
|
|
451
|
+
return {
|
|
452
|
+
data,
|
|
453
|
+
receipt: extractReceiptFromResponse(response, data),
|
|
454
|
+
requestId: response.headers.get("x-request-id") ?? response.headers.get("X-Request-Id"),
|
|
455
|
+
response,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
async edit(params, options) {
|
|
459
|
+
const { data, response } = await this.client.request({
|
|
460
|
+
method: "POST",
|
|
461
|
+
path: "/v1/images/edits",
|
|
462
|
+
body: params,
|
|
463
|
+
headers: buildCallHeaders(options, this.ctx.getWalletMaybe(), this.ctx.getTokenMaybe()),
|
|
464
|
+
signal: options?.signal,
|
|
465
|
+
timeoutMs: options?.timeoutMs,
|
|
466
|
+
}).withResponse();
|
|
467
|
+
return {
|
|
468
|
+
data,
|
|
469
|
+
receipt: extractReceiptFromResponse(response, data),
|
|
470
|
+
requestId: response.headers.get("x-request-id") ?? response.headers.get("X-Request-Id"),
|
|
471
|
+
response,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
// ---------------------------------------------------------------------------
|
|
476
|
+
// Audio
|
|
477
|
+
// ---------------------------------------------------------------------------
|
|
478
|
+
class AudioNamespace {
|
|
479
|
+
client;
|
|
480
|
+
ctx;
|
|
481
|
+
constructor(client, ctx) {
|
|
482
|
+
this.client = client;
|
|
483
|
+
this.ctx = ctx;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Text-to-speech. Returns the raw audio `Response` (stream the body via
|
|
487
|
+
* `response.body` or `await response.arrayBuffer()`), plus any receipt
|
|
488
|
+
* extracted from response headers.
|
|
489
|
+
*/
|
|
490
|
+
async speech(params, options) {
|
|
491
|
+
const response = await this.client.request({
|
|
492
|
+
method: "POST",
|
|
493
|
+
path: "/v1/audio/speech",
|
|
494
|
+
body: params,
|
|
495
|
+
headers: buildCallHeaders(options, this.ctx.getWalletMaybe(), this.ctx.getTokenMaybe()),
|
|
496
|
+
signal: options?.signal,
|
|
497
|
+
timeoutMs: options?.timeoutMs,
|
|
498
|
+
expectStream: true,
|
|
499
|
+
}).asResponse();
|
|
500
|
+
return {
|
|
501
|
+
response,
|
|
502
|
+
receipt: extractReceiptFromResponse(response),
|
|
503
|
+
requestId: response.headers.get("x-request-id") ?? response.headers.get("X-Request-Id"),
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Speech-to-text. Supports both multipart/form-data (preferred, OpenAI-
|
|
508
|
+
* compatible) when `file` is a `Blob`/`File`/`Uint8Array`, and base64-in-
|
|
509
|
+
* JSON (Compose legacy) when `file` is a string.
|
|
510
|
+
*/
|
|
511
|
+
async transcriptions(params, options) {
|
|
512
|
+
const useMultipart = typeof File !== "undefined" && params.file instanceof File
|
|
513
|
+
|| typeof Blob !== "undefined" && params.file instanceof Blob
|
|
514
|
+
|| (params.file && typeof params.file === "object" && "byteLength" in params.file);
|
|
515
|
+
if (useMultipart) {
|
|
516
|
+
const form = new FormData();
|
|
517
|
+
form.append("model", params.model);
|
|
518
|
+
const { file, filename, model: _model, ...rest } = params;
|
|
519
|
+
void _model;
|
|
520
|
+
const name = filename ?? "audio";
|
|
521
|
+
let blob;
|
|
522
|
+
if (typeof File !== "undefined" && file instanceof File)
|
|
523
|
+
blob = file;
|
|
524
|
+
else if (typeof Blob !== "undefined" && file instanceof Blob)
|
|
525
|
+
blob = file;
|
|
526
|
+
else if (file.byteLength !== undefined) {
|
|
527
|
+
const bytes = file;
|
|
528
|
+
const copy = new Uint8Array(new ArrayBuffer(bytes.byteLength));
|
|
529
|
+
copy.set(bytes);
|
|
530
|
+
blob = new Blob([copy.buffer]);
|
|
531
|
+
}
|
|
532
|
+
else
|
|
533
|
+
throw new BadRequestError({ message: "audio transcriptions: unsupported file type" });
|
|
534
|
+
form.append("file", blob, name);
|
|
535
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
536
|
+
if (value === undefined)
|
|
537
|
+
continue;
|
|
538
|
+
form.append(key, typeof value === "string" ? value : JSON.stringify(value));
|
|
539
|
+
}
|
|
540
|
+
const { data, response } = await this.client.request({
|
|
541
|
+
method: "POST",
|
|
542
|
+
path: "/v1/audio/transcriptions",
|
|
543
|
+
rawBody: form,
|
|
544
|
+
bodyType: "form-data",
|
|
545
|
+
headers: buildCallHeaders(options, this.ctx.getWalletMaybe(), this.ctx.getTokenMaybe()),
|
|
546
|
+
signal: options?.signal,
|
|
547
|
+
timeoutMs: options?.timeoutMs,
|
|
548
|
+
}).withResponse();
|
|
549
|
+
return {
|
|
550
|
+
data,
|
|
551
|
+
receipt: extractReceiptFromResponse(response, data),
|
|
552
|
+
requestId: response.headers.get("x-request-id") ?? response.headers.get("X-Request-Id"),
|
|
553
|
+
response,
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
const { data, response } = await this.client.request({
|
|
557
|
+
method: "POST",
|
|
558
|
+
path: "/v1/audio/transcriptions",
|
|
559
|
+
body: params,
|
|
560
|
+
headers: buildCallHeaders(options, this.ctx.getWalletMaybe(), this.ctx.getTokenMaybe()),
|
|
561
|
+
signal: options?.signal,
|
|
562
|
+
timeoutMs: options?.timeoutMs,
|
|
563
|
+
}).withResponse();
|
|
564
|
+
return {
|
|
565
|
+
data,
|
|
566
|
+
receipt: extractReceiptFromResponse(response, data),
|
|
567
|
+
requestId: response.headers.get("x-request-id") ?? response.headers.get("X-Request-Id"),
|
|
568
|
+
response,
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
// ---------------------------------------------------------------------------
|
|
573
|
+
// Videos
|
|
574
|
+
// ---------------------------------------------------------------------------
|
|
575
|
+
class VideosNamespace {
|
|
576
|
+
client;
|
|
577
|
+
ctx;
|
|
578
|
+
constructor(client, ctx) {
|
|
579
|
+
this.client = client;
|
|
580
|
+
this.ctx = ctx;
|
|
581
|
+
}
|
|
582
|
+
async generate(params, options) {
|
|
583
|
+
const { data, response } = await this.client.request({
|
|
584
|
+
method: "POST",
|
|
585
|
+
path: "/v1/videos/generations",
|
|
586
|
+
body: params,
|
|
587
|
+
headers: buildCallHeaders(options, this.ctx.getWalletMaybe(), this.ctx.getTokenMaybe()),
|
|
588
|
+
signal: options?.signal,
|
|
589
|
+
timeoutMs: options?.timeoutMs,
|
|
590
|
+
}).withResponse();
|
|
591
|
+
return {
|
|
592
|
+
data,
|
|
593
|
+
receipt: extractReceiptFromResponse(response, data),
|
|
594
|
+
requestId: response.headers.get("x-request-id") ?? response.headers.get("X-Request-Id"),
|
|
595
|
+
response,
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
get(videoId, options) {
|
|
599
|
+
return Promise.resolve(this.client.request({
|
|
600
|
+
method: "GET",
|
|
601
|
+
path: `/v1/videos/${encodeURIComponent(videoId)}`,
|
|
602
|
+
headers: buildCallHeaders(options, this.ctx.getWalletMaybe(), this.ctx.getTokenMaybe()),
|
|
603
|
+
signal: options?.signal,
|
|
604
|
+
timeoutMs: options?.timeoutMs,
|
|
605
|
+
}));
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Server-driven polling via SSE. Subscribes to `/v1/videos/:id/stream`
|
|
609
|
+
* and yields each status update as it arrives, terminating on the
|
|
610
|
+
* `completed` or `failed` state.
|
|
611
|
+
*/
|
|
612
|
+
stream(videoId, opts = {}) {
|
|
613
|
+
const iterator = streamVideoStatus(this.client, this.ctx, videoId, opts);
|
|
614
|
+
return new ComposeStreamIterator(iterator);
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Convenience helper: resolves once the video job hits a terminal state,
|
|
618
|
+
* driven by the server-side SSE poller. Aborts after `timeoutMs` elapses.
|
|
619
|
+
*/
|
|
620
|
+
async waitUntilDone(videoId, opts = {}) {
|
|
621
|
+
const stream = this.stream(videoId, opts);
|
|
622
|
+
for await (const event of stream) {
|
|
623
|
+
opts.onStatus?.(event);
|
|
624
|
+
}
|
|
625
|
+
const { final } = await stream.final();
|
|
626
|
+
return final;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
async function* streamVideoStatus(client, ctx, videoId, opts) {
|
|
630
|
+
const query = {};
|
|
631
|
+
if (opts.pollIntervalMs !== undefined)
|
|
632
|
+
query.pollIntervalMs = String(opts.pollIntervalMs);
|
|
633
|
+
if (opts.timeoutMs !== undefined)
|
|
634
|
+
query.timeoutMs = String(opts.timeoutMs);
|
|
635
|
+
const response = await client.request({
|
|
636
|
+
method: "GET",
|
|
637
|
+
path: `/v1/videos/${encodeURIComponent(videoId)}/stream`,
|
|
638
|
+
query,
|
|
639
|
+
headers: buildCallHeaders(opts, ctx.getWalletMaybe(), ctx.getTokenMaybe()),
|
|
640
|
+
signal: opts.signal,
|
|
641
|
+
timeoutMs: opts.timeoutMs,
|
|
642
|
+
expectStream: true,
|
|
643
|
+
}).asResponse();
|
|
644
|
+
if (!response.body) {
|
|
645
|
+
throw new ComposeError({ code: "upstream_error", message: "Streaming response had no body" });
|
|
646
|
+
}
|
|
647
|
+
let lastStatus = null;
|
|
648
|
+
try {
|
|
649
|
+
for await (const frame of parseSSEStream(response.body, { signal: opts.signal })) {
|
|
650
|
+
if (frame.data === "[DONE]") {
|
|
651
|
+
yield { type: "done" };
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
if (frame.event === "compose.video.status") {
|
|
655
|
+
try {
|
|
656
|
+
const parsed = JSON.parse(frame.data);
|
|
657
|
+
lastStatus = {
|
|
658
|
+
id: parsed.jobId,
|
|
659
|
+
object: "video.generation",
|
|
660
|
+
status: parsed.status,
|
|
661
|
+
url: parsed.url,
|
|
662
|
+
error: parsed.error,
|
|
663
|
+
progress: parsed.progress,
|
|
664
|
+
};
|
|
665
|
+
yield { type: "compose.video.status", ...parsed };
|
|
666
|
+
}
|
|
667
|
+
catch { /* skip malformed frame */ }
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
if (frame.event === "compose.error") {
|
|
671
|
+
try {
|
|
672
|
+
const parsed = JSON.parse(frame.data);
|
|
673
|
+
yield { type: "compose.error", code: parsed.code ?? "upstream_error", message: parsed.message ?? "Stream error", details: parsed.details };
|
|
674
|
+
}
|
|
675
|
+
catch { /* skip */ }
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
return {
|
|
680
|
+
final: lastStatus,
|
|
681
|
+
requestId: response.headers.get("x-request-id") ?? response.headers.get("X-Request-Id"),
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
finally {
|
|
685
|
+
try {
|
|
686
|
+
response.body?.cancel();
|
|
687
|
+
}
|
|
688
|
+
catch { /* best-effort */ }
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
// ---------------------------------------------------------------------------
|
|
692
|
+
// Top-level inference resource
|
|
693
|
+
// ---------------------------------------------------------------------------
|
|
694
|
+
export class InferenceResource {
|
|
695
|
+
chat;
|
|
696
|
+
responses;
|
|
697
|
+
embeddings;
|
|
698
|
+
images;
|
|
699
|
+
audio;
|
|
700
|
+
videos;
|
|
701
|
+
constructor(client, ctx) {
|
|
702
|
+
this.chat = { completions: new ChatCompletionsNamespace(client, ctx) };
|
|
703
|
+
this.responses = new ResponsesNamespace(client, ctx);
|
|
704
|
+
this.embeddings = new EmbeddingsNamespace(client, ctx);
|
|
705
|
+
this.images = new ImagesNamespace(client, ctx);
|
|
706
|
+
this.audio = new AudioNamespace(client, ctx);
|
|
707
|
+
this.videos = new VideosNamespace(client, ctx);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
//# sourceMappingURL=inference.js.map
|