judgeval 0.16.7__py3-none-any.whl → 0.16.8__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.
Potentially problematic release.
This version of judgeval might be problematic. Click here for more details.
- judgeval/api/api_types.py +1 -2
- judgeval/data/judgment_types.py +1 -2
- judgeval/tracer/__init__.py +7 -52
- judgeval/tracer/llm/config.py +12 -44
- judgeval/tracer/llm/constants.py +0 -1
- judgeval/tracer/llm/llm_anthropic/config.py +3 -17
- judgeval/tracer/llm/llm_anthropic/messages.py +440 -0
- judgeval/tracer/llm/llm_anthropic/messages_stream.py +322 -0
- judgeval/tracer/llm/llm_anthropic/wrapper.py +40 -621
- judgeval/tracer/llm/llm_google/__init__.py +3 -0
- judgeval/tracer/llm/llm_google/config.py +3 -21
- judgeval/tracer/llm/llm_google/generate_content.py +125 -0
- judgeval/tracer/llm/llm_google/wrapper.py +19 -454
- judgeval/tracer/llm/llm_openai/beta_chat_completions.py +192 -0
- judgeval/tracer/llm/llm_openai/chat_completions.py +437 -0
- judgeval/tracer/llm/llm_openai/config.py +3 -29
- judgeval/tracer/llm/llm_openai/responses.py +444 -0
- judgeval/tracer/llm/llm_openai/wrapper.py +43 -641
- judgeval/tracer/llm/llm_together/__init__.py +3 -0
- judgeval/tracer/llm/llm_together/chat_completions.py +398 -0
- judgeval/tracer/llm/llm_together/config.py +3 -20
- judgeval/tracer/llm/llm_together/wrapper.py +34 -485
- judgeval/tracer/llm/providers.py +4 -48
- judgeval/utils/decorators/dont_throw.py +30 -14
- judgeval/utils/wrappers/README.md +3 -0
- judgeval/utils/wrappers/__init__.py +15 -0
- judgeval/utils/wrappers/immutable_wrap_async.py +74 -0
- judgeval/utils/wrappers/immutable_wrap_async_iterator.py +84 -0
- judgeval/utils/wrappers/immutable_wrap_sync.py +66 -0
- judgeval/utils/wrappers/immutable_wrap_sync_iterator.py +84 -0
- judgeval/utils/wrappers/mutable_wrap_async.py +67 -0
- judgeval/utils/wrappers/mutable_wrap_sync.py +67 -0
- judgeval/utils/wrappers/utils.py +35 -0
- judgeval/version.py +1 -1
- {judgeval-0.16.7.dist-info → judgeval-0.16.8.dist-info}/METADATA +1 -1
- {judgeval-0.16.7.dist-info → judgeval-0.16.8.dist-info}/RECORD +40 -27
- judgeval/tracer/llm/llm_groq/config.py +0 -23
- judgeval/tracer/llm/llm_groq/wrapper.py +0 -498
- judgeval/tracer/local_eval_queue.py +0 -199
- /judgeval/{tracer/llm/llm_groq/__init__.py → utils/wrappers/py.typed} +0 -0
- {judgeval-0.16.7.dist-info → judgeval-0.16.8.dist-info}/WHEEL +0 -0
- {judgeval-0.16.7.dist-info → judgeval-0.16.8.dist-info}/entry_points.txt +0 -0
- {judgeval-0.16.7.dist-info → judgeval-0.16.8.dist-info}/licenses/LICENSE.md +0 -0
|
@@ -1,24 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
|
|
2
|
+
import importlib.util
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
from google.genai import Client
|
|
6
|
-
from google.genai.client import AsyncClient
|
|
4
|
+
HAS_GOOGLE_GENAI = importlib.util.find_spec("google.genai") is not None
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
from google.genai import Client
|
|
10
|
-
from google.genai.client import AsyncClient
|
|
11
|
-
|
|
12
|
-
HAS_GOOGLE_GENAI = True
|
|
13
|
-
except ImportError:
|
|
14
|
-
HAS_GOOGLE_GENAI = False
|
|
15
|
-
Client = AsyncClient = None # type: ignore[misc,assignment]
|
|
16
|
-
|
|
17
|
-
google_genai_Client = Client
|
|
18
|
-
google_genai_AsyncClient = AsyncClient
|
|
19
|
-
|
|
20
|
-
__all__ = [
|
|
21
|
-
"HAS_GOOGLE_GENAI",
|
|
22
|
-
"google_genai_Client",
|
|
23
|
-
"google_genai_AsyncClient",
|
|
24
|
-
]
|
|
6
|
+
__all__ = ["HAS_GOOGLE_GENAI"]
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import (
|
|
3
|
+
TYPE_CHECKING,
|
|
4
|
+
Any,
|
|
5
|
+
Dict,
|
|
6
|
+
Optional,
|
|
7
|
+
Tuple,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from judgeval.tracer.keys import AttributeKeys
|
|
11
|
+
from judgeval.tracer.utils import set_span_attribute
|
|
12
|
+
from judgeval.utils.serialize import safe_serialize
|
|
13
|
+
from judgeval.utils.wrappers import immutable_wrap_sync
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from judgeval.tracer import Tracer
|
|
17
|
+
from google.genai import Client
|
|
18
|
+
from google.genai.types import (
|
|
19
|
+
GenerateContentResponse,
|
|
20
|
+
GenerateContentResponseUsageMetadata,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _extract_google_tokens(
|
|
25
|
+
usage: GenerateContentResponseUsageMetadata,
|
|
26
|
+
) -> Tuple[int, int, int, int]:
|
|
27
|
+
prompt_tokens = (
|
|
28
|
+
usage.prompt_token_count if usage.prompt_token_count is not None else 0
|
|
29
|
+
)
|
|
30
|
+
completion_tokens = (
|
|
31
|
+
usage.candidates_token_count if usage.candidates_token_count is not None else 0
|
|
32
|
+
)
|
|
33
|
+
cache_read_input_tokens = (
|
|
34
|
+
usage.cached_content_token_count
|
|
35
|
+
if usage.cached_content_token_count is not None
|
|
36
|
+
else 0
|
|
37
|
+
)
|
|
38
|
+
cache_creation_input_tokens = 0
|
|
39
|
+
return (
|
|
40
|
+
prompt_tokens,
|
|
41
|
+
completion_tokens,
|
|
42
|
+
cache_read_input_tokens,
|
|
43
|
+
cache_creation_input_tokens,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _format_google_output(
|
|
48
|
+
response: GenerateContentResponse,
|
|
49
|
+
) -> Tuple[Optional[str], Optional[GenerateContentResponseUsageMetadata]]:
|
|
50
|
+
return response.text, response.usage_metadata
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def wrap_generate_content_sync(tracer: Tracer, client: Client) -> None:
|
|
54
|
+
original_func = client.models.generate_content
|
|
55
|
+
|
|
56
|
+
def pre_hook(ctx: Dict[str, Any], *args: Any, **kwargs: Any) -> None:
|
|
57
|
+
ctx["span"] = tracer.get_tracer().start_span(
|
|
58
|
+
"GOOGLE_API_CALL", attributes={AttributeKeys.JUDGMENT_SPAN_KIND: "llm"}
|
|
59
|
+
)
|
|
60
|
+
tracer.add_agent_attributes_to_span(ctx["span"])
|
|
61
|
+
set_span_attribute(
|
|
62
|
+
ctx["span"], AttributeKeys.GEN_AI_PROMPT, safe_serialize(kwargs)
|
|
63
|
+
)
|
|
64
|
+
ctx["model_name"] = kwargs.get("model", "")
|
|
65
|
+
set_span_attribute(
|
|
66
|
+
ctx["span"], AttributeKeys.GEN_AI_REQUEST_MODEL, ctx["model_name"]
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def post_hook(ctx: Dict[str, Any], result: GenerateContentResponse) -> None:
|
|
70
|
+
span = ctx.get("span")
|
|
71
|
+
if not span:
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
output, usage_data = _format_google_output(result)
|
|
75
|
+
set_span_attribute(span, AttributeKeys.GEN_AI_COMPLETION, output)
|
|
76
|
+
|
|
77
|
+
if usage_data:
|
|
78
|
+
prompt_tokens, completion_tokens, cache_read, cache_creation = (
|
|
79
|
+
_extract_google_tokens(usage_data)
|
|
80
|
+
)
|
|
81
|
+
set_span_attribute(
|
|
82
|
+
span, AttributeKeys.GEN_AI_USAGE_INPUT_TOKENS, prompt_tokens
|
|
83
|
+
)
|
|
84
|
+
set_span_attribute(
|
|
85
|
+
span, AttributeKeys.GEN_AI_USAGE_OUTPUT_TOKENS, completion_tokens
|
|
86
|
+
)
|
|
87
|
+
set_span_attribute(
|
|
88
|
+
span, AttributeKeys.GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, cache_read
|
|
89
|
+
)
|
|
90
|
+
set_span_attribute(
|
|
91
|
+
span,
|
|
92
|
+
AttributeKeys.GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS,
|
|
93
|
+
cache_creation,
|
|
94
|
+
)
|
|
95
|
+
set_span_attribute(
|
|
96
|
+
span,
|
|
97
|
+
AttributeKeys.JUDGMENT_USAGE_METADATA,
|
|
98
|
+
safe_serialize(usage_data),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
set_span_attribute(
|
|
102
|
+
span,
|
|
103
|
+
AttributeKeys.GEN_AI_RESPONSE_MODEL,
|
|
104
|
+
result.model_version if result.model_version else ctx["model_name"],
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def error_hook(ctx: Dict[str, Any], error: Exception) -> None:
|
|
108
|
+
span = ctx.get("span")
|
|
109
|
+
if span:
|
|
110
|
+
span.record_exception(error)
|
|
111
|
+
|
|
112
|
+
def finally_hook(ctx: Dict[str, Any]) -> None:
|
|
113
|
+
span = ctx.get("span")
|
|
114
|
+
if span:
|
|
115
|
+
span.end()
|
|
116
|
+
|
|
117
|
+
wrapped = immutable_wrap_sync(
|
|
118
|
+
original_func,
|
|
119
|
+
pre_hook=pre_hook,
|
|
120
|
+
post_hook=post_hook,
|
|
121
|
+
error_hook=error_hook,
|
|
122
|
+
finally_hook=finally_hook,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
setattr(client.models, "generate_content", wrapped)
|
|
@@ -1,465 +1,30 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
import
|
|
3
|
-
from typing import (
|
|
4
|
-
TYPE_CHECKING,
|
|
5
|
-
Callable,
|
|
6
|
-
Optional,
|
|
7
|
-
Protocol,
|
|
8
|
-
Tuple,
|
|
9
|
-
Union,
|
|
10
|
-
Iterator,
|
|
11
|
-
AsyncIterator,
|
|
12
|
-
Sequence,
|
|
13
|
-
runtime_checkable,
|
|
14
|
-
)
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
15
3
|
|
|
16
|
-
from judgeval.tracer.llm.llm_google.
|
|
17
|
-
|
|
18
|
-
google_genai_AsyncClient,
|
|
4
|
+
from judgeval.tracer.llm.llm_google.generate_content import (
|
|
5
|
+
wrap_generate_content_sync,
|
|
19
6
|
)
|
|
20
|
-
from judgeval.tracer.managers import sync_span_context, async_span_context
|
|
21
|
-
from judgeval.logger import judgeval_logger
|
|
22
|
-
from judgeval.tracer.keys import AttributeKeys
|
|
23
|
-
from judgeval.tracer.utils import set_span_attribute
|
|
24
|
-
from judgeval.utils.serialize import safe_serialize
|
|
25
7
|
|
|
26
8
|
if TYPE_CHECKING:
|
|
27
9
|
from judgeval.tracer import Tracer
|
|
28
|
-
from
|
|
29
|
-
|
|
30
|
-
# Keep the original client type for runtime compatibility
|
|
31
|
-
GoogleClientType = Union[google_genai_Client, google_genai_AsyncClient]
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
# Usage protocols
|
|
35
|
-
@runtime_checkable
|
|
36
|
-
class GoogleUsageMetadata(Protocol):
|
|
37
|
-
prompt_token_count: Optional[int]
|
|
38
|
-
candidates_token_count: Optional[int]
|
|
39
|
-
total_token_count: Optional[int]
|
|
40
|
-
cached_content_token_count: Optional[int]
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# Content protocols
|
|
44
|
-
@runtime_checkable
|
|
45
|
-
class GooglePart(Protocol):
|
|
46
|
-
text: str
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
@runtime_checkable
|
|
50
|
-
class GoogleContent(Protocol):
|
|
51
|
-
parts: Sequence[GooglePart]
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
@runtime_checkable
|
|
55
|
-
class GoogleCandidate(Protocol):
|
|
56
|
-
content: GoogleContent
|
|
57
|
-
finish_reason: Optional[str]
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
@runtime_checkable
|
|
61
|
-
class GoogleGenerateContentResponse(Protocol):
|
|
62
|
-
candidates: Sequence[GoogleCandidate]
|
|
63
|
-
usage_metadata: Optional[GoogleUsageMetadata]
|
|
64
|
-
model_version: Optional[str]
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
# Stream protocols
|
|
68
|
-
@runtime_checkable
|
|
69
|
-
class GoogleStreamChunk(Protocol):
|
|
70
|
-
candidates: Sequence[GoogleCandidate]
|
|
71
|
-
usage_metadata: Optional[GoogleUsageMetadata]
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
# Client protocols
|
|
75
|
-
@runtime_checkable
|
|
76
|
-
class GoogleClient(Protocol):
|
|
77
|
-
pass
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
@runtime_checkable
|
|
81
|
-
class GoogleAsyncClient(Protocol):
|
|
82
|
-
pass
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
# Union types
|
|
86
|
-
GoogleResponseType = GoogleGenerateContentResponse
|
|
87
|
-
GoogleStreamType = Union[Iterator[GoogleStreamChunk], AsyncIterator[GoogleStreamChunk]]
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def _extract_google_content(chunk: GoogleStreamChunk) -> str:
|
|
91
|
-
if chunk.candidates and len(chunk.candidates) > 0:
|
|
92
|
-
candidate = chunk.candidates[0]
|
|
93
|
-
if (
|
|
94
|
-
candidate.content
|
|
95
|
-
and candidate.content.parts
|
|
96
|
-
and len(candidate.content.parts) > 0
|
|
97
|
-
):
|
|
98
|
-
return candidate.content.parts[0].text or ""
|
|
99
|
-
return ""
|
|
100
|
-
|
|
10
|
+
from google.genai import Client
|
|
101
11
|
|
|
102
|
-
def _extract_google_tokens(
|
|
103
|
-
usage_data: GoogleUsageMetadata,
|
|
104
|
-
) -> Tuple[int, int, int, int]:
|
|
105
|
-
prompt_tokens = usage_data.prompt_token_count or 0
|
|
106
|
-
completion_tokens = usage_data.candidates_token_count or 0
|
|
107
|
-
cache_read_input_tokens = usage_data.cached_content_token_count or 0
|
|
108
|
-
cache_creation_input_tokens = 0 # Google GenAI doesn't have cache creation tokens
|
|
109
|
-
return (
|
|
110
|
-
prompt_tokens,
|
|
111
|
-
completion_tokens,
|
|
112
|
-
cache_read_input_tokens,
|
|
113
|
-
cache_creation_input_tokens,
|
|
114
|
-
)
|
|
115
12
|
|
|
13
|
+
def wrap_google_client(tracer: Tracer, client: Client) -> Client:
|
|
14
|
+
from judgeval.tracer.llm.llm_google.config import HAS_GOOGLE_GENAI
|
|
15
|
+
from judgeval.logger import judgeval_logger
|
|
116
16
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
usage_data: Optional[GoogleUsageMetadata] = None
|
|
122
|
-
|
|
123
|
-
try:
|
|
124
|
-
if isinstance(response, GoogleGenerateContentResponse):
|
|
125
|
-
usage_data = response.usage_metadata
|
|
126
|
-
if response.candidates and len(response.candidates) > 0:
|
|
127
|
-
candidate = response.candidates[0]
|
|
128
|
-
if (
|
|
129
|
-
candidate.content
|
|
130
|
-
and candidate.content.parts
|
|
131
|
-
and len(candidate.content.parts) > 0
|
|
132
|
-
):
|
|
133
|
-
message_content = candidate.content.parts[0].text
|
|
134
|
-
except (AttributeError, IndexError, TypeError):
|
|
135
|
-
pass
|
|
136
|
-
|
|
137
|
-
return message_content, usage_data
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
class TracedGoogleGenerator:
|
|
141
|
-
def __init__(
|
|
142
|
-
self,
|
|
143
|
-
tracer: Tracer,
|
|
144
|
-
generator: Iterator[GoogleStreamChunk],
|
|
145
|
-
client: GoogleClientType,
|
|
146
|
-
span: Span,
|
|
147
|
-
model_name: str,
|
|
148
|
-
):
|
|
149
|
-
self.tracer = tracer
|
|
150
|
-
self.generator = generator
|
|
151
|
-
self.client = client
|
|
152
|
-
self.span = span
|
|
153
|
-
self.model_name = model_name
|
|
154
|
-
self.accumulated_content = ""
|
|
155
|
-
|
|
156
|
-
def __iter__(self) -> Iterator[GoogleStreamChunk]:
|
|
157
|
-
return self
|
|
158
|
-
|
|
159
|
-
def __next__(self) -> GoogleStreamChunk:
|
|
160
|
-
try:
|
|
161
|
-
chunk = next(self.generator)
|
|
162
|
-
content = _extract_google_content(chunk)
|
|
163
|
-
if content:
|
|
164
|
-
self.accumulated_content += content
|
|
165
|
-
if chunk.usage_metadata:
|
|
166
|
-
prompt_tokens, completion_tokens, cache_read, cache_creation = (
|
|
167
|
-
_extract_google_tokens(chunk.usage_metadata)
|
|
168
|
-
)
|
|
169
|
-
set_span_attribute(
|
|
170
|
-
self.span, AttributeKeys.GEN_AI_USAGE_INPUT_TOKENS, prompt_tokens
|
|
171
|
-
)
|
|
172
|
-
set_span_attribute(
|
|
173
|
-
self.span,
|
|
174
|
-
AttributeKeys.GEN_AI_USAGE_OUTPUT_TOKENS,
|
|
175
|
-
completion_tokens,
|
|
176
|
-
)
|
|
177
|
-
set_span_attribute(
|
|
178
|
-
self.span,
|
|
179
|
-
AttributeKeys.GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS,
|
|
180
|
-
cache_read,
|
|
181
|
-
)
|
|
182
|
-
set_span_attribute(
|
|
183
|
-
self.span,
|
|
184
|
-
AttributeKeys.JUDGMENT_USAGE_METADATA,
|
|
185
|
-
safe_serialize(chunk.usage_metadata),
|
|
186
|
-
)
|
|
187
|
-
return chunk
|
|
188
|
-
except StopIteration:
|
|
189
|
-
set_span_attribute(
|
|
190
|
-
self.span, AttributeKeys.GEN_AI_COMPLETION, self.accumulated_content
|
|
191
|
-
)
|
|
192
|
-
self.span.end()
|
|
193
|
-
raise
|
|
194
|
-
except Exception as e:
|
|
195
|
-
if self.span:
|
|
196
|
-
self.span.record_exception(e)
|
|
197
|
-
self.span.end()
|
|
198
|
-
raise
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
class TracedGoogleAsyncGenerator:
|
|
202
|
-
def __init__(
|
|
203
|
-
self,
|
|
204
|
-
tracer: Tracer,
|
|
205
|
-
async_generator: AsyncIterator[GoogleStreamChunk],
|
|
206
|
-
client: GoogleClientType,
|
|
207
|
-
span: Span,
|
|
208
|
-
model_name: str,
|
|
209
|
-
):
|
|
210
|
-
self.tracer = tracer
|
|
211
|
-
self.async_generator = async_generator
|
|
212
|
-
self.client = client
|
|
213
|
-
self.span = span
|
|
214
|
-
self.model_name = model_name
|
|
215
|
-
self.accumulated_content = ""
|
|
216
|
-
|
|
217
|
-
def __aiter__(self) -> AsyncIterator[GoogleStreamChunk]:
|
|
218
|
-
return self
|
|
219
|
-
|
|
220
|
-
async def __anext__(self) -> GoogleStreamChunk:
|
|
221
|
-
try:
|
|
222
|
-
chunk = await self.async_generator.__anext__()
|
|
223
|
-
content = _extract_google_content(chunk)
|
|
224
|
-
if content:
|
|
225
|
-
self.accumulated_content += content
|
|
226
|
-
if chunk.usage_metadata:
|
|
227
|
-
prompt_tokens, completion_tokens, cache_read, cache_creation = (
|
|
228
|
-
_extract_google_tokens(chunk.usage_metadata)
|
|
229
|
-
)
|
|
230
|
-
set_span_attribute(
|
|
231
|
-
self.span, AttributeKeys.GEN_AI_USAGE_INPUT_TOKENS, prompt_tokens
|
|
232
|
-
)
|
|
233
|
-
set_span_attribute(
|
|
234
|
-
self.span,
|
|
235
|
-
AttributeKeys.GEN_AI_USAGE_OUTPUT_TOKENS,
|
|
236
|
-
completion_tokens,
|
|
237
|
-
)
|
|
238
|
-
set_span_attribute(
|
|
239
|
-
self.span,
|
|
240
|
-
AttributeKeys.GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS,
|
|
241
|
-
cache_read,
|
|
242
|
-
)
|
|
243
|
-
set_span_attribute(
|
|
244
|
-
self.span,
|
|
245
|
-
AttributeKeys.JUDGMENT_USAGE_METADATA,
|
|
246
|
-
safe_serialize(chunk.usage_metadata),
|
|
247
|
-
)
|
|
248
|
-
return chunk
|
|
249
|
-
except StopAsyncIteration:
|
|
250
|
-
set_span_attribute(
|
|
251
|
-
self.span, AttributeKeys.GEN_AI_COMPLETION, self.accumulated_content
|
|
252
|
-
)
|
|
253
|
-
self.span.end()
|
|
254
|
-
raise
|
|
255
|
-
except Exception as e:
|
|
256
|
-
if self.span:
|
|
257
|
-
self.span.record_exception(e)
|
|
258
|
-
self.span.end()
|
|
259
|
-
raise
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
def wrap_google_client(tracer: Tracer, client: GoogleClientType) -> GoogleClientType:
|
|
263
|
-
def wrapped(function: Callable, span_name: str):
|
|
264
|
-
@functools.wraps(function)
|
|
265
|
-
def wrapper(*args, **kwargs):
|
|
266
|
-
if kwargs.get("stream", False):
|
|
267
|
-
try:
|
|
268
|
-
span = tracer.get_tracer().start_span(
|
|
269
|
-
span_name, attributes={AttributeKeys.JUDGMENT_SPAN_KIND: "llm"}
|
|
270
|
-
)
|
|
271
|
-
tracer.add_agent_attributes_to_span(span)
|
|
272
|
-
set_span_attribute(
|
|
273
|
-
span, AttributeKeys.GEN_AI_PROMPT, safe_serialize(kwargs)
|
|
274
|
-
)
|
|
275
|
-
model_name = kwargs.get("model", "")
|
|
276
|
-
set_span_attribute(
|
|
277
|
-
span, AttributeKeys.GEN_AI_REQUEST_MODEL, model_name
|
|
278
|
-
)
|
|
279
|
-
except Exception as e:
|
|
280
|
-
judgeval_logger.error(
|
|
281
|
-
f"[google wrapped] Error adding span metadata: {e}"
|
|
282
|
-
)
|
|
283
|
-
stream_response = function(*args, **kwargs)
|
|
284
|
-
return TracedGoogleGenerator(
|
|
285
|
-
tracer, stream_response, client, span, model_name
|
|
286
|
-
)
|
|
287
|
-
else:
|
|
288
|
-
with sync_span_context(
|
|
289
|
-
tracer, span_name, {AttributeKeys.JUDGMENT_SPAN_KIND: "llm"}
|
|
290
|
-
) as span:
|
|
291
|
-
try:
|
|
292
|
-
tracer.add_agent_attributes_to_span(span)
|
|
293
|
-
set_span_attribute(
|
|
294
|
-
span, AttributeKeys.GEN_AI_PROMPT, safe_serialize(kwargs)
|
|
295
|
-
)
|
|
296
|
-
model_name = kwargs.get("model", "")
|
|
297
|
-
set_span_attribute(
|
|
298
|
-
span, AttributeKeys.GEN_AI_REQUEST_MODEL, model_name
|
|
299
|
-
)
|
|
300
|
-
except Exception as e:
|
|
301
|
-
judgeval_logger.error(
|
|
302
|
-
f"[google wrapped] Error adding span metadata: {e}"
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
response = function(*args, **kwargs)
|
|
306
|
-
|
|
307
|
-
try:
|
|
308
|
-
if isinstance(response, GoogleGenerateContentResponse):
|
|
309
|
-
output, usage_data = _format_google_output(response)
|
|
310
|
-
set_span_attribute(
|
|
311
|
-
span, AttributeKeys.GEN_AI_COMPLETION, output
|
|
312
|
-
)
|
|
313
|
-
if usage_data:
|
|
314
|
-
(
|
|
315
|
-
prompt_tokens,
|
|
316
|
-
completion_tokens,
|
|
317
|
-
cache_read,
|
|
318
|
-
cache_creation,
|
|
319
|
-
) = _extract_google_tokens(usage_data)
|
|
320
|
-
set_span_attribute(
|
|
321
|
-
span,
|
|
322
|
-
AttributeKeys.GEN_AI_USAGE_INPUT_TOKENS,
|
|
323
|
-
prompt_tokens,
|
|
324
|
-
)
|
|
325
|
-
set_span_attribute(
|
|
326
|
-
span,
|
|
327
|
-
AttributeKeys.GEN_AI_USAGE_OUTPUT_TOKENS,
|
|
328
|
-
completion_tokens,
|
|
329
|
-
)
|
|
330
|
-
set_span_attribute(
|
|
331
|
-
span,
|
|
332
|
-
AttributeKeys.GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS,
|
|
333
|
-
cache_read,
|
|
334
|
-
)
|
|
335
|
-
set_span_attribute(
|
|
336
|
-
span,
|
|
337
|
-
AttributeKeys.JUDGMENT_USAGE_METADATA,
|
|
338
|
-
safe_serialize(usage_data),
|
|
339
|
-
)
|
|
340
|
-
set_span_attribute(
|
|
341
|
-
span,
|
|
342
|
-
AttributeKeys.GEN_AI_RESPONSE_MODEL,
|
|
343
|
-
getattr(response, "model_version", model_name),
|
|
344
|
-
)
|
|
345
|
-
except Exception as e:
|
|
346
|
-
judgeval_logger.error(
|
|
347
|
-
f"[google wrapped] Error adding span metadata: {e}"
|
|
348
|
-
)
|
|
349
|
-
finally:
|
|
350
|
-
return response
|
|
351
|
-
|
|
352
|
-
return wrapper
|
|
353
|
-
|
|
354
|
-
def wrapped_async(function: Callable, span_name: str):
|
|
355
|
-
@functools.wraps(function)
|
|
356
|
-
async def wrapper(*args, **kwargs):
|
|
357
|
-
if kwargs.get("stream", False):
|
|
358
|
-
try:
|
|
359
|
-
span = tracer.get_tracer().start_span(
|
|
360
|
-
span_name, attributes={AttributeKeys.JUDGMENT_SPAN_KIND: "llm"}
|
|
361
|
-
)
|
|
362
|
-
tracer.add_agent_attributes_to_span(span)
|
|
363
|
-
set_span_attribute(
|
|
364
|
-
span, AttributeKeys.GEN_AI_PROMPT, safe_serialize(kwargs)
|
|
365
|
-
)
|
|
366
|
-
model_name = kwargs.get("model", "")
|
|
367
|
-
set_span_attribute(
|
|
368
|
-
span, AttributeKeys.GEN_AI_REQUEST_MODEL, model_name
|
|
369
|
-
)
|
|
370
|
-
except Exception as e:
|
|
371
|
-
judgeval_logger.error(
|
|
372
|
-
f"[google wrapped_async] Error adding span metadata: {e}"
|
|
373
|
-
)
|
|
374
|
-
stream_response = await function(*args, **kwargs)
|
|
375
|
-
return TracedGoogleAsyncGenerator(
|
|
376
|
-
tracer, stream_response, client, span, model_name
|
|
377
|
-
)
|
|
378
|
-
else:
|
|
379
|
-
async with async_span_context(
|
|
380
|
-
tracer, span_name, {AttributeKeys.JUDGMENT_SPAN_KIND: "llm"}
|
|
381
|
-
) as span:
|
|
382
|
-
try:
|
|
383
|
-
tracer.add_agent_attributes_to_span(span)
|
|
384
|
-
set_span_attribute(
|
|
385
|
-
span, AttributeKeys.GEN_AI_PROMPT, safe_serialize(kwargs)
|
|
386
|
-
)
|
|
387
|
-
model_name = kwargs.get("model", "")
|
|
388
|
-
set_span_attribute(
|
|
389
|
-
span, AttributeKeys.GEN_AI_REQUEST_MODEL, model_name
|
|
390
|
-
)
|
|
391
|
-
except Exception as e:
|
|
392
|
-
judgeval_logger.error(
|
|
393
|
-
f"[google wrapped_async] Error adding span metadata: {e}"
|
|
394
|
-
)
|
|
395
|
-
|
|
396
|
-
response = await function(*args, **kwargs)
|
|
397
|
-
|
|
398
|
-
try:
|
|
399
|
-
if isinstance(response, GoogleGenerateContentResponse):
|
|
400
|
-
output, usage_data = _format_google_output(response)
|
|
401
|
-
set_span_attribute(
|
|
402
|
-
span, AttributeKeys.GEN_AI_COMPLETION, output
|
|
403
|
-
)
|
|
404
|
-
if usage_data:
|
|
405
|
-
(
|
|
406
|
-
prompt_tokens,
|
|
407
|
-
completion_tokens,
|
|
408
|
-
cache_read,
|
|
409
|
-
cache_creation,
|
|
410
|
-
) = _extract_google_tokens(usage_data)
|
|
411
|
-
set_span_attribute(
|
|
412
|
-
span,
|
|
413
|
-
AttributeKeys.GEN_AI_USAGE_INPUT_TOKENS,
|
|
414
|
-
prompt_tokens,
|
|
415
|
-
)
|
|
416
|
-
set_span_attribute(
|
|
417
|
-
span,
|
|
418
|
-
AttributeKeys.GEN_AI_USAGE_OUTPUT_TOKENS,
|
|
419
|
-
completion_tokens,
|
|
420
|
-
)
|
|
421
|
-
set_span_attribute(
|
|
422
|
-
span,
|
|
423
|
-
AttributeKeys.GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS,
|
|
424
|
-
cache_read,
|
|
425
|
-
)
|
|
426
|
-
set_span_attribute(
|
|
427
|
-
span,
|
|
428
|
-
AttributeKeys.JUDGMENT_USAGE_METADATA,
|
|
429
|
-
safe_serialize(usage_data),
|
|
430
|
-
)
|
|
431
|
-
set_span_attribute(
|
|
432
|
-
span,
|
|
433
|
-
AttributeKeys.GEN_AI_RESPONSE_MODEL,
|
|
434
|
-
getattr(response, "model_version", model_name),
|
|
435
|
-
)
|
|
436
|
-
except Exception as e:
|
|
437
|
-
judgeval_logger.error(
|
|
438
|
-
f"[google wrapped_async] Error adding span metadata: {e}"
|
|
439
|
-
)
|
|
440
|
-
finally:
|
|
441
|
-
return response
|
|
442
|
-
|
|
443
|
-
return wrapper
|
|
444
|
-
|
|
445
|
-
span_name = "GOOGLE_API_CALL"
|
|
446
|
-
if google_genai_Client is not None and isinstance(client, google_genai_Client):
|
|
447
|
-
# Type narrowing for mypy
|
|
448
|
-
google_client = client # type: ignore[assignment]
|
|
449
|
-
setattr(
|
|
450
|
-
google_client.models,
|
|
451
|
-
"generate_content",
|
|
452
|
-
wrapped(google_client.models.generate_content, span_name),
|
|
453
|
-
)
|
|
454
|
-
elif google_genai_AsyncClient is not None and isinstance(
|
|
455
|
-
client, google_genai_AsyncClient
|
|
456
|
-
):
|
|
457
|
-
# Type narrowing for mypy
|
|
458
|
-
async_google_client = client # type: ignore[assignment]
|
|
459
|
-
setattr(
|
|
460
|
-
async_google_client.models,
|
|
461
|
-
"generate_content",
|
|
462
|
-
wrapped_async(async_google_client.models.generate_content, span_name),
|
|
17
|
+
if not HAS_GOOGLE_GENAI:
|
|
18
|
+
judgeval_logger.error(
|
|
19
|
+
"Cannot wrap Google GenAI client: 'google-genai' library not installed. "
|
|
20
|
+
"Install it with: pip install google-genai"
|
|
463
21
|
)
|
|
22
|
+
return client
|
|
23
|
+
|
|
24
|
+
from google.genai import Client
|
|
464
25
|
|
|
465
|
-
|
|
26
|
+
if isinstance(client, Client):
|
|
27
|
+
wrap_generate_content_sync(tracer, client)
|
|
28
|
+
return client
|
|
29
|
+
else:
|
|
30
|
+
raise TypeError(f"Invalid client type: {type(client)}")
|