fix(llmobs): openai-java payload mapping for responses, tool metadata, and prompt tracking#10644
fix(llmobs): openai-java payload mapping for responses, tool metadata, and prompt tracking#10644
Conversation
BenchmarksStartupParameters
See matching parameters
SummaryFound 0 performance improvements and 0 performance regressions! Performance is the same for 62 metrics, 9 unstable metrics. Startup time reports for petclinicgantt
title petclinic - global startup overhead: candidate=1.60.0-SNAPSHOT~3d12515bb7, baseline=1.61.0-SNAPSHOT~5580c61ac4
dateFormat X
axisFormat %s
section tracing
Agent [baseline] (1.058 s) : 0, 1057721
Total [baseline] (11.134 s) : 0, 11133804
Agent [candidate] (1.076 s) : 0, 1075626
Total [candidate] (11.153 s) : 0, 11153081
section appsec
Agent [baseline] (1.259 s) : 0, 1259346
Total [baseline] (11.201 s) : 0, 11201251
Agent [candidate] (1.249 s) : 0, 1249093
Total [candidate] (11.145 s) : 0, 11144880
section iast
Agent [baseline] (1.239 s) : 0, 1239499
Total [baseline] (11.408 s) : 0, 11407886
Agent [candidate] (1.238 s) : 0, 1237753
Total [candidate] (11.352 s) : 0, 11352262
section profiling
Agent [baseline] (1.192 s) : 0, 1191973
Total [baseline] (11.029 s) : 0, 11029415
Agent [candidate] (1.191 s) : 0, 1190882
Total [candidate] (11.087 s) : 0, 11087453
gantt
title petclinic - break down per module: candidate=1.60.0-SNAPSHOT~3d12515bb7, baseline=1.61.0-SNAPSHOT~5580c61ac4
dateFormat X
axisFormat %s
section tracing
crashtracking [baseline] (1.19 ms) : 0, 1190
crashtracking [candidate] (1.213 ms) : 0, 1213
BytebuddyAgent [baseline] (629.278 ms) : 0, 629278
BytebuddyAgent [candidate] (639.784 ms) : 0, 639784
AgentMeter [baseline] (29.387 ms) : 0, 29387
AgentMeter [candidate] (29.945 ms) : 0, 29945
GlobalTracer [baseline] (256.777 ms) : 0, 256777
GlobalTracer [candidate] (260.861 ms) : 0, 260861
AppSec [baseline] (31.656 ms) : 0, 31656
AppSec [candidate] (32.29 ms) : 0, 32290
Debugger [baseline] (60.481 ms) : 0, 60481
Debugger [candidate] (61.308 ms) : 0, 61308
Remote Config [baseline] (591.548 µs) : 0, 592
Remote Config [candidate] (598.512 µs) : 0, 599
Telemetry [baseline] (8.018 ms) : 0, 8018
Telemetry [candidate] (8.174 ms) : 0, 8174
Flare Poller [baseline] (4.269 ms) : 0, 4269
Flare Poller [candidate] (5.14 ms) : 0, 5140
section appsec
crashtracking [baseline] (1.201 ms) : 0, 1201
crashtracking [candidate] (1.184 ms) : 0, 1184
BytebuddyAgent [baseline] (665.509 ms) : 0, 665509
BytebuddyAgent [candidate] (660.488 ms) : 0, 660488
AgentMeter [baseline] (12.266 ms) : 0, 12266
AgentMeter [candidate] (12.15 ms) : 0, 12150
GlobalTracer [baseline] (260.977 ms) : 0, 260977
GlobalTracer [candidate] (258.415 ms) : 0, 258415
AppSec [baseline] (178.806 ms) : 0, 178806
AppSec [candidate] (177.543 ms) : 0, 177543
Debugger [baseline] (66.757 ms) : 0, 66757
Debugger [candidate] (66.325 ms) : 0, 66325
Remote Config [baseline] (638.436 µs) : 0, 638
Remote Config [candidate] (619.631 µs) : 0, 620
Telemetry [baseline] (8.461 ms) : 0, 8461
Telemetry [candidate] (8.258 ms) : 0, 8258
Flare Poller [baseline] (3.63 ms) : 0, 3630
Flare Poller [candidate] (3.615 ms) : 0, 3615
IAST [baseline] (24.653 ms) : 0, 24653
IAST [candidate] (24.202 ms) : 0, 24202
section iast
crashtracking [baseline] (1.211 ms) : 0, 1211
crashtracking [candidate] (1.216 ms) : 0, 1216
BytebuddyAgent [baseline] (804.891 ms) : 0, 804891
BytebuddyAgent [candidate] (803.152 ms) : 0, 803152
AgentMeter [baseline] (11.712 ms) : 0, 11712
AgentMeter [candidate] (11.72 ms) : 0, 11720
GlobalTracer [baseline] (248.815 ms) : 0, 248815
GlobalTracer [candidate] (249.18 ms) : 0, 249180
AppSec [baseline] (26.73 ms) : 0, 26730
AppSec [candidate] (26.798 ms) : 0, 26798
Debugger [baseline] (69.869 ms) : 0, 69869
Debugger [candidate] (69.676 ms) : 0, 69676
Remote Config [baseline] (529.736 µs) : 0, 530
Remote Config [candidate] (518.658 µs) : 0, 519
Telemetry [baseline] (10.306 ms) : 0, 10306
Telemetry [candidate] (10.099 ms) : 0, 10099
Flare Poller [baseline] (3.689 ms) : 0, 3689
Flare Poller [candidate] (3.639 ms) : 0, 3639
IAST [baseline] (25.561 ms) : 0, 25561
IAST [candidate] (25.528 ms) : 0, 25528
section profiling
ProfilingAgent [baseline] (94.071 ms) : 0, 94071
ProfilingAgent [candidate] (93.748 ms) : 0, 93748
crashtracking [baseline] (1.188 ms) : 0, 1188
crashtracking [candidate] (1.183 ms) : 0, 1183
BytebuddyAgent [baseline] (688.536 ms) : 0, 688536
BytebuddyAgent [candidate] (688.462 ms) : 0, 688462
AgentMeter [baseline] (9.115 ms) : 0, 9115
AgentMeter [candidate] (9.075 ms) : 0, 9075
GlobalTracer [baseline] (217.146 ms) : 0, 217146
GlobalTracer [candidate] (216.977 ms) : 0, 216977
AppSec [baseline] (32.391 ms) : 0, 32391
AppSec [candidate] (32.288 ms) : 0, 32288
Debugger [baseline] (65.656 ms) : 0, 65656
Debugger [candidate] (65.47 ms) : 0, 65470
Remote Config [baseline] (559.469 µs) : 0, 559
Remote Config [candidate] (555.13 µs) : 0, 555
Telemetry [baseline] (8.5 ms) : 0, 8500
Telemetry [candidate] (7.683 ms) : 0, 7683
Flare Poller [baseline] (3.473 ms) : 0, 3473
Flare Poller [candidate] (4.235 ms) : 0, 4235
Profiling [baseline] (94.643 ms) : 0, 94643
Profiling [candidate] (94.301 ms) : 0, 94301
Startup time reports for insecure-bankgantt
title insecure-bank - global startup overhead: candidate=1.60.0-SNAPSHOT~3d12515bb7, baseline=1.61.0-SNAPSHOT~5580c61ac4
dateFormat X
axisFormat %s
section tracing
Agent [baseline] (1.062 s) : 0, 1062273
Total [baseline] (8.888 s) : 0, 8888380
Agent [candidate] (1.061 s) : 0, 1061233
Total [candidate] (8.861 s) : 0, 8861113
section iast
Agent [baseline] (1.235 s) : 0, 1234635
Total [baseline] (9.541 s) : 0, 9541353
Agent [candidate] (1.227 s) : 0, 1226844
Total [candidate] (9.541 s) : 0, 9541287
gantt
title insecure-bank - break down per module: candidate=1.60.0-SNAPSHOT~3d12515bb7, baseline=1.61.0-SNAPSHOT~5580c61ac4
dateFormat X
axisFormat %s
section tracing
crashtracking [baseline] (1.196 ms) : 0, 1196
crashtracking [candidate] (1.203 ms) : 0, 1203
BytebuddyAgent [baseline] (632.575 ms) : 0, 632575
BytebuddyAgent [candidate] (632.625 ms) : 0, 632625
AgentMeter [baseline] (29.437 ms) : 0, 29437
AgentMeter [candidate] (29.564 ms) : 0, 29564
GlobalTracer [baseline] (258.02 ms) : 0, 258020
GlobalTracer [candidate] (257.908 ms) : 0, 257908
AppSec [baseline] (32.025 ms) : 0, 32025
AppSec [candidate] (31.869 ms) : 0, 31869
Debugger [baseline] (59.889 ms) : 0, 59889
Debugger [candidate] (59.896 ms) : 0, 59896
Remote Config [baseline] (584.354 µs) : 0, 584
Remote Config [candidate] (587.177 µs) : 0, 587
Telemetry [baseline] (8.083 ms) : 0, 8083
Telemetry [candidate] (8.043 ms) : 0, 8043
Flare Poller [baseline] (4.335 ms) : 0, 4335
Flare Poller [candidate] (3.511 ms) : 0, 3511
section iast
crashtracking [baseline] (1.209 ms) : 0, 1209
crashtracking [candidate] (1.195 ms) : 0, 1195
BytebuddyAgent [baseline] (800.882 ms) : 0, 800882
BytebuddyAgent [candidate] (797.058 ms) : 0, 797058
AgentMeter [baseline] (11.62 ms) : 0, 11620
AgentMeter [candidate] (11.35 ms) : 0, 11350
GlobalTracer [baseline] (248.864 ms) : 0, 248864
GlobalTracer [candidate] (247.016 ms) : 0, 247016
IAST [baseline] (25.666 ms) : 0, 25666
IAST [candidate] (25.317 ms) : 0, 25317
AppSec [baseline] (26.824 ms) : 0, 26824
AppSec [candidate] (26.377 ms) : 0, 26377
Debugger [baseline] (67.592 ms) : 0, 67592
Debugger [candidate] (68.144 ms) : 0, 68144
Remote Config [baseline] (540.643 µs) : 0, 541
Remote Config [candidate] (522.065 µs) : 0, 522
Telemetry [baseline] (11.285 ms) : 0, 11285
Telemetry [candidate] (10.168 ms) : 0, 10168
Flare Poller [baseline] (3.974 ms) : 0, 3974
Flare Poller [candidate] (3.63 ms) : 0, 3630
LoadParameters
See matching parameters
SummaryFound 0 performance improvements and 5 performance regressions! Performance is the same for 15 metrics, 16 unstable metrics.
Request duration reports for petclinicgantt
title petclinic - request duration [CI 0.99] : candidate=1.60.0-SNAPSHOT~3d12515bb7, baseline=1.61.0-SNAPSHOT~5580c61ac4
dateFormat X
axisFormat %s
section baseline
no_agent (17.899 ms) : 17718, 18081
. : milestone, 17899,
appsec (18.638 ms) : 18448, 18827
. : milestone, 18638,
code_origins (17.78 ms) : 17601, 17959
. : milestone, 17780,
iast (17.392 ms) : 17221, 17564
. : milestone, 17392,
profiling (18.583 ms) : 18392, 18774
. : milestone, 18583,
tracing (17.887 ms) : 17708, 18065
. : milestone, 17887,
section candidate
no_agent (19.141 ms) : 18944, 19338
. : milestone, 19141,
appsec (19.014 ms) : 18820, 19209
. : milestone, 19014,
code_origins (17.559 ms) : 17389, 17729
. : milestone, 17559,
iast (17.711 ms) : 17539, 17882
. : milestone, 17711,
profiling (18.811 ms) : 18628, 18994
. : milestone, 18811,
tracing (17.743 ms) : 17566, 17920
. : milestone, 17743,
Request duration reports for insecure-bankgantt
title insecure-bank - request duration [CI 0.99] : candidate=1.60.0-SNAPSHOT~3d12515bb7, baseline=1.61.0-SNAPSHOT~5580c61ac4
dateFormat X
axisFormat %s
section baseline
no_agent (1.161 ms) : 1150, 1172
. : milestone, 1161,
iast (3.192 ms) : 3149, 3235
. : milestone, 3192,
iast_FULL (5.668 ms) : 5613, 5724
. : milestone, 5668,
iast_GLOBAL (3.425 ms) : 3373, 3476
. : milestone, 3425,
profiling (2.178 ms) : 2157, 2199
. : milestone, 2178,
tracing (1.814 ms) : 1799, 1829
. : milestone, 1814,
section candidate
no_agent (1.184 ms) : 1173, 1196
. : milestone, 1184,
iast (3.252 ms) : 3209, 3295
. : milestone, 3252,
iast_FULL (6.014 ms) : 5954, 6074
. : milestone, 6014,
iast_GLOBAL (3.767 ms) : 3694, 3840
. : milestone, 3767,
profiling (2.058 ms) : 2038, 2077
. : milestone, 2058,
tracing (1.8 ms) : 1785, 1814
. : milestone, 1800,
DacapoParameters
See matching parameters
SummaryFound 0 performance improvements and 0 performance regressions! Performance is the same for 11 metrics, 1 unstable metrics. Execution time for tomcatgantt
title tomcat - execution time [CI 0.99] : candidate=1.60.0-SNAPSHOT~3d12515bb7, baseline=1.61.0-SNAPSHOT~5580c61ac4
dateFormat X
axisFormat %s
section baseline
no_agent (1.473 ms) : 1461, 1484
. : milestone, 1473,
appsec (2.505 ms) : 2450, 2559
. : milestone, 2505,
iast (2.252 ms) : 2183, 2321
. : milestone, 2252,
iast_GLOBAL (2.293 ms) : 2225, 2362
. : milestone, 2293,
profiling (2.093 ms) : 2039, 2148
. : milestone, 2093,
tracing (2.048 ms) : 1995, 2101
. : milestone, 2048,
section candidate
no_agent (1.472 ms) : 1461, 1484
. : milestone, 1472,
appsec (3.781 ms) : 3560, 4003
. : milestone, 3781,
iast (2.25 ms) : 2182, 2319
. : milestone, 2250,
iast_GLOBAL (2.294 ms) : 2225, 2362
. : milestone, 2294,
profiling (2.094 ms) : 2038, 2150
. : milestone, 2094,
tracing (2.052 ms) : 1999, 2106
. : milestone, 2052,
Execution time for biojavagantt
title biojava - execution time [CI 0.99] : candidate=1.60.0-SNAPSHOT~3d12515bb7, baseline=1.61.0-SNAPSHOT~5580c61ac4
dateFormat X
axisFormat %s
section baseline
no_agent (15.277 s) : 15277000, 15277000
. : milestone, 15277000,
appsec (14.971 s) : 14971000, 14971000
. : milestone, 14971000,
iast (18.25 s) : 18250000, 18250000
. : milestone, 18250000,
iast_GLOBAL (17.676 s) : 17676000, 17676000
. : milestone, 17676000,
profiling (15.037 s) : 15037000, 15037000
. : milestone, 15037000,
tracing (14.746 s) : 14746000, 14746000
. : milestone, 14746000,
section candidate
no_agent (14.851 s) : 14851000, 14851000
. : milestone, 14851000,
appsec (14.733 s) : 14733000, 14733000
. : milestone, 14733000,
iast (17.993 s) : 17993000, 17993000
. : milestone, 17993000,
iast_GLOBAL (18.212 s) : 18212000, 18212000
. : milestone, 18212000,
profiling (15.075 s) : 15075000, 15075000
. : milestone, 15075000,
tracing (14.767 s) : 14767000, 14767000
. : milestone, 14767000,
|
5cd257e to
cbd6226
Compare
…wthTestOpenAiLlmInteractions::test_completion
…teractions::test_chat_completion_tool_call
…d with python openai instrumentation and system-tests
… with variables + chat_template, longest-first overlap handling) and support map-based LLM input serialization (messages + prompt) in LLMObs mapper. Also filter empty instruction messages to match system-test expectations.
…st and return [image] (not empty) when stripped input_image URLs are missing, aligning mixed-input chat_template output with expected behavior.
…output.messages from request params so existing error-span tests pass.
…ol_definitions tags
…JSON argument parsing and remove duplicate manual parsing logic from ResponseDecorator.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0c879ba692
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
...enai-java-3.0/src/main/java/datadog/trace/instrumentation/openai_java/ResponseDecorator.java
Outdated
Show resolved
Hide resolved
...enai-java-3.0/src/main/java/datadog/trace/instrumentation/openai_java/ResponseDecorator.java
Outdated
Show resolved
Hide resolved
Kyle-Verhoog
left a comment
There was a problem hiding this comment.
LLMObs Team Review
Nice work aligning the Java SDK payloads with the intake schema — this is a big step for system test compliance. A few items to address/clarify below (inline), plus some overall notes:
Test Coverage Notes
What's well-covered: LLMObsSpanMapperTest expansion is great — covers _dd map, nested meta.error, map-based input with prompt/chat_template, tool definitions, tool calls + tool results. The decorator tests verify the new tags (source, integration, error, ddtrace.version).
Gaps to consider:
- Error paths: No test exercises the error-path defaults (model_name and empty output set during
withResponseCreateParamswhen the HTTP call fails). A test where the response errors out and verifying the span still hasmodel_nameand placeholder output would be valuable. - Prompt tracking:
enrichInputWithPromptTracking(),extractChatTemplate(),extractPromptFromParams(), andnormalizePromptVariable()have no unit tests. Template variable replacement edge cases (overlapping values, empty variables, image/file fallbacks) would increase confidence. - Custom/MCP tool calls:
ToolCallExtractor.getToolCall(ResponseCustomToolCall)andgetToolCall(McpCall)are new with no unit tests. JsonValueUtils: New utility class with no dedicated tests for recursive JSON-to-Object conversion.
Questions
- The min version bump from
3.0.0to3.0.1— what API was missing in3.0.0? This affects which customer versions get instrumented. - For the
_ddmap — does the intake expectapm_trace_idto equaltrace_id? In other SDKs these can differ (APM trace ID vs LLMObs ID).
| # If you change the following comment, update the pattern in the update_system_test_reference.sh script to match. | ||
| uses: DataDog/system-tests/.github/workflows/system-tests.yml@main # system tests are pinned for releases only | ||
| uses: DataDog/system-tests/.github/workflows/system-tests.yml@ea458202a7673efbe365e498d64d74a815c0a137 # system tests are pinned for releases only | ||
| secrets: |
There was a problem hiding this comment.
System tests are pinned to commit ea458202 instead of main. The comment says "system tests are pinned for releases only." This should be reverted to main before merge to avoid blocking future system test updates. If it's intentional for CI during development, just make sure it goes back before merging.
There was a problem hiding this comment.
Yes I also think this is a bad manipulation (hence my review is only limited to this file :) )
There was a problem hiding this comment.
Yes, it was intentional and has just been reverted. The reason for this was to verify that the system tests pass with changes that are not yet part of the main branch: DataDog/system-tests#6364 Without that, none of the related tests would run at all.
| writable.writeString(errored ? "error" : "ok", null); | ||
| writable.writeUTF8(DD); | ||
| writable.startMap(3); | ||
| writable.writeUTF8(SPAN_ID); |
There was a problem hiding this comment.
The _dd map writes span_id, trace_id, and apm_trace_id. The Python SDK also includes t_id (64-bit trace ID) and s_id in the _dd map. Can you verify this is the correct subset for the Java path? If the intake expects additional fields, spans may be rejected or processed incorrectly.
There was a problem hiding this comment.
This is aligned with dd-trace-py https://github.com/DataDog/dd-trace-py/blob/876c5f1ce4d173815537798a6a7b0ac15b0a4ede/ddtrace/llmobs/_llmobs.py#L618-L622. I don't find any t_id or s_id there.
|
|
||
| boolean errored = span.getError() == 1; | ||
| writable.writeUTF8(STATUS); | ||
| writable.writeString(span.getError() == 0 ? "ok" : "error", null); |
There was a problem hiding this comment.
The top-level error: 0/1 integer field has been removed and replaced with status: "ok"/"error" + error details nested under meta.error. Can you confirm no downstream consumers (EvP remapper, indexer facets, etc.) read error from the top level? This is a payload shape change that could be breaking if anything depends on the old field.
There was a problem hiding this comment.
This change is dictated by the TestOpenAiLlmInteractions::test_chat_completion assertion. I assume that the system test assertions are correct. Have they been verified as being compliant with the requirements of downstream consumers?
| } | ||
| } | ||
| } catch (Throwable ignored) { | ||
| // fall back to raw JSON if typed extraction is unavailable or fails |
There was a problem hiding this comment.
nit: catch (Throwable) swallows OutOfMemoryError, StackOverflowError, etc. Consider narrowing to catch (Exception) for both fallback paths here.
| boolean hasToolCalls = null != toolCalls && !toolCalls.isEmpty(); | ||
| boolean hasToolResults = null != toolResults && !toolResults.isEmpty(); | ||
| boolean hasContent = message.getContent() != null; | ||
| int mapSize = 1; |
There was a problem hiding this comment.
Behavioral change: previously content was always written (even as null). Now it's skipped when message.getContent() == null (e.g., tool-call-only messages). This is likely correct and matches Python SDK behavior, but worth confirming the intake handles messages without a content key.
There was a problem hiding this comment.
This change is driven by TestOpenAiResponses::test_responses_create_tool_call. When the content is null, it is expected to be missing; otherwise, the assertion fails.
6dcdaf4 to
717a8f0
Compare
| apply from: "$rootDir/gradle/java.gradle" | ||
|
|
||
| def minVer = '3.0.0' | ||
| def minVer = '3.0.1' |
There was a problem hiding this comment.
ResponseTextConfig fun verbosity(): Optional<Verbosity> was added in 3.0.1 openai/openai-java@c1de354#diff-6b385fb153d457757ba112e6117593cb59da6af308cce0f9b6f26e3885befc6cR73
ResponseTextConfig fun verbosity(): Optional was added in 3.0.1 openai/openai-java@c1de354#diff-6b385fb153d457757ba112e6117593cb59da6af308cce0f9b6f26e3885befc6cR73
This is aligned with dd-trace-py https://github.com/DataDog/dd-trace-py/blob/876c5f1ce4d173815537798a6a7b0ac15b0a4ede/ddtrace/llmobs/_llmobs.py#L618-L622. |
…and placeholder output set by withResponseCreateParams.
What Does This Do
Aligns OpenAI Java LLMObs span payloads with expected intake/system-test schema by:
_ml_obs_tag.integration_ml_obs_tag.source_ml_obs_tag.ddtrace.version_ml_obs_tag.error_ml_obs_tag.error_typemodel_name(and stable placeholder output where applicable) is set on error paths forchat/completions/embeddings/responses.
input.prompt,variables,chat_template)tool_definitions)stream,tool_choice,text.verbosity, etc.)_ddmap with span/trace idsmeta.errorinputserialization (messages+prompt)tool_definitionsintometa.Motivation
OpenAI/LLMObs system tests exposed schema and tag mismatches in Java payloads (especially response spans, tool metadata, error mapping, and prompt tracking structure). This change brings Java output in line with expected LLMObs intake contract and behavior.
Additional Notes
openai-java-3.0min version updated from3.0.0to3.0.1.ResponseTextConfig
fun verbosity(): Optional<Verbosity>was added in 3.0.1 openai/openai-java@c1de354#diff-6b385fb153d457757ba112e6117593cb59da6af308cce0f9b6f26e3885befc6cR73DataDog/dd-apm-test-agent#280
DataDog/system-tests#6364
Contributor Checklist
type:and (comp:orinst:) labels in addition to any other useful labelsclose,fix, or any linking keywords when referencing an issueUse
solvesinstead, and assign the PR milestone to the issueJira ticket: [PROJ-IDENT]
Note: Once your PR is ready to merge, add it to the merge queue by commenting
/merge./merge -ccancels the queue request./merge -f --reason "reason"skips all merge queue checks; please use this judiciously, as some checks do not run at the PR-level. For more information, see this doc.