purcar-chat-api / src /lib /tokenManager.ts
ihatebaselines's picture
Deploy Purcar gateway and local model engine
7e92e00 verified
Raw History Blame Contribute Delete
2.43 kB
import { UserProfile } from "@/types";
import { supabase } from './supabase';
export async function refreshTokenProfile() {
const {data:{user}}=await supabase.auth.getUser();
const day=new Date().toISOString().slice(0,10);
let used=0;
if(user){
const {data,error}=await supabase.from('ai_daily_usage').select('tokens_used').eq('user_id',user.id).eq('day',day).maybeSingle();
if(error) return;
used=data?.tokens_used || 0;
}
localStorage.setItem(STORE_KEY,JSON.stringify({id:user?.id || 'local',plan:'free',dailyTokenLimit:DAILY_FREE,tokensUsedToday:used,lastResetDate:day}));
window.dispatchEvent(new Event('purcar_token_update'));
}
const STORE_KEY = "purcar_user_v3";
export const DAILY_FREE = 50000; // 50k initial/daily tokens covering input + output
function todayKey(): string {
return new Date().toISOString().split("T")[0];
}
export function getLocalUserProfile(): UserProfile {
if (typeof window === "undefined") {
return { id: "local", plan: "free", dailyTokenLimit: DAILY_FREE, tokensUsedToday: 0, lastResetDate: todayKey() };
}
try {
const raw = localStorage.getItem(STORE_KEY);
if (!raw) return fresh();
const p: UserProfile = JSON.parse(raw);
// Ensure existing profiles receive the full 50k token budget
if (p.dailyTokenLimit < DAILY_FREE) {
p.dailyTokenLimit = DAILY_FREE;
}
if (p.lastResetDate !== todayKey()) {
const reset = { ...p, dailyTokenLimit: DAILY_FREE, tokensUsedToday: 0, lastResetDate: todayKey() };
localStorage.setItem(STORE_KEY, JSON.stringify(reset));
return reset;
}
return p;
} catch {
return fresh();
}
}
function fresh(): UserProfile {
const p: UserProfile = { id: "local", plan: "free", dailyTokenLimit: DAILY_FREE, tokensUsedToday: 0, lastResetDate: todayKey() };
if (typeof window !== "undefined") localStorage.setItem(STORE_KEY, JSON.stringify(p));
return p;
}
export function consumeTokens(count: number): { success: boolean; remaining: number } {
void count;
void refreshTokenProfile();
const p=getLocalUserProfile();
// Display cache only. Admission and charging happen atomically on the server.
return {success:true,remaining:Math.max(0,p.dailyTokenLimit-p.tokensUsedToday)};
}
export function estimateTokenCount(text: string): number {
// Rough BPE estimate: ~0.75 words per token
return Math.max(1, Math.ceil(text.split(/\s+/).length * 1.33));
}