@lmnr-ai/client 0.8.26 → 0.8.28
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.cjs +128 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -32
- package/dist/index.d.mts +71 -32
- package/dist/index.mjs +128 -53
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Datapoint, Dataset, EvaluationDatapoint, GetDatapointsResponse, InitEvaluationResponse, PushDatapointsResponse,
|
|
1
|
+
import { CachedSpan, Datapoint, Dataset, EvaluationDatapoint, GetDatapointsResponse, InitEvaluationResponse, PushDatapointsResponse, StringUUID } from "@lmnr-ai/types";
|
|
2
2
|
|
|
3
3
|
//#region src/resources/index.d.ts
|
|
4
4
|
declare class BaseResource {
|
|
@@ -257,49 +257,83 @@ declare class EvaluatorsResource extends BaseResource {
|
|
|
257
257
|
}
|
|
258
258
|
//#endregion
|
|
259
259
|
//#region src/resources/rollout-sessions.d.ts
|
|
260
|
+
/**
|
|
261
|
+
* Result of a debug-replay cache lookup (debug-replay v2).
|
|
262
|
+
*
|
|
263
|
+
* - `hit` — the server served a cached response for this input hash; replay it
|
|
264
|
+
* and mark the span CACHED.
|
|
265
|
+
* - `miss` — no cache entry; the caller latches process-wide live mode and runs
|
|
266
|
+
* every subsequent call live.
|
|
267
|
+
* - `live` — run THIS call live without latching (server COLD warmup-timeout
|
|
268
|
+
* degrade, or any non-OK / transport error here).
|
|
269
|
+
*/
|
|
270
|
+
type CacheOutcome = {
|
|
271
|
+
kind: "hit";
|
|
272
|
+
cached: CachedSpan;
|
|
273
|
+
} | {
|
|
274
|
+
kind: "miss";
|
|
275
|
+
} | {
|
|
276
|
+
kind: "live";
|
|
277
|
+
};
|
|
260
278
|
declare class RolloutSessionsResource extends BaseResource {
|
|
261
279
|
constructor(baseHttpUrl: string, projectApiKey: string);
|
|
262
280
|
/**
|
|
263
|
-
*
|
|
264
|
-
*
|
|
281
|
+
* Idempotently register (upsert) a debug session on the backend, keyed on the
|
|
282
|
+
* SDK-supplied session id. The backend stores the row so the session is
|
|
283
|
+
* visible in the UI; a null/omitted name never clobbers a name set elsewhere.
|
|
284
|
+
*
|
|
285
|
+
* Returns the backend-resolved `projectId` (derived from the API key) so the
|
|
286
|
+
* caller can build the debugger URL; null if the body can't be parsed.
|
|
265
287
|
*/
|
|
266
|
-
|
|
288
|
+
register({
|
|
267
289
|
sessionId,
|
|
268
|
-
name
|
|
269
|
-
params,
|
|
270
|
-
signal
|
|
290
|
+
name
|
|
271
291
|
}: {
|
|
272
292
|
sessionId: string;
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
293
|
+
name?: string;
|
|
294
|
+
}): Promise<string | null>;
|
|
295
|
+
/**
|
|
296
|
+
* Rename an existing debug session. Update-only: the backend returns 404 (and
|
|
297
|
+
* this throws) when the session id is unknown for the project, so a mistyped
|
|
298
|
+
* id surfaces as an error rather than silently creating a session. Creation
|
|
299
|
+
* stays the SDK's job via {@link register}.
|
|
300
|
+
*/
|
|
301
|
+
setName({
|
|
302
|
+
sessionId,
|
|
303
|
+
name
|
|
279
304
|
}: {
|
|
280
305
|
sessionId: string;
|
|
306
|
+
name: string;
|
|
281
307
|
}): Promise<void>;
|
|
282
|
-
|
|
308
|
+
/**
|
|
309
|
+
* Look up the debug-replay cache for a single LLM call (debug-replay v2).
|
|
310
|
+
*
|
|
311
|
+
* The server is keyed by `inputHash` (hex blake3 of the canonicalized,
|
|
312
|
+
* system-stripped input messages). It returns one of three outcomes:
|
|
313
|
+
* - `{ outcome: "hit", response }` — a cached response to replay.
|
|
314
|
+
* - `{ outcome: "miss" }` — no entry; caller latches live mode.
|
|
315
|
+
* - `{ outcome: "live" }` — run this call live (COLD degrade).
|
|
316
|
+
*
|
|
317
|
+
* Error posture: a non-OK response or a transport error degrades to
|
|
318
|
+
* `{ kind: "live" }` for THIS call only — it never throws and never latches
|
|
319
|
+
* the process-wide live flag (only a real MISS does that). This keeps a flaky
|
|
320
|
+
* cache backend from turning a replay into a crash.
|
|
321
|
+
*/
|
|
322
|
+
cache({
|
|
283
323
|
sessionId,
|
|
284
|
-
|
|
324
|
+
replayTraceId,
|
|
325
|
+
cacheUntil,
|
|
326
|
+
inputHash
|
|
285
327
|
}: {
|
|
286
328
|
sessionId: string;
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
329
|
+
replayTraceId: string;
|
|
330
|
+
cacheUntil: string;
|
|
331
|
+
inputHash: string;
|
|
332
|
+
}): Promise<CacheOutcome>;
|
|
333
|
+
delete({
|
|
334
|
+
sessionId
|
|
292
335
|
}: {
|
|
293
336
|
sessionId: string;
|
|
294
|
-
span: {
|
|
295
|
-
name: string;
|
|
296
|
-
startTime: string;
|
|
297
|
-
spanId: string;
|
|
298
|
-
traceId: string;
|
|
299
|
-
parentSpanId: string | undefined;
|
|
300
|
-
attributes: Record<string, any>;
|
|
301
|
-
spanType: SpanType;
|
|
302
|
-
};
|
|
303
337
|
}): Promise<void>;
|
|
304
338
|
}
|
|
305
339
|
//#endregion
|
|
@@ -371,13 +405,16 @@ declare class TracesResource extends BaseResource {
|
|
|
371
405
|
*
|
|
372
406
|
* A 404 response (the trace was not found in the project — typically because
|
|
373
407
|
* it has not been flushed yet) is logged as a warning and the call returns
|
|
374
|
-
* without throwing
|
|
408
|
+
* without throwing, since the 404 may be expected when pushing too soon
|
|
409
|
+
* after the trace run. Pass `failOnNotFound: true` to throw instead (e.g.
|
|
410
|
+
* CLI callers that must report the failure). Any other non-OK status throws.
|
|
375
411
|
*
|
|
376
412
|
* @param traceId - The trace id to push metadata to. Accepts a UUID string
|
|
377
413
|
* or a 32-char OTel hex trace id.
|
|
378
414
|
* @param metadata - The metadata patch. Top-level keys are merged into the
|
|
379
415
|
* trace's existing metadata. Must be non-empty (the server rejects empty
|
|
380
416
|
* patches with 400).
|
|
417
|
+
* @param options - `failOnNotFound`: throw on 404 instead of warn-and-return.
|
|
381
418
|
* @example
|
|
382
419
|
* ```typescript
|
|
383
420
|
* import { Laminar, observe, LaminarClient } from "@lmnr-ai/lmnr";
|
|
@@ -399,7 +436,9 @@ declare class TracesResource extends BaseResource {
|
|
|
399
436
|
* }
|
|
400
437
|
* ```
|
|
401
438
|
*/
|
|
402
|
-
pushMetadata(traceId: string, metadata: Record<string, unknown
|
|
439
|
+
pushMetadata(traceId: string, metadata: Record<string, unknown>, options?: {
|
|
440
|
+
failOnNotFound?: boolean;
|
|
441
|
+
}): Promise<void>;
|
|
403
442
|
}
|
|
404
443
|
//#endregion
|
|
405
444
|
//#region src/index.d.ts
|
|
@@ -433,5 +472,5 @@ declare class LaminarClient {
|
|
|
433
472
|
get traces(): TracesResource;
|
|
434
473
|
}
|
|
435
474
|
//#endregion
|
|
436
|
-
export { LaminarClient };
|
|
475
|
+
export { type CacheOutcome, LaminarClient, RolloutSessionsResource };
|
|
437
476
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Datapoint, Dataset, EvaluationDatapoint, GetDatapointsResponse, InitEvaluationResponse, PushDatapointsResponse,
|
|
1
|
+
import { CachedSpan, Datapoint, Dataset, EvaluationDatapoint, GetDatapointsResponse, InitEvaluationResponse, PushDatapointsResponse, StringUUID } from "@lmnr-ai/types";
|
|
2
2
|
|
|
3
3
|
//#region src/resources/index.d.ts
|
|
4
4
|
declare class BaseResource {
|
|
@@ -257,49 +257,83 @@ declare class EvaluatorsResource extends BaseResource {
|
|
|
257
257
|
}
|
|
258
258
|
//#endregion
|
|
259
259
|
//#region src/resources/rollout-sessions.d.ts
|
|
260
|
+
/**
|
|
261
|
+
* Result of a debug-replay cache lookup (debug-replay v2).
|
|
262
|
+
*
|
|
263
|
+
* - `hit` — the server served a cached response for this input hash; replay it
|
|
264
|
+
* and mark the span CACHED.
|
|
265
|
+
* - `miss` — no cache entry; the caller latches process-wide live mode and runs
|
|
266
|
+
* every subsequent call live.
|
|
267
|
+
* - `live` — run THIS call live without latching (server COLD warmup-timeout
|
|
268
|
+
* degrade, or any non-OK / transport error here).
|
|
269
|
+
*/
|
|
270
|
+
type CacheOutcome = {
|
|
271
|
+
kind: "hit";
|
|
272
|
+
cached: CachedSpan;
|
|
273
|
+
} | {
|
|
274
|
+
kind: "miss";
|
|
275
|
+
} | {
|
|
276
|
+
kind: "live";
|
|
277
|
+
};
|
|
260
278
|
declare class RolloutSessionsResource extends BaseResource {
|
|
261
279
|
constructor(baseHttpUrl: string, projectApiKey: string);
|
|
262
280
|
/**
|
|
263
|
-
*
|
|
264
|
-
*
|
|
281
|
+
* Idempotently register (upsert) a debug session on the backend, keyed on the
|
|
282
|
+
* SDK-supplied session id. The backend stores the row so the session is
|
|
283
|
+
* visible in the UI; a null/omitted name never clobbers a name set elsewhere.
|
|
284
|
+
*
|
|
285
|
+
* Returns the backend-resolved `projectId` (derived from the API key) so the
|
|
286
|
+
* caller can build the debugger URL; null if the body can't be parsed.
|
|
265
287
|
*/
|
|
266
|
-
|
|
288
|
+
register({
|
|
267
289
|
sessionId,
|
|
268
|
-
name
|
|
269
|
-
params,
|
|
270
|
-
signal
|
|
290
|
+
name
|
|
271
291
|
}: {
|
|
272
292
|
sessionId: string;
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
293
|
+
name?: string;
|
|
294
|
+
}): Promise<string | null>;
|
|
295
|
+
/**
|
|
296
|
+
* Rename an existing debug session. Update-only: the backend returns 404 (and
|
|
297
|
+
* this throws) when the session id is unknown for the project, so a mistyped
|
|
298
|
+
* id surfaces as an error rather than silently creating a session. Creation
|
|
299
|
+
* stays the SDK's job via {@link register}.
|
|
300
|
+
*/
|
|
301
|
+
setName({
|
|
302
|
+
sessionId,
|
|
303
|
+
name
|
|
279
304
|
}: {
|
|
280
305
|
sessionId: string;
|
|
306
|
+
name: string;
|
|
281
307
|
}): Promise<void>;
|
|
282
|
-
|
|
308
|
+
/**
|
|
309
|
+
* Look up the debug-replay cache for a single LLM call (debug-replay v2).
|
|
310
|
+
*
|
|
311
|
+
* The server is keyed by `inputHash` (hex blake3 of the canonicalized,
|
|
312
|
+
* system-stripped input messages). It returns one of three outcomes:
|
|
313
|
+
* - `{ outcome: "hit", response }` — a cached response to replay.
|
|
314
|
+
* - `{ outcome: "miss" }` — no entry; caller latches live mode.
|
|
315
|
+
* - `{ outcome: "live" }` — run this call live (COLD degrade).
|
|
316
|
+
*
|
|
317
|
+
* Error posture: a non-OK response or a transport error degrades to
|
|
318
|
+
* `{ kind: "live" }` for THIS call only — it never throws and never latches
|
|
319
|
+
* the process-wide live flag (only a real MISS does that). This keeps a flaky
|
|
320
|
+
* cache backend from turning a replay into a crash.
|
|
321
|
+
*/
|
|
322
|
+
cache({
|
|
283
323
|
sessionId,
|
|
284
|
-
|
|
324
|
+
replayTraceId,
|
|
325
|
+
cacheUntil,
|
|
326
|
+
inputHash
|
|
285
327
|
}: {
|
|
286
328
|
sessionId: string;
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
329
|
+
replayTraceId: string;
|
|
330
|
+
cacheUntil: string;
|
|
331
|
+
inputHash: string;
|
|
332
|
+
}): Promise<CacheOutcome>;
|
|
333
|
+
delete({
|
|
334
|
+
sessionId
|
|
292
335
|
}: {
|
|
293
336
|
sessionId: string;
|
|
294
|
-
span: {
|
|
295
|
-
name: string;
|
|
296
|
-
startTime: string;
|
|
297
|
-
spanId: string;
|
|
298
|
-
traceId: string;
|
|
299
|
-
parentSpanId: string | undefined;
|
|
300
|
-
attributes: Record<string, any>;
|
|
301
|
-
spanType: SpanType;
|
|
302
|
-
};
|
|
303
337
|
}): Promise<void>;
|
|
304
338
|
}
|
|
305
339
|
//#endregion
|
|
@@ -371,13 +405,16 @@ declare class TracesResource extends BaseResource {
|
|
|
371
405
|
*
|
|
372
406
|
* A 404 response (the trace was not found in the project — typically because
|
|
373
407
|
* it has not been flushed yet) is logged as a warning and the call returns
|
|
374
|
-
* without throwing
|
|
408
|
+
* without throwing, since the 404 may be expected when pushing too soon
|
|
409
|
+
* after the trace run. Pass `failOnNotFound: true` to throw instead (e.g.
|
|
410
|
+
* CLI callers that must report the failure). Any other non-OK status throws.
|
|
375
411
|
*
|
|
376
412
|
* @param traceId - The trace id to push metadata to. Accepts a UUID string
|
|
377
413
|
* or a 32-char OTel hex trace id.
|
|
378
414
|
* @param metadata - The metadata patch. Top-level keys are merged into the
|
|
379
415
|
* trace's existing metadata. Must be non-empty (the server rejects empty
|
|
380
416
|
* patches with 400).
|
|
417
|
+
* @param options - `failOnNotFound`: throw on 404 instead of warn-and-return.
|
|
381
418
|
* @example
|
|
382
419
|
* ```typescript
|
|
383
420
|
* import { Laminar, observe, LaminarClient } from "@lmnr-ai/lmnr";
|
|
@@ -399,7 +436,9 @@ declare class TracesResource extends BaseResource {
|
|
|
399
436
|
* }
|
|
400
437
|
* ```
|
|
401
438
|
*/
|
|
402
|
-
pushMetadata(traceId: string, metadata: Record<string, unknown
|
|
439
|
+
pushMetadata(traceId: string, metadata: Record<string, unknown>, options?: {
|
|
440
|
+
failOnNotFound?: boolean;
|
|
441
|
+
}): Promise<void>;
|
|
403
442
|
}
|
|
404
443
|
//#endregion
|
|
405
444
|
//#region src/index.d.ts
|
|
@@ -433,5 +472,5 @@ declare class LaminarClient {
|
|
|
433
472
|
get traces(): TracesResource;
|
|
434
473
|
}
|
|
435
474
|
//#endregion
|
|
436
|
-
export { LaminarClient };
|
|
475
|
+
export { type CacheOutcome, LaminarClient, RolloutSessionsResource };
|
|
437
476
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -3,8 +3,9 @@ import * as path from "path";
|
|
|
3
3
|
import pino from "pino";
|
|
4
4
|
import { PinoPretty } from "pino-pretty";
|
|
5
5
|
import { v4 } from "uuid";
|
|
6
|
+
import { errorMessage } from "@lmnr-ai/types";
|
|
6
7
|
//#region package.json
|
|
7
|
-
var version = "0.8.
|
|
8
|
+
var version = "0.8.28";
|
|
8
9
|
//#endregion
|
|
9
10
|
//#region src/version.ts
|
|
10
11
|
function getLangVersion() {
|
|
@@ -69,7 +70,7 @@ function initializeLogger(options) {
|
|
|
69
70
|
minimumLevel: level
|
|
70
71
|
}));
|
|
71
72
|
}
|
|
72
|
-
const logger$
|
|
73
|
+
const logger$4 = initializeLogger();
|
|
73
74
|
const isStringUUID = (id) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id);
|
|
74
75
|
const newUUID = () => {
|
|
75
76
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
@@ -78,9 +79,9 @@ const newUUID = () => {
|
|
|
78
79
|
const otelSpanIdToUUID = (spanId) => {
|
|
79
80
|
let id = spanId.toLowerCase();
|
|
80
81
|
if (id.startsWith("0x")) id = id.slice(2);
|
|
81
|
-
if (id.length !== 16) logger$
|
|
82
|
+
if (id.length !== 16) logger$4.warn(`Span ID ${spanId} is not 16 hex chars long. This is not a valid OpenTelemetry span ID.`);
|
|
82
83
|
if (!/^[0-9a-f]+$/.test(id)) {
|
|
83
|
-
logger$
|
|
84
|
+
logger$4.error(`Span ID ${spanId} is not a valid hex string. Generating a random UUID instead.`);
|
|
84
85
|
return newUUID();
|
|
85
86
|
}
|
|
86
87
|
return id.padStart(32, "0").replace(/^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/, "$1-$2-$3-$4-$5");
|
|
@@ -88,9 +89,9 @@ const otelSpanIdToUUID = (spanId) => {
|
|
|
88
89
|
const otelTraceIdToUUID = (traceId) => {
|
|
89
90
|
let id = traceId.toLowerCase();
|
|
90
91
|
if (id.startsWith("0x")) id = id.slice(2);
|
|
91
|
-
if (id.length !== 32) logger$
|
|
92
|
+
if (id.length !== 32) logger$4.warn(`Trace ID ${traceId} is not 32 hex chars long. This is not a valid OpenTelemetry trace ID.`);
|
|
92
93
|
if (!/^[0-9a-f]+$/.test(id)) {
|
|
93
|
-
logger$
|
|
94
|
+
logger$4.error(`Trace ID ${traceId} is not a valid hex string. Generating a random UUID instead.`);
|
|
94
95
|
return newUUID();
|
|
95
96
|
}
|
|
96
97
|
return id.replace(/^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/, "$1-$2-$3-$4-$5");
|
|
@@ -120,7 +121,7 @@ const loadEnv = (options) => {
|
|
|
120
121
|
};
|
|
121
122
|
//#endregion
|
|
122
123
|
//#region src/resources/datasets.ts
|
|
123
|
-
const logger$
|
|
124
|
+
const logger$3 = initializeLogger();
|
|
124
125
|
const DEFAULT_DATASET_PULL_LIMIT = 100;
|
|
125
126
|
const DEFAULT_DATASET_PUSH_BATCH_SIZE = 100;
|
|
126
127
|
var DatasetsResource = class extends BaseResource {
|
|
@@ -175,7 +176,7 @@ var DatasetsResource = class extends BaseResource {
|
|
|
175
176
|
let response;
|
|
176
177
|
for (let i = 0; i < points.length; i += batchSize) {
|
|
177
178
|
const batchNum = Math.floor(i / batchSize) + 1;
|
|
178
|
-
logger$
|
|
179
|
+
logger$3.debug(`Pushing batch ${batchNum} of ${totalBatches}`);
|
|
179
180
|
const batch = points.slice(i, i + batchSize);
|
|
180
181
|
const fetchResponse = await fetch(this.baseHttpUrl + "/v1/datasets/datapoints", {
|
|
181
182
|
method: "POST",
|
|
@@ -225,7 +226,7 @@ var DatasetsResource = class extends BaseResource {
|
|
|
225
226
|
};
|
|
226
227
|
//#endregion
|
|
227
228
|
//#region src/resources/evals.ts
|
|
228
|
-
const logger$
|
|
229
|
+
const logger$2 = initializeLogger();
|
|
229
230
|
const INITIAL_EVALUATION_DATAPOINT_MAX_DATA_LENGTH = 16e6;
|
|
230
231
|
var EvalsResource = class extends BaseResource {
|
|
231
232
|
constructor(baseHttpUrl, projectApiKey) {
|
|
@@ -361,7 +362,7 @@ var EvalsResource = class extends BaseResource {
|
|
|
361
362
|
* @returns {Promise<GetDatapointsResponse>} Response from the datapoint retrieval
|
|
362
363
|
*/
|
|
363
364
|
async getDatapoints({ datasetName, offset, limit }) {
|
|
364
|
-
logger$
|
|
365
|
+
logger$2.warn("evals.getDatapoints() is deprecated. Use client.datasets.pull() instead.");
|
|
365
366
|
const params = new URLSearchParams({
|
|
366
367
|
name: datasetName,
|
|
367
368
|
offset: offset.toString(),
|
|
@@ -378,7 +379,7 @@ var EvalsResource = class extends BaseResource {
|
|
|
378
379
|
let length = initialLength;
|
|
379
380
|
let lastResponse = null;
|
|
380
381
|
for (let i = 0; i < maxRetries; i++) {
|
|
381
|
-
logger$
|
|
382
|
+
logger$2.debug(`Retrying save datapoints... ${i + 1} of ${maxRetries}, length: ${length}`);
|
|
382
383
|
const response = await fetch(this.baseHttpUrl + `/v1/evals/${evalId}/datapoints`, {
|
|
383
384
|
method: "POST",
|
|
384
385
|
headers: this.headers(),
|
|
@@ -464,60 +465,129 @@ var EvaluatorsResource = class extends BaseResource {
|
|
|
464
465
|
};
|
|
465
466
|
//#endregion
|
|
466
467
|
//#region src/resources/rollout-sessions.ts
|
|
468
|
+
const logger$1 = initializeLogger();
|
|
469
|
+
/**
|
|
470
|
+
* Map the opaque HIT `response` payload onto a {@link CachedSpan} the provider
|
|
471
|
+
* wrappers can replay. The server-side shape of `response` is not yet frozen
|
|
472
|
+
* (app-server plan 01 leaves it as a `serde_json::Value`), so this stays
|
|
473
|
+
* deliberately tolerant: the whole payload is serialized into `output` (the only
|
|
474
|
+
* field the AI SDK wrapper's `parseCachedSpan` actually reads, via
|
|
475
|
+
* `JSON.parse`), and a `finishReason` is surfaced into `attributes` when the
|
|
476
|
+
* payload carries one. `name`/`input` are irrelevant to replay and left empty.
|
|
477
|
+
*/
|
|
478
|
+
const toCachedSpan = (response) => {
|
|
479
|
+
const output = typeof response === "string" ? response : JSON.stringify(response ?? null);
|
|
480
|
+
const attributes = {};
|
|
481
|
+
if (response !== null && typeof response === "object" && typeof response.finishReason === "string") attributes["ai.response.finishReason"] = response.finishReason;
|
|
482
|
+
return {
|
|
483
|
+
name: "",
|
|
484
|
+
input: "",
|
|
485
|
+
output,
|
|
486
|
+
attributes
|
|
487
|
+
};
|
|
488
|
+
};
|
|
467
489
|
var RolloutSessionsResource = class extends BaseResource {
|
|
468
490
|
constructor(baseHttpUrl, projectApiKey) {
|
|
469
491
|
super(baseHttpUrl, projectApiKey);
|
|
470
492
|
}
|
|
471
493
|
/**
|
|
472
|
-
*
|
|
473
|
-
*
|
|
494
|
+
* Idempotently register (upsert) a debug session on the backend, keyed on the
|
|
495
|
+
* SDK-supplied session id. The backend stores the row so the session is
|
|
496
|
+
* visible in the UI; a null/omitted name never clobbers a name set elsewhere.
|
|
497
|
+
*
|
|
498
|
+
* Returns the backend-resolved `projectId` (derived from the API key) so the
|
|
499
|
+
* caller can build the debugger URL; null if the body can't be parsed.
|
|
474
500
|
*/
|
|
475
|
-
async
|
|
501
|
+
async register({ sessionId, name }) {
|
|
476
502
|
const response = await fetch(`${this.baseHttpUrl}/v1/rollouts/${sessionId}`, {
|
|
477
503
|
method: "POST",
|
|
478
|
-
headers:
|
|
479
|
-
|
|
480
|
-
"Accept": "text/event-stream"
|
|
481
|
-
},
|
|
482
|
-
body: JSON.stringify({
|
|
483
|
-
name,
|
|
484
|
-
params
|
|
485
|
-
}),
|
|
486
|
-
signal
|
|
487
|
-
});
|
|
488
|
-
if (!response.ok) throw new Error(`SSE connection failed: ${response.status} ${response.statusText}`);
|
|
489
|
-
if (!response.body) throw new Error("No response body");
|
|
490
|
-
return response;
|
|
491
|
-
}
|
|
492
|
-
async delete({ sessionId }) {
|
|
493
|
-
const response = await fetch(`${this.baseHttpUrl}/v1/rollouts/${sessionId}`, {
|
|
494
|
-
method: "DELETE",
|
|
495
|
-
headers: this.headers()
|
|
504
|
+
headers: this.headers(),
|
|
505
|
+
body: JSON.stringify({ name })
|
|
496
506
|
});
|
|
497
507
|
if (!response.ok) await this.handleError(response);
|
|
508
|
+
try {
|
|
509
|
+
return (await response.json()).projectId ?? null;
|
|
510
|
+
} catch (e) {
|
|
511
|
+
logger$1.warn(`Failed to parse rollout register response: ${errorMessage(e)}`);
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
498
514
|
}
|
|
499
|
-
|
|
500
|
-
|
|
515
|
+
/**
|
|
516
|
+
* Rename an existing debug session. Update-only: the backend returns 404 (and
|
|
517
|
+
* this throws) when the session id is unknown for the project, so a mistyped
|
|
518
|
+
* id surfaces as an error rather than silently creating a session. Creation
|
|
519
|
+
* stays the SDK's job via {@link register}.
|
|
520
|
+
*/
|
|
521
|
+
async setName({ sessionId, name }) {
|
|
522
|
+
const response = await fetch(`${this.baseHttpUrl}/v1/rollouts/${sessionId}/name`, {
|
|
501
523
|
method: "PATCH",
|
|
502
524
|
headers: this.headers(),
|
|
503
|
-
body: JSON.stringify({
|
|
525
|
+
body: JSON.stringify({ name })
|
|
504
526
|
});
|
|
505
527
|
if (!response.ok) await this.handleError(response);
|
|
506
528
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
529
|
+
/**
|
|
530
|
+
* Look up the debug-replay cache for a single LLM call (debug-replay v2).
|
|
531
|
+
*
|
|
532
|
+
* The server is keyed by `inputHash` (hex blake3 of the canonicalized,
|
|
533
|
+
* system-stripped input messages). It returns one of three outcomes:
|
|
534
|
+
* - `{ outcome: "hit", response }` — a cached response to replay.
|
|
535
|
+
* - `{ outcome: "miss" }` — no entry; caller latches live mode.
|
|
536
|
+
* - `{ outcome: "live" }` — run this call live (COLD degrade).
|
|
537
|
+
*
|
|
538
|
+
* Error posture: a non-OK response or a transport error degrades to
|
|
539
|
+
* `{ kind: "live" }` for THIS call only — it never throws and never latches
|
|
540
|
+
* the process-wide live flag (only a real MISS does that). This keeps a flaky
|
|
541
|
+
* cache backend from turning a replay into a crash.
|
|
542
|
+
*/
|
|
543
|
+
async cache({ sessionId, replayTraceId, cacheUntil, inputHash }) {
|
|
544
|
+
let response;
|
|
545
|
+
try {
|
|
546
|
+
response = await fetch(`${this.baseHttpUrl}/v1/rollouts/${sessionId}/cache`, {
|
|
547
|
+
method: "POST",
|
|
548
|
+
headers: this.headers(),
|
|
549
|
+
body: JSON.stringify({
|
|
550
|
+
replayTraceId,
|
|
551
|
+
cacheUntil,
|
|
552
|
+
inputHash
|
|
553
|
+
})
|
|
554
|
+
});
|
|
555
|
+
} catch (e) {
|
|
556
|
+
logger$1.warn(`Debug cache lookup failed, running live: ${errorMessage(e)}`);
|
|
557
|
+
return { kind: "live" };
|
|
558
|
+
}
|
|
559
|
+
if (!response.ok) {
|
|
560
|
+
logger$1.warn(`Debug cache lookup returned ${response.status}, running live`);
|
|
561
|
+
return { kind: "live" };
|
|
562
|
+
}
|
|
563
|
+
let body;
|
|
564
|
+
try {
|
|
565
|
+
body = await response.json();
|
|
566
|
+
} catch (e) {
|
|
567
|
+
logger$1.warn(`Failed to parse debug cache response, running live: ${errorMessage(e)}`);
|
|
568
|
+
return { kind: "live" };
|
|
569
|
+
}
|
|
570
|
+
switch (body.outcome) {
|
|
571
|
+
case "hit":
|
|
572
|
+
if (body.response === null || body.response === void 0) {
|
|
573
|
+
logger$1.warn("Debug cache HIT had no response payload, running live");
|
|
574
|
+
return { kind: "live" };
|
|
575
|
+
}
|
|
576
|
+
return {
|
|
577
|
+
kind: "hit",
|
|
578
|
+
cached: toCachedSpan(body.response)
|
|
579
|
+
};
|
|
580
|
+
case "miss": return { kind: "miss" };
|
|
581
|
+
case "live": return { kind: "live" };
|
|
582
|
+
default:
|
|
583
|
+
logger$1.warn(`Unknown debug cache outcome "${body.outcome}", running live`);
|
|
584
|
+
return { kind: "live" };
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
async delete({ sessionId }) {
|
|
588
|
+
const response = await fetch(`${this.baseHttpUrl}/v1/rollouts/${sessionId}`, {
|
|
589
|
+
method: "DELETE",
|
|
590
|
+
headers: this.headers()
|
|
521
591
|
});
|
|
522
592
|
if (!response.ok) await this.handleError(response);
|
|
523
593
|
}
|
|
@@ -626,13 +696,16 @@ var TracesResource = class extends BaseResource {
|
|
|
626
696
|
*
|
|
627
697
|
* A 404 response (the trace was not found in the project — typically because
|
|
628
698
|
* it has not been flushed yet) is logged as a warning and the call returns
|
|
629
|
-
* without throwing
|
|
699
|
+
* without throwing, since the 404 may be expected when pushing too soon
|
|
700
|
+
* after the trace run. Pass `failOnNotFound: true` to throw instead (e.g.
|
|
701
|
+
* CLI callers that must report the failure). Any other non-OK status throws.
|
|
630
702
|
*
|
|
631
703
|
* @param traceId - The trace id to push metadata to. Accepts a UUID string
|
|
632
704
|
* or a 32-char OTel hex trace id.
|
|
633
705
|
* @param metadata - The metadata patch. Top-level keys are merged into the
|
|
634
706
|
* trace's existing metadata. Must be non-empty (the server rejects empty
|
|
635
707
|
* patches with 400).
|
|
708
|
+
* @param options - `failOnNotFound`: throw on 404 instead of warn-and-return.
|
|
636
709
|
* @example
|
|
637
710
|
* ```typescript
|
|
638
711
|
* import { Laminar, observe, LaminarClient } from "@lmnr-ai/lmnr";
|
|
@@ -654,7 +727,7 @@ var TracesResource = class extends BaseResource {
|
|
|
654
727
|
* }
|
|
655
728
|
* ```
|
|
656
729
|
*/
|
|
657
|
-
async pushMetadata(traceId, metadata) {
|
|
730
|
+
async pushMetadata(traceId, metadata, options) {
|
|
658
731
|
if (!metadata || Object.keys(metadata).length === 0) throw new Error("metadata must be a non-empty object");
|
|
659
732
|
const formattedTraceId = isStringUUID(traceId) ? traceId : otelTraceIdToUUID(traceId);
|
|
660
733
|
const url = this.baseHttpUrl + "/v1/traces/metadata";
|
|
@@ -667,7 +740,9 @@ var TracesResource = class extends BaseResource {
|
|
|
667
740
|
})
|
|
668
741
|
});
|
|
669
742
|
if (response.status === 404) {
|
|
670
|
-
|
|
743
|
+
const message = `Trace ${formattedTraceId} not found. The trace may not have been flushed yet — call await Laminar.flush() and retry.`;
|
|
744
|
+
if (options?.failOnNotFound) throw new Error(message);
|
|
745
|
+
logger.warn(message);
|
|
671
746
|
return;
|
|
672
747
|
}
|
|
673
748
|
if (!response.ok) await this.handleError(response);
|
|
@@ -717,6 +792,6 @@ var LaminarClient = class {
|
|
|
717
792
|
}
|
|
718
793
|
};
|
|
719
794
|
//#endregion
|
|
720
|
-
export { LaminarClient };
|
|
795
|
+
export { LaminarClient, RolloutSessionsResource };
|
|
721
796
|
|
|
722
797
|
//# sourceMappingURL=index.mjs.map
|