whetkit 0.4.0__tar.gz → 0.5.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.
Files changed (38) hide show
  1. {whetkit-0.4.0 → whetkit-0.5.0}/PKG-INFO +1 -1
  2. {whetkit-0.4.0 → whetkit-0.5.0}/pyproject.toml +1 -1
  3. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/cli.py +48 -7
  4. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/generate.py +5 -1
  5. {whetkit-0.4.0 → whetkit-0.5.0}/LICENSE +0 -0
  6. {whetkit-0.4.0 → whetkit-0.5.0}/README.md +0 -0
  7. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/__init__.py +0 -0
  8. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/curation/__init__.py +0 -0
  9. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/curation/export.py +0 -0
  10. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/curation/optimizer.py +0 -0
  11. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/curation/overlay.py +0 -0
  12. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/curation/plan.py +0 -0
  13. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/datasets/__init__.py +0 -0
  14. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/datasets/tasks.py +0 -0
  15. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/doctor.py +0 -0
  16. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/llm/__init__.py +0 -0
  17. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/llm/anthropic_provider.py +0 -0
  18. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/llm/base.py +0 -0
  19. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/llm/openai_provider.py +0 -0
  20. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/llm/pricing.py +0 -0
  21. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/llm/registry.py +0 -0
  22. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/mcp/__init__.py +0 -0
  23. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/mcp/client.py +0 -0
  24. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/mcp/introspect.py +0 -0
  25. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/mcp/transport.py +0 -0
  26. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/py.typed +0 -0
  27. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/report/__init__.py +0 -0
  28. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/report/builder.py +0 -0
  29. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/report/html.py +0 -0
  30. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/runner/__init__.py +0 -0
  31. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/runner/agent.py +0 -0
  32. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/scoring/__init__.py +0 -0
  33. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/scoring/aggregate.py +0 -0
  34. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/scoring/deterministic.py +0 -0
  35. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/scoring/judge.py +0 -0
  36. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/tracing/__init__.py +0 -0
  37. {whetkit-0.4.0 → whetkit-0.5.0}/src/whetkit/tracing/records.py +0 -0
  38. {whetkit-0.4.0 → whetkit-0.5.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.4.0
3
+ Version: 0.5.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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "whetkit"
3
- version = "0.4.0"
3
+ version = "0.5.0"
4
4
  description = "Measure and improve how well LLM agents select and use the tools exposed by an MCP server"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -271,6 +271,20 @@ def plan_init(
271
271
  help="Also keep every tool referenced by these tasks' expected_tools",
272
272
  ),
273
273
  ] = None,
274
+ from_traces: Annotated[
275
+ str | None,
276
+ typer.Option(
277
+ "--from-traces",
278
+ help=(
279
+ "Also keep every tool actually called in this trace store "
280
+ "(what real runs used, including tools no spec lists)"
281
+ ),
282
+ ),
283
+ ] = None,
284
+ traces_group: Annotated[
285
+ str | None,
286
+ typer.Option("--traces-group", help="Restrict --from-traces to one run group"),
287
+ ] = None,
274
288
  out: Annotated[
275
289
  str, typer.Option("--out", help="Where to write the plan YAML")
276
290
  ] = ".whetkit/curation-plan.yaml",
@@ -286,8 +300,18 @@ def plan_init(
286
300
  for task in load_tasks(from_tasks):
287
301
  for slot in task.expected_tool_slots:
288
302
  keep_set.update(slot)
303
+ if from_traces:
304
+ from whetkit.tracing import TraceStore
305
+
306
+ if not Path(from_traces).is_file():
307
+ raise typer.BadParameter(f"no trace store at {from_traces}")
308
+ with TraceStore(from_traces) as trace_store:
309
+ for run in trace_store.load_runs(traces_group):
310
+ keep_set.update(run.called_tool_names)
289
311
  if not keep_set:
290
- raise typer.BadParameter("nothing to keep — pass --keep and/or --from-tasks")
312
+ raise typer.BadParameter(
313
+ "nothing to keep — pass --keep, --from-tasks, and/or --from-traces"
314
+ )
291
315
 
292
316
  spec = _resolve_server(server, http_mode)
293
317
  inventory = asyncio.run(inspect_server(spec))
@@ -480,6 +504,17 @@ def run(
480
504
  help="Write a machine-readable summary (metrics + per-task outcomes) to this path",
481
505
  ),
482
506
  ] = None,
507
+ concurrency: Annotated[
508
+ int,
509
+ typer.Option(
510
+ "--concurrency",
511
+ help=(
512
+ "Run up to N tasks of a repetition in parallel (each gets its own "
513
+ "server connection). Only safe when tasks are independent — writes "
514
+ "to shared state should stay at 1."
515
+ ),
516
+ ),
517
+ ] = 1,
483
518
  reset_cmd: Annotated[
484
519
  str | None,
485
520
  typer.Option(
@@ -500,6 +535,8 @@ def run(
500
535
 
501
536
  if runs < 1:
502
537
  raise typer.BadParameter("--runs must be at least 1")
538
+ if concurrency < 1:
539
+ raise typer.BadParameter("--concurrency must be at least 1")
503
540
 
504
541
  task_list = load_tasks(tasks)
505
542
  servers = _resolve_task_servers(task_list, server, http_mode)
@@ -523,12 +560,16 @@ def run(
523
560
  name_map = curation_plan.rename_map()
524
561
 
525
562
  async def _run_once(group_name: str, cache: JudgeCache):
526
- task_runs = []
527
- for task in task_list:
528
- typer.echo(f"running {task.id} ...", err=True)
529
- task_runs.append(
530
- await run_task(task, servers[task.server], config, client_factory=client_factory)
531
- )
563
+ semaphore = asyncio.Semaphore(concurrency)
564
+
565
+ async def _one(task):
566
+ async with semaphore:
567
+ typer.echo(f"running {task.id} ...", err=True)
568
+ return await run_task(
569
+ task, servers[task.server], config, client_factory=client_factory
570
+ )
571
+
572
+ task_runs = list(await asyncio.gather(*(_one(task) for task in task_list)))
532
573
 
533
574
  with TraceStore(store_path) as trace_store:
534
575
  trace_store.save_runs(task_runs, run_group=group_name)
@@ -31,7 +31,11 @@ Rules for every task:
31
31
  is either one tool name, or a list of genuinely interchangeable
32
32
  alternatives for that step. Use ONLY tool names from the given list.
33
33
  - success_criteria is one or two concrete, checkable sentences a grader can
34
- verify from the agent's final answer alone.
34
+ verify from the agent's final answer alone. When the data source is live
35
+ or changing (news pages, network traffic, timestamps, prices), grade the
36
+ SHAPE of the answer ("names the current top story", "lists the requests
37
+ that were observed"), never a specific volatile value — a criterion that
38
+ is wrong an hour later is a flaky eval, not a strict one.
35
39
  {writes_rule}
36
40
  - Set "ordered": true only when the steps must happen in sequence.
37
41
  - ids are short kebab-case slugs, unique across the set.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes