1import OpenAI from 'openai';
2import { encoding_for_model } from 'tiktoken';
3
4const openai = new OpenAI();
5const enc = encoding_for_model('gpt-4o-mini');
6
7// 1. Price table per 1M tokens (USD)
8const PRICES = {
9 'gpt-4o-mini': { in: 0.15, out: 0.60, cached_in: 0.075 },
10 'gpt-4o': { in: 2.50, out: 10.0, cached_in: 1.25 },
11};
12
13export async function metered(model: keyof typeof PRICES, prompt: string, orgId: string) {
14 // 2. Pre-flight token estimate
15 const estIn = enc.encode(prompt).length;
16 if (estIn > 30_000) throw new Error('Prompt too long — summarize first');
17
18 // 3. Call with usage tracking
19 const res = await openai.chat.completions.create({
20 model, messages: [{ role: 'user', content: prompt }],
21 });
22 const u = res.usage!;
23 const cachedIn = (u as any).prompt_tokens_details?.cached_tokens ?? 0;
24 const freshIn = u.prompt_tokens - cachedIn;
25
26 // 4. Compute exact cost
27 const p = PRICES[model];
28 const cost = (freshIn * p.in + cachedIn * p.cached_in + u.completion_tokens * p.out) / 1_000_000;
29
30 // 5. Write cost ledger row
31 await db.insert('llm_cost', {
32 ts: new Date(), org_id: orgId, model,
33 tokens_in: freshIn, tokens_cached: cachedIn, tokens_out: u.completion_tokens,
34 cost_usd: cost,
35 });
36
37 return { reply: res.choices[0].message.content, cost };
38}