dd-trace 5.123.0 → 6.11.0

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 (55) hide show
  1. package/LICENSE-3rdparty.csv +0 -5
  2. package/index.d.ts +25 -240
  3. package/package.json +18 -19
  4. package/packages/datadog-instrumentations/src/cucumber.js +4 -4
  5. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -5
  6. package/packages/datadog-instrumentations/src/playwright-reporter.js +1 -149
  7. package/packages/datadog-instrumentations/src/playwright.js +33 -33
  8. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +8 -6
  9. package/packages/datadog-plugin-playwright/src/index.js +0 -4
  10. package/packages/dd-trace/src/appsec/reporter.js +0 -2
  11. package/packages/dd-trace/src/appsec/telemetry/index.js +0 -13
  12. package/packages/dd-trace/src/appsec/telemetry/rasp.js +0 -16
  13. package/packages/dd-trace/src/appsec/telemetry/waf.js +0 -24
  14. package/packages/dd-trace/src/carrier.js +48 -107
  15. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +5 -5
  16. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +0 -2
  17. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +3 -5
  18. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -4
  19. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +4 -6
  20. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +3 -5
  21. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +2 -4
  22. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +2 -4
  23. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +2 -4
  24. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +5 -5
  25. package/packages/dd-trace/src/debugger/constants.js +0 -1
  26. package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
  27. package/packages/dd-trace/src/debugger/devtools_client/index.js +10 -3
  28. package/packages/dd-trace/src/debugger/index.js +2 -4
  29. package/packages/dd-trace/src/exporters/common/request.js +2 -5
  30. package/packages/dd-trace/src/llmobs/experiments/client.js +2 -6
  31. package/packages/dd-trace/src/llmobs/experiments/dataset.js +11 -249
  32. package/packages/dd-trace/src/llmobs/experiments/experiment.js +10 -23
  33. package/packages/dd-trace/src/llmobs/experiments/index.js +6 -18
  34. package/packages/dd-trace/src/llmobs/experiments/noop.js +4 -64
  35. package/packages/dd-trace/src/llmobs/experiments/util.js +4 -60
  36. package/packages/dd-trace/src/openfeature/constants/constants.js +18 -0
  37. package/packages/dd-trace/src/openfeature/index.js +4 -7
  38. package/packages/dd-trace/src/openfeature/writers/base.js +11 -153
  39. package/packages/dd-trace/src/openfeature/writers/exposures.js +14 -62
  40. package/packages/dd-trace/src/openfeature/writers/util.js +15 -97
  41. package/packages/dd-trace/src/opentracing/propagation/text_map.js +13 -22
  42. package/packages/dd-trace/src/telemetry/metrics.js +8 -62
  43. package/packages/dd-trace/src/telemetry/send-data.js +0 -1
  44. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  45. package/vendor/dist/protobufjs/index.js +1 -1
  46. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  47. package/packages/dd-trace/src/debugger/inspect-segment.js +0 -154
  48. package/packages/dd-trace/src/evp_proxy/constants.js +0 -8
  49. package/packages/dd-trace/src/evp_proxy/direct.js +0 -64
  50. package/packages/dd-trace/src/evp_proxy/discovery.js +0 -133
  51. package/packages/dd-trace/src/evp_proxy/path.js +0 -22
  52. package/vendor/dist/https-proxy-agent/LICENSE +0 -22
  53. package/vendor/dist/https-proxy-agent/index.js +0 -4
  54. package/vendor/dist/proxy-from-env/LICENSE +0 -20
  55. package/vendor/dist/proxy-from-env/index.js +0 -1
@@ -2,30 +2,16 @@
2
2
 
3
3
  const { randomUUID } = require('node:crypto')
4
4
 
5
- /** @typedef {{add?: string[], remove?: string[], replace?: string[]}} TagOperations */
6
- /**
7
- * @typedef {object} PendingBatch
8
- * @property {object} attributes
9
- * @property {string[]} deleteRecordIds
10
- * @property {Map<string, object>} insertPayloads
11
- * @property {Map<string, object>} updatePayloads
12
- * @property {number} totalCount
13
- * @property {Map<string, TagOperations>} [inFlightTagOperations]
14
- */
15
-
16
- const { tagOperationsAreEmpty, validateTagsList } = require('./util')
17
-
18
- // Dataset record: { input, expectedOutput?, metadata?, id, tags? }.
5
+ // Dataset record: { input, expectedOutput?, metadata?, id }.
19
6
  // IDs are generated locally unless the caller supplies one.
20
7
  class DatasetRecord {
21
- constructor (input, expectedOutput = null, metadata = {}, id = null, tags = []) {
8
+ constructor (input, expectedOutput = null, metadata = {}, id = null) {
22
9
  if (id != null && (typeof id !== 'string' || id.length === 0)) {
23
10
  throw new Error('record id must be a non-empty string')
24
11
  }
25
12
  this.input = input
26
13
  this.expectedOutput = expectedOutput ?? null
27
14
  this.metadata = metadata ?? {}
28
- this.tags = validateTagsList(tags)
29
15
  this.id = id ?? randomUUID()
30
16
  }
31
17
  }
@@ -35,14 +21,12 @@ function versionFromMutationResult (result) {
35
21
  }
36
22
 
37
23
  function serializedRecord (record) {
38
- const output = {
24
+ return {
39
25
  id: record.id,
40
26
  input: record.input,
41
27
  expected_output: record.expectedOutput ?? null,
42
28
  metadata: record.metadata ?? {},
43
29
  }
44
- if (record.tags.length > 0) output.tags = record.tags
45
- return output
46
30
  }
47
31
 
48
32
  function serializedRecordUpdate (update) {
@@ -52,57 +36,9 @@ function serializedRecordUpdate (update) {
52
36
  output.expected_output = update.expectedOutput
53
37
  }
54
38
  if (Object.hasOwn(update, 'metadata') && update.metadata !== undefined) output.metadata = update.metadata
55
- if (Object.hasOwn(update, 'tagOperations')) {
56
- output.tag_operations = serializedTagOperations(update.tagOperations)
57
- }
58
- return output
59
- }
60
-
61
- function serializedTagOperations (operations) {
62
- const output = {}
63
- if (Object.hasOwn(operations, 'add')) output.add = operations.add
64
- if (Object.hasOwn(operations, 'remove')) output.remove = operations.remove
65
- if (Object.hasOwn(operations, 'replace')) output.set = operations.replace
66
39
  return output
67
40
  }
68
41
 
69
- /**
70
- * @param {TagOperations} operations
71
- * @returns {TagOperations}
72
- */
73
- function copyTagOperations (operations) {
74
- return Object.fromEntries(Object.entries(operations).map(([key, tags]) => [key, [...tags]]))
75
- }
76
-
77
- function mergeTagOperations (operations, operation, tags) {
78
- if (operation === 'replace') return { replace: [...tags] }
79
- if (Object.hasOwn(operations, 'replace')) {
80
- const replaced = new Set(operations.replace)
81
- for (const tag of tags) {
82
- if (operation === 'add') replaced.add(tag)
83
- else replaced.delete(tag)
84
- }
85
- return { replace: [...replaced].sort() }
86
- }
87
-
88
- const add = new Set(operations.add)
89
- const remove = new Set(operations.remove)
90
- for (const tag of tags) {
91
- if (operation === 'add') {
92
- if (remove.has(tag)) remove.delete(tag)
93
- else add.add(tag)
94
- } else if (add.has(tag)) {
95
- add.delete(tag)
96
- } else {
97
- remove.add(tag)
98
- }
99
- }
100
- const merged = {}
101
- if (add.size > 0) merged.add = [...add].sort()
102
- if (remove.size > 0) merged.remove = [...remove].sort()
103
- return merged
104
- }
105
-
106
42
  function valuesAreEqual (left, right) {
107
43
  return JSON.stringify(left) === JSON.stringify(right)
108
44
  }
@@ -127,25 +63,21 @@ class Dataset {
127
63
  #newRecordsById
128
64
  #updatedRecordsById
129
65
  #deletedRecordIds
130
- #pendingTagOperations
131
66
  #id
132
67
  #projectId
133
68
  #version
134
69
  #latestVersion
135
70
  #pushPromise
136
- #filterTags
137
71
 
138
- constructor (client, name, description = '', filterTags = []) {
72
+ constructor (client, name, description = '') {
139
73
  this.#client = client
140
74
  this.#name = name
141
75
  this.#description = description
142
- this.#filterTags = validateTagsList(filterTags)
143
76
  this.#records = []
144
77
  this.#recordsById = new Map()
145
78
  this.#newRecordsById = new Map()
146
79
  this.#updatedRecordsById = new Map()
147
80
  this.#deletedRecordIds = new Set()
148
- this.#pendingTagOperations = new Map()
149
81
  this.#id = null
150
82
  this.#projectId = null
151
83
  this.#version = null
@@ -154,8 +86,8 @@ class Dataset {
154
86
  }
155
87
 
156
88
  // Build a Dataset that already exists remotely (used by pullDataset).
157
- static fromExisting (client, name, description, id, projectId, records, version, latestVersion, filterTags) {
158
- const dataset = new Dataset(client, name, description, filterTags)
89
+ static fromExisting (client, name, description, id, projectId, records, version, latestVersion) {
90
+ const dataset = new Dataset(client, name, description)
159
91
  dataset.#id = id
160
92
  dataset.#projectId = projectId
161
93
  dataset.#version = version ?? null
@@ -165,70 +97,14 @@ class Dataset {
165
97
  }
166
98
 
167
99
  // Append a record. Accepts a DatasetRecord or (input, expectedOutput?, metadata?).
168
- addRecord (recordOrInput, expectedOutput, metadata, tags) {
100
+ addRecord (recordOrInput, expectedOutput, metadata) {
169
101
  const record = recordOrInput instanceof DatasetRecord
170
102
  ? recordOrInput
171
- : new DatasetRecord(recordOrInput, expectedOutput, metadata, null, tags)
103
+ : new DatasetRecord(recordOrInput, expectedOutput, metadata)
172
104
  this.#addRecord(record)
173
105
  return this
174
106
  }
175
107
 
176
- /**
177
- * Add tags to a dataset record.
178
- * @param {number} index Dataset record index.
179
- * @param {string[]} tags Tags in key:value format.
180
- * @returns {Dataset} This dataset for chaining.
181
- */
182
- addTags (index, tags) {
183
- const validated = validateTagsList(tags)
184
- const record = this.#recordAt(index)
185
- const next = new Set(record.tags)
186
- const added = []
187
- for (const tag of validated) {
188
- if (next.has(tag)) continue
189
- next.add(tag)
190
- added.push(tag)
191
- }
192
- record.tags = [...next].sort()
193
- this.#queueTagOperation(record.id, 'add', added)
194
- return this
195
- }
196
-
197
- /**
198
- * Remove tags from a dataset record.
199
- * @param {number} index Dataset record index.
200
- * @param {string[]} tags Tags in key:value format.
201
- * @returns {Dataset} This dataset for chaining.
202
- */
203
- removeTags (index, tags) {
204
- const validated = validateTagsList(tags)
205
- const record = this.#recordAt(index)
206
- const next = new Set(record.tags)
207
- const removed = []
208
- for (const tag of validated) {
209
- if (!next.has(tag)) continue
210
- next.delete(tag)
211
- removed.push(tag)
212
- }
213
- record.tags = [...next].sort()
214
- this.#queueTagOperation(record.id, 'remove', removed)
215
- return this
216
- }
217
-
218
- /**
219
- * Replace all tags on a dataset record.
220
- * @param {number} index Dataset record index.
221
- * @param {string[]} tags Tags in key:value format.
222
- * @returns {Dataset} This dataset for chaining.
223
- */
224
- replaceTags (index, tags) {
225
- const validated = validateTagsList(tags)
226
- const record = this.#recordAt(index)
227
- record.tags = validated
228
- this.#queueTagOperation(record.id, 'replace', validated)
229
- return this
230
- }
231
-
232
108
  /**
233
109
  * Update an existing dataset record. New records are updated in place and are sent with their insert.
234
110
  * @param {number} index Dataset record index.
@@ -254,9 +130,6 @@ class Dataset {
254
130
 
255
131
  const update = this.#updatedRecordsById.get(record.id) ?? { id: record.id }
256
132
  for (const field of providedFields) update[field] = record[field]
257
- if (this.#pendingTagOperations.has(record.id)) {
258
- update.tagOperations = this.#pendingTagOperations.get(record.id)
259
- }
260
133
  this.#updatedRecordsById.set(record.id, update)
261
134
  return this
262
135
  }
@@ -271,47 +144,13 @@ class Dataset {
271
144
  this.#records.splice(index, 1)
272
145
  this.#recordsById.delete(record.id)
273
146
 
274
- if (this.#newRecordsById.delete(record.id)) {
275
- this.#pendingTagOperations.delete(record.id)
276
- return this
277
- }
147
+ if (this.#newRecordsById.delete(record.id)) return this
278
148
 
279
149
  this.#updatedRecordsById.delete(record.id)
280
- this.#pendingTagOperations.delete(record.id)
281
150
  this.#deletedRecordIds.add(record.id)
282
151
  return this
283
152
  }
284
153
 
285
- /**
286
- * Queue a tag operation for the next dataset push.
287
- * @param {string} recordId Dataset record id.
288
- * @param {'add' | 'remove' | 'replace'} operation Tag operation to queue.
289
- * @param {string[]} tags Tags in key:value format.
290
- * @returns {void}
291
- */
292
- #queueTagOperation (recordId, operation, tags) {
293
- if (operation !== 'replace' && tags.length === 0) return
294
- const operations = mergeTagOperations(this.#pendingTagOperations.get(recordId) ?? {}, operation, tags)
295
- if (tagOperationsAreEmpty(operations)) {
296
- this.#pendingTagOperations.delete(recordId)
297
- const update = this.#updatedRecordsById.get(recordId)
298
- if (update) {
299
- const hasRecordUpdate = Object.hasOwn(update, 'input') ||
300
- Object.hasOwn(update, 'expectedOutput') ||
301
- Object.hasOwn(update, 'metadata')
302
- delete update.tagOperations
303
- if (!hasRecordUpdate) this.#updatedRecordsById.delete(recordId)
304
- }
305
- return
306
- }
307
- this.#pendingTagOperations.set(recordId, operations)
308
- if (!this.#newRecordsById.has(recordId)) {
309
- const update = this.#updatedRecordsById.get(recordId) ?? { id: recordId }
310
- update.tagOperations = operations
311
- this.#updatedRecordsById.set(recordId, update)
312
- }
313
- }
314
-
315
154
  name () {
316
155
  return this.#name
317
156
  }
@@ -344,14 +183,6 @@ class Dataset {
344
183
  return this.#latestVersion
345
184
  }
346
185
 
347
- /**
348
- * Return the tags used to filter this dataset.
349
- * @returns {string[]} Dataset record filter tags.
350
- */
351
- filterTags () {
352
- return [...this.#filterTags]
353
- }
354
-
355
186
  // Dashboard URL for this dataset, or null until pushed/pulled.
356
187
  url () {
357
188
  if (this.#id === null) return null
@@ -397,13 +228,10 @@ class Dataset {
397
228
  const pending = this.#pendingBatch()
398
229
  if (pending.totalCount === 0) return { pushedCount: 0, totalCount: 0 }
399
230
 
400
- this.#detachCommittedTagOperations(pending)
401
-
402
231
  let result
403
232
  try {
404
233
  result = await this.#client.batchUpdateDatasetRecords(projectId, this.#id, pending.attributes)
405
234
  } catch (err) {
406
- this.#restoreFailedTagOperations(pending)
407
235
  throw new Error(`Failed to push changes to dataset '${this.#name}': ${err.message}`)
408
236
  }
409
237
 
@@ -424,9 +252,6 @@ class Dataset {
424
252
  const updateRecords = []
425
253
  const updatePayloads = new Map()
426
254
  for (const [recordId, update] of this.#updatedRecordsById) {
427
- const tagOperations = this.#pendingTagOperations.get(recordId)
428
- if (tagOperations) update.tagOperations = tagOperations
429
- else delete update.tagOperations
430
255
  const payload = serializedRecordUpdate(update)
431
256
  updateRecords.push(payload)
432
257
  updatePayloads.set(recordId, payload)
@@ -452,58 +277,6 @@ class Dataset {
452
277
  }
453
278
  }
454
279
 
455
- /**
456
- * Detach tag changes sent by this batch so edits made while the request is in flight
457
- * are queued relative to the response that this batch will commit.
458
- * @param {PendingBatch} pending
459
- * @returns {void}
460
- */
461
- #detachCommittedTagOperations (pending) {
462
- pending.inFlightTagOperations = new Map()
463
- for (const [recordId] of pending.insertPayloads) {
464
- const operations = this.#pendingTagOperations.get(recordId)
465
- if (!operations) continue
466
- pending.inFlightTagOperations.set(recordId, copyTagOperations(operations))
467
- this.#pendingTagOperations.delete(recordId)
468
- }
469
- for (const [recordId, payload] of pending.updatePayloads) {
470
- const operations = this.#pendingTagOperations.get(recordId)
471
- if (!operations || !Object.hasOwn(payload, 'tag_operations')) continue
472
-
473
- pending.inFlightTagOperations.set(recordId, copyTagOperations(operations))
474
- this.#pendingTagOperations.delete(recordId)
475
- const update = this.#updatedRecordsById.get(recordId)
476
- if (update) delete update.tagOperations
477
- }
478
- }
479
-
480
- /**
481
- * Restore tag changes when a batch request fails, including edits made while it was in flight.
482
- * @param {PendingBatch} pending
483
- * @returns {void}
484
- */
485
- #restoreFailedTagOperations (pending) {
486
- if (!pending.inFlightTagOperations) return
487
- for (const [recordId, operations] of pending.inFlightTagOperations) {
488
- const record = this.#recordsById.get(recordId)
489
- if (!record || this.#newRecordsById.has(recordId) || this.#deletedRecordIds.has(recordId)) continue
490
-
491
- const update = this.#updatedRecordsById.get(recordId) ?? { id: recordId }
492
- const queuedOperations = this.#pendingTagOperations.get(recordId)
493
- const restoredOperations = queuedOperations
494
- ? { replace: [...record.tags] }
495
- : copyTagOperations(operations)
496
- this.#pendingTagOperations.set(recordId, restoredOperations)
497
- update.tagOperations = restoredOperations
498
- this.#updatedRecordsById.set(recordId, update)
499
- }
500
- }
501
-
502
- /**
503
- * Clear the changes represented by a completed batch while retaining concurrent local edits.
504
- * @param {PendingBatch} pending
505
- * @returns {void}
506
- */
507
280
  #clearCommittedChanges (pending) {
508
281
  for (const [recordId, payload] of pending.insertPayloads) {
509
282
  const current = this.#newRecordsById.get(recordId)
@@ -513,29 +286,19 @@ class Dataset {
513
286
  }
514
287
  if (valuesAreEqual(serializedRecord(current), payload)) {
515
288
  this.#newRecordsById.delete(recordId)
516
- this.#pendingTagOperations.delete(recordId)
517
289
  continue
518
290
  }
519
291
 
520
292
  this.#newRecordsById.delete(recordId)
521
293
  const update = this.#updatedRecordsById.get(recordId) ??
522
294
  updateFromInsertedRecord(recordId, current, payload)
523
- const queuedOperations = this.#pendingTagOperations.get(recordId)
524
- if (queuedOperations) update.tagOperations = queuedOperations
525
295
  this.#updatedRecordsById.set(recordId, update)
526
296
  }
527
297
 
528
298
  for (const [recordId, payload] of pending.updatePayloads) {
529
299
  const current = this.#updatedRecordsById.get(recordId)
530
- const queuedOperations = this.#pendingTagOperations.get(recordId)
531
- if (!current || queuedOperations) continue
532
-
533
- const comparison = { ...current }
534
- const committedOperations = pending.inFlightTagOperations?.get(recordId)
535
- if (committedOperations) comparison.tagOperations = committedOperations
536
- if (valuesAreEqual(serializedRecordUpdate(comparison), payload)) {
300
+ if (current && valuesAreEqual(serializedRecordUpdate(current), payload)) {
537
301
  this.#updatedRecordsById.delete(recordId)
538
- this.#pendingTagOperations.delete(recordId)
539
302
  }
540
303
  }
541
304
 
@@ -578,8 +341,7 @@ class Dataset {
578
341
  record.input,
579
342
  record.expectedOutput,
580
343
  record.metadata,
581
- record.id,
582
- record.tags
344
+ record.id
583
345
  )
584
346
  }
585
347
  if (this.#recordsById.has(record.id)) throw new Error(`Duplicate record id '${record.id}'`)
@@ -13,7 +13,6 @@ const {
13
13
  normalizeEvaluators,
14
14
  mergeTags,
15
15
  normalizeJsonMetricValue,
16
- recordTagsToObject,
17
16
  sleep,
18
17
  stringify,
19
18
  timestampMs,
@@ -21,7 +20,7 @@ const {
21
20
  } = require('./util')
22
21
 
23
22
  // One span per experiment row (LLM Obs experiment span wire format).
24
- function toSpan (row, metadata, ids, spanName, userTags, recordTags) {
23
+ function toSpan (row, metadata, ids, spanName, userTags) {
25
24
  const meta = {
26
25
  input: row.input ?? null,
27
26
  output: row.output ?? null,
@@ -34,20 +33,6 @@ function toSpan (row, metadata, ids, spanName, userTags, recordTags) {
34
33
  meta.error = { type: row.errorType ?? '', message: row.errorMessage ?? '', stack: row.errorStack ?? '' }
35
34
  }
36
35
 
37
- const tags = buildTags({
38
- ...userTags,
39
- ...recordTagsToObject(recordTags),
40
- }, {
41
- experiment_id: ids.experimentId,
42
- run_id: ids.runId,
43
- run_iteration: ids.runIteration,
44
- project_id: ids.projectId,
45
- dataset_id: ids.datasetId,
46
- dataset_record_id: ids.datasetRecordId,
47
- dataset_name: ids.datasetName,
48
- experiment_name: ids.experimentName,
49
- })
50
-
51
36
  return {
52
37
  span_id: row.spanId,
53
38
  trace_id: row.traceId,
@@ -58,7 +43,13 @@ function toSpan (row, metadata, ids, spanName, userTags, recordTags) {
58
43
  duration: row.durationNs,
59
44
  status: row.isError ? 'error' : 'ok',
60
45
  meta,
61
- tags,
46
+ tags: buildTags(userTags, {
47
+ experiment_id: ids.experimentId,
48
+ run_id: ids.runId,
49
+ run_iteration: ids.runIteration,
50
+ dataset_id: ids.datasetId,
51
+ dataset_record_id: ids.datasetRecordId,
52
+ }),
62
53
  }
63
54
  }
64
55
 
@@ -161,8 +152,6 @@ class Experiment {
161
152
  this.#evaluators = normalizeEvaluators(options.evaluators, 'row')
162
153
  this.#summaryEvaluators = normalizeEvaluators(options.summaryEvaluators, 'summary')
163
154
  this.#config = { ...options.config }
164
- const filterTags = this.#dataset.filterTags?.() ?? []
165
- if (filterTags.length > 0) this.#config.filtered_record_tags = filterTags
166
155
  this.#tags = { ...options.tags }
167
156
  this.#metadata = { ...options.metadata }
168
157
  this.#projectId = null
@@ -465,11 +454,9 @@ class Experiment {
465
454
  projectId,
466
455
  datasetId,
467
456
  datasetRecordId,
468
- datasetName: this.#dataset.name(),
469
- experimentName: this.#name,
470
457
  runId,
471
458
  runIteration,
472
- }, this.#task.name || this.#name, this.#tags, record.tags))
459
+ }, this.#task.name || this.#name, this.#tags))
473
460
  }
474
461
  }
475
462
 
@@ -534,7 +521,7 @@ class Experiment {
534
521
  dataset_name: this.#dataset.name(),
535
522
  experiment_name: this.#name,
536
523
  }
537
- const tags = mergeTags(this.#tags, { ...recordTagsToObject(record.tags), ...autoTags })
524
+ const tags = mergeTags(this.#tags, autoTags)
538
525
 
539
526
  const execute = () => this.#runWithRetries(
540
527
  () => this.#task(record.input, this.#config, record.metadata),
@@ -4,7 +4,6 @@ const log = require('../../log')
4
4
  const { ExperimentsClient } = require('./client')
5
5
  const { Dataset, DatasetRecord } = require('./dataset')
6
6
  const { Experiment, ExternalExperiment } = require('./experiment')
7
- const { validateTagsList } = require('./util')
8
7
  const NoopExperiments = require('./noop')
9
8
 
10
9
  // Poll `attempt` with exponential backoff until it returns true or the time
@@ -67,9 +66,7 @@ class Experiments {
67
66
  if (recordIds.has(record.id)) throw new Error(`Duplicate record id '${record.id}'`)
68
67
  recordIds.add(record.id)
69
68
  }
70
- dataset.addRecord(
71
- new DatasetRecord(record.inputData, record.expectedOutput, record.metadata, record.id, record.tags)
72
- )
69
+ dataset.addRecord(new DatasetRecord(record.inputData, record.expectedOutput, record.metadata, record.id))
73
70
  }
74
71
  }
75
72
  return dataset
@@ -77,16 +74,14 @@ class Experiments {
77
74
 
78
75
  // Pull an existing dataset by name (with its records). Polls with exponential
79
76
  // backoff to absorb read-after-write lag; pass `expectedRecordCount` to also
80
- // wait until that many records are readable. Pass `tags` to filter records by
81
- // dataset record tags.
77
+ // wait until that many records are readable.
82
78
  async pullDataset (name, options = {}) {
83
- const { expectedRecordCount, maxWaitMs = 30_000, tags, version } = options
84
- const filterTags = validateTagsList(tags)
79
+ const { expectedRecordCount, maxWaitMs = 30_000, version } = options
85
80
  const projectId = await this.#client.ensureProjectId()
86
81
 
87
82
  let pulledDataset = null
88
83
  let records = []
89
- const datasetVersion = version ?? null
84
+ let datasetVersion = version ?? null
90
85
  let latestVersion = null
91
86
  let lastError = ''
92
87
 
@@ -98,6 +93,7 @@ class Experiments {
98
93
  if (dataset.name() === name) {
99
94
  pulledDataset = dataset
100
95
  latestVersion = dataset.latestVersion()
96
+ datasetVersion = version ?? latestVersion
101
97
  break
102
98
  }
103
99
  }
@@ -111,7 +107,6 @@ class Experiments {
111
107
  // eslint-disable-next-line no-await-in-loop
112
108
  const page = await this.#client.listDatasetRecords(projectId, pulledDataset.id(), {
113
109
  cursor,
114
- tags: filterTags,
115
110
  version: datasetVersion,
116
111
  })
117
112
  for (const record of page.records) recs.push(record)
@@ -144,12 +139,6 @@ class Experiments {
144
139
  )
145
140
  }
146
141
 
147
- for (const record of records) {
148
- if (record.id === null || record.id === undefined || record.id === '') {
149
- throw new Error(`Failed to pull dataset '${name}': backend returned a record without an id`)
150
- }
151
- }
152
-
153
142
  return Dataset.fromExisting(
154
143
  this.#client,
155
144
  name,
@@ -158,8 +147,7 @@ class Experiments {
158
147
  projectId,
159
148
  records,
160
149
  datasetVersion,
161
- latestVersion,
162
- filterTags
150
+ latestVersion
163
151
  )
164
152
  }
165
153
 
@@ -11,29 +11,20 @@ class NoopDataset {
11
11
  #name
12
12
  #description
13
13
  #records
14
- #filterTags
15
14
 
16
15
  constructor (name = '', options = {}) {
17
16
  this.#name = name
18
17
  this.#description = typeof options === 'string' ? options : (options.description ?? '')
19
- this.#filterTags = typeof options === 'string' ? [] : [...(options.filterTags ?? [])]
20
18
  this.#records = (typeof options === 'string' ? [] : (options.records ?? [])).map(record => ({
21
19
  id: record.id ?? null,
22
20
  input: record.inputData,
23
21
  expectedOutput: record.expectedOutput ?? null,
24
22
  metadata: record.metadata ?? {},
25
- tags: [...(record.tags ?? [])],
26
23
  }))
27
24
  }
28
25
 
29
- addRecord (input, expectedOutput, metadata, tags) {
30
- this.#records.push({
31
- id: null,
32
- input,
33
- expectedOutput: expectedOutput ?? null,
34
- metadata: metadata ?? {},
35
- tags: [...(tags ?? [])],
36
- })
26
+ addRecord (input, expectedOutput, metadata) {
27
+ this.#records.push({ id: null, input, expectedOutput: expectedOutput ?? null, metadata: metadata ?? {} })
37
28
  return this
38
29
  }
39
30
 
@@ -55,49 +46,6 @@ class NoopDataset {
55
46
  return Promise.resolve({ pushedCount: 0, totalCount: 0 })
56
47
  }
57
48
 
58
- /**
59
- * Add tags to a dataset record.
60
- * @param {number} index Dataset record index.
61
- * @param {string[]} tags Tags in key:value format.
62
- * @returns {NoopDataset} This dataset for chaining.
63
- */
64
- addTags (index, tags) {
65
- const record = this.#records[index]
66
- if (!record) return this
67
- const normalizedTags = tags ?? []
68
- record.tags = [...new Set([...(record.tags ?? []), ...normalizedTags])].sort()
69
- return this
70
- }
71
-
72
- /**
73
- * Remove tags from a dataset record.
74
- * @param {number} index Dataset record index.
75
- * @param {string[]} tags Tags in key:value format.
76
- * @returns {NoopDataset} This dataset for chaining.
77
- */
78
- removeTags (index, tags) {
79
- const record = this.#records[index]
80
- if (!record) return this
81
- const normalizedTags = tags ?? []
82
- const removed = new Set(normalizedTags)
83
- record.tags = (record.tags ?? []).filter(tag => !removed.has(tag)).sort()
84
- return this
85
- }
86
-
87
- /**
88
- * Replace all tags on a dataset record.
89
- * @param {number} index Dataset record index.
90
- * @param {string[]} tags Tags in key:value format.
91
- * @returns {NoopDataset} This dataset for chaining.
92
- */
93
- replaceTags (index, tags) {
94
- const record = this.#records[index]
95
- if (!record) return this
96
- const normalizedTags = tags ?? []
97
- record.tags = [...normalizedTags]
98
- return this
99
- }
100
-
101
49
  name () {
102
50
  return this.#name
103
51
  }
@@ -122,14 +70,6 @@ class NoopDataset {
122
70
  return null
123
71
  }
124
72
 
125
- /**
126
- * Return the tags used to filter this dataset.
127
- * @returns {string[]} Dataset record filter tags.
128
- */
129
- filterTags () {
130
- return [...this.#filterTags]
131
- }
132
-
133
73
  records () {
134
74
  return [...this.#records]
135
75
  }
@@ -216,9 +156,9 @@ class NoopExperiments {
216
156
  return new NoopDataset(name, options)
217
157
  }
218
158
 
219
- pullDataset (name, options = {}) {
159
+ pullDataset (name) {
220
160
  this.#warn()
221
- return Promise.resolve(new NoopDataset(name, { filterTags: options.tags }))
161
+ return Promise.resolve(new NoopDataset(name))
222
162
  }
223
163
 
224
164
  experiment (options = {}) {