dd-trace 5.79.0 → 5.81.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 +79 -87
- package/ext/tags.d.ts +1 -0
- package/ext/tags.js +1 -0
- package/index.d.ts +46 -39
- package/initialize.mjs +10 -10
- package/loader-hook.mjs +10 -3
- package/package.json +23 -40
- package/packages/datadog-core/src/storage.js +4 -4
- package/packages/datadog-esbuild/index.js +36 -19
- package/packages/datadog-esbuild/src/utils.js +5 -1
- package/packages/datadog-instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/anthropic.js +12 -0
- package/packages/datadog-instrumentations/src/aws-sdk.js +13 -2
- package/packages/datadog-instrumentations/src/azure-service-bus.js +43 -36
- package/packages/datadog-instrumentations/src/cucumber.js +2 -2
- package/packages/datadog-instrumentations/src/find-my-way.js +6 -5
- package/packages/datadog-instrumentations/src/google-genai.js +120 -0
- package/packages/datadog-instrumentations/src/graphql.js +20 -0
- package/packages/datadog-instrumentations/src/helpers/hook.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +12 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +6 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +27 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +152 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +5 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langchain.js +237 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.js +9 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +11 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +139 -0
- package/packages/datadog-instrumentations/src/jest.js +1 -1
- package/packages/datadog-instrumentations/src/langchain.js +3 -109
- package/packages/datadog-instrumentations/src/mocha/main.js +1 -1
- package/packages/datadog-instrumentations/src/mysql2.js +1 -1
- package/packages/datadog-instrumentations/src/playwright.js +65 -16
- package/packages/datadog-instrumentations/src/router.js +1 -1
- package/packages/datadog-instrumentations/src/selenium.js +3 -1
- package/packages/datadog-instrumentations/src/ws.js +35 -17
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +3 -2
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +14 -5
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +23 -2
- package/packages/datadog-plugin-cypress/src/plugin.js +1 -1
- package/packages/datadog-plugin-cypress/src/support.js +73 -31
- package/packages/datadog-plugin-google-genai/src/index.js +17 -0
- package/packages/datadog-plugin-google-genai/src/tracing.js +41 -0
- package/packages/datadog-plugin-graphql/src/tools/transforms.js +5 -4
- package/packages/datadog-plugin-jest/src/util.js +4 -3
- package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +3 -1
- package/packages/datadog-plugin-langchain/src/tracing.js +7 -3
- package/packages/datadog-plugin-next/src/index.js +11 -3
- package/packages/datadog-plugin-openai/src/stream-helpers.js +1 -1
- package/packages/datadog-shimmer/src/shimmer.js +2 -2
- package/packages/dd-trace/src/aiguard/sdk.js +29 -14
- package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
- package/packages/dd-trace/src/appsec/iast/overhead-controller.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -2
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
- package/packages/dd-trace/src/appsec/reporter.js +0 -4
- package/packages/dd-trace/src/baggage.js +11 -0
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +4 -8
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +1 -1
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +4 -2
- package/packages/dd-trace/src/config.js +81 -7
- package/packages/dd-trace/src/config_defaults.js +15 -2
- package/packages/dd-trace/src/datastreams/encoding.js +23 -6
- package/packages/dd-trace/src/datastreams/pathway.js +40 -1
- package/packages/dd-trace/src/datastreams/processor.js +1 -1
- package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +15 -5
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +30 -15
- package/packages/dd-trace/src/debugger/devtools_client/inspector_promises_polyfill.js +2 -0
- package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +24 -18
- package/packages/dd-trace/src/debugger/devtools_client/send.js +18 -8
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +103 -15
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/constants.js +25 -0
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +56 -25
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +64 -23
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/symbols.js +3 -1
- package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +404 -0
- package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/state.js +7 -2
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
- package/packages/dd-trace/src/debugger/index.js +1 -1
- package/packages/dd-trace/src/encode/0.4.js +3 -3
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +2 -2
- package/packages/dd-trace/src/encode/span-stats.js +7 -1
- package/packages/dd-trace/src/exporters/agent/writer.js +6 -13
- package/packages/dd-trace/src/histogram.js +1 -1
- package/packages/dd-trace/src/id.js +60 -0
- package/packages/dd-trace/src/lambda/runtime/ritm.js +2 -3
- package/packages/dd-trace/src/llmobs/constants/tags.js +1 -0
- package/packages/dd-trace/src/llmobs/index.js +5 -5
- package/packages/dd-trace/src/llmobs/noop.js +6 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +1 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +104 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +486 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +2 -2
- package/packages/dd-trace/src/llmobs/plugins/{openai.js → openai/index.js} +87 -39
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +114 -0
- package/packages/dd-trace/src/llmobs/sdk.js +10 -1
- package/packages/dd-trace/src/llmobs/span_processor.js +11 -6
- package/packages/dd-trace/src/llmobs/tagger.js +35 -17
- package/packages/dd-trace/src/msgpack/chunk.js +2 -2
- package/packages/dd-trace/src/msgpack/encoder.js +2 -3
- package/packages/dd-trace/src/msgpack/index.js +2 -2
- package/packages/dd-trace/src/openfeature/flagging_provider.js +5 -3
- package/packages/dd-trace/src/opentelemetry/logs/index.js +3 -3
- package/packages/dd-trace/src/opentelemetry/logs/logger.js +14 -8
- package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +6 -4
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +9 -17
- package/packages/dd-trace/src/opentelemetry/metrics/constants.js +34 -0
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +81 -0
- package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +225 -0
- package/packages/dd-trace/src/opentelemetry/metrics/meter.js +171 -0
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +54 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +62 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +251 -0
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +532 -0
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +10 -18
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +36 -22
- package/packages/dd-trace/src/opentelemetry/otlp/protobuf_loader.js +2 -2
- package/packages/dd-trace/src/opentelemetry/span.js +1 -1
- package/packages/dd-trace/src/opentelemetry/tracer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
- package/packages/dd-trace/src/payload-tagging/index.js +2 -2
- package/packages/dd-trace/src/plugin_manager.js +4 -2
- package/packages/dd-trace/src/plugins/database.js +1 -0
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/plugin.js +7 -9
- package/packages/dd-trace/src/plugins/util/test.js +3 -3
- package/packages/dd-trace/src/plugins/util/url.js +119 -1
- package/packages/dd-trace/src/plugins/util/web.js +10 -41
- package/packages/dd-trace/src/process-tags/index.js +81 -0
- package/packages/dd-trace/src/profiling/config.js +1 -1
- package/packages/dd-trace/src/profiling/exporter_cli.js +7 -6
- package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/events.js +10 -1
- package/packages/dd-trace/src/proxy.js +5 -0
- package/packages/dd-trace/src/rate_limiter.js +1 -1
- package/packages/dd-trace/src/remote_config/manager.js +1 -1
- package/packages/dd-trace/src/require-package-json.js +1 -1
- package/packages/dd-trace/src/ritm.js +1 -1
- package/packages/dd-trace/src/service-naming/index.js +31 -4
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
- package/packages/dd-trace/src/{format.js → span_format.js} +9 -4
- package/packages/dd-trace/src/span_processor.js +16 -11
- package/packages/dd-trace/src/span_stats.js +15 -4
- package/packages/dd-trace/src/spanleak.js +1 -1
- package/packages/dd-trace/src/supported-configurations.json +13 -0
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +11 -2
- package/vendor/dist/@datadog/sketches-js/LICENSE +39 -0
- package/vendor/dist/@datadog/sketches-js/index.js +1 -0
- package/vendor/dist/@datadog/source-map/LICENSE +28 -0
- package/vendor/dist/@datadog/source-map/index.js +1 -0
- package/vendor/dist/@isaacs/ttlcache/LICENSE +55 -0
- package/vendor/dist/@isaacs/ttlcache/index.js +1 -0
- package/vendor/dist/@opentelemetry/core/LICENSE +201 -0
- package/vendor/dist/@opentelemetry/core/index.js +1 -0
- package/vendor/dist/@opentelemetry/resources/LICENSE +201 -0
- package/vendor/dist/@opentelemetry/resources/index.js +1 -0
- package/vendor/dist/astring/LICENSE +19 -0
- package/vendor/dist/astring/index.js +1 -0
- package/vendor/dist/crypto-randomuuid/index.js +1 -0
- package/vendor/dist/escape-string-regexp/LICENSE +9 -0
- package/vendor/dist/escape-string-regexp/index.js +1 -0
- package/vendor/dist/esquery/LICENSE +24 -0
- package/vendor/dist/esquery/index.js +1 -0
- package/vendor/dist/ignore/LICENSE +21 -0
- package/vendor/dist/ignore/index.js +1 -0
- package/vendor/dist/istanbul-lib-coverage/LICENSE +24 -0
- package/vendor/dist/istanbul-lib-coverage/index.js +1 -0
- package/vendor/dist/jest-docblock/LICENSE +21 -0
- package/vendor/dist/jest-docblock/index.js +1 -0
- package/vendor/dist/jsonpath-plus/LICENSE +22 -0
- package/vendor/dist/jsonpath-plus/index.js +1 -0
- package/vendor/dist/limiter/LICENSE +19 -0
- package/vendor/dist/limiter/index.js +1 -0
- package/vendor/dist/lodash.sortby/LICENSE +47 -0
- package/vendor/dist/lodash.sortby/index.js +1 -0
- package/vendor/dist/lru-cache/LICENSE +15 -0
- package/vendor/dist/lru-cache/index.js +1 -0
- package/vendor/dist/meriyah/LICENSE +7 -0
- package/vendor/dist/meriyah/index.js +1 -0
- package/vendor/dist/module-details-from-path/LICENSE +21 -0
- package/vendor/dist/module-details-from-path/index.js +1 -0
- package/vendor/dist/mutexify/promise/LICENSE +21 -0
- package/vendor/dist/mutexify/promise/index.js +1 -0
- package/vendor/dist/opentracing/LICENSE +201 -0
- package/vendor/dist/opentracing/binary_carrier.d.ts +11 -0
- package/vendor/dist/opentracing/constants.d.ts +61 -0
- package/vendor/dist/opentracing/examples/demo/demo.d.ts +2 -0
- package/vendor/dist/opentracing/ext/tags.d.ts +90 -0
- package/vendor/dist/opentracing/functions.d.ts +20 -0
- package/vendor/dist/opentracing/global_tracer.d.ts +14 -0
- package/vendor/dist/opentracing/index.d.ts +12 -0
- package/vendor/dist/opentracing/index.js +1 -0
- package/vendor/dist/opentracing/mock_tracer/index.d.ts +5 -0
- package/vendor/dist/opentracing/mock_tracer/mock_context.d.ts +13 -0
- package/vendor/dist/opentracing/mock_tracer/mock_report.d.ts +16 -0
- package/vendor/dist/opentracing/mock_tracer/mock_span.d.ts +50 -0
- package/vendor/dist/opentracing/mock_tracer/mock_tracer.d.ts +26 -0
- package/vendor/dist/opentracing/noop.d.ts +8 -0
- package/vendor/dist/opentracing/reference.d.ts +33 -0
- package/vendor/dist/opentracing/span.d.ts +147 -0
- package/vendor/dist/opentracing/span_context.d.ts +26 -0
- package/vendor/dist/opentracing/test/api_compatibility.d.ts +16 -0
- package/vendor/dist/opentracing/test/mocktracer_implemenation.d.ts +3 -0
- package/vendor/dist/opentracing/test/noop_implementation.d.ts +4 -0
- package/vendor/dist/opentracing/test/opentracing_api.d.ts +3 -0
- package/vendor/dist/opentracing/test/unittest.d.ts +2 -0
- package/vendor/dist/opentracing/tracer.d.ts +127 -0
- package/vendor/dist/path-to-regexp/LICENSE +21 -0
- package/vendor/dist/path-to-regexp/index.js +1 -0
- package/vendor/dist/pprof-format/LICENSE +8 -0
- package/vendor/dist/pprof-format/index.js +1 -0
- package/vendor/dist/protobufjs/LICENSE +39 -0
- package/vendor/dist/protobufjs/index.js +1 -0
- package/vendor/dist/protobufjs/minimal/LICENSE +39 -0
- package/vendor/dist/protobufjs/minimal/index.js +1 -0
- package/vendor/dist/retry/LICENSE +21 -0
- package/vendor/dist/retry/index.js +1 -0
- package/vendor/dist/rfdc/LICENSE +15 -0
- package/vendor/dist/rfdc/index.js +1 -0
- package/vendor/dist/semifies/LICENSE +201 -0
- package/vendor/dist/semifies/index.js +1 -0
- package/vendor/dist/shell-quote/LICENSE +24 -0
- package/vendor/dist/shell-quote/index.js +1 -0
- package/vendor/dist/source-map/LICENSE +28 -0
- package/vendor/dist/source-map/index.js +1 -0
- package/vendor/dist/source-map/lib/util/LICENSE +28 -0
- package/vendor/dist/source-map/lib/util/index.js +1 -0
- package/vendor/dist/source-map/mappings.wasm +0 -0
- package/vendor/dist/tlhunter-sorted-set/LICENSE +21 -0
- package/vendor/dist/tlhunter-sorted-set/index.js +1 -0
- package/vendor/dist/ttl-set/LICENSE +21 -0
- package/vendor/dist/ttl-set/index.js +1 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// Constants for role mapping
|
|
4
|
+
const ROLES = {
|
|
5
|
+
MODEL: 'model',
|
|
6
|
+
ASSISTANT: 'assistant',
|
|
7
|
+
USER: 'user',
|
|
8
|
+
REASONING: 'reasoning'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get the operation type from the method name
|
|
13
|
+
* @param {string} methodName
|
|
14
|
+
* @returns {'embedding' | 'llm'}
|
|
15
|
+
*/
|
|
16
|
+
function getOperation (methodName) {
|
|
17
|
+
return methodName.includes('embed') ? 'embedding' : 'llm'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Extract text parts from an array of parts
|
|
22
|
+
* @param {Array<{text?: string}>} parts
|
|
23
|
+
* @returns {string[]}
|
|
24
|
+
*/
|
|
25
|
+
function extractTextParts (parts) {
|
|
26
|
+
return parts
|
|
27
|
+
.filter(part => part.text)
|
|
28
|
+
.map(part => part.text)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Group parts by role (reasoning vs assistant)
|
|
33
|
+
* @param {Array<{text?: string, thought?: boolean}>} parts
|
|
34
|
+
* @returns {{reasoning: string, assistant: string}}
|
|
35
|
+
*/
|
|
36
|
+
function groupPartsByRole (parts) {
|
|
37
|
+
const grouped = {
|
|
38
|
+
reasoning: '',
|
|
39
|
+
assistant: ''
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (const part of parts) {
|
|
43
|
+
if (!part.text) continue
|
|
44
|
+
|
|
45
|
+
if (part.thought === true) {
|
|
46
|
+
grouped.reasoning += part.text
|
|
47
|
+
} else {
|
|
48
|
+
grouped.assistant += part.text
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return grouped
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if parts contain thought/reasoning content
|
|
57
|
+
* @param {Array<{thought?: boolean}>} parts
|
|
58
|
+
* @returns {boolean}
|
|
59
|
+
*/
|
|
60
|
+
function hasThoughtParts (parts) {
|
|
61
|
+
return parts.some(part => part.thought === true)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Determine the role from a candidate and its parts
|
|
66
|
+
* @param {object} candidate
|
|
67
|
+
* @param {Array<{thought?: boolean}>} parts
|
|
68
|
+
* @returns {string}
|
|
69
|
+
*/
|
|
70
|
+
function determineRole (candidate, parts = []) {
|
|
71
|
+
// Check parts for thought indicators
|
|
72
|
+
if (hasThoughtParts(parts)) {
|
|
73
|
+
return ROLES.REASONING
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Extract role from various possible locations
|
|
77
|
+
const rawRole = candidate.role ||
|
|
78
|
+
candidate.content?.role ||
|
|
79
|
+
candidate[0]?.content?.role
|
|
80
|
+
|
|
81
|
+
return normalizeRole(rawRole)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Normalize role to standard values
|
|
86
|
+
* @param {string} role
|
|
87
|
+
* @returns {string}
|
|
88
|
+
*/
|
|
89
|
+
function normalizeRole (role) {
|
|
90
|
+
if (role === ROLES.MODEL) return ROLES.ASSISTANT
|
|
91
|
+
if (role === ROLES.ASSISTANT) return ROLES.ASSISTANT
|
|
92
|
+
if (role === ROLES.USER) return ROLES.USER
|
|
93
|
+
if (role === ROLES.REASONING) return ROLES.REASONING
|
|
94
|
+
return ROLES.USER // default
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Extract metrics from response
|
|
99
|
+
* @param {object} response
|
|
100
|
+
* @returns {object}
|
|
101
|
+
*/
|
|
102
|
+
function extractMetrics (response) {
|
|
103
|
+
const metrics = {}
|
|
104
|
+
const tokenUsage = response.usageMetadata
|
|
105
|
+
|
|
106
|
+
if (!tokenUsage) return metrics
|
|
107
|
+
|
|
108
|
+
if (tokenUsage.promptTokenCount) {
|
|
109
|
+
metrics.inputTokens = tokenUsage.promptTokenCount
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (tokenUsage.candidatesTokenCount) {
|
|
113
|
+
metrics.outputTokens = tokenUsage.candidatesTokenCount
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const totalTokens = tokenUsage.totalTokenCount ||
|
|
117
|
+
(tokenUsage.promptTokenCount || 0) + (tokenUsage.candidatesTokenCount || 0)
|
|
118
|
+
if (totalTokens) {
|
|
119
|
+
metrics.totalTokens = totalTokens
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return metrics
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Extract metadata from config
|
|
127
|
+
* @param {object} config
|
|
128
|
+
* @returns {object}
|
|
129
|
+
*/
|
|
130
|
+
function extractMetadata (config) {
|
|
131
|
+
if (!config) return {}
|
|
132
|
+
|
|
133
|
+
const fieldMap = {
|
|
134
|
+
temperature: 'temperature',
|
|
135
|
+
top_p: 'topP',
|
|
136
|
+
top_k: 'topK',
|
|
137
|
+
candidate_count: 'candidateCount',
|
|
138
|
+
max_output_tokens: 'maxOutputTokens',
|
|
139
|
+
stop_sequences: 'stopSequences',
|
|
140
|
+
response_logprobs: 'responseLogprobs',
|
|
141
|
+
logprobs: 'logprobs',
|
|
142
|
+
presence_penalty: 'presencePenalty',
|
|
143
|
+
frequency_penalty: 'frequencyPenalty',
|
|
144
|
+
seed: 'seed',
|
|
145
|
+
response_mime_type: 'responseMimeType',
|
|
146
|
+
safety_settings: 'safetySettings',
|
|
147
|
+
automatic_function_calling: 'automaticFunctionCalling'
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const metadata = {}
|
|
151
|
+
for (const [metadataKey, configKey] of Object.entries(fieldMap)) {
|
|
152
|
+
metadata[metadataKey] = config[configKey] ?? null
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return metadata
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Format function call message
|
|
160
|
+
* @param {Array} parts
|
|
161
|
+
* @param {Array} functionCalls
|
|
162
|
+
* @param {string} role
|
|
163
|
+
* @returns {object}
|
|
164
|
+
*/
|
|
165
|
+
function formatFunctionCallMessage (parts, functionCalls, role) {
|
|
166
|
+
const toolCalls = functionCalls.map(part => ({
|
|
167
|
+
name: part.functionCall.name,
|
|
168
|
+
arguments: part.functionCall.args,
|
|
169
|
+
toolId: part.functionCall.id || '',
|
|
170
|
+
type: 'function_call'
|
|
171
|
+
}))
|
|
172
|
+
|
|
173
|
+
const textParts = extractTextParts(parts)
|
|
174
|
+
const content = textParts.length > 0 ? textParts.join('\n') : undefined
|
|
175
|
+
const message = { role, toolCalls }
|
|
176
|
+
|
|
177
|
+
if (content) message.content = content
|
|
178
|
+
|
|
179
|
+
return message
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Format function response message
|
|
184
|
+
* @param {Array} functionResponses
|
|
185
|
+
* @param {string} role
|
|
186
|
+
* @returns {object}
|
|
187
|
+
*/
|
|
188
|
+
function formatFunctionResponseMessage (functionResponses, role) {
|
|
189
|
+
const toolResults = functionResponses.map(part => ({
|
|
190
|
+
name: part.functionResponse.name,
|
|
191
|
+
result: JSON.stringify(part.functionResponse.response),
|
|
192
|
+
toolId: part.functionResponse.id,
|
|
193
|
+
type: 'function_response'
|
|
194
|
+
}))
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
role,
|
|
198
|
+
toolResults
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Aggregate streaming chunks into a single response
|
|
204
|
+
* @param {Array} chunks
|
|
205
|
+
* @returns {object}
|
|
206
|
+
*/
|
|
207
|
+
function aggregateStreamingChunks (chunks) {
|
|
208
|
+
const response = { candidates: [] }
|
|
209
|
+
|
|
210
|
+
for (const chunk of chunks) {
|
|
211
|
+
if (chunk.candidates) {
|
|
212
|
+
// Flatten candidates array
|
|
213
|
+
response.candidates.push(...chunk.candidates)
|
|
214
|
+
}
|
|
215
|
+
if (chunk.usageMetadata) {
|
|
216
|
+
response.usageMetadata = chunk.usageMetadata
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return response
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Format a content object into a message
|
|
225
|
+
* @param {object} content
|
|
226
|
+
* @returns {object}
|
|
227
|
+
*/
|
|
228
|
+
function formatContentObject (content) {
|
|
229
|
+
const parts = content.parts || []
|
|
230
|
+
const role = determineRole(content, parts)
|
|
231
|
+
|
|
232
|
+
// Check if this is a thought/reasoning part
|
|
233
|
+
if (hasThoughtParts(parts)) {
|
|
234
|
+
return {
|
|
235
|
+
role: ROLES.REASONING,
|
|
236
|
+
content: extractTextParts(parts).join('\n')
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Check for function calls
|
|
241
|
+
const functionCalls = parts.filter(part => part.functionCall)
|
|
242
|
+
if (functionCalls.length > 0) {
|
|
243
|
+
return formatFunctionCallMessage(parts, functionCalls, role)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Check for function responses
|
|
247
|
+
const functionResponses = parts.filter(part => part.functionResponse)
|
|
248
|
+
if (functionResponses.length > 0) {
|
|
249
|
+
return formatFunctionResponseMessage(functionResponses, role)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Regular text content
|
|
253
|
+
return {
|
|
254
|
+
role,
|
|
255
|
+
content: extractTextParts(parts).join('\n')
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Format input messages from contents
|
|
261
|
+
* @param {*} contents
|
|
262
|
+
* @returns {Array}
|
|
263
|
+
*/
|
|
264
|
+
function formatInputMessages (contents) {
|
|
265
|
+
if (!contents) return []
|
|
266
|
+
|
|
267
|
+
const contentArray = Array.isArray(contents) ? contents : [contents]
|
|
268
|
+
const messages = []
|
|
269
|
+
|
|
270
|
+
for (const content of contentArray) {
|
|
271
|
+
if (typeof content === 'string') {
|
|
272
|
+
messages.push({ role: ROLES.USER, content })
|
|
273
|
+
} else if (content.text) {
|
|
274
|
+
messages.push({ role: ROLES.USER, content: content.text })
|
|
275
|
+
} else if (content.parts) {
|
|
276
|
+
const message = formatContentObject(content)
|
|
277
|
+
if (message) messages.push(message)
|
|
278
|
+
} else {
|
|
279
|
+
messages.push({ role: ROLES.USER, content: JSON.stringify(content) })
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return messages
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Format embedding input from contents
|
|
288
|
+
* @param {*} contents
|
|
289
|
+
* @returns {Array}
|
|
290
|
+
*/
|
|
291
|
+
function formatEmbeddingInput (contents) {
|
|
292
|
+
if (!contents) return []
|
|
293
|
+
|
|
294
|
+
const contentArray = Array.isArray(contents) ? contents : [contents]
|
|
295
|
+
const documents = []
|
|
296
|
+
|
|
297
|
+
for (const content of contentArray) {
|
|
298
|
+
if (typeof content === 'string') {
|
|
299
|
+
documents.push({ text: content })
|
|
300
|
+
} else if (content.text) {
|
|
301
|
+
documents.push({ text: content.text })
|
|
302
|
+
} else if (content.parts) {
|
|
303
|
+
for (const part of content.parts) {
|
|
304
|
+
if (typeof part === 'string') {
|
|
305
|
+
documents.push({ text: part })
|
|
306
|
+
} else if (part.text) {
|
|
307
|
+
documents.push({ text: part.text })
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return documents
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Format a non-streaming candidate into messages
|
|
318
|
+
* @param {object} candidate
|
|
319
|
+
* @returns {Array}
|
|
320
|
+
*/
|
|
321
|
+
function formatNonStreamingCandidate (candidate) {
|
|
322
|
+
const messages = []
|
|
323
|
+
const content = Array.isArray(candidate) ? candidate[0].content : candidate.content
|
|
324
|
+
|
|
325
|
+
if (!content?.parts) return messages
|
|
326
|
+
|
|
327
|
+
const { parts } = content
|
|
328
|
+
|
|
329
|
+
// Check for function calls
|
|
330
|
+
const functionCalls = parts.filter(part => part.functionCall)
|
|
331
|
+
if (functionCalls.length > 0) {
|
|
332
|
+
messages.push(formatFunctionCallMessage(parts, functionCalls, ROLES.ASSISTANT))
|
|
333
|
+
return messages
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Check for executable code
|
|
337
|
+
const executableCode = parts.find(part => part.executableCode)
|
|
338
|
+
if (executableCode) {
|
|
339
|
+
messages.push({
|
|
340
|
+
role: ROLES.ASSISTANT,
|
|
341
|
+
content: JSON.stringify({
|
|
342
|
+
language: executableCode.executableCode.language,
|
|
343
|
+
code: executableCode.executableCode.code
|
|
344
|
+
})
|
|
345
|
+
})
|
|
346
|
+
return messages
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Check for code execution result
|
|
350
|
+
const codeExecutionResult = parts.find(part => part.codeExecutionResult)
|
|
351
|
+
if (codeExecutionResult) {
|
|
352
|
+
messages.push({
|
|
353
|
+
role: ROLES.ASSISTANT,
|
|
354
|
+
content: JSON.stringify({
|
|
355
|
+
outcome: codeExecutionResult.codeExecutionResult.outcome,
|
|
356
|
+
output: codeExecutionResult.codeExecutionResult.output
|
|
357
|
+
})
|
|
358
|
+
})
|
|
359
|
+
return messages
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Regular text content - may contain both reasoning and assistant parts
|
|
363
|
+
const partsByRole = groupPartsByRole(parts)
|
|
364
|
+
|
|
365
|
+
if (partsByRole.reasoning) {
|
|
366
|
+
messages.push({
|
|
367
|
+
role: ROLES.REASONING,
|
|
368
|
+
content: partsByRole.reasoning
|
|
369
|
+
})
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (partsByRole.assistant) {
|
|
373
|
+
messages.push({
|
|
374
|
+
role: ROLES.ASSISTANT,
|
|
375
|
+
content: partsByRole.assistant
|
|
376
|
+
})
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return messages
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Format streaming output from response
|
|
384
|
+
* @param {object} response
|
|
385
|
+
* @returns {Array}
|
|
386
|
+
*/
|
|
387
|
+
function formatStreamingOutput (response) {
|
|
388
|
+
const messages = []
|
|
389
|
+
const messagesByRole = new Map()
|
|
390
|
+
|
|
391
|
+
for (const candidate of response.candidates) {
|
|
392
|
+
const content = Array.isArray(candidate) ? candidate[0].content : candidate.content
|
|
393
|
+
if (!content?.parts) continue
|
|
394
|
+
|
|
395
|
+
// Skip special cases in streaming (handle them as non-streaming)
|
|
396
|
+
if (content.parts.some(part => part.functionCall ||
|
|
397
|
+
part.executableCode ||
|
|
398
|
+
part.codeExecutionResult)) {
|
|
399
|
+
messages.push(...formatNonStreamingCandidate(candidate))
|
|
400
|
+
continue
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Accumulate text parts by role
|
|
404
|
+
const partsByRole = groupPartsByRole(content.parts)
|
|
405
|
+
|
|
406
|
+
for (const [partRole, textContent] of Object.entries(partsByRole)) {
|
|
407
|
+
if (!textContent) continue
|
|
408
|
+
|
|
409
|
+
if (messagesByRole.has(partRole)) {
|
|
410
|
+
const index = messagesByRole.get(partRole)
|
|
411
|
+
messages[index].content += textContent
|
|
412
|
+
} else {
|
|
413
|
+
const messageIndex = messages.length
|
|
414
|
+
messages.push({ role: partRole, content: textContent })
|
|
415
|
+
messagesByRole.set(partRole, messageIndex)
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return messages.length > 0 ? messages : [{ content: '' }]
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Format non-streaming output from response
|
|
425
|
+
* @param {object} response
|
|
426
|
+
* @returns {Array}
|
|
427
|
+
*/
|
|
428
|
+
function formatNonStreamingOutput (response) {
|
|
429
|
+
const messages = []
|
|
430
|
+
|
|
431
|
+
for (const candidate of response.candidates) {
|
|
432
|
+
messages.push(...formatNonStreamingCandidate(candidate))
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return messages.length > 0 ? messages : [{ content: '' }]
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Format output messages from response
|
|
440
|
+
* @param {object} response
|
|
441
|
+
* @param {boolean} isStreaming
|
|
442
|
+
* @returns {Array}
|
|
443
|
+
*/
|
|
444
|
+
function formatOutputMessages (response, isStreaming = false) {
|
|
445
|
+
if (!response?.candidates?.length) {
|
|
446
|
+
return [{ content: '' }]
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (isStreaming) {
|
|
450
|
+
return formatStreamingOutput(response)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return formatNonStreamingOutput(response)
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Format embedding output from response
|
|
458
|
+
* @param {object} response
|
|
459
|
+
* @returns {string}
|
|
460
|
+
*/
|
|
461
|
+
function formatEmbeddingOutput (response) {
|
|
462
|
+
if (!response?.embeddings?.length) {
|
|
463
|
+
return ''
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const embeddingCount = response.embeddings.length
|
|
467
|
+
const firstEmbedding = response.embeddings[0]
|
|
468
|
+
|
|
469
|
+
if (firstEmbedding.values && Array.isArray(firstEmbedding.values)) {
|
|
470
|
+
const embeddingDim = firstEmbedding.values.length
|
|
471
|
+
return `[${embeddingCount} embedding(s) returned with size ${embeddingDim}]`
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
return `[${embeddingCount} embedding(s) returned]`
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
module.exports = {
|
|
478
|
+
getOperation,
|
|
479
|
+
extractMetrics,
|
|
480
|
+
extractMetadata,
|
|
481
|
+
aggregateStreamingChunks,
|
|
482
|
+
formatInputMessages,
|
|
483
|
+
formatEmbeddingInput,
|
|
484
|
+
formatOutputMessages,
|
|
485
|
+
formatEmbeddingOutput
|
|
486
|
+
}
|
|
@@ -174,13 +174,13 @@ class BaseLLMGeneratePlugin extends BaseLangChainLLMObsPlugin {
|
|
|
174
174
|
class EmbeddingsEmbedQueryPlugin extends BaseLangChainLLMObsPlugin {
|
|
175
175
|
static id = 'llmobs_langchain_embeddings_embed_query'
|
|
176
176
|
static lcType = 'embedding'
|
|
177
|
-
static prefix = 'tracing:
|
|
177
|
+
static prefix = 'tracing:orchestrion:@langchain/core:Embeddings_embedQuery'
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
class EmbeddingsEmbedDocumentsPlugin extends BaseLangChainLLMObsPlugin {
|
|
181
181
|
static id = 'llmobs_langchain_embeddings_embed_documents'
|
|
182
182
|
static lcType = 'embedding'
|
|
183
|
-
static prefix = 'tracing:
|
|
183
|
+
static prefix = 'tracing:orchestrion:@langchain/core:Embeddings_embedDocuments'
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
class ToolInvokePlugin extends BaseLangChainLLMObsPlugin {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const LLMObsPlugin = require('
|
|
3
|
+
const LLMObsPlugin = require('../base')
|
|
4
|
+
const { extractChatTemplateFromInstructions, normalizePromptVariables, extractTextFromContentItem } = require('./utils')
|
|
4
5
|
|
|
5
6
|
const allowedParamKeys = new Set([
|
|
6
7
|
'max_output_tokens',
|
|
@@ -67,6 +68,12 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
|
|
|
67
68
|
if (!error) {
|
|
68
69
|
const metrics = this._extractMetrics(response)
|
|
69
70
|
this._tagger.tagMetrics(span, metrics)
|
|
71
|
+
|
|
72
|
+
const responseModel = response.model
|
|
73
|
+
if (responseModel) {
|
|
74
|
+
// override the model name with the response model (more accurate)
|
|
75
|
+
this._tagger.tagModelName(span, responseModel)
|
|
76
|
+
}
|
|
70
77
|
}
|
|
71
78
|
}
|
|
72
79
|
|
|
@@ -85,11 +92,11 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
|
|
|
85
92
|
|
|
86
93
|
if (tokenUsage) {
|
|
87
94
|
// Responses API uses input_tokens, Chat/Completions use prompt_tokens
|
|
88
|
-
const inputTokens = tokenUsage.input_tokens ?? tokenUsage.prompt_tokens
|
|
95
|
+
const inputTokens = tokenUsage.input_tokens ?? tokenUsage.prompt_tokens ?? 0
|
|
89
96
|
if (inputTokens !== undefined) metrics.inputTokens = inputTokens
|
|
90
97
|
|
|
91
98
|
// Responses API uses output_tokens, Chat/Completions use completion_tokens
|
|
92
|
-
const outputTokens = tokenUsage.output_tokens ?? tokenUsage.completion_tokens
|
|
99
|
+
const outputTokens = tokenUsage.output_tokens ?? tokenUsage.completion_tokens ?? 0
|
|
93
100
|
if (outputTokens !== undefined) metrics.outputTokens = outputTokens
|
|
94
101
|
|
|
95
102
|
const totalTokens = tokenUsage.total_tokens || (inputTokens + outputTokens)
|
|
@@ -105,10 +112,14 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
|
|
|
105
112
|
} else if (tokenUsage.prompt_tokens_details) {
|
|
106
113
|
// Chat/Completions API - only include if > 0
|
|
107
114
|
const cacheReadTokens = tokenUsage.prompt_tokens_details.cached_tokens
|
|
108
|
-
if (cacheReadTokens) {
|
|
115
|
+
if (cacheReadTokens != null) {
|
|
109
116
|
metrics.cacheReadTokens = cacheReadTokens
|
|
110
117
|
}
|
|
111
118
|
}
|
|
119
|
+
// Reasoning tokens - Responses API returns `output_tokens_details`, `completion_tokens_details`
|
|
120
|
+
const reasoningOutputObject = tokenUsage.output_tokens_details ?? tokenUsage.completion_tokens_details
|
|
121
|
+
const reasoningOutputTokens = reasoningOutputObject?.reasoning_tokens ?? 0
|
|
122
|
+
if (reasoningOutputTokens !== undefined) metrics.reasoningOutputTokens = reasoningOutputTokens
|
|
112
123
|
}
|
|
113
124
|
|
|
114
125
|
return metrics
|
|
@@ -159,6 +170,16 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
|
|
|
159
170
|
_tagChatCompletion (span, inputs, response, error) {
|
|
160
171
|
const { messages, model, ...parameters } = inputs
|
|
161
172
|
|
|
173
|
+
const metadata = Object.entries(parameters).reduce((obj, [key, value]) => {
|
|
174
|
+
if (!['tools', 'functions'].includes(key)) {
|
|
175
|
+
obj[key] = value
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return obj
|
|
179
|
+
}, {})
|
|
180
|
+
|
|
181
|
+
this._tagger.tagMetadata(span, metadata)
|
|
182
|
+
|
|
162
183
|
if (error) {
|
|
163
184
|
this._tagger.tagLLMIO(span, messages, [{ content: '' }])
|
|
164
185
|
return
|
|
@@ -200,22 +221,13 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
|
|
|
200
221
|
}
|
|
201
222
|
|
|
202
223
|
this._tagger.tagLLMIO(span, messages, outputMessages)
|
|
203
|
-
|
|
204
|
-
const metadata = Object.entries(parameters).reduce((obj, [key, value]) => {
|
|
205
|
-
if (!['tools', 'functions'].includes(key)) {
|
|
206
|
-
obj[key] = value
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return obj
|
|
210
|
-
}, {})
|
|
211
|
-
|
|
212
|
-
this._tagger.tagMetadata(span, metadata)
|
|
213
224
|
}
|
|
214
225
|
|
|
215
226
|
#tagResponse (span, inputs, response, error) {
|
|
216
227
|
// Tag metadata - use allowlist approach for request parameters
|
|
217
228
|
|
|
218
|
-
const {
|
|
229
|
+
const { model, ...parameters } = inputs
|
|
230
|
+
let input = inputs.input
|
|
219
231
|
|
|
220
232
|
// Create input messages
|
|
221
233
|
const inputMessages = []
|
|
@@ -225,10 +237,33 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
|
|
|
225
237
|
inputMessages.push({ role: 'system', content: inputs.instructions })
|
|
226
238
|
}
|
|
227
239
|
|
|
240
|
+
// For reusable prompts, use response.instructions if no explicit input is provided
|
|
241
|
+
if (!input && inputs.prompt && response?.instructions) {
|
|
242
|
+
input = response.instructions
|
|
243
|
+
}
|
|
244
|
+
|
|
228
245
|
// Handle input - can be string or array of mixed messages
|
|
229
246
|
if (Array.isArray(input)) {
|
|
230
247
|
for (const item of input) {
|
|
231
|
-
if (item.type === '
|
|
248
|
+
if (item.type === 'message') {
|
|
249
|
+
// Handle instruction messages (from response.instructions for reusable prompts)
|
|
250
|
+
const role = item.role
|
|
251
|
+
if (!role) continue
|
|
252
|
+
|
|
253
|
+
let content = ''
|
|
254
|
+
if (Array.isArray(item.content)) {
|
|
255
|
+
const textParts = item.content
|
|
256
|
+
.map(extractTextFromContentItem)
|
|
257
|
+
.filter(Boolean)
|
|
258
|
+
content = textParts.join('')
|
|
259
|
+
} else if (typeof item.content === 'string') {
|
|
260
|
+
content = item.content
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (content) {
|
|
264
|
+
inputMessages.push({ role, content })
|
|
265
|
+
}
|
|
266
|
+
} else if (item.type === 'function_call') {
|
|
232
267
|
// Function call: convert to message with tool_calls
|
|
233
268
|
// Parse arguments if it's a JSON string
|
|
234
269
|
let parsedArgs = item.arguments
|
|
@@ -269,6 +304,15 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
|
|
|
269
304
|
inputMessages.push({ role: 'user', content: input })
|
|
270
305
|
}
|
|
271
306
|
|
|
307
|
+
const inputMetadata = Object.entries(parameters).reduce((obj, [key, value]) => {
|
|
308
|
+
if (allowedParamKeys.has(key)) {
|
|
309
|
+
obj[key] = value
|
|
310
|
+
}
|
|
311
|
+
return obj
|
|
312
|
+
}, {})
|
|
313
|
+
|
|
314
|
+
this._tagger.tagMetadata(span, inputMetadata)
|
|
315
|
+
|
|
272
316
|
if (error) {
|
|
273
317
|
this._tagger.tagLLMIO(span, inputMessages, [{ content: '' }])
|
|
274
318
|
return
|
|
@@ -287,17 +331,13 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
|
|
|
287
331
|
for (const item of response.output) {
|
|
288
332
|
// Handle reasoning type (reasoning responses)
|
|
289
333
|
if (item.type === 'reasoning') {
|
|
290
|
-
// Extract reasoning text from summary
|
|
291
|
-
let reasoningText = ''
|
|
292
|
-
if (Array.isArray(item.summary) && item.summary.length > 0) {
|
|
293
|
-
const summaryItem = item.summary[0]
|
|
294
|
-
if (summaryItem.type === 'summary_text' && summaryItem.text) {
|
|
295
|
-
reasoningText = summaryItem.text
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
334
|
outputMessages.push({
|
|
299
335
|
role: 'reasoning',
|
|
300
|
-
content:
|
|
336
|
+
content: JSON.stringify({
|
|
337
|
+
summary: item.summary ?? [],
|
|
338
|
+
encrypted_content: item.encrypted_content ?? null,
|
|
339
|
+
id: item.id ?? ''
|
|
340
|
+
})
|
|
301
341
|
})
|
|
302
342
|
} else if (item.type === 'function_call') {
|
|
303
343
|
// Handle function_call type (responses API tool calls)
|
|
@@ -369,24 +409,32 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
|
|
|
369
409
|
|
|
370
410
|
this._tagger.tagLLMIO(span, inputMessages, outputMessages)
|
|
371
411
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
412
|
+
// Handle prompt tracking for reusable prompts
|
|
413
|
+
if (inputs.prompt && response?.prompt) {
|
|
414
|
+
const { id, version } = response.prompt // ResponsePrompt
|
|
415
|
+
// TODO: Add proper tagger API for prompt metadata
|
|
416
|
+
if (id && version) {
|
|
417
|
+
const normalizedVariables = normalizePromptVariables(inputs.prompt.variables)
|
|
418
|
+
const chatTemplate = extractChatTemplateFromInstructions(response.instructions, normalizedVariables)
|
|
419
|
+
this._tagger._setTag(span, '_ml_obs.meta.input.prompt', {
|
|
420
|
+
id,
|
|
421
|
+
version,
|
|
422
|
+
variables: normalizedVariables,
|
|
423
|
+
chat_template: chatTemplate
|
|
424
|
+
})
|
|
375
425
|
}
|
|
376
|
-
|
|
377
|
-
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const outputMetadata = {}
|
|
378
429
|
|
|
379
430
|
// Add fields from response object (convert numbers to floats)
|
|
380
|
-
if (response.temperature !== undefined)
|
|
381
|
-
if (response.top_p !== undefined)
|
|
382
|
-
if (response.tool_choice !== undefined)
|
|
383
|
-
if (response.truncation !== undefined)
|
|
384
|
-
if (response.text !== undefined)
|
|
385
|
-
if (response.usage?.output_tokens_details?.reasoning_tokens !== undefined) {
|
|
386
|
-
metadata.reasoning_tokens = response.usage.output_tokens_details.reasoning_tokens
|
|
387
|
-
}
|
|
431
|
+
if (response.temperature !== undefined) outputMetadata.temperature = Number(response.temperature)
|
|
432
|
+
if (response.top_p !== undefined) outputMetadata.top_p = Number(response.top_p)
|
|
433
|
+
if (response.tool_choice !== undefined) outputMetadata.tool_choice = response.tool_choice
|
|
434
|
+
if (response.truncation !== undefined) outputMetadata.truncation = response.truncation
|
|
435
|
+
if (response.text !== undefined) outputMetadata.text = response.text
|
|
388
436
|
|
|
389
|
-
this._tagger.tagMetadata(span, metadata
|
|
437
|
+
this._tagger.tagMetadata(span, outputMetadata) // update the metadata with the output metadata
|
|
390
438
|
}
|
|
391
439
|
}
|
|
392
440
|
|