langsmith 0.8.8 → 0.8.9

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.
Files changed (46) hide show
  1. package/dist/_openapi_client/resources/annotation-queues/annotation-queues.cjs +10 -0
  2. package/dist/_openapi_client/resources/annotation-queues/annotation-queues.d.ts +10 -0
  3. package/dist/_openapi_client/resources/annotation-queues/annotation-queues.js +10 -0
  4. package/dist/_openapi_client/resources/annotation-queues/runs.cjs +13 -1
  5. package/dist/_openapi_client/resources/annotation-queues/runs.d.ts +13 -1
  6. package/dist/_openapi_client/resources/annotation-queues/runs.js +13 -1
  7. package/dist/_openapi_client/resources/datasets/experiment-runs.cjs +2 -0
  8. package/dist/_openapi_client/resources/datasets/experiment-runs.d.ts +2 -0
  9. package/dist/_openapi_client/resources/datasets/experiment-runs.js +2 -0
  10. package/dist/_openapi_client/resources/issues.d.ts +1 -1
  11. package/dist/_openapi_client/resources/public/runs.cjs +9 -7
  12. package/dist/_openapi_client/resources/public/runs.d.ts +8 -6
  13. package/dist/_openapi_client/resources/public/runs.js +9 -7
  14. package/dist/_openapi_client/resources/runs/runs.cjs +11 -6
  15. package/dist/_openapi_client/resources/runs/runs.d.ts +11 -6
  16. package/dist/_openapi_client/resources/runs/runs.js +11 -6
  17. package/dist/_openapi_client/resources/runs/share.cjs +4 -0
  18. package/dist/_openapi_client/resources/runs/share.d.ts +4 -0
  19. package/dist/_openapi_client/resources/runs/share.js +4 -0
  20. package/dist/_openapi_client/resources/threads.cjs +11 -8
  21. package/dist/_openapi_client/resources/threads.d.ts +11 -8
  22. package/dist/_openapi_client/resources/threads.js +11 -8
  23. package/dist/_openapi_client/resources/traces.cjs +6 -3
  24. package/dist/_openapi_client/resources/traces.d.ts +6 -3
  25. package/dist/_openapi_client/resources/traces.js +6 -3
  26. package/dist/client.cjs +262 -20
  27. package/dist/client.d.ts +53 -3
  28. package/dist/client.js +262 -20
  29. package/dist/evaluation/evaluate_comparative.cjs +7 -4
  30. package/dist/evaluation/evaluate_comparative.js +7 -4
  31. package/dist/index.cjs +1 -1
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.js +1 -1
  34. package/dist/sandbox/command_handle.cjs +34 -7
  35. package/dist/sandbox/command_handle.d.ts +1 -0
  36. package/dist/sandbox/command_handle.js +34 -7
  37. package/dist/sandbox/ws_execute.cjs +7 -3
  38. package/dist/sandbox/ws_execute.d.ts +4 -2
  39. package/dist/sandbox/ws_execute.js +7 -3
  40. package/dist/utils/v2_migration.cjs +18 -0
  41. package/dist/utils/v2_migration.d.ts +8 -0
  42. package/dist/utils/v2_migration.js +16 -0
  43. package/dist/utils/warn.cjs +30 -4
  44. package/dist/utils/warn.d.ts +4 -1
  45. package/dist/utils/warn.js +29 -4
  46. package/package.json +1 -1
@@ -8,6 +8,22 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.CommandHandle = void 0;
10
10
  const errors_js_1 = require("./errors.cjs");
11
+ /**
12
+ * Whether a 'started' frame proves *this* command was reattached to.
13
+ *
14
+ * A command and its WebSocket are separate things: the command keeps running on
15
+ * the server and the socket is only this client's attachment to it. The server
16
+ * acknowledges every successful reattachment with 'started', which for a command
17
+ * that produces no output is the only evidence it landed. One naming a different
18
+ * command is not evidence, and must not clear the reconnect budget.
19
+ */
20
+ function acknowledgesReconnect(msg, commandId) {
21
+ const acked = msg.command_id;
22
+ if (acked === commandId)
23
+ return true;
24
+ console.warn(`Ignoring reconnect acknowledgement for command ${String(acked)} while attached to ${String(commandId)}`);
25
+ return false;
26
+ }
11
27
  /**
12
28
  * Async handle to a running command with streaming output and auto-reconnect.
13
29
  *
@@ -107,6 +123,12 @@ class CommandHandle {
107
123
  writable: true,
108
124
  value: void 0
109
125
  });
126
+ Object.defineProperty(this, "_reconnectAttempts", {
127
+ enumerable: true,
128
+ configurable: true,
129
+ writable: true,
130
+ value: 0
131
+ });
110
132
  Object.defineProperty(this, "_onStdout", {
111
133
  enumerable: true,
112
134
  configurable: true,
@@ -192,7 +214,12 @@ class CommandHandle {
192
214
  return;
193
215
  for await (const msg of this._stream) {
194
216
  const msgType = msg.type;
195
- if (msgType === "stdout" || msgType === "stderr") {
217
+ if (msgType === "started") {
218
+ if (acknowledgesReconnect(msg, this._commandId)) {
219
+ this._reconnectAttempts = 0;
220
+ }
221
+ }
222
+ else if (msgType === "stdout" || msgType === "stderr") {
196
223
  const chunk = {
197
224
  stream: msgType,
198
225
  data: msg.data,
@@ -227,11 +254,11 @@ class CommandHandle {
227
254
  * - After kill(): no reconnect, error propagates
228
255
  */
229
256
  async *[Symbol.asyncIterator]() {
230
- let reconnectAttempts = 0;
257
+ this._reconnectAttempts = 0;
231
258
  while (true) {
232
259
  try {
233
260
  for await (const chunk of this._iterStream()) {
234
- reconnectAttempts = 0; // Reset on successful data
261
+ this._reconnectAttempts = 0; // Reset on successful data
235
262
  if (chunk.stream === "stdout") {
236
263
  this._lastStdoutOffset =
237
264
  chunk.offset + new TextEncoder().encode(chunk.data).length;
@@ -255,13 +282,13 @@ class CommandHandle {
255
282
  if (this._control && this._control.killed) {
256
283
  throw e;
257
284
  }
258
- reconnectAttempts++;
259
- if (reconnectAttempts > CommandHandle.MAX_AUTO_RECONNECTS) {
260
- throw new errors_js_1.LangSmithSandboxConnectionError(`Lost connection ${reconnectAttempts} times in succession, giving up`);
285
+ this._reconnectAttempts++;
286
+ if (this._reconnectAttempts > CommandHandle.MAX_AUTO_RECONNECTS) {
287
+ throw new errors_js_1.LangSmithSandboxConnectionError(`Failed to reattach to the command ${this._reconnectAttempts} times in succession, giving up`);
261
288
  }
262
289
  const isHotReload = e instanceof errors_js_1.LangSmithSandboxServerReloadError;
263
290
  if (!isHotReload) {
264
- const delay = Math.min(CommandHandle.BACKOFF_BASE * 2 ** (reconnectAttempts - 1), CommandHandle.BACKOFF_MAX);
291
+ const delay = Math.min(CommandHandle.BACKOFF_BASE * 2 ** (this._reconnectAttempts - 1), CommandHandle.BACKOFF_MAX);
265
292
  await new Promise((r) => setTimeout(r, delay * 1000));
266
293
  }
267
294
  if (this._commandId === null) {
@@ -45,6 +45,7 @@ export declare class CommandHandle {
45
45
  private _lastStdoutOffset;
46
46
  private _lastStderrOffset;
47
47
  private _started;
48
+ private _reconnectAttempts;
48
49
  private _onStdout?;
49
50
  private _onStderr?;
50
51
  /**
@@ -5,6 +5,22 @@
5
5
  * Port of Python's AsyncCommandHandle to TypeScript.
6
6
  */
7
7
  import { LangSmithSandboxConnectionError, LangSmithSandboxServerReloadError, LangSmithSandboxOperationError, LangSmithStreamEndedBeforeStartedError, } from "./errors.js";
8
+ /**
9
+ * Whether a 'started' frame proves *this* command was reattached to.
10
+ *
11
+ * A command and its WebSocket are separate things: the command keeps running on
12
+ * the server and the socket is only this client's attachment to it. The server
13
+ * acknowledges every successful reattachment with 'started', which for a command
14
+ * that produces no output is the only evidence it landed. One naming a different
15
+ * command is not evidence, and must not clear the reconnect budget.
16
+ */
17
+ function acknowledgesReconnect(msg, commandId) {
18
+ const acked = msg.command_id;
19
+ if (acked === commandId)
20
+ return true;
21
+ console.warn(`Ignoring reconnect acknowledgement for command ${String(acked)} while attached to ${String(commandId)}`);
22
+ return false;
23
+ }
8
24
  /**
9
25
  * Async handle to a running command with streaming output and auto-reconnect.
10
26
  *
@@ -104,6 +120,12 @@ export class CommandHandle {
104
120
  writable: true,
105
121
  value: void 0
106
122
  });
123
+ Object.defineProperty(this, "_reconnectAttempts", {
124
+ enumerable: true,
125
+ configurable: true,
126
+ writable: true,
127
+ value: 0
128
+ });
107
129
  Object.defineProperty(this, "_onStdout", {
108
130
  enumerable: true,
109
131
  configurable: true,
@@ -189,7 +211,12 @@ export class CommandHandle {
189
211
  return;
190
212
  for await (const msg of this._stream) {
191
213
  const msgType = msg.type;
192
- if (msgType === "stdout" || msgType === "stderr") {
214
+ if (msgType === "started") {
215
+ if (acknowledgesReconnect(msg, this._commandId)) {
216
+ this._reconnectAttempts = 0;
217
+ }
218
+ }
219
+ else if (msgType === "stdout" || msgType === "stderr") {
193
220
  const chunk = {
194
221
  stream: msgType,
195
222
  data: msg.data,
@@ -224,11 +251,11 @@ export class CommandHandle {
224
251
  * - After kill(): no reconnect, error propagates
225
252
  */
226
253
  async *[Symbol.asyncIterator]() {
227
- let reconnectAttempts = 0;
254
+ this._reconnectAttempts = 0;
228
255
  while (true) {
229
256
  try {
230
257
  for await (const chunk of this._iterStream()) {
231
- reconnectAttempts = 0; // Reset on successful data
258
+ this._reconnectAttempts = 0; // Reset on successful data
232
259
  if (chunk.stream === "stdout") {
233
260
  this._lastStdoutOffset =
234
261
  chunk.offset + new TextEncoder().encode(chunk.data).length;
@@ -252,13 +279,13 @@ export class CommandHandle {
252
279
  if (this._control && this._control.killed) {
253
280
  throw e;
254
281
  }
255
- reconnectAttempts++;
256
- if (reconnectAttempts > CommandHandle.MAX_AUTO_RECONNECTS) {
257
- throw new LangSmithSandboxConnectionError(`Lost connection ${reconnectAttempts} times in succession, giving up`);
282
+ this._reconnectAttempts++;
283
+ if (this._reconnectAttempts > CommandHandle.MAX_AUTO_RECONNECTS) {
284
+ throw new LangSmithSandboxConnectionError(`Failed to reattach to the command ${this._reconnectAttempts} times in succession, giving up`);
258
285
  }
259
286
  const isHotReload = e instanceof LangSmithSandboxServerReloadError;
260
287
  if (!isHotReload) {
261
- const delay = Math.min(CommandHandle.BACKOFF_BASE * 2 ** (reconnectAttempts - 1), CommandHandle.BACKOFF_MAX);
288
+ const delay = Math.min(CommandHandle.BACKOFF_BASE * 2 ** (this._reconnectAttempts - 1), CommandHandle.BACKOFF_MAX);
262
289
  await new Promise((r) => setTimeout(r, delay * 1000));
263
290
  }
264
291
  if (this._commandId === null) {
@@ -382,8 +382,10 @@ async function runWsStream(dataplaneUrl, apiKey, command, options = {}) {
382
382
  * Reconnect to an existing command over WebSocket.
383
383
  *
384
384
  * Returns a tuple of [async_message_iterator, control], same as runWsStream.
385
- * The iterator yields stdout, stderr, exit, and error messages.
386
- * No 'started' message is sent on reconnection.
385
+ * The iterator yields the server's 'started' acknowledgement, then stdout,
386
+ * stderr, exit, and error messages. That acknowledgement is the only evidence a
387
+ * reattachment landed for a command producing no output, so it is forwarded
388
+ * rather than dropped.
387
389
  */
388
390
  async function reconnectWsStream(dataplaneUrl, apiKey, commandId, options = {}) {
389
391
  const { stdoutOffset = 0, stderrOffset = 0, headers: extraHeaders } = options;
@@ -405,7 +407,9 @@ async function reconnectWsStream(dataplaneUrl, apiKey, commandId, options = {})
405
407
  // Read messages until exit or error
406
408
  for await (const msg of readWsMessages(ws)) {
407
409
  const msgType = msg.type;
408
- if (msgType === "stdout" || msgType === "stderr") {
410
+ if (msgType === "started" ||
411
+ msgType === "stdout" ||
412
+ msgType === "stderr") {
409
413
  yield msg;
410
414
  }
411
415
  else if (msgType === "exit") {
@@ -70,8 +70,10 @@ export declare function runWsStream(dataplaneUrl: string, apiKey: string | undef
70
70
  * Reconnect to an existing command over WebSocket.
71
71
  *
72
72
  * Returns a tuple of [async_message_iterator, control], same as runWsStream.
73
- * The iterator yields stdout, stderr, exit, and error messages.
74
- * No 'started' message is sent on reconnection.
73
+ * The iterator yields the server's 'started' acknowledgement, then stdout,
74
+ * stderr, exit, and error messages. That acknowledgement is the only evidence a
75
+ * reattachment landed for a command producing no output, so it is forwarded
76
+ * rather than dropped.
75
77
  */
76
78
  export declare function reconnectWsStream(dataplaneUrl: string, apiKey: string | undefined, commandId: string, options?: {
77
79
  stdoutOffset?: number;
@@ -369,8 +369,10 @@ export async function runWsStream(dataplaneUrl, apiKey, command, options = {}) {
369
369
  * Reconnect to an existing command over WebSocket.
370
370
  *
371
371
  * Returns a tuple of [async_message_iterator, control], same as runWsStream.
372
- * The iterator yields stdout, stderr, exit, and error messages.
373
- * No 'started' message is sent on reconnection.
372
+ * The iterator yields the server's 'started' acknowledgement, then stdout,
373
+ * stderr, exit, and error messages. That acknowledgement is the only evidence a
374
+ * reattachment landed for a command producing no output, so it is forwarded
375
+ * rather than dropped.
374
376
  */
375
377
  export async function reconnectWsStream(dataplaneUrl, apiKey, commandId, options = {}) {
376
378
  const { stdoutOffset = 0, stderrOffset = 0, headers: extraHeaders } = options;
@@ -392,7 +394,9 @@ export async function reconnectWsStream(dataplaneUrl, apiKey, commandId, options
392
394
  // Read messages until exit or error
393
395
  for await (const msg of readWsMessages(ws)) {
394
396
  const msgType = msg.type;
395
- if (msgType === "stdout" || msgType === "stderr") {
397
+ if (msgType === "started" ||
398
+ msgType === "stdout" ||
399
+ msgType === "stderr") {
396
400
  yield msg;
397
401
  }
398
402
  else if (msgType === "exit") {
@@ -1,7 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QueryBackend = void 0;
4
+ exports.getQueryBackend = getQueryBackend;
3
5
  exports.v2RunToRun = v2RunToRun;
4
6
  exports.loadTracesV2 = loadTracesV2;
7
+ exports.QueryBackend = {
8
+ CLICKHOUSE_ONLY: "clickhouse_only",
9
+ SMITHDB_ONLY: "smithdb_only",
10
+ DUAL: "dual",
11
+ };
12
+ /** Which backend(s) `/info`'s `instance_flags` indicate for run/trace queries. */
13
+ function getQueryBackend(instanceFlags) {
14
+ const flags = instanceFlags ?? {};
15
+ const chEnabled = Boolean(flags.ch_query_enabled ?? true);
16
+ const sdbEnabled = Boolean(flags.sdb_query_enabled ?? false);
17
+ if (!chEnabled && sdbEnabled)
18
+ return exports.QueryBackend.SMITHDB_ONLY;
19
+ if (chEnabled && sdbEnabled)
20
+ return exports.QueryBackend.DUAL;
21
+ return exports.QueryBackend.CLICKHOUSE_ONLY;
22
+ }
5
23
  // Omitting selects from `/v2/runs/query` returns only the run ID.
6
24
  const V2_RUN_SELECTS = [
7
25
  "ID",
@@ -1,6 +1,14 @@
1
1
  import type { Run as V2Run } from "../_openapi_client/resources/runs/runs.js";
2
2
  import type { Client } from "../client.js";
3
3
  import type { Run, TracerSession } from "../schemas.js";
4
+ export declare const QueryBackend: {
5
+ readonly CLICKHOUSE_ONLY: "clickhouse_only";
6
+ readonly SMITHDB_ONLY: "smithdb_only";
7
+ readonly DUAL: "dual";
8
+ };
9
+ export type QueryBackend = (typeof QueryBackend)[keyof typeof QueryBackend];
10
+ /** Which backend(s) `/info`'s `instance_flags` indicate for run/trace queries. */
11
+ export declare function getQueryBackend(instanceFlags?: Record<string, unknown> | null): QueryBackend;
4
12
  export declare function v2RunToRun(run: V2Run): Run;
5
13
  export declare function loadTracesV2(client: Client, project: TracerSession, options: {
6
14
  isRoot?: boolean;
@@ -1,3 +1,19 @@
1
+ export const QueryBackend = {
2
+ CLICKHOUSE_ONLY: "clickhouse_only",
3
+ SMITHDB_ONLY: "smithdb_only",
4
+ DUAL: "dual",
5
+ };
6
+ /** Which backend(s) `/info`'s `instance_flags` indicate for run/trace queries. */
7
+ export function getQueryBackend(instanceFlags) {
8
+ const flags = instanceFlags ?? {};
9
+ const chEnabled = Boolean(flags.ch_query_enabled ?? true);
10
+ const sdbEnabled = Boolean(flags.sdb_query_enabled ?? false);
11
+ if (!chEnabled && sdbEnabled)
12
+ return QueryBackend.SMITHDB_ONLY;
13
+ if (chEnabled && sdbEnabled)
14
+ return QueryBackend.DUAL;
15
+ return QueryBackend.CLICKHOUSE_ONLY;
16
+ }
1
17
  // Omitting selects from `/v2/runs/query` returns only the run ID.
2
18
  const V2_RUN_SELECTS = [
3
19
  "ID",
@@ -1,10 +1,36 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports._resetWarnedMessages = _resetWarnedMessages;
3
4
  exports.warnOnce = warnOnce;
4
5
  const warnedMessages = {};
5
- function warnOnce(message) {
6
- if (!warnedMessages[message]) {
7
- console.warn(message);
8
- warnedMessages[message] = true;
6
+ /**
7
+ * Clear the warn-once record so a later call warns again.
8
+ *
9
+ * Test-only. `warnedMessages` is module-global and never otherwise reset, so
10
+ * without this a test asserting "warning X was (not) emitted" depends on whether
11
+ * an earlier test in the same file already consumed X.
12
+ *
13
+ * @internal
14
+ */
15
+ function _resetWarnedMessages() {
16
+ for (const key of Object.keys(warnedMessages)) {
17
+ delete warnedMessages[key];
18
+ }
19
+ }
20
+ function warnOnce(message, options) {
21
+ const key = options?.code ?? message;
22
+ if (!warnedMessages[key]) {
23
+ warnedMessages[key] = true;
24
+ if (options?.type &&
25
+ typeof process !== "undefined" &&
26
+ typeof process.emitWarning === "function") {
27
+ process.emitWarning(message, { type: options.type, code: options.code });
28
+ }
29
+ else if (options?.type && options?.code) {
30
+ console.warn(`${options.type} [${options.code}]: ${message}`);
31
+ }
32
+ else {
33
+ console.warn(message);
34
+ }
9
35
  }
10
36
  }
@@ -1 +1,4 @@
1
- export declare function warnOnce(message: string): void;
1
+ export declare function warnOnce(message: string, options?: {
2
+ type?: string;
3
+ code?: string;
4
+ }): void;
@@ -1,7 +1,32 @@
1
1
  const warnedMessages = {};
2
- export function warnOnce(message) {
3
- if (!warnedMessages[message]) {
4
- console.warn(message);
5
- warnedMessages[message] = true;
2
+ /**
3
+ * Clear the warn-once record so a later call warns again.
4
+ *
5
+ * Test-only. `warnedMessages` is module-global and never otherwise reset, so
6
+ * without this a test asserting "warning X was (not) emitted" depends on whether
7
+ * an earlier test in the same file already consumed X.
8
+ *
9
+ * @internal
10
+ */
11
+ export function _resetWarnedMessages() {
12
+ for (const key of Object.keys(warnedMessages)) {
13
+ delete warnedMessages[key];
14
+ }
15
+ }
16
+ export function warnOnce(message, options) {
17
+ const key = options?.code ?? message;
18
+ if (!warnedMessages[key]) {
19
+ warnedMessages[key] = true;
20
+ if (options?.type &&
21
+ typeof process !== "undefined" &&
22
+ typeof process.emitWarning === "function") {
23
+ process.emitWarning(message, { type: options.type, code: options.code });
24
+ }
25
+ else if (options?.type && options?.code) {
26
+ console.warn(`${options.type} [${options.code}]: ${message}`);
27
+ }
28
+ else {
29
+ console.warn(message);
30
+ }
6
31
  }
7
32
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.8.8",
3
+ "version": "0.8.9",
4
4
  "description": "Client library to connect to the LangSmith Observability and Evaluation Platform.",
5
5
  "packageManager": "pnpm@10.33.0",
6
6
  "files": [