@trazum/core 1.8.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.
- package/LICENSE +21 -0
- package/README.md +163 -0
- package/dist/advisories.d.ts +23 -0
- package/dist/advisories.d.ts.map +1 -0
- package/dist/advisories.js +376 -0
- package/dist/advisories.js.map +1 -0
- package/dist/aws-sigv4.d.ts +88 -0
- package/dist/aws-sigv4.d.ts.map +1 -0
- package/dist/aws-sigv4.js +117 -0
- package/dist/aws-sigv4.js.map +1 -0
- package/dist/baseline.d.ts +171 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +273 -0
- package/dist/baseline.js.map +1 -0
- package/dist/cache.d.ts +26 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +28 -0
- package/dist/cache.js.map +1 -0
- package/dist/changes.d.ts +29 -0
- package/dist/changes.d.ts.map +1 -0
- package/dist/changes.js +142 -0
- package/dist/changes.js.map +1 -0
- package/dist/compare.d.ts +65 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/compare.js +58 -0
- package/dist/compare.js.map +1 -0
- package/dist/config-schema.d.ts +118 -0
- package/dist/config-schema.d.ts.map +1 -0
- package/dist/config-schema.js +315 -0
- package/dist/config-schema.js.map +1 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +112 -0
- package/dist/config.js.map +1 -0
- package/dist/detect.d.ts +70 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +228 -0
- package/dist/detect.js.map +1 -0
- package/dist/evaluate.d.ts +98 -0
- package/dist/evaluate.d.ts.map +1 -0
- package/dist/evaluate.js +110 -0
- package/dist/evaluate.js.map +1 -0
- package/dist/extract.d.ts +81 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +280 -0
- package/dist/extract.js.map +1 -0
- package/dist/gcp-auth.d.ts +58 -0
- package/dist/gcp-auth.d.ts.map +1 -0
- package/dist/gcp-auth.js +113 -0
- package/dist/gcp-auth.js.map +1 -0
- package/dist/glob.d.ts +49 -0
- package/dist/glob.d.ts.map +1 -0
- package/dist/glob.js +154 -0
- package/dist/glob.js.map +1 -0
- package/dist/host.d.ts +30 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +69 -0
- package/dist/host.js.map +1 -0
- package/dist/i18n/en.d.ts +4 -0
- package/dist/i18n/en.d.ts.map +1 -0
- package/dist/i18n/en.js +168 -0
- package/dist/i18n/en.js.map +1 -0
- package/dist/i18n/es.d.ts +4 -0
- package/dist/i18n/es.d.ts.map +1 -0
- package/dist/i18n/es.js +168 -0
- package/dist/i18n/es.js.map +1 -0
- package/dist/i18n/index.d.ts +36 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +50 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/types.d.ts +180 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/types.js +11 -0
- package/dist/i18n/types.js.map +1 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/dist/llm.d.ts +226 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +485 -0
- package/dist/llm.js.map +1 -0
- package/dist/nearest.d.ts +20 -0
- package/dist/nearest.d.ts.map +1 -0
- package/dist/nearest.js +54 -0
- package/dist/nearest.js.map +1 -0
- package/dist/net.d.ts +90 -0
- package/dist/net.d.ts.map +1 -0
- package/dist/net.js +203 -0
- package/dist/net.js.map +1 -0
- package/dist/node.d.ts +32 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +33 -0
- package/dist/node.js.map +1 -0
- package/dist/openrouter.d.ts +25 -0
- package/dist/openrouter.d.ts.map +1 -0
- package/dist/openrouter.js +72 -0
- package/dist/openrouter.js.map +1 -0
- package/dist/optimize.d.ts +38 -0
- package/dist/optimize.d.ts.map +1 -0
- package/dist/optimize.js +183 -0
- package/dist/optimize.js.map +1 -0
- package/dist/otlp.d.ts +91 -0
- package/dist/otlp.d.ts.map +1 -0
- package/dist/otlp.js +102 -0
- package/dist/otlp.js.map +1 -0
- package/dist/phrases.d.ts +169 -0
- package/dist/phrases.d.ts.map +1 -0
- package/dist/phrases.js +939 -0
- package/dist/phrases.js.map +1 -0
- package/dist/pricing-overlay.d.ts +55 -0
- package/dist/pricing-overlay.d.ts.map +1 -0
- package/dist/pricing-overlay.js +241 -0
- package/dist/pricing-overlay.js.map +1 -0
- package/dist/pricing.d.ts +115 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +400 -0
- package/dist/pricing.js.map +1 -0
- package/dist/profile.d.ts +71 -0
- package/dist/profile.d.ts.map +1 -0
- package/dist/profile.js +55 -0
- package/dist/profile.js.map +1 -0
- package/dist/promptfoo.d.ts +58 -0
- package/dist/promptfoo.d.ts.map +1 -0
- package/dist/promptfoo.js +149 -0
- package/dist/promptfoo.js.map +1 -0
- package/dist/prune.d.ts +91 -0
- package/dist/prune.d.ts.map +1 -0
- package/dist/prune.js +110 -0
- package/dist/prune.js.map +1 -0
- package/dist/reorder.d.ts +82 -0
- package/dist/reorder.d.ts.map +1 -0
- package/dist/reorder.js +215 -0
- package/dist/reorder.js.map +1 -0
- package/dist/review.d.ts +54 -0
- package/dist/review.d.ts.map +1 -0
- package/dist/review.js +131 -0
- package/dist/review.js.map +1 -0
- package/dist/rules.d.ts +5 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +279 -0
- package/dist/rules.js.map +1 -0
- package/dist/savings.d.ts +36 -0
- package/dist/savings.d.ts.map +1 -0
- package/dist/savings.js +83 -0
- package/dist/savings.js.map +1 -0
- package/dist/segment.d.ts +8 -0
- package/dist/segment.d.ts.map +1 -0
- package/dist/segment.js +74 -0
- package/dist/segment.js.map +1 -0
- package/dist/shared-prefix.d.ts +63 -0
- package/dist/shared-prefix.d.ts.map +1 -0
- package/dist/shared-prefix.js +151 -0
- package/dist/shared-prefix.js.map +1 -0
- package/dist/similarity.d.ts +13 -0
- package/dist/similarity.d.ts.map +1 -0
- package/dist/similarity.js +30 -0
- package/dist/similarity.js.map +1 -0
- package/dist/structure.d.ts +144 -0
- package/dist/structure.d.ts.map +1 -0
- package/dist/structure.js +455 -0
- package/dist/structure.js.map +1 -0
- package/dist/suggest.d.ts +100 -0
- package/dist/suggest.d.ts.map +1 -0
- package/dist/suggest.js +151 -0
- package/dist/suggest.js.map +1 -0
- package/dist/tokenizer.d.ts +57 -0
- package/dist/tokenizer.d.ts.map +1 -0
- package/dist/tokenizer.js +157 -0
- package/dist/tokenizer.js.map +1 -0
- package/dist/types.d.ts +296 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/walk.d.ts +40 -0
- package/dist/walk.d.ts.map +1 -0
- package/dist/walk.js +95 -0
- package/dist/walk.js.map +1 -0
- package/package.json +56 -0
- package/src/advisories.ts +431 -0
- package/src/aws-sigv4.ts +174 -0
- package/src/baseline.ts +390 -0
- package/src/cache.ts +54 -0
- package/src/changes.ts +158 -0
- package/src/compare.ts +131 -0
- package/src/config-schema.ts +451 -0
- package/src/config.ts +161 -0
- package/src/detect.ts +312 -0
- package/src/evaluate.ts +188 -0
- package/src/extract.ts +336 -0
- package/src/gcp-auth.ts +166 -0
- package/src/glob.ts +160 -0
- package/src/host.ts +90 -0
- package/src/i18n/en.ts +236 -0
- package/src/i18n/es.ts +236 -0
- package/src/i18n/index.ts +68 -0
- package/src/i18n/types.ts +230 -0
- package/src/index.ts +228 -0
- package/src/llm.ts +708 -0
- package/src/nearest.ts +61 -0
- package/src/net.ts +233 -0
- package/src/node.ts +63 -0
- package/src/openrouter.ts +125 -0
- package/src/optimize.ts +228 -0
- package/src/otlp.ts +179 -0
- package/src/phrases.ts +1047 -0
- package/src/pricing-overlay.ts +319 -0
- package/src/pricing.ts +468 -0
- package/src/profile.ts +124 -0
- package/src/promptfoo.ts +213 -0
- package/src/prune.ts +211 -0
- package/src/reorder.ts +307 -0
- package/src/review.ts +180 -0
- package/src/rules.ts +324 -0
- package/src/savings.ts +121 -0
- package/src/segment.ts +106 -0
- package/src/shared-prefix.ts +198 -0
- package/src/similarity.ts +28 -0
- package/src/structure.ts +652 -0
- package/src/suggest.ts +254 -0
- package/src/tokenizer.ts +190 -0
- package/src/types.ts +323 -0
- package/src/walk.ts +117 -0
package/dist/pricing.js
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model and pricing catalogue (USD per million tokens).
|
|
3
|
+
*
|
|
4
|
+
* Source: official Claude API documentation. Prices change: check
|
|
5
|
+
* `PRICING_LAST_REVIEWED` and update this file before making budget decisions.
|
|
6
|
+
* Amazon Bedrock and Vertex AI pricing is set by each partner and is NOT the
|
|
7
|
+
* pricing in this table.
|
|
8
|
+
*/
|
|
9
|
+
export const PRICING_LAST_REVIEWED = '2026-06-24';
|
|
10
|
+
/** Cost multipliers relative to the input price. */
|
|
11
|
+
/**
|
|
12
|
+
* The defaults, which are Anthropic's numbers.
|
|
13
|
+
*
|
|
14
|
+
* Still exported and still correct for every Anthropic model, but no longer the
|
|
15
|
+
* whole story: a model can override any of these through `multipliers`, and
|
|
16
|
+
* anything computing a cost should go through `multipliersFor` rather than
|
|
17
|
+
* reading these directly. A cache read is ~10% of input on Anthropic and ~50%
|
|
18
|
+
* on OpenAI, and using one number for both invents a saving.
|
|
19
|
+
*/
|
|
20
|
+
export const COST_MULTIPLIERS = {
|
|
21
|
+
/** Cache write with a 5-minute TTL. */
|
|
22
|
+
cacheWrite5m: 1.25,
|
|
23
|
+
/** Cache write with a 1-hour TTL. */
|
|
24
|
+
cacheWrite1h: 2.0,
|
|
25
|
+
/** Cache read: ~10% of the input price. */
|
|
26
|
+
cacheRead: 0.1,
|
|
27
|
+
/** Batch API: 50% discount on input and output. */
|
|
28
|
+
batch: 0.5,
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* The multipliers that apply to one model, defaults filled in.
|
|
32
|
+
*
|
|
33
|
+
* `batch` stays `null` when the provider has no batch API, which is different
|
|
34
|
+
* from "unspecified": the first should stop the advisory firing, the second
|
|
35
|
+
* should fall back to the default.
|
|
36
|
+
*/
|
|
37
|
+
export function multipliersFor(model) {
|
|
38
|
+
const m = model.multipliers ?? {};
|
|
39
|
+
return {
|
|
40
|
+
cacheWrite5m: m.cacheWrite5m ?? COST_MULTIPLIERS.cacheWrite5m,
|
|
41
|
+
cacheWrite1h: m.cacheWrite1h ?? COST_MULTIPLIERS.cacheWrite1h,
|
|
42
|
+
cacheRead: m.cacheRead ?? COST_MULTIPLIERS.cacheRead,
|
|
43
|
+
batch: m.batch === undefined ? COST_MULTIPLIERS.batch : m.batch,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export const MODELS = [
|
|
47
|
+
{
|
|
48
|
+
id: 'claude-fable-5',
|
|
49
|
+
provider: 'anthropic',
|
|
50
|
+
displayName: 'Claude Fable 5',
|
|
51
|
+
inputPerMTok: 10,
|
|
52
|
+
outputPerMTok: 50,
|
|
53
|
+
contextWindow: 1_000_000,
|
|
54
|
+
cacheMinTokens: 512,
|
|
55
|
+
capability: 'frontier',
|
|
56
|
+
tier: 'frontier',
|
|
57
|
+
notes: 'Highest capability. Requires 30-day data retention.',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'claude-mythos-5',
|
|
61
|
+
provider: 'anthropic',
|
|
62
|
+
displayName: 'Claude Mythos 5',
|
|
63
|
+
recommendable: false,
|
|
64
|
+
inputPerMTok: 10,
|
|
65
|
+
outputPerMTok: 50,
|
|
66
|
+
contextWindow: 1_000_000,
|
|
67
|
+
cacheMinTokens: 512,
|
|
68
|
+
capability: 'frontier',
|
|
69
|
+
tier: 'frontier',
|
|
70
|
+
notes: 'Available only through Project Glasswing.',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: 'claude-opus-5',
|
|
74
|
+
provider: 'anthropic',
|
|
75
|
+
displayName: 'Claude Opus 5',
|
|
76
|
+
inputPerMTok: 5,
|
|
77
|
+
outputPerMTok: 25,
|
|
78
|
+
contextWindow: 1_000_000,
|
|
79
|
+
cacheMinTokens: 512,
|
|
80
|
+
capability: 'large',
|
|
81
|
+
tier: 'opus',
|
|
82
|
+
notes: '512-token cache minimum: caches prompts that would miss on Opus 4.6.',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: 'claude-opus-4-8',
|
|
86
|
+
provider: 'anthropic',
|
|
87
|
+
displayName: 'Claude Opus 4.8',
|
|
88
|
+
inputPerMTok: 5,
|
|
89
|
+
outputPerMTok: 25,
|
|
90
|
+
contextWindow: 1_000_000,
|
|
91
|
+
cacheMinTokens: 1024,
|
|
92
|
+
capability: 'large',
|
|
93
|
+
tier: 'opus',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: 'claude-opus-4-7',
|
|
97
|
+
provider: 'anthropic',
|
|
98
|
+
displayName: 'Claude Opus 4.7',
|
|
99
|
+
inputPerMTok: 5,
|
|
100
|
+
outputPerMTok: 25,
|
|
101
|
+
contextWindow: 1_000_000,
|
|
102
|
+
cacheMinTokens: 2048,
|
|
103
|
+
capability: 'large',
|
|
104
|
+
tier: 'opus',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: 'claude-opus-4-6',
|
|
108
|
+
provider: 'anthropic',
|
|
109
|
+
displayName: 'Claude Opus 4.6',
|
|
110
|
+
inputPerMTok: 5,
|
|
111
|
+
outputPerMTok: 25,
|
|
112
|
+
contextWindow: 1_000_000,
|
|
113
|
+
cacheMinTokens: 4096,
|
|
114
|
+
capability: 'large',
|
|
115
|
+
tier: 'opus',
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: 'claude-sonnet-5',
|
|
119
|
+
provider: 'anthropic',
|
|
120
|
+
displayName: 'Claude Sonnet 5',
|
|
121
|
+
inputPerMTok: 3,
|
|
122
|
+
outputPerMTok: 15,
|
|
123
|
+
contextWindow: 1_000_000,
|
|
124
|
+
cacheMinTokens: 1024,
|
|
125
|
+
capability: 'mid',
|
|
126
|
+
tier: 'sonnet',
|
|
127
|
+
promo: { inputPerMTok: 2, outputPerMTok: 10, until: '2026-08-31' },
|
|
128
|
+
notes: 'Introductory pricing of 2/10 until 2026-08-31; 3/15 afterwards.',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: 'claude-sonnet-4-6',
|
|
132
|
+
provider: 'anthropic',
|
|
133
|
+
displayName: 'Claude Sonnet 4.6',
|
|
134
|
+
inputPerMTok: 3,
|
|
135
|
+
outputPerMTok: 15,
|
|
136
|
+
contextWindow: 1_000_000,
|
|
137
|
+
cacheMinTokens: 1024,
|
|
138
|
+
capability: 'mid',
|
|
139
|
+
tier: 'sonnet',
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: 'claude-haiku-4-5',
|
|
143
|
+
provider: 'anthropic',
|
|
144
|
+
displayName: 'Claude Haiku 4.5',
|
|
145
|
+
inputPerMTok: 1,
|
|
146
|
+
outputPerMTok: 5,
|
|
147
|
+
contextWindow: 200_000,
|
|
148
|
+
cacheMinTokens: 4096,
|
|
149
|
+
capability: 'small',
|
|
150
|
+
tier: 'haiku',
|
|
151
|
+
notes: '200K context window and a 64K output cap, smaller than the rest.',
|
|
152
|
+
},
|
|
153
|
+
// ------------------------------------------------------------------------
|
|
154
|
+
// OpenAI
|
|
155
|
+
//
|
|
156
|
+
// Caching is AUTOMATIC above 1,024 tokens — there is no cache_control to set,
|
|
157
|
+
// and a cached read costs about half the input price rather than a tenth. The
|
|
158
|
+
// reordering advice is identical (a prefix is a prefix), the marker advice is
|
|
159
|
+
// not, which is why `caching` and `multipliers` exist at all.
|
|
160
|
+
// ------------------------------------------------------------------------
|
|
161
|
+
{
|
|
162
|
+
id: 'gpt-5',
|
|
163
|
+
provider: 'openai',
|
|
164
|
+
displayName: 'GPT-5',
|
|
165
|
+
inputPerMTok: 1.25,
|
|
166
|
+
outputPerMTok: 10,
|
|
167
|
+
contextWindow: 400_000,
|
|
168
|
+
cacheMinTokens: 1024,
|
|
169
|
+
caching: 'automatic',
|
|
170
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
171
|
+
capability: 'frontier',
|
|
172
|
+
tier: 'frontier',
|
|
173
|
+
notes: 'Caching is automatic above 1,024 tokens; there is no cache_control to set.',
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
id: 'gpt-5-mini',
|
|
177
|
+
provider: 'openai',
|
|
178
|
+
displayName: 'GPT-5 mini',
|
|
179
|
+
inputPerMTok: 0.25,
|
|
180
|
+
outputPerMTok: 2,
|
|
181
|
+
contextWindow: 400_000,
|
|
182
|
+
cacheMinTokens: 1024,
|
|
183
|
+
caching: 'automatic',
|
|
184
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
185
|
+
capability: 'mid',
|
|
186
|
+
tier: 'sonnet',
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
id: 'gpt-5-nano',
|
|
190
|
+
provider: 'openai',
|
|
191
|
+
displayName: 'GPT-5 nano',
|
|
192
|
+
inputPerMTok: 0.05,
|
|
193
|
+
outputPerMTok: 0.4,
|
|
194
|
+
contextWindow: 400_000,
|
|
195
|
+
cacheMinTokens: 1024,
|
|
196
|
+
caching: 'automatic',
|
|
197
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
198
|
+
capability: 'small',
|
|
199
|
+
tier: 'haiku',
|
|
200
|
+
},
|
|
201
|
+
// ------------------------------------------------------------------------
|
|
202
|
+
// Google
|
|
203
|
+
// ------------------------------------------------------------------------
|
|
204
|
+
{
|
|
205
|
+
id: 'gemini-2.5-pro',
|
|
206
|
+
provider: 'google',
|
|
207
|
+
displayName: 'Gemini 2.5 Pro',
|
|
208
|
+
inputPerMTok: 1.25,
|
|
209
|
+
outputPerMTok: 10,
|
|
210
|
+
contextWindow: 1_048_576,
|
|
211
|
+
cacheMinTokens: 2048,
|
|
212
|
+
caching: 'explicit',
|
|
213
|
+
multipliers: { cacheRead: 0.25, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
214
|
+
capability: 'large',
|
|
215
|
+
tier: 'opus',
|
|
216
|
+
notes: 'Context caching is billed for storage per hour as well as per read.',
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: 'gemini-2.5-flash',
|
|
220
|
+
provider: 'google',
|
|
221
|
+
displayName: 'Gemini 2.5 Flash',
|
|
222
|
+
inputPerMTok: 0.3,
|
|
223
|
+
outputPerMTok: 2.5,
|
|
224
|
+
contextWindow: 1_048_576,
|
|
225
|
+
cacheMinTokens: 1024,
|
|
226
|
+
caching: 'explicit',
|
|
227
|
+
multipliers: { cacheRead: 0.25, cacheWrite5m: 1, cacheWrite1h: 1, batch: 0.5 },
|
|
228
|
+
capability: 'mid',
|
|
229
|
+
tier: 'sonnet',
|
|
230
|
+
},
|
|
231
|
+
// ------------------------------------------------------------------------
|
|
232
|
+
// Moonshot
|
|
233
|
+
// ------------------------------------------------------------------------
|
|
234
|
+
{
|
|
235
|
+
id: 'kimi-k2',
|
|
236
|
+
provider: 'moonshot',
|
|
237
|
+
displayName: 'Kimi K2',
|
|
238
|
+
inputPerMTok: 0.6,
|
|
239
|
+
outputPerMTok: 2.5,
|
|
240
|
+
contextWindow: 256_000,
|
|
241
|
+
cacheMinTokens: 1024,
|
|
242
|
+
caching: 'automatic',
|
|
243
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
244
|
+
capability: 'mid',
|
|
245
|
+
tier: 'sonnet',
|
|
246
|
+
notes: 'No batch API: the batch advisory stays quiet rather than offering a discount you cannot buy.',
|
|
247
|
+
},
|
|
248
|
+
// ------------------------------------------------------------------------
|
|
249
|
+
// DeepSeek
|
|
250
|
+
// ------------------------------------------------------------------------
|
|
251
|
+
{
|
|
252
|
+
id: 'deepseek-v3',
|
|
253
|
+
provider: 'deepseek',
|
|
254
|
+
displayName: 'DeepSeek V3',
|
|
255
|
+
inputPerMTok: 0.27,
|
|
256
|
+
outputPerMTok: 1.1,
|
|
257
|
+
contextWindow: 128_000,
|
|
258
|
+
cacheMinTokens: 1024,
|
|
259
|
+
caching: 'automatic',
|
|
260
|
+
multipliers: { cacheRead: 0.1, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
261
|
+
capability: 'mid',
|
|
262
|
+
tier: 'sonnet',
|
|
263
|
+
},
|
|
264
|
+
// ------------------------------------------------------------------------
|
|
265
|
+
// xAI
|
|
266
|
+
// ------------------------------------------------------------------------
|
|
267
|
+
{
|
|
268
|
+
id: 'grok-4',
|
|
269
|
+
provider: 'xai',
|
|
270
|
+
displayName: 'Grok 4',
|
|
271
|
+
inputPerMTok: 3,
|
|
272
|
+
outputPerMTok: 15,
|
|
273
|
+
contextWindow: 256_000,
|
|
274
|
+
cacheMinTokens: 1024,
|
|
275
|
+
caching: 'automatic',
|
|
276
|
+
multipliers: { cacheRead: 0.25, cacheWrite5m: 1, cacheWrite1h: 1, batch: null },
|
|
277
|
+
capability: 'large',
|
|
278
|
+
tier: 'opus',
|
|
279
|
+
},
|
|
280
|
+
// ------------------------------------------------------------------------
|
|
281
|
+
// Mistral
|
|
282
|
+
// ------------------------------------------------------------------------
|
|
283
|
+
{
|
|
284
|
+
id: 'mistral-large-2',
|
|
285
|
+
provider: 'mistral',
|
|
286
|
+
displayName: 'Mistral Large 2',
|
|
287
|
+
inputPerMTok: 2,
|
|
288
|
+
outputPerMTok: 6,
|
|
289
|
+
contextWindow: 128_000,
|
|
290
|
+
cacheMinTokens: 0,
|
|
291
|
+
caching: 'none',
|
|
292
|
+
multipliers: { batch: 0.5 },
|
|
293
|
+
capability: 'large',
|
|
294
|
+
tier: 'opus',
|
|
295
|
+
notes: 'No prompt caching: reordering still helps readability but saves nothing here.',
|
|
296
|
+
},
|
|
297
|
+
];
|
|
298
|
+
export const DEFAULT_MODEL = 'claude-opus-5';
|
|
299
|
+
function makeCatalogue(models, lastReviewed, overriddenModels = [], addedModels = []) {
|
|
300
|
+
return {
|
|
301
|
+
models,
|
|
302
|
+
byId: new Map(models.map((m) => [m.id, m])),
|
|
303
|
+
lastReviewed,
|
|
304
|
+
overriddenModels,
|
|
305
|
+
addedModels,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
/** The prices compiled into this release. */
|
|
309
|
+
export const BUNDLED_CATALOGUE = makeCatalogue(MODELS, PRICING_LAST_REVIEWED);
|
|
310
|
+
/** Looks a model up in a specific catalogue. */
|
|
311
|
+
export function modelFrom(catalogue, id) {
|
|
312
|
+
const model = catalogue.byId.get(id);
|
|
313
|
+
if (!model) {
|
|
314
|
+
throw new Error(`Unknown model: "${id}". Available: ${catalogue.models.map((m) => m.id).join(', ')}`);
|
|
315
|
+
}
|
|
316
|
+
return model;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Cheapest model of a tier within a catalogue.
|
|
320
|
+
*
|
|
321
|
+
* Compared on the **effective** price, so a model in a promotional window is
|
|
322
|
+
* ranked at what it actually costs today rather than at its list price.
|
|
323
|
+
*/
|
|
324
|
+
export function cheapestOfTierIn(catalogue, tier, on = new Date(),
|
|
325
|
+
/**
|
|
326
|
+
* Restrict to one provider. Omit to search the whole catalogue, which is what
|
|
327
|
+
* this did before other providers existed — kept as the default so the
|
|
328
|
+
* signature stays additive, though the advisory always passes one: switching
|
|
329
|
+
* vendor is a migration, not a cheaper model.
|
|
330
|
+
*/
|
|
331
|
+
provider) {
|
|
332
|
+
const candidates = catalogue.models.filter((m) => m.tier === tier &&
|
|
333
|
+
m.recommendable !== false &&
|
|
334
|
+
(provider === undefined || m.provider === provider));
|
|
335
|
+
const first = candidates[0];
|
|
336
|
+
if (!first)
|
|
337
|
+
throw new Error(`No models for tier "${tier}"`);
|
|
338
|
+
return candidates.reduce((best, m) => effectivePricing(m, on).inputPerMTok < effectivePricing(best, on).inputPerMTok ? m : best, first);
|
|
339
|
+
}
|
|
340
|
+
export function getModel(id) {
|
|
341
|
+
return modelFrom(BUNDLED_CATALOGUE, id);
|
|
342
|
+
}
|
|
343
|
+
export function listModels() {
|
|
344
|
+
return [...MODELS];
|
|
345
|
+
}
|
|
346
|
+
/** Effective price on a given date, applying any live promotion. */
|
|
347
|
+
export function effectivePricing(model, on = new Date()) {
|
|
348
|
+
if (model.promo) {
|
|
349
|
+
const until = new Date(`${model.promo.until}T23:59:59.999Z`);
|
|
350
|
+
if (on.getTime() <= until.getTime()) {
|
|
351
|
+
return {
|
|
352
|
+
inputPerMTok: model.promo.inputPerMTok,
|
|
353
|
+
outputPerMTok: model.promo.outputPerMTok,
|
|
354
|
+
promoApplied: true,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return {
|
|
359
|
+
inputPerMTok: model.inputPerMTok,
|
|
360
|
+
outputPerMTok: model.outputPerMTok,
|
|
361
|
+
promoApplied: false,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
/** Cheapest model of each capability tier, for recommendations. */
|
|
365
|
+
export function cheapestOfTier(tier) {
|
|
366
|
+
const candidates = MODELS.filter((m) => m.tier === tier && m.recommendable !== false);
|
|
367
|
+
const first = candidates[0];
|
|
368
|
+
if (!first)
|
|
369
|
+
throw new Error(`No models for tier "${tier}"`);
|
|
370
|
+
return candidates.reduce((best, m) => (m.inputPerMTok < best.inputPerMTok ? m : best), first);
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Whole days between a `YYYY-MM-DD` review date and now, or `null`.
|
|
374
|
+
*
|
|
375
|
+
* Every dollar figure Trazum prints descends from a price list, and the list
|
|
376
|
+
* carries the date it was checked. Printing only that date makes the reader do
|
|
377
|
+
* arithmetic against today to learn the one thing they wanted to know — whether
|
|
378
|
+
* to trust it — and a reader who is not already suspicious will not bother.
|
|
379
|
+
*
|
|
380
|
+
* `now` is a parameter for the reason `computeSavings` takes a `Date`: a function
|
|
381
|
+
* that reads the clock can only be asserted for shape.
|
|
382
|
+
*
|
|
383
|
+
* Compared at UTC midnight on both sides, so the answer does not change by one
|
|
384
|
+
* depending on what time of day the command runs. `null` for anything that is not
|
|
385
|
+
* a date — an overlay supplies this string, and a wrong one should read as
|
|
386
|
+
* unknown rather than as a confident number computed from `NaN`.
|
|
387
|
+
*/
|
|
388
|
+
export function reviewAgeDays(lastReviewed, now) {
|
|
389
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(lastReviewed))
|
|
390
|
+
return null;
|
|
391
|
+
const then = Date.parse(`${lastReviewed}T00:00:00Z`);
|
|
392
|
+
if (Number.isNaN(then))
|
|
393
|
+
return null;
|
|
394
|
+
const today = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
|
|
395
|
+
const days = Math.round((today - then) / 86_400_000);
|
|
396
|
+
// A future date is a typo or a clock problem, not an age. Reported as unknown
|
|
397
|
+
// rather than as a negative number of days, which reads like a bug either way.
|
|
398
|
+
return days < 0 ? null : days;
|
|
399
|
+
}
|
|
400
|
+
//# sourceMappingURL=pricing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pricing.js","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { TokenCounter } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* What a prompt is made of, so a team can decide which one to fix first.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this is not a score
|
|
6
|
+
*
|
|
7
|
+
* The obvious shape for "which prompts should we optimise?" is a number out of
|
|
8
|
+
* a hundred, and it is the wrong shape. `Complexity: 74` cannot be argued with,
|
|
9
|
+
* cannot be reproduced by hand, and does not tell anybody what to do on Monday.
|
|
10
|
+
* Worse, the weights that turn four measurements into one number are invented —
|
|
11
|
+
* and once invented they get tuned until the ranking looks right, which is
|
|
12
|
+
* fitting the metric to the answer.
|
|
13
|
+
*
|
|
14
|
+
* So this returns **the measurements**, each with a definition you can check
|
|
15
|
+
* against the prompt in front of you, and ranks on the one quantity that is not
|
|
16
|
+
* a matter of opinion: **what optimising it would actually save.** That figure
|
|
17
|
+
* comes from running the deterministic rules, not from a formula.
|
|
18
|
+
*
|
|
19
|
+
* The structural facts are the *explanation* for a prompt's position in that
|
|
20
|
+
* ranking, not a substitute for it. "1,204 tokens across 8 sentences" says why
|
|
21
|
+
* a prompt is worth looking at; "$310 a month recoverable" says whether it is
|
|
22
|
+
* worth looking at before the other thirty-nine.
|
|
23
|
+
*
|
|
24
|
+
* ## What each measurement means, exactly
|
|
25
|
+
*
|
|
26
|
+
* - **sentences** — spans ending in `.`, `!`, `?`, `。`, `!`, `?`, or a line
|
|
27
|
+
* break where the line ends without punctuation (a bullet is a sentence).
|
|
28
|
+
* Protected content is excluded, so a code block is not forty sentences.
|
|
29
|
+
* - **tokensPerSentence** — the verbosity signal, and the only ratio here. It
|
|
30
|
+
* is length-independent: a padded 300-token prompt and a padded 3,000-token
|
|
31
|
+
* one look the same, which is the point. Reported with units, never as an
|
|
32
|
+
* index.
|
|
33
|
+
* - **examples / exampleTokens** — from `findExamples`, the same detector the
|
|
34
|
+
* advisories use. Few-shot examples are usually the largest single block in
|
|
35
|
+
* an expensive prompt and the easiest to trim by one.
|
|
36
|
+
* - **formatTokens** — a restated output format (a JSON schema written out
|
|
37
|
+
* twice, or written out at all when the API takes a schema parameter).
|
|
38
|
+
* - **protectedTokens** — code, URLs, placeholders and tags. Counted separately
|
|
39
|
+
* because **no amount of optimising will touch them**, and a prompt that is
|
|
40
|
+
* 80% code has far less headroom than its size suggests. Leaving this out is
|
|
41
|
+
* how a ranking sends somebody to spend an afternoon on a file that cannot
|
|
42
|
+
* move.
|
|
43
|
+
*/
|
|
44
|
+
export interface PromptProfile {
|
|
45
|
+
tokens: number;
|
|
46
|
+
/** Tokens that cannot be touched: code, URLs, placeholders, tags. */
|
|
47
|
+
protectedTokens: number;
|
|
48
|
+
sentences: number;
|
|
49
|
+
/** `tokens / sentences`, rounded to one decimal. Zero when there are none. */
|
|
50
|
+
tokensPerSentence: number;
|
|
51
|
+
examples: number;
|
|
52
|
+
exampleTokens: number;
|
|
53
|
+
/** Tokens in a restated output format, or 0. */
|
|
54
|
+
formatTokens: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Sentences in the mutable part of the prompt.
|
|
58
|
+
*
|
|
59
|
+
* Deliberately simple and stated rather than clever. An abbreviation splits a
|
|
60
|
+
* sentence in two here, and that is accepted: the number is used as a
|
|
61
|
+
* denominator for a verbosity ratio, where a few percent of noise changes
|
|
62
|
+
* nothing, and the alternative is a sentence tokeniser this project would then
|
|
63
|
+
* have to defend.
|
|
64
|
+
*/
|
|
65
|
+
export declare function countSentences(prompt: string): number;
|
|
66
|
+
export interface ProfileOptions {
|
|
67
|
+
count?: TokenCounter;
|
|
68
|
+
}
|
|
69
|
+
/** Measures a prompt. Deterministic, offline, and free. */
|
|
70
|
+
export declare function profilePrompt(prompt: string, options?: ProfileOptions): PromptProfile;
|
|
71
|
+
//# sourceMappingURL=profile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,YAAY,EAAE,MAAM,CAAC;CACtB;AAID;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAuBrD;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED,2DAA2D;AAC3D,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,aAAa,CAqBzF"}
|
package/dist/profile.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { segment } from './segment.js';
|
|
2
|
+
import { findExamples, findRestatedFormat } from './structure.js';
|
|
3
|
+
import { estimateTokens } from './tokenizer.js';
|
|
4
|
+
const SENTENCE_END = /[.!?。!?]+/;
|
|
5
|
+
/**
|
|
6
|
+
* Sentences in the mutable part of the prompt.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately simple and stated rather than clever. An abbreviation splits a
|
|
9
|
+
* sentence in two here, and that is accepted: the number is used as a
|
|
10
|
+
* denominator for a verbosity ratio, where a few percent of noise changes
|
|
11
|
+
* nothing, and the alternative is a sentence tokeniser this project would then
|
|
12
|
+
* have to defend.
|
|
13
|
+
*/
|
|
14
|
+
export function countSentences(prompt) {
|
|
15
|
+
let total = 0;
|
|
16
|
+
for (const piece of segment(prompt)) {
|
|
17
|
+
if (piece.kind === 'protected')
|
|
18
|
+
continue;
|
|
19
|
+
for (const line of piece.text.split('\n')) {
|
|
20
|
+
const trimmed = line.trim();
|
|
21
|
+
if (trimmed === '')
|
|
22
|
+
continue;
|
|
23
|
+
const parts = trimmed
|
|
24
|
+
.split(SENTENCE_END)
|
|
25
|
+
.map((part) => part.trim())
|
|
26
|
+
.filter((part) => part !== '');
|
|
27
|
+
// A line with no terminal punctuation is still one thing being said — a
|
|
28
|
+
// bullet, a heading, a rule in a list. Counting it as zero would make
|
|
29
|
+
// every bulleted prompt look infinitely verbose.
|
|
30
|
+
total += Math.max(1, parts.length);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return total;
|
|
34
|
+
}
|
|
35
|
+
/** Measures a prompt. Deterministic, offline, and free. */
|
|
36
|
+
export function profilePrompt(prompt, options = {}) {
|
|
37
|
+
const count = options.count ?? estimateTokens;
|
|
38
|
+
const tokens = count(prompt);
|
|
39
|
+
const protectedTokens = segment(prompt)
|
|
40
|
+
.filter((piece) => piece.kind === 'protected')
|
|
41
|
+
.reduce((sum, piece) => sum + count(piece.text), 0);
|
|
42
|
+
const sentences = countSentences(prompt);
|
|
43
|
+
const examples = findExamples(prompt, count);
|
|
44
|
+
const format = findRestatedFormat(prompt, count);
|
|
45
|
+
return {
|
|
46
|
+
tokens,
|
|
47
|
+
protectedTokens,
|
|
48
|
+
sentences,
|
|
49
|
+
tokensPerSentence: sentences === 0 ? 0 : Math.round((tokens / sentences) * 10) / 10,
|
|
50
|
+
examples: examples.length,
|
|
51
|
+
exampleTokens: examples.reduce((sum, block) => sum + block.tokens, 0),
|
|
52
|
+
formatTokens: format?.restatedTokens ?? 0,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AA2DhD,MAAM,YAAY,GAAG,WAAW,CAAC;AAEjC;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACpC,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW;YAAE,SAAS;QAEzC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,OAAO,KAAK,EAAE;gBAAE,SAAS;YAE7B,MAAM,KAAK,GAAG,OAAO;iBAClB,KAAK,CAAC,YAAY,CAAC;iBACnB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;iBAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;YAEjC,wEAAwE;YACxE,sEAAsE;YACtE,iDAAiD;YACjD,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAMD,2DAA2D;AAC3D,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,OAAO,GAAmB,EAAE;IACxE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,cAAc,CAAC;IAE9C,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC;SACpC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC;SAC7C,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAEtD,MAAM,SAAS,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAEjD,OAAO;QACL,MAAM;QACN,eAAe;QACf,SAAS;QACT,iBAAiB,EAAE,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE;QACnF,QAAQ,EAAE,QAAQ,CAAC,MAAM;QACzB,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACrE,YAAY,EAAE,MAAM,EAAE,cAAc,IAAI,CAAC;KAC1C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exports a before/after pair as a suite somebody else's harness can run.
|
|
3
|
+
*
|
|
4
|
+
* `trazum eval` answers "does the model still say the same thing?" — semantic
|
|
5
|
+
* agreement, measured against the model's own variance. That is the question
|
|
6
|
+
* Trazum is qualified to ask, and it is not the question a team actually needs
|
|
7
|
+
* answered before shipping. *Their* question is whether the classifier still
|
|
8
|
+
* hits 94%, whether the JSON still parses, whether the refusal rate moved. Those
|
|
9
|
+
* are assertions about their task, and Trazum has no business inventing them.
|
|
10
|
+
*
|
|
11
|
+
* So this hands over the part it *can* build correctly — a suite in which the
|
|
12
|
+
* only variable is the prompt, with both versions and every case already wired
|
|
13
|
+
* — and leaves the assertions where they belong.
|
|
14
|
+
*
|
|
15
|
+
* ## Why JSON rather than YAML
|
|
16
|
+
*
|
|
17
|
+
* promptfoo reads `promptfooconfig.json` as readily as the YAML. This package
|
|
18
|
+
* has no dependencies and is not going to acquire a YAML emitter, and a
|
|
19
|
+
* hand-rolled one would be a quoting bug waiting for the first prompt
|
|
20
|
+
* containing a colon, a tab, or a line ending in a space. `JSON.stringify`
|
|
21
|
+
* escapes everything correctly, and JSON is a subset of YAML, so the file can
|
|
22
|
+
* be renamed if somebody prefers.
|
|
23
|
+
*/
|
|
24
|
+
export interface PromptfooExport {
|
|
25
|
+
/** The config, ready for `JSON.stringify`. */
|
|
26
|
+
config: Record<string, unknown>;
|
|
27
|
+
/** Things the reader has to know before trusting the run. */
|
|
28
|
+
warnings: PromptfooWarning[];
|
|
29
|
+
}
|
|
30
|
+
export type PromptfooWarning =
|
|
31
|
+
/** `${x}` or `{x}`: promptfoo substitutes `{{x}}` and will leave these alone. */
|
|
32
|
+
{
|
|
33
|
+
kind: 'unsupported-placeholder';
|
|
34
|
+
detail: string;
|
|
35
|
+
}
|
|
36
|
+
/** More than one distinct placeholder, and one value per case to fill them. */
|
|
37
|
+
| {
|
|
38
|
+
kind: 'multiple-placeholders';
|
|
39
|
+
detail: string;
|
|
40
|
+
}
|
|
41
|
+
/** The provider has no promptfoo id here, so the model string is a guess. */
|
|
42
|
+
| {
|
|
43
|
+
kind: 'unmapped-provider';
|
|
44
|
+
detail: string;
|
|
45
|
+
}
|
|
46
|
+
/** No placeholder at all: the case is appended, as `trazum eval` does. */
|
|
47
|
+
| {
|
|
48
|
+
kind: 'appended-input';
|
|
49
|
+
detail: string;
|
|
50
|
+
};
|
|
51
|
+
export interface PromptfooOptions {
|
|
52
|
+
/** Model id from Trazum's catalogue, mapped to a promptfoo provider. */
|
|
53
|
+
model?: string;
|
|
54
|
+
/** Rule level used to produce the optimised prompt, for the label. */
|
|
55
|
+
level?: string;
|
|
56
|
+
}
|
|
57
|
+
export declare function toPromptfoo(original: string, optimized: string, cases: readonly string[], options?: PromptfooOptions): PromptfooExport;
|
|
58
|
+
//# sourceMappingURL=promptfoo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promptfoo.d.ts","sourceRoot":"","sources":["../src/promptfoo.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,6DAA6D;IAC7D,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,MAAM,MAAM,gBAAgB;AAC1B,iFAAiF;AAC/E;IAAE,IAAI,EAAE,yBAAyB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACrD,+EAA+E;GAC7E;IAAE,IAAI,EAAE,uBAAuB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACnD,6EAA6E;GAC3E;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AAC/C,0EAA0E;GACxE;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/C,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA4ED,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,OAAO,GAAE,gBAAqB,GAC7B,eAAe,CAkFjB"}
|