๐Ÿ— KeyzHub
19Keys ยท community archive
15065 bytes raw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
#!/usr/bin/env python3
"""Evaluation harness for aave-compress: measures REAL compression ratios.

Why this script is paranoid about its own numbers
-------------------------------------------------
A competing tool ("caveman") marketed 65-75% token savings. Independent
re-benchmarking found the real number was 14-21%, because the original claim
was measured against an artificially verbose, non-representative baseline
corpus. This harness exists specifically to not repeat that mistake:

* The corpus (tests/fixtures/real_prompts.jsonl) is 20 realistic prompts in
  the register people actually type to an assistant. Several intentionally
  contain ZERO applicable AAVE markers โ€” that is an honest, expected outcome
  and it is reported, not hidden.
* Claude token counts come ONLY from Anthropic's count_tokens API
  (requires ANTHROPIC_API_KEY). tiktoken is NEVER used as a stand-in for a
  Claude model โ€” it undercounts Claude tokens by 15-20%+.
* --tiktoken-model provides an explicitly-labeled APPROXIMATE fallback for
  GPT-family models only.
* If no tokenizer is available at all, the run still completes and reports
  marker/word-level results, and says plainly that no token counts exist โ€”
  it never invents a percentage.

Usage:
    python eval/run_eval.py                         # Claude counts (needs ANTHROPIC_API_KEY)
    python eval/run_eval.py --model claude-opus-4-7 # different Claude target
    python eval/run_eval.py --tiktoken-model gpt-4o # approximate GPT-family fallback

Exit code 0 on any successful run (even with no token counts available);
exit code 1 only on an actual crash/bug.
"""

from __future__ import annotations

import argparse
import json
import sys
from datetime import datetime, timezone
from pathlib import Path
from statistics import mean

REPO_ROOT = Path(__file__).resolve().parent.parent
SRC_DIR = REPO_ROOT / "src"
if str(SRC_DIR) not in sys.path:
    sys.path.insert(0, str(SRC_DIR))

from aave_compress.engine import compress  # noqa: E402
from aave_compress.tokenizers import (  # noqa: E402
    count_tokens,
    count_tokens_tiktoken,
)

FIXTURES_PATH = REPO_ROOT / "tests" / "fixtures" / "real_prompts.jsonl"
RESULTS_DIR = REPO_ROOT / "eval" / "results"
LEVELS = ("light", "medium", "full")

NO_TOKENS_BANNER = (
    "NO TOKEN COUNTS AVAILABLE โ€” install/configure a tokenizer to get real numbers"
)


def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Measure real aave-compress compression ratios on a realistic prompt corpus."
    )
    parser.add_argument(
        "--model",
        default="claude-sonnet-5",
        help="Target model for token counting (default: claude-sonnet-5). "
        "Claude models require ANTHROPIC_API_KEY.",
    )
    parser.add_argument(
        "--tiktoken-model",
        default=None,
        metavar="GPT_MODEL",
        help="APPROXIMATE fallback: count tokens with tiktoken for this "
        "GPT-family model (e.g. gpt-4o) if the primary counter is "
        "unavailable. NEVER valid for Claude models.",
    )
    parser.add_argument(
        "--strip-filler",
        action="store_true",
        help="Run the generic filler-strip pass (filters.strip_fillers) "
        "before the AAVE markers, so the two layers can be compared.",
    )
    args = parser.parse_args(argv)
    if args.tiktoken_model and args.tiktoken_model.lower().startswith("claude"):
        parser.error(
            "--tiktoken-model must be a GPT-family model: tiktoken counts are "
            "never valid for Claude models (they undercount by 15-20%+). "
            "Set ANTHROPIC_API_KEY and use --model instead."
        )
    return args


def resolve_counter(args: argparse.Namespace):
    """Probe tokenizer availability ONCE, up front.

    Returns (counter_fn | None, label, method | None). counter_fn takes a
    string and returns an int token count. Errors are reported here a single
    time instead of once per (prompt, level) pair.
    """
    probe = "tokenizer availability probe"

    try:
        probed = count_tokens(probe, model=args.model)
        if probed.method == "anthropic_api":
            label = f"real Anthropic count_tokens API for {args.model}"
        else:
            label = (
                f"APPROXIMATE tiktoken counts for {args.model} "
                "(GPT-family only โ€” NOT valid for any Claude model)"
            )
        return (
            lambda text: count_tokens(text, model=args.model).tokens,
            label,
            probed.method,
        )
    except RuntimeError as exc:
        # Expected failure mode: missing ANTHROPIC_API_KEY (or missing
        # optional package). Reported once, here, not 60 times below.
        print("=" * 78)
        print(f"Token counting with {args.model!r} is UNAVAILABLE:")
        print(f"  {exc}")
        print()
        print(
            "To get real Claude token numbers, set the ANTHROPIC_API_KEY "
            "environment variable and re-run."
        )
    except Exception as exc:  # unexpected API failure โ€” degrade, don't crash
        print("=" * 78)
        print(f"Token counting with {args.model!r} failed unexpectedly:")
        print(f"  {type(exc).__name__}: {exc}")

    if args.tiktoken_model:
        try:
            count_tokens_tiktoken(probe, model=args.tiktoken_model)
        except RuntimeError as exc:
            print()
            print(f"tiktoken fallback also unavailable: {exc}")
            print("=" * 78)
            return None, NO_TOKENS_BANNER, None
        label = (
            f"APPROXIMATE tiktoken counts for {args.tiktoken_model} "
            "(GPT-family only โ€” NOT valid for any Claude model)"
        )
        print()
        print(f"Falling back to: {label}")
        print(
            "These numbers describe how a GPT-family tokenizer sees the text. "
            f"Do NOT quote them as {args.model} savings."
        )
        print("=" * 78)
        return (
            lambda text: count_tokens_tiktoken(text, model=args.tiktoken_model).tokens,
            label,
            "tiktoken_approx",
        )

    print()
    print(
        "No fallback tokenizer requested. Pass --tiktoken-model gpt-4o for an "
        "explicitly-approximate GPT-family count, or set ANTHROPIC_API_KEY "
        "for real Claude numbers."
    )
    print("=" * 78)
    return None, NO_TOKENS_BANNER, None


class SafeCounter:
    """Wraps a counter so one mid-run failure doesn't spam or crash the eval."""

    def __init__(self, fn):
        self._fn = fn
        self.failed = False

    def count(self, text: str) -> int | None:
        if self._fn is None or self.failed:
            return None
        try:
            return self._fn(text)
        except Exception as exc:
            self.failed = True
            print(
                f"WARNING: token counting failed mid-run "
                f"({type(exc).__name__}: {exc}); remaining token counts "
                "recorded as null."
            )
            return None


def load_corpus() -> list[dict]:
    prompts = []
    with open(FIXTURES_PATH, encoding="utf-8") as fh:
        for line_no, line in enumerate(fh, start=1):
            line = line.strip()
            if not line:
                continue
            record = json.loads(line)
            for key in ("id", "text", "domain"):
                if key not in record:
                    raise ValueError(
                        f"{FIXTURES_PATH}:{line_no} missing required key {key!r}"
                    )
            prompts.append(record)
    return prompts


def evaluate(
    prompts: list[dict], counter: SafeCounter, strip_filler: bool = False
) -> list[dict]:
    rows = []
    for prompt in prompts:
        text = prompt["text"]
        original_tokens = counter.count(text)
        original_words = len(text.split())
        record = {
            "id": prompt["id"],
            "domain": prompt["domain"],
            "original": text,
            "original_tokens": original_tokens,
            "original_words": original_words,
            "levels": {},
        }
        for level in LEVELS:
            result = compress(text, level, strip_filler=strip_filler)
            if result.compressed == text:
                compressed_tokens = original_tokens  # no change -> no extra call
            else:
                compressed_tokens = counter.count(result.compressed)
            compressed_words = len(result.compressed.split())
            token_ratio = (
                compressed_tokens / original_tokens
                if compressed_tokens is not None
                and original_tokens is not None
                and original_tokens > 0
                else None
            )
            record["levels"][level] = {
                "compressed": result.compressed,
                "markers_applied": result.markers_applied,
                "compressed_tokens": compressed_tokens,
                "token_ratio": token_ratio,
                "compressed_words": compressed_words,
                "word_ratio": (
                    compressed_words / original_words if original_words else None
                ),
            }
        rows.append(record)
    return rows


def summarize(rows: list[dict]) -> dict:
    summary: dict[str, dict] = {}
    for level in LEVELS:
        token_ratios = [
            r["levels"][level]["token_ratio"]
            for r in rows
            if r["levels"][level]["token_ratio"] is not None
        ]
        word_ratios = [
            r["levels"][level]["word_ratio"]
            for r in rows
            if r["levels"][level]["word_ratio"] is not None
        ]
        zero_marker = sum(
            1 for r in rows if not r["levels"][level]["markers_applied"]
        )
        orig_token_sum = sum(
            r["original_tokens"]
            for r in rows
            if r["original_tokens"] is not None
            and r["levels"][level]["compressed_tokens"] is not None
        )
        comp_token_sum = sum(
            r["levels"][level]["compressed_tokens"]
            for r in rows
            if r["original_tokens"] is not None
            and r["levels"][level]["compressed_tokens"] is not None
        )
        summary[level] = {
            "prompts": len(rows),
            "zero_marker_prompts": zero_marker,
            "prompts_with_token_counts": len(token_ratios),
            "mean_token_ratio": mean(token_ratios) if token_ratios else None,
            "min_token_ratio": min(token_ratios) if token_ratios else None,
            "max_token_ratio": max(token_ratios) if token_ratios else None,
            "corpus_token_ratio": (
                comp_token_sum / orig_token_sum if orig_token_sum else None
            ),
            "mean_word_ratio": mean(word_ratios) if word_ratios else None,
            "min_word_ratio": min(word_ratios) if word_ratios else None,
            "max_word_ratio": max(word_ratios) if word_ratios else None,
        }
    return summary


def _fmt(value: float | None, pattern: str = "{:.3f}") -> str:
    return pattern.format(value) if value is not None else "n/a"


def print_table(title: str, summary: dict, metric_prefix: str) -> None:
    print()
    print(title)
    header = (
        f"{'level':<8} {'prompts':>7} {'zero-marker':>11} "
        f"{'mean ratio':>10} {'min':>7} {'max':>7} {'mean savings':>12}"
    )
    print(header)
    print("-" * len(header))
    for level in LEVELS:
        s = summary[level]
        mean_ratio = s[f"mean_{metric_prefix}_ratio"]
        savings = (
            f"{(1 - mean_ratio) * 100:.1f}%" if mean_ratio is not None else "n/a"
        )
        print(
            f"{level:<8} {s['prompts']:>7} "
            f"{s['zero_marker_prompts']:>4}/{s['prompts']:<6} "
            f"{_fmt(mean_ratio):>10} "
            f"{_fmt(s[f'min_{metric_prefix}_ratio']):>7} "
            f"{_fmt(s[f'max_{metric_prefix}_ratio']):>7} "
            f"{savings:>12}"
        )
    print("(ratio = compressed/original; 1.000 = no change)")


def main(argv: list[str] | None = None) -> int:
    args = parse_args(argv)
    prompts = load_corpus()
    print(f"aave-compress eval harness")
    print(f"Corpus: {FIXTURES_PATH.relative_to(REPO_ROOT)} ({len(prompts)} prompts)")

    counter_fn, counter_label, counter_method = resolve_counter(args)
    counter = SafeCounter(counter_fn)
    tokens_available = counter_fn is not None
    if tokens_available:
        print(f"Token counting: {counter_label}")
    if args.strip_filler:
        print("Filler strip: ON (strip_fillers pass runs before AAVE markers)")

    rows = evaluate(prompts, counter, strip_filler=args.strip_filler)
    summary = summarize(rows)

    if tokens_available and not counter.failed:
        print_table(
            f"TOKEN COMPRESSION โ€” {counter_label}", summary, metric_prefix="token"
        )
    elif tokens_available and counter.failed:
        print()
        print(
            "Token counting failed partway through; per-prompt token counts "
            "are incomplete (see nulls in the results file)."
        )
        print_table(
            f"TOKEN COMPRESSION (PARTIAL) โ€” {counter_label}",
            summary,
            metric_prefix="token",
        )
    else:
        print()
        print(NO_TOKENS_BANNER)

    print_table(
        "WORD-LEVEL COMPRESSION (crude, tokenizer-independent; words != tokens)",
        summary,
        metric_prefix="word",
    )

    zero_full = summary["full"]["zero_marker_prompts"]
    print()
    print(
        f"Honesty check: {zero_full}/{len(prompts)} prompts had ZERO applicable "
        "markers even at level=full. That is expected on a realistic corpus โ€” "
        "do not cherry-pick them out when reporting numbers."
    )
    print(
        "Reminder: only quote ratios measured by this harness on this "
        "realistic corpus. Numbers measured against artificially verbose "
        "baselines are how the caveman tool's 65-75% claim became a real 14-21%."
    )

    RESULTS_DIR.mkdir(parents=True, exist_ok=True)
    output = {
        "generated_at": datetime.now(timezone.utc).isoformat(),
        "corpus": str(FIXTURES_PATH.relative_to(REPO_ROOT)),
        "n_prompts": len(prompts),
        "strip_filler": args.strip_filler,
        "token_counting": {
            "available": tokens_available and not counter.failed,
            "partial": tokens_available and counter.failed,
            "label": counter_label,
            "method": counter_method,
            "requested_model": args.model,
            "tiktoken_fallback_model": args.tiktoken_model,
        },
        "summary": summary,
        "prompts": rows,
    }
    results_path = RESULTS_DIR / "latest.json"
    with open(results_path, "w", encoding="utf-8") as fh:
        json.dump(output, fh, indent=2, ensure_ascii=False)
        fh.write("\n")
    print()
    print(f"Full per-prompt results written to {results_path.relative_to(REPO_ROOT)}")
    return 0


if __name__ == "__main__":
    sys.exit(main())