dd-trace 6.18.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/LICENSE-3rdparty.csv +1 -0
- package/ci/vitest-no-worker-init-setup.mjs +22 -12
- package/index.d.ts +166 -24
- package/package.json +7 -7
- package/packages/datadog-esbuild/index.js +9 -6
- package/packages/datadog-instrumentations/src/ai.js +26 -0
- package/packages/datadog-instrumentations/src/anthropic.js +111 -9
- package/packages/datadog-instrumentations/src/aws-sdk.js +2 -2
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +5 -1
- package/packages/datadog-instrumentations/src/cucumber.js +40 -4
- 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-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +8 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +2 -0
- package/packages/datadog-instrumentations/src/jest/session-error.js +102 -0
- package/packages/datadog-instrumentations/src/jest.js +3 -10
- package/packages/datadog-instrumentations/src/playwright.js +26 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +4 -1
- package/packages/datadog-instrumentations/src/vitest-worker.js +34 -3
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +83 -29
- package/packages/datadog-plugin-cucumber/src/index.js +19 -4
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +7 -0
- package/packages/datadog-plugin-fetch/src/index.js +3 -0
- package/packages/datadog-plugin-openai/src/stream-helpers.js +50 -1
- package/packages/datadog-plugin-oracledb/src/connection-parser.js +3 -1
- package/packages/datadog-plugin-undici/src/index.js +5 -0
- package/packages/dd-trace/src/aiguard/client.js +38 -19
- package/packages/dd-trace/src/aiguard/integrations/anthropic.js +29 -4
- package/packages/dd-trace/src/aiguard/integrations/index.js +1 -1
- package/packages/dd-trace/src/aiguard/integrations/openai.js +20 -56
- package/packages/dd-trace/src/aiguard/integrations/stream.js +60 -0
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +64 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +8 -4
- package/packages/dd-trace/src/config/remote_config.js +12 -0
- package/packages/dd-trace/src/config/supported-configurations.json +26 -5
- package/packages/dd-trace/src/constants.js +2 -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 +33 -8
- 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/gen-ai-tags.js +127 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +18 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +33 -8
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +77 -41
- package/packages/dd-trace/src/llmobs/plugins/base.js +154 -15
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +162 -10
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +65 -16
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +14 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +34 -35
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +10 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +25 -2
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +8 -7
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +15 -2
- package/packages/dd-trace/src/llmobs/plugins/openai/realtime.js +5 -0
- package/packages/dd-trace/src/llmobs/plugins/vertexai.js +7 -0
- package/packages/dd-trace/src/llmobs/prompts/prompt.js +2 -1
- package/packages/dd-trace/src/llmobs/sdk.js +19 -3
- package/packages/dd-trace/src/llmobs/span_processor.js +52 -91
- package/packages/dd-trace/src/llmobs/tagger.js +37 -36
- 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/index.js +5 -0
- 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/plugins/util/test.js +4 -0
- package/packages/dd-trace/src/priority_sampler.js +155 -33
- package/packages/dd-trace/src/profiler.js +51 -5
- package/packages/dd-trace/src/profiling/profiler.js +24 -20
- package/packages/dd-trace/src/remote_config/capabilities.js +9 -0
- package/packages/dd-trace/src/ritm.js +12 -9
- 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 +32 -3
- 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/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/packages/datadog-instrumentations/src/postgres.js +0 -7
- package/packages/datadog-instrumentations/src/supabase.js +0 -15
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// Used by both the main thread, which renders log template values, and the devtools client worker, which captures
|
|
4
|
+
// snapshots and compiles probe expressions, so that both redact the same identifiers.
|
|
5
|
+
|
|
6
|
+
// What a redacted value is rendered as in a log message.
|
|
7
|
+
const REDACTED_PLACEHOLDER = '{redacted}'
|
|
8
|
+
|
|
9
|
+
const DEFAULT_REDACTED_IDENTIFIERS = [
|
|
10
|
+
'2fa',
|
|
11
|
+
'_csrf',
|
|
12
|
+
'_csrf_token',
|
|
13
|
+
'_session',
|
|
14
|
+
'_xsrf',
|
|
15
|
+
'access_token',
|
|
16
|
+
'aiohttp_session',
|
|
17
|
+
'api_key',
|
|
18
|
+
'apisecret',
|
|
19
|
+
'apisignature',
|
|
20
|
+
'applicationkey',
|
|
21
|
+
'appkey',
|
|
22
|
+
'auth',
|
|
23
|
+
'authtoken',
|
|
24
|
+
'authorization',
|
|
25
|
+
'cc_number',
|
|
26
|
+
'certificatepin',
|
|
27
|
+
'cipher',
|
|
28
|
+
'client_secret',
|
|
29
|
+
'clientid',
|
|
30
|
+
'connect.sid',
|
|
31
|
+
'connectionstring',
|
|
32
|
+
'cookie',
|
|
33
|
+
'credentials',
|
|
34
|
+
'creditcard',
|
|
35
|
+
'csrf',
|
|
36
|
+
'csrf_token',
|
|
37
|
+
'cvv',
|
|
38
|
+
'databaseurl',
|
|
39
|
+
'db_url',
|
|
40
|
+
'encryption_key',
|
|
41
|
+
'encryptionkeyid',
|
|
42
|
+
'geo_location',
|
|
43
|
+
'gpg_key',
|
|
44
|
+
'ip_address',
|
|
45
|
+
'jti',
|
|
46
|
+
'jwt',
|
|
47
|
+
'license_key',
|
|
48
|
+
'masterkey',
|
|
49
|
+
'mysql_pwd',
|
|
50
|
+
'nonce',
|
|
51
|
+
'oauth',
|
|
52
|
+
'oauthtoken',
|
|
53
|
+
'otp',
|
|
54
|
+
'passhash',
|
|
55
|
+
'passwd',
|
|
56
|
+
'password',
|
|
57
|
+
'passwordb',
|
|
58
|
+
'pem_file',
|
|
59
|
+
'pgp_key',
|
|
60
|
+
'PHPSESSID',
|
|
61
|
+
'pin',
|
|
62
|
+
'pincode',
|
|
63
|
+
'pkcs8',
|
|
64
|
+
'private_key',
|
|
65
|
+
'publickey',
|
|
66
|
+
'pwd',
|
|
67
|
+
'recaptcha_key',
|
|
68
|
+
'refresh_token',
|
|
69
|
+
'routingnumber',
|
|
70
|
+
'salt',
|
|
71
|
+
'secret',
|
|
72
|
+
'secretKey',
|
|
73
|
+
'secrettoken',
|
|
74
|
+
'securitycode',
|
|
75
|
+
'security_answer',
|
|
76
|
+
'security_question',
|
|
77
|
+
'serviceaccountcredentials',
|
|
78
|
+
'session',
|
|
79
|
+
'sessionid',
|
|
80
|
+
'sessionkey',
|
|
81
|
+
'set_cookie',
|
|
82
|
+
'signature',
|
|
83
|
+
'signaturekey',
|
|
84
|
+
'ssh_key',
|
|
85
|
+
'ssn',
|
|
86
|
+
'symfony',
|
|
87
|
+
'token',
|
|
88
|
+
'transactionid',
|
|
89
|
+
'twilio_token',
|
|
90
|
+
'user_session',
|
|
91
|
+
'voterid',
|
|
92
|
+
'x-auth-token',
|
|
93
|
+
'x_api_key',
|
|
94
|
+
'x_csrftoken',
|
|
95
|
+
'x_forwarded_for',
|
|
96
|
+
'x_real_ip',
|
|
97
|
+
'XSRF-TOKEN',
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
module.exports = {
|
|
101
|
+
createIsRedactedIdentifier,
|
|
102
|
+
REDACTED_PLACEHOLDER,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Create a predicate for whether the value of an identifier must be redacted, based on the default list of redacted
|
|
107
|
+
* identifiers and the user's configuration.
|
|
108
|
+
*
|
|
109
|
+
* @param {object} config - The `dynamicInstrumentation` config.
|
|
110
|
+
* @param {string[]} config.DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS - Identifiers to redact in addition to the
|
|
111
|
+
* defaults.
|
|
112
|
+
* @param {string[]} config.DD_DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS - Identifiers to never redact.
|
|
113
|
+
* @returns {(name: string, isSymbol?: boolean) => boolean} Whether the identifier must be redacted. If `isSymbol` is
|
|
114
|
+
* `true`, `name` is a `Symbol(...)` description.
|
|
115
|
+
*/
|
|
116
|
+
function createIsRedactedIdentifier ({
|
|
117
|
+
DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS: redactedIdentifiers,
|
|
118
|
+
DD_DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS: excludedIdentifiers,
|
|
119
|
+
}) {
|
|
120
|
+
const excluded = new Set(excludedIdentifiers.map((name) => normalizeName(name)))
|
|
121
|
+
const redacted = new Set()
|
|
122
|
+
for (const name of [...DEFAULT_REDACTED_IDENTIFIERS, ...redactedIdentifiers]) {
|
|
123
|
+
const normalized = normalizeName(name)
|
|
124
|
+
if (!excluded.has(normalized)) redacted.add(normalized)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return function isRedactedIdentifier (name, isSymbol) {
|
|
128
|
+
return redacted.has(normalizeName(name, isSymbol))
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @param {string} name - The identifier name.
|
|
134
|
+
* @param {boolean} [isSymbol] - Whether the name is a `Symbol(...)` description.
|
|
135
|
+
*/
|
|
136
|
+
function normalizeName (name, isSymbol) {
|
|
137
|
+
if (isSymbol) name = name.slice(7, -1) // Remove `Symbol(` and `)`
|
|
138
|
+
return name.toLowerCase().replaceAll(/[-_@$.]/g, '')
|
|
139
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const UINT64_MODULO = 2n ** 64n
|
|
4
|
+
|
|
5
|
+
// Knuth's factor for the sampling algorithm shared across Datadog tracers.
|
|
6
|
+
const SAMPLING_KNUTH_FACTOR = 1_111_111_111_111_111_111n
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Hashes the lower 64 bits of a trace ID for deterministic trace sampling.
|
|
10
|
+
*
|
|
11
|
+
* @param {bigint} traceId
|
|
12
|
+
*/
|
|
13
|
+
function knuthHash (traceId) {
|
|
14
|
+
return (traceId * SAMPLING_KNUTH_FACTOR) % UINT64_MODULO
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = knuthHash
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const INPUT_TOKENS_METRIC_KEY = 'input_tokens'
|
|
4
|
+
const OUTPUT_TOKENS_METRIC_KEY = 'output_tokens'
|
|
5
|
+
const TOTAL_TOKENS_METRIC_KEY = 'total_tokens'
|
|
6
|
+
const CACHE_READ_INPUT_TOKENS_METRIC_KEY = 'cache_read_input_tokens'
|
|
7
|
+
const CACHE_WRITE_INPUT_TOKENS_METRIC_KEY = 'cache_write_input_tokens'
|
|
8
|
+
const CACHE_WRITE_5M_INPUT_TOKENS_METRIC_KEY = 'ephemeral_5m_input_tokens'
|
|
9
|
+
const CACHE_WRITE_1H_INPUT_TOKENS_METRIC_KEY = 'ephemeral_1h_input_tokens'
|
|
10
|
+
const REASONING_OUTPUT_TOKENS_METRIC_KEY = 'reasoning_output_tokens'
|
|
11
|
+
|
|
3
12
|
module.exports = {
|
|
4
13
|
SPAN_KINDS: ['llm', 'agent', 'workflow', 'task', 'tool', 'embedding', 'retrieval', 'experiment'],
|
|
5
14
|
SPAN_KIND: '_ml_obs.meta.span.kind',
|
|
@@ -42,24 +51,40 @@ module.exports = {
|
|
|
42
51
|
MODEL_NAME: '_ml_obs.meta.model_name',
|
|
43
52
|
MODEL_PROVIDER: '_ml_obs.meta.model_provider',
|
|
44
53
|
UNKNOWN_MODEL_PROVIDER: 'unknown',
|
|
54
|
+
DEFAULT_MODEL: 'custom',
|
|
45
55
|
|
|
46
56
|
INPUT_DOCUMENTS: '_ml_obs.meta.input.documents',
|
|
57
|
+
EXPERIMENT_INPUT: '_ml_obs.meta.input',
|
|
47
58
|
INPUT_MESSAGES: '_ml_obs.meta.input.messages',
|
|
48
59
|
INPUT_VALUE: '_ml_obs.meta.input.value',
|
|
49
60
|
INPUT_PROMPT: '_ml_obs.meta.input.prompt',
|
|
50
61
|
|
|
51
62
|
OUTPUT_DOCUMENTS: '_ml_obs.meta.output.documents',
|
|
63
|
+
EXPERIMENT_OUTPUT: '_ml_obs.meta.output',
|
|
52
64
|
OUTPUT_MESSAGES: '_ml_obs.meta.output.messages',
|
|
53
65
|
OUTPUT_VALUE: '_ml_obs.meta.output.value',
|
|
54
66
|
|
|
55
|
-
INPUT_TOKENS_METRIC_KEY
|
|
56
|
-
OUTPUT_TOKENS_METRIC_KEY
|
|
57
|
-
TOTAL_TOKENS_METRIC_KEY
|
|
58
|
-
CACHE_READ_INPUT_TOKENS_METRIC_KEY
|
|
59
|
-
CACHE_WRITE_INPUT_TOKENS_METRIC_KEY
|
|
60
|
-
CACHE_WRITE_5M_INPUT_TOKENS_METRIC_KEY
|
|
61
|
-
CACHE_WRITE_1H_INPUT_TOKENS_METRIC_KEY
|
|
62
|
-
REASONING_OUTPUT_TOKENS_METRIC_KEY
|
|
67
|
+
INPUT_TOKENS_METRIC_KEY,
|
|
68
|
+
OUTPUT_TOKENS_METRIC_KEY,
|
|
69
|
+
TOTAL_TOKENS_METRIC_KEY,
|
|
70
|
+
CACHE_READ_INPUT_TOKENS_METRIC_KEY,
|
|
71
|
+
CACHE_WRITE_INPUT_TOKENS_METRIC_KEY,
|
|
72
|
+
CACHE_WRITE_5M_INPUT_TOKENS_METRIC_KEY,
|
|
73
|
+
CACHE_WRITE_1H_INPUT_TOKENS_METRIC_KEY,
|
|
74
|
+
REASONING_OUTPUT_TOKENS_METRIC_KEY,
|
|
75
|
+
|
|
76
|
+
// integrations build metric objects with these camelCase spellings. Null prototype: a metric
|
|
77
|
+
// named after an `Object.prototype` member must not resolve to an inherited property.
|
|
78
|
+
METRIC_KEY_ALIASES: Object.assign(Object.create(null), {
|
|
79
|
+
inputTokens: INPUT_TOKENS_METRIC_KEY,
|
|
80
|
+
outputTokens: OUTPUT_TOKENS_METRIC_KEY,
|
|
81
|
+
totalTokens: TOTAL_TOKENS_METRIC_KEY,
|
|
82
|
+
cacheReadTokens: CACHE_READ_INPUT_TOKENS_METRIC_KEY,
|
|
83
|
+
cacheWriteTokens: CACHE_WRITE_INPUT_TOKENS_METRIC_KEY,
|
|
84
|
+
cacheWrite5mTokens: CACHE_WRITE_5M_INPUT_TOKENS_METRIC_KEY,
|
|
85
|
+
cacheWrite1hTokens: CACHE_WRITE_1H_INPUT_TOKENS_METRIC_KEY,
|
|
86
|
+
reasoningOutputTokens: REASONING_OUTPUT_TOKENS_METRIC_KEY,
|
|
87
|
+
}),
|
|
63
88
|
|
|
64
89
|
DROPPED_IO_COLLECTION_ERROR: 'dropped_io',
|
|
65
90
|
|
|
@@ -5,8 +5,23 @@
|
|
|
5
5
|
|
|
6
6
|
const { Dataset, DatasetRecord } = require('./dataset')
|
|
7
7
|
const { ExperimentResult } = require('./result')
|
|
8
|
+
const { RemoteEvaluatorError } = require('./remote-evaluator')
|
|
8
9
|
|
|
9
10
|
const API_BASE_PATH = '/api/v2/llm-obs/v1'
|
|
11
|
+
const EVALUATOR_INFERENCE_API_BASE_PATH = '/api/unstable/llm-obs/v1'
|
|
12
|
+
|
|
13
|
+
class ExperimentsRequestError extends Error {
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} message
|
|
16
|
+
* @param {number} statusCode
|
|
17
|
+
* @param {object} responseBody
|
|
18
|
+
*/
|
|
19
|
+
constructor (message, statusCode, responseBody) {
|
|
20
|
+
super(message)
|
|
21
|
+
this.statusCode = statusCode
|
|
22
|
+
this.responseBody = responseBody
|
|
23
|
+
}
|
|
24
|
+
}
|
|
10
25
|
|
|
11
26
|
// Control-plane host for a Datadog site, e.g.
|
|
12
27
|
// datadoghq.com -> api.datadoghq.com
|
|
@@ -151,7 +166,15 @@ class ExperimentsClient {
|
|
|
151
166
|
|
|
152
167
|
const text = await response.text()
|
|
153
168
|
if (!response.ok) {
|
|
154
|
-
|
|
169
|
+
let responseBody = {}
|
|
170
|
+
try {
|
|
171
|
+
responseBody = text ? JSON.parse(text) : {}
|
|
172
|
+
} catch {}
|
|
173
|
+
throw new ExperimentsRequestError(
|
|
174
|
+
`${method} ${path} failed: HTTP ${response.status} ${text}`,
|
|
175
|
+
response.status,
|
|
176
|
+
responseBody
|
|
177
|
+
)
|
|
155
178
|
}
|
|
156
179
|
return text ? JSON.parse(text) : {}
|
|
157
180
|
}
|
|
@@ -247,6 +270,41 @@ class ExperimentsClient {
|
|
|
247
270
|
return experimentFromResource(this, response?.data ?? null)
|
|
248
271
|
}
|
|
249
272
|
|
|
273
|
+
/**
|
|
274
|
+
* @param {string} evalName
|
|
275
|
+
* @param {object} context
|
|
276
|
+
*/
|
|
277
|
+
evaluatorInfer (evalName, context) {
|
|
278
|
+
const encodedName = encodeURIComponent(evalName)
|
|
279
|
+
const path = `${EVALUATOR_INFERENCE_API_BASE_PATH}/evaluators/${encodedName}/infer`
|
|
280
|
+
return this.jsonApiRequest('POST', path, 'evaluator_inference', { context }).then(response => {
|
|
281
|
+
const attributes = response?.data?.attributes ?? {}
|
|
282
|
+
return {
|
|
283
|
+
value: attributes.value,
|
|
284
|
+
reasoning: attributes.reasoning,
|
|
285
|
+
assessment: attributes.assessment,
|
|
286
|
+
status: attributes.status,
|
|
287
|
+
}
|
|
288
|
+
}).catch(err => {
|
|
289
|
+
const attributes = err.responseBody?.data?.attributes ?? {}
|
|
290
|
+
const backendError = attributes.error
|
|
291
|
+
if (backendError !== null && typeof backendError === 'object' && !Array.isArray(backendError)) {
|
|
292
|
+
const fallback = err.statusCode === undefined ? err.message : `HTTP ${err.statusCode}`
|
|
293
|
+
throw new RemoteEvaluatorError(
|
|
294
|
+
`Remote evaluator '${evalName}' failed: ${backendError.message ?? fallback}`,
|
|
295
|
+
{ status: attributes.status, backendError }
|
|
296
|
+
)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const apiError = Array.isArray(err.responseBody?.errors) ? err.responseBody.errors[0] : undefined
|
|
300
|
+
const detail = apiError !== null && typeof apiError === 'object'
|
|
301
|
+
? apiError.detail ?? apiError.title
|
|
302
|
+
: undefined
|
|
303
|
+
const fallback = err.statusCode === undefined ? err.message : `HTTP ${err.statusCode}`
|
|
304
|
+
throw new Error(`Failed to call evaluator '${evalName}': ${detail ?? fallback}`)
|
|
305
|
+
})
|
|
306
|
+
}
|
|
307
|
+
|
|
250
308
|
postExperimentEvents (experimentId, attributes) {
|
|
251
309
|
return this.jsonApiRequest(
|
|
252
310
|
'POST',
|
|
@@ -278,4 +336,10 @@ class ExperimentsClient {
|
|
|
278
336
|
}
|
|
279
337
|
}
|
|
280
338
|
|
|
281
|
-
module.exports = {
|
|
339
|
+
module.exports = {
|
|
340
|
+
ExperimentsClient,
|
|
341
|
+
apiHost,
|
|
342
|
+
appHost,
|
|
343
|
+
API_BASE_PATH,
|
|
344
|
+
EVALUATOR_INFERENCE_API_BASE_PATH,
|
|
345
|
+
}
|
|
@@ -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
|
+
}
|