inferencekit-cli 0.1.0__py3-none-any.whl
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.
- inferencekit/__init__.py +9 -0
- inferencekit/__main__.py +5 -0
- inferencekit/analysis/__init__.py +1 -0
- inferencekit/analysis/ncu_csv.py +284 -0
- inferencekit/analysis/nsys_sqlite.py +518 -0
- inferencekit/canonical.py +104 -0
- inferencekit/checkpoint.py +396 -0
- inferencekit/cli.py +274 -0
- inferencekit/cuda/bw_peak.cu +252 -0
- inferencekit/cuda/torch_bw.py +60 -0
- inferencekit/engines/__init__.py +7 -0
- inferencekit/engines/adapters.py +190 -0
- inferencekit/engines/clip/__init__.py +13 -0
- inferencekit/engines/clip/comfyui.py +749 -0
- inferencekit/engines/clip/diffusers_ltx.py +601 -0
- inferencekit/engines/clip/noise.py +46 -0
- inferencekit/engines/clip/official_ltx.py +316 -0
- inferencekit/engines/clip/server.py +542 -0
- inferencekit/engines/clip/stepdump.py +182 -0
- inferencekit/engines/gates.py +94 -0
- inferencekit/engines/sglang_worker.py +86 -0
- inferencekit/engines/vllm_worker.py +129 -0
- inferencekit/evidence.py +427 -0
- inferencekit/gpuinfo.py +93 -0
- inferencekit/hardware.py +135 -0
- inferencekit/mcp_server.py +178 -0
- inferencekit/ops/__init__.py +30 -0
- inferencekit/ops/bench.py +184 -0
- inferencekit/ops/bench_audio.py +384 -0
- inferencekit/ops/bench_clip.py +819 -0
- inferencekit/ops/bench_image.py +393 -0
- inferencekit/ops/bench_rounds.py +297 -0
- inferencekit/ops/bench_serve.py +866 -0
- inferencekit/ops/ceiling.py +135 -0
- inferencekit/ops/compare.py +378 -0
- inferencekit/ops/correctness.py +153 -0
- inferencekit/ops/doctor.py +486 -0
- inferencekit/ops/gates.py +1772 -0
- inferencekit/ops/gates_clip.py +930 -0
- inferencekit/ops/intake.py +1321 -0
- inferencekit/ops/ledger.py +749 -0
- inferencekit/ops/manifest.py +309 -0
- inferencekit/ops/ncu.py +183 -0
- inferencekit/ops/nsys.py +224 -0
- inferencekit/ops/prompts.py +316 -0
- inferencekit/ops/quality.py +2161 -0
- inferencekit/ops/quality_clip.py +690 -0
- inferencekit/ops/quality_clip_steps.py +341 -0
- inferencekit/ops/report.py +317 -0
- inferencekit/ops/review.py +119 -0
- inferencekit/ops/rocprof.py +89 -0
- inferencekit/ops/roofline.py +392 -0
- inferencekit/ops/store.py +61 -0
- inferencekit/proc.py +172 -0
- inferencekit/prompts/ATTRIBUTION.md +56 -0
- inferencekit/prompts/__init__.py +167 -0
- inferencekit/prompts/build.py +518 -0
- inferencekit/prompts/dev-load.jsonl.gz +0 -0
- inferencekit/prompts/dev-load.manifest.json +212 -0
- inferencekit/prompts/dev.jsonl.gz +0 -0
- inferencekit/prompts/dev.manifest.json +214 -0
- inferencekit/prompts/exclusions.json +54 -0
- inferencekit/quality/__init__.py +23 -0
- inferencekit/quality/client.py +66 -0
- inferencekit/quality/data/gsm8k_LICENSE.txt +21 -0
- inferencekit/quality/fake.py +329 -0
- inferencekit/quality/gsm8k.py +224 -0
- inferencekit/quality/logprobs.py +106 -0
- inferencekit/quality/metrics.py +235 -0
- inferencekit/quality/niah.py +63 -0
- inferencekit/quality/parquet.py +296 -0
- inferencekit/quality/sampling.py +262 -0
- inferencekit/quality/step_replay.py +217 -0
- inferencekit/quality/video_metrics.py +304 -0
- inferencekit/registry.py +206 -0
- inferencekit/review/__init__.py +6 -0
- inferencekit/review/diff.py +163 -0
- inferencekit/review/media.py +181 -0
- inferencekit/review/page.py +920 -0
- inferencekit/review/rundir.py +844 -0
- inferencekit/roofline_headers.py +504 -0
- inferencekit/schemas/evidence.v1.json +130 -0
- inferencekit/schemas/evidence.v2.json +499 -0
- inferencekit/schemas/intake.v1.json +276 -0
- inferencekit/schemas/run-manifest.v1.json +118 -0
- inferencekit/serve/__init__.py +8 -0
- inferencekit/serve/audio_client.py +330 -0
- inferencekit/serve/client.py +446 -0
- inferencekit/serve/clip_client.py +351 -0
- inferencekit/serve/clip_rounds.py +429 -0
- inferencekit/serve/image_client.py +373 -0
- inferencekit/serve/loadgen.py +136 -0
- inferencekit/serve/metrics.py +203 -0
- inferencekit/serve/profiles.py +105 -0
- inferencekit/serve/prompts_io.py +125 -0
- inferencekit/serve/rounds.py +1111 -0
- inferencekit/serve/servers.py +897 -0
- inferencekit/stats.py +249 -0
- inferencekit_cli-0.1.0.dist-info/METADATA +233 -0
- inferencekit_cli-0.1.0.dist-info/RECORD +104 -0
- inferencekit_cli-0.1.0.dist-info/WHEEL +4 -0
- inferencekit_cli-0.1.0.dist-info/entry_points.txt +2 -0
- inferencekit_cli-0.1.0.dist-info/licenses/LICENSE +202 -0
- inferencekit_cli-0.1.0.dist-info/licenses/NOTICE +9 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Prompt set attribution
|
|
2
|
+
|
|
3
|
+
The prompt sets in this directory (`dev.jsonl.gz`, `dev-load.jsonl.gz`: gzipped JSONL) are
|
|
4
|
+
built by `scripts/build_prompts.py` from the openly licensed sources below. Every item records
|
|
5
|
+
its source and licence in its `source` field (`<dataset>/<licence>`) and the exact origin in
|
|
6
|
+
`source_ref`. Each manifest (`*.manifest.json`) lists the source URLs and the sha256 of the
|
|
7
|
+
bytes the set was built from; its own `sha256` is that of the uncompressed JSONL
|
|
8
|
+
(`gunzip -c dev.jsonl.gz | sha256sum`).
|
|
9
|
+
|
|
10
|
+
| Source | Used for | Licence |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| [databricks-dolly-15k](https://huggingface.co/datasets/databricks/databricks-dolly-15k) (Databricks, Inc.; revision `bdd27f4d94b9`) | very_short, short (instructions); medium (context paragraph + instruction) | [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/) |
|
|
13
|
+
| [GSM8K](https://github.com/openai/grade-school-math) train split (OpenAI; commit `3101c7d50724`) | short (questions only) | [MIT](https://github.com/openai/grade-school-math/blob/master/LICENSE) |
|
|
14
|
+
| Public-domain novels, plain-text editions from [Project Gutenberg](https://www.gutenberg.org/) (titles in the manifests) | long (passages of ~1,700-2,600 words plus a question) | Public domain in the USA |
|
|
15
|
+
|
|
16
|
+
## Changes made
|
|
17
|
+
- dolly: whitespace normalised; an item's context paragraph(s) and instruction joined as one
|
|
18
|
+
user message; responses not used.
|
|
19
|
+
- GSM8K: whitespace normalised; answers not used.
|
|
20
|
+
- Novels: the distributor's licence header and footer removed; paragraphs re-flowed; a
|
|
21
|
+
contiguous passage selected and followed by a question written for this set.
|
|
22
|
+
|
|
23
|
+
## Share-alike
|
|
24
|
+
Prompts derived from databricks-dolly-15k are adaptations of a CC BY-SA 3.0 work and are
|
|
25
|
+
distributed under the same licence (CC BY-SA 3.0). That covers the dolly-derived lines of
|
|
26
|
+
`dev.jsonl.gz` and `dev-load.jsonl.gz` (their `source` field starts with `databricks-dolly-15k`),
|
|
27
|
+
not the inferencekit code, which is Apache-2.0. Items from GSM8K keep their MIT licence
|
|
28
|
+
(copyright (c) 2021 OpenAI; full text in `../quality/data/gsm8k_LICENSE.txt`). The novels are in the public domain in the USA; check your
|
|
29
|
+
local law before redistributing them elsewhere.
|
|
30
|
+
|
|
31
|
+
## Lengths
|
|
32
|
+
Buckets are measured with a tokenizer-free proxy, `proxy_tokens = whitespace words * 1.3`:
|
|
33
|
+
very_short 1-5 words; short 20-50; medium 240-360; long 2,000-4,000 proxy tokens. For
|
|
34
|
+
English prose the real token count of a 30k-150k-vocabulary BPE tokenizer is usually within
|
|
35
|
+
about 15% of the proxy.
|
|
36
|
+
|
|
37
|
+
## Exclusions
|
|
38
|
+
A few source rows are left out because their text would trip the release leak scanner.
|
|
39
|
+
`exclusions.json` lists them by position only (dolly or GSM8K row, or novel and passage), keyed
|
|
40
|
+
by the sha256 of the source file, so a rebuild from the same source bytes gives the same sets.
|
|
41
|
+
|
|
42
|
+
## Sealed set
|
|
43
|
+
A sealed set is built by the same code (`inferencekit prompts seal`) from source groups the dev
|
|
44
|
+
sets do not use. A group is one dolly context paragraph (every row asking about that paragraph),
|
|
45
|
+
one instruction or question, or one novel, so the sealed set shares no prompt, no context
|
|
46
|
+
paragraph and no novel with the dev sets; a sealed prompt that shares more than half of its
|
|
47
|
+
12-word sequences with a dev prompt is dropped as well. Which groups are chosen depends on a
|
|
48
|
+
private random secret, so the sealed set cannot be rebuilt from this repository alone. It is not
|
|
49
|
+
distributed here. Its sha256 and the secret's `secret_sha256` are published ahead of the
|
|
50
|
+
evaluation, and the secret file is revealed afterwards so anyone can rebuild the set and check
|
|
51
|
+
the hashes.
|
|
52
|
+
|
|
53
|
+
`secret_sha256` is the sha256 of the secret file's bytes as `prompts seal` writes it (64 hex
|
|
54
|
+
characters and a newline): `sha256sum SEED.secret`, or `echo <secret> | sha256sum`.
|
|
55
|
+
`inferencekit prompts verify --set sealed.jsonl --secret-file SEED.secret --sources DIR` checks
|
|
56
|
+
that hash and rebuilds the set to compare it with the published sha256.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""Versioned prompt sets: the inputs every engine sees, identical for all of them.
|
|
2
|
+
|
|
3
|
+
A prompt set is a JSONL file (one request per line), optionally gzipped (``<set>.jsonl.gz``;
|
|
4
|
+
every reader takes either, detected by content), plus ``<set>.manifest.json``:
|
|
5
|
+
|
|
6
|
+
{"id": "short-0007", "bucket": "short", "messages": [{"role": "user", "content": "..."}],
|
|
7
|
+
"max_tokens": 256, "source": "databricks-dolly-15k/CC-BY-SA-3.0", "kind": "chat", ...}
|
|
8
|
+
|
|
9
|
+
{"name": "dev", "version": "1.0", "n": 64, "buckets": {"short": 16, ...},
|
|
10
|
+
"sha256": "<sha256 of the JSONL bytes>", "sealed": false, ...}
|
|
11
|
+
|
|
12
|
+
Two sets ship with the package, gzipped: ``dev`` (16 prompts per bucket, single-user runs)
|
|
13
|
+
and ``dev-load`` (1,200+ short and medium prompts, so a load test never repeats a prompt
|
|
14
|
+
inside a level). A set's sha256 is always that of the uncompressed JSONL bytes, so a
|
|
15
|
+
gzipped and a plain copy of one set have the same identity. A ``sealed`` set is built by the same code from *different* source
|
|
16
|
+
items and never ships; only its sha256 is published ahead of time.
|
|
17
|
+
|
|
18
|
+
Lengths are measured with a tokenizer-free proxy, ``proxy_tokens = words * 1.3``
|
|
19
|
+
(whitespace-separated words), so bucket membership does not depend on any one model's
|
|
20
|
+
tokenizer. For English prose, 1.3 tokens per word is the usual rule of thumb for
|
|
21
|
+
BPE tokenizers with 30k-150k vocabularies; the true count for a given model differs by
|
|
22
|
+
roughly +/-15%, which the bucket ranges absorb.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import gzip
|
|
28
|
+
import hashlib
|
|
29
|
+
import json
|
|
30
|
+
import zlib
|
|
31
|
+
from importlib import resources
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
|
|
34
|
+
# Bucket rules. very_short is defined in words (1-5), the others in proxy tokens.
|
|
35
|
+
BUCKETS = ("very_short", "short", "medium", "long")
|
|
36
|
+
PROXY_FACTOR = 1.3
|
|
37
|
+
BUCKET_RULES = {
|
|
38
|
+
"very_short": {"words": [1, 5]},
|
|
39
|
+
"short": {"proxy_tokens": [20, 50]},
|
|
40
|
+
"medium": {"proxy_tokens": [240, 360]},
|
|
41
|
+
"long": {"proxy_tokens": [2000, 4000]},
|
|
42
|
+
}
|
|
43
|
+
SHIPPED = ("dev", "dev-load")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def words(text: str) -> int:
|
|
47
|
+
return len(text.split())
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def proxy_tokens(text: str) -> int:
|
|
51
|
+
"""Tokenizer-free length estimate: whitespace words * 1.3, rounded."""
|
|
52
|
+
return round(words(text) * PROXY_FACTOR)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def prompt_text(item: dict) -> str:
|
|
56
|
+
"""All message contents of an item joined; what the length rules are applied to."""
|
|
57
|
+
return "\n".join(str(m.get("content", "")) for m in item.get("messages", []))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def in_bucket(text: str, bucket: str) -> bool:
|
|
61
|
+
rule = BUCKET_RULES[bucket]
|
|
62
|
+
if "words" in rule:
|
|
63
|
+
lo, hi = rule["words"]
|
|
64
|
+
return lo <= words(text) <= hi
|
|
65
|
+
lo, hi = rule["proxy_tokens"]
|
|
66
|
+
return lo <= words(text) * PROXY_FACTOR <= hi
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def data_dir() -> Path:
|
|
70
|
+
"""Directory holding the shipped sets (inside the wheel or the source checkout)."""
|
|
71
|
+
try:
|
|
72
|
+
d = resources.files("inferencekit").joinpath("prompts")
|
|
73
|
+
if d.is_dir():
|
|
74
|
+
return Path(str(d))
|
|
75
|
+
except (ModuleNotFoundError, FileNotFoundError): # pragma: no cover
|
|
76
|
+
pass
|
|
77
|
+
return Path(__file__).resolve().parent
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
GZIP_MAGIC = b"\x1f\x8b"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def existing(path: str | Path) -> Path:
|
|
84
|
+
"""``path``, or its gzipped / plain twin when only that exists (``x.jsonl`` <-> ``x.jsonl.gz``)."""
|
|
85
|
+
p = Path(path)
|
|
86
|
+
if p.exists():
|
|
87
|
+
return p
|
|
88
|
+
twin = p.with_name(p.name + ".gz") if p.name.endswith(".jsonl") else \
|
|
89
|
+
p.with_name(p.name[:-3]) if p.name.endswith(".jsonl.gz") else None
|
|
90
|
+
return twin if twin is not None and twin.is_file() else p
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def read_bytes(path: str | Path) -> bytes:
|
|
94
|
+
"""The JSONL bytes of a prompt-set file: gunzipped when the file is gzip (by content, not name).
|
|
95
|
+
|
|
96
|
+
A damaged gzip file (bad header, corrupt deflate stream, truncated, wrong CRC or length)
|
|
97
|
+
raises ``ValueError("corrupt gzip (...)")`` (callers add the path); a missing file raises ``OSError``."""
|
|
98
|
+
raw = Path(path).read_bytes()
|
|
99
|
+
if raw[:2] != GZIP_MAGIC:
|
|
100
|
+
return raw
|
|
101
|
+
try:
|
|
102
|
+
return gzip.decompress(raw)
|
|
103
|
+
except (zlib.error, EOFError, gzip.BadGzipFile) as e:
|
|
104
|
+
raise ValueError(f"corrupt gzip ({e})") from None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def read_text(path: str | Path) -> str:
|
|
108
|
+
return read_bytes(path).decode("utf-8")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def gzip_bytes(data: bytes) -> bytes:
|
|
112
|
+
"""Deterministic gzip (no file name, mtime 0) of JSONL bytes."""
|
|
113
|
+
return gzip.compress(data, compresslevel=9, mtime=0)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def set_stem(path: str | Path) -> str:
|
|
117
|
+
"""File name without ``.jsonl.gz`` / ``.jsonl`` / ``.gz``: ``dev.jsonl.gz`` -> ``dev``."""
|
|
118
|
+
name = Path(path).name
|
|
119
|
+
for suffix in (".jsonl.gz", ".jsonl", ".gz"):
|
|
120
|
+
if name.endswith(suffix) and len(name) > len(suffix):
|
|
121
|
+
return name[: -len(suffix)]
|
|
122
|
+
return Path(name).stem
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def resolve(name_or_path: str) -> Path:
|
|
126
|
+
"""'dev' / 'dev-load' -> the shipped set (``.jsonl.gz``, or ``.jsonl`` in a rebuilt checkout);
|
|
127
|
+
anything else is a path to a ``.jsonl`` or ``.jsonl.gz`` file."""
|
|
128
|
+
if name_or_path in SHIPPED:
|
|
129
|
+
d = data_dir()
|
|
130
|
+
gz = d / f"{name_or_path}.jsonl.gz"
|
|
131
|
+
return gz if gz.is_file() else d / f"{name_or_path}.jsonl"
|
|
132
|
+
p = existing(name_or_path)
|
|
133
|
+
if p.is_dir():
|
|
134
|
+
raise ValueError(f"{p} is a directory; pass the .jsonl file")
|
|
135
|
+
return p
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def manifest_path(jsonl: Path) -> Path:
|
|
139
|
+
return Path(jsonl).with_name(set_stem(jsonl) + ".manifest.json")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def sha256_file(path: Path) -> str:
|
|
143
|
+
"""sha256 of the file's JSONL bytes (after gunzip for a ``.jsonl.gz``): the set's identity."""
|
|
144
|
+
return hashlib.sha256(read_bytes(path)).hexdigest()
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def load(name_or_path: str, buckets: list[str] | tuple[str, ...] | None = None) -> list[dict]:
|
|
148
|
+
"""Items of a prompt set, optionally only some buckets, in file order."""
|
|
149
|
+
path = resolve(name_or_path)
|
|
150
|
+
items = [json.loads(line) for line in read_text(path).splitlines() if line.strip()]
|
|
151
|
+
if buckets:
|
|
152
|
+
items = [it for it in items if it.get("bucket") in buckets]
|
|
153
|
+
return items
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def load_manifest(name_or_path: str) -> dict:
|
|
157
|
+
return json.loads(manifest_path(resolve(name_or_path)).read_text(encoding="utf-8"))
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def identity(name_or_path: str) -> dict:
|
|
161
|
+
"""``{name, sha256}`` for the evidence workload (sha256 of the bytes actually read)."""
|
|
162
|
+
path = resolve(name_or_path)
|
|
163
|
+
try:
|
|
164
|
+
name = load_manifest(name_or_path).get("name") or path.stem
|
|
165
|
+
except (OSError, ValueError):
|
|
166
|
+
name = set_stem(path)
|
|
167
|
+
return {"name": name, "sha256": sha256_file(path)}
|