@curvet/sdk 0.1.0 → 0.3.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 +45 -0
- package/README.md +73 -1
- package/dist/index.cjs +301 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +233 -24
- package/dist/index.d.ts +233 -24
- package/dist/index.js +290 -19
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@curvet/sdk` are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.3.0] - 2026-06-27
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Pollable workflow runs** for long workflows (video/audio/3D nodes) — no more
|
|
12
|
+
one long-lived HTTP call:
|
|
13
|
+
- `workflows.submit(id, params)` — start a run, returns a `runId` immediately.
|
|
14
|
+
- `workflows.runs.retrieve(runId)` — live status: `currentNode`, `progress`,
|
|
15
|
+
per-node history, and the final `result`.
|
|
16
|
+
- `workflows.runAndPoll(id, params, { onProgress })` — submit + auto-poll to
|
|
17
|
+
completion (mirrors `video.generate`).
|
|
18
|
+
- `WorkflowRunFailedError` and `WorkflowRunTimeoutError` (both carry `runId`).
|
|
19
|
+
- `examples/pollable-workflow.ts`.
|
|
20
|
+
|
|
21
|
+
### Notes
|
|
22
|
+
- `workflows.run()` (synchronous) is unchanged.
|
|
23
|
+
- Requires the matching backend (media-node execution + pollable run endpoints).
|
|
24
|
+
|
|
25
|
+
## [0.2.1] - 2026-06-20
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- Additional resources: `audio.generate`/`submit`, `threeD.generate`/`submit`,
|
|
29
|
+
`analytics.get`, `workflows.run` (JSON or multipart file inputs), `food.*`
|
|
30
|
+
(list/search/recommendations), and `voice.stt`.
|
|
31
|
+
- `FormData` (multipart) support in the HTTP layer for file uploads.
|
|
32
|
+
|
|
33
|
+
## [0.1.0] - 2026-06-19
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
- Initial release. One typed client over the Curvet Playground API:
|
|
37
|
+
`chat.create`, `image.generate`, `video.generate`/`submit` with **async
|
|
38
|
+
auto-polling**, `jobs.retrieve`, `models.list`, `balance.get`.
|
|
39
|
+
- Typed error taxonomy (`AuthError`, `InsufficientBalanceError`, `RateLimitError`,
|
|
40
|
+
`JobFailedError`, …) and automatic retry/backoff on 429/5xx.
|
|
41
|
+
- Live model catalog (never hardcoded). Ships ESM + CJS + type declarations.
|
|
42
|
+
|
|
43
|
+
[0.3.0]: https://github.com/Curvet-in/curvet-sdk/releases/tag/v0.3.0
|
|
44
|
+
[0.2.1]: https://github.com/Curvet-in/curvet-sdk/releases/tag/v0.2.1
|
|
45
|
+
[0.1.0]: https://github.com/Curvet-in/curvet-sdk/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -83,11 +83,57 @@ if (job.status !== "completed") {
|
|
|
83
83
|
const status = await curvet.jobs.retrieve(job.jobId!);
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
###
|
|
86
|
+
### Audio & 3D (async, same as video)
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
const audio = await curvet.audio.generate({ model: "fish-audio", prompt: "Hello there" });
|
|
90
|
+
const mesh = await curvet.threeD.generate({ model: "meshy-3d", prompt: "a ceramic mug" });
|
|
91
|
+
console.log(audio.mediaUrl, mesh.mediaUrl);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Models, balance & analytics
|
|
87
95
|
|
|
88
96
|
```ts
|
|
89
97
|
const chatModels = await curvet.models.list({ type: "chat" });
|
|
90
98
|
const balance = await curvet.balance.get();
|
|
99
|
+
const analytics = await curvet.analytics.get({ startDate: "2026-01-01", endDate: "2026-02-01" });
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Workflows
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
// JSON inputs:
|
|
106
|
+
const out = await curvet.workflows.run("workflowId", { inputs: { topic: "ai" } });
|
|
107
|
+
|
|
108
|
+
// With file inputs (multipart, handled for you):
|
|
109
|
+
await curvet.workflows.run("workflowId", {
|
|
110
|
+
inputs: { caption: "hello" },
|
|
111
|
+
files: { image: new Blob([bytes], { type: "image/png" }) },
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
For long workflows (video/audio/3D nodes), use the **pollable** API — submit
|
|
116
|
+
and auto-poll to completion with live progress, instead of one long HTTP call:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
const run = await curvet.workflows.runAndPoll(
|
|
120
|
+
"workflowId",
|
|
121
|
+
{ inputs: { topic: "ai" } },
|
|
122
|
+
{ onProgress: (r) => console.log(r.status, r.progress + "%", r.currentNode?.label) },
|
|
123
|
+
);
|
|
124
|
+
console.log(run.result);
|
|
125
|
+
|
|
126
|
+
// Or fire-and-forget + poll yourself:
|
|
127
|
+
const { runId } = await curvet.workflows.submit("workflowId", { inputs: {} });
|
|
128
|
+
const status = await curvet.workflows.runs.retrieve(runId);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Food & speech-to-text
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
const dishes = await curvet.food.search("paneer", { limit: 5 });
|
|
135
|
+
const stt = await curvet.voice.stt({ audio: audioBytes, filename: "clip.wav" });
|
|
136
|
+
console.log(stt.text);
|
|
91
137
|
```
|
|
92
138
|
|
|
93
139
|
## Errors
|
|
@@ -155,6 +201,32 @@ npm run build # ESM + CJS + d.ts via tsup
|
|
|
155
201
|
CURVET_TEST_APP_KEY=cvt_app_xxx npm test
|
|
156
202
|
```
|
|
157
203
|
|
|
204
|
+
## Releasing
|
|
205
|
+
|
|
206
|
+
Publishing happens locally with the release script — no npm token, no CI.
|
|
207
|
+
`npm publish` prompts for your passkey/2FA, which you approve in the browser.
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
./scripts/release.sh # publish the current package.json version
|
|
211
|
+
./scripts/release.sh patch # bump patch, then publish (0.2.0 -> 0.2.1)
|
|
212
|
+
./scripts/release.sh minor # bump minor, then publish
|
|
213
|
+
./scripts/release.sh major # bump major, then publish
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
The script validates (typecheck + tests + build) before bumping, tags the
|
|
217
|
+
version, publishes (you approve the passkey prompt), then pushes the tag and
|
|
218
|
+
commit to GitHub. The order is deliberate — if the publish is cancelled,
|
|
219
|
+
nothing is pushed; just re-run `npm publish && git push --follow-tags origin main`
|
|
220
|
+
to finish.
|
|
221
|
+
|
|
222
|
+
> A tokenless CI alternative using npm Trusted Publishing (OIDC) is also included
|
|
223
|
+
> at [`.github/workflows/publish.yml`](.github/workflows/publish.yml) for when
|
|
224
|
+
> GitHub Actions is available.
|
|
225
|
+
|
|
226
|
+
## Changelog
|
|
227
|
+
|
|
228
|
+
See [CHANGELOG.md](CHANGELOG.md) for release notes.
|
|
229
|
+
|
|
158
230
|
## License
|
|
159
231
|
|
|
160
232
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
APIError: () => APIError,
|
|
24
|
+
Analytics: () => Analytics,
|
|
25
|
+
Audio: () => Audio,
|
|
24
26
|
AuthError: () => AuthError,
|
|
25
27
|
BadRequestError: () => BadRequestError,
|
|
26
28
|
Balance: () => Balance,
|
|
@@ -29,17 +31,25 @@ __export(index_exports, {
|
|
|
29
31
|
Curvet: () => Curvet,
|
|
30
32
|
CurvetError: () => CurvetError,
|
|
31
33
|
DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
|
|
34
|
+
Food: () => Food,
|
|
32
35
|
Images: () => Images,
|
|
33
36
|
InsufficientBalanceError: () => InsufficientBalanceError,
|
|
34
37
|
Job: () => Job,
|
|
35
38
|
JobFailedError: () => JobFailedError,
|
|
36
39
|
JobTimeoutError: () => JobTimeoutError,
|
|
37
40
|
Jobs: () => Jobs,
|
|
41
|
+
MediaResource: () => MediaResource,
|
|
38
42
|
Models: () => Models,
|
|
39
43
|
NotFoundError: () => NotFoundError,
|
|
40
44
|
PermissionError: () => PermissionError,
|
|
41
45
|
RateLimitError: () => RateLimitError,
|
|
42
|
-
|
|
46
|
+
ThreeD: () => ThreeD,
|
|
47
|
+
Video: () => Video,
|
|
48
|
+
Voice: () => Voice,
|
|
49
|
+
WorkflowRunFailedError: () => WorkflowRunFailedError,
|
|
50
|
+
WorkflowRunTimeoutError: () => WorkflowRunTimeoutError,
|
|
51
|
+
WorkflowRuns: () => WorkflowRuns,
|
|
52
|
+
Workflows: () => Workflows
|
|
43
53
|
});
|
|
44
54
|
module.exports = __toCommonJS(index_exports);
|
|
45
55
|
|
|
@@ -82,6 +92,18 @@ var JobTimeoutError = class extends CurvetError {
|
|
|
82
92
|
this.jobId = jobId;
|
|
83
93
|
}
|
|
84
94
|
};
|
|
95
|
+
var WorkflowRunFailedError = class extends CurvetError {
|
|
96
|
+
constructor(message, runId, opts = {}) {
|
|
97
|
+
super(message, opts);
|
|
98
|
+
this.runId = runId;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
var WorkflowRunTimeoutError = class extends CurvetError {
|
|
102
|
+
constructor(message, runId, opts = {}) {
|
|
103
|
+
super(message, opts);
|
|
104
|
+
this.runId = runId;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
85
107
|
function errorFromResponse(status, body, requestId, headers) {
|
|
86
108
|
const b = body ?? {};
|
|
87
109
|
const message = typeof b.error === "string" ? b.error : `HTTP ${status}`;
|
|
@@ -170,8 +192,12 @@ var HttpClient = class {
|
|
|
170
192
|
};
|
|
171
193
|
let payload;
|
|
172
194
|
if (body !== void 0) {
|
|
173
|
-
|
|
174
|
-
|
|
195
|
+
if (typeof FormData !== "undefined" && body instanceof FormData) {
|
|
196
|
+
payload = body;
|
|
197
|
+
} else {
|
|
198
|
+
headers["content-type"] = "application/json";
|
|
199
|
+
payload = JSON.stringify(body);
|
|
200
|
+
}
|
|
175
201
|
}
|
|
176
202
|
let attempt = 0;
|
|
177
203
|
for (; ; ) {
|
|
@@ -405,20 +431,17 @@ var Job = class {
|
|
|
405
431
|
}
|
|
406
432
|
};
|
|
407
433
|
|
|
408
|
-
// src/resources/
|
|
409
|
-
var
|
|
410
|
-
constructor(client, defaults, path
|
|
434
|
+
// src/resources/media.ts
|
|
435
|
+
var MediaResource = class {
|
|
436
|
+
constructor(client, defaults, path) {
|
|
411
437
|
this.client = client;
|
|
412
438
|
this.defaults = defaults;
|
|
413
439
|
this.path = path;
|
|
414
440
|
}
|
|
415
441
|
/**
|
|
416
|
-
* Submit
|
|
417
|
-
*
|
|
418
|
-
*
|
|
419
|
-
* The media POST long-polls server-side and can block well past a normal
|
|
420
|
-
* request timeout, so we default its timeout to the poll budget and disable
|
|
421
|
-
* auto-retry (a retried POST would enqueue a duplicate, double-charged job).
|
|
442
|
+
* Submit WITHOUT polling. The media POST long-polls server-side and can block
|
|
443
|
+
* well past a normal request timeout, so we default its timeout to the poll
|
|
444
|
+
* budget and disable auto-retry (a retried POST would enqueue a duplicate job).
|
|
422
445
|
*/
|
|
423
446
|
async submit(params, options) {
|
|
424
447
|
const reqOptions = {
|
|
@@ -434,10 +457,7 @@ var Video = class {
|
|
|
434
457
|
});
|
|
435
458
|
return normalizeMediaPost(body);
|
|
436
459
|
}
|
|
437
|
-
/**
|
|
438
|
-
* Submit and resolve to the finished media. Handles the 200-vs-202 split and
|
|
439
|
-
* polls `/jobs/:id` internally. Throws JobFailedError / JobTimeoutError.
|
|
440
|
-
*/
|
|
460
|
+
/** Submit and resolve to the finished media (auto-polls /jobs/:id). */
|
|
441
461
|
async generate(params, options) {
|
|
442
462
|
const submitted = await this.submit(params, options);
|
|
443
463
|
if (submitted.status === "completed" || submitted.status === "failed") {
|
|
@@ -458,6 +478,27 @@ var Video = class {
|
|
|
458
478
|
}
|
|
459
479
|
};
|
|
460
480
|
|
|
481
|
+
// src/resources/video.ts
|
|
482
|
+
var Video = class extends MediaResource {
|
|
483
|
+
constructor(client, defaults) {
|
|
484
|
+
super(client, defaults, "/video");
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
// src/resources/audio.ts
|
|
489
|
+
var Audio = class extends MediaResource {
|
|
490
|
+
constructor(client, defaults) {
|
|
491
|
+
super(client, defaults, "/audio");
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
// src/resources/threeD.ts
|
|
496
|
+
var ThreeD = class extends MediaResource {
|
|
497
|
+
constructor(client, defaults) {
|
|
498
|
+
super(client, defaults, "/3d");
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
|
|
461
502
|
// src/resources/models.ts
|
|
462
503
|
var Models = class {
|
|
463
504
|
constructor(client, cacheTtlMs = 6e4) {
|
|
@@ -510,6 +551,227 @@ var Balance = class {
|
|
|
510
551
|
}
|
|
511
552
|
};
|
|
512
553
|
|
|
554
|
+
// src/resources/analytics.ts
|
|
555
|
+
var Analytics = class {
|
|
556
|
+
constructor(client) {
|
|
557
|
+
this.client = client;
|
|
558
|
+
}
|
|
559
|
+
/** Usage analytics for the app, optionally bounded by a date range. */
|
|
560
|
+
async get(params = {}) {
|
|
561
|
+
const { startDate, endDate, ...options } = params;
|
|
562
|
+
const body = await this.client.request({
|
|
563
|
+
method: "GET",
|
|
564
|
+
path: "/analytics",
|
|
565
|
+
query: { startDate, endDate },
|
|
566
|
+
options
|
|
567
|
+
});
|
|
568
|
+
return body.analytics;
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
// src/resources/workflows.ts
|
|
573
|
+
function normalizeRun(body) {
|
|
574
|
+
const b = body ?? {};
|
|
575
|
+
return {
|
|
576
|
+
runId: b.runId,
|
|
577
|
+
status: b.status,
|
|
578
|
+
progress: b.progress,
|
|
579
|
+
totalNodes: b.totalNodes,
|
|
580
|
+
completedNodeCount: b.completedNodeCount,
|
|
581
|
+
currentNode: b.currentNode ?? null,
|
|
582
|
+
nodesExecuted: b.nodesExecuted,
|
|
583
|
+
result: b.result,
|
|
584
|
+
error: b.error ?? null,
|
|
585
|
+
startTime: b.startTime,
|
|
586
|
+
endTime: b.endTime,
|
|
587
|
+
raw: body
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
function buildBody(params, extra = {}) {
|
|
591
|
+
const hasFiles = params.files && Object.keys(params.files).length > 0;
|
|
592
|
+
if (hasFiles) {
|
|
593
|
+
const form = new FormData();
|
|
594
|
+
form.append("inputs", JSON.stringify(params.inputs ?? {}));
|
|
595
|
+
if (params.includeFullState !== void 0) {
|
|
596
|
+
form.append("includeFullState", String(params.includeFullState));
|
|
597
|
+
}
|
|
598
|
+
for (const [k, v] of Object.entries(extra)) form.append(k, String(v));
|
|
599
|
+
for (const [field, file] of Object.entries(params.files)) {
|
|
600
|
+
form.append(field, file);
|
|
601
|
+
}
|
|
602
|
+
return form;
|
|
603
|
+
}
|
|
604
|
+
return {
|
|
605
|
+
inputs: params.inputs ?? {},
|
|
606
|
+
includeFullState: params.includeFullState,
|
|
607
|
+
...extra
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
var WorkflowRuns = class {
|
|
611
|
+
constructor(client) {
|
|
612
|
+
this.client = client;
|
|
613
|
+
}
|
|
614
|
+
/** Fetch the current status of an async run once (no polling). */
|
|
615
|
+
async retrieve(runId, options) {
|
|
616
|
+
const body = await this.client.request({
|
|
617
|
+
method: "GET",
|
|
618
|
+
path: `/workflows/runs/${encodeURIComponent(runId)}`,
|
|
619
|
+
options
|
|
620
|
+
});
|
|
621
|
+
return normalizeRun(body);
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
var Workflows = class {
|
|
625
|
+
constructor(client) {
|
|
626
|
+
this.client = client;
|
|
627
|
+
this.runs = new WorkflowRuns(client);
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Execute a workflow synchronously (blocks until it finishes). Best for short
|
|
631
|
+
* workflows; for long ones (video/audio/3D nodes) prefer `runAndPoll`.
|
|
632
|
+
* Sends JSON, or multipart/form-data when file inputs are provided.
|
|
633
|
+
*/
|
|
634
|
+
async run(id, params = {}, options) {
|
|
635
|
+
const reqOptions = { ...options, maxRetries: options?.maxRetries ?? 0 };
|
|
636
|
+
return this.client.request({
|
|
637
|
+
method: "POST",
|
|
638
|
+
path: `/workflows/${encodeURIComponent(id)}/run`,
|
|
639
|
+
body: buildBody(params),
|
|
640
|
+
options: reqOptions
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Submit a workflow in async (pollable) mode — returns immediately with a
|
|
645
|
+
* runId. Poll `runs.retrieve(runId)` for status, or use `runAndPoll`.
|
|
646
|
+
*/
|
|
647
|
+
async submit(id, params = {}, options) {
|
|
648
|
+
const reqOptions = { ...options, maxRetries: options?.maxRetries ?? 0 };
|
|
649
|
+
const res = await this.client.request({
|
|
650
|
+
method: "POST",
|
|
651
|
+
path: `/workflows/${encodeURIComponent(id)}/run`,
|
|
652
|
+
body: buildBody(params, { async: true }),
|
|
653
|
+
options: reqOptions
|
|
654
|
+
});
|
|
655
|
+
return { runId: res?.runId, status: res?.status ?? "running", raw: res };
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Submit and poll to completion. Resolves with the completed run (including
|
|
659
|
+
* `result`), reporting progress via `onProgress`. Throws WorkflowRunFailedError
|
|
660
|
+
* on failure, WorkflowRunTimeoutError on timeout.
|
|
661
|
+
*/
|
|
662
|
+
async runAndPoll(id, params = {}, opts = {}) {
|
|
663
|
+
const submitted = await this.submit(id, params, opts);
|
|
664
|
+
if (!submitted.runId) {
|
|
665
|
+
throw new CurvetError("Workflow submit did not return a runId", {
|
|
666
|
+
raw: submitted.raw
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
const intervalMs = opts.pollIntervalMs ?? 2500;
|
|
670
|
+
const timeoutMs = opts.pollTimeoutMs ?? 3e5;
|
|
671
|
+
let run;
|
|
672
|
+
try {
|
|
673
|
+
run = await pollUntil(
|
|
674
|
+
() => this.runs.retrieve(submitted.runId, { signal: opts.signal }),
|
|
675
|
+
{
|
|
676
|
+
intervalMs,
|
|
677
|
+
timeoutMs,
|
|
678
|
+
signal: opts.signal,
|
|
679
|
+
isTerminal: (r) => r.status === "completed" || r.status === "failed" || r.status === "stopped",
|
|
680
|
+
onTick: (r) => opts.onProgress?.(r)
|
|
681
|
+
}
|
|
682
|
+
);
|
|
683
|
+
} catch (e) {
|
|
684
|
+
if (e instanceof PollTimeoutError) {
|
|
685
|
+
throw new WorkflowRunTimeoutError(
|
|
686
|
+
`Workflow run ${submitted.runId} did not finish within ${timeoutMs}ms`,
|
|
687
|
+
submitted.runId
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
throw e;
|
|
691
|
+
}
|
|
692
|
+
if (run.status === "failed" || run.status === "stopped") {
|
|
693
|
+
throw new WorkflowRunFailedError(
|
|
694
|
+
run.error || `Workflow run ${submitted.runId} ${run.status}`,
|
|
695
|
+
submitted.runId,
|
|
696
|
+
{ raw: run.raw }
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
return run;
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
// src/resources/food.ts
|
|
704
|
+
var Food = class {
|
|
705
|
+
constructor(client) {
|
|
706
|
+
this.client = client;
|
|
707
|
+
}
|
|
708
|
+
/** List dishes (default limit 20). */
|
|
709
|
+
async list(opts) {
|
|
710
|
+
const { limit, ...options } = opts ?? {};
|
|
711
|
+
const body = await this.client.request({
|
|
712
|
+
method: "GET",
|
|
713
|
+
path: "/food",
|
|
714
|
+
query: { limit },
|
|
715
|
+
options
|
|
716
|
+
});
|
|
717
|
+
return body.data;
|
|
718
|
+
}
|
|
719
|
+
/** Full-text search for dishes. */
|
|
720
|
+
async search(query, opts) {
|
|
721
|
+
const { limit, ...options } = opts ?? {};
|
|
722
|
+
const body = await this.client.request({
|
|
723
|
+
method: "GET",
|
|
724
|
+
path: "/food/search",
|
|
725
|
+
query: { q: query, limit },
|
|
726
|
+
options
|
|
727
|
+
});
|
|
728
|
+
return body.data;
|
|
729
|
+
}
|
|
730
|
+
/** Natural-language dish recommendations. */
|
|
731
|
+
async recommendations(prompt, options) {
|
|
732
|
+
const body = await this.client.request({
|
|
733
|
+
method: "POST",
|
|
734
|
+
path: "/food/recommendations",
|
|
735
|
+
body: { prompt },
|
|
736
|
+
options
|
|
737
|
+
});
|
|
738
|
+
return body.data;
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
// src/resources/voice.ts
|
|
743
|
+
var Voice = class {
|
|
744
|
+
constructor(client) {
|
|
745
|
+
this.client = client;
|
|
746
|
+
}
|
|
747
|
+
async stt(params, options) {
|
|
748
|
+
const form = new FormData();
|
|
749
|
+
form.append("audio", toBlob(params.audio), params.filename ?? "audio");
|
|
750
|
+
if (params.provider) form.append("provider", params.provider);
|
|
751
|
+
if (params.model) form.append("model", params.model);
|
|
752
|
+
if (params.prompt) form.append("prompt", params.prompt);
|
|
753
|
+
if (params.languageCode) form.append("languageCode", params.languageCode);
|
|
754
|
+
if (params.allowFallback !== void 0) {
|
|
755
|
+
form.append("allowFallback", String(params.allowFallback));
|
|
756
|
+
}
|
|
757
|
+
const reqOptions = {
|
|
758
|
+
...options,
|
|
759
|
+
timeout: options?.timeout ?? 12e4,
|
|
760
|
+
maxRetries: options?.maxRetries ?? 0
|
|
761
|
+
};
|
|
762
|
+
return this.client.request({
|
|
763
|
+
method: "POST",
|
|
764
|
+
path: "/voice/stt/public",
|
|
765
|
+
body: form,
|
|
766
|
+
options: reqOptions
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
function toBlob(audio) {
|
|
771
|
+
if (typeof Blob !== "undefined" && audio instanceof Blob) return audio;
|
|
772
|
+
return new Blob([audio]);
|
|
773
|
+
}
|
|
774
|
+
|
|
513
775
|
// src/client.ts
|
|
514
776
|
var DEFAULT_BASE_URL = "https://curvet.ai/api/v1/playground";
|
|
515
777
|
var Curvet = class {
|
|
@@ -526,13 +788,16 @@ var Curvet = class {
|
|
|
526
788
|
"No fetch implementation available. Use Node 18+ or pass { fetch }."
|
|
527
789
|
);
|
|
528
790
|
}
|
|
529
|
-
const
|
|
791
|
+
const playgroundBase = options.baseURL ?? DEFAULT_BASE_URL;
|
|
792
|
+
const v1Base = playgroundBase.replace(/\/playground\/?$/, "");
|
|
793
|
+
const shared = {
|
|
530
794
|
appKey,
|
|
531
|
-
baseURL: options.baseURL ?? DEFAULT_BASE_URL,
|
|
532
795
|
timeout: options.timeout ?? 6e4,
|
|
533
796
|
maxRetries: options.maxRetries ?? 2,
|
|
534
797
|
fetch: fetchImpl
|
|
535
|
-
}
|
|
798
|
+
};
|
|
799
|
+
const client = new HttpClient({ ...shared, baseURL: playgroundBase });
|
|
800
|
+
const v1Client = new HttpClient({ ...shared, baseURL: v1Base });
|
|
536
801
|
const jobDefaults = {
|
|
537
802
|
pollIntervalMs: options.defaultPollIntervalMs ?? 2500,
|
|
538
803
|
pollTimeoutMs: options.defaultPollTimeoutMs ?? 18e4
|
|
@@ -541,8 +806,14 @@ var Curvet = class {
|
|
|
541
806
|
this.image = new Images(client);
|
|
542
807
|
this.jobs = new Jobs(client, jobDefaults);
|
|
543
808
|
this.video = new Video(client, jobDefaults);
|
|
809
|
+
this.audio = new Audio(client, jobDefaults);
|
|
810
|
+
this.threeD = new ThreeD(client, jobDefaults);
|
|
544
811
|
this.models = new Models(client);
|
|
545
812
|
this.balance = new Balance(client);
|
|
813
|
+
this.analytics = new Analytics(client);
|
|
814
|
+
this.workflows = new Workflows(client);
|
|
815
|
+
this.food = new Food(v1Client);
|
|
816
|
+
this.voice = new Voice(v1Client);
|
|
546
817
|
}
|
|
547
818
|
};
|
|
548
819
|
function envKey() {
|
|
@@ -555,6 +826,8 @@ function defaultFetch() {
|
|
|
555
826
|
// Annotate the CommonJS export names for ESM import in node:
|
|
556
827
|
0 && (module.exports = {
|
|
557
828
|
APIError,
|
|
829
|
+
Analytics,
|
|
830
|
+
Audio,
|
|
558
831
|
AuthError,
|
|
559
832
|
BadRequestError,
|
|
560
833
|
Balance,
|
|
@@ -563,16 +836,24 @@ function defaultFetch() {
|
|
|
563
836
|
Curvet,
|
|
564
837
|
CurvetError,
|
|
565
838
|
DEFAULT_BASE_URL,
|
|
839
|
+
Food,
|
|
566
840
|
Images,
|
|
567
841
|
InsufficientBalanceError,
|
|
568
842
|
Job,
|
|
569
843
|
JobFailedError,
|
|
570
844
|
JobTimeoutError,
|
|
571
845
|
Jobs,
|
|
846
|
+
MediaResource,
|
|
572
847
|
Models,
|
|
573
848
|
NotFoundError,
|
|
574
849
|
PermissionError,
|
|
575
850
|
RateLimitError,
|
|
576
|
-
|
|
851
|
+
ThreeD,
|
|
852
|
+
Video,
|
|
853
|
+
Voice,
|
|
854
|
+
WorkflowRunFailedError,
|
|
855
|
+
WorkflowRunTimeoutError,
|
|
856
|
+
WorkflowRuns,
|
|
857
|
+
Workflows
|
|
577
858
|
});
|
|
578
859
|
//# sourceMappingURL=index.cjs.map
|