coder-eval 0.8.2__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.
- coder_eval/.gitattributes +2 -0
- coder_eval/__init__.py +3 -0
- coder_eval/agent.py +302 -0
- coder_eval/agents/__init__.py +41 -0
- coder_eval/agents/_logging.py +89 -0
- coder_eval/agents/antigravity_agent.py +811 -0
- coder_eval/agents/claude_code_agent.py +1701 -0
- coder_eval/agents/codex_agent.py +2055 -0
- coder_eval/agents/noop_agent.py +100 -0
- coder_eval/agents/registry.py +163 -0
- coder_eval/agents/watchdog.py +116 -0
- coder_eval/analysis.py +88 -0
- coder_eval/cli/__init__.py +87 -0
- coder_eval/cli/aggregate_command.py +153 -0
- coder_eval/cli/console.py +7 -0
- coder_eval/cli/evaluate_command.py +164 -0
- coder_eval/cli/plan_command.py +152 -0
- coder_eval/cli/report_command.py +121 -0
- coder_eval/cli/run_command.py +686 -0
- coder_eval/cli/run_helpers.py +137 -0
- coder_eval/cli/run_task_internal_command.py +210 -0
- coder_eval/cli/utils.py +37 -0
- coder_eval/config.py +212 -0
- coder_eval/criteria/__init__.py +163 -0
- coder_eval/criteria/_classification_aggregate.py +106 -0
- coder_eval/criteria/agent_judge.py +425 -0
- coder_eval/criteria/base.py +248 -0
- coder_eval/criteria/classification_match.py +107 -0
- coder_eval/criteria/command_executed.py +181 -0
- coder_eval/criteria/commands_efficiency.py +73 -0
- coder_eval/criteria/file_check.py +119 -0
- coder_eval/criteria/file_contains.py +85 -0
- coder_eval/criteria/file_exists.py +45 -0
- coder_eval/criteria/file_matches_regex.py +86 -0
- coder_eval/criteria/json_check.py +184 -0
- coder_eval/criteria/llm_judge.py +260 -0
- coder_eval/criteria/reference_comparison.py +130 -0
- coder_eval/criteria/run_command.py +220 -0
- coder_eval/criteria/skill_triggered.py +111 -0
- coder_eval/criteria/uipath_eval.py +223 -0
- coder_eval/errors/__init__.py +26 -0
- coder_eval/errors/agent.py +26 -0
- coder_eval/errors/budget.py +28 -0
- coder_eval/errors/categories.py +205 -0
- coder_eval/errors/categorization.py +240 -0
- coder_eval/errors/executor.py +124 -0
- coder_eval/errors/judge.py +12 -0
- coder_eval/errors/retry.py +211 -0
- coder_eval/errors/timeout.py +63 -0
- coder_eval/evaluation/__init__.py +10 -0
- coder_eval/evaluation/checker.py +260 -0
- coder_eval/evaluation/judge_anthropic.py +55 -0
- coder_eval/evaluation/judge_bedrock.py +114 -0
- coder_eval/evaluation/judge_context.py +497 -0
- coder_eval/evaluation/judge_models.py +53 -0
- coder_eval/evaluation/judge_persistence.py +291 -0
- coder_eval/evaluation/judge_usage.py +50 -0
- coder_eval/evaluation/sub_agent.py +231 -0
- coder_eval/evaluation/summaries.py +48 -0
- coder_eval/evaluation/verdict_tool.py +213 -0
- coder_eval/formatting.py +191 -0
- coder_eval/isolation/__init__.py +15 -0
- coder_eval/isolation/docker_runner.py +1253 -0
- coder_eval/logging_config.py +399 -0
- coder_eval/models/__init__.py +337 -0
- coder_eval/models/agent_config.py +373 -0
- coder_eval/models/container_paths.py +26 -0
- coder_eval/models/criteria.py +942 -0
- coder_eval/models/enums.py +121 -0
- coder_eval/models/experiment.py +314 -0
- coder_eval/models/judge.py +90 -0
- coder_eval/models/judge_defaults.py +11 -0
- coder_eval/models/limits.py +89 -0
- coder_eval/models/merge_strategy.py +132 -0
- coder_eval/models/mutations.py +95 -0
- coder_eval/models/results.py +787 -0
- coder_eval/models/routing.py +160 -0
- coder_eval/models/sandbox.py +371 -0
- coder_eval/models/tasks.py +631 -0
- coder_eval/models/telemetry.py +454 -0
- coder_eval/models/templates.py +108 -0
- coder_eval/orchestration/__init__.py +13 -0
- coder_eval/orchestration/batch.py +690 -0
- coder_eval/orchestration/config.py +111 -0
- coder_eval/orchestration/config_merge.py +431 -0
- coder_eval/orchestration/evaluation.py +94 -0
- coder_eval/orchestration/experiment.py +837 -0
- coder_eval/orchestration/overrides.py +206 -0
- coder_eval/orchestration/task_loader.py +437 -0
- coder_eval/orchestrator.py +2078 -0
- coder_eval/path_utils.py +79 -0
- coder_eval/plugins.py +81 -0
- coder_eval/pricing.py +169 -0
- coder_eval/py.typed +0 -0
- coder_eval/reports.py +1066 -0
- coder_eval/reports_experiment.py +761 -0
- coder_eval/reports_html.py +1659 -0
- coder_eval/reports_stats.py +250 -0
- coder_eval/resources/__init__.py +137 -0
- coder_eval/resources/default_experiment.yaml +85 -0
- coder_eval/resources/default_ignore_patterns.yaml +60 -0
- coder_eval/resources/tags.yaml +55 -0
- coder_eval/sandbox.py +1127 -0
- coder_eval/scoring/__init__.py +11 -0
- coder_eval/scoring/ast_similarity.py +38 -0
- coder_eval/scoring/complexity.py +115 -0
- coder_eval/scoring/quality.py +154 -0
- coder_eval/scoring/signature_similarity.py +57 -0
- coder_eval/scoring/similarity.py +103 -0
- coder_eval/scoring/token_similarity.py +44 -0
- coder_eval/simulation/__init__.py +27 -0
- coder_eval/simulation/termination.py +88 -0
- coder_eval/simulation/user_simulator.py +324 -0
- coder_eval/streaming/__init__.py +44 -0
- coder_eval/streaming/callbacks.py +57 -0
- coder_eval/streaming/collector.py +193 -0
- coder_eval/streaming/events.py +198 -0
- coder_eval/streaming/renderers.py +248 -0
- coder_eval/streaming/wire.py +115 -0
- coder_eval/telemetry.py +407 -0
- coder_eval/utils.py +517 -0
- coder_eval-0.8.2.dist-info/METADATA +242 -0
- coder_eval-0.8.2.dist-info/RECORD +127 -0
- coder_eval-0.8.2.dist-info/WHEEL +4 -0
- coder_eval-0.8.2.dist-info/entry_points.txt +5 -0
- coder_eval-0.8.2.dist-info/licenses/LICENSE +201 -0
- coder_eval-0.8.2.dist-info/licenses/NOTICE +18 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"""Unified exports for all coder_eval data models.
|
|
2
|
+
|
|
3
|
+
All models can be imported from coder_eval.models regardless of
|
|
4
|
+
which submodule they're defined in.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# Agent config
|
|
8
|
+
from coder_eval.models.agent_config import (
|
|
9
|
+
AgentConfig,
|
|
10
|
+
AntigravityAgentConfig,
|
|
11
|
+
BaseAgentConfig,
|
|
12
|
+
ClaudeCodeAgentConfig,
|
|
13
|
+
CodexAgentConfig,
|
|
14
|
+
LocalPluginConfig,
|
|
15
|
+
NoneAgentConfig,
|
|
16
|
+
ResolvedAgentConfig,
|
|
17
|
+
parse_agent_config,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Container paths (leaf constants; re-exported so consumers obey CE001)
|
|
21
|
+
from coder_eval.models.container_paths import (
|
|
22
|
+
CONTAINER_INPUT_DIR,
|
|
23
|
+
CONTAINER_OUTPUT_DIR,
|
|
24
|
+
CONTAINER_TASK_DIR,
|
|
25
|
+
CONTAINER_WORK_DIR,
|
|
26
|
+
RESERVED_CONTAINER_DIRS,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# Enums
|
|
30
|
+
# Criteria
|
|
31
|
+
from coder_eval.models.criteria import (
|
|
32
|
+
AgentJudgeCriterion,
|
|
33
|
+
BaseSuccessCriterion,
|
|
34
|
+
ClassificationMatchCriterion,
|
|
35
|
+
CommandExecutedCriterion,
|
|
36
|
+
CommandsEfficiencyCriterion,
|
|
37
|
+
FileCheckCriterion,
|
|
38
|
+
FileContainsCriterion,
|
|
39
|
+
FileExistsCriterion,
|
|
40
|
+
FileMatchesRegexCriterion,
|
|
41
|
+
JMESPathAssertion,
|
|
42
|
+
JsonCheckCriterion,
|
|
43
|
+
LLMJudgeCriterion,
|
|
44
|
+
ReferenceComparisonCriterion,
|
|
45
|
+
RegexPattern,
|
|
46
|
+
RunCommandCriterion,
|
|
47
|
+
SkillTriggeredCriterion,
|
|
48
|
+
SuccessCriterion,
|
|
49
|
+
UiPathEvalCriterion,
|
|
50
|
+
)
|
|
51
|
+
from coder_eval.models.enums import (
|
|
52
|
+
AgentKind,
|
|
53
|
+
AgentState,
|
|
54
|
+
ApiBackend,
|
|
55
|
+
FinalStatus,
|
|
56
|
+
PermissionMode,
|
|
57
|
+
PreservationMode,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Experiment
|
|
61
|
+
from coder_eval.models.experiment import (
|
|
62
|
+
ExperimentDefaults,
|
|
63
|
+
ExperimentDefinition,
|
|
64
|
+
ExperimentResult,
|
|
65
|
+
ExperimentVariant,
|
|
66
|
+
ResolvedTask,
|
|
67
|
+
TaskExperimentSummary,
|
|
68
|
+
TaskResult,
|
|
69
|
+
VariantAggregate,
|
|
70
|
+
VariantResult,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Judge
|
|
74
|
+
from coder_eval.models.judge import JudgeVerdict
|
|
75
|
+
|
|
76
|
+
# Judge defaults
|
|
77
|
+
from coder_eval.models.judge_defaults import DEFAULT_JUDGE_MODEL
|
|
78
|
+
|
|
79
|
+
# Limits
|
|
80
|
+
from coder_eval.models.limits import RunLimits
|
|
81
|
+
|
|
82
|
+
# Merge strategy
|
|
83
|
+
from coder_eval.models.merge_strategy import (
|
|
84
|
+
APPEND_ORDER_KEY,
|
|
85
|
+
MERGE_STRATEGY_KEY,
|
|
86
|
+
AppendOrder,
|
|
87
|
+
MergeField,
|
|
88
|
+
MergeStrategy,
|
|
89
|
+
append_order_of,
|
|
90
|
+
classify_annotation,
|
|
91
|
+
merge_strategy_of,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Mutations
|
|
95
|
+
from coder_eval.models.mutations import (
|
|
96
|
+
PromptMutation,
|
|
97
|
+
PromptPrefix,
|
|
98
|
+
PromptReplace,
|
|
99
|
+
PromptSuffix,
|
|
100
|
+
PromptTemplate,
|
|
101
|
+
apply_prompt_mutations,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Results
|
|
105
|
+
from coder_eval.models.results import (
|
|
106
|
+
ClassificationCriterionResult,
|
|
107
|
+
ClassLabelStats,
|
|
108
|
+
ConfigLineageEntry,
|
|
109
|
+
ConfusionEntry,
|
|
110
|
+
CriterionAggregate,
|
|
111
|
+
CriterionResult,
|
|
112
|
+
CriterionResultUnion,
|
|
113
|
+
CriterionStats,
|
|
114
|
+
EvaluationResult,
|
|
115
|
+
FailedRowSummary,
|
|
116
|
+
JudgeCriterionResult,
|
|
117
|
+
JudgeTranscript,
|
|
118
|
+
JudgeTranscriptToolCall,
|
|
119
|
+
PostRunResult,
|
|
120
|
+
ResultSummary,
|
|
121
|
+
RunSummary,
|
|
122
|
+
SimulationTelemetry,
|
|
123
|
+
SkippedTask,
|
|
124
|
+
SuiteRollup,
|
|
125
|
+
TaskConfigRecord,
|
|
126
|
+
ThresholdCheck,
|
|
127
|
+
TurnRecord,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Routing
|
|
131
|
+
from coder_eval.models.routing import (
|
|
132
|
+
ROUTE_NAMES,
|
|
133
|
+
ApiRoute,
|
|
134
|
+
BedrockRoute,
|
|
135
|
+
DirectRoute,
|
|
136
|
+
JudgeTransport,
|
|
137
|
+
resolve_route,
|
|
138
|
+
to_bedrock_inference_profile,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Sandbox
|
|
142
|
+
from coder_eval.models.sandbox import (
|
|
143
|
+
DockerBuildConfig,
|
|
144
|
+
DockerDriverConfig,
|
|
145
|
+
NodeEnvConfig,
|
|
146
|
+
PythonEnvConfig,
|
|
147
|
+
ResourceLimits,
|
|
148
|
+
SandboxConfig,
|
|
149
|
+
validate_template_sources_list,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Tasks
|
|
153
|
+
from coder_eval.models.tasks import (
|
|
154
|
+
DEFAULT_SIMULATION_STOP_TOKEN,
|
|
155
|
+
CriteriaCheckTiming,
|
|
156
|
+
Dataset,
|
|
157
|
+
PostRunCommand,
|
|
158
|
+
PreRunCommand,
|
|
159
|
+
ReferenceSource,
|
|
160
|
+
SimulationConfig,
|
|
161
|
+
TaskDefinition,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Telemetry
|
|
165
|
+
from coder_eval.models.telemetry import (
|
|
166
|
+
AssistantMessage,
|
|
167
|
+
CommandStatistics,
|
|
168
|
+
CommandTelemetry,
|
|
169
|
+
ContentBlock,
|
|
170
|
+
ReconciliationMessage,
|
|
171
|
+
SlowestCommandInfo,
|
|
172
|
+
TokenUsage,
|
|
173
|
+
TranscriptMessage,
|
|
174
|
+
UserMessage,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# Templates
|
|
178
|
+
from coder_eval.models.templates import (
|
|
179
|
+
BaseTemplateSource,
|
|
180
|
+
RepoSource,
|
|
181
|
+
StarterFile,
|
|
182
|
+
StarterFilesSource,
|
|
183
|
+
TemplateDirSource,
|
|
184
|
+
TemplateSource,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
__all__ = [ # noqa: RUF022 - Keep grouped by category for readability
|
|
189
|
+
# Agent config
|
|
190
|
+
"AgentConfig",
|
|
191
|
+
"AntigravityAgentConfig",
|
|
192
|
+
"BaseAgentConfig",
|
|
193
|
+
"ClaudeCodeAgentConfig",
|
|
194
|
+
"CodexAgentConfig",
|
|
195
|
+
"LocalPluginConfig",
|
|
196
|
+
"NoneAgentConfig",
|
|
197
|
+
"ResolvedAgentConfig",
|
|
198
|
+
"parse_agent_config",
|
|
199
|
+
# Enums
|
|
200
|
+
"AgentKind",
|
|
201
|
+
"AgentState",
|
|
202
|
+
"ApiBackend",
|
|
203
|
+
"FinalStatus",
|
|
204
|
+
"PermissionMode",
|
|
205
|
+
"PreservationMode",
|
|
206
|
+
# Criteria
|
|
207
|
+
"BaseSuccessCriterion",
|
|
208
|
+
"ClassificationMatchCriterion",
|
|
209
|
+
"FileExistsCriterion",
|
|
210
|
+
"FileContainsCriterion",
|
|
211
|
+
"FileCheckCriterion",
|
|
212
|
+
"JMESPathAssertion",
|
|
213
|
+
"JsonCheckCriterion",
|
|
214
|
+
"RegexPattern",
|
|
215
|
+
"RunCommandCriterion",
|
|
216
|
+
"FileMatchesRegexCriterion",
|
|
217
|
+
"ReferenceComparisonCriterion",
|
|
218
|
+
"CommandExecutedCriterion",
|
|
219
|
+
"CommandsEfficiencyCriterion",
|
|
220
|
+
"UiPathEvalCriterion",
|
|
221
|
+
"LLMJudgeCriterion",
|
|
222
|
+
"AgentJudgeCriterion",
|
|
223
|
+
"SkillTriggeredCriterion",
|
|
224
|
+
"SuccessCriterion",
|
|
225
|
+
# Routing
|
|
226
|
+
"ROUTE_NAMES",
|
|
227
|
+
"ApiRoute",
|
|
228
|
+
"DirectRoute",
|
|
229
|
+
"BedrockRoute",
|
|
230
|
+
"JudgeTransport",
|
|
231
|
+
"resolve_route",
|
|
232
|
+
"to_bedrock_inference_profile",
|
|
233
|
+
# Templates
|
|
234
|
+
"BaseTemplateSource",
|
|
235
|
+
"RepoSource",
|
|
236
|
+
"TemplateDirSource",
|
|
237
|
+
"StarterFilesSource",
|
|
238
|
+
"StarterFile",
|
|
239
|
+
"TemplateSource",
|
|
240
|
+
# Sandbox
|
|
241
|
+
"DockerBuildConfig",
|
|
242
|
+
"CONTAINER_INPUT_DIR",
|
|
243
|
+
"CONTAINER_OUTPUT_DIR",
|
|
244
|
+
"CONTAINER_TASK_DIR",
|
|
245
|
+
"CONTAINER_WORK_DIR",
|
|
246
|
+
"RESERVED_CONTAINER_DIRS",
|
|
247
|
+
"DockerDriverConfig",
|
|
248
|
+
"NodeEnvConfig",
|
|
249
|
+
"PythonEnvConfig",
|
|
250
|
+
"SandboxConfig",
|
|
251
|
+
"ResourceLimits",
|
|
252
|
+
"validate_template_sources_list",
|
|
253
|
+
# Telemetry
|
|
254
|
+
"AssistantMessage",
|
|
255
|
+
"CommandTelemetry",
|
|
256
|
+
"CommandStatistics",
|
|
257
|
+
"ContentBlock",
|
|
258
|
+
"ReconciliationMessage",
|
|
259
|
+
"SlowestCommandInfo",
|
|
260
|
+
"TokenUsage",
|
|
261
|
+
"TranscriptMessage",
|
|
262
|
+
"UserMessage",
|
|
263
|
+
# Results
|
|
264
|
+
"ClassificationCriterionResult",
|
|
265
|
+
"ClassLabelStats",
|
|
266
|
+
"ConfigLineageEntry",
|
|
267
|
+
"ConfusionEntry",
|
|
268
|
+
"CriterionAggregate",
|
|
269
|
+
"CriterionResult",
|
|
270
|
+
"CriterionResultUnion",
|
|
271
|
+
"CriterionStats",
|
|
272
|
+
"FailedRowSummary",
|
|
273
|
+
"ThresholdCheck",
|
|
274
|
+
"JudgeCriterionResult",
|
|
275
|
+
"JudgeTranscript",
|
|
276
|
+
"JudgeTranscriptToolCall",
|
|
277
|
+
"PostRunResult",
|
|
278
|
+
"ResultSummary",
|
|
279
|
+
"TurnRecord",
|
|
280
|
+
"EvaluationResult",
|
|
281
|
+
"SimulationTelemetry",
|
|
282
|
+
"SuiteRollup",
|
|
283
|
+
"TaskConfigRecord",
|
|
284
|
+
"RunSummary",
|
|
285
|
+
"SkippedTask",
|
|
286
|
+
# Judge defaults
|
|
287
|
+
"DEFAULT_JUDGE_MODEL",
|
|
288
|
+
# Judge
|
|
289
|
+
"JudgeVerdict",
|
|
290
|
+
# Limits
|
|
291
|
+
"RunLimits",
|
|
292
|
+
# Merge strategy
|
|
293
|
+
"MERGE_STRATEGY_KEY",
|
|
294
|
+
"APPEND_ORDER_KEY",
|
|
295
|
+
"AppendOrder",
|
|
296
|
+
"MergeField",
|
|
297
|
+
"MergeStrategy",
|
|
298
|
+
"append_order_of",
|
|
299
|
+
"classify_annotation",
|
|
300
|
+
"merge_strategy_of",
|
|
301
|
+
# Tasks
|
|
302
|
+
"TaskDefinition",
|
|
303
|
+
"DEFAULT_SIMULATION_STOP_TOKEN",
|
|
304
|
+
"CriteriaCheckTiming",
|
|
305
|
+
"Dataset",
|
|
306
|
+
"PostRunCommand",
|
|
307
|
+
"PreRunCommand",
|
|
308
|
+
"ReferenceSource",
|
|
309
|
+
"SimulationConfig",
|
|
310
|
+
# Mutations
|
|
311
|
+
"PromptPrefix",
|
|
312
|
+
"PromptSuffix",
|
|
313
|
+
"PromptReplace",
|
|
314
|
+
"PromptTemplate",
|
|
315
|
+
"PromptMutation",
|
|
316
|
+
"apply_prompt_mutations",
|
|
317
|
+
# Experiment
|
|
318
|
+
"ExperimentDefaults",
|
|
319
|
+
"ExperimentDefinition",
|
|
320
|
+
"ExperimentResult",
|
|
321
|
+
"ExperimentVariant",
|
|
322
|
+
"ResolvedTask",
|
|
323
|
+
"TaskExperimentSummary",
|
|
324
|
+
"TaskResult",
|
|
325
|
+
"VariantAggregate",
|
|
326
|
+
"VariantResult",
|
|
327
|
+
]
|
|
328
|
+
|
|
329
|
+
# Type aliases (forward compatible)
|
|
330
|
+
# Typed as the discriminated union so callers that iterate the result list
|
|
331
|
+
# and use ``isinstance(cr, JudgeCriterionResult)`` get the precise variant
|
|
332
|
+
# membership. The runtime objects are concrete subclasses regardless; the
|
|
333
|
+
# alias change is purely a type-checking precision fix that mirrors the
|
|
334
|
+
# ``EvaluationResult.success_criteria_results`` field type.
|
|
335
|
+
type CriteriaResults = list[CriterionResultUnion]
|
|
336
|
+
type SuccessCriteria = list[SuccessCriterion]
|
|
337
|
+
type TurnRecords = list[TurnRecord]
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
"""Agent configuration model and SDK pass-through validation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import dataclasses
|
|
6
|
+
from typing import Annotated, Any, ClassVar, Literal, Self, TypedDict
|
|
7
|
+
|
|
8
|
+
from claude_agent_sdk import ClaudeAgentOptions
|
|
9
|
+
from pydantic import (
|
|
10
|
+
AliasChoices,
|
|
11
|
+
BaseModel,
|
|
12
|
+
BeforeValidator,
|
|
13
|
+
ConfigDict,
|
|
14
|
+
Field,
|
|
15
|
+
SerializeAsAny,
|
|
16
|
+
field_validator,
|
|
17
|
+
model_validator,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from coder_eval.models.enums import AgentKind, PermissionMode
|
|
21
|
+
from coder_eval.models.merge_strategy import MergeField
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
type SettingSource = Literal["user", "project", "local"]
|
|
25
|
+
"""Vendor-neutral mirror of claude_agent_sdk.SettingSource (no SDK dependency)."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class LocalPluginConfig(TypedDict):
|
|
29
|
+
"""Vendor-neutral local plugin/skills source: a directory the agent scans for skills.
|
|
30
|
+
|
|
31
|
+
Mirrors the runtime shape of claude_agent_sdk.SdkPluginConfig but carries no SDK
|
|
32
|
+
dependency, so the agnostic BaseAgentConfig can declare ``plugins`` without leaking a
|
|
33
|
+
Claude-Code type onto Codex / NoOp configs. Entries remain plain dicts at runtime
|
|
34
|
+
(TypedDict), so all consumers — Codex skill discovery, docker_runner auto-mount,
|
|
35
|
+
utils.process_plugins, and the Claude SDK pass-through — are unchanged.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
type: Literal["local"]
|
|
39
|
+
path: str
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
_VALID_SDK_OPTION_FIELDS: frozenset[str] = frozenset(f.name for f in dataclasses.fields(ClaudeAgentOptions))
|
|
43
|
+
# Keys that `coder_eval` already owns at the AgentConfig level OR that are
|
|
44
|
+
# transport / lifecycle / security-critical. Setting them via the
|
|
45
|
+
# sdk_options pass-through would either silently shadow a typed field, let
|
|
46
|
+
# the user inject pre-LLM lifecycle hooks (hooks / mcp_servers /
|
|
47
|
+
# permission_prompt_tool_name / can_use_tool / agents), or bypass
|
|
48
|
+
# framework-managed runtime state (cwd / env / resume / max_turns / ...).
|
|
49
|
+
# Most relevantly: AgentJudgeCriterion forces setting_sources=[] for
|
|
50
|
+
# security; allowing `hooks` through sdk_options would re-open that hole.
|
|
51
|
+
# NOTE on the allow/deny model: validation works as ALLOW iff
|
|
52
|
+
# (key in _VALID_SDK_OPTION_FIELDS) AND (key not in _FRAMEWORK_OWNED_SDK_FIELDS)
|
|
53
|
+
# i.e. the user-visible set is ``_VALID - _FRAMEWORK_OWNED``. The denylist is
|
|
54
|
+
# explicit so the curated rationale (transport / lifecycle / security / typed-
|
|
55
|
+
# mirror) stays close to the code. To keep this from being fail-open as the
|
|
56
|
+
# SDK grows: ``tests/test_sdk_option_classification.py`` asserts EVERY field
|
|
57
|
+
# on ``ClaudeAgentOptions`` is classified — either typed-mirrored or in
|
|
58
|
+
# ``_FRAMEWORK_OWNED_SDK_FIELDS``. A new SDK release adding an unclassified
|
|
59
|
+
# field will fail that test loudly rather than silently being passed through.
|
|
60
|
+
_FRAMEWORK_OWNED_SDK_FIELDS: frozenset[str] = frozenset(
|
|
61
|
+
{
|
|
62
|
+
# mirrored as typed AgentConfig fields:
|
|
63
|
+
"model",
|
|
64
|
+
"permission_mode",
|
|
65
|
+
"allowed_tools",
|
|
66
|
+
"disallowed_tools",
|
|
67
|
+
"plugins",
|
|
68
|
+
"system_prompt",
|
|
69
|
+
"system_prompt_file",
|
|
70
|
+
"settings",
|
|
71
|
+
# transport / runtime — set by the agent, not the user:
|
|
72
|
+
"cwd",
|
|
73
|
+
"env",
|
|
74
|
+
"stderr",
|
|
75
|
+
"debug_stderr",
|
|
76
|
+
"resume",
|
|
77
|
+
"max_turns",
|
|
78
|
+
"session_id",
|
|
79
|
+
"session_store",
|
|
80
|
+
"session_store_flush",
|
|
81
|
+
# session lifecycle — coder_eval owns this via `resume` and the
|
|
82
|
+
# orchestrator's "advance session_id only on clean turns" logic.
|
|
83
|
+
# Letting YAML override would silently bypass that.
|
|
84
|
+
"continue_conversation",
|
|
85
|
+
"fork_session",
|
|
86
|
+
# budgeting — overlaps with RunLimits.max_usd / RunLimits.max_total_tokens
|
|
87
|
+
# which the orchestrator enforces with explicit FinalStatus codes
|
|
88
|
+
# (TOKEN_BUDGET_EXCEEDED / COST_BUDGET_EXCEEDED). Two independent
|
|
89
|
+
# budget guards would disagree on counts; route everything through
|
|
90
|
+
# RunLimits.
|
|
91
|
+
"max_budget_usd",
|
|
92
|
+
"task_budget",
|
|
93
|
+
# security-critical: arbitrary code injection or settings-bypass
|
|
94
|
+
# surfaces. Keep out of the YAML-visible knob.
|
|
95
|
+
"hooks",
|
|
96
|
+
"mcp_servers",
|
|
97
|
+
"cli_path",
|
|
98
|
+
"extra_args",
|
|
99
|
+
"agents",
|
|
100
|
+
"can_use_tool",
|
|
101
|
+
"permission_prompt_tool_name",
|
|
102
|
+
"tools",
|
|
103
|
+
"sandbox",
|
|
104
|
+
"skills",
|
|
105
|
+
"add_dirs",
|
|
106
|
+
"setting_sources", # framework-controlled to prevent hook injection
|
|
107
|
+
# telemetry: required by ClaudeCodeAgent to recover per-emission
|
|
108
|
+
# output_tokens via message_delta stream events (works around
|
|
109
|
+
# anthropics/claude-code#22686 where the assistant event's
|
|
110
|
+
# output_tokens is a partial streaming snapshot). Letting YAML
|
|
111
|
+
# turn it off would silently drop per-message output accounting.
|
|
112
|
+
"include_partial_messages",
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
# Precomputed user-visible allowlist (= valid SDK fields minus framework-owned).
|
|
116
|
+
# Pre-sorted once at module load so error paths don't recompute it.
|
|
117
|
+
_USER_VISIBLE_SDK_FIELDS: tuple[str, ...] = tuple(sorted(_VALID_SDK_OPTION_FIELDS - _FRAMEWORK_OWNED_SDK_FIELDS))
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class BaseAgentConfig(BaseModel):
|
|
121
|
+
"""Base configuration for all agent types."""
|
|
122
|
+
|
|
123
|
+
model_config = ConfigDict(validate_assignment=True, populate_by_name=True, extra="forbid")
|
|
124
|
+
|
|
125
|
+
# Cross-field merge exclusion: setting either prompt field at any layer clears
|
|
126
|
+
# the sibling (the generic resolver honors this uniformly). ClassVar -> not a
|
|
127
|
+
# model field; Pydantic does not validate/assign it.
|
|
128
|
+
_merge_exclusive_groups: ClassVar[tuple[tuple[str, ...], ...]] = (("system_prompt", "system_prompt_file"),)
|
|
129
|
+
|
|
130
|
+
type: str | None = Field(
|
|
131
|
+
default=None,
|
|
132
|
+
description=(
|
|
133
|
+
"The type of agent to use (claude-code, codex, or any plugin-registered kind). "
|
|
134
|
+
"May be omitted on the task and supplied via experiment defaults or --type. "
|
|
135
|
+
"Validated against the agent registry by parse_agent_config / task resolution."
|
|
136
|
+
),
|
|
137
|
+
)
|
|
138
|
+
model: str | None = Field(default=None, description="Specific model to use (if applicable)")
|
|
139
|
+
permission_mode: PermissionMode = Field(
|
|
140
|
+
default=PermissionMode.ACCEPT_EDITS, description="Permission mode for agent actions"
|
|
141
|
+
)
|
|
142
|
+
allowed_tools: list[str] | None = MergeField(
|
|
143
|
+
strategy="replace", default=None, description="List of allowed tools (e.g., ['Read', 'Write', 'Bash'])"
|
|
144
|
+
)
|
|
145
|
+
disallowed_tools: list[str] | None = MergeField(
|
|
146
|
+
strategy="replace", default=None, description="List of disallowed tools (e.g., ['TodoWrite'])"
|
|
147
|
+
)
|
|
148
|
+
plugins: list[LocalPluginConfig] | None = MergeField(
|
|
149
|
+
strategy="replace", default=None, description="List of plugins (local skills/plugin sources)"
|
|
150
|
+
)
|
|
151
|
+
system_prompt: str | None = Field(
|
|
152
|
+
default=None,
|
|
153
|
+
description=(
|
|
154
|
+
"Custom system prompt. Replaces the default system prompt. "
|
|
155
|
+
"Supports inline text or multi-line YAML strings. "
|
|
156
|
+
"Mutually exclusive with system_prompt_file."
|
|
157
|
+
),
|
|
158
|
+
)
|
|
159
|
+
system_prompt_file: str | None = Field(
|
|
160
|
+
default=None,
|
|
161
|
+
description=(
|
|
162
|
+
"Path to a file containing the system prompt (relative to task YAML). "
|
|
163
|
+
"The file contents are loaded at task resolution time and set as system_prompt. "
|
|
164
|
+
"Mutually exclusive with system_prompt."
|
|
165
|
+
),
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# Customizable ignore patterns for file tracking
|
|
169
|
+
ignore_patterns: list[str] = MergeField(
|
|
170
|
+
strategy="replace",
|
|
171
|
+
default_factory=list,
|
|
172
|
+
description=(
|
|
173
|
+
"Pattern overrides applied when copying the workspace into a judge "
|
|
174
|
+
"sub-agent sandbox. Plain entries add to the defaults; entries "
|
|
175
|
+
"prefixed with '!' remove a default (gitignore-style negation)."
|
|
176
|
+
),
|
|
177
|
+
validation_alias=AliasChoices("ignore_patterns", "additional_ignore_patterns"),
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
@field_validator("ignore_patterns")
|
|
181
|
+
@classmethod
|
|
182
|
+
def _validate_ignore_patterns(cls, values: list[str]) -> list[str]:
|
|
183
|
+
from coder_eval.resources import normalize_ignore_pattern_entry
|
|
184
|
+
|
|
185
|
+
return [normalize_ignore_pattern_entry(v) for v in values]
|
|
186
|
+
|
|
187
|
+
@model_validator(mode="after")
|
|
188
|
+
def check_prompt_exclusivity(self) -> Self:
|
|
189
|
+
"""Ensure system_prompt and system_prompt_file are mutually exclusive."""
|
|
190
|
+
if self.system_prompt is not None and self.system_prompt_file is not None:
|
|
191
|
+
raise ValueError("Only one of 'system_prompt' or 'system_prompt_file' can be provided, not both")
|
|
192
|
+
return self
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class ClaudeCodeAgentConfig(BaseAgentConfig):
|
|
196
|
+
"""Claude Code agent configuration."""
|
|
197
|
+
|
|
198
|
+
type: Literal[AgentKind.CLAUDE_CODE] # type: ignore[assignment]
|
|
199
|
+
|
|
200
|
+
claude_settings: str | dict[str, Any] | None = MergeField(
|
|
201
|
+
strategy="deep",
|
|
202
|
+
default=None,
|
|
203
|
+
description=(
|
|
204
|
+
"Claude Code settings passed via --settings. Accepts a JSON-serializable dict "
|
|
205
|
+
"(inlined) or a file path string. Use permissions.deny to block tool access to "
|
|
206
|
+
'specific paths: {"permissions": {"deny": ["Read(/some/path/**)"]}}. '
|
|
207
|
+
"Merged deeply across config layers when both sides are dicts; a str/None value replaces."
|
|
208
|
+
),
|
|
209
|
+
)
|
|
210
|
+
sdk_options: dict[str, Any] = Field(
|
|
211
|
+
default_factory=dict,
|
|
212
|
+
description=(
|
|
213
|
+
"Pass-through dict of Claude Code SDK ClaudeAgentOptions fields that coder_eval "
|
|
214
|
+
"does not own directly (e.g. 'effort'). Keys must be valid ClaudeAgentOptions "
|
|
215
|
+
"fields and must NOT be framework-managed (model/allowed_tools/permission_mode/...). "
|
|
216
|
+
"Validated at YAML load; values are forwarded verbatim to the SDK."
|
|
217
|
+
),
|
|
218
|
+
)
|
|
219
|
+
setting_sources: list[SettingSource] | None = MergeField(
|
|
220
|
+
strategy="replace",
|
|
221
|
+
default=None,
|
|
222
|
+
description=(
|
|
223
|
+
"Claude Code setting sources to load (e.g., ['project', 'user']). "
|
|
224
|
+
"Set to [] for maximum isolation (no host settings or hooks) — used by judge agents and simulators. "
|
|
225
|
+
"Defaults to None, which at runtime becomes ['project'] so .mcp.json is discovered. "
|
|
226
|
+
"Users may override this value for custom setting loading behavior."
|
|
227
|
+
),
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
@field_validator("sdk_options")
|
|
231
|
+
@classmethod
|
|
232
|
+
def _validate_sdk_options_keys(cls, v: dict[str, Any]) -> dict[str, Any]:
|
|
233
|
+
if not v:
|
|
234
|
+
return v
|
|
235
|
+
for key in v:
|
|
236
|
+
if key not in _VALID_SDK_OPTION_FIELDS:
|
|
237
|
+
raise ValueError(
|
|
238
|
+
f"sdk_options key {key!r} is not a ClaudeAgentOptions field "
|
|
239
|
+
+ f"(valid keys: {list(_USER_VISIBLE_SDK_FIELDS)})"
|
|
240
|
+
)
|
|
241
|
+
if key in _FRAMEWORK_OWNED_SDK_FIELDS:
|
|
242
|
+
raise ValueError(
|
|
243
|
+
f"sdk_options key {key!r} is framework-managed; set it as a top-level AgentConfig field instead"
|
|
244
|
+
)
|
|
245
|
+
return v
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class CodexAgentConfig(BaseAgentConfig):
|
|
249
|
+
"""Codex agent configuration."""
|
|
250
|
+
|
|
251
|
+
type: Literal[AgentKind.CODEX] # type: ignore[assignment]
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
# Gemini "thinking level" (reasoning effort) for the Antigravity backend. Mirrors
|
|
255
|
+
# google.antigravity.types.ThinkingLevel as a plain Literal so this config module
|
|
256
|
+
# imports without the optional `google-antigravity` SDK installed (the SDK is an
|
|
257
|
+
# opt-in extra; base installs must still load every config class).
|
|
258
|
+
type ThinkingLevel = Literal["minimal", "low", "medium", "high"]
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class AntigravityAgentConfig(BaseAgentConfig):
|
|
262
|
+
"""Antigravity agent configuration (Google's Gemini coding agent harness).
|
|
263
|
+
|
|
264
|
+
Runs via the ``google-antigravity`` SDK's local harness, authenticated with
|
|
265
|
+
``GEMINI_API_KEY``. ``model`` defaults (when unset on the task / ``--model`` /
|
|
266
|
+
``ANTIGRAVITY_MODEL``) to the recommended Gemini 3.1 Pro coding model
|
|
267
|
+
(``gemini-3.1-pro-preview``).
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
type: Literal[AgentKind.ANTIGRAVITY] # type: ignore[assignment]
|
|
271
|
+
|
|
272
|
+
thinking_level: ThinkingLevel = Field(
|
|
273
|
+
default="medium",
|
|
274
|
+
description=(
|
|
275
|
+
"Gemini reasoning effort (minimal/low/medium/high). 'medium' is Google's recommended "
|
|
276
|
+
"daily-driver default — the API otherwise defaults to the more expensive 'high'."
|
|
277
|
+
),
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class NoneAgentConfig(BaseAgentConfig):
|
|
282
|
+
"""No-op ("agentless") agent configuration.
|
|
283
|
+
|
|
284
|
+
Selected with ``agent: {type: none}``. Binds to ``NoOpAgent`` (Null Object
|
|
285
|
+
pattern): coder-eval sets up the sandbox, runs ``pre_run``, and checks the
|
|
286
|
+
success_criteria directly — the agent's ``start``/``communicate``/``stop``
|
|
287
|
+
are no-ops and no model API call is made. Use for system / canary checks
|
|
288
|
+
(e.g. Orchestrator or Integration Service connectivity) that reuse the eval
|
|
289
|
+
infrastructure (sandbox, reports, evalboard, ADX) without involving an agent.
|
|
290
|
+
|
|
291
|
+
The task must declare no ``initial_prompt`` / ``initial_prompt_file`` and no
|
|
292
|
+
``simulation`` (no agent reads them), and every criterion must be
|
|
293
|
+
agent-independent (no ``requires_agent`` criteria) — enforced by
|
|
294
|
+
``TaskDefinition.check_none_agent``. The inherited ``model`` / prompt /
|
|
295
|
+
tool fields are accepted but ignored.
|
|
296
|
+
"""
|
|
297
|
+
|
|
298
|
+
type: Literal[AgentKind.NONE] # type: ignore[assignment]
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
# Discriminated union type for type hints, validation, and YAML serialization
|
|
302
|
+
# Only includes the concrete subclasses (not BaseAgentConfig) since the discriminator
|
|
303
|
+
# must be a Literal type. BaseAgentConfig is returned by parse_agent_config when type=None.
|
|
304
|
+
type AgentConfig = Annotated[
|
|
305
|
+
ClaudeCodeAgentConfig | CodexAgentConfig | AntigravityAgentConfig | NoneAgentConfig,
|
|
306
|
+
Field(discriminator="type"),
|
|
307
|
+
]
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def parse_agent_config(**kwargs: Any) -> BaseAgentConfig:
|
|
311
|
+
"""Factory function for agent configuration with registry-driven dispatch.
|
|
312
|
+
|
|
313
|
+
Routes to the config class the agent registry binds to the ``type`` kind —
|
|
314
|
+
``ClaudeCodeAgentConfig`` / ``CodexAgentConfig`` / ``NoneAgentConfig`` for the
|
|
315
|
+
built-ins, or any plugin-registered config subclass. If ``type`` is None (or
|
|
316
|
+
omitted) returns a bare ``BaseAgentConfig`` so type resolution can happen
|
|
317
|
+
later at the experiment / CLI layer.
|
|
318
|
+
|
|
319
|
+
This replaces the import-time ``TypeAdapter(AgentConfig)`` discriminated-union
|
|
320
|
+
dispatch (which could only ever see the built-in kinds) with a per-kind
|
|
321
|
+
lookup resolved *after* plugin load — the BYOA seam.
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
**kwargs: Configuration fields including the ``type`` kind.
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
The registered config subclass for ``type``, or ``BaseAgentConfig`` when
|
|
328
|
+
``type`` is None.
|
|
329
|
+
|
|
330
|
+
Raises:
|
|
331
|
+
ValueError: If ``type`` is a kind no agent is registered for.
|
|
332
|
+
ValidationError: If configuration values are invalid for the config class.
|
|
333
|
+
|
|
334
|
+
Example:
|
|
335
|
+
>>> cfg = parse_agent_config(type="claude-code", model="claude-opus-4-7")
|
|
336
|
+
>>> isinstance(cfg, ClaudeCodeAgentConfig)
|
|
337
|
+
True
|
|
338
|
+
"""
|
|
339
|
+
from coder_eval.agents.registry import AgentRegistry
|
|
340
|
+
from coder_eval.plugins import ensure_plugins_loaded
|
|
341
|
+
|
|
342
|
+
agent_type = kwargs.get("type")
|
|
343
|
+
|
|
344
|
+
if agent_type is None:
|
|
345
|
+
# No type specified - return BaseAgentConfig with type=None
|
|
346
|
+
# This allows type resolution to happen at the experiment/CLI layer
|
|
347
|
+
filtered_kwargs = {k: v for k, v in kwargs.items() if k != "type"}
|
|
348
|
+
return BaseAgentConfig(**filtered_kwargs)
|
|
349
|
+
|
|
350
|
+
ensure_plugins_loaded()
|
|
351
|
+
registration = AgentRegistry.get(agent_type)
|
|
352
|
+
if registration is None:
|
|
353
|
+
raise AgentRegistry.unregistered_kind_error(agent_type)
|
|
354
|
+
return registration.config_class.model_validate(kwargs)
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def _coerce_agent_config(value: Any) -> Any:
|
|
358
|
+
"""Coerce a raw agent-config dict to its registered subclass via the registry.
|
|
359
|
+
|
|
360
|
+
Already-built config instances (and ``None``) pass through untouched.
|
|
361
|
+
"""
|
|
362
|
+
if isinstance(value, dict):
|
|
363
|
+
return parse_agent_config(**value)
|
|
364
|
+
return value
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
# The canonical annotation for any field that holds a resolved agent config.
|
|
368
|
+
# BeforeValidator routes a dict through registry dispatch (so plugin kinds resolve
|
|
369
|
+
# to their subclass); SerializeAsAny keeps subclass-only fields on model_dump()
|
|
370
|
+
# instead of the base schema silently dropping them. Used by every persisted
|
|
371
|
+
# agent-config field (TaskDefinition.agent, EvaluationResult.agent_config) so the
|
|
372
|
+
# round-trip guarantee is uniform across the built-in union and plugin kinds.
|
|
373
|
+
type ResolvedAgentConfig = Annotated[SerializeAsAny[BaseAgentConfig], BeforeValidator(_coerce_agent_config)]
|