judgeval 0.4.0__py3-none-any.whl → 0.5.0__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.
judgeval/__init__.py CHANGED
@@ -2,6 +2,7 @@
2
2
  from judgeval.clients import client, together_client
3
3
  from judgeval.judgment_client import JudgmentClient
4
4
  from judgeval.version_check import check_latest_version
5
+ from judgeval.local_eval_queue import LocalEvaluationQueue
5
6
 
6
7
  check_latest_version()
7
8
 
@@ -10,4 +11,5 @@ __all__ = [
10
11
  "client",
11
12
  "together_client",
12
13
  "JudgmentClient",
14
+ "LocalEvaluationQueue",
13
15
  ]
judgeval/clients.py CHANGED
@@ -2,7 +2,6 @@ import os
2
2
  from dotenv import load_dotenv
3
3
  from openai import OpenAI
4
4
  from typing import Optional
5
- from together import Together, AsyncTogether
6
5
 
7
6
  PATH_TO_DOTENV = os.path.join(os.path.dirname(__file__), ".env")
8
7
  load_dotenv(dotenv_path=PATH_TO_DOTENV)
@@ -28,6 +27,8 @@ async_together_client: Optional["AsyncTogether"] = None
28
27
  together_api_key = os.getenv("TOGETHERAI_API_KEY") or os.getenv("TOGETHER_API_KEY")
29
28
  if together_api_key:
30
29
  try:
30
+ from together import Together, AsyncTogether
31
+
31
32
  together_client = Together(api_key=together_api_key)
32
33
  async_together_client = AsyncTogether(api_key=together_api_key)
33
34
  except Exception:
@@ -53,8 +53,7 @@ from judgeval.common.api.constants import (
53
53
  CheckExampleKeysPayload,
54
54
  )
55
55
  from judgeval.utils.requests import requests
56
-
57
- import orjson
56
+ from judgeval.common.api.json_encoder import json_encoder
58
57
 
59
58
 
60
59
  class JudgmentAPIException(exceptions.HTTPError):
@@ -111,7 +110,7 @@ class JudgmentApiClient:
111
110
  r = requests.request(
112
111
  method,
113
112
  url,
114
- data=self._serialize(payload),
113
+ json=json_encoder(payload),
115
114
  headers=self._headers(),
116
115
  **self._request_kwargs(),
117
116
  )
@@ -368,16 +367,3 @@ class JudgmentApiClient:
368
367
  "verify": True,
369
368
  "timeout": 30,
370
369
  }
371
-
372
- def _serialize(self, data: Any) -> str:
373
- def fallback_encoder(obj):
374
- try:
375
- return repr(obj)
376
- except Exception:
377
- try:
378
- return str(obj)
379
- except Exception as e:
380
- return f"<Unserializable object of type {type(obj).__name__}: {e}>"
381
-
382
- # orjson returns bytes, so we need to decode to str
383
- return orjson.dumps(data, default=fallback_encoder).decode("utf-8")
@@ -0,0 +1,242 @@
1
+ """
2
+
3
+ This is a modified version of https://docs.powertools.aws.dev/lambda/python/2.35.1/api/event_handler/openapi/encoders.html
4
+
5
+ """
6
+
7
+ import dataclasses
8
+ import datetime
9
+ from collections import defaultdict, deque
10
+ from decimal import Decimal
11
+ from enum import Enum
12
+ from pathlib import Path, PurePath
13
+ from re import Pattern
14
+ from types import GeneratorType
15
+ from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, Type, Union
16
+ from uuid import UUID
17
+
18
+ from pydantic import BaseModel
19
+ from pydantic.types import SecretBytes, SecretStr
20
+
21
+
22
+ """
23
+ This module contains the encoders used by jsonable_encoder to convert Python objects to JSON serializable data types.
24
+ """
25
+
26
+
27
+ def _model_dump(
28
+ model: BaseModel, mode: Literal["json", "python"] = "json", **kwargs: Any
29
+ ) -> Any:
30
+ return model.model_dump(mode=mode, **kwargs)
31
+
32
+
33
+ def json_encoder(
34
+ obj: Any,
35
+ custom_serializer: Optional[Callable[[Any], str]] = None,
36
+ ) -> Any:
37
+ """
38
+ JSON encodes an arbitrary Python object into JSON serializable data types.
39
+
40
+ This is a modified version of fastapi.encoders.jsonable_encoder that supports
41
+ encoding of pydantic.BaseModel objects.
42
+
43
+ Parameters
44
+ ----------
45
+ obj : Any
46
+ The object to encode
47
+ custom_serializer : Callable, optional
48
+ A custom serializer to use for encoding the object, when everything else fails.
49
+
50
+ Returns
51
+ -------
52
+ Any
53
+ The JSON serializable data types
54
+ """
55
+ # Pydantic models
56
+ if isinstance(obj, BaseModel):
57
+ return _dump_base_model(
58
+ obj=obj,
59
+ )
60
+
61
+ # Dataclasses
62
+ if dataclasses.is_dataclass(obj):
63
+ obj_dict = dataclasses.asdict(obj)
64
+ return json_encoder(
65
+ obj_dict,
66
+ )
67
+
68
+ # Enums
69
+ if isinstance(obj, Enum):
70
+ return obj.value
71
+
72
+ # Paths
73
+ if isinstance(obj, PurePath):
74
+ return str(obj)
75
+
76
+ # Scalars
77
+ if isinstance(obj, (str, int, float, type(None))):
78
+ return obj
79
+
80
+ # Dictionaries
81
+ if isinstance(obj, dict):
82
+ return _dump_dict(
83
+ obj=obj,
84
+ )
85
+
86
+ # Sequences
87
+ if isinstance(obj, (list, set, frozenset, GeneratorType, tuple, deque)):
88
+ return _dump_sequence(
89
+ obj=obj,
90
+ )
91
+
92
+ # Other types
93
+ if type(obj) in ENCODERS_BY_TYPE:
94
+ return ENCODERS_BY_TYPE[type(obj)](obj)
95
+
96
+ for encoder, classes_tuple in encoders_by_class_tuples.items():
97
+ if isinstance(obj, classes_tuple):
98
+ return encoder(obj)
99
+
100
+ # Use custom serializer if present
101
+ if custom_serializer:
102
+ return custom_serializer(obj)
103
+
104
+ # Default
105
+ return _dump_other(
106
+ obj=obj,
107
+ )
108
+
109
+
110
+ def _dump_base_model(
111
+ *,
112
+ obj: Any,
113
+ ):
114
+ """
115
+ Dump a BaseModel object to a dict, using the same parameters as jsonable_encoder
116
+ """
117
+ obj_dict = _model_dump(
118
+ obj,
119
+ mode="json",
120
+ )
121
+ if "__root__" in obj_dict:
122
+ obj_dict = obj_dict["__root__"]
123
+
124
+ return json_encoder(
125
+ obj_dict,
126
+ )
127
+
128
+
129
+ def _dump_dict(
130
+ *,
131
+ obj: Any,
132
+ ) -> Dict[str, Any]:
133
+ """
134
+ Dump a dict to a dict, using the same parameters as jsonable_encoder
135
+ """
136
+ encoded_dict = {}
137
+ allowed_keys = set(obj.keys())
138
+ for key, value in obj.items():
139
+ if key in allowed_keys:
140
+ encoded_key = json_encoder(
141
+ key,
142
+ )
143
+ encoded_value = json_encoder(
144
+ value,
145
+ )
146
+ encoded_dict[encoded_key] = encoded_value
147
+ return encoded_dict
148
+
149
+
150
+ def _dump_sequence(
151
+ *,
152
+ obj: Any,
153
+ ) -> List[Any]:
154
+ """
155
+ Dump a sequence to a list, using the same parameters as jsonable_encoder
156
+ """
157
+ encoded_list = []
158
+ for item in obj:
159
+ encoded_list.append(
160
+ json_encoder(
161
+ item,
162
+ ),
163
+ )
164
+ return encoded_list
165
+
166
+
167
+ def _dump_other(
168
+ *,
169
+ obj: Any,
170
+ ) -> Any:
171
+ """
172
+ Dump an object to a hashable object, using the same parameters as jsonable_encoder
173
+ """
174
+ try:
175
+ data = dict(obj)
176
+ except Exception:
177
+ return repr(obj)
178
+
179
+ return json_encoder(
180
+ data,
181
+ )
182
+
183
+
184
+ def iso_format(o: Union[datetime.date, datetime.time]) -> str:
185
+ """
186
+ ISO format for date and time
187
+ """
188
+ return o.isoformat()
189
+
190
+
191
+ def decimal_encoder(dec_value: Decimal) -> Union[int, float]:
192
+ """
193
+ Encodes a Decimal as int of there's no exponent, otherwise float
194
+
195
+ This is useful when we use ConstrainedDecimal to represent Numeric(x,0)
196
+ where an integer (but not int typed) is used. Encoding this as a float
197
+ results in failed round-tripping between encode and parse.
198
+
199
+ >>> decimal_encoder(Decimal("1.0"))
200
+ 1.0
201
+
202
+ >>> decimal_encoder(Decimal("1"))
203
+ 1
204
+ """
205
+ if dec_value.as_tuple().exponent >= 0: # type: ignore[operator]
206
+ return int(dec_value)
207
+ else:
208
+ return float(dec_value)
209
+
210
+
211
+ ENCODERS_BY_TYPE: Dict[Type[Any], Callable[[Any], Any]] = {
212
+ bytes: lambda o: o.decode(),
213
+ datetime.date: iso_format,
214
+ datetime.datetime: iso_format,
215
+ datetime.time: iso_format,
216
+ datetime.timedelta: lambda td: td.total_seconds(),
217
+ Decimal: decimal_encoder,
218
+ Enum: lambda o: o.value,
219
+ frozenset: list,
220
+ deque: list,
221
+ GeneratorType: list,
222
+ Path: str,
223
+ Pattern: lambda o: o.pattern,
224
+ SecretBytes: str,
225
+ SecretStr: str,
226
+ set: list,
227
+ UUID: str,
228
+ }
229
+
230
+
231
+ # Generates a mapping of encoders to a tuple of classes that they can encode
232
+ def generate_encoders_by_class_tuples(
233
+ type_encoder_map: Dict[Any, Callable[[Any], Any]],
234
+ ) -> Dict[Callable[[Any], Any], Tuple[Any, ...]]:
235
+ encoders: Dict[Callable[[Any], Any], Tuple[Any, ...]] = defaultdict(tuple)
236
+ for type_, encoder in type_encoder_map.items():
237
+ encoders[encoder] += (type_,)
238
+ return encoders
239
+
240
+
241
+ # Mapping of encoders to a tuple of classes that they can encode
242
+ encoders_by_class_tuples = generate_encoders_by_class_tuples(ENCODERS_BY_TYPE)