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.
Files changed (104) hide show
  1. inferencekit/__init__.py +9 -0
  2. inferencekit/__main__.py +5 -0
  3. inferencekit/analysis/__init__.py +1 -0
  4. inferencekit/analysis/ncu_csv.py +284 -0
  5. inferencekit/analysis/nsys_sqlite.py +518 -0
  6. inferencekit/canonical.py +104 -0
  7. inferencekit/checkpoint.py +396 -0
  8. inferencekit/cli.py +274 -0
  9. inferencekit/cuda/bw_peak.cu +252 -0
  10. inferencekit/cuda/torch_bw.py +60 -0
  11. inferencekit/engines/__init__.py +7 -0
  12. inferencekit/engines/adapters.py +190 -0
  13. inferencekit/engines/clip/__init__.py +13 -0
  14. inferencekit/engines/clip/comfyui.py +749 -0
  15. inferencekit/engines/clip/diffusers_ltx.py +601 -0
  16. inferencekit/engines/clip/noise.py +46 -0
  17. inferencekit/engines/clip/official_ltx.py +316 -0
  18. inferencekit/engines/clip/server.py +542 -0
  19. inferencekit/engines/clip/stepdump.py +182 -0
  20. inferencekit/engines/gates.py +94 -0
  21. inferencekit/engines/sglang_worker.py +86 -0
  22. inferencekit/engines/vllm_worker.py +129 -0
  23. inferencekit/evidence.py +427 -0
  24. inferencekit/gpuinfo.py +93 -0
  25. inferencekit/hardware.py +135 -0
  26. inferencekit/mcp_server.py +178 -0
  27. inferencekit/ops/__init__.py +30 -0
  28. inferencekit/ops/bench.py +184 -0
  29. inferencekit/ops/bench_audio.py +384 -0
  30. inferencekit/ops/bench_clip.py +819 -0
  31. inferencekit/ops/bench_image.py +393 -0
  32. inferencekit/ops/bench_rounds.py +297 -0
  33. inferencekit/ops/bench_serve.py +866 -0
  34. inferencekit/ops/ceiling.py +135 -0
  35. inferencekit/ops/compare.py +378 -0
  36. inferencekit/ops/correctness.py +153 -0
  37. inferencekit/ops/doctor.py +486 -0
  38. inferencekit/ops/gates.py +1772 -0
  39. inferencekit/ops/gates_clip.py +930 -0
  40. inferencekit/ops/intake.py +1321 -0
  41. inferencekit/ops/ledger.py +749 -0
  42. inferencekit/ops/manifest.py +309 -0
  43. inferencekit/ops/ncu.py +183 -0
  44. inferencekit/ops/nsys.py +224 -0
  45. inferencekit/ops/prompts.py +316 -0
  46. inferencekit/ops/quality.py +2161 -0
  47. inferencekit/ops/quality_clip.py +690 -0
  48. inferencekit/ops/quality_clip_steps.py +341 -0
  49. inferencekit/ops/report.py +317 -0
  50. inferencekit/ops/review.py +119 -0
  51. inferencekit/ops/rocprof.py +89 -0
  52. inferencekit/ops/roofline.py +392 -0
  53. inferencekit/ops/store.py +61 -0
  54. inferencekit/proc.py +172 -0
  55. inferencekit/prompts/ATTRIBUTION.md +56 -0
  56. inferencekit/prompts/__init__.py +167 -0
  57. inferencekit/prompts/build.py +518 -0
  58. inferencekit/prompts/dev-load.jsonl.gz +0 -0
  59. inferencekit/prompts/dev-load.manifest.json +212 -0
  60. inferencekit/prompts/dev.jsonl.gz +0 -0
  61. inferencekit/prompts/dev.manifest.json +214 -0
  62. inferencekit/prompts/exclusions.json +54 -0
  63. inferencekit/quality/__init__.py +23 -0
  64. inferencekit/quality/client.py +66 -0
  65. inferencekit/quality/data/gsm8k_LICENSE.txt +21 -0
  66. inferencekit/quality/fake.py +329 -0
  67. inferencekit/quality/gsm8k.py +224 -0
  68. inferencekit/quality/logprobs.py +106 -0
  69. inferencekit/quality/metrics.py +235 -0
  70. inferencekit/quality/niah.py +63 -0
  71. inferencekit/quality/parquet.py +296 -0
  72. inferencekit/quality/sampling.py +262 -0
  73. inferencekit/quality/step_replay.py +217 -0
  74. inferencekit/quality/video_metrics.py +304 -0
  75. inferencekit/registry.py +206 -0
  76. inferencekit/review/__init__.py +6 -0
  77. inferencekit/review/diff.py +163 -0
  78. inferencekit/review/media.py +181 -0
  79. inferencekit/review/page.py +920 -0
  80. inferencekit/review/rundir.py +844 -0
  81. inferencekit/roofline_headers.py +504 -0
  82. inferencekit/schemas/evidence.v1.json +130 -0
  83. inferencekit/schemas/evidence.v2.json +499 -0
  84. inferencekit/schemas/intake.v1.json +276 -0
  85. inferencekit/schemas/run-manifest.v1.json +118 -0
  86. inferencekit/serve/__init__.py +8 -0
  87. inferencekit/serve/audio_client.py +330 -0
  88. inferencekit/serve/client.py +446 -0
  89. inferencekit/serve/clip_client.py +351 -0
  90. inferencekit/serve/clip_rounds.py +429 -0
  91. inferencekit/serve/image_client.py +373 -0
  92. inferencekit/serve/loadgen.py +136 -0
  93. inferencekit/serve/metrics.py +203 -0
  94. inferencekit/serve/profiles.py +105 -0
  95. inferencekit/serve/prompts_io.py +125 -0
  96. inferencekit/serve/rounds.py +1111 -0
  97. inferencekit/serve/servers.py +897 -0
  98. inferencekit/stats.py +249 -0
  99. inferencekit_cli-0.1.0.dist-info/METADATA +233 -0
  100. inferencekit_cli-0.1.0.dist-info/RECORD +104 -0
  101. inferencekit_cli-0.1.0.dist-info/WHEEL +4 -0
  102. inferencekit_cli-0.1.0.dist-info/entry_points.txt +2 -0
  103. inferencekit_cli-0.1.0.dist-info/licenses/LICENSE +202 -0
  104. 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)}