runbios-sdk 0.3.0 → 0.4.0-dev.333
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 +66 -0
- package/dist/client.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/resources/inference.js +1 -1
- package/dist/resources/loop.d.ts +128 -17
- package/dist/resources/loop.js +168 -17
- package/dist/types.d.ts +632 -34
- package/package.json +1 -1
package/dist/resources/loop.js
CHANGED
|
@@ -138,7 +138,9 @@ export class Loop {
|
|
|
138
138
|
* an earlier import already had, and the sentences in `notes` are written
|
|
139
139
|
* from those counts, so the numbers and the prose describe the same call and
|
|
140
140
|
* a re-send cannot report a reviewed conversation as waiting for review.
|
|
141
|
-
* `refused` is rows whose shape could not be read, which the file fixes
|
|
141
|
+
* `refused` is rows whose shape could not be read, which the file fixes,
|
|
142
|
+
* and `refused_reasons` says why, one plain `sentence` per reason with its
|
|
143
|
+
* `count` (show those, never the codes);
|
|
142
144
|
* `not_saved` is rows that were understood and then could not be stored,
|
|
143
145
|
* which the file cannot fix.
|
|
144
146
|
*
|
|
@@ -173,6 +175,22 @@ export class Loop {
|
|
|
173
175
|
* in every message, the answer and its tool calls. Samples kept in your own
|
|
174
176
|
* storage cannot be searched; a search answers how many were left out as
|
|
175
177
|
* `unsearched`.
|
|
178
|
+
*
|
|
179
|
+
* `signal` (`good`, `bad`, `corrected`), `signalled` and `unsignalled` read
|
|
180
|
+
* a PERSON's feedback: the judge's score of an unreviewed sample is not
|
|
181
|
+
* feedback, so a sample it scored stays in `unsignalled`. `judge` splits
|
|
182
|
+
* those unreviewed samples: `picked` (scored 0.8 or more, trained on),
|
|
183
|
+
* `below_bar` (not trained on) and `unscored`; the three add up to
|
|
184
|
+
* `unsignalled`. A row the judge scored carries `judge` (`score`,
|
|
185
|
+
* `picked`).
|
|
186
|
+
*
|
|
187
|
+
* `pipeline` lists what one pipeline trains on (its tags or its datasets,
|
|
188
|
+
* narrowed by its conditions), each row with `state`: what happens to it in
|
|
189
|
+
* that pipeline (`held_back`, `learned`, `trained`, `waiting` or
|
|
190
|
+
* `left_out`, with `reason`, the sentence to show). `dataset` lists what
|
|
191
|
+
* one saved dataset selects; not with `pipeline`. `attempt` (a run id)
|
|
192
|
+
* with `split` (`train` or `held_back`) lists the samples one attempt
|
|
193
|
+
* trained on or held back.
|
|
176
194
|
*/
|
|
177
195
|
async listTraces(params = {}) {
|
|
178
196
|
const q = new URLSearchParams();
|
|
@@ -204,10 +222,18 @@ export class Loop {
|
|
|
204
222
|
q.set('unlabelled', 'true');
|
|
205
223
|
if (params.pipeline)
|
|
206
224
|
q.set('pipeline', params.pipeline);
|
|
225
|
+
if (params.dataset)
|
|
226
|
+
q.set('dataset', params.dataset);
|
|
227
|
+
if (params.attempt)
|
|
228
|
+
q.set('attempt', params.attempt);
|
|
229
|
+
if (params.split)
|
|
230
|
+
q.set('split', params.split);
|
|
207
231
|
if (params.signal)
|
|
208
232
|
q.set('signal', params.signal);
|
|
209
233
|
if (params.unsignalled)
|
|
210
234
|
q.set('unsignalled', 'true');
|
|
235
|
+
if (params.judge)
|
|
236
|
+
q.set('judge', params.judge);
|
|
211
237
|
if (params.limit != null)
|
|
212
238
|
q.set('limit', String(params.limit));
|
|
213
239
|
if (params.offset != null)
|
|
@@ -218,6 +244,12 @@ export class Loop {
|
|
|
218
244
|
/**
|
|
219
245
|
* Read one conversation, with every verdict and tag recorded on it.
|
|
220
246
|
*
|
|
247
|
+
* When the judge scored it, `judge` carries the score and whether it was
|
|
248
|
+
* picked, and `signals` holds the judge's score (source `judge`, verdict
|
|
249
|
+
* `scored`) with `picked`, its per-aspect `metadata.scores` and its
|
|
250
|
+
* `reason`; `reason_unavailable` means the reason could not be read from
|
|
251
|
+
* storage just now. A person's feedback decides over it.
|
|
252
|
+
*
|
|
221
253
|
* A conversation kept in your own storage carries `payload_stored_externally`.
|
|
222
254
|
* When its text could not be read just now it also carries
|
|
223
255
|
* `payload_unavailable`, with `messages` null and `completion` empty: it is
|
|
@@ -367,13 +399,20 @@ export class Loop {
|
|
|
367
399
|
return res.signals;
|
|
368
400
|
}
|
|
369
401
|
/**
|
|
370
|
-
* The pipelines one conversation feeds: every live pipeline whose tags
|
|
371
|
-
* select it, and whether each is
|
|
402
|
+
* The pipelines one conversation feeds: every live pipeline whose tags or
|
|
403
|
+
* datasets select it (and whose conditions keep it), and whether each is
|
|
404
|
+
* switched on.
|
|
372
405
|
*
|
|
373
406
|
* Being listed means the pipeline would select it, not that it will be
|
|
374
407
|
* trained on -- a set still drops answers it cannot use and duplicates, and
|
|
375
|
-
* holds some back to judge by; `note` says so.
|
|
376
|
-
*
|
|
408
|
+
* holds some back to judge by; `note` says so. Each pipeline's `state` says
|
|
409
|
+
* what happens to it there: `held_back` (to test its attempts),
|
|
410
|
+
* `learned` (by version `version_no`), `trained` (by attempt `attempt_no`,
|
|
411
|
+
* which did not go live), `waiting` (for the next attempt) or `left_out`;
|
|
412
|
+
* `reason` is the sentence to show (for `left_out`, why). An empty list is
|
|
413
|
+
* a real answer: the conversation
|
|
414
|
+
* trains nothing until it carries a pipeline's tag or joins one of its
|
|
415
|
+
* datasets.
|
|
377
416
|
*/
|
|
378
417
|
async getTracePipelines(traceId) {
|
|
379
418
|
return this._http.fetchGet(`/api/loop/traces/${encodeURIComponent(traceId)}/pipelines`);
|
|
@@ -481,6 +520,12 @@ export class Loop {
|
|
|
481
520
|
* every call was recorded. Rows are per tag: a call naming two tags is
|
|
482
521
|
* counted under each, so never add them up as calls. One pipeline's share
|
|
483
522
|
* is `capture` on its detail.
|
|
523
|
+
*
|
|
524
|
+
* The workspace's totals: `samples_recent` (recorded in the last 48
|
|
525
|
+
* hours), `feedback` (every sample by the feedback a person gave it: good,
|
|
526
|
+
* bad, corrected or unreviewed; they add up to `traces`) and `pipelines`
|
|
527
|
+
* (how many there are, and how many are training, waiting on you
|
|
528
|
+
* (`needs_you`), serving or paused).
|
|
484
529
|
*/
|
|
485
530
|
async stats() {
|
|
486
531
|
const res = await this._http.fetchGet('/api/loop/stats');
|
|
@@ -491,8 +536,10 @@ export class Loop {
|
|
|
491
536
|
* its attempt number, the version it became (null when it did not), its
|
|
492
537
|
* base model, its win rate, what it cost -- and each day's feedback counted
|
|
493
538
|
* by verdict. `rule_id` narrows the attempts to one pipeline; the feedback
|
|
494
|
-
* counts are the workspace's. `
|
|
495
|
-
* `
|
|
539
|
+
* counts are the workspace's. `samples` is the samples recorded each day
|
|
540
|
+
* over `days`, by their feedback today (`good`, `bad`, `corrected`,
|
|
541
|
+
* `unreviewed`): with `rule_id`, that pipeline's. `truncated` says older
|
|
542
|
+
* attempts exist beyond `limit`.
|
|
496
543
|
*/
|
|
497
544
|
async getMetricsHistory(params = {}) {
|
|
498
545
|
const q = new URLSearchParams();
|
|
@@ -509,13 +556,20 @@ export class Loop {
|
|
|
509
556
|
//
|
|
510
557
|
// Every time a pipeline trains it makes an attempt (a training run). The
|
|
511
558
|
// attempts that went live became its versions.
|
|
512
|
-
/**
|
|
559
|
+
/**
|
|
560
|
+
* Every pipeline's attempts, newest first. Filter by pipeline (`rule_id`),
|
|
561
|
+
* by the state they are sitting in, or by what became of them (`result`,
|
|
562
|
+
* as {@link listPipelineAttempts} takes it). Each row carries `attempt`:
|
|
563
|
+
* the attempt as its pipeline's own list says it, `explanation` included.
|
|
564
|
+
*/
|
|
513
565
|
async listTrainingRuns(params = {}) {
|
|
514
566
|
const q = new URLSearchParams();
|
|
515
567
|
if (params.rule_id)
|
|
516
568
|
q.set('rule_id', params.rule_id);
|
|
517
569
|
if (params.state)
|
|
518
570
|
q.set('state', params.state);
|
|
571
|
+
if (params.result)
|
|
572
|
+
q.set('result', params.result);
|
|
519
573
|
if (params.limit != null)
|
|
520
574
|
q.set('limit', String(params.limit));
|
|
521
575
|
if (params.offset != null)
|
|
@@ -530,7 +584,11 @@ export class Loop {
|
|
|
530
584
|
* `timeline` is written in the same transaction as each state change, so it
|
|
531
585
|
* is the record of what actually happened rather than a reconstruction.
|
|
532
586
|
* `available_actions` is the honest answer to "can I promote this": a button
|
|
533
|
-
* that cannot work should never be offered.
|
|
587
|
+
* that cannot work should never be offered. `explanation` is what became
|
|
588
|
+
* of it in plain words, as on the pipeline's attempts; `run.trained_on` is
|
|
589
|
+
* what it trained on, as it was then (its tags, conditions and each
|
|
590
|
+
* dataset at its revision); `traces_filter_attempt` true means
|
|
591
|
+
* `listTraces({ attempt: id, split })` lists its samples.
|
|
534
592
|
*/
|
|
535
593
|
async getTrainingRun(id) {
|
|
536
594
|
return this._http.fetchGet(`/api/loop/training-runs/${encodeURIComponent(id)}`);
|
|
@@ -582,8 +640,9 @@ export class Loop {
|
|
|
582
640
|
* The workspace's pipelines, a page at a time: its versions (the attempts
|
|
583
641
|
* that became the live model), its latest 5 attempts (`attempts_total` says
|
|
584
642
|
* how many; {@link listPipelineAttempts} pages through every one), which
|
|
585
|
-
* version is live, the attempt in flight,
|
|
586
|
-
* for
|
|
643
|
+
* version is live, the attempt in flight, what the pipeline is waiting
|
|
644
|
+
* for (`minimums`), and how many samples it has recorded (`samples_total`,
|
|
645
|
+
* `samples_recent` in the last 48 hours).
|
|
587
646
|
*
|
|
588
647
|
* `limit` is 100 when absent and at most 100; `q` matches the name, the tag
|
|
589
648
|
* or the model (case-insensitive); `status` keeps the pipelines `training`,
|
|
@@ -623,6 +682,12 @@ export class Loop {
|
|
|
623
682
|
* somebody does (`paused`, with `paused_reason` saying why; `complete` at
|
|
624
683
|
* `max_versions`). The monthly safety limit is the platform's and has no
|
|
625
684
|
* field: when it holds the pipeline, `next_reason` says so.
|
|
685
|
+
*
|
|
686
|
+
* What it trains on: its `tags`, the `datasets` it adds, and the
|
|
687
|
+
* `conditions` that narrow both. `auto_scoring` is the judge's scoring of
|
|
688
|
+
* its unreviewed samples (null where the platform does not score them):
|
|
689
|
+
* `message` is the service's sentence for it, and `minimums.picked_by_judge`
|
|
690
|
+
* how many of the samples it has the judge picked.
|
|
626
691
|
*/
|
|
627
692
|
async getPipeline(id) {
|
|
628
693
|
const res = await this._http.fetchGet(`/api/loop/pipelines/${encodeURIComponent(id)}`);
|
|
@@ -633,7 +698,8 @@ export class Loop {
|
|
|
633
698
|
* it made, winner or not, each the shape `attempts` has on the pipeline
|
|
634
699
|
* (with its `explanation` in plain words). `limit` is 50 when absent and at
|
|
635
700
|
* most 200. Narrow by `result` (`became_version`, `not_better`,
|
|
636
|
-
* `inconclusive
|
|
701
|
+
* `inconclusive` -- tested, could not decide --, `not_tested` -- ended
|
|
702
|
+
* before it was tested --, `failed`, `stopped`, `running` -- which includes one
|
|
637
703
|
* waiting for a decision -- or `waiting_for_review`) and by `model` (`main`,
|
|
638
704
|
* or `second` for the second model's attempts); `total` follows the
|
|
639
705
|
* filters.
|
|
@@ -658,7 +724,8 @@ export class Loop {
|
|
|
658
724
|
// One body -- a name, a model, tags, when to train, how to promote -- and
|
|
659
725
|
// the platform decides the rest: the machines and the limits on each
|
|
660
726
|
// attempt. Saving a pipeline IS the authorization to spend; you pay the GPU
|
|
661
|
-
// time its attempts actually use
|
|
727
|
+
// time its attempts actually use and, where the platform scores samples,
|
|
728
|
+
// the judge's scoring of unreviewed samples.
|
|
662
729
|
/**
|
|
663
730
|
* The base models a pipeline can train: only those the engine trains with
|
|
664
731
|
* LoRA SFT and the platform can serve for the comparison, recommended first.
|
|
@@ -697,12 +764,21 @@ export class Loop {
|
|
|
697
764
|
* version, on what it has not learned plus a replay of what it already
|
|
698
765
|
* learned (about half of what it trains on, picked at random); a winner
|
|
699
766
|
* is merged in.
|
|
700
|
-
* What is charged is the GPU time the
|
|
701
|
-
*
|
|
767
|
+
* What is charged is the GPU time the attempts actually use and, where the
|
|
768
|
+
* platform scores samples, the judge's scoring of unreviewed samples, shown
|
|
769
|
+
* on the attempt it was for; the platform's own per-run and monthly limits
|
|
770
|
+
* stop a run that goes wrong. `holdout.percent`
|
|
702
771
|
* (5 to 20, default 5) is the share of its samples held back to judge each
|
|
703
772
|
* attempt and never trained on. `training` sets any of its training
|
|
704
773
|
* settings (`epochs`, `learning_rate`, `lora_rank`, `lora_alpha`,
|
|
705
774
|
* `replay_percent`); left out, or null, every one is automatic.
|
|
775
|
+
* `datasets` (ids from {@link listDatasets}) adds their samples to what its
|
|
776
|
+
* tags select, and `conditions` narrows both. The judge's scoring of
|
|
777
|
+
* unreviewed samples is on unless `auto_scoring: { enabled: false }`.
|
|
778
|
+
* Where the platform scores samples (the pipeline's `auto_scoring` is not
|
|
779
|
+
* null), while the pipeline is short of the samples its next attempt
|
|
780
|
+
* needs, the judge scores samples nobody reviewed and the ones scoring 0.8
|
|
781
|
+
* or more are trained on; what it cost shows on the attempt it was for.
|
|
706
782
|
*/
|
|
707
783
|
async createPipeline(params) {
|
|
708
784
|
return this._http.fetchPost('/api/loop/pipelines', params);
|
|
@@ -718,7 +794,12 @@ export class Loop {
|
|
|
718
794
|
* `training: null` puts every one back to automatic; the next attempt
|
|
719
795
|
* trains with them, and earlier attempts keep the settings they trained
|
|
720
796
|
* with. Out of range is 422 `TRAINING_SETTING_OUT_OF_RANGE`, naming the
|
|
721
|
-
* field.
|
|
797
|
+
* field. `datasets` (`[]` takes every one off) and `conditions` (null
|
|
798
|
+
* clears them) change what it trains on from the next attempt: nothing is
|
|
799
|
+
* trained again, versions already made keep what they learned, and to make
|
|
800
|
+
* the live version forget samples, delete them ({@link deleteTrace}).
|
|
801
|
+
* `auto_scoring: { enabled: false }` stops the judge's scoring of
|
|
802
|
+
* unreviewed samples. Saving is the authorization, as on a create.
|
|
722
803
|
*/
|
|
723
804
|
async updatePipeline(id, params) {
|
|
724
805
|
return this._http.fetchPut(`/api/loop/pipelines/${encodeURIComponent(id)}`, params);
|
|
@@ -763,6 +844,76 @@ export class Loop {
|
|
|
763
844
|
async importIntoPipeline(id, params) {
|
|
764
845
|
return this._http.fetchPost(`/api/loop/pipelines/${encodeURIComponent(id)}/import`, params);
|
|
765
846
|
}
|
|
847
|
+
// ── datasets ──────────────────────────────────────────────────────────
|
|
848
|
+
//
|
|
849
|
+
// A dataset is a saved, named set of conditions -- tags (any or all of
|
|
850
|
+
// them), feedback, where samples came from, which model answered, when
|
|
851
|
+
// they were recorded -- and a live selection, not a copy: samples that
|
|
852
|
+
// match later join it. Every pipeline already trains on its own tag; add a
|
|
853
|
+
// dataset to a pipeline (`datasets` on create or edit) to train it on more.
|
|
854
|
+
// One dataset can feed any number of pipelines.
|
|
855
|
+
/** The workspace's datasets, the most recently changed first: `{ datasets, total, limit, offset }`. */
|
|
856
|
+
async listDatasets(params = {}) {
|
|
857
|
+
const q = new URLSearchParams();
|
|
858
|
+
if (params.limit != null)
|
|
859
|
+
q.set('limit', String(params.limit));
|
|
860
|
+
if (params.offset)
|
|
861
|
+
q.set('offset', String(params.offset));
|
|
862
|
+
const qs = q.toString();
|
|
863
|
+
const res = await this._http.fetchGet(`/api/loop/datasets${qs ? `?${qs}` : ''}`);
|
|
864
|
+
const datasets = res.datasets || [];
|
|
865
|
+
return { ...res, datasets, total: typeof res.total === 'number' ? res.total : datasets.length };
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* One dataset: its conditions, how many samples it selects now (`samples`,
|
|
869
|
+
* the same number `listTraces({ dataset: id })` totals) by feedback, how
|
|
870
|
+
* many are in a benchmark (never trained on), and the pipelines that train
|
|
871
|
+
* on it (`used_by`).
|
|
872
|
+
*/
|
|
873
|
+
async getDataset(id) {
|
|
874
|
+
return this._http.fetchGet(`/api/loop/datasets/${encodeURIComponent(id)}`);
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Save a dataset: a `name` (unique in the workspace, whatever its case:
|
|
878
|
+
* 409 `DATASET_NAME_TAKEN`) and any of its conditions. Nothing trains on
|
|
879
|
+
* it until a pipeline adds it.
|
|
880
|
+
*/
|
|
881
|
+
async createDataset(params) {
|
|
882
|
+
return this._http.fetchPost('/api/loop/datasets', params);
|
|
883
|
+
}
|
|
884
|
+
/**
|
|
885
|
+
* Change a dataset: only the fields sent change (null clears `origin`,
|
|
886
|
+
* `model`, `from` or `to`; `[]` clears `tags` or `feedback`). Pass
|
|
887
|
+
* `expected_revision` to be refused 409 `REVISION_MISMATCH` rather than
|
|
888
|
+
* overwrite someone else's edit. Every pipeline in `used_by` trains on the
|
|
889
|
+
* new selection from its next attempt: samples that join count as new
|
|
890
|
+
* samples, nothing is trained again, and versions already made keep what
|
|
891
|
+
* they learned.
|
|
892
|
+
*/
|
|
893
|
+
async updateDataset(id, params) {
|
|
894
|
+
return this._http.fetchPut(`/api/loop/datasets/${encodeURIComponent(id)}`, params);
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Delete a dataset. Its samples stay. Every pipeline that trained on it
|
|
898
|
+
* (`removed_from`) stops taking its samples from its next attempt; versions
|
|
899
|
+
* already made keep what they learned. It cannot be undone.
|
|
900
|
+
*/
|
|
901
|
+
async deleteDataset(id) {
|
|
902
|
+
const res = await this._http.fetchDelete(`/api/loop/datasets/${encodeURIComponent(id)}`);
|
|
903
|
+
return { ...res, removed_from: res.removed_from || [] };
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Count what a set of conditions selects, before saving anything: the
|
|
907
|
+
* samples, by feedback, how many are in a benchmark, and the models that
|
|
908
|
+
* answered them. `datasets` adds saved datasets' samples, as a pipeline
|
|
909
|
+
* does, so a pipeline's tags, datasets and conditions can be counted in
|
|
910
|
+
* one call. The counts are before duplicates and over-long conversations
|
|
911
|
+
* are set aside; a pipeline's own `minimums` are the exact training
|
|
912
|
+
* numbers. It writes nothing.
|
|
913
|
+
*/
|
|
914
|
+
async previewDataset(params = {}) {
|
|
915
|
+
return this._http.fetchPost('/api/loop/datasets/preview', params);
|
|
916
|
+
}
|
|
766
917
|
// ── a pipeline's benchmarks, and its versions side by side ─────────────
|
|
767
918
|
/** The pipeline's benchmarks in priority order (1 is the primary), the ones it stopped using, and its promotion policy. */
|
|
768
919
|
async listPipelineBenchmarks(id) {
|
|
@@ -805,7 +956,7 @@ export class Loop {
|
|
|
805
956
|
* and `.b` are each attempt's mean and pass rate over those same
|
|
806
957
|
* conversations -- the like-for-like figures; each side's `holdout` is over
|
|
807
958
|
* its own, possibly larger, set. When the two were scored differently
|
|
808
|
-
* (another
|
|
959
|
+
* (another judge or rubric), `shared_sample.comparable` is false,
|
|
809
960
|
* `why` says so, no direction is given and `filter` is refused (400); a
|
|
810
961
|
* conversation whose right answer changed between them has no direction
|
|
811
962
|
* either. Numbers and ids only: read an answer with `listEvaluationItems`.
|