k-eval 0.3.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.
- k_eval/__init__.py +0 -0
- k_eval/agent/__init__.py +0 -0
- k_eval/agent/domain/__init__.py +0 -0
- k_eval/agent/domain/agent.py +14 -0
- k_eval/agent/domain/factory.py +18 -0
- k_eval/agent/domain/observer.py +27 -0
- k_eval/agent/domain/result.py +18 -0
- k_eval/agent/domain/usage.py +12 -0
- k_eval/agent/infrastructure/__init__.py +0 -0
- k_eval/agent/infrastructure/claude_sdk.py +222 -0
- k_eval/agent/infrastructure/errors.py +19 -0
- k_eval/agent/infrastructure/factory.py +32 -0
- k_eval/agent/infrastructure/observer.py +50 -0
- k_eval/agent/infrastructure/registry.py +21 -0
- k_eval/cli/__init__.py +0 -0
- k_eval/cli/main.py +505 -0
- k_eval/cli/output/__init__.py +0 -0
- k_eval/cli/output/aggregator.py +89 -0
- k_eval/cli/output/eee.py +224 -0
- k_eval/config/__init__.py +0 -0
- k_eval/config/domain/__init__.py +0 -0
- k_eval/config/domain/agent.py +8 -0
- k_eval/config/domain/condition.py +12 -0
- k_eval/config/domain/condition_mcp_server.py +13 -0
- k_eval/config/domain/config.py +26 -0
- k_eval/config/domain/dataset.py +11 -0
- k_eval/config/domain/execution.py +15 -0
- k_eval/config/domain/judge.py +8 -0
- k_eval/config/domain/mcp_server.py +39 -0
- k_eval/config/domain/observer.py +9 -0
- k_eval/config/infrastructure/__init__.py +0 -0
- k_eval/config/infrastructure/env_interpolation.py +50 -0
- k_eval/config/infrastructure/errors.py +37 -0
- k_eval/config/infrastructure/observer.py +23 -0
- k_eval/config/infrastructure/yaml_loader.py +120 -0
- k_eval/core/__init__.py +0 -0
- k_eval/core/errors.py +9 -0
- k_eval/dataset/__init__.py +0 -0
- k_eval/dataset/domain/__init__.py +0 -0
- k_eval/dataset/domain/load_result.py +16 -0
- k_eval/dataset/domain/loader.py +12 -0
- k_eval/dataset/domain/observer.py +15 -0
- k_eval/dataset/domain/sample.py +13 -0
- k_eval/dataset/infrastructure/__init__.py +0 -0
- k_eval/dataset/infrastructure/errors.py +10 -0
- k_eval/dataset/infrastructure/jsonl_loader.py +130 -0
- k_eval/dataset/infrastructure/observer.py +36 -0
- k_eval/evaluation/__init__.py +0 -0
- k_eval/evaluation/application/__init__.py +0 -0
- k_eval/evaluation/application/runner.py +232 -0
- k_eval/evaluation/domain/__init__.py +0 -0
- k_eval/evaluation/domain/observer.py +71 -0
- k_eval/evaluation/domain/run.py +20 -0
- k_eval/evaluation/domain/summary.py +18 -0
- k_eval/evaluation/infrastructure/__init__.py +0 -0
- k_eval/evaluation/infrastructure/composite_observer.py +128 -0
- k_eval/evaluation/infrastructure/observer.py +129 -0
- k_eval/evaluation/infrastructure/progress_observer.py +370 -0
- k_eval/judge/__init__.py +0 -0
- k_eval/judge/domain/__init__.py +0 -0
- k_eval/judge/domain/factory.py +15 -0
- k_eval/judge/domain/judge.py +16 -0
- k_eval/judge/domain/observer.py +26 -0
- k_eval/judge/domain/score.py +21 -0
- k_eval/judge/infrastructure/__init__.py +0 -0
- k_eval/judge/infrastructure/errors.py +10 -0
- k_eval/judge/infrastructure/factory.py +30 -0
- k_eval/judge/infrastructure/litellm.py +205 -0
- k_eval/judge/infrastructure/observer.py +53 -0
- k_eval-0.3.0.dist-info/METADATA +188 -0
- k_eval-0.3.0.dist-info/RECORD +74 -0
- k_eval-0.3.0.dist-info/WHEEL +5 -0
- k_eval-0.3.0.dist-info/entry_points.txt +2 -0
- k_eval-0.3.0.dist-info/top_level.txt +1 -0
k_eval/__init__.py
ADDED
|
File without changes
|
k_eval/agent/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Agent Protocol — structural interface for all agent implementations."""
|
|
2
|
+
|
|
3
|
+
from typing import Protocol
|
|
4
|
+
|
|
5
|
+
from k_eval.agent.domain.result import AgentResult
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Agent(Protocol):
|
|
9
|
+
"""Structural interface satisfied by any agent implementation.
|
|
10
|
+
|
|
11
|
+
Each instance is constructed once per (condition, sample) evaluation.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
async def ask(self, question: str) -> AgentResult: ...
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""AgentFactory Protocol — structural interface for constructing Agent instances."""
|
|
2
|
+
|
|
3
|
+
from typing import Protocol
|
|
4
|
+
|
|
5
|
+
from k_eval.agent.domain.agent import Agent
|
|
6
|
+
from k_eval.config.domain.condition_mcp_server import ConditionMcpServer
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AgentFactory(Protocol):
|
|
10
|
+
"""Constructs a new Agent instance for a given (condition, sample) pair."""
|
|
11
|
+
|
|
12
|
+
def create(
|
|
13
|
+
self,
|
|
14
|
+
condition: str,
|
|
15
|
+
sample_idx: str,
|
|
16
|
+
system_prompt: str,
|
|
17
|
+
mcp_servers: list[ConditionMcpServer],
|
|
18
|
+
) -> Agent: ...
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""AgentObserver port — domain events emitted during agent invocations."""
|
|
2
|
+
|
|
3
|
+
from typing import Protocol
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AgentObserver(Protocol):
|
|
7
|
+
"""Observer port for agent domain events.
|
|
8
|
+
|
|
9
|
+
Implementations may log to structlog, record for tests, or emit metrics.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def agent_invocation_started(
|
|
13
|
+
self, condition: str, sample_idx: str, model: str
|
|
14
|
+
) -> None: ...
|
|
15
|
+
|
|
16
|
+
def agent_invocation_completed(
|
|
17
|
+
self,
|
|
18
|
+
condition: str,
|
|
19
|
+
sample_idx: str,
|
|
20
|
+
duration_ms: int,
|
|
21
|
+
num_turns: int,
|
|
22
|
+
cost_usd: float | None,
|
|
23
|
+
) -> None: ...
|
|
24
|
+
|
|
25
|
+
def agent_invocation_failed(
|
|
26
|
+
self, condition: str, sample_idx: str, reason: str
|
|
27
|
+
) -> None: ...
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""AgentResult value object — the outcome of a single agent invocation."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict
|
|
4
|
+
|
|
5
|
+
from k_eval.agent.domain.usage import UsageMetrics
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AgentResult(BaseModel, frozen=True):
|
|
9
|
+
"""Immutable value object capturing all outcome data from one agent invocation."""
|
|
10
|
+
|
|
11
|
+
model_config = ConfigDict(frozen=True)
|
|
12
|
+
|
|
13
|
+
response: str
|
|
14
|
+
cost_usd: float | None
|
|
15
|
+
duration_ms: int
|
|
16
|
+
duration_api_ms: int
|
|
17
|
+
num_turns: int
|
|
18
|
+
usage: UsageMetrics | None
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""UsageMetrics value object — token usage from an agent invocation."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class UsageMetrics(BaseModel, frozen=True):
|
|
7
|
+
"""Immutable value object capturing token usage from a single agent invocation."""
|
|
8
|
+
|
|
9
|
+
model_config = ConfigDict(frozen=True)
|
|
10
|
+
|
|
11
|
+
input_tokens: int | None
|
|
12
|
+
output_tokens: int | None
|
|
File without changes
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"""ClaudeAgentSDKAgent — agent implementation using the Claude Agent SDK."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from claude_agent_sdk import query
|
|
6
|
+
from claude_agent_sdk._errors import ClaudeSDKError
|
|
7
|
+
from claude_agent_sdk.types import (
|
|
8
|
+
ClaudeAgentOptions,
|
|
9
|
+
McpHttpServerConfig,
|
|
10
|
+
McpSdkServerConfig,
|
|
11
|
+
McpSSEServerConfig,
|
|
12
|
+
McpStdioServerConfig,
|
|
13
|
+
ResultMessage,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from k_eval.agent.domain.observer import AgentObserver
|
|
17
|
+
from k_eval.agent.domain.result import AgentResult
|
|
18
|
+
from k_eval.agent.domain.usage import UsageMetrics
|
|
19
|
+
from k_eval.agent.infrastructure.errors import AgentInvocationError
|
|
20
|
+
from k_eval.config.domain.agent import AgentConfig
|
|
21
|
+
from k_eval.config.domain.condition_mcp_server import ConditionMcpServer
|
|
22
|
+
from k_eval.config.domain.mcp_server import HttpMcpServer, SseMcpServer, StdioMcpServer
|
|
23
|
+
|
|
24
|
+
type McpServerConfigMap = dict[
|
|
25
|
+
str,
|
|
26
|
+
McpStdioServerConfig
|
|
27
|
+
| McpSSEServerConfig
|
|
28
|
+
| McpHttpServerConfig
|
|
29
|
+
| McpSdkServerConfig,
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ClaudeAgentSDKAgent:
|
|
34
|
+
"""Agent implementation that delegates to the Claude Agent SDK.
|
|
35
|
+
|
|
36
|
+
One instance is constructed per (condition, sample) evaluation run.
|
|
37
|
+
The condition and sample_idx are injected at construction time so that
|
|
38
|
+
observer events carry full context without polluting the ask() signature.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
config: AgentConfig,
|
|
44
|
+
condition: str,
|
|
45
|
+
sample_idx: str,
|
|
46
|
+
system_prompt: str,
|
|
47
|
+
mcp_servers: list[ConditionMcpServer],
|
|
48
|
+
observer: AgentObserver,
|
|
49
|
+
) -> None:
|
|
50
|
+
self._config = config
|
|
51
|
+
self._condition = condition
|
|
52
|
+
self._sample_idx = sample_idx
|
|
53
|
+
self._system_prompt = system_prompt
|
|
54
|
+
self._mcp_servers = mcp_servers
|
|
55
|
+
self._observer = observer
|
|
56
|
+
|
|
57
|
+
async def ask(self, question: str) -> AgentResult:
|
|
58
|
+
"""Invoke the agent with a question and return the structured result.
|
|
59
|
+
|
|
60
|
+
Opens a new SDK session per call — correct for independent eval samples.
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
AgentInvocationError: if the SDK raises, the agent returns an error,
|
|
64
|
+
or no ResultMessage is present in the response stream.
|
|
65
|
+
"""
|
|
66
|
+
self._observer.agent_invocation_started(
|
|
67
|
+
condition=self._condition,
|
|
68
|
+
sample_idx=self._sample_idx,
|
|
69
|
+
model=self._config.model,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
options = ClaudeAgentOptions(
|
|
74
|
+
model=self._config.model,
|
|
75
|
+
system_prompt=self._system_prompt,
|
|
76
|
+
mcp_servers=self._build_mcp_servers(),
|
|
77
|
+
disallowed_tools=self._build_disallowed_tools(),
|
|
78
|
+
permission_mode="bypassPermissions",
|
|
79
|
+
setting_sources=[],
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
result_message = await self._collect_result(
|
|
83
|
+
prompt=question, options=options
|
|
84
|
+
)
|
|
85
|
+
except AgentInvocationError as exc:
|
|
86
|
+
reason = str(exc).removeprefix("Failed to invoke agent: ")
|
|
87
|
+
self._observer.agent_invocation_failed(
|
|
88
|
+
condition=self._condition,
|
|
89
|
+
sample_idx=self._sample_idx,
|
|
90
|
+
reason=reason,
|
|
91
|
+
)
|
|
92
|
+
raise
|
|
93
|
+
|
|
94
|
+
self._observer.agent_invocation_completed(
|
|
95
|
+
condition=self._condition,
|
|
96
|
+
sample_idx=self._sample_idx,
|
|
97
|
+
duration_ms=result_message.duration_ms,
|
|
98
|
+
num_turns=result_message.num_turns,
|
|
99
|
+
cost_usd=result_message.total_cost_usd,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
assert result_message.result is not None # guaranteed by _collect_result
|
|
103
|
+
return AgentResult(
|
|
104
|
+
response=result_message.result,
|
|
105
|
+
cost_usd=result_message.total_cost_usd,
|
|
106
|
+
duration_ms=result_message.duration_ms,
|
|
107
|
+
duration_api_ms=result_message.duration_api_ms,
|
|
108
|
+
num_turns=result_message.num_turns,
|
|
109
|
+
usage=self._map_usage(raw=result_message.usage),
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
async def _collect_result(
|
|
113
|
+
self, prompt: str, options: ClaudeAgentOptions
|
|
114
|
+
) -> ResultMessage:
|
|
115
|
+
"""Run the SDK query and extract the single ResultMessage.
|
|
116
|
+
|
|
117
|
+
Raises:
|
|
118
|
+
AgentInvocationError: on SDK errors or missing/error ResultMessage.
|
|
119
|
+
"""
|
|
120
|
+
result_message: ResultMessage | None = None
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
async for message in query(prompt=prompt, options=options):
|
|
124
|
+
if isinstance(message, ResultMessage):
|
|
125
|
+
result_message = message
|
|
126
|
+
except ClaudeSDKError as exc:
|
|
127
|
+
raise AgentInvocationError(reason=str(exc), retriable=True) from exc
|
|
128
|
+
except Exception as exc:
|
|
129
|
+
# The SDK internally raises a bare Exception (not ClaudeSDKError) when
|
|
130
|
+
# its message reader encounters a fatal error (e.g. subprocess exit).
|
|
131
|
+
raise AgentInvocationError(reason=str(exc), retriable=True) from exc
|
|
132
|
+
|
|
133
|
+
if result_message is None:
|
|
134
|
+
raise AgentInvocationError(reason="no ResultMessage in response stream")
|
|
135
|
+
|
|
136
|
+
if result_message.is_error:
|
|
137
|
+
raise AgentInvocationError(
|
|
138
|
+
reason=f"agent returned error response: {result_message.result}"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if result_message.result is None:
|
|
142
|
+
raise AgentInvocationError(reason="ResultMessage has no result text")
|
|
143
|
+
|
|
144
|
+
return result_message
|
|
145
|
+
|
|
146
|
+
def _build_mcp_servers(self) -> McpServerConfigMap:
|
|
147
|
+
"""Convert ConditionMcpServer list to the SDK's TypedDict format."""
|
|
148
|
+
servers: McpServerConfigMap = {}
|
|
149
|
+
|
|
150
|
+
for server in self._mcp_servers:
|
|
151
|
+
config = server.config
|
|
152
|
+
|
|
153
|
+
if isinstance(config, StdioMcpServer):
|
|
154
|
+
servers[server.name] = self._build_stdio_server(config=config)
|
|
155
|
+
elif isinstance(config, SseMcpServer):
|
|
156
|
+
servers[server.name] = self._build_sse_server(config=config)
|
|
157
|
+
elif isinstance(config, HttpMcpServer):
|
|
158
|
+
servers[server.name] = self._build_http_server(config=config)
|
|
159
|
+
else:
|
|
160
|
+
raise AgentInvocationError(
|
|
161
|
+
reason=f"unsupported MCP server type for server '{server.name}'"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
return servers
|
|
165
|
+
|
|
166
|
+
def _build_stdio_server(self, config: StdioMcpServer) -> McpStdioServerConfig:
|
|
167
|
+
"""Build a McpStdioServerConfig TypedDict from a StdioMcpServer model."""
|
|
168
|
+
server: McpStdioServerConfig = McpStdioServerConfig(command=config.command)
|
|
169
|
+
if config.args:
|
|
170
|
+
server["args"] = list(config.args)
|
|
171
|
+
if config.env:
|
|
172
|
+
server["env"] = dict(config.env)
|
|
173
|
+
return server
|
|
174
|
+
|
|
175
|
+
def _build_sse_server(self, config: SseMcpServer) -> McpSSEServerConfig:
|
|
176
|
+
"""Build a McpSSEServerConfig TypedDict from a SseMcpServer model."""
|
|
177
|
+
server: McpSSEServerConfig = McpSSEServerConfig(type="sse", url=config.url)
|
|
178
|
+
if config.headers:
|
|
179
|
+
server["headers"] = dict(config.headers)
|
|
180
|
+
return server
|
|
181
|
+
|
|
182
|
+
def _build_http_server(self, config: HttpMcpServer) -> McpHttpServerConfig:
|
|
183
|
+
"""Build a McpHttpServerConfig TypedDict from an HttpMcpServer model."""
|
|
184
|
+
server: McpHttpServerConfig = McpHttpServerConfig(type="http", url=config.url)
|
|
185
|
+
if config.headers:
|
|
186
|
+
server["headers"] = dict(config.headers)
|
|
187
|
+
return server
|
|
188
|
+
|
|
189
|
+
def _build_disallowed_tools(self) -> list[str]:
|
|
190
|
+
"""Build the disallowed tools list — all Claude built-in tools.
|
|
191
|
+
|
|
192
|
+
allowed_tools alone does not remove built-in tools from the agent's
|
|
193
|
+
context; it only controls approval requirements. Explicitly disallowing
|
|
194
|
+
all built-in tools ensures the agent cannot use web search, file I/O,
|
|
195
|
+
or any other built-in capability regardless of permission_mode.
|
|
196
|
+
"""
|
|
197
|
+
return [
|
|
198
|
+
"Bash",
|
|
199
|
+
"Edit",
|
|
200
|
+
"Glob",
|
|
201
|
+
"Grep",
|
|
202
|
+
"LS",
|
|
203
|
+
"MultiEdit",
|
|
204
|
+
"NotebookEdit",
|
|
205
|
+
"NotebookRead",
|
|
206
|
+
"Read",
|
|
207
|
+
"Task",
|
|
208
|
+
"TodoRead",
|
|
209
|
+
"TodoWrite",
|
|
210
|
+
"WebFetch",
|
|
211
|
+
"WebSearch",
|
|
212
|
+
"Write",
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
def _map_usage(self, raw: dict[str, Any] | None) -> UsageMetrics | None:
|
|
216
|
+
"""Map the SDK's raw usage dict to a typed UsageMetrics value object."""
|
|
217
|
+
if raw is None:
|
|
218
|
+
return None
|
|
219
|
+
return UsageMetrics(
|
|
220
|
+
input_tokens=raw.get("input_tokens"),
|
|
221
|
+
output_tokens=raw.get("output_tokens"),
|
|
222
|
+
)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Error types raised by agent infrastructure."""
|
|
2
|
+
|
|
3
|
+
from k_eval.core.errors import KEvalError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AgentInvocationError(KEvalError):
|
|
7
|
+
"""Raised when the agent cannot be invoked or returns an error response."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, reason: str, retriable: bool = False) -> None:
|
|
10
|
+
super().__init__(f"Failed to invoke agent: {reason}", retriable=retriable)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AgentTypeNotSupportedError(KEvalError):
|
|
14
|
+
"""Raised when the agent type specified in config is not a known agent type."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, agent_type: str) -> None:
|
|
17
|
+
super().__init__(
|
|
18
|
+
f"Failed to create agent factory: unsupported agent type '{agent_type}'"
|
|
19
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""ClaudeAgentSDKAgentFactory — constructs ClaudeAgentSDKAgent instances."""
|
|
2
|
+
|
|
3
|
+
from k_eval.agent.domain.agent import Agent
|
|
4
|
+
from k_eval.agent.domain.observer import AgentObserver
|
|
5
|
+
from k_eval.agent.infrastructure.claude_sdk import ClaudeAgentSDKAgent
|
|
6
|
+
from k_eval.config.domain.agent import AgentConfig
|
|
7
|
+
from k_eval.config.domain.condition_mcp_server import ConditionMcpServer
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ClaudeAgentSDKAgentFactory:
|
|
11
|
+
"""Creates ClaudeAgentSDKAgent instances configured for a given condition and sample."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, config: AgentConfig, observer: AgentObserver) -> None:
|
|
14
|
+
self._config = config
|
|
15
|
+
self._observer = observer
|
|
16
|
+
|
|
17
|
+
def create(
|
|
18
|
+
self,
|
|
19
|
+
condition: str,
|
|
20
|
+
sample_idx: str,
|
|
21
|
+
system_prompt: str,
|
|
22
|
+
mcp_servers: list[ConditionMcpServer],
|
|
23
|
+
) -> Agent:
|
|
24
|
+
"""Construct a new ClaudeAgentSDKAgent for the given condition and sample."""
|
|
25
|
+
return ClaudeAgentSDKAgent(
|
|
26
|
+
config=self._config,
|
|
27
|
+
condition=condition,
|
|
28
|
+
sample_idx=sample_idx,
|
|
29
|
+
system_prompt=system_prompt,
|
|
30
|
+
mcp_servers=mcp_servers,
|
|
31
|
+
observer=self._observer,
|
|
32
|
+
)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Structlog implementation of the AgentObserver port."""
|
|
2
|
+
|
|
3
|
+
import structlog
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class StructlogAgentObserver:
|
|
7
|
+
"""Delegates agent domain events to structlog.
|
|
8
|
+
|
|
9
|
+
Satisfies the AgentObserver protocol structurally.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
self._log = structlog.get_logger()
|
|
14
|
+
|
|
15
|
+
def agent_invocation_started(
|
|
16
|
+
self, condition: str, sample_idx: str, model: str
|
|
17
|
+
) -> None:
|
|
18
|
+
self._log.info(
|
|
19
|
+
"agent.invocation_started",
|
|
20
|
+
condition=condition,
|
|
21
|
+
sample_idx=sample_idx,
|
|
22
|
+
model=model,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
def agent_invocation_completed(
|
|
26
|
+
self,
|
|
27
|
+
condition: str,
|
|
28
|
+
sample_idx: str,
|
|
29
|
+
duration_ms: int,
|
|
30
|
+
num_turns: int,
|
|
31
|
+
cost_usd: float | None,
|
|
32
|
+
) -> None:
|
|
33
|
+
self._log.info(
|
|
34
|
+
"agent.invocation_completed",
|
|
35
|
+
condition=condition,
|
|
36
|
+
sample_idx=sample_idx,
|
|
37
|
+
duration_ms=duration_ms,
|
|
38
|
+
num_turns=num_turns,
|
|
39
|
+
cost_usd=cost_usd,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def agent_invocation_failed(
|
|
43
|
+
self, condition: str, sample_idx: str, reason: str
|
|
44
|
+
) -> None:
|
|
45
|
+
self._log.error(
|
|
46
|
+
"agent.invocation_failed",
|
|
47
|
+
condition=condition,
|
|
48
|
+
sample_idx=sample_idx,
|
|
49
|
+
reason=reason,
|
|
50
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""AgentFactoryRegistry — maps AgentConfig.type to the correct AgentFactory."""
|
|
2
|
+
|
|
3
|
+
from k_eval.agent.domain.factory import AgentFactory
|
|
4
|
+
from k_eval.agent.domain.observer import AgentObserver
|
|
5
|
+
from k_eval.agent.infrastructure.errors import AgentTypeNotSupportedError
|
|
6
|
+
from k_eval.agent.infrastructure.factory import ClaudeAgentSDKAgentFactory
|
|
7
|
+
from k_eval.config.domain.agent import AgentConfig
|
|
8
|
+
|
|
9
|
+
_SUPPORTED_TYPE = "claude_code_sdk"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_agent_factory(config: AgentConfig, observer: AgentObserver) -> AgentFactory:
|
|
13
|
+
"""Return the appropriate AgentFactory for the given AgentConfig.
|
|
14
|
+
|
|
15
|
+
Raises:
|
|
16
|
+
AgentTypeNotSupportedError: if config.type is not a known agent type.
|
|
17
|
+
"""
|
|
18
|
+
if config.type == _SUPPORTED_TYPE:
|
|
19
|
+
return ClaudeAgentSDKAgentFactory(config=config, observer=observer)
|
|
20
|
+
|
|
21
|
+
raise AgentTypeNotSupportedError(agent_type=config.type)
|
k_eval/cli/__init__.py
ADDED
|
File without changes
|