Skip to content

Commit 7bab34e

Browse files
authored
JUD-2088: Claude agent sdk (#629)
* Claude Agent SDK * CI Fix * test fix * comments * fix test import * update * fix * clean docstring * resolve comments
1 parent 4633638 commit 7bab34e

File tree

10 files changed

+4607
-3329
lines changed

10 files changed

+4607
-3329
lines changed

.github/workflows/ci.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ jobs:
4747
pip install uv
4848
uv sync --dev
4949
50+
- name: Install Claude Code CLI
51+
run: |
52+
npm install -g @anthropic-ai/claude-code
53+
5054
- name: Run tests
5155
run: |
5256
cd src

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ dev = [
8181
"openlit>=1.35.5",
8282
"dotenv>=0.9.9",
8383
"datasets>=4.4.1",
84+
"claude-agent-sdk>=0.1.6",
8485
]
8586

8687

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
from __future__ import annotations
2+
from typing import TYPE_CHECKING
3+
import sys
4+
from judgeval.logger import judgeval_logger
5+
6+
if TYPE_CHECKING:
7+
from judgeval.v1.tracer.base_tracer import BaseTracer
8+
9+
__all__ = ["setup_claude_agent_sdk"]
10+
11+
try:
12+
import claude_agent_sdk # type: ignore
13+
except ImportError:
14+
raise ImportError(
15+
"Claude Agent SDK is not installed and required for the claude agent sdk integration. Please install it with `pip install claude-agent-sdk`."
16+
)
17+
18+
19+
def setup_claude_agent_sdk(
20+
tracer: "BaseTracer",
21+
) -> bool:
22+
"""
23+
Setup Judgeval integration with Claude Agent SDK. Will automatically patch the SDK for automatic tracing.
24+
25+
Args:
26+
tracer: Judgeval Tracer instance
27+
28+
Returns:
29+
bool: True if setup was successful, False otherwise.
30+
31+
Example:
32+
```python
33+
import claude_agent_sdk
34+
from judgeval.v1.integrations.claude_agent_sdk import setup_claude_agent_sdk
35+
36+
tracer = Tracer(project_name="my-project")
37+
setup_claude_agent_sdk(tracer=tracer)
38+
39+
# Now use claude_agent_sdk normally - all calls automatically traced
40+
```
41+
"""
42+
from judgeval.v1.integrations.claude_agent_sdk.wrapper import (
43+
_create_client_wrapper_class,
44+
_create_tool_wrapper_class,
45+
_wrap_tool_factory,
46+
_wrap_query_function,
47+
)
48+
49+
try:
50+
# Store original classes before patching
51+
original_client = (
52+
claude_agent_sdk.ClaudeSDKClient
53+
if hasattr(claude_agent_sdk, "ClaudeSDKClient")
54+
else None
55+
)
56+
original_tool_class = (
57+
claude_agent_sdk.SdkMcpTool
58+
if hasattr(claude_agent_sdk, "SdkMcpTool")
59+
else None
60+
)
61+
original_tool_fn = (
62+
claude_agent_sdk.tool if hasattr(claude_agent_sdk, "tool") else None
63+
)
64+
original_query_fn = (
65+
claude_agent_sdk.query if hasattr(claude_agent_sdk, "query") else None
66+
)
67+
68+
# Patch ClaudeSDKClient
69+
if original_client:
70+
wrapped_client = _create_client_wrapper_class(original_client, tracer)
71+
claude_agent_sdk.ClaudeSDKClient = wrapped_client # type: ignore
72+
73+
# Update all modules that already imported ClaudeSDKClient
74+
for module in list(sys.modules.values()):
75+
if module and hasattr(module, "ClaudeSDKClient"):
76+
if getattr(module, "ClaudeSDKClient", None) is original_client:
77+
setattr(module, "ClaudeSDKClient", wrapped_client)
78+
79+
# Patch SdkMcpTool
80+
if original_tool_class:
81+
wrapped_tool_class = _create_tool_wrapper_class(original_tool_class, tracer)
82+
claude_agent_sdk.SdkMcpTool = wrapped_tool_class # type: ignore
83+
84+
# Update all modules that already imported SdkMcpTool
85+
for module in list(sys.modules.values()):
86+
if module and hasattr(module, "SdkMcpTool"):
87+
if getattr(module, "SdkMcpTool", None) is original_tool_class:
88+
setattr(module, "SdkMcpTool", wrapped_tool_class)
89+
90+
# Patch tool() decorator
91+
if original_tool_fn:
92+
wrapped_tool_fn = _wrap_tool_factory(original_tool_fn, tracer)
93+
claude_agent_sdk.tool = wrapped_tool_fn # type: ignore
94+
95+
# Update all modules that already imported tool
96+
for module in list(sys.modules.values()):
97+
if module and hasattr(module, "tool"):
98+
if getattr(module, "tool", None) is original_tool_fn:
99+
setattr(module, "tool", wrapped_tool_fn)
100+
101+
# Patch standalone query() function if it exists
102+
# Note: The standalone query() uses InternalClient, not ClaudeSDKClient,
103+
# so we need to wrap it separately to add tracing
104+
if original_query_fn:
105+
wrapped_query_fn = _wrap_query_function(original_query_fn, tracer)
106+
claude_agent_sdk.query = wrapped_query_fn # type: ignore
107+
108+
# Update all modules that already imported query
109+
for module in list(sys.modules.values()):
110+
if module and hasattr(module, "query"):
111+
if getattr(module, "query", None) is original_query_fn:
112+
setattr(module, "query", wrapped_query_fn)
113+
114+
judgeval_logger.info("Claude Agent SDK integration setup successful")
115+
return True
116+
117+
except Exception as e:
118+
judgeval_logger.error(f"Failed to setup Claude Agent SDK integration: {e}")
119+
return False

0 commit comments

Comments
 (0)