deepeval 3.5.9__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.
@@ -31,6 +31,7 @@ def update_current_span(
31
31
  if not current_span:
32
32
  return
33
33
  if test_case:
34
+
34
35
  current_span.input = test_case.input
35
36
  current_span.output = test_case.actual_output
36
37
  current_span.expected_output = test_case.expected_output
@@ -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
 
@@ -796,6 +796,9 @@ class Observer:
796
796
  # Now create the span instance with the correct trace_uuid and parent_uuid
797
797
  span_instance = self.create_span_instance()
798
798
 
799
+ # stash call arguments so they are available during the span lifetime
800
+ setattr(span_instance, "_function_kwargs", self.function_kwargs)
801
+
799
802
  # Add the span to active spans and to its trace
800
803
  trace_manager.add_span(span_instance)
801
804
  trace_manager.add_span_to_trace(span_instance)
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:
deepeval/utils.py CHANGED
@@ -21,12 +21,9 @@ from rich.progress import Progress
21
21
  from rich.console import Console, Theme
22
22
 
23
23
  from deepeval.confident.api import set_confident_api_key
24
- from deepeval.constants import CONFIDENT_OPEN_BROWSER
25
24
  from deepeval.config.settings import get_settings
26
25
  from deepeval.config.utils import (
27
- parse_bool,
28
26
  get_env_bool,
29
- bool_to_env_str,
30
27
  set_env_bool,
31
28
  )
32
29
 
@@ -418,6 +415,10 @@ def normalize_text(text: str) -> str:
418
415
  return white_space_fix(remove_articles(remove_punc(lower(text))))
419
416
 
420
417
 
418
+ def is_missing(s: Optional[str]) -> bool:
419
+ return s is None or (isinstance(s, str) and s.strip() == "")
420
+
421
+
421
422
  ###############################################
422
423
  # Source: https://github.com/tingofurro/summac
423
424
  ###############################################
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: deepeval
3
- Version: 3.5.9
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=pEL83SMnaXGf-TcVHz9_kQx6-qWl_kZPXsJxqkdMIto,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
@@ -142,10 +142,11 @@ deepeval/confident/api.py,sha256=bOC71TaVAEgoXFtJ9yMo0-atmUUdBuvaclMGczMcR6o,845
142
142
  deepeval/confident/types.py,sha256=-slFhDof_1maMgpLxqDRZv6kz6ZVY2hP_0uj_aveJKU,533
143
143
  deepeval/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
144
144
  deepeval/config/settings.py,sha256=gRRi6nXEUKse13xAShU9MA18zo14vpIgl_R0xJ_0vnM,21314
145
- deepeval/config/settings_manager.py,sha256=PsBS_5dRJASak2AUDwjhjLSiezNz1fje0R3onoFCKC0,4014
145
+ deepeval/config/settings_manager.py,sha256=enahSZN8krRu7-L94OBCt99fwUIqQtMRL97PlzsuKEY,4021
146
146
  deepeval/config/utils.py,sha256=gSOVv18Tx1R72GucbdQesbZLFL-Y9EzbS4p7qd2w_xE,3799
147
147
  deepeval/constants.py,sha256=Qe-es-WDPJndgBspEQXxddDCVanrAu03YWCpXsUkdo0,1368
148
- deepeval/dataset/__init__.py,sha256=rcum_VjBXu8eisCdr6sl84BgoZUs3x0tYbB2PnPtHGY,212
148
+ deepeval/contextvars.py,sha256=oqXtuYiKd4Zvc1rNoR1gcRBxzZYCGTMVn7XostwvkRI,524
149
+ deepeval/dataset/__init__.py,sha256=N2c-rkuxWYiiJSOZArw0H02Cwo7cnfzFuNYJlvsIBEg,249
149
150
  deepeval/dataset/api.py,sha256=ZxkEqAF4nZH_Ys_1f5r9N2LFI_vBcAJxt8eJm7Mplpw,831
150
151
  deepeval/dataset/dataset.py,sha256=dDWTSPWN8i_mZBOAgZt0r5Id6q6aeDf8jAKxv81mP1o,51113
151
152
  deepeval/dataset/golden.py,sha256=T-rTk4Hw1tANx_Iimv977F6Y4QK3s5OIB4PecU5FJDM,2338
@@ -158,7 +159,7 @@ deepeval/evaluate/api.py,sha256=rkblH0ZFAAdyuF0Ymh7JE1pIJPR9yFuPrn9SQaCEQp4,435
158
159
  deepeval/evaluate/compare.py,sha256=tdSJY4E7YJ_zO3dzvpwngZHLiUI2YQcTWJOLI83htsQ,9855
159
160
  deepeval/evaluate/configs.py,sha256=QfWjaWNxLsgEe8-5j4PIs5WcSyEckiWt0qdpXSpl57M,928
160
161
  deepeval/evaluate/evaluate.py,sha256=NPAJ2iJqJI_RurXKUIC0tft_ozYMIKwZf5iPfmnNhQc,10412
161
- deepeval/evaluate/execute.py,sha256=fJLBl45Vf4rA4Pm7k932TG-0BNIvf90klQyurXb-b_4,88057
162
+ deepeval/evaluate/execute.py,sha256=7RCjn2GGcjqK6cp9-0BtHL6PPJNw5-KXqXL60GN3G5Y,88672
162
163
  deepeval/evaluate/types.py,sha256=IGZ3Xsj0UecPI3JNeTpJaK1gDvlepokfCmHwtItIW9M,831
163
164
  deepeval/evaluate/utils.py,sha256=kkliSGzuICeUsXDtlMMPfN95dUKlqarNhfciSffd4gI,23143
164
165
  deepeval/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -393,12 +394,12 @@ deepeval/openai/__init__.py,sha256=g6ErXRZIJ08XkWJQPdnwWKk_dSOi26ucY23gx62c6OA,1
393
394
  deepeval/openai/extractors.py,sha256=q062nlYKuPVwqfLFYCD1yWv7xHF1U_XrYdAp5ve2l_E,4942
394
395
  deepeval/openai/patch.py,sha256=tPDqXaBScBJveM9P5xLT_mVwkubw0bOey-efvdjZIfg,7466
395
396
  deepeval/openai/utils.py,sha256=-84VZGUsnzRkYAFWc_DGaGuQTDCUItk0VtUTdjtSxg4,2748
396
- deepeval/openai_agents/__init__.py,sha256=u-e9laod3LyPfLcI5lr7Yhk8ArfWvlpr-D4_idWIt0A,321
397
- deepeval/openai_agents/agent.py,sha256=gZcmfqTgrQaJV8g6ChmmdpyArEp6oDIqHSaYPDEd344,6100
398
- deepeval/openai_agents/callback_handler.py,sha256=jrV2Uv9FjfU1BQQe6V_ltT3QS8ZcalxMbqzJI2vvJXo,4713
399
- deepeval/openai_agents/extractors.py,sha256=0jZxwgY1NQ3mMxVWPpLcMpKlbj-aYV7rwuzRzG8hdZs,11529
400
- deepeval/openai_agents/patch.py,sha256=zSmRV5yOReHC6IylhT93SM1nQpmH3sEWfYcJqa_iM84,3684
401
- deepeval/openai_agents/runner.py,sha256=U8Kh4jHhDIYVkIIxytcGCKRFHdgxxhpATHd9jnbh1Eg,10999
397
+ deepeval/openai_agents/__init__.py,sha256=F4c6MtsdV7LWj0YamQcMGs4_u5sOYZJXWOQP8kV5xUg,314
398
+ deepeval/openai_agents/agent.py,sha256=_SQdd0JzZK-ZvpP7yPEi22Y7fVk16PC00ROahdDQdCQ,951
399
+ deepeval/openai_agents/callback_handler.py,sha256=4Tt2OAGfYd35C5LBMekxz0SDivYryKGm3lxls1WT7cY,4842
400
+ deepeval/openai_agents/extractors.py,sha256=jcV-IeWLIh64astJRy_dRBAbUOIab1vp0Wzda7AgVyk,13963
401
+ deepeval/openai_agents/patch.py,sha256=MNvbGe5NLq0rC7L-7lnqcxKhclQvLuBKZnZyAifSHLY,10241
402
+ deepeval/openai_agents/runner.py,sha256=WtHuzhYHgC571uJYGjbTz3R23VaKnlKybGJSRCxM9pY,12310
402
403
  deepeval/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
403
404
  deepeval/plugins/plugin.py,sha256=_dwsdx4Dg9DbXxK3f7zJY4QWTJQWc7QE1HmIg2Zjjag,1515
404
405
  deepeval/progress_context.py,sha256=ZSKpxrE9sdgt9G3REKnVeXAv7GJXHHVGgLynpG1Pudw,3557
@@ -445,23 +446,23 @@ deepeval/test_run/hyperparameters.py,sha256=f7M07w1EfT8YPtiD9xVIVYa3ZewkxewSkK7k
445
446
  deepeval/test_run/test_run.py,sha256=eCo_NESZruIAtSu2feSbz9AtOcu9v92TNiS0OON_i-I,33611
446
447
  deepeval/tracing/__init__.py,sha256=OPsA_VmYNLC1M-WYJ37R6SxGyLnoXIkuyMBTcAneeao,530
447
448
  deepeval/tracing/api.py,sha256=rq4rB5f3tfrv6l4mRJmDrwRj5CH4dyatwxhG7p8xbVk,4867
448
- deepeval/tracing/context.py,sha256=mA82v7nXVLdM6tQrul8zt7H_sap-8Nfrm2uCpbT5ffM,5337
449
+ deepeval/tracing/context.py,sha256=rzm42zYzP7jmQJO08AV-Qmw86ik45qRfF4UQNpGcmJw,5338
449
450
  deepeval/tracing/offline_evals/__init__.py,sha256=bEniJAl7PmS9u2ksiOTfHtlCPJ9_CJV5R6umrUOX5MM,102
450
451
  deepeval/tracing/offline_evals/api.py,sha256=eBfqh2uWyeRkIeGhjrN1bTQzAEow-XPubs-42WEZ2QQ,510
451
452
  deepeval/tracing/offline_evals/span.py,sha256=pXqTVXs-WnjRVpCYYEbNe0zSM6Wz9GsKHsM5ZcWxrmM,1802
452
453
  deepeval/tracing/offline_evals/thread.py,sha256=bcSGFcZJKnszArOLIlWvnCyt0zSmsd7Xsw5rl4RTVFg,1981
453
454
  deepeval/tracing/offline_evals/trace.py,sha256=vTflaTKysKRiYvKA-Nx6PUJ3J6NrRLXiIdWieVcm90E,1868
454
455
  deepeval/tracing/otel/__init__.py,sha256=HQsaF5yLPwyW5qg8AOV81_nG_7pFHnatOTHi9Wx3HEk,88
455
- deepeval/tracing/otel/exporter.py,sha256=YfzV2zyxRwD6FO-qV0PR-y1nk1kInPJp0TtIuWNHbh0,28606
456
- 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
457
458
  deepeval/tracing/patchers.py,sha256=DAPNkhrDtoeyJIVeQDUMhTz-xGcXu00eqjQZmov8FiU,3096
458
459
  deepeval/tracing/perf_epoch_bridge.py,sha256=iyAPddB6Op7NpMtPHJ29lDm53Btz9yLaN6xSCfTRQm4,1825
459
- deepeval/tracing/tracing.py,sha256=b-0T3W6lAEOEGhODx0e-yIwBkm5V46EDNAWS9lcWkD0,42306
460
+ deepeval/tracing/tracing.py,sha256=WFXfGLt58Ia9yCohDZBIUGX6mwieoF8489UziuC-NJI,42458
460
461
  deepeval/tracing/types.py,sha256=l_utWKerNlE5H3mOKpeUJLsvpP3cMyjH7HRANNgTmSQ,5306
461
- deepeval/tracing/utils.py,sha256=w_kdhuyBCygllnbqLpDdKJqpJo42t3ZMlGhNicV2A8c,6467
462
- deepeval/utils.py,sha256=r8tV_NYJSi6ib-oQw6cLw3L7ZSe4KIJVJc1ng6-kDX4,17179
463
- deepeval-3.5.9.dist-info/LICENSE.md,sha256=0ATkuLv6QgsJTBODUHC5Rak_PArA6gv2t7inJzNTP38,11352
464
- deepeval-3.5.9.dist-info/METADATA,sha256=gsap42eEwklR_la4cilLkE2HKQxOP_rnSPmGtvjdqzg,18743
465
- deepeval-3.5.9.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
466
- deepeval-3.5.9.dist-info/entry_points.txt,sha256=fVr8UphXTfJe9I2rObmUtfU3gkSrYeM0pLy-NbJYg10,94
467
- deepeval-3.5.9.dist-info/RECORD,,
462
+ deepeval/tracing/utils.py,sha256=RUcsDpS_aobK3zuNfZGNvjk7aBbBfHOj3aYu2hRZzg0,7993
463
+ deepeval/utils.py,sha256=-_o3W892u7naX4Y7a8if4mP0Rtkgtapg6Krr1ZBpj0o,17197
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,,