deepeval 3.6.0__py3-none-any.whl → 3.6.1__py3-none-any.whl

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.
deepeval/_version.py CHANGED
@@ -1 +1 @@
1
- __version__: str = "3.6.0"
1
+ __version__: str = "3.6.1"
@@ -83,7 +83,9 @@ class DeepEvalTracingProcessor(TracingProcessor):
83
83
  if not span.started_at:
84
84
  return
85
85
  current_span = current_span_context.get()
86
- if current_span and isinstance(current_span, LlmSpan):
86
+ if current_span and isinstance(
87
+ current_span, LlmSpan
88
+ ): # llm span started by
87
89
  return
88
90
 
89
91
  span_type = self.get_span_kind(span.span_data)
@@ -101,10 +103,15 @@ class DeepEvalTracingProcessor(TracingProcessor):
101
103
  current_trace_context.get(), span.span_data
102
104
  )
103
105
 
106
+ span_type = self.get_span_kind(span.span_data)
104
107
  current_span = current_span_context.get()
105
- if current_span and isinstance(current_span, LlmSpan):
108
+ if (
109
+ current_span
110
+ and isinstance(current_span, LlmSpan)
111
+ and span_type == "llm"
112
+ ): # addtional check if the span kind data is llm too
106
113
  update_span_properties(current_span, span.span_data)
107
- return
114
+
108
115
  observer = self.span_observers.pop(span.span_id, None)
109
116
  if observer:
110
117
  observer.__exit__(None, None, None)
@@ -42,6 +42,7 @@ from deepeval.tracing.types import TraceAttributes
42
42
  from deepeval.test_case import ToolCall
43
43
  from dataclasses import dataclass
44
44
  import deepeval
45
+ from deepeval.tracing.utils import make_json_serializable_for_metadata
45
46
 
46
47
 
47
48
  @dataclass
@@ -360,6 +361,8 @@ class ConfidentSpanExporter(SpanExporter):
360
361
  raw_trace_expected_tools
361
362
  )
362
363
  trace_metadata = self._parse_json_string(raw_trace_metadata)
364
+ if trace_metadata:
365
+ trace_metadata = make_json_serializable_for_metadata(trace_metadata)
363
366
  trace_metric_collection = parse_string(raw_trace_metric_collection)
364
367
 
365
368
  base_span_wrapper.trace_input = trace_input
@@ -426,6 +429,9 @@ class ConfidentSpanExporter(SpanExporter):
426
429
  span_tools_called = self._parse_list_of_tools(raw_span_tools_called)
427
430
  span_expected_tools = self._parse_list_of_tools(raw_span_expected_tools)
428
431
  span_metadata = self._parse_json_string(raw_span_metadata)
432
+ if span_metadata:
433
+ span_metadata = make_json_serializable_for_metadata(span_metadata)
434
+
429
435
  span_metric_collection = parse_string(raw_span_metric_collection)
430
436
 
431
437
  # Set Span Attributes
@@ -4,6 +4,8 @@ from deepeval.tracing import trace_manager, BaseSpan
4
4
  from opentelemetry.sdk.trace.export import ReadableSpan
5
5
  import json
6
6
 
7
+ from deepeval.tracing.utils import make_json_serializable
8
+
7
9
  GEN_AI_OPERATION_NAMES = ["chat", "generate_content", "task_completion"]
8
10
 
9
11
 
@@ -103,10 +105,13 @@ def check_llm_input_from_gen_ai_attributes(
103
105
  output = None
104
106
  try:
105
107
  input = json.loads(span.attributes.get("gen_ai.input.messages"))
108
+ input = _flatten_input(input)
109
+
106
110
  except Exception as e:
107
111
  pass
108
112
  try:
109
113
  output = json.loads(span.attributes.get("gen_ai.output.messages"))
114
+ output = _flatten_input(output)
110
115
  except Exception as e:
111
116
  pass
112
117
 
@@ -127,6 +132,61 @@ def check_llm_input_from_gen_ai_attributes(
127
132
  return input, output
128
133
 
129
134
 
135
+ def _flatten_input(input: list) -> list:
136
+ if input and isinstance(input, list):
137
+ try:
138
+ result: List[dict] = []
139
+ for m in input:
140
+ if isinstance(m, dict):
141
+ role = m.get("role")
142
+ if not role:
143
+ role = "assistant"
144
+ parts = m.get("parts")
145
+ if parts:
146
+ for part in parts:
147
+ if isinstance(part, dict):
148
+ ptype = part.get("type")
149
+ if ptype == "text":
150
+ result.append(
151
+ {
152
+ "role": role,
153
+ "content": part.get("content"),
154
+ }
155
+ )
156
+ else:
157
+ result.append(
158
+ {
159
+ "role": role,
160
+ "content": make_json_serializable(
161
+ part
162
+ ),
163
+ }
164
+ )
165
+ else:
166
+ result.append(
167
+ {
168
+ "role": role,
169
+ "content": make_json_serializable(part),
170
+ }
171
+ )
172
+ else:
173
+ result.append(
174
+ {"role": role, "content": m.get("content")}
175
+ ) # no parts
176
+ else:
177
+ result.append(
178
+ {
179
+ "role": "assistant",
180
+ "content": make_json_serializable(m),
181
+ }
182
+ )
183
+ return result
184
+ except Exception as e:
185
+ return input
186
+
187
+ return input
188
+
189
+
130
190
  def check_tool_name_from_gen_ai_attributes(span: ReadableSpan) -> Optional[str]:
131
191
  try:
132
192
  gen_ai_tool_name = span.attributes.get("gen_ai.tool.name")
@@ -374,6 +434,8 @@ def check_pydantic_ai_agent_input_output(
374
434
  except Exception:
375
435
  pass
376
436
 
437
+ input_val = _flatten_input(input_val)
438
+ output_val = _flatten_input(output_val)
377
439
  return input_val, output_val
378
440
 
379
441
 
deepeval/tracing/utils.py CHANGED
@@ -100,6 +100,60 @@ def make_json_serializable(obj):
100
100
  return _serialize(obj)
101
101
 
102
102
 
103
+ def make_json_serializable_for_metadata(obj):
104
+ """
105
+ Recursively converts an object to a JSON‐serializable form,
106
+ replacing circular references with "<circular>".
107
+ """
108
+ seen = set() # Store `id` of objects we've visited
109
+
110
+ def _serialize(o):
111
+ oid = id(o)
112
+
113
+ # strip Nulls
114
+ if isinstance(o, str):
115
+ return _strip_nul(o)
116
+
117
+ # Primitive types are already serializable
118
+ if isinstance(o, (str, int, float, bool)) or o is None:
119
+ return str(o)
120
+
121
+ # Detect circular reference
122
+ if oid in seen:
123
+ return "<circular>"
124
+
125
+ # Mark current object as seen
126
+ seen.add(oid)
127
+
128
+ # Handle containers
129
+ if isinstance(o, (list, tuple, set, deque)): # TODO: check if more
130
+ serialized = []
131
+ for item in o:
132
+ serialized.append(_serialize(item))
133
+
134
+ return serialized
135
+
136
+ if isinstance(o, dict):
137
+ result = {}
138
+ for key, value in o.items():
139
+ # Convert key to string (JSON only allows string keys)
140
+ result[str(key)] = _serialize(value)
141
+ return result
142
+
143
+ # Handle objects with __dict__
144
+ if hasattr(o, "__dict__"):
145
+ result = {}
146
+ for key, value in vars(o).items():
147
+ if not key.startswith("_"):
148
+ result[key] = _serialize(value)
149
+ return result
150
+
151
+ # Fallback: convert to string
152
+ return _strip_nul(str(o))
153
+
154
+ return _serialize(obj)
155
+
156
+
103
157
  def to_zod_compatible_iso(
104
158
  dt: datetime, microsecond_precision: bool = False
105
159
  ) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: deepeval
3
- Version: 3.6.0
3
+ Version: 3.6.1
4
4
  Summary: The LLM Evaluation Framework
5
5
  Home-page: https://github.com/confident-ai/deepeval
6
6
  License: Apache-2.0
@@ -1,5 +1,5 @@
1
1
  deepeval/__init__.py,sha256=6fsb813LD_jNhqR-xZnSdE5E-KsBbC3tc4oIg5ZMgTw,2115
2
- deepeval/_version.py,sha256=6gidwc6mZETsJYHWtOUcpPNi74ZeM3A60PXBZMkuLUY,27
2
+ deepeval/_version.py,sha256=60ky4ZrqXl83ooFzPWUHtPFcXD1XP6b9GQDnqw3EHOU,27
3
3
  deepeval/annotation/__init__.py,sha256=ZFhUVNNuH_YgQSZJ-m5E9iUb9TkAkEV33a6ouMDZ8EI,111
4
4
  deepeval/annotation/annotation.py,sha256=3j3-syeJepAcEj3u3e4T_BeRDzNr7yXGDIoNQGMKpwQ,2298
5
5
  deepeval/annotation/api.py,sha256=EYN33ACVzVxsFleRYm60KB4Exvff3rPJKt1VBuuX970,2147
@@ -396,7 +396,7 @@ deepeval/openai/patch.py,sha256=tPDqXaBScBJveM9P5xLT_mVwkubw0bOey-efvdjZIfg,7466
396
396
  deepeval/openai/utils.py,sha256=-84VZGUsnzRkYAFWc_DGaGuQTDCUItk0VtUTdjtSxg4,2748
397
397
  deepeval/openai_agents/__init__.py,sha256=F4c6MtsdV7LWj0YamQcMGs4_u5sOYZJXWOQP8kV5xUg,314
398
398
  deepeval/openai_agents/agent.py,sha256=_SQdd0JzZK-ZvpP7yPEi22Y7fVk16PC00ROahdDQdCQ,951
399
- deepeval/openai_agents/callback_handler.py,sha256=HSMVKgkbFz6NWOsij0bHlznoDs0CbYYvClc_8345PIc,4637
399
+ deepeval/openai_agents/callback_handler.py,sha256=4Tt2OAGfYd35C5LBMekxz0SDivYryKGm3lxls1WT7cY,4842
400
400
  deepeval/openai_agents/extractors.py,sha256=jcV-IeWLIh64astJRy_dRBAbUOIab1vp0Wzda7AgVyk,13963
401
401
  deepeval/openai_agents/patch.py,sha256=MNvbGe5NLq0rC7L-7lnqcxKhclQvLuBKZnZyAifSHLY,10241
402
402
  deepeval/openai_agents/runner.py,sha256=WtHuzhYHgC571uJYGjbTz3R23VaKnlKybGJSRCxM9pY,12310
@@ -453,16 +453,16 @@ deepeval/tracing/offline_evals/span.py,sha256=pXqTVXs-WnjRVpCYYEbNe0zSM6Wz9GsKHs
453
453
  deepeval/tracing/offline_evals/thread.py,sha256=bcSGFcZJKnszArOLIlWvnCyt0zSmsd7Xsw5rl4RTVFg,1981
454
454
  deepeval/tracing/offline_evals/trace.py,sha256=vTflaTKysKRiYvKA-Nx6PUJ3J6NrRLXiIdWieVcm90E,1868
455
455
  deepeval/tracing/otel/__init__.py,sha256=HQsaF5yLPwyW5qg8AOV81_nG_7pFHnatOTHi9Wx3HEk,88
456
- deepeval/tracing/otel/exporter.py,sha256=YfzV2zyxRwD6FO-qV0PR-y1nk1kInPJp0TtIuWNHbh0,28606
457
- deepeval/tracing/otel/utils.py,sha256=xydJyUgR1s4hTGcz-onvVHyiT7F6lUXRuh_nprHkKbU,12396
456
+ deepeval/tracing/otel/exporter.py,sha256=wPO1ITKpjueLOSNLO6nD2QL9LAd8Xcu6en8hRkB61Wo,28891
457
+ deepeval/tracing/otel/utils.py,sha256=THXOoqLau4w6Jlz0YJV3K3vQcVptxo14hcDQCJiPeks,14821
458
458
  deepeval/tracing/patchers.py,sha256=DAPNkhrDtoeyJIVeQDUMhTz-xGcXu00eqjQZmov8FiU,3096
459
459
  deepeval/tracing/perf_epoch_bridge.py,sha256=iyAPddB6Op7NpMtPHJ29lDm53Btz9yLaN6xSCfTRQm4,1825
460
460
  deepeval/tracing/tracing.py,sha256=WFXfGLt58Ia9yCohDZBIUGX6mwieoF8489UziuC-NJI,42458
461
461
  deepeval/tracing/types.py,sha256=l_utWKerNlE5H3mOKpeUJLsvpP3cMyjH7HRANNgTmSQ,5306
462
- deepeval/tracing/utils.py,sha256=w_kdhuyBCygllnbqLpDdKJqpJo42t3ZMlGhNicV2A8c,6467
462
+ deepeval/tracing/utils.py,sha256=RUcsDpS_aobK3zuNfZGNvjk7aBbBfHOj3aYu2hRZzg0,7993
463
463
  deepeval/utils.py,sha256=-_o3W892u7naX4Y7a8if4mP0Rtkgtapg6Krr1ZBpj0o,17197
464
- deepeval-3.6.0.dist-info/LICENSE.md,sha256=0ATkuLv6QgsJTBODUHC5Rak_PArA6gv2t7inJzNTP38,11352
465
- deepeval-3.6.0.dist-info/METADATA,sha256=XHRGHFIWxy8_kHr2iCFDXk-tX-ubqjwXtqofMTSVZiQ,18743
466
- deepeval-3.6.0.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
467
- deepeval-3.6.0.dist-info/entry_points.txt,sha256=fVr8UphXTfJe9I2rObmUtfU3gkSrYeM0pLy-NbJYg10,94
468
- deepeval-3.6.0.dist-info/RECORD,,
464
+ deepeval-3.6.1.dist-info/LICENSE.md,sha256=0ATkuLv6QgsJTBODUHC5Rak_PArA6gv2t7inJzNTP38,11352
465
+ deepeval-3.6.1.dist-info/METADATA,sha256=UrYM0bqzIvhmMlevcqO-Hcbbm2e5r26FwWEzz2rKua8,18743
466
+ deepeval-3.6.1.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
467
+ deepeval-3.6.1.dist-info/entry_points.txt,sha256=fVr8UphXTfJe9I2rObmUtfU3gkSrYeM0pLy-NbJYg10,94
468
+ deepeval-3.6.1.dist-info/RECORD,,