@lmnr-ai/client 0.8.54 → 0.8.55

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/dist/index.d.mts CHANGED
@@ -32,6 +32,12 @@ declare class BaseResource {
32
32
  Accept: string;
33
33
  };
34
34
  protected handleError(response: Response): Promise<void>;
35
+ /**
36
+ * For routes that answer errors as `{error: "<message>"}` with user-facing
37
+ * text: raise that message instead of the raw JSON body, falling back to the
38
+ * body when it isn't that shape (a proxy error page, a plain-text 413).
39
+ */
40
+ protected handleErrorMessage(response: Response): Promise<never>;
35
41
  }
36
42
  declare class BrowserEventsResource extends BaseResource {
37
43
  constructor(baseHttpUrl: string, auth: LaminarAuth);
@@ -304,6 +310,59 @@ declare class EvaluatorsResource extends BaseResource {
304
310
  */
305
311
  score(options: ScoreOptions): Promise<void>;
306
312
  }
313
+ /** Starts a remote run. See {@link HostedEvalsResource.startRun}. */
314
+ interface StartHostedEvalRunOptions {
315
+ /** Id returned by {@link HostedEvalsResource.upload}. */
316
+ uploadId: string;
317
+ /** The orchestrator runs `lmnr-cli@<cliVersion>`, so the spec format matches. */
318
+ cliVersion: string;
319
+ /**
320
+ * Run spec written by lmnr-cli and read back by the same CLI version. Sent
321
+ * as JSON; the server passes it through without reading it.
322
+ */
323
+ spec: object;
324
+ }
325
+ /**
326
+ * Hosted evals: Harbor evals with trials in Laminar-managed sandboxes.
327
+ *
328
+ * - Images: `missingLayers` / `uploadLayer` / `createImage` push an image built
329
+ * locally (layers named by their `diff_id`); trials start from it.
330
+ * - Remote runs: `upload` the `.tar.gz` of the evals folder, then `startRun`
331
+ * to orchestrate it in a Laminar sandbox. Results land in the project as
332
+ * regular evaluations; v0 returns no run status.
333
+ */
334
+ declare class HostedEvalsResource extends BaseResource {
335
+ constructor(baseHttpUrl: string, auth: LaminarAuth);
336
+ /** The image layers (by `diff_id`) the project hasn't uploaded yet. */
337
+ missingLayers(diffIds: string[]): Promise<string[]>;
338
+ /**
339
+ * Upload one image layer, gzip-compressed. `diffId` is the digest of its
340
+ * uncompressed tar, as listed in the image config's `rootfs.diff_ids`.
341
+ */
342
+ uploadLayer(diffId: string, gzipped: ReadableStream<Uint8Array>): Promise<{
343
+ digest: string;
344
+ size: number;
345
+ }>;
346
+ /**
347
+ * Push the image described by `config` (its config JSON, byte for byte) once
348
+ * its layers are uploaded. Returns the manifest digest trials start from.
349
+ */
350
+ createImage(config: string): Promise<{
351
+ image: string;
352
+ }>;
353
+ /**
354
+ * Upload a `.tar.gz` of the `evals/` folder's contents (suites at the archive
355
+ * root). Sent as the raw request body; the server caps it at 1 GiB.
356
+ */
357
+ upload(archive: Uint8Array): Promise<{
358
+ uploadId: string;
359
+ }>;
360
+ /** Start a remote run. Returns once its orchestrator sandbox has started. */
361
+ startRun(options: StartHostedEvalRunOptions): Promise<{
362
+ sandboxId: string;
363
+ }>;
364
+ private url;
365
+ }
307
366
  /**
308
367
  * Plaintext secrets for create/update. On update this is an OVERLAY: an
309
368
  * omitted key keeps the stored secret, so credentials never need re-sending.
@@ -342,12 +401,6 @@ interface UpdateLlmProfileOptions {
342
401
  */
343
402
  declare class LlmProfilesResource extends BaseResource {
344
403
  constructor(baseHttpUrl: string, auth: LaminarAuth);
345
- /**
346
- * Errors come back as `{error: string}` (matches the signals surface) with
347
- * user-facing text, so unwrap the envelope before raising rather than
348
- * showing the raw JSON body.
349
- */
350
- private raiseLlmProfileError;
351
404
  private request;
352
405
  /**
353
406
  * Every workspace LLM profile visible to the caller, ordered
@@ -503,17 +556,13 @@ interface UpdateSignalOptions {
503
556
  llmProfileId?: string;
504
557
  model?: string;
505
558
  }
506
- /** Signals CRUD over the CLI user-token surface (`/v1/cli/signals`). */
559
+ /**
560
+ * Signals CRUD over the CLI user-token surface (`/v1/cli/signals`). Errors come
561
+ * back as `{error}` with user-facing text (e.g. "A signal named X already
562
+ * exists in this project"), raised verbatim by `handleErrorMessage`.
563
+ */
507
564
  declare class SignalsResource extends BaseResource {
508
565
  constructor(baseHttpUrl: string, auth: LaminarAuth);
509
- /**
510
- * Every signal route answers errors as `{ error: "<message>" }`, and those
511
- * messages are written to be shown to the user verbatim (e.g. "A signal named
512
- * X already exists in this project"). The shared `handleError` would surface
513
- * the raw JSON body, so unwrap the envelope here and fall back to it when the
514
- * body isn't the expected shape.
515
- */
516
- private raiseSignalError;
517
566
  list(name?: string): Promise<Signal[]>;
518
567
  get(signalId: string): Promise<Signal>;
519
568
  /**
@@ -652,6 +701,7 @@ export declare class LaminarClient {
652
701
  private _datasets;
653
702
  private _evals;
654
703
  private _evaluators;
704
+ private _hostedEvals;
655
705
  private _llmProfiles;
656
706
  private _rolloutSessions;
657
707
  private _signals;
@@ -719,6 +769,7 @@ export declare class LaminarClient {
719
769
  get datasets(): DatasetsResource;
720
770
  get evals(): EvalsResource;
721
771
  get evaluators(): EvaluatorsResource;
772
+ get hostedEvals(): HostedEvalsResource;
722
773
  get llmProfiles(): LlmProfilesResource;
723
774
  get signals(): SignalsResource;
724
775
  get rolloutSessions(): RolloutSessionsResource;
@@ -726,5 +777,5 @@ export declare class LaminarClient {
726
777
  get tags(): TagsResource;
727
778
  get traces(): TracesResource;
728
779
  }
729
- export type { CacheOutcome, CliProject, CreateLlmProfileOptions, CreateSignalOptions, LaminarAuth, LlmProfileSecretsInput, ProjectKeyProbe, UpdateLlmProfileOptions, UpdateSignalOptions };
780
+ export type { CacheOutcome, CliProject, CreateLlmProfileOptions, CreateSignalOptions, LaminarAuth, LlmProfileSecretsInput, ProjectKeyProbe, StartHostedEvalRunOptions, UpdateLlmProfileOptions, UpdateSignalOptions };
730
781
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -5,7 +5,7 @@ import { PinoPretty } from "pino-pretty";
5
5
  import { v4 } from "uuid";
6
6
  import { errorMessage } from "@lmnr-ai/types";
7
7
  // package.json
8
- var version = "0.8.54";
8
+ var version = "0.8.55";
9
9
  // src/version.ts
10
10
  function getLangVersion() {
11
11
  if (typeof process !== "undefined" && process.versions && process.versions.node) return `node-${process.versions.node}`;
@@ -35,6 +35,20 @@ var BaseResource = class {
35
35
  const errorMsg = await response.text();
36
36
  throw new Error(`${response.status} ${errorMsg}`);
37
37
  }
38
+ /**
39
+ * For routes that answer errors as `{error: "<message>"}` with user-facing
40
+ * text: raise that message instead of the raw JSON body, falling back to the
41
+ * body when it isn't that shape (a proxy error page, a plain-text 413).
42
+ */
43
+ async handleErrorMessage(response) {
44
+ const body = await response.text();
45
+ let message = body;
46
+ try {
47
+ const parsed = JSON.parse(body);
48
+ if (typeof parsed?.error === "string" && parsed.error.length > 0) message = parsed.error;
49
+ } catch {}
50
+ throw new Error(`${response.status} ${message}`);
51
+ }
38
52
  };
39
53
  // src/resources/browser-events.ts
40
54
  var BrowserEventsResource = class extends BaseResource {
@@ -592,6 +606,90 @@ var EvaluatorsResource = class extends BaseResource {
592
606
  if (!response.ok) await this.handleError(response);
593
607
  }
594
608
  };
609
+ // src/resources/hosted-evals.ts
610
+ /**
611
+ * Hosted evals: Harbor evals with trials in Laminar-managed sandboxes.
612
+ *
613
+ * - Images: `missingLayers` / `uploadLayer` / `createImage` push an image built
614
+ * locally (layers named by their `diff_id`); trials start from it.
615
+ * - Remote runs: `upload` the `.tar.gz` of the evals folder, then `startRun`
616
+ * to orchestrate it in a Laminar sandbox. Results land in the project as
617
+ * regular evaluations; v0 returns no run status.
618
+ */
619
+ var HostedEvalsResource = class extends BaseResource {
620
+ constructor(baseHttpUrl, auth) {
621
+ super(baseHttpUrl, auth);
622
+ }
623
+ /** The image layers (by `diff_id`) the project hasn't uploaded yet. */
624
+ async missingLayers(diffIds) {
625
+ const response = await fetch(this.url("/images/layers/missing"), {
626
+ method: "POST",
627
+ headers: this.headers(),
628
+ body: JSON.stringify({ diffIds })
629
+ });
630
+ if (!response.ok) await this.handleErrorMessage(response);
631
+ return (await response.json()).missing;
632
+ }
633
+ /**
634
+ * Upload one image layer, gzip-compressed. `diffId` is the digest of its
635
+ * uncompressed tar, as listed in the image config's `rootfs.diff_ids`.
636
+ */
637
+ async uploadLayer(diffId, gzipped) {
638
+ const response = await fetch(this.url(`/images/layers/${diffId}`), {
639
+ method: "PUT",
640
+ headers: {
641
+ ...this.headers(),
642
+ "Content-Type": "application/gzip"
643
+ },
644
+ body: gzipped,
645
+ duplex: "half"
646
+ });
647
+ if (!response.ok) await this.handleErrorMessage(response);
648
+ return await response.json();
649
+ }
650
+ /**
651
+ * Push the image described by `config` (its config JSON, byte for byte) once
652
+ * its layers are uploaded. Returns the manifest digest trials start from.
653
+ */
654
+ async createImage(config) {
655
+ const response = await fetch(this.url("/images"), {
656
+ method: "POST",
657
+ headers: this.headers(),
658
+ body: JSON.stringify({ config })
659
+ });
660
+ if (!response.ok) await this.handleErrorMessage(response);
661
+ return await response.json();
662
+ }
663
+ /**
664
+ * Upload a `.tar.gz` of the `evals/` folder's contents (suites at the archive
665
+ * root). Sent as the raw request body; the server caps it at 1 GiB.
666
+ */
667
+ async upload(archive) {
668
+ const response = await fetch(this.url("/uploads"), {
669
+ method: "POST",
670
+ headers: {
671
+ ...this.headers(),
672
+ "Content-Type": "application/gzip"
673
+ },
674
+ body: archive
675
+ });
676
+ if (!response.ok) await this.handleErrorMessage(response);
677
+ return await response.json();
678
+ }
679
+ /** Start a remote run. Returns once its orchestrator sandbox has started. */
680
+ async startRun(options) {
681
+ const response = await fetch(this.url("/runs"), {
682
+ method: "POST",
683
+ headers: this.headers(),
684
+ body: JSON.stringify(options)
685
+ });
686
+ if (!response.ok) await this.handleErrorMessage(response);
687
+ return await response.json();
688
+ }
689
+ url(path) {
690
+ return `${this.baseHttpUrl}${this.apiPrefix}/hosted-evals${path}`;
691
+ }
692
+ };
595
693
  // src/resources/llm-profiles.ts
596
694
  /**
597
695
  * Workspace LLM profile CRUD over the CLI user-token surface
@@ -603,27 +701,13 @@ var LlmProfilesResource = class extends BaseResource {
603
701
  constructor(baseHttpUrl, auth) {
604
702
  super(baseHttpUrl, auth);
605
703
  }
606
- /**
607
- * Errors come back as `{error: string}` (matches the signals surface) with
608
- * user-facing text, so unwrap the envelope before raising rather than
609
- * showing the raw JSON body.
610
- */
611
- async raiseLlmProfileError(response) {
612
- const body = await response.text();
613
- let message = body;
614
- try {
615
- const parsed = JSON.parse(body);
616
- if (typeof parsed?.error === "string" && parsed.error.length > 0) message = parsed.error;
617
- } catch {}
618
- throw new Error(`${response.status} ${message}`);
619
- }
620
704
  async request(method, path, body) {
621
705
  const response = await fetch(`${this.baseHttpUrl}${this.apiPrefix}/llm-profiles${path}`, {
622
706
  method,
623
707
  headers: this.headers(),
624
708
  ...body !== void 0 ? { body: JSON.stringify(body) } : {}
625
709
  });
626
- if (!response.ok) await this.raiseLlmProfileError(response);
710
+ if (!response.ok) await this.handleErrorMessage(response);
627
711
  return response;
628
712
  }
629
713
  /**
@@ -841,34 +925,22 @@ var RolloutSessionsResource = class extends BaseResource {
841
925
  }
842
926
  };
843
927
  // src/resources/signals.ts
844
- /** Signals CRUD over the CLI user-token surface (`/v1/cli/signals`). */
928
+ /**
929
+ * Signals CRUD over the CLI user-token surface (`/v1/cli/signals`). Errors come
930
+ * back as `{error}` with user-facing text (e.g. "A signal named X already
931
+ * exists in this project"), raised verbatim by `handleErrorMessage`.
932
+ */
845
933
  var SignalsResource = class extends BaseResource {
846
934
  constructor(baseHttpUrl, auth) {
847
935
  super(baseHttpUrl, auth);
848
936
  }
849
- /**
850
- * Every signal route answers errors as `{ error: "<message>" }`, and those
851
- * messages are written to be shown to the user verbatim (e.g. "A signal named
852
- * X already exists in this project"). The shared `handleError` would surface
853
- * the raw JSON body, so unwrap the envelope here and fall back to it when the
854
- * body isn't the expected shape.
855
- */
856
- async raiseSignalError(response) {
857
- const body = await response.text();
858
- let message = body;
859
- try {
860
- const parsed = JSON.parse(body);
861
- if (typeof parsed?.error === "string" && parsed.error.length > 0) message = parsed.error;
862
- } catch {}
863
- throw new Error(`${response.status} ${message}`);
864
- }
865
937
  async list(name) {
866
938
  const query = name ? `?${new URLSearchParams({ name }).toString()}` : "";
867
939
  const response = await fetch(`${this.baseHttpUrl}${this.apiPrefix}/signals${query}`, {
868
940
  method: "GET",
869
941
  headers: this.headers()
870
942
  });
871
- if (!response.ok) await this.raiseSignalError(response);
943
+ if (!response.ok) await this.handleErrorMessage(response);
872
944
  const body = await response.json();
873
945
  return Array.isArray(body?.signals) ? body.signals : [];
874
946
  }
@@ -877,7 +949,7 @@ var SignalsResource = class extends BaseResource {
877
949
  method: "GET",
878
950
  headers: this.headers()
879
951
  });
880
- if (!response.ok) await this.raiseSignalError(response);
952
+ if (!response.ok) await this.handleErrorMessage(response);
881
953
  return response.json();
882
954
  }
883
955
  /**
@@ -891,7 +963,7 @@ var SignalsResource = class extends BaseResource {
891
963
  method: "GET",
892
964
  headers: this.headers()
893
965
  });
894
- if (!response.ok) await this.raiseSignalError(response);
966
+ if (!response.ok) await this.handleErrorMessage(response);
895
967
  const body = await response.json();
896
968
  return Array.isArray(body?.versions) ? body.versions : [];
897
969
  }
@@ -901,7 +973,7 @@ var SignalsResource = class extends BaseResource {
901
973
  headers: this.headers(),
902
974
  body: JSON.stringify(options)
903
975
  });
904
- if (!response.ok) await this.raiseSignalError(response);
976
+ if (!response.ok) await this.handleErrorMessage(response);
905
977
  return response.json();
906
978
  }
907
979
  /**
@@ -915,7 +987,7 @@ var SignalsResource = class extends BaseResource {
915
987
  headers: this.headers(),
916
988
  body: JSON.stringify(options)
917
989
  });
918
- if (!response.ok) await this.raiseSignalError(response);
990
+ if (!response.ok) await this.handleErrorMessage(response);
919
991
  return response.json();
920
992
  }
921
993
  /** Deletes the signal, its triggers, its alerts, and its ClickHouse events. */
@@ -924,7 +996,7 @@ var SignalsResource = class extends BaseResource {
924
996
  method: "DELETE",
925
997
  headers: this.headers()
926
998
  });
927
- if (!response.ok) await this.raiseSignalError(response);
999
+ if (!response.ok) await this.handleErrorMessage(response);
928
1000
  return response.json();
929
1001
  }
930
1002
  /**
@@ -938,7 +1010,7 @@ var SignalsResource = class extends BaseResource {
938
1010
  headers: this.headers(),
939
1011
  body: JSON.stringify({ traceIds })
940
1012
  });
941
- if (!response.ok) await this.raiseSignalError(response);
1013
+ if (!response.ok) await this.handleErrorMessage(response);
942
1014
  return response.json();
943
1015
  }
944
1016
  };
@@ -1123,6 +1195,7 @@ var LaminarClient = class LaminarClient {
1123
1195
  this._datasets = new DatasetsResource(this.baseUrl, this.auth);
1124
1196
  this._evals = new EvalsResource(this.baseUrl, this.auth);
1125
1197
  this._evaluators = new EvaluatorsResource(this.baseUrl, this.auth);
1198
+ this._hostedEvals = new HostedEvalsResource(this.baseUrl, this.auth);
1126
1199
  this._llmProfiles = new LlmProfilesResource(this.baseUrl, this.auth);
1127
1200
  this._rolloutSessions = new RolloutSessionsResource(this.baseUrl, this.auth);
1128
1201
  this._signals = new SignalsResource(this.baseUrl, this.auth);
@@ -1194,6 +1267,9 @@ var LaminarClient = class LaminarClient {
1194
1267
  get evaluators() {
1195
1268
  return this._evaluators;
1196
1269
  }
1270
+ get hostedEvals() {
1271
+ return this._hostedEvals;
1272
+ }
1197
1273
  get llmProfiles() {
1198
1274
  return this._llmProfiles;
1199
1275
  }