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.
- package/LICENSE-3rdparty.csv +0 -5
- package/index.d.ts +25 -240
- package/package.json +18 -19
- package/packages/datadog-instrumentations/src/cucumber.js +4 -4
- package/packages/datadog-instrumentations/src/mocha/utils.js +2 -5
- package/packages/datadog-instrumentations/src/playwright-reporter.js +1 -149
- package/packages/datadog-instrumentations/src/playwright.js +33 -33
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +8 -6
- package/packages/datadog-plugin-playwright/src/index.js +0 -4
- package/packages/dd-trace/src/appsec/reporter.js +0 -2
- package/packages/dd-trace/src/appsec/telemetry/index.js +0 -13
- package/packages/dd-trace/src/appsec/telemetry/rasp.js +0 -16
- package/packages/dd-trace/src/appsec/telemetry/waf.js +0 -24
- package/packages/dd-trace/src/carrier.js +48 -107
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +5 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +0 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +3 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -4
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +4 -6
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +3 -5
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +2 -4
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +2 -4
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +2 -4
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +5 -5
- package/packages/dd-trace/src/debugger/constants.js +0 -1
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/index.js +10 -3
- package/packages/dd-trace/src/debugger/index.js +2 -4
- package/packages/dd-trace/src/exporters/common/request.js +2 -5
- package/packages/dd-trace/src/llmobs/experiments/client.js +2 -6
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +11 -249
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +10 -23
- package/packages/dd-trace/src/llmobs/experiments/index.js +6 -18
- package/packages/dd-trace/src/llmobs/experiments/noop.js +4 -64
- package/packages/dd-trace/src/llmobs/experiments/util.js +4 -60
- package/packages/dd-trace/src/openfeature/constants/constants.js +18 -0
- package/packages/dd-trace/src/openfeature/index.js +4 -7
- package/packages/dd-trace/src/openfeature/writers/base.js +11 -153
- package/packages/dd-trace/src/openfeature/writers/exposures.js +14 -62
- package/packages/dd-trace/src/openfeature/writers/util.js +15 -97
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +13 -22
- package/packages/dd-trace/src/telemetry/metrics.js +8 -62
- package/packages/dd-trace/src/telemetry/send-data.js +0 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/packages/dd-trace/src/debugger/inspect-segment.js +0 -154
- package/packages/dd-trace/src/evp_proxy/constants.js +0 -8
- package/packages/dd-trace/src/evp_proxy/direct.js +0 -64
- package/packages/dd-trace/src/evp_proxy/discovery.js +0 -133
- package/packages/dd-trace/src/evp_proxy/path.js +0 -22
- package/vendor/dist/https-proxy-agent/LICENSE +0 -22
- package/vendor/dist/https-proxy-agent/index.js +0 -4
- package/vendor/dist/proxy-from-env/LICENSE +0 -20
- 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
|
-
|
|
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
|
|
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
|
-
|
|
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 = ''
|
|
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
|
|
158
|
-
const dataset = new Dataset(client, name, description
|
|
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
|
|
100
|
+
addRecord (recordOrInput, expectedOutput, metadata) {
|
|
169
101
|
const record = recordOrInput instanceof DatasetRecord
|
|
170
102
|
? recordOrInput
|
|
171
|
-
: new DatasetRecord(recordOrInput, expectedOutput, metadata
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
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.
|
|
81
|
-
// dataset record tags.
|
|
77
|
+
// wait until that many records are readable.
|
|
82
78
|
async pullDataset (name, options = {}) {
|
|
83
|
-
const { expectedRecordCount, maxWaitMs = 30_000,
|
|
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
|
-
|
|
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
|
|
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
|
|
159
|
+
pullDataset (name) {
|
|
220
160
|
this.#warn()
|
|
221
|
-
return Promise.resolve(new NoopDataset(name
|
|
161
|
+
return Promise.resolve(new NoopDataset(name))
|
|
222
162
|
}
|
|
223
163
|
|
|
224
164
|
experiment (options = {}) {
|