#!/usr/bin/env python3 """What a task set's answers cost a model in tokens, written four ways. python3 -I token_cost.py answers.json --tokenizer NAME=MODEL.gguf [...] --tool PATH [--out FILE] answers.json is what `wai-quantum eval corpus` writes: each task's answer as plain OpenQASM (the program alone, in a fenced block), in the answer view layer (whole, and with the request alone), and as a job's JSON. For each tokenizer, the vocabulary of a model read from its GGUF file by a tokenizer tool (one that takes `-m MODEL -f FILE --ids --no-bos --log-disable` and prints the ids as a list), this counts the tokens of every answer in each form, and reports the totals, the per-answer means, each form against plain, and the bytes. A tokenizer's size is its vocabulary's, read from the GGUF file's tokenizer.ggml.tokens. Standard library only; it reads the model files and writes nothing but its report. """ import argparse import json import os import struct import subprocess import sys import tempfile FORMS = ["plain", "views", "views_request", "job"] def vocabulary(path): """The number of tokens in a GGUF file's tokenizer.ggml.tokens, or None.""" sizes = {0: 1, 1: 1, 2: 2, 3: 2, 4: 4, 5: 4, 6: 4, 7: 1, 10: 8, 11: 8, 12: 8} with open(path, "rb") as f: if f.read(4) != b"GGUF": return None (version,) = struct.unpack("