Skip to content

Commit 335d036

Browse files
committed
Add /evp_proxy/v4 support for openai-java tests
1 parent 8829033 commit 335d036

4 files changed

Lines changed: 47 additions & 19 deletions

File tree

utils/_context/_scenarios/integration_frameworks.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ def __init__(self, name: str, doc: str) -> None:
3030
name,
3131
doc=doc,
3232
github_workflow="endtoend",
33-
agent_image="ghcr.io/datadog/dd-apm-test-agent/ddapm-test-agent:v1.38.0",
33+
# TODO update "local" -> "v1.42.0" once https://github.com/DataDog/dd-apm-test-agent/pull/280 merged and released
34+
agent_image="ghcr.io/datadog/dd-apm-test-agent/ddapm-test-agent:local",
3435
scenario_groups=(groups.integration_frameworks,),
3536
)
3637

@@ -166,4 +167,4 @@ def _set_dd_trace_integrations_enabled(self, library: str) -> None:
166167
# Reduce telemetry intervals for faster metric reporting in tests
167168
self.environment["DD_TELEMETRY_HEARTBEAT_INTERVAL"] = "1"
168169
self.environment["DD_TELEMETRY_METRICS_INTERVAL"] = "1"
169-
self.environment["DD_TRACE_DEBUG"] = "true"
170+
self.environment["DD_TRACE_DEBUG"] = "true"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.gradle/
2+
build/

utils/build/docker/java/openai_app/src/main/java/SingleFileServer.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,14 @@ private static String doOpenAIChatCompletion(Context ctx, OpenAIClient openaiCli
144144

145145
var builder = ChatCompletionCreateParams.builder();
146146
builder.model(payload.optString("model"));
147-
147+
148148
// Parse messages array
149149
JSONArray messages = payload.getJSONArray("messages");
150150
for (int i = 0; i < messages.length(); i++) {
151151
JSONObject message = messages.getJSONObject(i);
152152
String role = message.getString("role");
153153
String content = message.getString("content");
154-
154+
155155
if ("user".equals(role)) {
156156
builder.addUserMessage(content);
157157
} else if ("system".equals(role)) {
@@ -166,13 +166,13 @@ private static String doOpenAIChatCompletion(Context ctx, OpenAIClient openaiCli
166166
// maxCompletionTokens is preferable
167167
builder.maxTokens(parameters.optLong("max_tokens"));
168168
}
169-
if (!Double.isNaN(parameters.optDouble("temperature"))) {
169+
if (!Double.isNaN(parameters.optDouble("temperature"))) {
170170
builder.temperature(parameters.optDouble("temperature"));
171171
}
172172
if (tools != null) {
173173
for (int i = 0; i < tools.length(); i++) {
174174
JSONObject tool = tools.getJSONObject(i);
175-
175+
176176
JSONObject functionObj = tool.getJSONObject("function");
177177
Map<String, Object> functionMap = functionObj.toMap();
178178

@@ -212,7 +212,7 @@ private static String doOpenAIEmbedding (Context ctx, OpenAIClient openaiClient)
212212
builder.input(input);
213213

214214
openaiClient.embeddings().create(builder.build());
215-
215+
216216
return toJson(new HashMap<String, String>());
217217
}
218218

@@ -242,7 +242,7 @@ private static String doOpenAIResponsesCreate (Context ctx, OpenAIClient openaiC
242242
builder.maxOutputTokens((long) parameters.getDouble("max_output_tokens"));
243243
}
244244

245-
if (!Double.isNaN(parameters.optDouble("temperature"))) {
245+
if (!Double.isNaN(parameters.optDouble("temperature"))) {
246246
builder.temperature(parameters.getDouble("temperature"));
247247
}
248248

@@ -257,7 +257,7 @@ private static String doOpenAIResponsesCreate (Context ctx, OpenAIClient openaiC
257257

258258
if (tools != null) {
259259
List<Object> toolsList = (List<Object>) deepConvertJsonToJava(tools);
260-
260+
261261
builder.tools(JsonValue.from(toolsList));
262262
builder.toolChoice(JsonValue.from("auto"));
263263
}
@@ -267,7 +267,7 @@ private static String doOpenAIResponsesCreate (Context ctx, OpenAIClient openaiC
267267
streamResponse.stream().forEach(chunk -> {
268268
// consume the stream
269269
});
270-
}
270+
}
271271
} else {
272272
openaiClient.responses().create(builder.build());
273273
}
@@ -303,7 +303,7 @@ private static String doAnthropicCreate (Context ctx, AnthropicClient anthropicC
303303
List<Object> systemList = (List<Object>) deepConvertJsonToJava(system);
304304
builder.system(com.anthropic.core.JsonValue.from(systemList));
305305
}
306-
306+
307307
builder.model(model);
308308

309309
if (messages != null) {
@@ -402,8 +402,8 @@ private static String doCreateTrace (JSONObject traceStructure) {
402402
}
403403

404404
private static void doTraceChildren(JSONArray children) {
405-
if (children == null) {
406-
return;
405+
if (children == null) {
406+
return;
407407
}
408408

409409
for (int i = 0; i < children.length(); i++) {

utils/docker_fixtures/_test_agent.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from collections.abc import Generator
33
import contextlib
44
import datetime
5+
import gzip
56
import hashlib
67
from http import HTTPStatus
78
import json
@@ -11,6 +12,7 @@
1112
from typing import TypedDict, Any, cast
1213
import urllib.parse
1314

15+
import msgpack
1416
import pytest
1517
import requests
1618
from retry import retry
@@ -386,24 +388,47 @@ def info(self):
386388
self._write_log("info", resp_json)
387389
return resp_json
388390

391+
def _decode_llmobs_body(self, body_b64: str) -> list[Any]:
392+
"""Decode base64 body; handle gzip (Java), then JSON or MessagePack (Java) encoding.
393+
394+
Returns a list of events (each event is a dict with 'spans'). Java can send multiple
395+
concatenated msgpack objects in one request; we use Unpacker to decode all of them
396+
(same as llm-obs test/conftest.py).
397+
"""
398+
decoded = base64.b64decode(body_b64)
399+
if decoded[:2] == b"\x1f\x8b":
400+
decoded = gzip.decompress(decoded)
401+
# JSON (Python/Node tracer): starts with { or [
402+
if decoded.lstrip().startswith((b"{", b"[")):
403+
parsed = json.loads(decoded)
404+
return [parsed] if isinstance(parsed, dict) else parsed
405+
# MessagePack (Java tracer): binary format; may be multiple concatenated objects
406+
unpacker = msgpack.Unpacker(unicode_errors="replace", strict_map_key=False)
407+
unpacker.feed(decoded)
408+
return list(unpacker)
409+
389410
def llmobs_requests(self) -> list[Any]:
390-
reqs = [r for r in self.requests() if r["url"].endswith("/evp_proxy/v2/api/v2/llmobs")]
411+
reqs = [
412+
r
413+
for r in self.requests()
414+
if r["url"].endswith("/evp_proxy/v2/api/v2/llmobs") or r["url"].endswith("/evp_proxy/v4/api/v2/llmobs")
415+
]
391416

392417
events = []
393418
for r in reqs:
394-
decoded_body = base64.b64decode(r["body"])
395-
events.append(json.loads(decoded_body))
419+
events.append(self._decode_llmobs_body(r["body"]))
396420
return events
397421

398-
def llmobs_evaluations_requests(self):
422+
def llmobs_evaluations_requests(self) -> list[Any]:
399423
reqs = [
400424
r
401425
for r in self.requests()
402426
if r["url"].endswith("/evp_proxy/v2/api/intake/llm-obs/v1/eval-metric")
403427
or r["url"].endswith("/evp_proxy/v2/api/intake/llm-obs/v2/eval-metric")
404428
]
405-
406-
return [json.loads(base64.b64decode(r["body"])) for r in reqs]
429+
# One decoded body per request (evaluations are typically single JSON per request)
430+
decoded_per_request = [self._decode_llmobs_body(r["body"]) for r in reqs]
431+
return [events[0] for events in decoded_per_request]
407432

408433
@contextlib.contextmanager
409434
def snapshot_context(self, token: str, ignores: list[str] | None = None):

0 commit comments

Comments
 (0)