Skip to content

Commit eb84677

Browse files
committed
Make genai prices auto-update in the worker
1 parent 4e677db commit eb84677

File tree

4 files changed

+67
-5
lines changed

4 files changed

+67
-5
lines changed

gateway/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"@opentelemetry/api": "^1.9.0",
1212
"@opentelemetry/exporter-trace-otlp-http": "^0.203.0",
1313
"@opentelemetry/resources": "^2.0.1",
14-
"@pydantic/genai-prices": "~0.0.36",
14+
"@pydantic/genai-prices": "~0.0.38",
1515
"@pydantic/logfire-api": "^0.9.0",
1616
"eventsource-parser": "^3.0.6",
1717
"mime-types": "^3.0.1",

gateway/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import type { KeysDb, LimitDb } from './db'
1919
import { gateway } from './gateway'
2020
import type { DefaultProviderProxy, Middleware, Next } from './providers/default'
2121
import type { RateLimiter } from './rateLimiter'
22+
import { refreshGenaiPrices } from './refreshGenaiPrices'
2223
import type { SubFetch } from './types'
2324
import { ctHeader, ResponseError, response405, textResponse } from './utils'
2425

@@ -48,6 +49,7 @@ export async function gatewayFetch(
4849
ctx: ExecutionContext,
4950
options: GatewayOptions,
5051
): Promise<Response> {
52+
ctx.waitUntil(refreshGenaiPrices())
5153
let { pathname: proxyPath, search: queryString } = url
5254
if (options.proxyPrefixLength) {
5355
proxyPath = proxyPath.slice(options.proxyPrefixLength)

gateway/src/refreshGenaiPrices.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { type Provider, updatePrices, waitForUpdate } from '@pydantic/genai-prices'
2+
3+
// data will be refetched every 30 minutes
4+
const PRICE_TTL = 1000 * 60 * 30
5+
let genaiData: Provider[] | null = null
6+
let genaiDataTimestamp: number | null = null
7+
let isFetching = false
8+
9+
export function refreshGenaiPrices() {
10+
updatePrices(({ setProviderData, remoteDataUrl }) => {
11+
if (genaiDataTimestamp !== null) {
12+
console.debug('genai prices in-memory cache found')
13+
14+
if (genaiData !== null) {
15+
setProviderData(genaiData)
16+
}
17+
18+
if (Date.now() - genaiDataTimestamp < PRICE_TTL) {
19+
// this will be the most frequent, cheap path
20+
console.debug('genai prices in-memory data is fresh')
21+
return
22+
} else {
23+
console.debug('genai prices in-memory cache is stale, attempting to fetch remote data')
24+
}
25+
}
26+
27+
if (isFetching) {
28+
console.debug('genai-prices data fetch already in progress, skipping')
29+
return
30+
}
31+
32+
console.debug('Fetching genai-prices data')
33+
isFetching = true
34+
35+
// Note: **DO NOT** await this promise
36+
const freshDataPromise = fetch(remoteDataUrl)
37+
.then(async (response) => {
38+
if (!response.ok) {
39+
console.error('Failed fetching provider data, response status %d', response.status)
40+
return null
41+
}
42+
43+
const freshData = (await response.json()) as Provider[]
44+
console.debug('Updated genai prices data, %d providers', freshData.length)
45+
genaiDataTimestamp = Date.now()
46+
genaiData = freshData
47+
return freshData
48+
})
49+
.catch((error: unknown) => {
50+
console.error('Failed fetching provider data err: %o', error)
51+
return null
52+
})
53+
.finally(() => {
54+
isFetching = false
55+
})
56+
57+
setProviderData(freshDataPromise)
58+
})
59+
return waitForUpdate()
60+
}

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)