dd-trace 6.19.0 → 6.20.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/index.d.ts +163 -22
- package/package.json +2 -2
- package/packages/datadog-esbuild/index.js +9 -6
- package/packages/datadog-instrumentations/src/ai.js +26 -0
- package/packages/datadog-instrumentations/src/aws-sdk.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +0 -7
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +25 -0
- package/packages/datadog-plugin-openai/src/stream-helpers.js +50 -1
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
- package/packages/dd-trace/src/config/remote_config.js +12 -0
- package/packages/dd-trace/src/config/supported-configurations.json +21 -0
- package/packages/dd-trace/src/debugger/constants.js +13 -4
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +14 -1
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +109 -6
- package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +65 -6
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +22 -10
- package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +12 -8
- package/packages/dd-trace/src/debugger/devtools_client/send.js +14 -0
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +43 -5
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +3 -8
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +2 -122
- package/packages/dd-trace/src/debugger/guardrail-metrics.js +0 -2
- package/packages/dd-trace/src/debugger/index.js +60 -28
- package/packages/dd-trace/src/debugger/inspect-segment.js +92 -23
- package/packages/dd-trace/src/debugger/pause-duration-histogram.js +74 -0
- package/packages/dd-trace/src/debugger/probe_sampler.js +192 -52
- package/packages/dd-trace/src/debugger/redaction.js +139 -0
- package/packages/dd-trace/src/knuth-hash.js +17 -0
- package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
- package/packages/dd-trace/src/llmobs/experiments/client.js +66 -2
- package/packages/dd-trace/src/llmobs/experiments/evaluator.js +138 -0
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +213 -90
- package/packages/dd-trace/src/llmobs/experiments/index.js +40 -1
- package/packages/dd-trace/src/llmobs/experiments/noop.js +53 -2
- package/packages/dd-trace/src/llmobs/experiments/remote-evaluator.js +110 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +34 -6
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +2 -2
- package/packages/dd-trace/src/llmobs/sdk.js +19 -3
- package/packages/dd-trace/src/llmobs/span_processor.js +42 -26
- package/packages/dd-trace/src/llmobs/tagger.js +35 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -6
- package/packages/dd-trace/src/openfeature/constants/constants.js +10 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +14 -1
- package/packages/dd-trace/src/openfeature/writers/base.js +39 -18
- package/packages/dd-trace/src/openfeature/writers/flag-eval-evp-hook.js +105 -0
- package/packages/dd-trace/src/openfeature/writers/flag-evaluation-aggregation.js +190 -0
- package/packages/dd-trace/src/openfeature/writers/flag-evaluation-consumer.js +233 -0
- package/packages/dd-trace/src/openfeature/writers/flag-evaluation-context.js +262 -0
- package/packages/dd-trace/src/openfeature/writers/flag-evaluation-payload.js +181 -0
- package/packages/dd-trace/src/openfeature/writers/flag-evaluation-pii.js +108 -0
- package/packages/dd-trace/src/openfeature/writers/flag-evaluation-telemetry.js +154 -0
- package/packages/dd-trace/src/openfeature/writers/flag-evaluation-worker.js +77 -0
- package/packages/dd-trace/src/openfeature/writers/flag-evaluations.js +398 -0
- package/packages/dd-trace/src/openfeature/writers/util.js +5 -4
- package/packages/dd-trace/src/opentelemetry/context_manager.js +3 -0
- package/packages/dd-trace/src/opentelemetry/span_context.js +6 -2
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +6 -0
- package/packages/dd-trace/src/opentelemetry/tracer.js +16 -18
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +116 -25
- package/packages/dd-trace/src/opentracing/propagation/tracestate.js +80 -16
- package/packages/dd-trace/src/opentracing/span.js +14 -9
- package/packages/dd-trace/src/opentracing/tracer.js +10 -3
- package/packages/dd-trace/src/otel-sampling.js +171 -0
- package/packages/dd-trace/src/plugins/index.js +0 -1
- package/packages/dd-trace/src/priority_sampler.js +155 -33
- package/packages/dd-trace/src/remote_config/capabilities.js +9 -0
- package/packages/dd-trace/src/sampler.js +2 -5
- package/packages/dd-trace/src/sampling_rule.js +4 -8
- package/packages/dd-trace/src/span_format.js +6 -0
- package/packages/dd-trace/src/span_processor.js +26 -2
- package/packages/dd-trace/src/standalone/index.js +5 -4
- package/packages/dd-trace/src/standalone/tracesource_priority_sampler.js +25 -5
- package/packages/dd-trace/src/telemetry/metrics.js +4 -3
- package/packages/datadog-instrumentations/src/postgres.js +0 -7
- package/packages/datadog-instrumentations/src/supabase.js +0 -15
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { validateEvaluatorName } = require('./util')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {object} evaluator
|
|
7
|
+
* @param {string | undefined} name
|
|
8
|
+
*/
|
|
9
|
+
function resolveEvaluatorName (evaluator, name) {
|
|
10
|
+
const evaluatorName = name === undefined
|
|
11
|
+
? evaluator.constructor.name
|
|
12
|
+
: typeof name === 'string' ? name.trim() : name
|
|
13
|
+
validateEvaluatorName(evaluatorName)
|
|
14
|
+
return evaluatorName
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Context passed to a record-level class evaluator.
|
|
19
|
+
*/
|
|
20
|
+
class EvaluatorContext {
|
|
21
|
+
/**
|
|
22
|
+
* @param {{inputData: unknown, outputData: unknown, expectedOutput?: unknown, metadata?: object,
|
|
23
|
+
* spanId?: string, traceId?: string}} options
|
|
24
|
+
*/
|
|
25
|
+
constructor ({ inputData, outputData, expectedOutput, metadata = {}, spanId, traceId }) {
|
|
26
|
+
this.inputData = inputData
|
|
27
|
+
this.outputData = outputData
|
|
28
|
+
this.expectedOutput = expectedOutput
|
|
29
|
+
this.metadata = metadata
|
|
30
|
+
this.spanId = spanId
|
|
31
|
+
this.traceId = traceId
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Context passed to a summary class evaluator.
|
|
37
|
+
*/
|
|
38
|
+
class SummaryEvaluatorContext {
|
|
39
|
+
/**
|
|
40
|
+
* @param {{inputs: unknown[], outputs: unknown[], expectedOutputs: unknown[], evaluationResults: object,
|
|
41
|
+
* metadata?: object[]}} options
|
|
42
|
+
*/
|
|
43
|
+
constructor ({ inputs, outputs, expectedOutputs, evaluationResults, metadata = [] }) {
|
|
44
|
+
this.inputs = inputs
|
|
45
|
+
this.outputs = outputs
|
|
46
|
+
this.expectedOutputs = expectedOutputs
|
|
47
|
+
this.evaluationResults = evaluationResults
|
|
48
|
+
this.metadata = metadata
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* A metric value with optional evaluation details.
|
|
54
|
+
*/
|
|
55
|
+
class EvaluatorResult {
|
|
56
|
+
/**
|
|
57
|
+
* @param {unknown} value
|
|
58
|
+
* @param {{reasoning?: string, assessment?: string, metadata?: object, tags?: object}} [options]
|
|
59
|
+
*/
|
|
60
|
+
constructor (value, options = {}) {
|
|
61
|
+
this.value = value
|
|
62
|
+
this.reasoning = options.reasoning
|
|
63
|
+
this.assessment = options.assessment
|
|
64
|
+
this.metadata = options.metadata
|
|
65
|
+
this.tags = options.tags
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A result that emits several named metrics from one evaluator invocation.
|
|
71
|
+
*/
|
|
72
|
+
class MultiEvaluatorResult {
|
|
73
|
+
/**
|
|
74
|
+
* @param {Record<string, unknown>} values
|
|
75
|
+
* @param {boolean} [prefix]
|
|
76
|
+
*/
|
|
77
|
+
constructor (values, prefix = true) {
|
|
78
|
+
if (values === null || typeof values !== 'object' || Array.isArray(values)) {
|
|
79
|
+
throw new TypeError('MultiEvaluatorResult.values must be an object')
|
|
80
|
+
}
|
|
81
|
+
const keys = Object.keys(values)
|
|
82
|
+
if (keys.length === 0) throw new Error('MultiEvaluatorResult.values must be a non-empty object')
|
|
83
|
+
for (const key of keys) validateEvaluatorName(key)
|
|
84
|
+
this.values = values
|
|
85
|
+
this.prefix = prefix
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Base class for reusable record-level evaluators.
|
|
91
|
+
*
|
|
92
|
+
* Subclasses may implement evaluate() synchronously or asynchronously.
|
|
93
|
+
*/
|
|
94
|
+
class BaseEvaluator {
|
|
95
|
+
/**
|
|
96
|
+
* @param {string} [name] Evaluator name. Defaults to the subclass name.
|
|
97
|
+
*/
|
|
98
|
+
constructor (name) {
|
|
99
|
+
this.name = resolveEvaluatorName(this, name)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param {EvaluatorContext} _context
|
|
104
|
+
*/
|
|
105
|
+
evaluate (_context) {
|
|
106
|
+
throw new Error('BaseEvaluator subclasses must implement evaluate(context)')
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Base class for reusable summary evaluators.
|
|
112
|
+
*
|
|
113
|
+
* Subclasses may implement evaluate() synchronously or asynchronously.
|
|
114
|
+
*/
|
|
115
|
+
class BaseSummaryEvaluator {
|
|
116
|
+
/**
|
|
117
|
+
* @param {string} [name] Evaluator name. Defaults to the subclass name.
|
|
118
|
+
*/
|
|
119
|
+
constructor (name) {
|
|
120
|
+
this.name = resolveEvaluatorName(this, name)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @param {SummaryEvaluatorContext} _context
|
|
125
|
+
*/
|
|
126
|
+
evaluate (_context) {
|
|
127
|
+
throw new Error('BaseSummaryEvaluator subclasses must implement evaluate(context)')
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = {
|
|
132
|
+
BaseEvaluator,
|
|
133
|
+
BaseSummaryEvaluator,
|
|
134
|
+
EvaluatorContext,
|
|
135
|
+
EvaluatorResult,
|
|
136
|
+
MultiEvaluatorResult,
|
|
137
|
+
SummaryEvaluatorContext,
|
|
138
|
+
}
|
|
@@ -2,7 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
const id = require('../../id')
|
|
4
4
|
const log = require('../../log')
|
|
5
|
+
const { validateAssessment, validateReasoning } = require('../eval-metric')
|
|
5
6
|
|
|
7
|
+
const {
|
|
8
|
+
BaseEvaluator,
|
|
9
|
+
BaseSummaryEvaluator,
|
|
10
|
+
EvaluatorContext,
|
|
11
|
+
EvaluatorResult,
|
|
12
|
+
MultiEvaluatorResult,
|
|
13
|
+
SummaryEvaluatorContext,
|
|
14
|
+
} = require('./evaluator')
|
|
15
|
+
const { RemoteEvaluator, RemoteEvaluatorError } = require('./remote-evaluator')
|
|
6
16
|
const { Row, ExperimentResult, ExperimentRun } = require('./result')
|
|
7
17
|
const {
|
|
8
18
|
buildSpanMetadata,
|
|
@@ -69,7 +79,17 @@ function toSpan (row, metadata, ids, spanName, userTags, recordTags) {
|
|
|
69
79
|
|
|
70
80
|
// One metric per evaluator per row or summary evaluator.
|
|
71
81
|
function toMetric (
|
|
72
|
-
label,
|
|
82
|
+
label,
|
|
83
|
+
value,
|
|
84
|
+
errorMessage,
|
|
85
|
+
spanId,
|
|
86
|
+
traceId,
|
|
87
|
+
timestampMs,
|
|
88
|
+
experimentId,
|
|
89
|
+
userTags,
|
|
90
|
+
source = 'custom',
|
|
91
|
+
ids = {},
|
|
92
|
+
extras = {}
|
|
73
93
|
) {
|
|
74
94
|
const metric = {
|
|
75
95
|
metric_source: source,
|
|
@@ -77,17 +97,30 @@ function toMetric (
|
|
|
77
97
|
span_id: spanId,
|
|
78
98
|
trace_id: traceId,
|
|
79
99
|
timestamp_ms: timestampMs,
|
|
80
|
-
tags: buildTags(userTags, {
|
|
100
|
+
tags: buildTags({ ...userTags, ...extras.tags }, {
|
|
81
101
|
experiment_id: experimentId,
|
|
82
102
|
run_id: ids.runId,
|
|
83
103
|
run_iteration: ids.runIteration,
|
|
104
|
+
project_name: userTags.project_name,
|
|
84
105
|
}),
|
|
85
106
|
experiment_id: experimentId,
|
|
86
107
|
}
|
|
87
108
|
|
|
109
|
+
if (extras.reasoning !== undefined) {
|
|
110
|
+
validateReasoning(extras.reasoning)
|
|
111
|
+
metric.reasoning = extras.reasoning
|
|
112
|
+
}
|
|
113
|
+
if (extras.assessment !== undefined) {
|
|
114
|
+
validateAssessment(extras.assessment)
|
|
115
|
+
metric.assessment = extras.assessment
|
|
116
|
+
}
|
|
117
|
+
if (extras.metadata !== undefined) metric.metadata = extras.metadata
|
|
118
|
+
if (extras.status !== undefined) metric.status = extras.status
|
|
119
|
+
if (extras.evalSourceType !== undefined) metric.eval_source_type = extras.evalSourceType
|
|
120
|
+
|
|
88
121
|
if (errorMessage !== null) {
|
|
89
122
|
metric.metric_type = 'categorical'
|
|
90
|
-
metric.error = { message: errorMessage }
|
|
123
|
+
metric.error = extras.error ?? { message: errorMessage }
|
|
91
124
|
return metric
|
|
92
125
|
}
|
|
93
126
|
|
|
@@ -100,6 +133,31 @@ function toMetric (
|
|
|
100
133
|
return metric
|
|
101
134
|
}
|
|
102
135
|
|
|
136
|
+
/**
|
|
137
|
+
* @param {unknown} result
|
|
138
|
+
* @param {string} label
|
|
139
|
+
* @returns {Array<{label: string, value: unknown, extras: object}>}
|
|
140
|
+
*/
|
|
141
|
+
function extractEvaluatorResults (result, label) {
|
|
142
|
+
if (!(result instanceof MultiEvaluatorResult)) {
|
|
143
|
+
return [{
|
|
144
|
+
label,
|
|
145
|
+
value: result instanceof EvaluatorResult ? result.value : result,
|
|
146
|
+
extras: result instanceof EvaluatorResult ? result : {},
|
|
147
|
+
}]
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const results = []
|
|
151
|
+
for (const [key, value] of Object.entries(result.values)) {
|
|
152
|
+
results.push({
|
|
153
|
+
label: result.prefix ? `${label}-${key}` : key,
|
|
154
|
+
value: value instanceof EvaluatorResult ? value.value : value,
|
|
155
|
+
extras: value instanceof EvaluatorResult ? value : {},
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
return results
|
|
159
|
+
}
|
|
160
|
+
|
|
103
161
|
function createFallbackSpanContext (startNs) {
|
|
104
162
|
// Root-span id generation, same convention as opentracing/span.js: a single
|
|
105
163
|
// random 64-bit id for the span, reused as the trace id's low 64 bits with a
|
|
@@ -543,13 +601,15 @@ class Experiment {
|
|
|
543
601
|
const metrics = []
|
|
544
602
|
const evaluatorResults = {}
|
|
545
603
|
let hasRowError = false
|
|
546
|
-
for (const [label] of this.#evaluators) evaluatorResults[label] = []
|
|
547
604
|
|
|
548
605
|
for (let i = 0; i < results.length; i++) {
|
|
549
606
|
const result = results[i]
|
|
550
607
|
const row = result.row
|
|
551
608
|
rows[i] = row
|
|
552
|
-
for (const
|
|
609
|
+
for (const label of Object.keys(result.evaluatorValues)) {
|
|
610
|
+
if (!Object.hasOwn(evaluatorResults, label)) evaluatorResults[label] = new Array(i).fill(null)
|
|
611
|
+
}
|
|
612
|
+
for (const label of Object.keys(evaluatorResults)) {
|
|
553
613
|
const value = Object.hasOwn(result.evaluatorValues, label) ? result.evaluatorValues[label] : null
|
|
554
614
|
evaluatorResults[label].push(value)
|
|
555
615
|
}
|
|
@@ -671,8 +731,13 @@ class Experiment {
|
|
|
671
731
|
|
|
672
732
|
const evaluatorResults = await Promise.all(pending)
|
|
673
733
|
for (const result of evaluatorResults) {
|
|
674
|
-
|
|
675
|
-
|
|
734
|
+
for (const value of result.values) {
|
|
735
|
+
if (Object.hasOwn(evaluatorValues, value.label)) {
|
|
736
|
+
throw new Error(`Evaluator metric label '${value.label}' was emitted more than once`)
|
|
737
|
+
}
|
|
738
|
+
if (value.metric !== null) metrics.push(value.metric)
|
|
739
|
+
evaluatorValues[value.label] = value.value
|
|
740
|
+
}
|
|
676
741
|
if (result.error !== undefined && firstError === undefined) firstError = result.error
|
|
677
742
|
}
|
|
678
743
|
if (firstError !== undefined) throw firstError
|
|
@@ -703,64 +768,101 @@ class Experiment {
|
|
|
703
768
|
row.evaluationErrors[label] = TASK_ERROR_MESSAGE
|
|
704
769
|
return {
|
|
705
770
|
label,
|
|
706
|
-
|
|
707
|
-
metric: toMetric(
|
|
771
|
+
values: [{
|
|
708
772
|
label,
|
|
709
|
-
null,
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
773
|
+
value: null,
|
|
774
|
+
metric: toMetric(
|
|
775
|
+
label,
|
|
776
|
+
null,
|
|
777
|
+
TASK_ERROR_MESSAGE,
|
|
778
|
+
row.spanId,
|
|
779
|
+
row.traceId,
|
|
780
|
+
timestampMs,
|
|
781
|
+
experimentId,
|
|
782
|
+
this.#tags,
|
|
783
|
+
'custom',
|
|
784
|
+
{ runId, runIteration }
|
|
785
|
+
),
|
|
786
|
+
}],
|
|
787
|
+
error: undefined,
|
|
719
788
|
}
|
|
720
789
|
}
|
|
721
790
|
|
|
791
|
+
const isRemoteEvaluator = evaluator instanceof RemoteEvaluator
|
|
722
792
|
try {
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
793
|
+
let evaluate
|
|
794
|
+
if (evaluator instanceof BaseEvaluator) {
|
|
795
|
+
const context = new EvaluatorContext({
|
|
796
|
+
inputData: record.input,
|
|
797
|
+
outputData: row.output,
|
|
798
|
+
expectedOutput: record.expectedOutput,
|
|
799
|
+
metadata: buildSpanMetadata(record.metadata, this.#config),
|
|
800
|
+
spanId: row.spanId,
|
|
801
|
+
traceId: row.traceId,
|
|
802
|
+
})
|
|
803
|
+
evaluate = isRemoteEvaluator
|
|
804
|
+
? () => evaluator.evaluate(context, this.#client)
|
|
805
|
+
: () => evaluator.evaluate(context)
|
|
806
|
+
} else {
|
|
807
|
+
evaluate = () => evaluator(record.input, row.output, record.expectedOutput)
|
|
808
|
+
}
|
|
809
|
+
const result = await limit(() => this.#runWithRetries(evaluate, maxRetries, retryDelay), throwOnErrors)
|
|
810
|
+
const values = []
|
|
811
|
+
for (const evaluatorResult of extractEvaluatorResults(result, label)) {
|
|
812
|
+
row.evaluations[evaluatorResult.label] = evaluatorResult.value
|
|
813
|
+
values.push({
|
|
814
|
+
label: evaluatorResult.label,
|
|
815
|
+
value: evaluatorResult.value,
|
|
816
|
+
metric: toMetric(
|
|
817
|
+
evaluatorResult.label,
|
|
818
|
+
evaluatorResult.value,
|
|
819
|
+
null,
|
|
820
|
+
row.spanId,
|
|
821
|
+
row.traceId,
|
|
822
|
+
timestampMs,
|
|
823
|
+
experimentId,
|
|
824
|
+
this.#tags,
|
|
825
|
+
'custom',
|
|
826
|
+
{ runId, runIteration },
|
|
827
|
+
evaluatorResult.extras
|
|
828
|
+
),
|
|
829
|
+
})
|
|
744
830
|
}
|
|
831
|
+
return { label, values, error: undefined }
|
|
745
832
|
} catch (err) {
|
|
746
833
|
if (throwOnErrors) throw err
|
|
747
|
-
const
|
|
834
|
+
const backendError = err instanceof RemoteEvaluatorError && hasEntries(err.backendError)
|
|
835
|
+
? err.backendError
|
|
836
|
+
: undefined
|
|
837
|
+
const msg = backendError?.message ?? err.message ?? String(err)
|
|
748
838
|
row.evaluationErrors[label] = msg
|
|
839
|
+
const extras = isRemoteEvaluator
|
|
840
|
+
? {
|
|
841
|
+
error: backendError,
|
|
842
|
+
status: err.status ?? 'ERROR',
|
|
843
|
+
evalSourceType: 'managed',
|
|
844
|
+
}
|
|
845
|
+
: {}
|
|
749
846
|
return {
|
|
750
847
|
label,
|
|
751
|
-
|
|
752
|
-
metric: toMetric(
|
|
848
|
+
values: [{
|
|
753
849
|
label,
|
|
754
|
-
null,
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
850
|
+
value: null,
|
|
851
|
+
metric: toMetric(
|
|
852
|
+
label,
|
|
853
|
+
null,
|
|
854
|
+
msg,
|
|
855
|
+
row.spanId,
|
|
856
|
+
row.traceId,
|
|
857
|
+
timestampMs,
|
|
858
|
+
experimentId,
|
|
859
|
+
this.#tags,
|
|
860
|
+
'custom',
|
|
861
|
+
{ runId, runIteration },
|
|
862
|
+
extras
|
|
863
|
+
),
|
|
864
|
+
}],
|
|
865
|
+
error: undefined,
|
|
764
866
|
}
|
|
765
867
|
}
|
|
766
868
|
}
|
|
@@ -892,7 +994,6 @@ class Experiment {
|
|
|
892
994
|
const summaryEvaluations = {}
|
|
893
995
|
const timestampMs = Date.now()
|
|
894
996
|
const pending = new Array(this.#summaryEvaluators.length)
|
|
895
|
-
let firstError
|
|
896
997
|
|
|
897
998
|
for (let i = 0; i < this.#summaryEvaluators.length; i++) {
|
|
898
999
|
const [label, evaluator] = this.#summaryEvaluators[i]
|
|
@@ -917,14 +1018,14 @@ class Experiment {
|
|
|
917
1018
|
throw err
|
|
918
1019
|
}
|
|
919
1020
|
for (const result of results) {
|
|
920
|
-
|
|
921
|
-
if (
|
|
922
|
-
|
|
1021
|
+
for (const value of result.values) {
|
|
1022
|
+
if (Object.hasOwn(summaryEvaluations, value.label)) {
|
|
1023
|
+
throw new Error(`Summary evaluator metric label '${value.label}' was emitted more than once`)
|
|
1024
|
+
}
|
|
1025
|
+
summaryEvaluations[value.label] = value.evaluation
|
|
1026
|
+
options.metrics.push(value.metric)
|
|
923
1027
|
}
|
|
924
|
-
summaryEvaluations[result.label] = result.evaluation
|
|
925
|
-
options.metrics.push(result.metric)
|
|
926
1028
|
}
|
|
927
|
-
if (firstError !== undefined) throw firstError
|
|
928
1029
|
|
|
929
1030
|
return summaryEvaluations
|
|
930
1031
|
}
|
|
@@ -941,45 +1042,67 @@ class Experiment {
|
|
|
941
1042
|
options,
|
|
942
1043
|
}) {
|
|
943
1044
|
try {
|
|
944
|
-
const
|
|
945
|
-
|
|
1045
|
+
const context = new SummaryEvaluatorContext({
|
|
1046
|
+
inputs,
|
|
1047
|
+
outputs,
|
|
1048
|
+
expectedOutputs,
|
|
1049
|
+
evaluationResults: evaluatorResults,
|
|
1050
|
+
metadata,
|
|
1051
|
+
})
|
|
1052
|
+
const evaluate = evaluator instanceof BaseSummaryEvaluator
|
|
1053
|
+
? () => evaluator.evaluate(context)
|
|
1054
|
+
: () => evaluator(inputs, outputs, expectedOutputs, evaluatorResults, metadata)
|
|
1055
|
+
const result = await options.limit(() => this.#runWithRetries(
|
|
1056
|
+
evaluate,
|
|
946
1057
|
options.maxRetries,
|
|
947
1058
|
options.retryDelay
|
|
948
1059
|
), options.throwOnErrors)
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
evaluation
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1060
|
+
const values = []
|
|
1061
|
+
for (const evaluatorResult of extractEvaluatorResults(result, label)) {
|
|
1062
|
+
const evaluation = { value: evaluatorResult.value, error: null }
|
|
1063
|
+
if (evaluatorResult.extras.reasoning !== undefined) evaluation.reasoning = evaluatorResult.extras.reasoning
|
|
1064
|
+
if (evaluatorResult.extras.assessment !== undefined) evaluation.assessment = evaluatorResult.extras.assessment
|
|
1065
|
+
if (evaluatorResult.extras.metadata !== undefined) evaluation.metadata = evaluatorResult.extras.metadata
|
|
1066
|
+
if (evaluatorResult.extras.tags !== undefined) evaluation.tags = evaluatorResult.extras.tags
|
|
1067
|
+
values.push({
|
|
1068
|
+
label: evaluatorResult.label,
|
|
1069
|
+
evaluation,
|
|
1070
|
+
metric: toMetric(
|
|
1071
|
+
evaluatorResult.label,
|
|
1072
|
+
evaluatorResult.value,
|
|
1073
|
+
null,
|
|
1074
|
+
'',
|
|
1075
|
+
'',
|
|
1076
|
+
timestampMs,
|
|
1077
|
+
options.experimentId,
|
|
1078
|
+
this.#tags,
|
|
1079
|
+
'summary',
|
|
1080
|
+
{ runId: options.runId, runIteration: options.runIteration },
|
|
1081
|
+
evaluatorResult.extras
|
|
1082
|
+
),
|
|
1083
|
+
})
|
|
964
1084
|
}
|
|
1085
|
+
return { values }
|
|
965
1086
|
} catch (err) {
|
|
966
1087
|
if (options.throwOnErrors) throw err
|
|
967
1088
|
const msg = err.message ?? String(err)
|
|
968
1089
|
return {
|
|
969
|
-
|
|
970
|
-
evaluation: { value: null, error: msg },
|
|
971
|
-
metric: toMetric(
|
|
1090
|
+
values: [{
|
|
972
1091
|
label,
|
|
973
|
-
null,
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1092
|
+
evaluation: { value: null, error: msg },
|
|
1093
|
+
metric: toMetric(
|
|
1094
|
+
label,
|
|
1095
|
+
null,
|
|
1096
|
+
msg,
|
|
1097
|
+
'',
|
|
1098
|
+
'',
|
|
1099
|
+
timestampMs,
|
|
1100
|
+
options.experimentId,
|
|
1101
|
+
this.#tags,
|
|
1102
|
+
'summary',
|
|
1103
|
+
{ runId: options.runId, runIteration: options.runIteration }
|
|
1104
|
+
),
|
|
1105
|
+
}],
|
|
983
1106
|
}
|
|
984
1107
|
}
|
|
985
1108
|
}
|
|
@@ -4,6 +4,8 @@ const log = require('../../log')
|
|
|
4
4
|
const { ExperimentsClient } = require('./client')
|
|
5
5
|
const { Dataset } = require('./dataset')
|
|
6
6
|
const { Experiment, ExternalExperiment } = require('./experiment')
|
|
7
|
+
const evaluatorTypes = require('./evaluator')
|
|
8
|
+
const remoteEvaluatorTypes = require('./remote-evaluator')
|
|
7
9
|
const { validateTagsList } = require('./util')
|
|
8
10
|
const NoopExperiments = require('./noop')
|
|
9
11
|
|
|
@@ -40,6 +42,38 @@ class Experiments {
|
|
|
40
42
|
this.#client = this.#clientForProject(this.#projectName)
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
get BaseEvaluator () {
|
|
46
|
+
return evaluatorTypes.BaseEvaluator
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get BaseSummaryEvaluator () {
|
|
50
|
+
return evaluatorTypes.BaseSummaryEvaluator
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get EvaluatorContext () {
|
|
54
|
+
return evaluatorTypes.EvaluatorContext
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get SummaryEvaluatorContext () {
|
|
58
|
+
return evaluatorTypes.SummaryEvaluatorContext
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get EvaluatorResult () {
|
|
62
|
+
return evaluatorTypes.EvaluatorResult
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get MultiEvaluatorResult () {
|
|
66
|
+
return evaluatorTypes.MultiEvaluatorResult
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get RemoteEvaluator () {
|
|
70
|
+
return remoteEvaluatorTypes.RemoteEvaluator
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get RemoteEvaluatorError () {
|
|
74
|
+
return remoteEvaluatorTypes.RemoteEvaluatorError
|
|
75
|
+
}
|
|
76
|
+
|
|
43
77
|
/**
|
|
44
78
|
* @param {string} projectName
|
|
45
79
|
* @returns {ExperimentsClient}
|
|
@@ -220,4 +254,9 @@ function createExperiments (config, llmobs) {
|
|
|
220
254
|
return new Experiments(config, llmobs)
|
|
221
255
|
}
|
|
222
256
|
|
|
223
|
-
module.exports = {
|
|
257
|
+
module.exports = {
|
|
258
|
+
Experiments,
|
|
259
|
+
createExperiments,
|
|
260
|
+
...evaluatorTypes,
|
|
261
|
+
...remoteEvaluatorTypes,
|
|
262
|
+
}
|
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const log = require('../../log')
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
let ExternalExperiment
|
|
6
|
+
let evaluatorTypes
|
|
7
|
+
let remoteEvaluatorTypes
|
|
8
|
+
|
|
9
|
+
function getExternalExperiment () {
|
|
10
|
+
ExternalExperiment ??= require('./experiment').ExternalExperiment
|
|
11
|
+
return ExternalExperiment
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getEvaluatorTypes () {
|
|
15
|
+
evaluatorTypes ??= require('./evaluator')
|
|
16
|
+
return evaluatorTypes
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getRemoteEvaluatorTypes () {
|
|
20
|
+
remoteEvaluatorTypes ??= require('./remote-evaluator')
|
|
21
|
+
return remoteEvaluatorTypes
|
|
22
|
+
}
|
|
5
23
|
|
|
6
24
|
const NOOP_EXPERIMENT_ID = '00000000-0000-0000-0000-000000000000'
|
|
7
25
|
const NOOP_SPAN_ID = '0000000000000000'
|
|
@@ -246,6 +264,38 @@ class NoopExperiments {
|
|
|
246
264
|
log.warn('LLMObs experiments unavailable: %s', this.#reason)
|
|
247
265
|
}
|
|
248
266
|
|
|
267
|
+
get BaseEvaluator () {
|
|
268
|
+
return getEvaluatorTypes().BaseEvaluator
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
get BaseSummaryEvaluator () {
|
|
272
|
+
return getEvaluatorTypes().BaseSummaryEvaluator
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
get EvaluatorContext () {
|
|
276
|
+
return getEvaluatorTypes().EvaluatorContext
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
get SummaryEvaluatorContext () {
|
|
280
|
+
return getEvaluatorTypes().SummaryEvaluatorContext
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
get EvaluatorResult () {
|
|
284
|
+
return getEvaluatorTypes().EvaluatorResult
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
get MultiEvaluatorResult () {
|
|
288
|
+
return getEvaluatorTypes().MultiEvaluatorResult
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
get RemoteEvaluator () {
|
|
292
|
+
return getRemoteEvaluatorTypes().RemoteEvaluator
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
get RemoteEvaluatorError () {
|
|
296
|
+
return getRemoteEvaluatorTypes().RemoteEvaluatorError
|
|
297
|
+
}
|
|
298
|
+
|
|
249
299
|
createDataset (name, options = {}) {
|
|
250
300
|
this.#warn()
|
|
251
301
|
return new NoopDataset(name, options)
|
|
@@ -267,7 +317,8 @@ class NoopExperiments {
|
|
|
267
317
|
*/
|
|
268
318
|
startExperiment (options = {}) {
|
|
269
319
|
this.#warn()
|
|
270
|
-
|
|
320
|
+
const NoopExternalExperiment = getExternalExperiment()
|
|
321
|
+
return Promise.resolve(new NoopExternalExperiment(new NoopExperiment(options.name, true)))
|
|
271
322
|
}
|
|
272
323
|
}
|
|
273
324
|
|