daishi-sdk 0.1.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/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/generated.d.ts +870 -0
- package/dist/generated.js +367 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.js +165 -0
- package/package.json +33 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
// Generated from the Daishi Studio API's OpenAPI document, https://daishi.ai/api/v1/openapi.json. Do not edit.
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
/** The API version these types describe. */
|
|
4
|
+
export const API_VERSION = '1.0.0';
|
|
5
|
+
/** One method per API operation. The transport is the subclass's. */
|
|
6
|
+
export class Operations {
|
|
7
|
+
/**
|
|
8
|
+
* API index.
|
|
9
|
+
*
|
|
10
|
+
* Discover the API without a token: endpoints, docs, the MCP path.
|
|
11
|
+
*
|
|
12
|
+
* @see https://daishi.ai/docs/developer/rest#ep-index
|
|
13
|
+
*/
|
|
14
|
+
index() {
|
|
15
|
+
return this.request('GET', '/');
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* OpenAPI document.
|
|
19
|
+
*
|
|
20
|
+
* The API as an OpenAPI 3.1 document, for generators and API clients.
|
|
21
|
+
*
|
|
22
|
+
* @see https://daishi.ai/docs/developer/rest#ep-openapi
|
|
23
|
+
*/
|
|
24
|
+
openapi() {
|
|
25
|
+
return this.request('GET', '/openapi.json');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Who am I.
|
|
29
|
+
*
|
|
30
|
+
* The account, plan, this token’s scopes and the limits that bound scenarios and runs.
|
|
31
|
+
*
|
|
32
|
+
* @see https://daishi.ai/docs/developer/rest#ep-me
|
|
33
|
+
*/
|
|
34
|
+
me() {
|
|
35
|
+
return this.request('GET', '/me');
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Scenario schema.
|
|
39
|
+
*
|
|
40
|
+
* JSON Schema (draft 2020-12) for a scenario body, with the live resource types and anchor ids.
|
|
41
|
+
*
|
|
42
|
+
* @see https://daishi.ai/docs/developer/rest#ep-schema
|
|
43
|
+
*/
|
|
44
|
+
schema() {
|
|
45
|
+
return this.request('GET', '/scenarios/schema');
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Library scenarios.
|
|
49
|
+
*
|
|
50
|
+
* The scenarios the platform ships. Any of their ids can be launched directly.
|
|
51
|
+
*
|
|
52
|
+
* @see https://daishi.ai/docs/developer/rest#ep-library-scenarios
|
|
53
|
+
*/
|
|
54
|
+
libraryScenarios() {
|
|
55
|
+
return this.request('GET', '/library/scenarios');
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Anchor bots.
|
|
59
|
+
*
|
|
60
|
+
* Baseline bot policies and populations a scenario may field as `env.anchors`.
|
|
61
|
+
*
|
|
62
|
+
* @see https://daishi.ai/docs/developer/rest#ep-library-anchors
|
|
63
|
+
*/
|
|
64
|
+
libraryAnchors() {
|
|
65
|
+
return this.request('GET', '/library/anchors');
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Validate a scenario.
|
|
69
|
+
*
|
|
70
|
+
* Dry run: the normalized definition, its content hash and the plan receipt. Nothing is saved.
|
|
71
|
+
*
|
|
72
|
+
* @see https://daishi.ai/docs/developer/rest#ep-validate
|
|
73
|
+
*/
|
|
74
|
+
validate(body) {
|
|
75
|
+
return this.request('POST', '/scenarios/validate', { body });
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* List your scenarios.
|
|
79
|
+
*
|
|
80
|
+
* Every scenario saved on the account, newest first.
|
|
81
|
+
*
|
|
82
|
+
* @see https://daishi.ai/docs/developer/rest#ep-list-scenarios
|
|
83
|
+
*/
|
|
84
|
+
listScenarios() {
|
|
85
|
+
return this.request('GET', '/scenarios');
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Create a scenario.
|
|
89
|
+
*
|
|
90
|
+
* Save a scenario. Idempotent on name plus content hash.
|
|
91
|
+
*
|
|
92
|
+
* @see https://daishi.ai/docs/developer/rest#ep-create-scenario
|
|
93
|
+
*/
|
|
94
|
+
createScenario(body) {
|
|
95
|
+
return this.request('POST', '/scenarios', { body });
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get a scenario.
|
|
99
|
+
*
|
|
100
|
+
* One saved scenario by id.
|
|
101
|
+
*
|
|
102
|
+
* @see https://daishi.ai/docs/developer/rest#ep-get-scenario
|
|
103
|
+
*/
|
|
104
|
+
getScenario(id) {
|
|
105
|
+
return this.request('GET', `/scenarios/${encodeURIComponent(id)}`);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Update a scenario.
|
|
109
|
+
*
|
|
110
|
+
* Edit a saved scenario in place. Omitted fields keep their value.
|
|
111
|
+
*
|
|
112
|
+
* @see https://daishi.ai/docs/developer/rest#ep-update-scenario
|
|
113
|
+
*/
|
|
114
|
+
updateScenario(id, body) {
|
|
115
|
+
return this.request('PUT', `/scenarios/${encodeURIComponent(id)}`, { body });
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Delete a scenario.
|
|
119
|
+
*
|
|
120
|
+
* Remove a saved scenario. Played runs keep their snapshot.
|
|
121
|
+
*
|
|
122
|
+
* @see https://daishi.ai/docs/developer/rest#ep-delete-scenario
|
|
123
|
+
*/
|
|
124
|
+
deleteScenario(id) {
|
|
125
|
+
return this.request('DELETE', `/scenarios/${encodeURIComponent(id)}`);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* List skills.
|
|
129
|
+
*
|
|
130
|
+
* The built-in skills, your own, and the authoring bounds.
|
|
131
|
+
*
|
|
132
|
+
* @see https://daishi.ai/docs/developer/rest#ep-list-skills
|
|
133
|
+
*/
|
|
134
|
+
listSkills() {
|
|
135
|
+
return this.request('GET', '/skills');
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Create a skill.
|
|
139
|
+
*
|
|
140
|
+
* Save a prompt module on the account.
|
|
141
|
+
*
|
|
142
|
+
* @see https://daishi.ai/docs/developer/rest#ep-create-skill
|
|
143
|
+
*/
|
|
144
|
+
createSkill(body) {
|
|
145
|
+
return this.request('POST', '/skills', { body });
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get a skill.
|
|
149
|
+
*
|
|
150
|
+
* One skill by id: a built-in or one of yours.
|
|
151
|
+
*
|
|
152
|
+
* @see https://daishi.ai/docs/developer/rest#ep-get-skill
|
|
153
|
+
*/
|
|
154
|
+
getSkill(id) {
|
|
155
|
+
return this.request('GET', `/skills/${encodeURIComponent(id)}`);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Update a skill.
|
|
159
|
+
*
|
|
160
|
+
* Edit in place. Omitted fields keep their value.
|
|
161
|
+
*
|
|
162
|
+
* @see https://daishi.ai/docs/developer/rest#ep-update-skill
|
|
163
|
+
*/
|
|
164
|
+
updateSkill(id, body) {
|
|
165
|
+
return this.request('PUT', `/skills/${encodeURIComponent(id)}`, { body });
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Delete a skill.
|
|
169
|
+
*
|
|
170
|
+
* Remove one of your skills. Played runs keep their snapshot.
|
|
171
|
+
*
|
|
172
|
+
* @see https://daishi.ai/docs/developer/rest#ep-delete-skill
|
|
173
|
+
*/
|
|
174
|
+
deleteSkill(id) {
|
|
175
|
+
return this.request('DELETE', `/skills/${encodeURIComponent(id)}`);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Estimate a run.
|
|
179
|
+
*
|
|
180
|
+
* What a launch of this body would cost and commit, or the refusal it would get. Nothing is queued.
|
|
181
|
+
*
|
|
182
|
+
* @see https://daishi.ai/docs/developer/rest#ep-estimate
|
|
183
|
+
*/
|
|
184
|
+
estimate(body) {
|
|
185
|
+
return this.request('POST', '/runs/estimate', { body });
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Launch a run.
|
|
189
|
+
*
|
|
190
|
+
* Queue a run, or a series batch when `trials` is above 1.
|
|
191
|
+
*
|
|
192
|
+
* @see https://daishi.ai/docs/developer/rest#ep-launch
|
|
193
|
+
*/
|
|
194
|
+
launch(body) {
|
|
195
|
+
return this.request('POST', '/runs', { body });
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* List your runs.
|
|
199
|
+
*
|
|
200
|
+
* Your runs, newest first.
|
|
201
|
+
*
|
|
202
|
+
* @see https://daishi.ai/docs/developer/rest#ep-list-runs
|
|
203
|
+
*/
|
|
204
|
+
listRuns(query = {}) {
|
|
205
|
+
return this.request('GET', '/runs', { query });
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Get a run.
|
|
209
|
+
*
|
|
210
|
+
* One run: status, the as-played configuration and, once finished, the scored results per seat.
|
|
211
|
+
*
|
|
212
|
+
* @see https://daishi.ai/docs/developer/rest#ep-get-run
|
|
213
|
+
*/
|
|
214
|
+
getRun(id) {
|
|
215
|
+
return this.request('GET', `/runs/${encodeURIComponent(id)}`);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Usage and allowances.
|
|
219
|
+
*
|
|
220
|
+
* The meter’s record of your runs and where the account stands against the plan this month.
|
|
221
|
+
*
|
|
222
|
+
* @see https://daishi.ai/docs/developer/rest#ep-usage
|
|
223
|
+
*/
|
|
224
|
+
usage() {
|
|
225
|
+
return this.request('GET', '/usage');
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Live worlds.
|
|
229
|
+
*
|
|
230
|
+
* Every world your account has playing right now, as small boards, and every run waiting for one.
|
|
231
|
+
*
|
|
232
|
+
* @see https://daishi.ai/docs/developer/rest#ep-live
|
|
233
|
+
*/
|
|
234
|
+
live() {
|
|
235
|
+
return this.request('GET', '/live');
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Get a batch report.
|
|
239
|
+
*
|
|
240
|
+
* A series batch as one report: per-model intervals, survival, cost, every trial, and a verdict.
|
|
241
|
+
*
|
|
242
|
+
* @see https://daishi.ai/docs/developer/rest#ep-get-batch
|
|
243
|
+
*/
|
|
244
|
+
getBatch(id) {
|
|
245
|
+
return this.request('GET', `/batches/${encodeURIComponent(id)}`);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Estimate an experiment.
|
|
249
|
+
*
|
|
250
|
+
* What creating this experiment would cost and check against, or the refusal it would get. Nothing is queued.
|
|
251
|
+
*
|
|
252
|
+
* @see https://daishi.ai/docs/developer/rest#ep-estimate-experiment
|
|
253
|
+
*/
|
|
254
|
+
estimateExperiment(body) {
|
|
255
|
+
return this.request('POST', '/experiments/estimate', { body });
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Create an experiment.
|
|
259
|
+
*
|
|
260
|
+
* Queue a paired experiment: two to four arms of one scenario on shared seeds, under a stopping rule.
|
|
261
|
+
*
|
|
262
|
+
* @see https://daishi.ai/docs/developer/rest#ep-create-experiment
|
|
263
|
+
*/
|
|
264
|
+
createExperiment(body) {
|
|
265
|
+
return this.request('POST', '/experiments', { body });
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* List your experiments.
|
|
269
|
+
*
|
|
270
|
+
* Your experiments, newest first, with the verdict on record.
|
|
271
|
+
*
|
|
272
|
+
* @see https://daishi.ai/docs/developer/rest#ep-list-experiments
|
|
273
|
+
*/
|
|
274
|
+
listExperiments() {
|
|
275
|
+
return this.request('GET', '/experiments');
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Get an experiment report.
|
|
279
|
+
*
|
|
280
|
+
* The paired report as scored right now: per arm, per pair, each comparison against the control, the decision and a verdict.
|
|
281
|
+
*
|
|
282
|
+
* @see https://daishi.ai/docs/developer/rest#ep-get-experiment
|
|
283
|
+
*/
|
|
284
|
+
getExperiment(id) {
|
|
285
|
+
return this.request('GET', `/experiments/${encodeURIComponent(id)}`);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Cancel an experiment.
|
|
289
|
+
*
|
|
290
|
+
* Stop a queued or running experiment: every unfinished trial of every arm, then the record.
|
|
291
|
+
*
|
|
292
|
+
* @see https://daishi.ai/docs/developer/rest#ep-cancel-experiment
|
|
293
|
+
*/
|
|
294
|
+
cancelExperiment(id) {
|
|
295
|
+
return this.request('POST', `/experiments/${encodeURIComponent(id)}/cancel`);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Cancel a run.
|
|
299
|
+
*
|
|
300
|
+
* Stop a queued or running run.
|
|
301
|
+
*
|
|
302
|
+
* @see https://daishi.ai/docs/developer/rest#ep-cancel
|
|
303
|
+
*/
|
|
304
|
+
cancel(id) {
|
|
305
|
+
return this.request('POST', `/runs/${encodeURIComponent(id)}/cancel`);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* List a run’s logs.
|
|
309
|
+
*
|
|
310
|
+
* The harness log files kept for one of your runs, with their sizes and how long they are kept.
|
|
311
|
+
*
|
|
312
|
+
* @see https://daishi.ai/docs/developer/rest#ep-run-logs
|
|
313
|
+
*/
|
|
314
|
+
runLogs(id) {
|
|
315
|
+
return this.request('GET', `/runs/${encodeURIComponent(id)}/logs`);
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Read a run log.
|
|
319
|
+
*
|
|
320
|
+
* One log file, as the JSONL the harness wrote.
|
|
321
|
+
*
|
|
322
|
+
* @see https://daishi.ai/docs/developer/rest#ep-run-log
|
|
323
|
+
*/
|
|
324
|
+
runLog(id, file) {
|
|
325
|
+
return this.request('GET', `/runs/${encodeURIComponent(id)}/logs/${encodeURIComponent(file)}`, { text: true });
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* List a run’s guest seats.
|
|
329
|
+
*
|
|
330
|
+
* Every guest seat of one of your world runs: its link, its state and who claimed it.
|
|
331
|
+
*
|
|
332
|
+
* @see https://daishi.ai/docs/developer/rest#ep-list-invites
|
|
333
|
+
*/
|
|
334
|
+
listInvites(id) {
|
|
335
|
+
return this.request('GET', `/runs/${encodeURIComponent(id)}/invites`);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Extend the signup window.
|
|
339
|
+
*
|
|
340
|
+
* Move every open seat’s claim-by deadline to `minutes` from now.
|
|
341
|
+
*
|
|
342
|
+
* @see https://daishi.ai/docs/developer/rest#ep-extend-invites
|
|
343
|
+
*/
|
|
344
|
+
extendInvites(id, body) {
|
|
345
|
+
return this.request('POST', `/runs/${encodeURIComponent(id)}/invites/extend`, { body });
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Regenerate a seat’s link.
|
|
349
|
+
*
|
|
350
|
+
* A fresh link for one seat; the old token stops resolving at once.
|
|
351
|
+
*
|
|
352
|
+
* @see https://daishi.ai/docs/developer/rest#ep-regenerate-invite
|
|
353
|
+
*/
|
|
354
|
+
regenerateInvite(id, inviteId) {
|
|
355
|
+
return this.request('POST', `/runs/${encodeURIComponent(id)}/invites/${encodeURIComponent(inviteId)}/regenerate`);
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Withdraw a seat.
|
|
359
|
+
*
|
|
360
|
+
* Withdraw one guest seat: its link stops resolving and the seat leaves the roster at launch.
|
|
361
|
+
*
|
|
362
|
+
* @see https://daishi.ai/docs/developer/rest#ep-revoke-invite
|
|
363
|
+
*/
|
|
364
|
+
revokeInvite(id, inviteId) {
|
|
365
|
+
return this.request('POST', `/runs/${encodeURIComponent(id)}/invites/${encodeURIComponent(inviteId)}/revoke`);
|
|
366
|
+
}
|
|
367
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Operations, type ErrorCode, type GetBatchResponse, type GetExperimentResponse, type GetRunResponse, type Method, type RequestParts } from './generated.js';
|
|
2
|
+
export * from './generated.js';
|
|
3
|
+
export declare const VERSION = "0.1.0";
|
|
4
|
+
export declare const DEFAULT_BASE_URL = "https://daishi.ai/api/v1";
|
|
5
|
+
export interface DaishiOptions {
|
|
6
|
+
/** A Studio access token (`dsk_...`). Default: the `DAISHI_TOKEN` environment variable. */
|
|
7
|
+
token?: string;
|
|
8
|
+
/** Default: `https://daishi.ai/api/v1`. */
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
/** Retries after a refusal that is safe to repeat. Default 3; 0 turns retrying off. */
|
|
11
|
+
maxRetries?: number;
|
|
12
|
+
/** Per-attempt timeout in milliseconds. Default 60000. */
|
|
13
|
+
timeoutMs?: number;
|
|
14
|
+
/** A fetch implementation. Default: the global one (Node 18+, browsers, Deno, Bun). */
|
|
15
|
+
fetch?: typeof fetch;
|
|
16
|
+
}
|
|
17
|
+
export interface WaitOptions<T> {
|
|
18
|
+
/** Milliseconds between polls. Default 30000. */
|
|
19
|
+
intervalMs?: number;
|
|
20
|
+
/** Stops the wait; `AbortSignal.timeout(ms)` gives it a deadline. */
|
|
21
|
+
signal?: AbortSignal;
|
|
22
|
+
/** Called with every poll's answer, the last one included. */
|
|
23
|
+
onPoll?: (answer: T) => void;
|
|
24
|
+
}
|
|
25
|
+
/** A refusal from the API, or a transport failure after the retries ran out. */
|
|
26
|
+
export declare class DaishiError extends Error {
|
|
27
|
+
/** HTTP status; 0 when no response arrived. */
|
|
28
|
+
readonly status: number;
|
|
29
|
+
/** The API's error code (`unknown_run`, `rate_limited`, ...), or `http_<status>` when the body carried none. */
|
|
30
|
+
readonly code: ErrorCode | (string & {});
|
|
31
|
+
/** Seconds the `Retry-After` header asked for, when it sent one. */
|
|
32
|
+
readonly retryAfterSeconds: number | null;
|
|
33
|
+
/** The parsed error body, when there was one. */
|
|
34
|
+
readonly body: unknown;
|
|
35
|
+
readonly name = "DaishiError";
|
|
36
|
+
constructor(
|
|
37
|
+
/** HTTP status; 0 when no response arrived. */
|
|
38
|
+
status: number,
|
|
39
|
+
/** The API's error code (`unknown_run`, `rate_limited`, ...), or `http_<status>` when the body carried none. */
|
|
40
|
+
code: ErrorCode | (string & {}), message: string,
|
|
41
|
+
/** Seconds the `Retry-After` header asked for, when it sent one. */
|
|
42
|
+
retryAfterSeconds?: number | null,
|
|
43
|
+
/** The parsed error body, when there was one. */
|
|
44
|
+
body?: unknown);
|
|
45
|
+
/** Where the code is explained. */
|
|
46
|
+
get docs(): string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The Daishi Studio API. Every operation is a method (`getRun`, `launch`,
|
|
50
|
+
* `createScenario`, ...) typed from the API's OpenAPI document; the
|
|
51
|
+
* `waitFor*` helpers poll until a run, batch or experiment ends.
|
|
52
|
+
*
|
|
53
|
+
* Retries: a 429 is retried after the seconds its `Retry-After` names, and
|
|
54
|
+
* a `run_launching` refusal after a short pause, on any method, because the
|
|
55
|
+
* server refused before doing anything. A gateway error (502, 503, 504) or
|
|
56
|
+
* a dropped connection is retried on GET only: a write that may have landed
|
|
57
|
+
* is never sent twice.
|
|
58
|
+
*/
|
|
59
|
+
export declare class Daishi extends Operations {
|
|
60
|
+
readonly baseUrl: string;
|
|
61
|
+
private readonly token;
|
|
62
|
+
private readonly maxRetries;
|
|
63
|
+
private readonly timeoutMs;
|
|
64
|
+
private readonly fetchImpl;
|
|
65
|
+
constructor(options?: DaishiOptions);
|
|
66
|
+
protected request<T>(method: Method, path: string, parts?: RequestParts): Promise<T>;
|
|
67
|
+
/**
|
|
68
|
+
* Polls a run until it ends (finished, failed or cancelled) and answers
|
|
69
|
+
* with the last read. A finished run is read until its `results` arrive,
|
|
70
|
+
* which is when the match archives.
|
|
71
|
+
*/
|
|
72
|
+
waitForRun(id: string, options?: WaitOptions<GetRunResponse>): Promise<GetRunResponse>;
|
|
73
|
+
/** Polls a series batch until no trial is still to play. */
|
|
74
|
+
waitForBatch(id: string, options?: WaitOptions<GetBatchResponse>): Promise<GetBatchResponse>;
|
|
75
|
+
/** Polls a paired experiment until it finishes or is cancelled. */
|
|
76
|
+
waitForExperiment(id: string, options?: WaitOptions<GetExperimentResponse>): Promise<GetExperimentResponse>;
|
|
77
|
+
private poll;
|
|
78
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { Operations } from './generated.js';
|
|
2
|
+
export * from './generated.js';
|
|
3
|
+
export const VERSION = '0.1.0';
|
|
4
|
+
export const DEFAULT_BASE_URL = 'https://daishi.ai/api/v1';
|
|
5
|
+
/** A refusal from the API, or a transport failure after the retries ran out. */
|
|
6
|
+
export class DaishiError extends Error {
|
|
7
|
+
status;
|
|
8
|
+
code;
|
|
9
|
+
retryAfterSeconds;
|
|
10
|
+
body;
|
|
11
|
+
name = 'DaishiError';
|
|
12
|
+
constructor(
|
|
13
|
+
/** HTTP status; 0 when no response arrived. */
|
|
14
|
+
status,
|
|
15
|
+
/** The API's error code (`unknown_run`, `rate_limited`, ...), or `http_<status>` when the body carried none. */
|
|
16
|
+
code, message,
|
|
17
|
+
/** Seconds the `Retry-After` header asked for, when it sent one. */
|
|
18
|
+
retryAfterSeconds = null,
|
|
19
|
+
/** The parsed error body, when there was one. */
|
|
20
|
+
body = null) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.status = status;
|
|
23
|
+
this.code = code;
|
|
24
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
25
|
+
this.body = body;
|
|
26
|
+
}
|
|
27
|
+
/** Where the code is explained. */
|
|
28
|
+
get docs() {
|
|
29
|
+
return `https://daishi.ai/docs/developer/errors#${this.code}`;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const RUN_DONE = new Set(['finished', 'failed', 'cancelled']);
|
|
33
|
+
const EXPERIMENT_DONE = new Set(['finished', 'cancelled']);
|
|
34
|
+
const GATEWAY = new Set([502, 503, 504]);
|
|
35
|
+
const sleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
36
|
+
if (signal?.aborted)
|
|
37
|
+
return reject(signal.reason);
|
|
38
|
+
const t = setTimeout(() => {
|
|
39
|
+
signal?.removeEventListener('abort', onAbort);
|
|
40
|
+
resolve();
|
|
41
|
+
}, ms);
|
|
42
|
+
const onAbort = () => {
|
|
43
|
+
clearTimeout(t);
|
|
44
|
+
reject(signal.reason);
|
|
45
|
+
};
|
|
46
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
47
|
+
});
|
|
48
|
+
function envToken() {
|
|
49
|
+
const env = globalThis.process?.env;
|
|
50
|
+
return env?.DAISHI_TOKEN || undefined;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The Daishi Studio API. Every operation is a method (`getRun`, `launch`,
|
|
54
|
+
* `createScenario`, ...) typed from the API's OpenAPI document; the
|
|
55
|
+
* `waitFor*` helpers poll until a run, batch or experiment ends.
|
|
56
|
+
*
|
|
57
|
+
* Retries: a 429 is retried after the seconds its `Retry-After` names, and
|
|
58
|
+
* a `run_launching` refusal after a short pause, on any method, because the
|
|
59
|
+
* server refused before doing anything. A gateway error (502, 503, 504) or
|
|
60
|
+
* a dropped connection is retried on GET only: a write that may have landed
|
|
61
|
+
* is never sent twice.
|
|
62
|
+
*/
|
|
63
|
+
export class Daishi extends Operations {
|
|
64
|
+
baseUrl;
|
|
65
|
+
token;
|
|
66
|
+
maxRetries;
|
|
67
|
+
timeoutMs;
|
|
68
|
+
fetchImpl;
|
|
69
|
+
constructor(options = {}) {
|
|
70
|
+
super();
|
|
71
|
+
this.token = options.token ?? envToken();
|
|
72
|
+
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, '');
|
|
73
|
+
this.maxRetries = options.maxRetries ?? 3;
|
|
74
|
+
this.timeoutMs = options.timeoutMs ?? 60_000;
|
|
75
|
+
const f = options.fetch ?? globalThis.fetch;
|
|
76
|
+
if (!f)
|
|
77
|
+
throw new Error('No fetch implementation: use Node 18 or later, or pass options.fetch.');
|
|
78
|
+
this.fetchImpl = f.bind(globalThis);
|
|
79
|
+
}
|
|
80
|
+
async request(method, path, parts = {}) {
|
|
81
|
+
const url = new URL(this.baseUrl + (path === '/' ? '' : path));
|
|
82
|
+
for (const [k, v] of Object.entries(parts.query ?? {}))
|
|
83
|
+
if (v !== undefined && v !== null)
|
|
84
|
+
url.searchParams.set(k, String(v));
|
|
85
|
+
const headers = { accept: parts.text ? 'application/x-ndjson, application/json' : 'application/json' };
|
|
86
|
+
if (this.token)
|
|
87
|
+
headers.authorization = `Bearer ${this.token}`;
|
|
88
|
+
if (parts.body !== undefined)
|
|
89
|
+
headers['content-type'] = 'application/json';
|
|
90
|
+
const body = parts.body !== undefined ? JSON.stringify(parts.body) : undefined;
|
|
91
|
+
for (let attempt = 0;; attempt++) {
|
|
92
|
+
const canRetry = attempt < this.maxRetries;
|
|
93
|
+
let res;
|
|
94
|
+
try {
|
|
95
|
+
res = await this.fetchImpl(url, { method, headers, body, signal: AbortSignal.timeout(this.timeoutMs) });
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
if (method === 'GET' && canRetry) {
|
|
99
|
+
await sleep(backoffMs(attempt));
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
throw new DaishiError(0, 'network_error', `${method} ${url.pathname} failed: ${err.message ?? String(err)}`, null, null);
|
|
103
|
+
}
|
|
104
|
+
if (res.ok)
|
|
105
|
+
return (parts.text ? await res.text() : await res.json());
|
|
106
|
+
const text = await res.text();
|
|
107
|
+
let parsed = null;
|
|
108
|
+
try {
|
|
109
|
+
parsed = JSON.parse(text);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
parsed = null;
|
|
113
|
+
}
|
|
114
|
+
const code = parsed?.error ?? `http_${res.status}`;
|
|
115
|
+
const retryAfter = parseRetryAfter(res.headers.get('retry-after'));
|
|
116
|
+
const retryable = res.status === 429 || code === 'run_launching' || (method === 'GET' && GATEWAY.has(res.status));
|
|
117
|
+
if (retryable && canRetry) {
|
|
118
|
+
await sleep(retryAfter !== null ? retryAfter * 1000 : backoffMs(attempt));
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
throw new DaishiError(res.status, code, parsed?.message ?? (text.slice(0, 200) || res.statusText), retryAfter, parsed);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Polls a run until it ends (finished, failed or cancelled) and answers
|
|
126
|
+
* with the last read. A finished run is read until its `results` arrive,
|
|
127
|
+
* which is when the match archives.
|
|
128
|
+
*/
|
|
129
|
+
waitForRun(id, options = {}) {
|
|
130
|
+
return this.poll(() => this.getRun(id), (r) => RUN_DONE.has(r.run.status) && (r.run.status !== 'finished' || r.results !== null), options);
|
|
131
|
+
}
|
|
132
|
+
/** Polls a series batch until no trial is still to play. */
|
|
133
|
+
waitForBatch(id, options = {}) {
|
|
134
|
+
return this.poll(() => this.getBatch(id), (r) => r.batch.pending === 0, options);
|
|
135
|
+
}
|
|
136
|
+
/** Polls a paired experiment until it finishes or is cancelled. */
|
|
137
|
+
waitForExperiment(id, options = {}) {
|
|
138
|
+
return this.poll(() => this.getExperiment(id), (r) => EXPERIMENT_DONE.has(r.experiment.status), options);
|
|
139
|
+
}
|
|
140
|
+
async poll(read, done, options) {
|
|
141
|
+
const interval = options.intervalMs ?? 30_000;
|
|
142
|
+
for (;;) {
|
|
143
|
+
options.signal?.throwIfAborted();
|
|
144
|
+
const answer = await read();
|
|
145
|
+
options.onPoll?.(answer);
|
|
146
|
+
if (done(answer))
|
|
147
|
+
return answer;
|
|
148
|
+
await sleep(interval, options.signal);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function parseRetryAfter(header) {
|
|
153
|
+
if (!header)
|
|
154
|
+
return null;
|
|
155
|
+
const seconds = Number(header);
|
|
156
|
+
if (Number.isFinite(seconds) && seconds >= 0)
|
|
157
|
+
return seconds;
|
|
158
|
+
const at = Date.parse(header);
|
|
159
|
+
return Number.isNaN(at) ? null : Math.max(0, Math.ceil((at - Date.now()) / 1000));
|
|
160
|
+
}
|
|
161
|
+
/** 1 s, 2 s, 4 s ... with up to a quarter of jitter, capped at 30 s. */
|
|
162
|
+
function backoffMs(attempt) {
|
|
163
|
+
const base = Math.min(30_000, 1000 * 2 ** attempt);
|
|
164
|
+
return base + Math.floor(Math.random() * base * 0.25);
|
|
165
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "daishi-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Typed client for the Daishi Studio API: scenarios, runs, series batches and paired experiments, with retries and waiting helpers.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"daishi",
|
|
7
|
+
"ai",
|
|
8
|
+
"agents",
|
|
9
|
+
"evaluation",
|
|
10
|
+
"benchmark",
|
|
11
|
+
"llm",
|
|
12
|
+
"multi-agent"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://daishi.ai/docs/developer",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "Arkeous LLC",
|
|
17
|
+
"type": "module",
|
|
18
|
+
"main": "./dist/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"default": "./dist/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"sideEffects": false,
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18"
|
|
32
|
+
}
|
|
33
|
+
}
|