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.
@@ -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 switched on.
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. An empty list is a real
376
- * answer: the conversation trains nothing until it carries a pipeline's tag.
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. `truncated` says older attempts exist beyond
495
- * `limit`.
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
- /** Attempts, newest first. Filter by pipeline (`rule_id`) or by the state they are sitting in. */
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, and what the pipeline is waiting
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`, `failed`, `stopped`, `running` -- which includes one
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 runs actually use; the platform's own
701
- * per-run and monthly limits stop a run that goes wrong. `holdout.percent`
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. Saving is the authorization, as on a create.
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 scoring model or rubric), `shared_sample.comparable` is false,
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`.