EvalHarness/evalharness/__init__.py
sora 27cf8b3c7e Usability round: progress plugin, CLI provider flags, top-level run(), vendored BFCL checker
- progress/: Rich per-sample terminal progress plugin (Run Plan panel,
  in-flight/rate/ETA bar); shared console + log-through-live to avoid
  interleaved writes, rollback() pairs begin_sample on the retry path,
  begin moved inside the semaphore (in-flight = actually generating),
  graceful degradation when rich is absent
- cli.py: --provider/--api-url/--model composition (openai-chat |
  openai-pool), --disable-thinking/--perf/--textools as first-class
  flags, per-bench phase lines and done/failed result lines
- __init__: top-level run()/arun() entries (event-loop safe for notebooks)
- third_party/bfcl: vendored official BFCL ast_checker + type mappings
  (Apache-2.0, provenance in __init__.py); imports rerouted locally,
  underscore_to_dot parameterized; verified bit-identical with the
  bfcl-eval package on 100 real rows -- removes the heavy extra
  (pinned numpy + cloud SDK wall) from the install path
- runner: progress/status hooks through generate+evaluate, checkpoint
  key scheme fix (empty-store falsy bug), tiered retry backoff,
  multi-segment pool {range} expansion fix, adapter-instance passthrough
- pyproject: tree_sitter family joins core deps; [bfcl] extra retired
- README: rewritten (zh) -- install/quickstart/flags reference/bench
  table/reliability/extension/architecture/validation

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-10 05:46:45 +00:00

65 lines
1.8 KiB
Python

"""EvalHarness: a plugin-based LLM/agent evaluation harness (data layer first)."""
from .data import (
ChatMessage,
Dataset,
DatasetSpec,
FieldSpec,
Sample,
SandboxSpec,
ToolInfo,
get_dataset,
list_datasets,
register_dataset,
)
__version__ = '0.1.0'
async def arun(bench, model, **kwargs):
"""ASYNC entry: for callers already inside an event loop.
import evalharness
rep = await evalharness.arun('gsm8k', 'openai/http://...?m', limit=200)
"""
return await _run_dispatch(bench, model, **kwargs)
def run(bench, model, **kwargs):
"""SYNC entry (primary API, mirroring inspect_ai.run / lm_eval).
import evalharness
rep = evalharness.run('gsm8k', 'openai/http://...?m', limit=200)
Creates its own event loop; safe to call from scripts/notebooks.
"""
import asyncio
try: # already inside a loop (notebook)? run in a thread
asyncio.get_running_loop()
import concurrent.futures
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
return pool.submit(asyncio.run, _run_dispatch(bench, model, **kwargs)).result()
except RuntimeError:
return asyncio.run(_run_dispatch(bench, model, **kwargs))
async def _run_dispatch(bench, model, **kwargs):
from .data import Dataset as _Dataset, get_dataset as _gd
from .model import run_eval as _run_eval
if isinstance(bench, str):
ds = _gd(bench, subset=kwargs.pop('subset', None))
elif isinstance(bench, (_Dataset, list)):
ds = bench
else:
raise TypeError(f'bench must be str/Dataset/list, got {type(bench)}')
return await _run_eval(ds, model, **kwargs)
__all__ = [
'Dataset', 'DatasetSpec', 'FieldSpec', 'Sample', 'ChatMessage', 'SandboxSpec', 'ToolInfo',
'get_dataset', 'list_datasets', 'register_dataset', 'run', 'arun',
]