logitgate 0.2.1__tar.gz → 0.3.0__tar.gz
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.
- {logitgate-0.2.1/src/logitgate.egg-info → logitgate-0.3.0}/PKG-INFO +139 -32
- {logitgate-0.2.1 → logitgate-0.3.0}/README.md +138 -29
- {logitgate-0.2.1 → logitgate-0.3.0}/pyproject.toml +3 -3
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/__init__.py +83 -8
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/backends/__init__.py +8 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/backends/base.py +123 -29
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/backends/dory_pytorch.py +4 -3
- logitgate-0.3.0/src/logitgate/backends/dory_recurrent.py +113 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/backends/llamacpp.py +8 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/backends/ollama.py +71 -85
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/backends/openai_compat.py +17 -0
- logitgate-0.3.0/src/logitgate/backends/pytorch_native.py +198 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/backends/recurrent.py +8 -2
- logitgate-0.3.0/src/logitgate/benchmark.py +376 -0
- logitgate-0.3.0/src/logitgate/calibration/__init__.py +11 -0
- logitgate-0.3.0/src/logitgate/calibration/conformal.py +334 -0
- logitgate-0.3.0/src/logitgate/cascade.py +232 -0
- logitgate-0.3.0/src/logitgate/cli.py +681 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/config.py +39 -1
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/debiasing/__init__.py +10 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/debiasing/inversion.py +29 -4
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/debiasing/permutation.py +178 -0
- logitgate-0.3.0/src/logitgate/exceptions.py +54 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/gates/__init__.py +4 -2
- logitgate-0.3.0/src/logitgate/gates/ast_gate.py +451 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/gates/base.py +48 -1
- logitgate-0.3.0/src/logitgate/gates/judge_gate.py +324 -0
- logitgate-0.3.0/src/logitgate/gates/logit_gate.py +804 -0
- logitgate-0.3.0/src/logitgate/hardware/__init__.py +9 -0
- logitgate-0.3.0/src/logitgate/hardware/memory_guard.py +82 -0
- logitgate-0.3.0/src/logitgate/integrations/__init__.py +49 -0
- logitgate-0.3.0/src/logitgate/integrations/langgraph.py +86 -0
- logitgate-0.3.0/src/logitgate/integrations/pytest_plugin.py +143 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/models.py +23 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/snapshot.py +107 -0
- {logitgate-0.2.1 → logitgate-0.3.0/src/logitgate.egg-info}/PKG-INFO +139 -32
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate.egg-info/SOURCES.txt +12 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/tests/test_ast_gate.py +59 -1
- {logitgate-0.2.1 → logitgate-0.3.0}/tests/test_backends_and_cli.py +357 -1
- logitgate-0.3.0/tests/test_calibration.py +151 -0
- logitgate-0.3.0/tests/test_cascade_routing.py +303 -0
- logitgate-0.3.0/tests/test_conformal_guarantees.py +64 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/tests/test_debiasing_math.py +85 -1
- logitgate-0.3.0/tests/test_dx_and_tooling.py +407 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/tests/test_logit_gate.py +21 -0
- logitgate-0.3.0/tests/test_multi_rubric_gate.py +282 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/tests/test_usability_and_validation.py +147 -0
- logitgate-0.2.1/src/logitgate/cascade.py +0 -579
- logitgate-0.2.1/src/logitgate/cli.py +0 -264
- logitgate-0.2.1/src/logitgate/exceptions.py +0 -29
- logitgate-0.2.1/src/logitgate/gates/ast_gate.py +0 -560
- logitgate-0.2.1/src/logitgate/gates/judge_gate.py +0 -320
- logitgate-0.2.1/src/logitgate/gates/logit_gate.py +0 -985
- logitgate-0.2.1/src/logitgate/integrations/__init__.py +0 -10
- logitgate-0.2.1/src/logitgate/integrations/langgraph.py +0 -30
- logitgate-0.2.1/src/logitgate/integrations/pytest_plugin.py +0 -68
- {logitgate-0.2.1 → logitgate-0.3.0}/LICENSE +0 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/setup.cfg +0 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/debiasing/softmax.py +0 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/integrations/deepeval.py +0 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate/py.typed +0 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate.egg-info/dependency_links.txt +0 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate.egg-info/entry_points.txt +0 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate.egg-info/requires.txt +0 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/src/logitgate.egg-info/top_level.txt +0 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/tests/test_cascade.py +0 -0
- {logitgate-0.2.1 → logitgate-0.3.0}/tests/test_integrations.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: logitgate
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Fast, calibrated first-token logit evaluation and 3-layer cascaded routing for LLMs.
|
|
5
5
|
Author-email: Jason Bradley <jasonbdly@gmail.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -16,8 +16,6 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.15
|
|
21
19
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
20
|
Requires-Python: >=3.10
|
|
23
21
|
Description-Content-Type: text/markdown
|
|
@@ -65,28 +63,36 @@ Dynamic: license-file
|
|
|
65
63
|
|
|
66
64
|
**Fast, calibrated first-token logit evaluation and 3-layer cascaded routing for LLMs.**
|
|
67
65
|
|
|
68
|
-
|
|
66
|
+
LLM-as-a-judge asks a model to write a critique and then a verdict. That costs a full autoregressive generation per check: 1.5–6 s and a few hundred tokens on our hardware. LogitGate reads the verdict straight from the **first-token log-probabilities** (`max_tokens=1`, 0 generated tokens), cancels the model's label-order prior with a forward + reverse pass, and only falls back to a generative judge when that read is genuinely ambiguous.
|
|
67
|
+
|
|
68
|
+
Measured on the repo's 24-case multi-domain suite (RTX 4070, Ollama, [full methodology](docs/benchmarks.md)): **36–41 ms** single-pass and **90–113 ms** debiased per check. End-to-end, the cascade ran **10.6x–17x** faster than a `think=False` generative judge, at 23/24 accuracy versus the judge's 23–24/24. On pure CPU the gap shrinks to 1.4x, because prefill dominates there. Don't take our word for it: [`logitgate benchmark`](#reproduce-the-numbers-on-your-hardware) reruns the comparison on your model in about a minute.
|
|
69
69
|
|
|
70
70
|
---
|
|
71
71
|
|
|
72
72
|
## How It Works in 60 Seconds
|
|
73
73
|
|
|
74
|
-
When evaluating whether an LLM output satisfies a rubric, traditional frameworks prompt a model to generate a 200–500 word critique before outputting `PASS` or `FAIL`.
|
|
74
|
+
When evaluating whether an LLM output satisfies a rubric, traditional frameworks prompt a model to generate a 200–500 word critique before outputting `PASS` or `FAIL`. That takes **seconds per check**, consumes hundreds of tokens, and is sensitive to label order and position.
|
|
75
75
|
|
|
76
76
|
`LogitGate` uses a **3-layer cascaded pipeline**:
|
|
77
77
|
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
78
|
+
```
|
|
79
|
+
Candidate Artifact
|
|
80
|
+
│
|
|
81
|
+
▼
|
|
82
|
+
┌──────────────────────────────────────┐
|
|
83
|
+
│ Layer 0: ASTGate / ContractGate ├──► [FAIL: syntax/schema, <0.1ms]
|
|
84
|
+
└──────────────────┬───────────────────┘
|
|
85
|
+
│ Passed
|
|
86
|
+
▼
|
|
87
|
+
┌──────────────────────────────────────┐
|
|
88
|
+
│ Layer 1: LogitGate ├──► [PASS: P >= 0.85, ~40–110ms]
|
|
89
|
+
└──────────────────┬───────────────────┤
|
|
90
|
+
│ Ambiguous └──► [FAIL: P <= 0.20, ~40–110ms]
|
|
91
|
+
│ (0.20 < P < 0.85)
|
|
92
|
+
▼
|
|
93
|
+
┌──────────────────────────────────────┐
|
|
94
|
+
│ Layer 2: JudgeGate ├──► [Actionable Diagnostic Report]
|
|
95
|
+
└──────────────────────────────────────┘
|
|
90
96
|
```
|
|
91
97
|
|
|
92
98
|
1. **Layer 0 (`ASTGate` / `ContractGate`)**: Deterministic checks in **<0.1 ms** on CPU (Python AST syntax, JSON/Pydantic schemas, regex patterns, acyclic DAG topology). Failures reject immediately with zero LLM overhead.
|
|
@@ -98,7 +104,7 @@ flowchart TD
|
|
|
98
104
|
## Installation
|
|
99
105
|
|
|
100
106
|
```bash
|
|
101
|
-
# Core package
|
|
107
|
+
# Core package: only pydantic + httpx
|
|
102
108
|
pip install logitgate
|
|
103
109
|
|
|
104
110
|
# With optional framework integrations:
|
|
@@ -112,6 +118,28 @@ pip install "logitgate[all]" # All integrations
|
|
|
112
118
|
|
|
113
119
|
---
|
|
114
120
|
|
|
121
|
+
## Pick a Backend (10 seconds)
|
|
122
|
+
|
|
123
|
+
LogitGate needs an endpoint that returns **token log-probabilities**. Anything below works; a gate's `backend=` argument overrides the global default.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
export LOGITGATE_BACKEND="ollama://localhost:11434/qwen2.5:7b" # Ollama
|
|
127
|
+
export LOGITGATE_BACKEND="vllm://localhost:8000/v1" # vLLM / any OpenAI-compatible server
|
|
128
|
+
export LOGITGATE_BACKEND="llamacpp://localhost:8080" # llama-server
|
|
129
|
+
export LOGITGATE_BACKEND="stub://" # offline stub for CI plumbing (fixed P=0.95)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
With nothing configured, LogitGate falls back to the offline stub and emits a one-time `StubBackendWarning`, so a fake `P=0.95` never passes for a real result. Errors say what to do next instead of dumping an `httpx` traceback:
|
|
133
|
+
|
|
134
|
+
```text
|
|
135
|
+
logitgate.exceptions.BackendUnavailableError: Cannot connect to Ollama at http://localhost:11434.
|
|
136
|
+
Is ollama running? (start it with `ollama serve`) Or set backend='stub://' for offline testing.
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
A missing Ollama model suggests the exact `ollama pull <model>` command, an OpenAI 401 points at `OPENAI_API_KEY`, and a cold-model timeout says so. `BackendUnavailableError` is also a `ConnectionError`, so existing retry logic keeps working.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
115
143
|
## 30-Second Quickstart
|
|
116
144
|
|
|
117
145
|
### 1. Single Atomic Rubric (3-Layer Cascaded Fast Path)
|
|
@@ -153,26 +181,28 @@ print(f"Tokens Generated: {verdict.total_tokens_generated}") # 0 tokens!
|
|
|
153
181
|
|
|
154
182
|
### 2. Multi-Rubric Batching (Prefix-Preserved KV-Cache Reuse)
|
|
155
183
|
|
|
156
|
-
When verifying multiple criteria against a candidate artifact, **do not combine them into a single monolithic compound rubric** (which dilutes logit signals and conflates orthogonal requirements). Instead, evaluate isolated atomic rubrics
|
|
184
|
+
When verifying multiple criteria against a candidate artifact, **do not combine them into a single monolithic compound rubric** (which dilutes logit signals and conflates orthogonal requirements). Instead, evaluate isolated atomic rubrics with `MultiRubricGate`.
|
|
157
185
|
|
|
158
|
-
The candidate code is anchored at Token 0, sharing 95–99% of prefix tokens in KV
|
|
186
|
+
The candidate code is anchored at Token 0, sharing 95–99% of prefix tokens in KV-cache across all checks. `MultiRubricGate` enforces weakest-link aggregation: passes only if *every* atomic rubric passes, and records per-rubric traces so diagnostics show exactly which requirement failed:
|
|
159
187
|
|
|
160
188
|
```python
|
|
161
|
-
from logitgate import
|
|
189
|
+
from logitgate import MultiRubricGate
|
|
162
190
|
|
|
163
|
-
# Best Practice: Keep rubrics atomic — evaluate multiple criteria via
|
|
191
|
+
# Best Practice: Keep rubrics atomic — evaluate multiple criteria via MultiRubricGate
|
|
164
192
|
rubrics = [
|
|
165
193
|
"Does the function calculate Fibonacci numbers recursively?",
|
|
166
194
|
"Does the function explicitly handle non-positive inputs (n <= 0)?",
|
|
167
195
|
"Does the function include parameter and return type annotations?",
|
|
168
196
|
]
|
|
169
197
|
|
|
170
|
-
gate =
|
|
198
|
+
gate = MultiRubricGate(rubrics)
|
|
171
199
|
|
|
172
200
|
# Evaluates all 3 rubrics in a single batch reusing the shared KV-cache prefix
|
|
173
|
-
|
|
201
|
+
verdict = gate(candidate_code)
|
|
202
|
+
print(f"Passed: {verdict.passed} | {verdict.reason}")
|
|
174
203
|
|
|
175
|
-
|
|
204
|
+
# Or inspect individual per-rubric verdicts:
|
|
205
|
+
for rubric, v in zip(rubrics, gate.evaluate_each(candidate_code)):
|
|
176
206
|
status = "PASS" if v.passed else "FAIL"
|
|
177
207
|
print(f"[{status}] P={v.probability:.4f} | {rubric}")
|
|
178
208
|
```
|
|
@@ -186,6 +216,7 @@ for rubric, v in zip(rubrics, verdicts):
|
|
|
186
216
|
| **`ASTGate`** | Layer 0 | **< 0.1 ms** | 0 | Sub-millisecond deterministic checks: Python syntax, forbidden imports, regex, acyclic DAGs, custom callables. |
|
|
187
217
|
| **`ContractGate`** | Layer 0 | **< 0.1 ms** | 0 | General-domain contract validation: Pydantic models, JSON Schemas, length bounds, required sections. |
|
|
188
218
|
| **`LogitGate`** | Layer 1 | **~40–110 ms** | 0 | Calibrated binary classification from first-token logits using symmetric log-odds debiasing ($\bar{L} = \frac{L_1 + L_2}{2T}$). |
|
|
219
|
+
| **`MultiRubricGate`** | Layer 1 | **~50–140 ms** | 0 | Atomic multi-rubric verification sharing KV prefix. Weakest-link aggregation (fails if any criterion fails). |
|
|
189
220
|
| **`ChoiceGate`** | Layer 1 | **~40–120 ms** | 0 | Discrete K-ary routing and intent classification across arbitrary string options with permutation averaging. |
|
|
190
221
|
| **`ScoreGate`** | Layer 1 | **~50–160 ms** | 0 | Continuous probability-weighted rubric scoring (𝔼[S] = Σ s · P(s)) with inverted scale debiasing. |
|
|
191
222
|
| **`JudgeGate`** | Layer 2 | **1.5–5.0 s** | 60–300 | Generative System-2 reasoning judge with reason-then-verdict parsing and self-healing 1-token logit probes. |
|
|
@@ -195,6 +226,73 @@ for rubric, v in zip(rubrics, verdicts):
|
|
|
195
226
|
|
|
196
227
|
---
|
|
197
228
|
|
|
229
|
+
## Where LogitGate Fits (and Where It Doesn't)
|
|
230
|
+
|
|
231
|
+
| Tool | What it does | How LogitGate relates |
|
|
232
|
+
| :--- | :--- | :--- |
|
|
233
|
+
| **DeepEval / G-Eval** | Offline evaluation suites and CI scoring; G-Eval weights score tokens by probability. | Complementary. LogitGate is the *inline* path: a sub-100 ms check you can put between agent turns. It ships a DeepEval integration (`LogitGateMetric`) for suites. |
|
|
234
|
+
| **RouteLLM** | Picks *which model generates* a response, before generation. | Different stage. LogitGate judges the output *after* generation. `ChoiceGate` can route, but on content, not on model cost. |
|
|
235
|
+
| **Flow-Judge, Lynx, Prometheus 2** | Dedicated fine-tuned judge models that reason autoregressively. | LogitGate is non-autoregressive and model-agnostic. It projects a verdict from the first-token logits of a model you already serve, with no extra VRAM. A fine-tuned judge works fine as the Layer-2 `JudgeGate` backend. |
|
|
236
|
+
|
|
237
|
+
**When not to use it:**
|
|
238
|
+
|
|
239
|
+
- **No logprobs, no LogitGate.** APIs that don't expose `top_logprobs` (e.g. Anthropic) can't host Layer 1. OpenAI caps `top_logprobs` at 20, which is enough for binary and small-K gates.
|
|
240
|
+
- **Multi-step reasoning rubrics** ("is this 12-step derivation correct?") exceed what a single forward pass can judge. Accuracy drops on our reasoning-depth suite ([falsification boundaries](docs/benchmarks.md)). Let those escalate to `JudgeGate`, or decompose them into atomic rubrics.
|
|
241
|
+
- **Tiny models on CPU.** 1–1.5B models are fast but reach 58–63% cascade accuracy on our multi-domain suite. Measure before you trust them.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Reproduce the Numbers on Your Hardware
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
logitgate benchmark --backend ollama://localhost:11434 --model qwen2.5:7b
|
|
249
|
+
logitgate benchmark --backend vllm://localhost:8000/v1 --model Qwen/Qwen2.5-7B-Instruct \
|
|
250
|
+
--dataset benchmarks/data/multi_domain_24.json --repeat 3 --json > results.json
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
The command runs the same cases through three configurations and reports p50/p95 latency, generated tokens, LLM calls per case, accuracy, and the cascade's fast-path rate:
|
|
254
|
+
|
|
255
|
+
1. A generative `JudgeGate` alone (`think=False` by default, so the baseline is a *fast* judge)
|
|
256
|
+
2. A single `LogitGate`
|
|
257
|
+
3. `LogitGate >> JudgeGate` (`on_ambiguous_only`)
|
|
258
|
+
|
|
259
|
+
Modes run as separate passes after an untimed warm-up, so model load time isn't charged to whichever mode runs first. `--json` records the LogitGate version, backend, model, platform, and a dataset hash, so a result can be traced to exactly what produced it. With the debiased default, LogitGate makes **two** forward passes per check, and the report counts both.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Custom Gates & Routing Traits
|
|
264
|
+
|
|
265
|
+
`Cascade` never checks concrete classes. It routes on three traits declared on every gate, so wrappers, decorators, and your own gates get the same short-circuiting as the built-ins:
|
|
266
|
+
|
|
267
|
+
| Trait | Meaning | Built-ins |
|
|
268
|
+
| :--- | :--- | :--- |
|
|
269
|
+
| `layer_tier` | `0` deterministic (failures are final), `1` single forward pass (failures may escalate), `2` generative | `ASTGate`=0, `LogitGate`/`ChoiceGate`/`ScoreGate`=1, `JudgeGate`=2 |
|
|
270
|
+
| `can_fast_path` | A PASSED verdict skips the next generative gate | `False` for `ASTGate` (a valid schema says nothing about semantics) |
|
|
271
|
+
| `escalation_policy` | When a tier-2 gate runs: `on_ambiguous_or_fail`, `on_ambiguous_only`, `always` | `JudgeGate(trigger=...)` |
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
from logitgate import BaseGate, DecisionStatus, JudgeGate, LayerTrace, Verdict
|
|
275
|
+
|
|
276
|
+
class RegexPIIGate(BaseGate):
|
|
277
|
+
"""Deterministic: a hit is a decisive FAIL; a miss proves nothing semantic."""
|
|
278
|
+
|
|
279
|
+
name = "RegexPII"
|
|
280
|
+
layer_tier = 0
|
|
281
|
+
can_fast_path = False
|
|
282
|
+
|
|
283
|
+
def evaluate(self, candidate, **kwargs):
|
|
284
|
+
leaked = "@" in str(candidate)
|
|
285
|
+
status = DecisionStatus.FAILED if leaked else DecisionStatus.PASSED
|
|
286
|
+
trace = LayerTrace(layer_name=self.name, status=status, latency_ms=0.01)
|
|
287
|
+
return Verdict(passed=not leaked, status=status, triggered_layer=self.name,
|
|
288
|
+
total_latency_ms=0.01, traces=[trace])
|
|
289
|
+
|
|
290
|
+
pipeline = RegexPIIGate() >> JudgeGate(rubric="Does the reply avoid leaking personal data?")
|
|
291
|
+
print(pipeline.evaluate("Contact jane@example.com").triggered_layer) # RegexPII; judge never called
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
198
296
|
## Benchmark Highlights
|
|
199
297
|
|
|
200
298
|
Across a 24-item multi-domain suite (RAG groundedness, customer support policy, SQL safety, and Python security), `LogitGate` resolves **95.8%–100.0% of cases at Layers 0 and 1**, avoiding multi-token generation:
|
|
@@ -247,13 +345,11 @@ For complete research data, mathematical derivations, and production recipes, ex
|
|
|
247
345
|
|
|
248
346
|
## Common Recipes
|
|
249
347
|
|
|
250
|
-
### Multi-Rubric Batch Evaluation (`
|
|
348
|
+
### Multi-Rubric Batch Evaluation (`MultiRubricGate`)
|
|
251
349
|
Evaluates multiple rubrics against a single candidate artifact while anchoring the candidate at Token 0 to share 95–99% of prefix tokens in KV-cache:
|
|
252
350
|
|
|
253
351
|
```python
|
|
254
|
-
from logitgate import
|
|
255
|
-
|
|
256
|
-
gate = LogitGate("Base rubric")
|
|
352
|
+
from logitgate import MultiRubricGate
|
|
257
353
|
|
|
258
354
|
rubrics = [
|
|
259
355
|
"Does the code include full type annotations?",
|
|
@@ -261,8 +357,9 @@ rubrics = [
|
|
|
261
357
|
"Does the code implement proper error handling?",
|
|
262
358
|
]
|
|
263
359
|
|
|
360
|
+
gate = MultiRubricGate(rubrics)
|
|
264
361
|
candidate_code = "def fetch(url: str, timeout: int = 10) -> str: pass"
|
|
265
|
-
verdicts = gate.
|
|
362
|
+
verdicts = gate.evaluate_each(candidate_code)
|
|
266
363
|
|
|
267
364
|
for rubric, v in zip(rubrics, verdicts):
|
|
268
365
|
print(f"[{'PASS' if v.passed else 'FAIL'}] P={v.probability:.2f} | {rubric}")
|
|
@@ -296,7 +393,9 @@ from logitgate import ScoreGate
|
|
|
296
393
|
|
|
297
394
|
scorer = ScoreGate(
|
|
298
395
|
rubric="Rate the architectural code quality from 1 (disastrous) to 5 (flawless):",
|
|
299
|
-
|
|
396
|
+
min_score=1,
|
|
397
|
+
max_score=5,
|
|
398
|
+
threshold=3.5,
|
|
300
399
|
symmetric_debiasing=True, # Evaluates 1->5 and 5->1 to eliminate scale anchoring
|
|
301
400
|
)
|
|
302
401
|
|
|
@@ -321,10 +420,18 @@ cat schema.json | logitgate eval --rubric "Must be valid JSON schema"
|
|
|
321
420
|
# Replay and explain a frozen failure snapshot from CI
|
|
322
421
|
logitgate explain .logitgate/failures/latest.json
|
|
323
422
|
|
|
423
|
+
# Inspect margins, entropy, and label-order bias without calling any model
|
|
424
|
+
logitgate explain --offline
|
|
425
|
+
|
|
426
|
+
# Judge vs LogitGate vs cascade latency/cost on your backend
|
|
427
|
+
logitgate benchmark --backend "ollama://localhost:11434" --model "qwen2.5:7b"
|
|
428
|
+
|
|
324
429
|
# Auto-calibrate model temperature on micro-dataset
|
|
325
430
|
logitgate calibrate --backend "ollama://localhost:11434" --model "qwen2.5:14b"
|
|
326
431
|
```
|
|
327
432
|
|
|
433
|
+
Exit codes: `0` passed / ok, `1` verdict failed or bad input, `2` backend unreachable or misconfigured.
|
|
434
|
+
|
|
328
435
|
---
|
|
329
436
|
|
|
330
437
|
## BibTeX Citation
|
|
@@ -336,7 +443,7 @@ If you use LogitGate in your research or production systems, please cite it as f
|
|
|
336
443
|
author = {Jason Bradley},
|
|
337
444
|
title = {LogitGate: Calibrated First-Token Logit Evaluation and Cascaded Routing for LLMs},
|
|
338
445
|
year = {2026},
|
|
339
|
-
version = {0.
|
|
446
|
+
version = {0.3.0},
|
|
340
447
|
url = {https://github.com/jasonbdly/LogitGate},
|
|
341
448
|
license = {Apache-2.0}
|
|
342
449
|
}
|
|
@@ -10,28 +10,36 @@
|
|
|
10
10
|
|
|
11
11
|
**Fast, calibrated first-token logit evaluation and 3-layer cascaded routing for LLMs.**
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
LLM-as-a-judge asks a model to write a critique and then a verdict. That costs a full autoregressive generation per check: 1.5–6 s and a few hundred tokens on our hardware. LogitGate reads the verdict straight from the **first-token log-probabilities** (`max_tokens=1`, 0 generated tokens), cancels the model's label-order prior with a forward + reverse pass, and only falls back to a generative judge when that read is genuinely ambiguous.
|
|
14
|
+
|
|
15
|
+
Measured on the repo's 24-case multi-domain suite (RTX 4070, Ollama, [full methodology](docs/benchmarks.md)): **36–41 ms** single-pass and **90–113 ms** debiased per check. End-to-end, the cascade ran **10.6x–17x** faster than a `think=False` generative judge, at 23/24 accuracy versus the judge's 23–24/24. On pure CPU the gap shrinks to 1.4x, because prefill dominates there. Don't take our word for it: [`logitgate benchmark`](#reproduce-the-numbers-on-your-hardware) reruns the comparison on your model in about a minute.
|
|
14
16
|
|
|
15
17
|
---
|
|
16
18
|
|
|
17
19
|
## How It Works in 60 Seconds
|
|
18
20
|
|
|
19
|
-
When evaluating whether an LLM output satisfies a rubric, traditional frameworks prompt a model to generate a 200–500 word critique before outputting `PASS` or `FAIL`.
|
|
21
|
+
When evaluating whether an LLM output satisfies a rubric, traditional frameworks prompt a model to generate a 200–500 word critique before outputting `PASS` or `FAIL`. That takes **seconds per check**, consumes hundreds of tokens, and is sensitive to label order and position.
|
|
20
22
|
|
|
21
23
|
`LogitGate` uses a **3-layer cascaded pipeline**:
|
|
22
24
|
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
25
|
+
```
|
|
26
|
+
Candidate Artifact
|
|
27
|
+
│
|
|
28
|
+
▼
|
|
29
|
+
┌──────────────────────────────────────┐
|
|
30
|
+
│ Layer 0: ASTGate / ContractGate ├──► [FAIL: syntax/schema, <0.1ms]
|
|
31
|
+
└──────────────────┬───────────────────┘
|
|
32
|
+
│ Passed
|
|
33
|
+
▼
|
|
34
|
+
┌──────────────────────────────────────┐
|
|
35
|
+
│ Layer 1: LogitGate ├──► [PASS: P >= 0.85, ~40–110ms]
|
|
36
|
+
└──────────────────┬───────────────────┤
|
|
37
|
+
│ Ambiguous └──► [FAIL: P <= 0.20, ~40–110ms]
|
|
38
|
+
│ (0.20 < P < 0.85)
|
|
39
|
+
▼
|
|
40
|
+
┌──────────────────────────────────────┐
|
|
41
|
+
│ Layer 2: JudgeGate ├──► [Actionable Diagnostic Report]
|
|
42
|
+
└──────────────────────────────────────┘
|
|
35
43
|
```
|
|
36
44
|
|
|
37
45
|
1. **Layer 0 (`ASTGate` / `ContractGate`)**: Deterministic checks in **<0.1 ms** on CPU (Python AST syntax, JSON/Pydantic schemas, regex patterns, acyclic DAG topology). Failures reject immediately with zero LLM overhead.
|
|
@@ -43,7 +51,7 @@ flowchart TD
|
|
|
43
51
|
## Installation
|
|
44
52
|
|
|
45
53
|
```bash
|
|
46
|
-
# Core package
|
|
54
|
+
# Core package: only pydantic + httpx
|
|
47
55
|
pip install logitgate
|
|
48
56
|
|
|
49
57
|
# With optional framework integrations:
|
|
@@ -57,6 +65,28 @@ pip install "logitgate[all]" # All integrations
|
|
|
57
65
|
|
|
58
66
|
---
|
|
59
67
|
|
|
68
|
+
## Pick a Backend (10 seconds)
|
|
69
|
+
|
|
70
|
+
LogitGate needs an endpoint that returns **token log-probabilities**. Anything below works; a gate's `backend=` argument overrides the global default.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
export LOGITGATE_BACKEND="ollama://localhost:11434/qwen2.5:7b" # Ollama
|
|
74
|
+
export LOGITGATE_BACKEND="vllm://localhost:8000/v1" # vLLM / any OpenAI-compatible server
|
|
75
|
+
export LOGITGATE_BACKEND="llamacpp://localhost:8080" # llama-server
|
|
76
|
+
export LOGITGATE_BACKEND="stub://" # offline stub for CI plumbing (fixed P=0.95)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
With nothing configured, LogitGate falls back to the offline stub and emits a one-time `StubBackendWarning`, so a fake `P=0.95` never passes for a real result. Errors say what to do next instead of dumping an `httpx` traceback:
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
logitgate.exceptions.BackendUnavailableError: Cannot connect to Ollama at http://localhost:11434.
|
|
83
|
+
Is ollama running? (start it with `ollama serve`) Or set backend='stub://' for offline testing.
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
A missing Ollama model suggests the exact `ollama pull <model>` command, an OpenAI 401 points at `OPENAI_API_KEY`, and a cold-model timeout says so. `BackendUnavailableError` is also a `ConnectionError`, so existing retry logic keeps working.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
60
90
|
## 30-Second Quickstart
|
|
61
91
|
|
|
62
92
|
### 1. Single Atomic Rubric (3-Layer Cascaded Fast Path)
|
|
@@ -98,26 +128,28 @@ print(f"Tokens Generated: {verdict.total_tokens_generated}") # 0 tokens!
|
|
|
98
128
|
|
|
99
129
|
### 2. Multi-Rubric Batching (Prefix-Preserved KV-Cache Reuse)
|
|
100
130
|
|
|
101
|
-
When verifying multiple criteria against a candidate artifact, **do not combine them into a single monolithic compound rubric** (which dilutes logit signals and conflates orthogonal requirements). Instead, evaluate isolated atomic rubrics
|
|
131
|
+
When verifying multiple criteria against a candidate artifact, **do not combine them into a single monolithic compound rubric** (which dilutes logit signals and conflates orthogonal requirements). Instead, evaluate isolated atomic rubrics with `MultiRubricGate`.
|
|
102
132
|
|
|
103
|
-
The candidate code is anchored at Token 0, sharing 95–99% of prefix tokens in KV
|
|
133
|
+
The candidate code is anchored at Token 0, sharing 95–99% of prefix tokens in KV-cache across all checks. `MultiRubricGate` enforces weakest-link aggregation: passes only if *every* atomic rubric passes, and records per-rubric traces so diagnostics show exactly which requirement failed:
|
|
104
134
|
|
|
105
135
|
```python
|
|
106
|
-
from logitgate import
|
|
136
|
+
from logitgate import MultiRubricGate
|
|
107
137
|
|
|
108
|
-
# Best Practice: Keep rubrics atomic — evaluate multiple criteria via
|
|
138
|
+
# Best Practice: Keep rubrics atomic — evaluate multiple criteria via MultiRubricGate
|
|
109
139
|
rubrics = [
|
|
110
140
|
"Does the function calculate Fibonacci numbers recursively?",
|
|
111
141
|
"Does the function explicitly handle non-positive inputs (n <= 0)?",
|
|
112
142
|
"Does the function include parameter and return type annotations?",
|
|
113
143
|
]
|
|
114
144
|
|
|
115
|
-
gate =
|
|
145
|
+
gate = MultiRubricGate(rubrics)
|
|
116
146
|
|
|
117
147
|
# Evaluates all 3 rubrics in a single batch reusing the shared KV-cache prefix
|
|
118
|
-
|
|
148
|
+
verdict = gate(candidate_code)
|
|
149
|
+
print(f"Passed: {verdict.passed} | {verdict.reason}")
|
|
119
150
|
|
|
120
|
-
|
|
151
|
+
# Or inspect individual per-rubric verdicts:
|
|
152
|
+
for rubric, v in zip(rubrics, gate.evaluate_each(candidate_code)):
|
|
121
153
|
status = "PASS" if v.passed else "FAIL"
|
|
122
154
|
print(f"[{status}] P={v.probability:.4f} | {rubric}")
|
|
123
155
|
```
|
|
@@ -131,6 +163,7 @@ for rubric, v in zip(rubrics, verdicts):
|
|
|
131
163
|
| **`ASTGate`** | Layer 0 | **< 0.1 ms** | 0 | Sub-millisecond deterministic checks: Python syntax, forbidden imports, regex, acyclic DAGs, custom callables. |
|
|
132
164
|
| **`ContractGate`** | Layer 0 | **< 0.1 ms** | 0 | General-domain contract validation: Pydantic models, JSON Schemas, length bounds, required sections. |
|
|
133
165
|
| **`LogitGate`** | Layer 1 | **~40–110 ms** | 0 | Calibrated binary classification from first-token logits using symmetric log-odds debiasing ($\bar{L} = \frac{L_1 + L_2}{2T}$). |
|
|
166
|
+
| **`MultiRubricGate`** | Layer 1 | **~50–140 ms** | 0 | Atomic multi-rubric verification sharing KV prefix. Weakest-link aggregation (fails if any criterion fails). |
|
|
134
167
|
| **`ChoiceGate`** | Layer 1 | **~40–120 ms** | 0 | Discrete K-ary routing and intent classification across arbitrary string options with permutation averaging. |
|
|
135
168
|
| **`ScoreGate`** | Layer 1 | **~50–160 ms** | 0 | Continuous probability-weighted rubric scoring (𝔼[S] = Σ s · P(s)) with inverted scale debiasing. |
|
|
136
169
|
| **`JudgeGate`** | Layer 2 | **1.5–5.0 s** | 60–300 | Generative System-2 reasoning judge with reason-then-verdict parsing and self-healing 1-token logit probes. |
|
|
@@ -140,6 +173,73 @@ for rubric, v in zip(rubrics, verdicts):
|
|
|
140
173
|
|
|
141
174
|
---
|
|
142
175
|
|
|
176
|
+
## Where LogitGate Fits (and Where It Doesn't)
|
|
177
|
+
|
|
178
|
+
| Tool | What it does | How LogitGate relates |
|
|
179
|
+
| :--- | :--- | :--- |
|
|
180
|
+
| **DeepEval / G-Eval** | Offline evaluation suites and CI scoring; G-Eval weights score tokens by probability. | Complementary. LogitGate is the *inline* path: a sub-100 ms check you can put between agent turns. It ships a DeepEval integration (`LogitGateMetric`) for suites. |
|
|
181
|
+
| **RouteLLM** | Picks *which model generates* a response, before generation. | Different stage. LogitGate judges the output *after* generation. `ChoiceGate` can route, but on content, not on model cost. |
|
|
182
|
+
| **Flow-Judge, Lynx, Prometheus 2** | Dedicated fine-tuned judge models that reason autoregressively. | LogitGate is non-autoregressive and model-agnostic. It projects a verdict from the first-token logits of a model you already serve, with no extra VRAM. A fine-tuned judge works fine as the Layer-2 `JudgeGate` backend. |
|
|
183
|
+
|
|
184
|
+
**When not to use it:**
|
|
185
|
+
|
|
186
|
+
- **No logprobs, no LogitGate.** APIs that don't expose `top_logprobs` (e.g. Anthropic) can't host Layer 1. OpenAI caps `top_logprobs` at 20, which is enough for binary and small-K gates.
|
|
187
|
+
- **Multi-step reasoning rubrics** ("is this 12-step derivation correct?") exceed what a single forward pass can judge. Accuracy drops on our reasoning-depth suite ([falsification boundaries](docs/benchmarks.md)). Let those escalate to `JudgeGate`, or decompose them into atomic rubrics.
|
|
188
|
+
- **Tiny models on CPU.** 1–1.5B models are fast but reach 58–63% cascade accuracy on our multi-domain suite. Measure before you trust them.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Reproduce the Numbers on Your Hardware
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
logitgate benchmark --backend ollama://localhost:11434 --model qwen2.5:7b
|
|
196
|
+
logitgate benchmark --backend vllm://localhost:8000/v1 --model Qwen/Qwen2.5-7B-Instruct \
|
|
197
|
+
--dataset benchmarks/data/multi_domain_24.json --repeat 3 --json > results.json
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The command runs the same cases through three configurations and reports p50/p95 latency, generated tokens, LLM calls per case, accuracy, and the cascade's fast-path rate:
|
|
201
|
+
|
|
202
|
+
1. A generative `JudgeGate` alone (`think=False` by default, so the baseline is a *fast* judge)
|
|
203
|
+
2. A single `LogitGate`
|
|
204
|
+
3. `LogitGate >> JudgeGate` (`on_ambiguous_only`)
|
|
205
|
+
|
|
206
|
+
Modes run as separate passes after an untimed warm-up, so model load time isn't charged to whichever mode runs first. `--json` records the LogitGate version, backend, model, platform, and a dataset hash, so a result can be traced to exactly what produced it. With the debiased default, LogitGate makes **two** forward passes per check, and the report counts both.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Custom Gates & Routing Traits
|
|
211
|
+
|
|
212
|
+
`Cascade` never checks concrete classes. It routes on three traits declared on every gate, so wrappers, decorators, and your own gates get the same short-circuiting as the built-ins:
|
|
213
|
+
|
|
214
|
+
| Trait | Meaning | Built-ins |
|
|
215
|
+
| :--- | :--- | :--- |
|
|
216
|
+
| `layer_tier` | `0` deterministic (failures are final), `1` single forward pass (failures may escalate), `2` generative | `ASTGate`=0, `LogitGate`/`ChoiceGate`/`ScoreGate`=1, `JudgeGate`=2 |
|
|
217
|
+
| `can_fast_path` | A PASSED verdict skips the next generative gate | `False` for `ASTGate` (a valid schema says nothing about semantics) |
|
|
218
|
+
| `escalation_policy` | When a tier-2 gate runs: `on_ambiguous_or_fail`, `on_ambiguous_only`, `always` | `JudgeGate(trigger=...)` |
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
from logitgate import BaseGate, DecisionStatus, JudgeGate, LayerTrace, Verdict
|
|
222
|
+
|
|
223
|
+
class RegexPIIGate(BaseGate):
|
|
224
|
+
"""Deterministic: a hit is a decisive FAIL; a miss proves nothing semantic."""
|
|
225
|
+
|
|
226
|
+
name = "RegexPII"
|
|
227
|
+
layer_tier = 0
|
|
228
|
+
can_fast_path = False
|
|
229
|
+
|
|
230
|
+
def evaluate(self, candidate, **kwargs):
|
|
231
|
+
leaked = "@" in str(candidate)
|
|
232
|
+
status = DecisionStatus.FAILED if leaked else DecisionStatus.PASSED
|
|
233
|
+
trace = LayerTrace(layer_name=self.name, status=status, latency_ms=0.01)
|
|
234
|
+
return Verdict(passed=not leaked, status=status, triggered_layer=self.name,
|
|
235
|
+
total_latency_ms=0.01, traces=[trace])
|
|
236
|
+
|
|
237
|
+
pipeline = RegexPIIGate() >> JudgeGate(rubric="Does the reply avoid leaking personal data?")
|
|
238
|
+
print(pipeline.evaluate("Contact jane@example.com").triggered_layer) # RegexPII; judge never called
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
143
243
|
## Benchmark Highlights
|
|
144
244
|
|
|
145
245
|
Across a 24-item multi-domain suite (RAG groundedness, customer support policy, SQL safety, and Python security), `LogitGate` resolves **95.8%–100.0% of cases at Layers 0 and 1**, avoiding multi-token generation:
|
|
@@ -192,13 +292,11 @@ For complete research data, mathematical derivations, and production recipes, ex
|
|
|
192
292
|
|
|
193
293
|
## Common Recipes
|
|
194
294
|
|
|
195
|
-
### Multi-Rubric Batch Evaluation (`
|
|
295
|
+
### Multi-Rubric Batch Evaluation (`MultiRubricGate`)
|
|
196
296
|
Evaluates multiple rubrics against a single candidate artifact while anchoring the candidate at Token 0 to share 95–99% of prefix tokens in KV-cache:
|
|
197
297
|
|
|
198
298
|
```python
|
|
199
|
-
from logitgate import
|
|
200
|
-
|
|
201
|
-
gate = LogitGate("Base rubric")
|
|
299
|
+
from logitgate import MultiRubricGate
|
|
202
300
|
|
|
203
301
|
rubrics = [
|
|
204
302
|
"Does the code include full type annotations?",
|
|
@@ -206,8 +304,9 @@ rubrics = [
|
|
|
206
304
|
"Does the code implement proper error handling?",
|
|
207
305
|
]
|
|
208
306
|
|
|
307
|
+
gate = MultiRubricGate(rubrics)
|
|
209
308
|
candidate_code = "def fetch(url: str, timeout: int = 10) -> str: pass"
|
|
210
|
-
verdicts = gate.
|
|
309
|
+
verdicts = gate.evaluate_each(candidate_code)
|
|
211
310
|
|
|
212
311
|
for rubric, v in zip(rubrics, verdicts):
|
|
213
312
|
print(f"[{'PASS' if v.passed else 'FAIL'}] P={v.probability:.2f} | {rubric}")
|
|
@@ -241,7 +340,9 @@ from logitgate import ScoreGate
|
|
|
241
340
|
|
|
242
341
|
scorer = ScoreGate(
|
|
243
342
|
rubric="Rate the architectural code quality from 1 (disastrous) to 5 (flawless):",
|
|
244
|
-
|
|
343
|
+
min_score=1,
|
|
344
|
+
max_score=5,
|
|
345
|
+
threshold=3.5,
|
|
245
346
|
symmetric_debiasing=True, # Evaluates 1->5 and 5->1 to eliminate scale anchoring
|
|
246
347
|
)
|
|
247
348
|
|
|
@@ -266,10 +367,18 @@ cat schema.json | logitgate eval --rubric "Must be valid JSON schema"
|
|
|
266
367
|
# Replay and explain a frozen failure snapshot from CI
|
|
267
368
|
logitgate explain .logitgate/failures/latest.json
|
|
268
369
|
|
|
370
|
+
# Inspect margins, entropy, and label-order bias without calling any model
|
|
371
|
+
logitgate explain --offline
|
|
372
|
+
|
|
373
|
+
# Judge vs LogitGate vs cascade latency/cost on your backend
|
|
374
|
+
logitgate benchmark --backend "ollama://localhost:11434" --model "qwen2.5:7b"
|
|
375
|
+
|
|
269
376
|
# Auto-calibrate model temperature on micro-dataset
|
|
270
377
|
logitgate calibrate --backend "ollama://localhost:11434" --model "qwen2.5:14b"
|
|
271
378
|
```
|
|
272
379
|
|
|
380
|
+
Exit codes: `0` passed / ok, `1` verdict failed or bad input, `2` backend unreachable or misconfigured.
|
|
381
|
+
|
|
273
382
|
---
|
|
274
383
|
|
|
275
384
|
## BibTeX Citation
|
|
@@ -281,7 +390,7 @@ If you use LogitGate in your research or production systems, please cite it as f
|
|
|
281
390
|
author = {Jason Bradley},
|
|
282
391
|
title = {LogitGate: Calibrated First-Token Logit Evaluation and Cascaded Routing for LLMs},
|
|
283
392
|
year = {2026},
|
|
284
|
-
version = {0.
|
|
393
|
+
version = {0.3.0},
|
|
285
394
|
url = {https://github.com/jasonbdly/LogitGate},
|
|
286
395
|
license = {Apache-2.0}
|
|
287
396
|
}
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "logitgate"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "Fast, calibrated first-token logit evaluation and 3-layer cascaded routing for LLMs."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -22,8 +22,6 @@ classifiers = [
|
|
|
22
22
|
"Programming Language :: Python :: 3.11",
|
|
23
23
|
"Programming Language :: Python :: 3.12",
|
|
24
24
|
"Programming Language :: Python :: 3.13",
|
|
25
|
-
"Programming Language :: Python :: 3.14",
|
|
26
|
-
"Programming Language :: Python :: 3.15",
|
|
27
25
|
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
28
26
|
]
|
|
29
27
|
dependencies = [
|
|
@@ -62,6 +60,8 @@ all = [
|
|
|
62
60
|
[tool.pytest.ini_options]
|
|
63
61
|
asyncio_mode = "auto"
|
|
64
62
|
asyncio_default_fixture_loop_scope = "function"
|
|
63
|
+
# Many tests rely on the implicit offline stub on purpose; the warning has dedicated tests.
|
|
64
|
+
filterwarnings = ["ignore::logitgate.exceptions.StubBackendWarning"]
|
|
65
65
|
|
|
66
66
|
[tool.mypy]
|
|
67
67
|
warn_unused_ignores = true
|