whetkit 0.5.0__tar.gz → 0.6.0__tar.gz
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.
- {whetkit-0.5.0 → whetkit-0.6.0}/PKG-INFO +13 -4
- {whetkit-0.5.0 → whetkit-0.6.0}/README.md +12 -3
- {whetkit-0.5.0 → whetkit-0.6.0}/pyproject.toml +1 -1
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/cli.py +2 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/scoring/aggregate.py +22 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/LICENSE +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/__init__.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/curation/__init__.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/curation/export.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/curation/optimizer.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/curation/overlay.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/curation/plan.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/datasets/__init__.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/datasets/tasks.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/doctor.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/generate.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/llm/__init__.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/llm/anthropic_provider.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/llm/base.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/llm/openai_provider.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/llm/pricing.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/llm/registry.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/mcp/__init__.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/mcp/client.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/mcp/introspect.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/mcp/transport.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/py.typed +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/report/__init__.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/report/builder.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/report/html.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/runner/__init__.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/runner/agent.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/scoring/__init__.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/scoring/deterministic.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/scoring/judge.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/tracing/__init__.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/tracing/records.py +0 -0
- {whetkit-0.5.0 → whetkit-0.6.0}/src/whetkit/tracing/store.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: whetkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: Measure and improve how well LLM agents select and use the tools exposed by an MCP server
|
|
5
5
|
Keywords: mcp,model-context-protocol,llm,agents,evals,tool-use,tool-selection,benchmark
|
|
6
6
|
Author: Mohammed Benlamlih
|
|
@@ -163,12 +163,21 @@ back to the original world.
|
|
|
163
163
|
|---|---|
|
|
164
164
|
| `whetkit doctor` | Lint the tool surface: vague descriptions, cryptic names, near-duplicates, context bloat. `--json`; `--fail-on warn` for CI. |
|
|
165
165
|
| `whetkit inspect` | Tool inventory: names, params, description tokens, schema complexity. |
|
|
166
|
-
| `whetkit generate` | Draft eval tasks from the inventory
|
|
167
|
-
| `whetkit run` | Agentic eval
|
|
168
|
-
| `whetkit curate` | Baseline → LLM-proposed overlay plan → curated eval → before/after report. |
|
|
166
|
+
| `whetkit generate` | Draft eval tasks from the inventory (server-context aware, read-only unless `--allow-writes`); review-before-trust YAML. |
|
|
167
|
+
| `whetkit run` | Agentic eval with real tool execution. `--runs N` for mean±range and flaky-task detection, `--concurrency` for independent tasks, `--reset-cmd` for stateful fixtures, `--plan` to score a curated view, `--summary-json` for machine-readable results (with cost estimates). |
|
|
168
|
+
| `whetkit curate` | Baseline → LLM-proposed overlay plan → curated eval → before/after report. `--prune-unused` for the cost play. |
|
|
169
|
+
| `whetkit fix` | Self-correcting curation: propose → eval → feed regressions back → revise, up to `--max-iterations`; keeps the best plan by measured results. |
|
|
170
|
+
| `whetkit plan-init` | Scaffold a view plan: `--keep a,b`, `--from-tasks tasks/`, `--from-traces traces.sqlite3` — hide everything else. |
|
|
171
|
+
| `whetkit diff` | Compare two `--summary-json` files: metric deltas + per-task PASS/MISS/FLAKY transitions. |
|
|
172
|
+
| `whetkit export` | Share a plan: `--to markdown` (upstream-PR fix table) or `--to json` (gateway overrides). |
|
|
169
173
|
| `whetkit report` | Rebuild the HTML/JSON report from stored traces. |
|
|
170
174
|
| `whetkit overlay` | Serve the curated view as a stdio MCP server. |
|
|
171
175
|
|
|
176
|
+
Authenticated servers: any string in `server.json` may reference `${ENV_VARS}`
|
|
177
|
+
(e.g. an `Authorization` header) — credentials never live in the file.
|
|
178
|
+
Summaries flag probable task-spec gaps, errored runs, and failed tool calls
|
|
179
|
+
so a bad score always says why.
|
|
180
|
+
|
|
172
181
|
## How scoring works
|
|
173
182
|
|
|
174
183
|
- **Deterministic tool-match**: each task lists the expected tool calls
|
|
@@ -132,12 +132,21 @@ back to the original world.
|
|
|
132
132
|
|---|---|
|
|
133
133
|
| `whetkit doctor` | Lint the tool surface: vague descriptions, cryptic names, near-duplicates, context bloat. `--json`; `--fail-on warn` for CI. |
|
|
134
134
|
| `whetkit inspect` | Tool inventory: names, params, description tokens, schema complexity. |
|
|
135
|
-
| `whetkit generate` | Draft eval tasks from the inventory
|
|
136
|
-
| `whetkit run` | Agentic eval
|
|
137
|
-
| `whetkit curate` | Baseline → LLM-proposed overlay plan → curated eval → before/after report. |
|
|
135
|
+
| `whetkit generate` | Draft eval tasks from the inventory (server-context aware, read-only unless `--allow-writes`); review-before-trust YAML. |
|
|
136
|
+
| `whetkit run` | Agentic eval with real tool execution. `--runs N` for mean±range and flaky-task detection, `--concurrency` for independent tasks, `--reset-cmd` for stateful fixtures, `--plan` to score a curated view, `--summary-json` for machine-readable results (with cost estimates). |
|
|
137
|
+
| `whetkit curate` | Baseline → LLM-proposed overlay plan → curated eval → before/after report. `--prune-unused` for the cost play. |
|
|
138
|
+
| `whetkit fix` | Self-correcting curation: propose → eval → feed regressions back → revise, up to `--max-iterations`; keeps the best plan by measured results. |
|
|
139
|
+
| `whetkit plan-init` | Scaffold a view plan: `--keep a,b`, `--from-tasks tasks/`, `--from-traces traces.sqlite3` — hide everything else. |
|
|
140
|
+
| `whetkit diff` | Compare two `--summary-json` files: metric deltas + per-task PASS/MISS/FLAKY transitions. |
|
|
141
|
+
| `whetkit export` | Share a plan: `--to markdown` (upstream-PR fix table) or `--to json` (gateway overrides). |
|
|
138
142
|
| `whetkit report` | Rebuild the HTML/JSON report from stored traces. |
|
|
139
143
|
| `whetkit overlay` | Serve the curated view as a stdio MCP server. |
|
|
140
144
|
|
|
145
|
+
Authenticated servers: any string in `server.json` may reference `${ENV_VARS}`
|
|
146
|
+
(e.g. an `Authorization` header) — credentials never live in the file.
|
|
147
|
+
Summaries flag probable task-spec gaps, errored runs, and failed tool calls
|
|
148
|
+
so a bad score always says why.
|
|
149
|
+
|
|
141
150
|
## How scoring works
|
|
142
151
|
|
|
143
152
|
- **Deterministic tool-match**: each task lists the expected tool calls
|
|
@@ -79,6 +79,8 @@ def _summary_payload(group_name: str, summary, task_runs: list) -> dict:
|
|
|
79
79
|
"tool_hit": score.tool_hit,
|
|
80
80
|
"judge_passed": score.judge.passed if score.judge else None,
|
|
81
81
|
"spec_gap": score.spec_gap,
|
|
82
|
+
"run_status": str(score.run_status),
|
|
83
|
+
"tool_errors": score.tool_errors,
|
|
82
84
|
"called": score.tool_match.called,
|
|
83
85
|
"missing": score.tool_match.missing_slots,
|
|
84
86
|
"extra_calls": score.tool_match.extra_calls,
|
|
@@ -15,6 +15,7 @@ class TaskScore(BaseModel):
|
|
|
15
15
|
run_status: RunStatus
|
|
16
16
|
tool_match: ToolMatchResult
|
|
17
17
|
judge: JudgeVerdict | None = None
|
|
18
|
+
tool_errors: int = 0
|
|
18
19
|
|
|
19
20
|
@property
|
|
20
21
|
def tool_hit(self) -> bool:
|
|
@@ -82,6 +83,14 @@ class EvalSummary(BaseModel):
|
|
|
82
83
|
return 0.0
|
|
83
84
|
return sum(len(s.tool_match.extra_calls) for s in self.scores) / len(self.scores)
|
|
84
85
|
|
|
86
|
+
@property
|
|
87
|
+
def error_run_count(self) -> int:
|
|
88
|
+
return sum(s.run_status == RunStatus.ERROR for s in self.scores)
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def total_tool_errors(self) -> int:
|
|
92
|
+
return sum(s.tool_errors for s in self.scores)
|
|
93
|
+
|
|
85
94
|
def summary_lines(self) -> list[str]:
|
|
86
95
|
lines = [
|
|
87
96
|
f"Tasks: {self.task_count}",
|
|
@@ -93,6 +102,17 @@ class EvalSummary(BaseModel):
|
|
|
93
102
|
]
|
|
94
103
|
if (rate := self.judge_pass_rate) is not None:
|
|
95
104
|
lines.insert(2, f"Judge pass-rate: {rate:.0%}")
|
|
105
|
+
if self.error_run_count:
|
|
106
|
+
lines.append(
|
|
107
|
+
f"⚠ Errored runs: {self.error_run_count}/{self.task_count} — the agent "
|
|
108
|
+
"loop failed (connection/provider); scores below reflect failures, "
|
|
109
|
+
"not tool selection"
|
|
110
|
+
)
|
|
111
|
+
if self.total_tool_errors:
|
|
112
|
+
lines.append(
|
|
113
|
+
f"⚠ Failed tool calls: {self.total_tool_errors} — agents received "
|
|
114
|
+
"errors from the server; read the traces before trusting the scores"
|
|
115
|
+
)
|
|
96
116
|
return lines
|
|
97
117
|
|
|
98
118
|
|
|
@@ -175,12 +195,14 @@ async def score_runs(
|
|
|
175
195
|
called = run.called_tool_names
|
|
176
196
|
if name_map:
|
|
177
197
|
called = [name_map.get(name, name) for name in called]
|
|
198
|
+
tool_errors = sum(call.is_error for turn in run.turns for call in turn.tool_calls)
|
|
178
199
|
scores.append(
|
|
179
200
|
TaskScore(
|
|
180
201
|
task_id=task.id,
|
|
181
202
|
run_status=run.status,
|
|
182
203
|
tool_match=score_tool_match(task, called, mode),
|
|
183
204
|
judge=verdict,
|
|
205
|
+
tool_errors=tool_errors,
|
|
184
206
|
)
|
|
185
207
|
)
|
|
186
208
|
return EvalSummary(scores=scores)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|