From 5936a959b9eeebc942a9ea84634416f5df990f83 Mon Sep 17 00:00:00 2001 From: audrzejq <31422031+audrzejq@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:34:25 +0100 Subject: [PATCH] add cron for models monitor --- packages/web/api/cron/monitor-chutes.ts | 34 +++++++++++++++++++++ packages/web/api/cron/monitor-openrouter.ts | 31 +++++++++++++++++++ vercel.json | 10 ++++++ 3 files changed, 75 insertions(+) create mode 100644 packages/web/api/cron/monitor-chutes.ts create mode 100644 packages/web/api/cron/monitor-openrouter.ts diff --git a/packages/web/api/cron/monitor-chutes.ts b/packages/web/api/cron/monitor-chutes.ts new file mode 100644 index 000000000..9573397d1 --- /dev/null +++ b/packages/web/api/cron/monitor-chutes.ts @@ -0,0 +1,34 @@ +import { runMonitor, round6 } from '../../../../models-monitor/lib.ts' + +export const config = { maxDuration: 60 } + +export default async function handler(req: Request) { + if (req.headers.get('authorization') !== `Bearer ${process.env.CRON_SECRET}`) { + return new Response('Unauthorized', { status: 401 }) + } + + try { + await runMonitor({ + name: 'Chutes', + title: '🛰 Chutes Models Update', + s3Key: 'chutes/snapshot.json', + async fetchModels() { + const headers: Record = {} + if (process.env.CHUTES_API_KEY) headers['Authorization'] = `Bearer ${process.env.CHUTES_API_KEY}` + + const res = await fetch('https://llm.chutes.ai/v1/models', { headers }) + if (!res.ok) throw new Error(`Chutes API error: ${res.status}`) + const data = await res.json() as { data: Array<{ id: string; pricing: { prompt: number; completion: number } }> } + return data.data.map(m => ({ + id: m.id, + price_prompt: round6(m.pricing.prompt), + price_completion: round6(m.pricing.completion), + })) + }, + }) + return new Response('ok') + } catch (err) { + console.error(err) + return new Response(String(err), { status: 500 }) + } +} diff --git a/packages/web/api/cron/monitor-openrouter.ts b/packages/web/api/cron/monitor-openrouter.ts new file mode 100644 index 000000000..bd08cde56 --- /dev/null +++ b/packages/web/api/cron/monitor-openrouter.ts @@ -0,0 +1,31 @@ +import { runMonitor, round6 } from '../../../../models-monitor/lib.ts' + +export const config = { maxDuration: 60 } + +export default async function handler(req: Request) { + if (req.headers.get('authorization') !== `Bearer ${process.env.CRON_SECRET}`) { + return new Response('Unauthorized', { status: 401 }) + } + + try { + await runMonitor({ + name: 'OpenRouter', + title: '🛰 OpenRouter Models Update', + s3Key: 'openrouter/snapshot.json', + async fetchModels() { + const res = await fetch('https://openrouter.ai/api/v1/models') + if (!res.ok) throw new Error(`OpenRouter API error: ${res.status}`) + const data = await res.json() as { data: Array<{ id: string; pricing: { prompt: string; completion: string } }> } + return data.data.map(m => ({ + id: m.id, + price_prompt: round6(parseFloat(m.pricing.prompt) * 1_000_000), + price_completion: round6(parseFloat(m.pricing.completion) * 1_000_000), + })) + }, + }) + return new Response('ok') + } catch (err) { + console.error(err) + return new Response(String(err), { status: 500 }) + } +} diff --git a/vercel.json b/vercel.json index 6022ed540..5727cf4fa 100644 --- a/vercel.json +++ b/vercel.json @@ -6,6 +6,16 @@ "installCommand": "bun install", "framework": null, "cleanUrls": false, + "crons": [ + { + "path": "/api/cron/monitor-openrouter", + "schedule": "50 * * * *" + }, + { + "path": "/api/cron/monitor-chutes", + "schedule": "50 * * * *" + } + ], "rewrites": [ { "source": "/api.json",