@goodandready/dsh-moa 0.2.23 → 0.2.25
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/lib/pricing.js +93 -6
- package/package.json +1 -1
package/lib/pricing.js
CHANGED
|
@@ -28,6 +28,7 @@ export const DIRECT_VENDOR_RATES = {
|
|
|
28
28
|
export const FALLBACK_RATES = { input: 0.50, output: 1.50, cacheHit: 0.15 }
|
|
29
29
|
|
|
30
30
|
let memoryCatalog = null
|
|
31
|
+
let memoryCachePath = null
|
|
31
32
|
let lastFetchedAt = 0
|
|
32
33
|
|
|
33
34
|
/**
|
|
@@ -35,6 +36,7 @@ let lastFetchedAt = 0
|
|
|
35
36
|
*/
|
|
36
37
|
export function resetMemoryCatalog() {
|
|
37
38
|
memoryCatalog = null
|
|
39
|
+
memoryCachePath = null
|
|
38
40
|
lastFetchedAt = 0
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -42,13 +44,14 @@ export function resetMemoryCatalog() {
|
|
|
42
44
|
* Load cached catalog from disk into memory.
|
|
43
45
|
*/
|
|
44
46
|
export function loadCachedCatalog(cachePath = CACHE_FILE) {
|
|
45
|
-
if (memoryCatalog) return memoryCatalog
|
|
47
|
+
if (memoryCatalog && memoryCachePath === cachePath) return memoryCatalog
|
|
46
48
|
try {
|
|
47
49
|
if (existsSync(cachePath)) {
|
|
48
50
|
const raw = readFileSync(cachePath, 'utf8')
|
|
49
51
|
const parsed = JSON.parse(raw)
|
|
50
52
|
if (parsed && typeof parsed.models === 'object') {
|
|
51
53
|
memoryCatalog = parsed.models
|
|
54
|
+
memoryCachePath = cachePath
|
|
52
55
|
lastFetchedAt = parsed.updatedAt || 0
|
|
53
56
|
return memoryCatalog
|
|
54
57
|
}
|
|
@@ -57,6 +60,7 @@ export function loadCachedCatalog(cachePath = CACHE_FILE) {
|
|
|
57
60
|
// Ignore read/parse errors
|
|
58
61
|
}
|
|
59
62
|
memoryCatalog = {}
|
|
63
|
+
memoryCachePath = cachePath
|
|
60
64
|
return memoryCatalog
|
|
61
65
|
}
|
|
62
66
|
|
|
@@ -154,20 +158,103 @@ export function resolveModelRates(slot = {}, customPrices = {}, cachePath = CACH
|
|
|
154
158
|
if (catalog[fullKey]) return { ...catalog[fullKey] }
|
|
155
159
|
if (catalog[model]) return { ...catalog[model] }
|
|
156
160
|
|
|
157
|
-
|
|
161
|
+
if (!model) return { ...FALLBACK_RATES }
|
|
162
|
+
|
|
163
|
+
const entries = Object.entries(catalog)
|
|
164
|
+
if (entries.length === 0) return { ...FALLBACK_RATES }
|
|
165
|
+
|
|
166
|
+
// 3a. Exact case-insensitive fullKey match
|
|
167
|
+
for (const [catId, catRate] of entries) {
|
|
158
168
|
const catLower = catId.toLowerCase()
|
|
159
|
-
if (catLower
|
|
160
|
-
|
|
169
|
+
if (catLower === fullKey) return { ...catRate }
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 3b. Exact model match with provider matching
|
|
173
|
+
if (provider) {
|
|
174
|
+
for (const [catId, catRate] of entries) {
|
|
175
|
+
const catLower = catId.toLowerCase()
|
|
176
|
+
const slash = catLower.indexOf('/')
|
|
177
|
+
const catProv = slash !== -1 ? catLower.slice(0, slash) : ''
|
|
178
|
+
const catMod = slash !== -1 ? catLower.slice(slash + 1) : catLower
|
|
179
|
+
if (catProv === provider && (catMod === model || catLower.endsWith(`/${model}`))) {
|
|
180
|
+
return { ...catRate }
|
|
181
|
+
}
|
|
161
182
|
}
|
|
162
183
|
}
|
|
163
184
|
|
|
164
|
-
|
|
185
|
+
// 3c. Exact model match across providers or suffix match
|
|
186
|
+
for (const [catId, catRate] of entries) {
|
|
165
187
|
const catLower = catId.toLowerCase()
|
|
166
|
-
|
|
188
|
+
const slash = catLower.indexOf('/')
|
|
189
|
+
const catMod = slash !== -1 ? catLower.slice(slash + 1) : catLower
|
|
190
|
+
if (catMod === model || catLower.endsWith(`/${model}`)) {
|
|
167
191
|
return { ...catRate }
|
|
168
192
|
}
|
|
169
193
|
}
|
|
170
194
|
|
|
195
|
+
// 3d. Candidate resolution: prioritize same provider, select longest matching ID,
|
|
196
|
+
// and avoid bare substring collisions across distinct models with different rates.
|
|
197
|
+
let pool = entries
|
|
198
|
+
if (provider) {
|
|
199
|
+
const provEntries = entries.filter(([catId]) => {
|
|
200
|
+
const catLower = catId.toLowerCase()
|
|
201
|
+
const slash = catLower.indexOf('/')
|
|
202
|
+
return slash !== -1 && catLower.slice(0, slash) === provider
|
|
203
|
+
})
|
|
204
|
+
if (provEntries.length > 0) {
|
|
205
|
+
pool = provEntries
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let bestCandidates = []
|
|
210
|
+
let bestScore = 0
|
|
211
|
+
|
|
212
|
+
for (const [catId, catRate] of pool) {
|
|
213
|
+
const catLower = catId.toLowerCase()
|
|
214
|
+
const slash = catLower.indexOf('/')
|
|
215
|
+
const catMod = slash !== -1 ? catLower.slice(slash + 1) : catLower
|
|
216
|
+
|
|
217
|
+
let score = 0
|
|
218
|
+
if (model.startsWith(catMod + '-') || model.startsWith(catMod + ':') || model.startsWith(catMod + '.')) {
|
|
219
|
+
// Query model is a specific variant/revision of catalog model (e.g. model="claude-3-sonnet-20240229", catMod="claude-3-sonnet")
|
|
220
|
+
score = 10000 + catMod.length
|
|
221
|
+
} else if (catMod.startsWith(model + '-') || catMod.startsWith(model + ':') || catMod.startsWith(model + '.')) {
|
|
222
|
+
// Catalog model is a revision/variant of query model
|
|
223
|
+
score = 5000 + model.length
|
|
224
|
+
} else if (model.includes(catMod) && catMod.length >= 4) {
|
|
225
|
+
score = 2000 + catMod.length
|
|
226
|
+
} else if (catMod.includes(model) && model.length >= 4) {
|
|
227
|
+
score = 1000 + model.length
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (score > 0) {
|
|
231
|
+
if (score > bestScore) {
|
|
232
|
+
bestScore = score
|
|
233
|
+
bestCandidates = [{ catId, catRate, score, catMod }]
|
|
234
|
+
} else if (score === bestScore) {
|
|
235
|
+
bestCandidates.push({ catId, catRate, score, catMod })
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (bestCandidates.length === 1) {
|
|
241
|
+
return { ...bestCandidates[0].catRate }
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (bestCandidates.length > 1) {
|
|
245
|
+
const firstRate = bestCandidates[0].catRate
|
|
246
|
+
const allAgree = bestCandidates.every(c =>
|
|
247
|
+
c.catRate.input === firstRate.input &&
|
|
248
|
+
c.catRate.output === firstRate.output
|
|
249
|
+
)
|
|
250
|
+
if (allAgree) {
|
|
251
|
+
return { ...firstRate }
|
|
252
|
+
}
|
|
253
|
+
// Conflict between different models with different pricing sharing a substring:
|
|
254
|
+
// return safe fallback rate instead of assigning a wrong model's price.
|
|
255
|
+
return { ...FALLBACK_RATES }
|
|
256
|
+
}
|
|
257
|
+
|
|
171
258
|
// 4. Fallback rate
|
|
172
259
|
return { ...FALLBACK_RATES }
|
|
173
260
|
}
|