@itsoltech/basal-client 0.2.0 → 0.4.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/README.md +72 -4
- package/dist/cjs/batches.cjs +191 -0
- package/dist/cjs/batches.d.cts +166 -0
- package/dist/cjs/errors.cjs +31 -0
- package/dist/cjs/errors.d.cts +15 -0
- package/dist/cjs/index.cjs +119 -55
- package/dist/cjs/index.d.cts +32 -18
- package/dist/cjs/questions.cjs +7 -2
- package/dist/cjs/questions.d.cts +2 -0
- package/dist/cjs/tasks.cjs +175 -0
- package/dist/cjs/tasks.d.cts +108 -0
- package/dist/esm/batches.d.ts +166 -0
- package/dist/esm/batches.js +182 -0
- package/dist/esm/errors.d.ts +15 -0
- package/dist/esm/errors.js +23 -0
- package/dist/esm/index.d.ts +32 -18
- package/dist/esm/index.js +107 -40
- package/dist/esm/questions.d.ts +2 -0
- package/dist/esm/questions.js +7 -2
- package/dist/esm/tasks.d.ts +108 -0
- package/dist/esm/tasks.js +164 -0
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -14,48 +14,26 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.BasalClient = exports.
|
|
17
|
+
exports.BasalClient = exports.parseBatchResults = exports.batchInput = void 0;
|
|
18
18
|
const questions_cjs_1 = require("./questions.cjs");
|
|
19
|
+
const errors_cjs_1 = require("./errors.cjs");
|
|
20
|
+
const batches_cjs_1 = require("./batches.cjs");
|
|
19
21
|
__exportStar(require("./questions.cjs"), exports);
|
|
20
22
|
__exportStar(require("./media.cjs"), exports);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
statusCode;
|
|
27
|
-
body;
|
|
28
|
-
retryAfter;
|
|
29
|
-
constructor(statusCode, body, retryAfter) {
|
|
30
|
-
super(`Basal HTTP ${statusCode}`);
|
|
31
|
-
this.statusCode = statusCode;
|
|
32
|
-
this.body = body;
|
|
33
|
-
this.retryAfter = retryAfter;
|
|
34
|
-
this.name = "BasalHTTPError";
|
|
35
|
-
// Node's default inspection and JSON.stringify must not print reflected state or credentials.
|
|
36
|
-
Object.defineProperty(this, "body", { enumerable: false });
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
exports.BasalHTTPError = BasalHTTPError;
|
|
40
|
-
class BasalTransportError extends BasalError {
|
|
41
|
-
}
|
|
42
|
-
exports.BasalTransportError = BasalTransportError;
|
|
43
|
-
class BasalProtocolError extends BasalError {
|
|
44
|
-
}
|
|
45
|
-
exports.BasalProtocolError = BasalProtocolError;
|
|
46
|
-
class BasalResponseTooLargeError extends BasalError {
|
|
47
|
-
}
|
|
48
|
-
exports.BasalResponseTooLargeError = BasalResponseTooLargeError;
|
|
23
|
+
__exportStar(require("./tasks.cjs"), exports);
|
|
24
|
+
__exportStar(require("./errors.cjs"), exports);
|
|
25
|
+
var batches_cjs_2 = require("./batches.cjs");
|
|
26
|
+
Object.defineProperty(exports, "batchInput", { enumerable: true, get: function () { return batches_cjs_2.batchInput; } });
|
|
27
|
+
Object.defineProperty(exports, "parseBatchResults", { enumerable: true, get: function () { return batches_cjs_2.parseBatchResults; } });
|
|
49
28
|
async function readBody(response, limit, signal) {
|
|
50
29
|
// Count bytes from the actual stream, including decompressed responses; never trust Content-Length alone.
|
|
51
30
|
const reader = response.body?.getReader();
|
|
52
31
|
if (!reader)
|
|
53
|
-
return
|
|
32
|
+
return new Uint8Array();
|
|
54
33
|
const cancel = () => { void reader.cancel().catch(() => { }); };
|
|
55
34
|
signal.addEventListener("abort", cancel, { once: true });
|
|
56
35
|
if (signal.aborted)
|
|
57
36
|
cancel();
|
|
58
|
-
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
59
37
|
const parts = [];
|
|
60
38
|
let bytes = 0;
|
|
61
39
|
let complete = false;
|
|
@@ -68,20 +46,38 @@ async function readBody(response, limit, signal) {
|
|
|
68
46
|
}
|
|
69
47
|
bytes += value.byteLength;
|
|
70
48
|
if (bytes > limit) {
|
|
71
|
-
throw new BasalResponseTooLargeError(`Basal response exceeds ${limit} bytes`);
|
|
49
|
+
throw new errors_cjs_1.BasalResponseTooLargeError(`Basal response exceeds ${limit} bytes`);
|
|
72
50
|
}
|
|
73
|
-
parts.push(
|
|
51
|
+
parts.push(value);
|
|
74
52
|
}
|
|
75
|
-
parts.push(decoder.decode());
|
|
76
|
-
return parts.join("");
|
|
77
53
|
}
|
|
78
54
|
finally {
|
|
79
55
|
signal.removeEventListener("abort", cancel);
|
|
80
|
-
// A
|
|
56
|
+
// A read failure must also stop the underlying HTTP body, before releasing ownership.
|
|
81
57
|
if (!complete)
|
|
82
58
|
cancel();
|
|
83
59
|
reader.releaseLock();
|
|
84
60
|
}
|
|
61
|
+
if (parts.length === 1)
|
|
62
|
+
return parts[0];
|
|
63
|
+
const out = new Uint8Array(bytes);
|
|
64
|
+
let offset = 0;
|
|
65
|
+
for (const part of parts) {
|
|
66
|
+
out.set(part, offset);
|
|
67
|
+
offset += part.byteLength;
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
function positiveTimeout(value, name) {
|
|
72
|
+
if (!Number.isFinite(value) || value <= 0 || value > 2_147_483_647) {
|
|
73
|
+
throw new TypeError(`${name} must be positive and at most 2147483647`);
|
|
74
|
+
}
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
function byteLimit(value, name) {
|
|
78
|
+
if (!Number.isSafeInteger(value) || value <= 0)
|
|
79
|
+
throw new TypeError(`${name} must be a positive safe integer`);
|
|
80
|
+
return value;
|
|
85
81
|
}
|
|
86
82
|
/** Reusable fetch client. Server root URL, optional proxy prefix, without /v1.
|
|
87
83
|
* No automatic retries. AbortSignal cancels a request; timeout includes body reading.
|
|
@@ -117,55 +113,79 @@ class BasalClient {
|
|
|
117
113
|
this.headers.Authorization = `Bearer ${options.apiKey}`;
|
|
118
114
|
}
|
|
119
115
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
116
|
+
request(method, path, body, signal) {
|
|
117
|
+
return this.call({ method, path, json: body, signal });
|
|
118
|
+
}
|
|
119
|
+
async call(call) {
|
|
120
|
+
// Serialization and option errors are local input errors, not failures of the HTTP transport.
|
|
121
|
+
const encoded = call.json === undefined ? undefined : JSON.stringify(call.json);
|
|
122
|
+
const timeoutMs = call.timeoutMs === undefined ? this.timeoutMs : positiveTimeout(call.timeoutMs, "timeoutMs");
|
|
123
|
+
const limit = call.maxBytes === undefined ? this.maxResponseBytes : byteLimit(call.maxBytes, "maxBytes");
|
|
124
|
+
const headers = { ...this.headers, ...call.headers };
|
|
125
|
+
if (encoded !== undefined)
|
|
126
|
+
headers["Content-Type"] = "application/json";
|
|
127
|
+
const { signal } = call;
|
|
123
128
|
const controller = new AbortController();
|
|
124
129
|
const abort = () => controller.abort(signal?.reason);
|
|
125
130
|
signal?.addEventListener("abort", abort, { once: true });
|
|
126
131
|
if (signal?.aborted)
|
|
127
132
|
abort();
|
|
128
|
-
const timer = setTimeout(() => controller.abort(new Error("Basal request timed out")),
|
|
133
|
+
const timer = setTimeout(() => controller.abort(new Error("Basal request timed out")), timeoutMs);
|
|
129
134
|
let onAbort = () => { };
|
|
130
135
|
const aborted = new Promise((_, reject) => {
|
|
131
|
-
onAbort = () => reject(new BasalTransportError("Basal HTTP request aborted", { cause: controller.signal.reason }));
|
|
136
|
+
onAbort = () => reject(new errors_cjs_1.BasalTransportError("Basal HTTP request aborted", { cause: controller.signal.reason }));
|
|
132
137
|
controller.signal.addEventListener("abort", onAbort, { once: true });
|
|
133
138
|
if (controller.signal.aborted)
|
|
134
139
|
onAbort();
|
|
135
140
|
});
|
|
136
141
|
const work = async () => {
|
|
137
142
|
let response;
|
|
138
|
-
let
|
|
143
|
+
let content;
|
|
144
|
+
let raw = "";
|
|
139
145
|
try {
|
|
140
|
-
|
|
141
|
-
method, headers:
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
146
|
+
const init = {
|
|
147
|
+
method: call.method, headers, signal: controller.signal, redirect: "error", credentials: "omit", cache: "no-store",
|
|
148
|
+
};
|
|
149
|
+
if (encoded !== undefined)
|
|
150
|
+
init.body = encoded;
|
|
151
|
+
else if (call.body instanceof Blob)
|
|
152
|
+
init.body = call.body;
|
|
153
|
+
else if (call.body !== undefined) {
|
|
154
|
+
// Streamed request bodies require half-duplex fetch (Node.js, Chromium over HTTP/2).
|
|
155
|
+
init.body = call.body(controller.signal);
|
|
156
|
+
init.duplex = "half";
|
|
157
|
+
}
|
|
158
|
+
response = await this.fetch(this.baseUrl + call.path, init);
|
|
145
159
|
if (response.redirected || ([301, 302, 303, 307, 308].includes(response.status) && response.headers.has("Location"))) {
|
|
146
160
|
await response.body?.cancel().catch(() => { });
|
|
147
|
-
throw new BasalTransportError("Basal transport returned a redirect");
|
|
161
|
+
throw new errors_cjs_1.BasalTransportError("Basal transport returned a redirect");
|
|
148
162
|
}
|
|
149
|
-
|
|
163
|
+
// A per-download cap limits successful artifact bytes. Error envelopes retain the client's normal cap
|
|
164
|
+
// so a small artifact cap does not hide an actionable 404/429 and its Retry-After.
|
|
165
|
+
content = await readBody(response, response.ok ? limit : this.maxResponseBytes, controller.signal);
|
|
166
|
+
if (!call.bytes || !response.ok)
|
|
167
|
+
raw = new TextDecoder("utf-8", { fatal: true }).decode(content);
|
|
150
168
|
}
|
|
151
169
|
catch (cause) {
|
|
152
|
-
if (cause instanceof BasalError)
|
|
170
|
+
if (cause instanceof errors_cjs_1.BasalError)
|
|
153
171
|
throw cause;
|
|
154
|
-
throw new BasalTransportError("Basal HTTP transport failed", { cause });
|
|
172
|
+
throw new errors_cjs_1.BasalTransportError("Basal HTTP transport failed", { cause });
|
|
155
173
|
}
|
|
174
|
+
if (call.bytes && response.ok)
|
|
175
|
+
return content;
|
|
156
176
|
let data;
|
|
157
177
|
try {
|
|
158
178
|
data = JSON.parse(raw);
|
|
159
179
|
}
|
|
160
180
|
catch (cause) {
|
|
161
181
|
if (response.ok)
|
|
162
|
-
throw new BasalProtocolError("Basal returned invalid JSON", { cause });
|
|
182
|
+
throw new errors_cjs_1.BasalProtocolError("Basal returned invalid JSON", { cause });
|
|
163
183
|
data = raw;
|
|
164
184
|
}
|
|
165
185
|
if (!response.ok)
|
|
166
|
-
throw new BasalHTTPError(response.status, data, response.headers.get("Retry-After"));
|
|
186
|
+
throw new errors_cjs_1.BasalHTTPError(response.status, data, response.headers.get("Retry-After"));
|
|
167
187
|
if (data === null || typeof data !== "object" || Array.isArray(data)) {
|
|
168
|
-
throw new BasalProtocolError("Basal returned a JSON value other than an object");
|
|
188
|
+
throw new errors_cjs_1.BasalProtocolError("Basal returned a JSON value other than an object");
|
|
169
189
|
}
|
|
170
190
|
return data;
|
|
171
191
|
};
|
|
@@ -178,6 +198,9 @@ class BasalClient {
|
|
|
178
198
|
controller.signal.removeEventListener("abort", onAbort);
|
|
179
199
|
clearTimeout(timer);
|
|
180
200
|
signal?.removeEventListener("abort", abort);
|
|
201
|
+
// An answer can arrive before a streamed upload is fully sent (e.g. 413); stop its source.
|
|
202
|
+
if (typeof call.body === "function")
|
|
203
|
+
controller.abort(new Error("Basal request finished"));
|
|
181
204
|
}
|
|
182
205
|
}
|
|
183
206
|
async decide(state, questionsOrSignal, options = {}) {
|
|
@@ -197,7 +220,7 @@ class BasalClient {
|
|
|
197
220
|
parsed.set(id, question.read(response, id));
|
|
198
221
|
}
|
|
199
222
|
catch (cause) {
|
|
200
|
-
throw new BasalProtocolError("Invalid typed Basal answer", { cause });
|
|
223
|
+
throw new errors_cjs_1.BasalProtocolError("Invalid typed Basal answer", { cause });
|
|
201
224
|
}
|
|
202
225
|
const typed = { ...response, answers: Object.fromEntries(parsed) };
|
|
203
226
|
Object.defineProperty(typed, "raw", { value: response });
|
|
@@ -212,5 +235,46 @@ class BasalClient {
|
|
|
212
235
|
return typed;
|
|
213
236
|
}
|
|
214
237
|
models(signal) { return this.request("GET", "v1/models", undefined, signal); }
|
|
238
|
+
/** Upload a JSONL input file (`purpose=batch`) from caller-owned bytes, a Blob or a stream; never fetches URLs. */
|
|
239
|
+
async uploadFile(request, options = {}) {
|
|
240
|
+
const form = (0, batches_cjs_1.uploadForm)(request);
|
|
241
|
+
return await this.call({
|
|
242
|
+
method: "POST", path: "v1/files", body: form.body, headers: { "Content-Type": form.contentType },
|
|
243
|
+
signal: options.signal, timeoutMs: options.timeoutMs,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
async listFiles(options = {}) {
|
|
247
|
+
return await this.call({ method: "GET", path: (0, batches_cjs_1.listPath)("v1/files", options), signal: options.signal });
|
|
248
|
+
}
|
|
249
|
+
async getFile(fileId, options = {}) {
|
|
250
|
+
return await this.call({ method: "GET", path: (0, batches_cjs_1.resourcePath)("v1/files", fileId), signal: options.signal });
|
|
251
|
+
}
|
|
252
|
+
async deleteFile(fileId, options = {}) {
|
|
253
|
+
return await this.call({ method: "DELETE", path: (0, batches_cjs_1.resourcePath)("v1/files", fileId), signal: options.signal });
|
|
254
|
+
}
|
|
255
|
+
/** Raw file bytes (JSONL), buffered up to `maxBytes` (default `maxResponseBytes`); never JSON-parsed. */
|
|
256
|
+
async downloadFile(fileId, options = {}) {
|
|
257
|
+
return await this.call({
|
|
258
|
+
method: "GET", path: (0, batches_cjs_1.resourcePath)("v1/files", fileId, "/content"), bytes: true,
|
|
259
|
+
headers: { Accept: "application/jsonl, application/json" },
|
|
260
|
+
signal: options.signal, timeoutMs: options.timeoutMs, maxBytes: options.maxBytes,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
async createBatch(request, options = {}) {
|
|
264
|
+
const headers = (0, batches_cjs_1.idempotencyHeader)(options.idempotencyKey);
|
|
265
|
+
return await this.call({ method: "POST", path: "v1/batches", json: (0, batches_cjs_1.createBody)(request), headers, signal: options.signal });
|
|
266
|
+
}
|
|
267
|
+
async listBatches(options = {}) {
|
|
268
|
+
const { after, limit } = options;
|
|
269
|
+
const query = { ...(after === undefined ? {} : { after }), ...(limit === undefined ? {} : { limit }) };
|
|
270
|
+
return await this.call({ method: "GET", path: (0, batches_cjs_1.listPath)("v1/batches", query), signal: options.signal });
|
|
271
|
+
}
|
|
272
|
+
async getBatch(batchId, options = {}) {
|
|
273
|
+
return await this.call({ method: "GET", path: (0, batches_cjs_1.resourcePath)("v1/batches", batchId), signal: options.signal });
|
|
274
|
+
}
|
|
275
|
+
/** Request cancellation; the batch moves to `cancelling` and later `cancelled`. Does not wait. */
|
|
276
|
+
async cancelBatch(batchId, options = {}) {
|
|
277
|
+
return await this.call({ method: "POST", path: (0, batches_cjs_1.resourcePath)("v1/batches", batchId, "/cancel"), signal: options.signal });
|
|
278
|
+
}
|
|
215
279
|
}
|
|
216
280
|
exports.BasalClient = BasalClient;
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { type QuestionSet, type TypedDecisionResponse, type DecisionOptions } from "./questions.cjs";
|
|
2
|
+
import { type Batch, type BatchList, type CreateBatchOptions, type CreateBatchRequest, type DeletedFile, type DownloadOptions, type FileList, type FileObject, type ListFilesOptions, type ListOptions, type RequestOptions, type TransferOptions, type UploadFileRequest } from "./batches.cjs";
|
|
2
3
|
export * from "./questions.cjs";
|
|
3
4
|
export * from "./media.cjs";
|
|
5
|
+
export * from "./tasks.cjs";
|
|
6
|
+
export * from "./errors.cjs";
|
|
7
|
+
export { batchInput, parseBatchResults } from "./batches.cjs";
|
|
8
|
+
export type { Batch, BatchErrorCode, BatchErrorEntry, BatchErrors, BatchInputLine, BatchList, BatchResultLine, BatchStatus, CreateBatchOptions, CreateBatchRequest, DeletedFile, DownloadOptions, ExpiresAfter, FileList, FileObject, FilePurpose, ListFilesOptions, ListOptions, ListPage, Metadata, RequestCounts, RequestOptions, TransferOptions, UploadContent, UploadFileRequest, } from "./batches.cjs";
|
|
9
|
+
import type { TaskAnswer } from "./tasks.cjs";
|
|
4
10
|
/** Native Basal wire types. Criterion order is significant; do not sort keys. */
|
|
5
11
|
export type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
6
12
|
[key: string]: JsonValue;
|
|
@@ -23,7 +29,7 @@ export interface ToolRanking {
|
|
|
23
29
|
}
|
|
24
30
|
export type ToolDefinition = Record<string, JsonValue>;
|
|
25
31
|
export interface Question {
|
|
26
|
-
type?: "choice" | "noul" | "score" | "multi" | "act" | "tool";
|
|
32
|
+
type?: "choice" | "noul" | "score" | "multi" | "act" | "tool" | "classify" | "verify" | "compress" | "extract";
|
|
27
33
|
instructions?: JsonValue;
|
|
28
34
|
criteria?: Record<string, JsonValue> | JsonValue[];
|
|
29
35
|
levels?: Record<string, JsonValue> | JsonValue[];
|
|
@@ -35,7 +41,7 @@ export interface Question {
|
|
|
35
41
|
base?: "noul" | "choice" | "score";
|
|
36
42
|
costs?: Record<string, number> | Record<string, Record<string, number>>;
|
|
37
43
|
defer_action?: string;
|
|
38
|
-
max_error?: number;
|
|
44
|
+
max_error?: number | readonly number[];
|
|
39
45
|
objective?: MultiObjective;
|
|
40
46
|
exclusive?: readonly string[];
|
|
41
47
|
fallback?: string;
|
|
@@ -43,7 +49,17 @@ export interface Question {
|
|
|
43
49
|
tools?: readonly ToolDefinition[];
|
|
44
50
|
extra?: readonly ("answer_directly" | "ask_user" | "none")[];
|
|
45
51
|
rank?: number;
|
|
52
|
+
description_chars?: number;
|
|
46
53
|
multi?: boolean;
|
|
54
|
+
labels?: Record<string, JsonValue> | JsonValue[];
|
|
55
|
+
multi_label?: boolean;
|
|
56
|
+
none?: boolean;
|
|
57
|
+
claims?: Record<string, JsonValue> | JsonValue[];
|
|
58
|
+
binary?: boolean;
|
|
59
|
+
unit?: 'sentence' | 'line' | 'paragraph';
|
|
60
|
+
keep_ratio?: number;
|
|
61
|
+
min_keep?: number;
|
|
62
|
+
schema?: Record<string, JsonValue>;
|
|
47
63
|
}
|
|
48
64
|
export interface DecisionRequest {
|
|
49
65
|
state: JsonValue;
|
|
@@ -122,7 +138,7 @@ export interface ActAnswer<K extends string = string, A extends string = string>
|
|
|
122
138
|
max_error?: string;
|
|
123
139
|
deferred?: boolean;
|
|
124
140
|
}
|
|
125
|
-
export type Answer = ChoiceAnswer | NoulAnswer | ScoreAnswer | MultiAnswer | ActAnswer | ToolAnswer;
|
|
141
|
+
export type Answer = ChoiceAnswer | NoulAnswer | ScoreAnswer | MultiAnswer | ActAnswer | ToolAnswer | TaskAnswer;
|
|
126
142
|
export interface Usage {
|
|
127
143
|
input_tokens: number;
|
|
128
144
|
output_tokens: number;
|
|
@@ -152,21 +168,6 @@ export interface ModelMetadata {
|
|
|
152
168
|
export interface ModelsResponse {
|
|
153
169
|
models: ModelMetadata[];
|
|
154
170
|
}
|
|
155
|
-
export declare class BasalError extends Error {
|
|
156
|
-
}
|
|
157
|
-
/** Non-2xx response. Server body and Retry-After are available without logging them. */
|
|
158
|
-
export declare class BasalHTTPError extends BasalError {
|
|
159
|
-
readonly statusCode: number;
|
|
160
|
-
readonly body: unknown;
|
|
161
|
-
readonly retryAfter: string | null;
|
|
162
|
-
constructor(statusCode: number, body: unknown, retryAfter: string | null);
|
|
163
|
-
}
|
|
164
|
-
export declare class BasalTransportError extends BasalError {
|
|
165
|
-
}
|
|
166
|
-
export declare class BasalProtocolError extends BasalError {
|
|
167
|
-
}
|
|
168
|
-
export declare class BasalResponseTooLargeError extends BasalError {
|
|
169
|
-
}
|
|
170
171
|
/** Web Fetch input accepted by browsers and server runtimes. */
|
|
171
172
|
export type RequestInput = string | URL | Request;
|
|
172
173
|
/** A transport adapter must honor signal/redirect and return a standard Web Response. */
|
|
@@ -190,7 +191,20 @@ export declare class BasalClient {
|
|
|
190
191
|
private readonly fetch;
|
|
191
192
|
constructor(options?: BasalClientOptions);
|
|
192
193
|
private request;
|
|
194
|
+
private call;
|
|
193
195
|
decide(request: DecisionRequest, signal?: AbortSignal): Promise<DecisionResponse>;
|
|
194
196
|
decide<const Q extends QuestionSet>(state: JsonValue, questions: Q, options?: DecisionOptions): Promise<TypedDecisionResponse<Q>>;
|
|
195
197
|
models(signal?: AbortSignal): Promise<ModelsResponse>;
|
|
198
|
+
/** Upload a JSONL input file (`purpose=batch`) from caller-owned bytes, a Blob or a stream; never fetches URLs. */
|
|
199
|
+
uploadFile(request: UploadFileRequest, options?: TransferOptions): Promise<FileObject>;
|
|
200
|
+
listFiles(options?: ListFilesOptions): Promise<FileList>;
|
|
201
|
+
getFile(fileId: string, options?: RequestOptions): Promise<FileObject>;
|
|
202
|
+
deleteFile(fileId: string, options?: RequestOptions): Promise<DeletedFile>;
|
|
203
|
+
/** Raw file bytes (JSONL), buffered up to `maxBytes` (default `maxResponseBytes`); never JSON-parsed. */
|
|
204
|
+
downloadFile(fileId: string, options?: DownloadOptions): Promise<Uint8Array>;
|
|
205
|
+
createBatch(request: CreateBatchRequest, options?: CreateBatchOptions): Promise<Batch>;
|
|
206
|
+
listBatches(options?: ListOptions): Promise<BatchList>;
|
|
207
|
+
getBatch(batchId: string, options?: RequestOptions): Promise<Batch>;
|
|
208
|
+
/** Request cancellation; the batch moves to `cancelling` and later `cancelled`. Does not wait. */
|
|
209
|
+
cancelBatch(batchId: string, options?: RequestOptions): Promise<Batch>;
|
|
196
210
|
}
|
package/dist/cjs/questions.cjs
CHANGED
|
@@ -40,13 +40,13 @@ function evidence(options) {
|
|
|
40
40
|
class QuestionSpec {
|
|
41
41
|
wire;
|
|
42
42
|
constructor(kind, instructions, fields) {
|
|
43
|
-
this.wire = structuredClone({ ...fields, type: kind, instructions: text(instructions, 'instructions') });
|
|
43
|
+
this.wire = structuredClone({ ...fields, type: kind, ...(['classify', 'verify', 'compress', 'extract'].includes(kind) ? (instructions ? { instructions } : {}) : { instructions: text(instructions, 'instructions') }) });
|
|
44
44
|
}
|
|
45
45
|
toJSON() { return structuredClone(this.wire); }
|
|
46
46
|
/** Validate and retrieve an answer with this question's result type. */
|
|
47
47
|
read(response, id) {
|
|
48
48
|
const answer = response.answers?.[id];
|
|
49
|
-
if (!answer || answer.type !== this.wire.type || !answer.probabilities || typeof answer.probabilities !== 'object' || Array.isArray(answer.probabilities)) {
|
|
49
|
+
if (!answer || !('probabilities' in answer) || answer.type !== this.wire.type || !answer.probabilities || typeof answer.probabilities !== 'object' || Array.isArray(answer.probabilities)) {
|
|
50
50
|
throw new TypeError('Basal returned a missing or mismatched answer');
|
|
51
51
|
}
|
|
52
52
|
const fields = {
|
|
@@ -216,6 +216,11 @@ class ToolQuestion extends QuestionSpec {
|
|
|
216
216
|
if (options.multi !== undefined && typeof options.multi !== 'boolean')
|
|
217
217
|
throw new TypeError('invalid multi');
|
|
218
218
|
const fields = { tools, ...(options.extra === undefined ? {} : { extra: options.extra }) };
|
|
219
|
+
if (options.description_chars !== undefined) {
|
|
220
|
+
if (!Number.isSafeInteger(options.description_chars) || options.description_chars < 0)
|
|
221
|
+
throw new TypeError('description_chars must be a non-negative safe integer');
|
|
222
|
+
fields.description_chars = options.description_chars;
|
|
223
|
+
}
|
|
219
224
|
if (options.rank !== undefined) {
|
|
220
225
|
if (!Number.isSafeInteger(options.rank) || options.rank < 1)
|
|
221
226
|
throw new TypeError('rank must be a positive integer');
|
package/dist/cjs/questions.d.cts
CHANGED
|
@@ -33,6 +33,8 @@ export interface ToolOptions extends MultiOptions {
|
|
|
33
33
|
rank?: number;
|
|
34
34
|
multi?: boolean;
|
|
35
35
|
max_error?: number;
|
|
36
|
+
/** Tool-description truncation cap; omitted keeps the server default (300), zero disables truncation. */
|
|
37
|
+
description_chars?: number;
|
|
36
38
|
}
|
|
37
39
|
export interface ActOptions<K extends string = string, A extends string = string> extends EvidenceOptions {
|
|
38
40
|
criteria?: Record<K, string>;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExtractQuestion = exports.CompressQuestion = exports.VerifyQuestion = exports.ClassifyQuestion = void 0;
|
|
4
|
+
exports.classify = classify;
|
|
5
|
+
exports.verify = verify;
|
|
6
|
+
exports.compress = compress;
|
|
7
|
+
exports.extract = extract;
|
|
8
|
+
const questions_cjs_1 = require("./questions.cjs");
|
|
9
|
+
function object(v) {
|
|
10
|
+
if (!v || typeof v !== 'object' || Array.isArray(v))
|
|
11
|
+
throw new TypeError('expected task object');
|
|
12
|
+
return v;
|
|
13
|
+
}
|
|
14
|
+
function probability(v) { if (typeof v !== 'number' || !Number.isFinite(v) || v < 0 || v > 1)
|
|
15
|
+
throw new TypeError('invalid probability'); }
|
|
16
|
+
function probabilities(v) { for (const n of Object.values(object(v)))
|
|
17
|
+
probability(n); }
|
|
18
|
+
function automation(v) { if (Object.values(object(v)).some(n => n !== null && typeof n !== 'boolean'))
|
|
19
|
+
throw new TypeError('invalid automation'); }
|
|
20
|
+
function strings(v) { if (!Array.isArray(v) || v.some(k => typeof k !== 'string'))
|
|
21
|
+
throw new TypeError('expected string list'); }
|
|
22
|
+
function offset(v) { if (!Number.isSafeInteger(v) || v < 0)
|
|
23
|
+
throw new TypeError('invalid Unicode code point offset/count'); }
|
|
24
|
+
function span(v) { offset(v.start); offset(v.end); if (v.end < v.start)
|
|
25
|
+
throw new TypeError('reversed source span'); }
|
|
26
|
+
function risks(v) {
|
|
27
|
+
if (v === undefined)
|
|
28
|
+
return;
|
|
29
|
+
const a = Array.isArray(v) ? v : [v];
|
|
30
|
+
if (a.length > 64 || a.some(n => typeof n !== 'number' || !Number.isFinite(n) || n <= 0 || n >= 1))
|
|
31
|
+
throw new TypeError('invalid max_error');
|
|
32
|
+
}
|
|
33
|
+
function instructions(v) { if (typeof v !== 'string')
|
|
34
|
+
throw new TypeError('instructions must be a string'); return v; }
|
|
35
|
+
class TaskQuestion extends questions_cjs_1.QuestionSpec {
|
|
36
|
+
constructor(kind, text, fields) { super(kind, instructions(text), fields); }
|
|
37
|
+
read(response, id) {
|
|
38
|
+
const d = object(response.answers?.[id]);
|
|
39
|
+
if (d.type !== this.toJSON().type || d.experimental !== true)
|
|
40
|
+
throw new TypeError('Basal returned a missing or mismatched task');
|
|
41
|
+
if (d.type === 'classify') {
|
|
42
|
+
probabilities(d.scores);
|
|
43
|
+
const scores = object(d.scores);
|
|
44
|
+
if (this.toJSON().multi_label) {
|
|
45
|
+
strings(d.labels);
|
|
46
|
+
if (d.labels.some(k => !Object.hasOwn(scores, k)))
|
|
47
|
+
throw new TypeError('unknown label');
|
|
48
|
+
probability(d.set_confidence);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
if (typeof d.label !== 'string' || !Object.hasOwn(scores, d.label) || d.top_label !== d.label)
|
|
52
|
+
throw new TypeError('invalid label');
|
|
53
|
+
probability(d.confidence);
|
|
54
|
+
automation(d.automated);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else if (d.type === 'verify') {
|
|
58
|
+
const claims = object(d.claims);
|
|
59
|
+
for (const raw of Object.values(claims)) {
|
|
60
|
+
const c = object(raw);
|
|
61
|
+
const p = object(c.probabilities);
|
|
62
|
+
probabilities(p);
|
|
63
|
+
if (typeof c.verdict !== 'string' || !['supported', 'contradicted', 'not_stated', 'not_supported'].includes(c.verdict) || !Object.hasOwn(p, c.verdict))
|
|
64
|
+
throw new TypeError('invalid verdict');
|
|
65
|
+
probability(c.confidence);
|
|
66
|
+
automation(c.automated);
|
|
67
|
+
}
|
|
68
|
+
strings(d.not_supported);
|
|
69
|
+
if (typeof d.all_supported !== 'boolean' || d.not_supported.some(k => !Object.hasOwn(claims, k)))
|
|
70
|
+
throw new TypeError('invalid verification summary');
|
|
71
|
+
}
|
|
72
|
+
else if (d.type === 'compress') {
|
|
73
|
+
if (typeof d.compressed !== 'string' || !['sentence', 'line', 'paragraph'].includes(d.unit) || !Array.isArray(d.units) || !Array.isArray(d.kept))
|
|
74
|
+
throw new TypeError('invalid compression');
|
|
75
|
+
for (const u of d.units) {
|
|
76
|
+
const v = object(u);
|
|
77
|
+
span(v);
|
|
78
|
+
probability(v.probability);
|
|
79
|
+
}
|
|
80
|
+
for (const k of d.kept) {
|
|
81
|
+
offset(k);
|
|
82
|
+
if (k >= d.units.length)
|
|
83
|
+
throw new TypeError('invalid kept unit');
|
|
84
|
+
}
|
|
85
|
+
offset(d.original_chars);
|
|
86
|
+
offset(d.compressed_chars);
|
|
87
|
+
if (typeof d.ratio !== 'number' || !Number.isFinite(d.ratio) || d.ratio < 0)
|
|
88
|
+
throw new TypeError('invalid ratio');
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
object(d.data);
|
|
92
|
+
for (const raw of Object.values(object(d.fields))) {
|
|
93
|
+
const f = object(raw);
|
|
94
|
+
if (!Object.hasOwn(f, 'value') || !['choice', 'span'].includes(f.source) || !['validated', 'per decision'].includes(f.calibration))
|
|
95
|
+
throw new TypeError('invalid extraction field');
|
|
96
|
+
probability(f.confidence);
|
|
97
|
+
if (f.probabilities !== undefined)
|
|
98
|
+
probabilities(f.probabilities);
|
|
99
|
+
for (const k of ['present_probability', 'line_probability', 'span_probability'])
|
|
100
|
+
if (f[k] !== undefined)
|
|
101
|
+
probability(f[k]);
|
|
102
|
+
if (f.text !== undefined) {
|
|
103
|
+
if (typeof f.text !== 'string')
|
|
104
|
+
throw new TypeError('invalid source text');
|
|
105
|
+
span(f);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return structuredClone(d);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
class ClassifyQuestion extends TaskQuestion {
|
|
113
|
+
constructor(text, labels, options = {}) {
|
|
114
|
+
if (!labels || typeof labels !== 'object' || Object.keys(labels).length < 1 || Object.keys(labels).length > 256)
|
|
115
|
+
throw new TypeError('classify requires 1..256 labels');
|
|
116
|
+
risks(options.max_error);
|
|
117
|
+
for (const k of ['multi_label', 'none'])
|
|
118
|
+
if (options[k] !== undefined && typeof options[k] !== 'boolean')
|
|
119
|
+
throw new TypeError(`invalid ${k}`);
|
|
120
|
+
if (options.option_keys !== undefined && !['show', 'hide'].includes(options.option_keys))
|
|
121
|
+
throw new TypeError('invalid option_keys');
|
|
122
|
+
if (options.threshold !== undefined) {
|
|
123
|
+
probability(options.threshold);
|
|
124
|
+
if (options.objective !== undefined && options.objective !== 'threshold')
|
|
125
|
+
throw new TypeError('threshold requires threshold objective');
|
|
126
|
+
}
|
|
127
|
+
for (const k of ['min', 'max'])
|
|
128
|
+
if (options[k] !== undefined)
|
|
129
|
+
offset(options[k]);
|
|
130
|
+
if (options.min !== undefined && options.max !== undefined && options.min > options.max)
|
|
131
|
+
throw new TypeError('min exceeds max');
|
|
132
|
+
if (options.objective !== undefined && !['threshold', 'exact', 'iou', 'f1'].includes(options.objective))
|
|
133
|
+
throw new TypeError('invalid objective');
|
|
134
|
+
if (!options.multi_label && ['threshold', 'min', 'max', 'objective'].some(k => Object.hasOwn(options, k)))
|
|
135
|
+
throw new TypeError('selection options require multi_label');
|
|
136
|
+
super('classify', text, { labels: structuredClone(labels), ...options });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
exports.ClassifyQuestion = ClassifyQuestion;
|
|
140
|
+
class VerifyQuestion extends TaskQuestion {
|
|
141
|
+
constructor(text, claims, options = {}) {
|
|
142
|
+
if (!claims || typeof claims !== 'object' || !Object.keys(claims).length)
|
|
143
|
+
throw new TypeError('verify requires claims');
|
|
144
|
+
risks(options.max_error);
|
|
145
|
+
if (options.binary !== undefined && typeof options.binary !== 'boolean')
|
|
146
|
+
throw new TypeError('invalid binary');
|
|
147
|
+
super('verify', text, { claims: structuredClone(claims), ...options });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
exports.VerifyQuestion = VerifyQuestion;
|
|
151
|
+
class CompressQuestion extends TaskQuestion {
|
|
152
|
+
constructor(text = '', options = {}) {
|
|
153
|
+
if (options.unit !== undefined && !['sentence', 'line', 'paragraph'].includes(options.unit))
|
|
154
|
+
throw new TypeError('invalid unit');
|
|
155
|
+
if (options.keep_ratio !== undefined) {
|
|
156
|
+
probability(options.keep_ratio);
|
|
157
|
+
if (options.keep_ratio === 0)
|
|
158
|
+
throw new TypeError('keep_ratio must be positive');
|
|
159
|
+
}
|
|
160
|
+
if (options.threshold !== undefined)
|
|
161
|
+
probability(options.threshold);
|
|
162
|
+
if (options.min_keep !== undefined)
|
|
163
|
+
offset(options.min_keep);
|
|
164
|
+
super('compress', text, options);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
exports.CompressQuestion = CompressQuestion;
|
|
168
|
+
class ExtractQuestion extends TaskQuestion {
|
|
169
|
+
constructor(text, schema) { object(schema); super('extract', text, { schema }); }
|
|
170
|
+
}
|
|
171
|
+
exports.ExtractQuestion = ExtractQuestion;
|
|
172
|
+
function classify(text, labels, options) { return new ClassifyQuestion(text, labels, options); }
|
|
173
|
+
function verify(text, claims, options) { return new VerifyQuestion(text, claims, options); }
|
|
174
|
+
function compress(text = '', options) { return new CompressQuestion(text, options); }
|
|
175
|
+
function extract(text, schema) { return new ExtractQuestion(text, schema); }
|