judgeval 0.9.3__py3-none-any.whl → 0.9.4__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.
@@ -57,7 +57,7 @@ from judgeval.utils.serialize import safe_serialize
57
57
  from judgeval.version import get_version
58
58
  from judgeval.warnings import JudgmentWarning
59
59
 
60
- from judgeval.tracer.keys import AttributeKeys, ResourceKeys, InternalAttributeKeys
60
+ from judgeval.tracer.keys import AttributeKeys, InternalAttributeKeys
61
61
  from judgeval.api import JudgmentSyncClient
62
62
  from judgeval.tracer.llm import wrap_provider
63
63
  from judgeval.utils.url import url_for
@@ -65,6 +65,7 @@ from judgeval.tracer.local_eval_queue import LocalEvaluationQueue
65
65
  from judgeval.tracer.processors import (
66
66
  JudgmentSpanProcessor,
67
67
  NoOpJudgmentSpanProcessor,
68
+ NoOpSpanProcessor,
68
69
  )
69
70
  from judgeval.tracer.utils import set_span_attribute, TraceScorerConfig
70
71
 
@@ -85,19 +86,6 @@ class AgentContext(TypedDict):
85
86
  parent_agent_id: str | None
86
87
 
87
88
 
88
- def resolve_project_id(
89
- api_key: str, organization_id: str, project_name: str
90
- ) -> str | None:
91
- try:
92
- client = JudgmentSyncClient(
93
- api_key=api_key,
94
- organization_id=organization_id,
95
- )
96
- return client.projects_resolve({"project_name": project_name})["project_id"]
97
- except Exception:
98
- return None
99
-
100
-
101
89
  class Tracer:
102
90
  _active_tracers: List[Tracer] = []
103
91
 
@@ -188,38 +176,20 @@ class Tracer:
188
176
  self.cost_context = ContextVar("current_cost_context", default=None)
189
177
 
190
178
  if self.enable_monitoring:
191
- project_id = resolve_project_id(
192
- self.api_key, self.organization_id, self.project_name
193
- )
194
-
195
- resource_attributes = resource_attributes or {}
196
- resource_attributes.update(
197
- {
198
- ResourceKeys.SERVICE_NAME: self.project_name,
199
- ResourceKeys.TELEMETRY_SDK_NAME: "judgeval",
200
- ResourceKeys.TELEMETRY_SDK_VERSION: get_version(),
201
- }
202
- )
203
-
204
- if project_id is not None:
205
- resource_attributes[ResourceKeys.JUDGMENT_PROJECT_ID] = project_id
206
- else:
207
- judgeval_logger.error(
208
- f"Failed to resolve project {self.project_name}, please create it first at https://app.judgmentlabs.ai/projects. Skipping Judgment export."
209
- )
210
-
211
- resource = Resource.create(resource_attributes)
212
-
213
179
  self.judgment_processor = JudgmentSpanProcessor(
214
180
  self,
215
- self.api_url,
181
+ self.project_name,
216
182
  self.api_key,
217
183
  self.organization_id,
218
184
  max_queue_size=2**18,
219
185
  export_timeout_millis=30000,
186
+ resource_attributes=resource_attributes,
220
187
  )
221
- self.processors.append(self.judgment_processor)
188
+
189
+ resource = Resource.create(self.judgment_processor.resource_attributes)
222
190
  self.provider = TracerProvider(resource=resource)
191
+
192
+ self.processors.append(self.judgment_processor)
223
193
  for processor in self.processors:
224
194
  self.provider.add_span_processor(processor)
225
195
 
@@ -253,6 +223,14 @@ class Tracer:
253
223
  def get_current_cost_context(self):
254
224
  return self.cost_context
255
225
 
226
+ def get_processor(self):
227
+ """Get the judgment span processor instance.
228
+
229
+ Returns:
230
+ The JudgmentSpanProcessor or NoOpJudgmentSpanProcessor instance used by this tracer.
231
+ """
232
+ return self.judgment_processor
233
+
256
234
  def set_customer_id(self, customer_id: str) -> None:
257
235
  span = self.get_current_span()
258
236
  if span and span.is_recording():
@@ -913,11 +891,7 @@ class Tracer:
913
891
  proper cleanup before program termination.
914
892
  """
915
893
  try:
916
- success = self.force_flush(timeout_millis=30000)
917
- if not success:
918
- judgeval_logger.warning(
919
- "Some spans may not have been exported before program exit"
920
- )
894
+ self.force_flush(timeout_millis=30000)
921
895
  except Exception as e:
922
896
  judgeval_logger.warning(f"Error during atexit flush: {e}")
923
897
 
@@ -1074,3 +1048,13 @@ def format_inputs(
1074
1048
  return inputs
1075
1049
  except Exception:
1076
1050
  return {}
1051
+
1052
+
1053
+ # Export processor classes for direct access
1054
+ __all__ = [
1055
+ "Tracer",
1056
+ "wrap",
1057
+ "JudgmentSpanProcessor",
1058
+ "NoOpJudgmentSpanProcessor",
1059
+ "NoOpSpanProcessor",
1060
+ ]
@@ -6,8 +6,13 @@ from opentelemetry.sdk.trace import ReadableSpan, Span, SpanProcessor, SpanConte
6
6
  from opentelemetry.sdk.trace.export import (
7
7
  BatchSpanProcessor,
8
8
  )
9
+ from opentelemetry.sdk.resources import Resource
9
10
  from judgeval.tracer.exporters import JudgmentSpanExporter
10
- from judgeval.tracer.keys import AttributeKeys, InternalAttributeKeys
11
+ from judgeval.tracer.keys import AttributeKeys, InternalAttributeKeys, ResourceKeys
12
+ from judgeval.api import JudgmentSyncClient
13
+ from judgeval.logger import judgeval_logger
14
+ from judgeval.utils.url import url_for
15
+ from judgeval.version import get_version
11
16
 
12
17
  if TYPE_CHECKING:
13
18
  from judgeval.tracer import Tracer
@@ -31,15 +36,27 @@ class JudgmentSpanProcessor(BatchSpanProcessor):
31
36
  def __init__(
32
37
  self,
33
38
  tracer: Tracer,
34
- endpoint: str,
39
+ project_name: str,
35
40
  api_key: str,
36
41
  organization_id: str,
37
42
  /,
38
43
  *,
39
44
  max_queue_size: int = 2**18,
40
45
  export_timeout_millis: int = 30000,
46
+ resource_attributes: Optional[dict[str, Any]] = None,
41
47
  ):
42
48
  self.tracer = tracer
49
+ self.project_name = project_name
50
+ self.api_key = api_key
51
+ self.organization_id = organization_id
52
+
53
+ # Resolve project_id
54
+ self.project_id = self._resolve_project_id()
55
+
56
+ # Set up resource attributes with project_id
57
+ self._setup_resource_attributes(resource_attributes or {})
58
+
59
+ endpoint = url_for("/otel/v1/traces")
43
60
  super().__init__(
44
61
  JudgmentSpanExporter(
45
62
  endpoint=endpoint,
@@ -53,6 +70,38 @@ class JudgmentSpanProcessor(BatchSpanProcessor):
53
70
  defaultdict(dict)
54
71
  )
55
72
 
73
+ def _resolve_project_id(self) -> str | None:
74
+ """Resolve project_id from project_name using the API."""
75
+ try:
76
+ client = JudgmentSyncClient(
77
+ api_key=self.api_key,
78
+ organization_id=self.organization_id,
79
+ )
80
+ return client.projects_resolve({"project_name": self.project_name})[
81
+ "project_id"
82
+ ]
83
+ except Exception:
84
+ return None
85
+
86
+ def _setup_resource_attributes(self, resource_attributes: dict[str, Any]) -> None:
87
+ """Set up resource attributes including project_id."""
88
+ resource_attributes.update(
89
+ {
90
+ ResourceKeys.SERVICE_NAME: self.project_name,
91
+ ResourceKeys.TELEMETRY_SDK_NAME: "judgeval",
92
+ ResourceKeys.TELEMETRY_SDK_VERSION: get_version(),
93
+ }
94
+ )
95
+
96
+ if self.project_id is not None:
97
+ resource_attributes[ResourceKeys.JUDGMENT_PROJECT_ID] = self.project_id
98
+ else:
99
+ judgeval_logger.error(
100
+ f"Failed to resolve project {self.project_name}, please create it first at https://app.judgmentlabs.ai/projects. Skipping Judgment export."
101
+ )
102
+
103
+ self.resource_attributes = resource_attributes
104
+
56
105
  def _get_span_key(self, span_context: SpanContext) -> tuple[int, int]:
57
106
  return (span_context.trace_id, span_context.span_id)
58
107
 
@@ -103,11 +152,18 @@ class JudgmentSpanProcessor(BatchSpanProcessor):
103
152
 
104
153
  attributes = dict(current_span.attributes or {})
105
154
  attributes[AttributeKeys.JUDGMENT_UPDATE_ID] = current_update_id
155
+
156
+ existing_resource_attrs = (
157
+ dict(current_span.resource.attributes) if current_span.resource else {}
158
+ )
159
+ merged_resource_attrs = {**existing_resource_attrs, **self.resource_attributes}
160
+ merged_resource = Resource.create(merged_resource_attrs)
161
+
106
162
  partial_span = ReadableSpan(
107
163
  name=current_span.name,
108
164
  context=span_context,
109
165
  parent=current_span.parent,
110
- resource=current_span.resource,
166
+ resource=merged_resource,
111
167
  attributes=attributes,
112
168
  events=current_span.events,
113
169
  links=current_span.links,
@@ -137,11 +193,20 @@ class JudgmentSpanProcessor(BatchSpanProcessor):
137
193
  attributes = dict(span.attributes or {})
138
194
  attributes[AttributeKeys.JUDGMENT_UPDATE_ID] = 20
139
195
 
196
+ existing_resource_attrs = (
197
+ dict(span.resource.attributes) if span.resource else {}
198
+ )
199
+ merged_resource_attrs = {
200
+ **existing_resource_attrs,
201
+ **self.resource_attributes,
202
+ }
203
+ merged_resource = Resource.create(merged_resource_attrs)
204
+
140
205
  final_span = ReadableSpan(
141
206
  name=span.name,
142
207
  context=span.context,
143
208
  parent=span.parent,
144
- resource=span.resource,
209
+ resource=merged_resource,
145
210
  attributes=attributes,
146
211
  events=span.events,
147
212
  links=span.links,
@@ -160,7 +225,7 @@ class JudgmentSpanProcessor(BatchSpanProcessor):
160
225
 
161
226
  class NoOpJudgmentSpanProcessor(JudgmentSpanProcessor):
162
227
  def __init__(self):
163
- super().__init__(None, "", "", "") # type: ignore[arg-type]
228
+ pass
164
229
 
165
230
  def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None:
166
231
  pass
@@ -177,5 +242,18 @@ class NoOpJudgmentSpanProcessor(JudgmentSpanProcessor):
177
242
  def emit_partial(self) -> None:
178
243
  pass
179
244
 
245
+ def set_internal_attribute(
246
+ self, span_context: SpanContext, key: str, value: Any
247
+ ) -> None:
248
+ pass
249
+
250
+ def get_internal_attribute(
251
+ self, span_context: SpanContext, key: str, default: Any = None
252
+ ) -> Any:
253
+ return default
254
+
255
+ def increment_update_id(self, span_context: SpanContext) -> int:
256
+ return 0
257
+
180
258
 
181
- __all__ = ("NoOpSpanProcessor", "JudgmentSpanProcessor", "NoOpJudgmentSpanProcessor")
259
+ __all__ = ["NoOpSpanProcessor", "JudgmentSpanProcessor", "NoOpJudgmentSpanProcessor"]
@@ -19,6 +19,8 @@ from pydantic import BaseModel
19
19
  from pydantic.types import SecretBytes, SecretStr
20
20
  import orjson
21
21
 
22
+ from judgeval.logger import judgeval_logger
23
+
22
24
 
23
25
  """
24
26
  This module contains the encoders used by jsonable_encoder to convert Python objects to JSON serializable data types.
@@ -244,4 +246,8 @@ encoders_by_class_tuples = generate_encoders_by_class_tuples(ENCODERS_BY_TYPE)
244
246
 
245
247
  # Seralize arbitrary object to a json string
246
248
  def safe_serialize(obj: Any) -> str:
247
- return orjson.dumps(json_encoder(obj)).decode()
249
+ try:
250
+ return orjson.dumps(json_encoder(obj)).decode()
251
+ except Exception as e:
252
+ judgeval_logger.warning(f"Error serializing object: {e}")
253
+ return orjson.dumps(repr(obj)).decode()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: judgeval
3
- Version: 0.9.3
3
+ Version: 0.9.4
4
4
  Summary: Judgeval Package
5
5
  Project-URL: Homepage, https://github.com/JudgmentLabs/judgeval
6
6
  Project-URL: Issues, https://github.com/JudgmentLabs/judgeval/issues
@@ -9,7 +9,7 @@ License-Expression: Apache-2.0
9
9
  License-File: LICENSE.md
10
10
  Classifier: Operating System :: OS Independent
11
11
  Classifier: Programming Language :: Python :: 3
12
- Requires-Python: >=3.11
12
+ Requires-Python: >=3.10
13
13
  Requires-Dist: boto3>=1.40.11
14
14
  Requires-Dist: click<8.2.0
15
15
  Requires-Dist: dotenv
@@ -46,7 +46,7 @@ judgeval/scorers/judgeval_scorers/api_scorers/instruction_adherence.py,sha256=aQ
46
46
  judgeval/scorers/judgeval_scorers/api_scorers/prompt_scorer.py,sha256=C-9Q7s9K7mcgFMcEL0I_7XQZMRqrL5MFRi9G6Dx8-v8,8505
47
47
  judgeval/scorers/judgeval_scorers/api_scorers/tool_dependency.py,sha256=BhrLnIASZOTT9XJ6giYSoVfdR7NYsjRRTOTNioNtEiU,610
48
48
  judgeval/scorers/judgeval_scorers/api_scorers/tool_order.py,sha256=bMu0WMJaXdMyDTN42sVLoWV-lrUHCEa8iDrCI_K7nlQ,808
49
- judgeval/tracer/__init__.py,sha256=WmRmfvBOkzwEaAljwJ_ClNDJt1zB6J562G5BYXNMySY,36914
49
+ judgeval/tracer/__init__.py,sha256=0DM6ixBI75FuVG7UMG_k-KHJm1MyFbRyhAUPm2GYu9A,36057
50
50
  judgeval/tracer/constants.py,sha256=ae8tivAW97awJQxdRB9OMqX50wOLX3zqChT_AGkPBu0,85
51
51
  judgeval/tracer/keys.py,sha256=qXPoZSkEhVF-YYfQ9-zeDMVdr4GtpPf2W7MPJaN2AQo,2889
52
52
  judgeval/tracer/local_eval_queue.py,sha256=Amt7xkdmVJH1l2itm-ogiIW5oDaLnACisGfsdZjazn0,7228
@@ -58,7 +58,7 @@ judgeval/tracer/exporters/store.py,sha256=KQV3cyqteesByQjR-9VdPXT9OlUZ-6F08ogqj8
58
58
  judgeval/tracer/exporters/utils.py,sha256=JRcoSQuEHxMDJbXfyrUIfA2SHBVkZM82h4bTbYGxkNw,1154
59
59
  judgeval/tracer/llm/__init__.py,sha256=p9uwWPg9k-NcWjj9TbwQj55sHhBOqRYx2-Ld6YHaFUs,42625
60
60
  judgeval/tracer/llm/providers.py,sha256=QQLJlSNnDjXRAc2Wqw78o254COJUSXX39D7D_mx3NVA,2651
61
- judgeval/tracer/processors/__init__.py,sha256=Fpm_cocYj3fwWAE9hcVder6TcYcycbXJWpmdyOjw5uY,5802
61
+ judgeval/tracer/processors/__init__.py,sha256=fjk3zGxQGp6adnj1-QdSaiRJk-VhyzuKG5vCalvbucI,8645
62
62
  judgeval/trainer/__init__.py,sha256=h_DDVV7HFF7HUPAJFpt2d9wjqgnmEVcHxqZyB1k7pPQ,257
63
63
  judgeval/trainer/config.py,sha256=8s0X8B334PJomorwONaUpb6K8cAMxRdYAeQdtx7HPHs,4258
64
64
  judgeval/trainer/console.py,sha256=PJ0rCnDwC7aoW-VsLDS96ZyMyagh-l9EOJKff1ATIpo,4342
@@ -69,12 +69,12 @@ judgeval/utils/decorators.py,sha256=rdqY1w0zNL6O6GU6Wdeo0-x5EgpFTEhU2vkgiWsRYdc,
69
69
  judgeval/utils/file_utils.py,sha256=3LI1YCZwO5ogTgJreyOgRgDksey3natO2Td1PQqaPyY,3252
70
70
  judgeval/utils/guards.py,sha256=QBb6m6KElxdvt2bskLZCKh_zGHbBcqV-VfGzT63o3hY,807
71
71
  judgeval/utils/meta.py,sha256=wQFCLJTNKF9yUdXcw37AT6mC-wqzZpAvjn5gP_6flD8,349
72
- judgeval/utils/serialize.py,sha256=AtpdMQXC03xhImLJjSNCi-PqsUek2mcwvtd2ryrATTQ,6057
72
+ judgeval/utils/serialize.py,sha256=QXR-8Nj5rqOrI9zLx0oRLdk6DW6Bc7j8eyF4zQ7PLxA,6256
73
73
  judgeval/utils/testing.py,sha256=kJOq4LlEXaNThfg9oSIRqSK7IH8AwLgbukjn5uxMY7A,3661
74
74
  judgeval/utils/url.py,sha256=Shf0v3XcbaWpL0m1eGJEEO_z4TsQCnDB2Rl25OTUmiI,195
75
75
  judgeval/utils/version_check.py,sha256=kcF6SvB6GbVKI0Gv9QRVm-kvBn9_z-c3jmPORsXO3h0,1015
76
- judgeval-0.9.3.dist-info/METADATA,sha256=vMjBFO5pCziw56JqqnRvqAMJD7WXFdVL3yfIHqADC3c,8869
77
- judgeval-0.9.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
78
- judgeval-0.9.3.dist-info/entry_points.txt,sha256=-eoeD-oDLn4A7MSgeBS9Akwanf3_0r0cgEleBcIOjg0,46
79
- judgeval-0.9.3.dist-info/licenses/LICENSE.md,sha256=tKmCg7k5QOmxPK19XMfzim04QiQJPmgIm0pAn55IJwk,11352
80
- judgeval-0.9.3.dist-info/RECORD,,
76
+ judgeval-0.9.4.dist-info/METADATA,sha256=Hr4y27-wt-658_DJd_D7oAUpDebQS5a9jfdQwGvfmbg,8869
77
+ judgeval-0.9.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
78
+ judgeval-0.9.4.dist-info/entry_points.txt,sha256=-eoeD-oDLn4A7MSgeBS9Akwanf3_0r0cgEleBcIOjg0,46
79
+ judgeval-0.9.4.dist-info/licenses/LICENSE.md,sha256=tKmCg7k5QOmxPK19XMfzim04QiQJPmgIm0pAn55IJwk,11352
80
+ judgeval-0.9.4.dist-info/RECORD,,