snifftest 0.1.0

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.
@@ -0,0 +1,136 @@
1
+ # The panel `snifftest bench` asks the same questions as the judgment rules.
2
+ #
3
+ # Nothing here is a claim that a model exists. Each entry carries a pattern and
4
+ # a preference; on the day of the run every entry is matched against the
5
+ # provider's own model list and the exact slug, its published prices and
6
+ # whether it takes a JSON mode are written into the raw output. A model the
7
+ # provider does not list becomes a row that says "not available on <date>" and
8
+ # is never swapped for a near neighbour.
9
+ #
10
+ # Most of the panel is routed through OpenRouter so that one key reproduces the
11
+ # whole bench and every row carries the same proxy hop. The last entry calls one
12
+ # of those models directly, which is what makes that hop measurable rather than
13
+ # an asterisk.
14
+ #
15
+ # Jev is in the rotation, as the last row. Its accuracy still comes from
16
+ # `snifftest eval`, where arm C is scored against arms A and B over the same
17
+ # seeded corpus and joined into the headline table with `--eval`; what the row
18
+ # below adds is a latency measured in the same interleaved rotation, with the
19
+ # same repeats, as every other row. Two latency numbers in one column measured
20
+ # two different ways is not a comparison.
21
+ #
22
+ # A row may also name `alternates`. A preferred slug the provider has stopped
23
+ # listing then falls to a substitute the panel file named, and the tables print
24
+ # which one was taken; a row with no alternates says "not available" instead of
25
+ # quietly measuring a neighbour.
26
+ #
27
+ # A row may also declare `max_tokens` and `reasoning`. Those two settings are
28
+ # the only way the rows differ, they are there because a reasoning model spends
29
+ # its internal tokens out of the same completion budget as its answer, and both
30
+ # are printed under the tables so the difference is declared rather than hidden.
31
+
32
+ version: 1
33
+
34
+ prices:
35
+ # OpenRouter publishes a price per model, so only the direct row needs a file.
36
+ anthropic: prices/anthropic-2026-09-17.yaml
37
+ # No dated published price has been recorded for the judgment service, so no
38
+ # file is named here and its row prints its cost as unknown. A constant in
39
+ # this repo's source is not a source. When a dated one exists it goes in
40
+ # `prices/` beside the Anthropic file and this line points at it.
41
+
42
+ models:
43
+ - id: haiku
44
+ label: Claude Haiku 4.5
45
+ tier: fast
46
+ provider: openrouter
47
+ match: "^anthropic/claude-haiku-4"
48
+ prefer:
49
+ - anthropic/claude-haiku-4.5
50
+
51
+ - id: sonnet
52
+ label: Claude Sonnet 5
53
+ tier: mid
54
+ provider: openrouter
55
+ match: "^anthropic/claude-sonnet-5"
56
+ prefer:
57
+ - anthropic/claude-sonnet-5
58
+
59
+ - id: opus
60
+ label: Claude Opus 5
61
+ tier: deep
62
+ provider: openrouter
63
+ match: "^anthropic/claude-opus-5"
64
+ prefer:
65
+ - anthropic/claude-opus-5
66
+
67
+ - id: openai-fast
68
+ label: OpenAI, fast tier
69
+ tier: fast
70
+ provider: openrouter
71
+ match: "^openai/gpt-5[^/]*mini$"
72
+ prefer:
73
+ - openai/gpt-5-mini
74
+
75
+ - id: gemini-flash
76
+ label: Gemini Flash
77
+ tier: fast
78
+ provider: openrouter
79
+ match: "^google/gemini-[0-9.]+-flash$"
80
+ prefer:
81
+ - google/gemini-2.5-flash
82
+
83
+ - id: deepseek
84
+ label: DeepSeek, chat tier
85
+ tier: fast
86
+ provider: openrouter
87
+ match: "^deepseek/deepseek-chat"
88
+ prefer:
89
+ - deepseek/deepseek-chat-v3
90
+ note: >
91
+ DeepSeek publishes no model called Flash; this is its chat tier, which is
92
+ the cheap-and-quick row the panel wanted.
93
+
94
+ - id: openai-deep
95
+ label: OpenAI, deep tier
96
+ tier: deep
97
+ provider: openrouter
98
+ match: "^openai/gpt-5(\\.[0-9]+)?$"
99
+ prefer:
100
+ - openai/gpt-5
101
+ # This row reasons, and on OpenRouter the reasoning tokens come out of the
102
+ # same completion budget as the answer. On the default budget it can spend
103
+ # the whole of it thinking and stop before it writes the JSON object, which
104
+ # lands in the table as a parse failure and reads as a verdict on the model
105
+ # rather than on the request. So the budget is raised and the effort is
106
+ # declared: low keeps the row affordable, and the footnote prints both, so
107
+ # nobody reads this row as the model's best.
108
+ max_tokens: 4000
109
+ reasoning:
110
+ effort: low
111
+
112
+ - id: sonnet-direct
113
+ label: Claude Sonnet 5, direct
114
+ tier: control
115
+ provider: anthropic
116
+ match: "^claude-sonnet-5"
117
+ prefer:
118
+ - claude-sonnet-5
119
+ note: >
120
+ The overhead control. Same model as the sonnet row, called without the
121
+ OpenRouter hop, so the pair measures the hop.
122
+
123
+ - id: jev
124
+ label: Jev, the judgment arm
125
+ tier: judgment
126
+ provider: jev
127
+ match: "^jev-"
128
+ prefer:
129
+ - jev-latest
130
+ note: >
131
+ Asked its own way, one request per paragraph carrying every rule as a
132
+ separate question, which is the shape the service documents and the shape
133
+ arm C uses. The request count matches the other rows; the request shape
134
+ does not. It returns a probability and no boolean, so its flag column is
135
+ that probability against the shipped threshold rather than a decision the
136
+ service made.
@@ -0,0 +1,33 @@
1
+ # Published list prices for the Anthropic direct row, in US dollars per million
2
+ # tokens.
3
+ #
4
+ # This file exists because the Messages API returns token counts and no prices,
5
+ # and because a price written into source code is a number with no provenance
6
+ # and no expiry. Every table that uses these numbers cites this file, its
7
+ # source and the date below.
8
+ #
9
+ # `match` is a substring of the served model id, so a dated id such as
10
+ # `claude-sonnet-5-20260101` lands on its family. A served model that matches no
11
+ # row here has no cost at all in the report. It is never a zero, which would
12
+ # read as "this call was free".
13
+ #
14
+ # Re-verify on the run date and write a new dated file rather than editing this
15
+ # one; the run that used the old numbers should keep pointing at them.
16
+
17
+ version: 1
18
+ source: >-
19
+ Anthropic published list prices, transcribed on 2026-09-17; re-verify against
20
+ the published price list on the run date before quoting these figures.
21
+ verified_on: 2026-09-17
22
+ currency: usd_per_million_tokens
23
+
24
+ models:
25
+ - match: haiku-4-5
26
+ input: 1
27
+ output: 5
28
+ - match: sonnet-5
29
+ input: 2
30
+ output: 10
31
+ - match: opus-5
32
+ input: 5
33
+ output: 25
@@ -0,0 +1,27 @@
1
+ # Published list prices for the OpenAI direct rows, in US dollars per million
2
+ # tokens.
3
+ #
4
+ # This file exists because the models endpoint returns ids and nothing else: no
5
+ # prices, and no list of the parameters each model takes. A price written into
6
+ # source code is a number with no provenance and no expiry, so it lives here
7
+ # instead, and every table that uses these numbers cites this file, its source
8
+ # and the date below.
9
+ #
10
+ # `match` is a substring of the served model id, so a dated id lands on its
11
+ # family. A served model that matches no row here has no cost at all in the
12
+ # report. It is never a zero, which would read as "this call was free".
13
+ #
14
+ # Re-verify on the run date and write a new dated file rather than editing this
15
+ # one; the run that used the old numbers should keep pointing at them.
16
+
17
+ version: 1
18
+ source: >-
19
+ OpenAI published list prices (developers.openai.com/api/docs/pricing),
20
+ transcribed on 2026-09-17
21
+ verified_on: 2026-09-17
22
+ currency: usd_per_million_tokens
23
+
24
+ models:
25
+ - match: gpt-5.6-sol
26
+ input: 4
27
+ output: 20