runbios-sdk 0.4.0 → 0.5.0-dev.338
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 +37 -0
- package/README.md +124 -16
- package/dist/client.d.ts +36 -0
- package/dist/client.js +106 -1
- package/dist/index.d.ts +9 -5
- package/dist/index.js +11 -5
- package/dist/resources/inference.js +1 -1
- package/dist/resources/loop-chat.d.ts +87 -0
- package/dist/resources/loop-chat.js +32 -0
- package/dist/resources/loop.d.ts +93 -7
- package/dist/resources/loop.js +218 -45
- package/dist/types.d.ts +494 -34
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,43 @@ surface is added, renamed or removed, the patch for a fix inside an unchanged
|
|
|
5
5
|
surface. The SDK, the Python SDK (`runbios-sdk` on PyPI) and the MCP server
|
|
6
6
|
(`runbios-mcp`) are released together, at one version.
|
|
7
7
|
|
|
8
|
+
## 0.5.0
|
|
9
|
+
|
|
10
|
+
Added
|
|
11
|
+
|
|
12
|
+
- Conscious Loop, `loop.chat({ pipeline, messages, model? })`: one call asks a
|
|
13
|
+
model with the pipeline ID in the `X-Loop-Pipeline` header (it is
|
|
14
|
+
`inference.chatCompletions` with the `loop` option, a normal billed call)
|
|
15
|
+
and returns `{ answer, sample_id, raw, good(), bad(), correct(text) }`.
|
|
16
|
+
`good`, `bad` and `correct` send the feedback words `good`, `bad` and
|
|
17
|
+
`corrected` on the sample ID, and wait and send again while the sample is
|
|
18
|
+
still being saved (`409 SAMPLE_NOT_YET_RECORDED`), as `signal` does; on an
|
|
19
|
+
answer with no sample ID they throw before any request. `model` defaults to
|
|
20
|
+
the pipeline's own model, `loop-<pipeline ID>`; while the pipeline has no
|
|
21
|
+
live version nothing answers to that name, and `chat` then rejects with
|
|
22
|
+
`LoopNoVersionError`, an `ApiError` (the API's 404: its `status`, `code`,
|
|
23
|
+
`requestId` and `body`, with a plain message; `pipeline`, `model`, and the
|
|
24
|
+
original error as `cause`). The pipeline ID is lowercased before it names
|
|
25
|
+
the header and the model, as the platform reads it, and anything that is
|
|
26
|
+
not a pipeline ID (lowercase letters, digits, dot, dash or underscore, at
|
|
27
|
+
most 63) is refused before any request.
|
|
28
|
+
Exported: `LoopChatParams`, `LoopChatResult`, `LoopNoVersionError`.
|
|
29
|
+
- README: a Conscious Loop section, "What it is" and the three steps.
|
|
30
|
+
|
|
31
|
+
Changed
|
|
32
|
+
|
|
33
|
+
- `client.loop` is given the client's `Inference` resource, so `loop.chat`
|
|
34
|
+
uses the same key resolution and base URL as `client.inference`.
|
|
35
|
+
- The loop's module comments say what is saved: a call carrying the
|
|
36
|
+
pipeline ID header is saved whatever the recording switches say (the header
|
|
37
|
+
is the consent for that one call); they no longer say nothing is captured
|
|
38
|
+
until a source is switched on, and no longer offer DPO or GRPO. They no
|
|
39
|
+
longer name an environment: where the platform has the loop switched off,
|
|
40
|
+
every method answers `LOOP_COMING_SOON`.
|
|
41
|
+
- README: the one-header section says pipeline ID and sample ID, not tag and
|
|
42
|
+
trace id; going back to the version before is possible for 30 days (it
|
|
43
|
+
said always).
|
|
44
|
+
|
|
8
45
|
## 0.4.0
|
|
9
46
|
|
|
10
47
|
Added
|
package/README.md
CHANGED
|
@@ -219,27 +219,32 @@ POSTs are retried automatically, and `idempotencyKey` is sent only if supplied.
|
|
|
219
219
|
|
|
220
220
|
#### Record conversations into a Conscious Loop pipeline (one header)
|
|
221
221
|
|
|
222
|
-
Add `loop: { pipeline: '<
|
|
223
|
-
`messages` or `streamMessages` and the call is sent
|
|
224
|
-
more header, `X-Loop-Pipeline: <
|
|
225
|
-
|
|
226
|
-
its tool calls -- as a sample of
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
222
|
+
Add `loop: { pipeline: '<pipeline ID>' }` to `chatCompletions`,
|
|
223
|
+
`streamChatCompletions`, `messages` or `streamMessages` and the call is sent
|
|
224
|
+
exactly as before with one more header, `X-Loop-Pipeline: <pipeline ID>`
|
|
225
|
+
(several pipelines: `pipeline: ['a', 'b']`). The gateway records the exchange
|
|
226
|
+
-- what was asked, what the model answered and its tool calls -- as a sample of
|
|
227
|
+
the pipelines those pipeline IDs name (a pipeline's `own_tag`, read in
|
|
228
|
+
lowercase), even where recording is off for that model: the header is your
|
|
229
|
+
consent for that one exchange. With `chatCompletions` and
|
|
230
|
+
`streamChatCompletions` it works for serverless models and dedicated
|
|
231
|
+
deployments, including the pipeline's own `loop-<pipeline ID>` model.
|
|
232
|
+
`messages` and `streamMessages` (the Anthropic shape) work for serverless
|
|
233
|
+
models; a dedicated deployment, and so a pipeline's `loop-<pipeline ID>` model,
|
|
234
|
+
serves the OpenAI shape and refuses `/v1/messages` with 400, so call it with
|
|
235
|
+
`chatCompletions`. Only the pipeline IDs of pipelines that exist are recorded:
|
|
236
|
+
a call naming a misspelt ID, or a deleted pipeline's, is not recorded under it.
|
|
237
|
+
The reply still carries a sample ID: if recording is switched on for that
|
|
238
|
+
model, the call is still recorded the way that model records every call and
|
|
239
|
+
feedback on it is accepted; when nothing records it, feedback on it is
|
|
240
|
+
refused. `client.loop.stats()` lists such calls under `capture`.
|
|
237
241
|
|
|
238
242
|
The gateway answers with the sample's id in `X-Loop-Sample-Id`. A non-streaming
|
|
239
243
|
result carries it as `loop_sample_id` (non-enumerable: it is never part of the
|
|
240
244
|
answer's JSON), and `loop.onSampleId` hears it as soon as the headers arrive,
|
|
241
245
|
before the first streamed chunk. It is `null` when the gateway recorded nothing.
|
|
242
|
-
Send feedback on that
|
|
246
|
+
Send feedback on that sample ID: `client.loop.upvote`, `downvote` and `correct`
|
|
247
|
+
take it.
|
|
243
248
|
|
|
244
249
|
```typescript
|
|
245
250
|
const reply = await client.inference.chatCompletions({
|
|
@@ -319,6 +324,109 @@ includes failed admitted requests. The saved workspace RPM cap is a subdivision
|
|
|
319
324
|
of the org's tier allowance, not a claim about effective remaining RPM.
|
|
320
325
|
An incomplete 200 response is an error, never "zero usage" or "unlimited".
|
|
321
326
|
|
|
327
|
+
### Conscious Loop
|
|
328
|
+
|
|
329
|
+
#### What it is
|
|
330
|
+
|
|
331
|
+
The Conscious Loop keeps improving a model from your app's conversations and
|
|
332
|
+
your feedback. You make a pipeline for one use case (in the console, or with
|
|
333
|
+
`client.loop.createPipeline`). Your app's conversations are saved to it as
|
|
334
|
+
samples, you say how each answer did, and when enough samples have feedback
|
|
335
|
+
a training starts: your model learns from them. Every training is tested before
|
|
336
|
+
it can go live: on questions from your own samples that it never trained on, and on
|
|
337
|
+
your benchmarks if you add any. A training that does better, or that you
|
|
338
|
+
approve, becomes the next version: v1, v2, v3 and on, with no limit. The
|
|
339
|
+
**live version** is the version your app gets answers from right now. It
|
|
340
|
+
answers to the model name `loop-<pipeline ID>`, and each new version takes
|
|
341
|
+
that name over. For 30 days after a new version goes live you can go back to
|
|
342
|
+
the one before.
|
|
343
|
+
|
|
344
|
+
Two names to know:
|
|
345
|
+
|
|
346
|
+
- **Pipeline ID**, e.g. `support-replies`: made from the pipeline's name when
|
|
347
|
+
you created it, and it never changes. The pipeline's Connect page shows it.
|
|
348
|
+
It is the `pipeline` you pass here, sent as the `X-Loop-Pipeline` header.
|
|
349
|
+
- **Sample ID**: comes back in the reply's headers (`X-Loop-Sample-Id`), and
|
|
350
|
+
feedback names it. `client.loop.chat` hands it to you as `sample_id` and
|
|
351
|
+
sends the feedback for you. It is missing only when the platform is not
|
|
352
|
+
recording calls just then; then nothing was saved. A sample ID does not
|
|
353
|
+
prove the pipeline ID is right: if no pipeline has that ID (misspelt, or
|
|
354
|
+
the pipeline was deleted), the conversation is not saved under it. If
|
|
355
|
+
recording is switched on for the model that answered, the call is still
|
|
356
|
+
recorded the way that model records every call, and feedback on it is then
|
|
357
|
+
accepted; when nothing records it, feedback on it is refused (first "not
|
|
358
|
+
recorded yet", then "not found"). `client.loop.stats()` counts such calls
|
|
359
|
+
under `capture` (reason `no_pipeline`), and the pipeline's Connect page
|
|
360
|
+
shows its pipeline ID.
|
|
361
|
+
|
|
362
|
+
The API key needs `serverless` to call a serverless model (`deployments:read`
|
|
363
|
+
for a deployment and for the pipeline's own `loop-` model) and `loop:write` to
|
|
364
|
+
send feedback.
|
|
365
|
+
|
|
366
|
+
#### The three steps
|
|
367
|
+
|
|
368
|
+
**1. Send your app's conversations.** `client.loop.chat` is a normal, billed
|
|
369
|
+
model call with the pipeline ID added, and the conversation is saved as a
|
|
370
|
+
sample of the pipeline:
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
const reply = await client.loop.chat({
|
|
374
|
+
pipeline: 'support-replies',
|
|
375
|
+
messages: [{ role: 'user', content: 'Where is my order?' }],
|
|
376
|
+
model: 'meta-llama/Llama-3.1-8B-Instruct', // before v1: the model your app uses now
|
|
377
|
+
});
|
|
378
|
+
console.log(reply.answer, reply.sample_id);
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
Before the pipeline's first version, pass `model`. Left out, `chat` asks
|
|
382
|
+
`loop-support-replies`, and while no version is live nothing answers to that
|
|
383
|
+
name: it rejects with `LoopNoVersionError` (an `ApiError`) and says so in plain
|
|
384
|
+
words. The pipeline ID is sent in lowercase, as the platform reads it. Any
|
|
385
|
+
other chat parameter (`temperature`, `max_tokens`, `tools`, ...) is passed on.
|
|
386
|
+
Already calling `client.inference.chatCompletions`? Add
|
|
387
|
+
`loop: { pipeline: 'support-replies' }` instead (see the one-header section
|
|
388
|
+
above; it also works when streaming).
|
|
389
|
+
|
|
390
|
+
**2. Tell it how each answer did.**
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
await reply.good(); // Good
|
|
394
|
+
// await reply.bad(); // Wrong
|
|
395
|
+
// await reply.correct('It ships on Monday.'); // Wrong + the right answer
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
Feedback sent the instant an answer arrives can reach the platform before the
|
|
399
|
+
conversation is saved (`409 SAMPLE_NOT_YET_RECORDED`): `good`, `bad` and
|
|
400
|
+
`correct` wait 2 seconds and send it again, up to 6 tries. With a sample ID
|
|
401
|
+
from elsewhere, `client.loop.upvote(id)`, `downvote(id)` and
|
|
402
|
+
`correct(id, text)` do the same.
|
|
403
|
+
|
|
404
|
+
What your feedback does:
|
|
405
|
+
|
|
406
|
+
| Feedback | What happens |
|
|
407
|
+
|---|---|
|
|
408
|
+
| Good | The answer is learned as it is. |
|
|
409
|
+
| Wrong | The answer is not learned. |
|
|
410
|
+
| Wrong + right answer | The right answer is learned instead. |
|
|
411
|
+
| No feedback | If automatic scoring is on, the judge scores it. The judge is an AI that compares each answer with the right answer; where nobody gave one, as here, it scores the answer on its instructions alone. 0.8 or more is learned like Good. |
|
|
412
|
+
|
|
413
|
+
Training that learns from rankings or rewards (like DPO or GRPO) is not
|
|
414
|
+
available yet; every version learns from your good and corrected answers.
|
|
415
|
+
Samples and feedback are kept 30 days by default. Right answers are cleaned of
|
|
416
|
+
emails, long numbers and keys before they are saved.
|
|
417
|
+
|
|
418
|
+
**3. Use the improved model.** Leave `model` out once a version is live:
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
const next = await client.loop.chat({
|
|
422
|
+
pipeline: 'support-replies',
|
|
423
|
+
messages: [{ role: 'user', content: 'Can I return a gift?' }],
|
|
424
|
+
}); // asks loop-support-replies, the live version
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
Any OpenAI-compatible client can call `loop-support-replies` too. It always
|
|
428
|
+
answers with the live version, and each new version takes over the name.
|
|
429
|
+
|
|
322
430
|
### Models
|
|
323
431
|
|
|
324
432
|
The catalog lists only models hosted on Run BiOS (the platform's own verified
|
package/dist/client.d.ts
CHANGED
|
@@ -160,6 +160,26 @@ export declare class ComingSoonError extends ApiError {
|
|
|
160
160
|
}
|
|
161
161
|
/** The code an import answers with when some rows were understood and then could not be stored. */
|
|
162
162
|
export declare const IMPORT_INCOMPLETE_CODE = "IMPORT_INCOMPLETE";
|
|
163
|
+
/**
|
|
164
|
+
* `client.loop.chat` was asked to use the pipeline's own model
|
|
165
|
+
* (`loop-<pipeline ID>`) and nothing answers to that name: the pipeline has no
|
|
166
|
+
* live version right now -- before its first version, or after its only live
|
|
167
|
+
* version was taken back -- or no pipeline has that ID. Pass `model`, the
|
|
168
|
+
* model your app uses now, until a version is live.
|
|
169
|
+
*
|
|
170
|
+
* It is the API's own 404 with a plain message: an `ApiError` (`status`,
|
|
171
|
+
* `code`, `requestId` and `body` are the refusal's), so `instanceof ApiError`
|
|
172
|
+
* still catches it. `cause` is the original error.
|
|
173
|
+
*/
|
|
174
|
+
export declare class LoopNoVersionError extends ApiError {
|
|
175
|
+
/** The pipeline ID that was asked. */
|
|
176
|
+
readonly pipeline: string;
|
|
177
|
+
/** The model name that answered nothing. */
|
|
178
|
+
readonly model: string;
|
|
179
|
+
/** The API's own refusal (a 404). */
|
|
180
|
+
readonly cause: ApiError;
|
|
181
|
+
constructor(pipeline: string, model: string, cause: ApiError);
|
|
182
|
+
}
|
|
163
183
|
/**
|
|
164
184
|
* An import that did not finish: some rows could not be stored, or some
|
|
165
185
|
* verdicts that arrived with them could not be written.
|
|
@@ -261,6 +281,22 @@ export declare class HttpClient {
|
|
|
261
281
|
* Throws {@link ApiError} on non-2xx responses.
|
|
262
282
|
*/
|
|
263
283
|
request<T>(method: string, path: string, body?: unknown, extraHeaders?: Record<string, string>): Promise<T>;
|
|
284
|
+
/**
|
|
285
|
+
* Send a GET and answer the body as text, with the response's headers: for
|
|
286
|
+
* the routes that answer a file rather than JSON (a dataset's export).
|
|
287
|
+
* Throws {@link ApiError} on non-2xx responses, read from the JSON error
|
|
288
|
+
* envelope as every other call is.
|
|
289
|
+
*
|
|
290
|
+
* THE TIMEOUT IS A WAIT FOR THE NEXT PART, NOT FOR THE WHOLE FILE. A file
|
|
291
|
+
* is streamed: a large dataset takes minutes to arrive, steadily. The
|
|
292
|
+
* client's `timeout` bounds the wait for the answer to start and then for
|
|
293
|
+
* each next part of it, so a long file that keeps coming is read whole,
|
|
294
|
+
* and one that stops coming is still given up on.
|
|
295
|
+
*/
|
|
296
|
+
fetchText(path: string): Promise<{
|
|
297
|
+
text: string;
|
|
298
|
+
headers: Headers;
|
|
299
|
+
}>;
|
|
264
300
|
/** Send a GET request. */
|
|
265
301
|
fetchGet<T>(path: string, extraHeaders?: Record<string, string>): Promise<T>;
|
|
266
302
|
/** Send a POST request with a JSON body. */
|
package/dist/client.js
CHANGED
|
@@ -231,6 +231,38 @@ export class ComingSoonError extends ApiError {
|
|
|
231
231
|
}
|
|
232
232
|
/** The code an import answers with when some rows were understood and then could not be stored. */
|
|
233
233
|
export const IMPORT_INCOMPLETE_CODE = 'IMPORT_INCOMPLETE';
|
|
234
|
+
/**
|
|
235
|
+
* `client.loop.chat` was asked to use the pipeline's own model
|
|
236
|
+
* (`loop-<pipeline ID>`) and nothing answers to that name: the pipeline has no
|
|
237
|
+
* live version right now -- before its first version, or after its only live
|
|
238
|
+
* version was taken back -- or no pipeline has that ID. Pass `model`, the
|
|
239
|
+
* model your app uses now, until a version is live.
|
|
240
|
+
*
|
|
241
|
+
* It is the API's own 404 with a plain message: an `ApiError` (`status`,
|
|
242
|
+
* `code`, `requestId` and `body` are the refusal's), so `instanceof ApiError`
|
|
243
|
+
* still catches it. `cause` is the original error.
|
|
244
|
+
*/
|
|
245
|
+
export class LoopNoVersionError extends ApiError {
|
|
246
|
+
/** The pipeline ID that was asked. */
|
|
247
|
+
pipeline;
|
|
248
|
+
/** The model name that answered nothing. */
|
|
249
|
+
model;
|
|
250
|
+
/** The API's own refusal (a 404). */
|
|
251
|
+
cause;
|
|
252
|
+
constructor(pipeline, model, cause) {
|
|
253
|
+
super(cause.status, cause.body);
|
|
254
|
+
this.message =
|
|
255
|
+
`Pipeline "${pipeline}" has no live version right now, so nothing answers to "${model}". ` +
|
|
256
|
+
'Until a version is live, pass model: the model your app uses now ' +
|
|
257
|
+
'(the conversations still go to the pipeline). ' +
|
|
258
|
+
'If the pipeline has a live version, check the pipeline ID on its Connect page.';
|
|
259
|
+
this.name = 'LoopNoVersionError';
|
|
260
|
+
this.retryAfter = cause.retryAfter;
|
|
261
|
+
this.pipeline = pipeline;
|
|
262
|
+
this.model = model;
|
|
263
|
+
this.cause = cause;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
234
266
|
/**
|
|
235
267
|
* An import that did not finish: some rows could not be stored, or some
|
|
236
268
|
* verdicts that arrived with them could not be written.
|
|
@@ -364,6 +396,27 @@ export function envInferenceKey() {
|
|
|
364
396
|
export function envBaseUrl() {
|
|
365
397
|
return envVar('RUNBIOS_BASE_URL') ?? envVar('BIOS_BASE_URL');
|
|
366
398
|
}
|
|
399
|
+
/**
|
|
400
|
+
* A response's body as text, read part by part, calling `progressed` before
|
|
401
|
+
* each read so the caller's timer measures the wait for the next part rather
|
|
402
|
+
* than for the whole body. A body that cannot be streamed is read whole.
|
|
403
|
+
*/
|
|
404
|
+
async function readTextAsItArrives(res, progressed) {
|
|
405
|
+
if (!res.body)
|
|
406
|
+
return res.text();
|
|
407
|
+
const reader = res.body.getReader();
|
|
408
|
+
const decoder = new TextDecoder();
|
|
409
|
+
const parts = [];
|
|
410
|
+
for (;;) {
|
|
411
|
+
progressed();
|
|
412
|
+
const { done, value } = await reader.read();
|
|
413
|
+
if (done)
|
|
414
|
+
break;
|
|
415
|
+
parts.push(decoder.decode(value, { stream: true }));
|
|
416
|
+
}
|
|
417
|
+
parts.push(decoder.decode());
|
|
418
|
+
return parts.join('');
|
|
419
|
+
}
|
|
367
420
|
// ============================================================================
|
|
368
421
|
// HttpClient — shared HTTP transport used by resource modules via composition
|
|
369
422
|
// ============================================================================
|
|
@@ -378,7 +431,7 @@ export class HttpClient {
|
|
|
378
431
|
constructor(config) {
|
|
379
432
|
// Default host stays api.runbios.ai for now; cutover to api.runbios.ai is
|
|
380
433
|
// planned once its DNS exists.
|
|
381
|
-
this.baseUrl = (config.baseUrl || envBaseUrl() || 'https://api.runbios.ai').replace(/\/+$/, '');
|
|
434
|
+
this.baseUrl = (config.baseUrl || envBaseUrl() || 'https://api-dev.runbios.ai').replace(/\/+$/, '');
|
|
382
435
|
this.apiKey = config.apiKey ?? envApiKey();
|
|
383
436
|
this.accessToken = config.accessToken;
|
|
384
437
|
this.orgId = config.orgId;
|
|
@@ -469,6 +522,58 @@ export class HttpClient {
|
|
|
469
522
|
}
|
|
470
523
|
return data;
|
|
471
524
|
}
|
|
525
|
+
/**
|
|
526
|
+
* Send a GET and answer the body as text, with the response's headers: for
|
|
527
|
+
* the routes that answer a file rather than JSON (a dataset's export).
|
|
528
|
+
* Throws {@link ApiError} on non-2xx responses, read from the JSON error
|
|
529
|
+
* envelope as every other call is.
|
|
530
|
+
*
|
|
531
|
+
* THE TIMEOUT IS A WAIT FOR THE NEXT PART, NOT FOR THE WHOLE FILE. A file
|
|
532
|
+
* is streamed: a large dataset takes minutes to arrive, steadily. The
|
|
533
|
+
* client's `timeout` bounds the wait for the answer to start and then for
|
|
534
|
+
* each next part of it, so a long file that keeps coming is read whole,
|
|
535
|
+
* and one that stops coming is still given up on.
|
|
536
|
+
*/
|
|
537
|
+
async fetchText(path) {
|
|
538
|
+
const url = `${this.baseUrl}${path}`;
|
|
539
|
+
const headers = this.buildHeaders({ 'Accept': 'application/x-ndjson, application/json' });
|
|
540
|
+
const controller = new AbortController();
|
|
541
|
+
let timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
542
|
+
const progressed = () => {
|
|
543
|
+
clearTimeout(timeoutId);
|
|
544
|
+
timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
545
|
+
};
|
|
546
|
+
let res;
|
|
547
|
+
let text;
|
|
548
|
+
let reading = false;
|
|
549
|
+
try {
|
|
550
|
+
res = await fetch(url, { method: 'GET', headers, signal: controller.signal });
|
|
551
|
+
reading = true;
|
|
552
|
+
text = await readTextAsItArrives(res, progressed);
|
|
553
|
+
}
|
|
554
|
+
catch (err) {
|
|
555
|
+
if (controller.signal.aborted || (err instanceof DOMException && err.name === 'AbortError')) {
|
|
556
|
+
throw new ApiError(0, {
|
|
557
|
+
error: reading
|
|
558
|
+
? `Request timed out: nothing more arrived for ${this.timeout}ms`
|
|
559
|
+
: `Request timed out after ${this.timeout}ms`,
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
throw err;
|
|
563
|
+
}
|
|
564
|
+
finally {
|
|
565
|
+
clearTimeout(timeoutId);
|
|
566
|
+
}
|
|
567
|
+
if (!res.ok) {
|
|
568
|
+
let data = {};
|
|
569
|
+
try {
|
|
570
|
+
data = JSON.parse(text);
|
|
571
|
+
}
|
|
572
|
+
catch { /* a body that is not JSON carries no envelope */ }
|
|
573
|
+
throw errorFromResponse(res, data);
|
|
574
|
+
}
|
|
575
|
+
return { text, headers: res.headers };
|
|
576
|
+
}
|
|
472
577
|
/** Send a GET request. */
|
|
473
578
|
fetchGet(path, extraHeaders) {
|
|
474
579
|
return this.request('GET', path, undefined, extraHeaders);
|
package/dist/index.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ import { Loop } from './resources/loop.js';
|
|
|
36
36
|
* SDK version. Sent as part of the User-Agent header.
|
|
37
37
|
* Must match package.json "version" -- enforced by a contract test.
|
|
38
38
|
*/
|
|
39
|
-
export declare const VERSION = "0.
|
|
39
|
+
export declare const VERSION = "0.5.0-dev.338";
|
|
40
40
|
export declare class RunBiOS {
|
|
41
41
|
/** Search models, fetch configs, check adapter compatibility. */
|
|
42
42
|
readonly models: Models;
|
|
@@ -55,9 +55,12 @@ export declare class RunBiOS {
|
|
|
55
55
|
*/
|
|
56
56
|
readonly inference: Inference;
|
|
57
57
|
/**
|
|
58
|
-
* The Conscious Loop
|
|
59
|
-
*
|
|
60
|
-
*
|
|
58
|
+
* The Conscious Loop: pipelines that keep improving a model from your app's
|
|
59
|
+
* conversations and your feedback. `loop.chat` asks a model and saves the
|
|
60
|
+
* conversation as a sample; `good()`, `bad()` and `correct(text)` on its
|
|
61
|
+
* answer say how it did. A call is saved only when it carries the pipeline
|
|
62
|
+
* ID (`chat`, or the `loop` option of `inference`), or when recording is
|
|
63
|
+
* switched on for its source.
|
|
61
64
|
*/
|
|
62
65
|
readonly loop: Loop;
|
|
63
66
|
/** @internal */
|
|
@@ -74,8 +77,9 @@ export { Integrations, type HuggingFaceIntegration, type IntegrationBrowseParams
|
|
|
74
77
|
export { Training } from './resources/training.js';
|
|
75
78
|
export { Wallet } from './resources/wallet.js';
|
|
76
79
|
export { GPU, type GPURecommendation } from './resources/gpu.js';
|
|
80
|
+
export { LoopNoVersionError, type LoopChatParams, type LoopChatResult, } from './resources/loop-chat.js';
|
|
77
81
|
export { Inference, validateChatRequest, parseSSE, buildInferenceRequest, contextSizingBasis, type ContextSizingBasis, CONTEXT_DEFAULT_CEILING, CONTEXT_EDITABLE_FLOOR, type ChatMessage, type FunctionTool, type ChatCompletionParams, type ChatCompletionResponse, type ChatCompletionChunk, type InferenceVerbParams, type CompletionParams, type EmbeddingParams, type RerankParams, type AnthropicMessageParams, type AnthropicMessageResponse, type LoopCaptureOption, type LoopSampleIdOnResult, LOOP_PIPELINE_HEADER, LOOP_SAMPLE_ID_HEADER, type ServerlessUsageWindow, type ServerlessTimeseriesMetric, type ServerlessWorkspaceLimits, type ServerlessUsageEnvelope, type ServerlessUsageOverviewResponse, type ServerlessUsageRowsResponse, type ServerlessUsageTimeseriesResponse, type ServerlessUsageDailyResponse, type ServerlessSavingsTotals, type ServerlessSavingsPeriod, type ServerlessUsageSavingsResponse, } from './resources/inference.js';
|
|
78
|
-
export type { BiOSConfig, PaginatedResponse, ApiErrorBody, AvailableGpuAlternative, CapacityMinimumRequirement, InferenceBookingAccepted, Model, ModelSearchParams, ModelSearchResponse, ModelDetailResponse, InferenceModel, InferenceModelListResponse, ModelConfig, Dataset, DatasetListParams, DatasetListResponse, DatasetUploadParams, DatasetPreview, DatasetPreviewParams, DatasetImportHFParams, DatasetRegisterHFParams, DatasetHubSearchParams, DatasetHubPreviewParams, DatasetValidation, DatasetFormatVariant, DatasetFormatSpec, DatasetFormatSpecs, DatasetStorageUsage, TrainingMethod, RLHFAlgorithm, AdapterType, TrainingJobStatus, TrainingStatusFilter, TrainingCreateParams, TrainingStartFromParams, TrainingStartFrom, TrainingExportStep, TrainingListParams, TrainingListResponse, TrainingJob, TrainingMetrics, TrainingResourceMetrics, TrainingDeviceMetrics, MetricPoint, MetricGraphConfig, TrainingCheckpoint, TrainingLogs, TrainingLogEntry, TrainingStopResponse, TrainingResumeResponse, CanonicalTrainingRequest, TrainingPreflightDataset, TrainingPreflightWarning, TrainingPreflightResponse, TrainingCapabilityChoice, TrainingConfigFieldCapability, TrainingCapabilities, TrainingFeature, GPUChoice, WalletBalance, Transaction, TransactionListResponse, TransactionListParams, GPUInfo, GPUPricingResponse, GPUOptionsParams, TrainingRecommendParams, TrainingAdvisorVerdict, TrainingAdvisorRecommendation, TrainingAdvisorUserShape, TrainingAdvisorJustification, TrainingAdvisorBasis, GPUOption, GPUOptionSuggestion, GPUOptionsResponse, InferenceStatus, InferenceCreateParams, InferenceUpdateParams, InferenceDeployment, InferenceDeploymentSummary, InferenceCreateResponse, InferenceListResponse, InferenceUpdateResponse, InferenceLifecycleResponse, InferenceDeleteResponse, InferenceGPUOptionMarket, InferenceGPUOption, InferenceGPUAlternative, InferenceGPUOptionsParams, InferenceGPUOptionsResponse, InferenceCanonicalRequest, InferencePreflightWarning, InferencePreflightResponse, AdapterCompatibility, AdapterCompatibilityResponse, AdapterCompatibilityParams, SupportedArchitecture, ArchitectureScope, SupportedArchitecturesResponse, ApiKey, ApiKeyScope, ApiKeyIntrospection, Organization, OrgMember, OrgInvite, Workspace, Integration, IntegrationCreateParams, StorageUsage, StorageObject, PromptTokensDetails, ChatCompletionUsage, ServingKind, TrainingRulePausedReason, TrainingTrigger, TrainingRecipe, TrainingRunState, TrainingVerdict, TrainingDecision, EvaluationStatus, EvaluationItemStatus, ItemErrorCode, EvaluationWinner, EvaluationItemWinnerFilter, ReportStatusFilter, ReportSort, EvaluationWarningCode, TrainingToolCall, TrainingMessage, TrainingRuleServing, TrainingRunPromoteRequest, TrainingRunRejectRequest, TrainingRunRollbackRequest, TrainingRunCancelRequest, TrainingRunListParams, TrainingRun, TrainingRunSummary, TrainingRunEvent, TrainingRunLinks, TrainingRunActions, EvaluationRef, EvaluationDecoding, EvaluationJudgeDimension, EvaluationDimensionScore, EvaluationWarning, EvaluationMargin, Evaluation, EvaluationItemJudge, EvaluationReferenceKind, EvaluationReference, EvaluationPairwiseOrder, EvaluationItemPairwise, EvaluationItemSide, EvaluationItem, EvaluationItemListParams, BenchmarkStatus, BenchmarkSourceKind, BenchmarkRunStatus, BenchmarkScoring, BenchmarkCheck, BenchmarkDimensionScore, Benchmark, BenchmarkItem, BenchmarkRun, BenchmarkRunItemSide, BenchmarkRunItem, BenchmarkRunItemListParams, BenchmarkHistoryPoint, BenchmarkSource, BenchmarkCreateParams, BenchmarkListParams, BenchmarkItemListParams, BenchmarkHistoryParams, BenchmarkRetireParams, InferenceAlias, InferenceAliasRequest, TrainingRuleDeleteResponse, TrainingRunListResponse, TrainingRunBenchmarkRun, TrainingRunResponse, TrainingRunActionResponse, EvaluationItemsResponse, BenchmarkRunItemsResponse, InferenceAliasListResponse, InferenceAliasResponse, InferenceAliasDeleteResponse, BenchmarkListResponse, BenchmarkItemsResponse, BenchmarkHistoryResponse, TrainingRuleRunRefusalCode, TrainingRuleSaveRefusalCode, TrainingRuleMonthlyLimitRefusal, PipelineStatus, PipelineActiveRun, PipelineVersion, PipelineAttempt, AttemptExplanation, PipelineAttemptListParams, PipelineAttemptListResponse, PipelineAttemptModelFilter, PipelineAttemptResultFilter, PipelineAttemptTrigger, Lineage, PipelineChain, PipelineChainRebuild, TrainingRunRollbackRefusalCode, LoopTraceRebuild, LoopTraceDeleteResponse, PipelineAttemptOutcome, PipelineLiveDeploymentStatus, PipelineLiveDeployment, LoopStorageWhere, LoopStorage, Pipeline, PipelineListParams, PipelineListStatus, PipelineListResponse, PipelineTrainingSettings, PipelineTrainingDefaults, PipelineTrainingRequest, PipelineResponse, PipelineTrainWhen, PipelineSchedule, PipelineWriteRequest, PipelineMutationResponse, PipelineImportParams, PipelineFloors, HeldBackFloors, LoopModel, LoopModelsResponse, LoopUnavailableModel, LoopUnavailableReasonCode, LoopTracePipeline, LoopTracePipelinesResponse, LoopMetricsHistoryParams, LoopMetricPoint, LoopSignalDay, LoopMetricsHistory, LoopCaptureFeedback, LoopFeedbackWord, LoopCaptureHealth, LoopCaptureMiss, LoopSignalManyItem, LoopSignalManyResult, PromotionPolicyState, RuleBenchmark, PipelineBenchmarksResponse, PipelineBenchmarkAttachParams, PipelineBenchmarkMoveParams, CompareBenchmark, VersionBenchmarkScore, PromotionMeasurement, PromotionOutcome, VersionHoldout, VersionScores, VersionScoreDifference, VersionCompareResponse, VersionCompareParams, TrainingRunPromotion, VerdictCheck, VerdictCheckKey, VerdictChecksWithheld, TrainingRunCost, AttemptDirection, AttemptHoldout, AttemptCompareSide, AttemptSharedSample, AttemptSharedFigures, AttemptBenchmarkSide, AttemptCompareBenchmark, AttemptCompareItemSide, AttemptCompareItem, AttemptCompareResponse, AttemptCompareParams, LoopTraceJudge, LoopJudgeFilter, LoopSampleState, LoopSampleStateName, LoopSampleDay, LoopImportRefusal, PipelineAutoScoring, PipelineAutoScoringStatus, LoopDatasetFeedback, LoopSampleOrigin, LoopFeedbackCounts, LoopPipelineCounts, LoopDatasetPipelineRef, LoopDataset, LoopDatasetWrite, LoopDatasetCreate, LoopDatasetPreviewParams, LoopDatasetPreviewModel, LoopDatasetPreview, LoopDatasetListParams, LoopDatasetListResponse, LoopDatasetDeleteResponse, PipelineDatasetRef, PipelineConditions, PipelineConditionsWrite, TrainedOnDataset, TrainedOn, } from './types.js';
|
|
82
|
+
export type { BiOSConfig, PaginatedResponse, ApiErrorBody, AvailableGpuAlternative, CapacityMinimumRequirement, InferenceBookingAccepted, Model, ModelSearchParams, ModelSearchResponse, ModelDetailResponse, InferenceModel, InferenceModelListResponse, ModelConfig, Dataset, DatasetListParams, DatasetListResponse, DatasetUploadParams, DatasetPreview, DatasetPreviewParams, DatasetImportHFParams, DatasetRegisterHFParams, DatasetHubSearchParams, DatasetHubPreviewParams, DatasetValidation, DatasetFormatVariant, DatasetFormatSpec, DatasetFormatSpecs, DatasetStorageUsage, TrainingMethod, RLHFAlgorithm, AdapterType, TrainingJobStatus, TrainingStatusFilter, TrainingCreateParams, TrainingStartFromParams, TrainingStartFrom, TrainingExportStep, TrainingListParams, TrainingListResponse, TrainingJob, TrainingMetrics, TrainingResourceMetrics, TrainingDeviceMetrics, MetricPoint, MetricGraphConfig, TrainingCheckpoint, TrainingLogs, TrainingLogEntry, TrainingStopResponse, TrainingResumeResponse, CanonicalTrainingRequest, TrainingPreflightDataset, TrainingPreflightWarning, TrainingPreflightResponse, TrainingCapabilityChoice, TrainingConfigFieldCapability, TrainingCapabilities, TrainingFeature, GPUChoice, WalletBalance, Transaction, TransactionListResponse, TransactionListParams, GPUInfo, GPUPricingResponse, GPUOptionsParams, TrainingRecommendParams, TrainingAdvisorVerdict, TrainingAdvisorRecommendation, TrainingAdvisorUserShape, TrainingAdvisorJustification, TrainingAdvisorBasis, GPUOption, GPUOptionSuggestion, GPUOptionsResponse, InferenceStatus, InferenceCreateParams, InferenceUpdateParams, InferenceDeployment, InferenceDeploymentSummary, InferenceCreateResponse, InferenceListResponse, InferenceUpdateResponse, InferenceLifecycleResponse, InferenceDeleteResponse, InferenceGPUOptionMarket, InferenceGPUOption, InferenceGPUAlternative, InferenceGPUOptionsParams, InferenceGPUOptionsResponse, InferenceCanonicalRequest, InferencePreflightWarning, InferencePreflightResponse, AdapterCompatibility, AdapterCompatibilityResponse, AdapterCompatibilityParams, SupportedArchitecture, ArchitectureScope, SupportedArchitecturesResponse, ApiKey, ApiKeyScope, ApiKeyIntrospection, Organization, OrgMember, OrgInvite, Workspace, Integration, IntegrationCreateParams, StorageUsage, StorageObject, PromptTokensDetails, ChatCompletionUsage, ServingKind, TrainingRulePausedReason, TrainingTrigger, TrainingRecipe, TrainingRunState, TrainingVerdict, TrainingDecision, EvaluationStatus, EvaluationItemStatus, ItemErrorCode, EvaluationWinner, EvaluationItemWinnerFilter, ReportStatusFilter, ReportSort, EvaluationWarningCode, TrainingToolCall, TrainingMessage, TrainingRuleServing, TrainingRunPromoteRequest, TrainingRunRejectRequest, TrainingRunRollbackRequest, TrainingRunCancelRequest, TrainingRunListParams, TrainingRun, TrainingRunSummary, TrainingRunEvent, TrainingRunLinks, TrainingRunActions, EvaluationRef, EvaluationDecoding, EvaluationJudgeDimension, EvaluationDimensionScore, EvaluationWarning, EvaluationMargin, Evaluation, EvaluationItemJudge, EvaluationReferenceKind, EvaluationReference, EvaluationPairwiseOrder, EvaluationItemPairwise, EvaluationItemSide, EvaluationItem, EvaluationItemListParams, BenchmarkStatus, BenchmarkSourceKind, BenchmarkRunStatus, BenchmarkScoring, BenchmarkCheck, BenchmarkDimensionScore, Benchmark, BenchmarkItem, BenchmarkRun, BenchmarkRunItemSide, BenchmarkRunItem, BenchmarkRunItemListParams, BenchmarkHistoryPoint, BenchmarkSource, BenchmarkCreateParams, BenchmarkListParams, BenchmarkItemListParams, BenchmarkHistoryParams, BenchmarkRetireParams, InferenceAlias, InferenceAliasRequest, TrainingRuleDeleteResponse, TrainingRunListResponse, TrainingRunBenchmarkRun, TrainingRunResponse, TrainingRunActionResponse, EvaluationItemsResponse, BenchmarkRunItemsResponse, InferenceAliasListResponse, InferenceAliasResponse, InferenceAliasDeleteResponse, BenchmarkListResponse, BenchmarkItemsResponse, BenchmarkHistoryResponse, BenchmarkQuestionsParams, BenchmarkQuestionsTraining, BenchmarkQuestionResult, BenchmarkQuestion, BenchmarkQuestionsResponse, TrainingCurvePoint, TrainingCurve, TrainingRuleRunRefusalCode, TrainingRuleSaveRefusalCode, TrainingRuleMonthlyLimitRefusal, PipelineStatus, PipelineActiveRun, PipelineVersion, PipelineAttempt, AttemptExplanation, AttemptMissedBar, PipelineModelRole, PipelineComparedWith, PipelineTotals, PipelineTrainingsByResult, PipelineTrainingsByModel, PipelineSpentParts, PipelineAttemptListParams, PipelineAttemptListResponse, PipelineAttemptModelFilter, PipelineAttemptResultFilter, PipelineAttemptTrigger, PipelineSecondModel, PipelineSecondModelState, Lineage, PipelineChain, PipelineChainRebuild, TrainingRunRollbackRefusalCode, TrainingRunRetestRefusalCode, LoopTraceRebuild, LoopTraceDeleteResponse, PipelineAttemptOutcome, PipelineLiveDeploymentStatus, PipelineLiveDeployment, LoopStorageWhere, LoopStorage, Pipeline, PipelineListParams, PipelineListStatus, PipelineListResponse, PipelineTrainingSettings, PipelineTrainingDefaults, PipelineTrainingRequest, PipelineResponse, PipelineTrainWhen, PipelineSchedule, PipelineWriteRequest, PipelineMutationResponse, PipelineImportParams, PipelineFloors, HeldBackFloors, LoopModel, LoopModelsResponse, LoopUnavailableModel, LoopUnavailableReasonCode, LoopTracePipeline, LoopTracePipelinesResponse, LoopMetricsHistoryParams, LoopMetricPoint, LoopSignalDay, LoopMetricsHistory, LoopCaptureFeedback, LoopFeedbackWord, LoopCaptureHealth, LoopCaptureMiss, LoopSignalManyItem, LoopSignalManyResult, PromotionPolicyState, RuleBenchmark, PipelineBenchmarksResponse, PipelineBenchmarkAttachParams, PipelineBenchmarkMoveParams, CompareBenchmark, VersionBenchmarkScore, PromotionMeasurement, PromotionOutcome, VersionHoldout, VersionCommonSet, VersionScores, VersionScoreDifference, VersionCompareResponse, VersionCompareParams, TrainingRunPromotion, VerdictCheck, VerdictCheckKey, VerdictChecksWithheld, TrainingRunCost, AttemptDirection, AttemptHoldout, AttemptCompareSide, AttemptSharedSample, AttemptSharedFigures, AttemptBenchmarkSide, AttemptCompareBenchmark, AttemptCompareItemSide, AttemptCompareItem, AttemptCompareResponse, AttemptCompareParams, LoopTraceJudge, LoopJudgeFilter, LoopSampleState, LoopSampleStateName, LoopSampleDay, LoopImportRefusal, PipelineAutoScoring, PipelineAutoScoringStatus, LoopDatasetFeedback, LoopSampleOrigin, LoopFeedbackCounts, LoopPipelineCounts, LoopDatasetPipelineRef, LoopDataset, LoopDatasetWrite, LoopDatasetCreate, LoopDatasetPreviewParams, LoopDatasetPreviewModel, LoopDatasetPreview, LoopDatasetListParams, LoopDatasetListResponse, LoopDatasetCreator, LoopDatasetLearnedBy, LoopDatasetExportParams, LoopDatasetExportLine, LoopDatasetExport, LoopDatasetDeleteResponse, PipelineDatasetRef, PipelineConditions, PipelineConditionsWrite, TrainedOnDataset, TrainedOn, } from './types.js';
|
|
79
83
|
/** The closed set of run states a training run never leaves. */
|
|
80
84
|
export { TERMINAL_RUN_STATES } from './types.js';
|
|
81
85
|
/** The most versions a pipeline may be set to make. */
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,7 @@ import { Loop } from './resources/loop.js';
|
|
|
36
36
|
* SDK version. Sent as part of the User-Agent header.
|
|
37
37
|
* Must match package.json "version" -- enforced by a contract test.
|
|
38
38
|
*/
|
|
39
|
-
export const VERSION = '0.
|
|
39
|
+
export const VERSION = '0.5.0-dev.338';
|
|
40
40
|
export class RunBiOS {
|
|
41
41
|
/** Search models, fetch configs, check adapter compatibility. */
|
|
42
42
|
models;
|
|
@@ -55,9 +55,12 @@ export class RunBiOS {
|
|
|
55
55
|
*/
|
|
56
56
|
inference;
|
|
57
57
|
/**
|
|
58
|
-
* The Conscious Loop
|
|
59
|
-
*
|
|
60
|
-
*
|
|
58
|
+
* The Conscious Loop: pipelines that keep improving a model from your app's
|
|
59
|
+
* conversations and your feedback. `loop.chat` asks a model and saves the
|
|
60
|
+
* conversation as a sample; `good()`, `bad()` and `correct(text)` on its
|
|
61
|
+
* answer say how it did. A call is saved only when it carries the pipeline
|
|
62
|
+
* ID (`chat`, or the `loop` option of `inference`), or when recording is
|
|
63
|
+
* switched on for its source.
|
|
61
64
|
*/
|
|
62
65
|
loop;
|
|
63
66
|
/** @internal */
|
|
@@ -71,7 +74,6 @@ export class RunBiOS {
|
|
|
71
74
|
this.training = new Training(http);
|
|
72
75
|
this.wallet = new Wallet(http);
|
|
73
76
|
this.gpu = new GPU(http);
|
|
74
|
-
this.loop = new Loop(http);
|
|
75
77
|
// Inference-key resolution, most specific first:
|
|
76
78
|
// 1. an explicit inferenceKey (a per-deployment sk-bios-... key)
|
|
77
79
|
// 2. RUNBIOS_INFERENCE_KEY from the environment (legacy: BIOS_INFERENCE_KEY)
|
|
@@ -85,6 +87,9 @@ export class RunBiOS {
|
|
|
85
87
|
baseUrl: config.inferenceBaseUrl || config.baseUrl,
|
|
86
88
|
timeout: config.inferenceTimeout,
|
|
87
89
|
}, http);
|
|
90
|
+
// Loop.chat asks a model through the same Inference resource, so it uses
|
|
91
|
+
// the same key resolution and base URL as client.inference.
|
|
92
|
+
this.loop = new Loop(http, this.inference);
|
|
88
93
|
}
|
|
89
94
|
async introspect() {
|
|
90
95
|
return this._http.fetchGet('/api/api-keys/introspect');
|
|
@@ -102,6 +107,7 @@ export { Integrations } from './resources/integrations.js';
|
|
|
102
107
|
export { Training } from './resources/training.js';
|
|
103
108
|
export { Wallet } from './resources/wallet.js';
|
|
104
109
|
export { GPU } from './resources/gpu.js';
|
|
110
|
+
export { LoopNoVersionError, } from './resources/loop-chat.js';
|
|
105
111
|
export { Inference, validateChatRequest, parseSSE, buildInferenceRequest, contextSizingBasis, CONTEXT_DEFAULT_CEILING, CONTEXT_EDITABLE_FLOOR, LOOP_PIPELINE_HEADER, LOOP_SAMPLE_ID_HEADER, } from './resources/inference.js';
|
|
106
112
|
/** The closed set of run states a training run never leaves. */
|
|
107
113
|
export { TERMINAL_RUN_STATES } from './types.js';
|
|
@@ -373,7 +373,7 @@ export class Inference {
|
|
|
373
373
|
this.key = config.inferenceKey || envInferenceKey() || envApiKey();
|
|
374
374
|
// Same default host as HttpClient (api.runbios.ai); api.runbios.ai cutover
|
|
375
375
|
// is planned once its DNS exists — update both call sites together.
|
|
376
|
-
this.baseUrl = (config.baseUrl || envBaseUrl() || 'https://api.runbios.ai').replace(/\/+$/, '');
|
|
376
|
+
this.baseUrl = (config.baseUrl || envBaseUrl() || 'https://api-dev.runbios.ai').replace(/\/+$/, '');
|
|
377
377
|
this.timeout = config.timeout ?? 900_000;
|
|
378
378
|
this._http = http;
|
|
379
379
|
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { LoopSignal } from '../types.js';
|
|
2
|
+
import type { ChatMessage, ChatCompletionParams, ChatCompletionResponse } from './inference.js';
|
|
3
|
+
/** The model name a pipeline's live version answers to: `loop-` and its pipeline ID. */
|
|
4
|
+
export declare const PIPELINE_MODEL_PREFIX = "loop-";
|
|
5
|
+
/**
|
|
6
|
+
* A pipeline ID as the platform reads it. The gateways trim and lowercase the
|
|
7
|
+
* `X-Loop-Pipeline` header and keep only this shape, and the pipeline's own
|
|
8
|
+
* model name is `loop-` and the ID exactly, matched case for case. So `chat`
|
|
9
|
+
* lowercases the ID before it names the header and the model, and refuses
|
|
10
|
+
* anything else before any request: sent, it would be dropped and nothing saved.
|
|
11
|
+
*/
|
|
12
|
+
export declare const PIPELINE_ID_PATTERN: RegExp;
|
|
13
|
+
/**
|
|
14
|
+
* What `client.loop.chat` takes: the pipeline ID, the conversation, and
|
|
15
|
+
* optionally the model to ask. Anything else (`temperature`, `max_tokens`,
|
|
16
|
+
* `tools`, `requestId`, ...) is passed to `client.inference.chatCompletions`
|
|
17
|
+
* as it is.
|
|
18
|
+
*/
|
|
19
|
+
export interface LoopChatParams extends Record<string, unknown> {
|
|
20
|
+
/**
|
|
21
|
+
* The pipeline ID (its `own_tag`, e.g. `support-replies`): made from the
|
|
22
|
+
* pipeline's name when it was created, and it never changes. The Connect
|
|
23
|
+
* page shows it. Sent as the `X-Loop-Pipeline` header, lowercased as the
|
|
24
|
+
* platform reads it.
|
|
25
|
+
*/
|
|
26
|
+
pipeline: string;
|
|
27
|
+
/** The conversation to send, as for `chatCompletions`. */
|
|
28
|
+
messages: ChatMessage[];
|
|
29
|
+
/**
|
|
30
|
+
* The model to ask. Leave it out to ask the pipeline's own model,
|
|
31
|
+
* `loop-<pipeline ID>`, which answers with the live version. Before the
|
|
32
|
+
* pipeline's first version nothing answers to that name: pass the model your
|
|
33
|
+
* app uses now (a serverless model id or a deployment name).
|
|
34
|
+
*/
|
|
35
|
+
model?: string;
|
|
36
|
+
tools?: ChatCompletionParams['tools'];
|
|
37
|
+
toolChoice?: ChatCompletionParams['toolChoice'];
|
|
38
|
+
reasoningEffort?: ChatCompletionParams['reasoningEffort'];
|
|
39
|
+
inferenceKey?: string;
|
|
40
|
+
idempotencyKey?: string;
|
|
41
|
+
requestId?: string;
|
|
42
|
+
signal?: AbortSignal;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The answer `client.loop.chat` returns, with the feedback for it one call
|
|
46
|
+
* away. `good()`, `bad()` and `correct(text)` send feedback on `sample_id`,
|
|
47
|
+
* and wait and send again while the sample is still being saved (409
|
|
48
|
+
* `SAMPLE_NOT_YET_RECORDED`), as `signal` does.
|
|
49
|
+
*/
|
|
50
|
+
export interface LoopChatResult {
|
|
51
|
+
/**
|
|
52
|
+
* The model's answer as text: `choices[0].message.content`. Empty when the
|
|
53
|
+
* model answered only with tool calls; `raw` has them.
|
|
54
|
+
*/
|
|
55
|
+
readonly answer: string;
|
|
56
|
+
/**
|
|
57
|
+
* The sample ID the reply's `X-Loop-Sample-Id` header carried, which
|
|
58
|
+
* feedback names. null when no sample ID came back (the platform was not
|
|
59
|
+
* recording calls just then), so the answer was not saved as a sample and
|
|
60
|
+
* cannot take feedback. A sample ID does not prove the pipeline ID is
|
|
61
|
+
* right: when no pipeline has it (misspelt, or the pipeline was deleted),
|
|
62
|
+
* the conversation is not saved under it. If recording is switched on for
|
|
63
|
+
* the model that answered, the call is still recorded the way that model
|
|
64
|
+
* records every call, and feedback on it is then accepted; when nothing
|
|
65
|
+
* records it, feedback on it is refused (409 `SAMPLE_NOT_YET_RECORDED` at
|
|
66
|
+
* first, 404 later). `stats()` counts such calls under `capture` (reason
|
|
67
|
+
* `no_pipeline`); the pipeline's Connect page shows its pipeline ID.
|
|
68
|
+
*/
|
|
69
|
+
readonly sample_id: string | null;
|
|
70
|
+
/** The whole `/v1/chat/completions` answer, exactly as the endpoint sent it. */
|
|
71
|
+
readonly raw: ChatCompletionResponse;
|
|
72
|
+
/** Good: the answer is learned as it is. */
|
|
73
|
+
good(reason?: string): Promise<LoopSignal>;
|
|
74
|
+
/** Wrong: the answer is not learned. If you know the right answer, `correct` is worth far more. */
|
|
75
|
+
bad(reason?: string): Promise<LoopSignal>;
|
|
76
|
+
/** Wrong, and here is the right answer: the right answer is learned instead of the model's. */
|
|
77
|
+
correct(rightAnswer: string, reason?: string): Promise<LoopSignal>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The error `client.loop.chat` rejects with when the pipeline's own model
|
|
81
|
+
* answers nothing. It is defined in client.ts beside the SDK's other
|
|
82
|
+
* `ApiError` subclasses: a class that extends `ApiError` is built when its
|
|
83
|
+
* module loads, and client.ts must have finished loading by then.
|
|
84
|
+
*/
|
|
85
|
+
export { LoopNoVersionError } from '../client.js';
|
|
86
|
+
/** The answer's text: a string content, or the text parts of a list of parts. */
|
|
87
|
+
export declare function chatAnswerText(raw: ChatCompletionResponse): string;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** The model name a pipeline's live version answers to: `loop-` and its pipeline ID. */
|
|
2
|
+
export const PIPELINE_MODEL_PREFIX = 'loop-';
|
|
3
|
+
/**
|
|
4
|
+
* A pipeline ID as the platform reads it. The gateways trim and lowercase the
|
|
5
|
+
* `X-Loop-Pipeline` header and keep only this shape, and the pipeline's own
|
|
6
|
+
* model name is `loop-` and the ID exactly, matched case for case. So `chat`
|
|
7
|
+
* lowercases the ID before it names the header and the model, and refuses
|
|
8
|
+
* anything else before any request: sent, it would be dropped and nothing saved.
|
|
9
|
+
*/
|
|
10
|
+
export const PIPELINE_ID_PATTERN = /^[a-z0-9][a-z0-9._-]{0,62}$/;
|
|
11
|
+
/**
|
|
12
|
+
* The error `client.loop.chat` rejects with when the pipeline's own model
|
|
13
|
+
* answers nothing. It is defined in client.ts beside the SDK's other
|
|
14
|
+
* `ApiError` subclasses: a class that extends `ApiError` is built when its
|
|
15
|
+
* module loads, and client.ts must have finished loading by then.
|
|
16
|
+
*/
|
|
17
|
+
export { LoopNoVersionError } from '../client.js';
|
|
18
|
+
/** The answer's text: a string content, or the text parts of a list of parts. */
|
|
19
|
+
export function chatAnswerText(raw) {
|
|
20
|
+
const choices = raw.choices;
|
|
21
|
+
const first = Array.isArray(choices) ? choices[0] : undefined;
|
|
22
|
+
const content = first?.message?.content;
|
|
23
|
+
if (typeof content === 'string')
|
|
24
|
+
return content;
|
|
25
|
+
if (Array.isArray(content)) {
|
|
26
|
+
return content
|
|
27
|
+
.map((part) => (part && typeof part === 'object' && typeof part.text === 'string'
|
|
28
|
+
? part.text : ''))
|
|
29
|
+
.join('');
|
|
30
|
+
}
|
|
31
|
+
return '';
|
|
32
|
+
}
|