Path: blob/master/src/packages/util/db-schema/purchase-quotas.ts
6030 views
import { CREATED_BY, ID } from "./crm";1import { SCHEMA as schema } from "./index";2import { LLM_USERNAMES } from "./llm-utils";3import type { Service } from "./purchases";4import { Table } from "./types";56export type { Service };78// Users will set their spend limits for these broad categories.9// TODO: right now there is a separate limit for each quota spec,10// which has got ridiculous.11const SERVICE_CATEGORIES = ["money", "compute", "license", "ai"];12type ServiceCategory = (typeof SERVICE_CATEGORIES)[number];1314export interface Spec {15display: string; // what to show user to describe this service16noSet?: boolean; // if true, then no spend limits are set for this.17color: string;18category: ServiceCategory;19// tooltip more detailed description20description?: string;21}2223export type QuotaSpec = Record<Service, Spec>;2425// for each category of service, this says whether or not it is a pay as you go service,26// which can impact how spend options are determined.27const IS_PAYG: { [name: ServiceCategory]: boolean } = {28money: false,29compute: true,30license: false,31ai: true,32} as const;3334export function isPaygService(service: Service): boolean {35const category = QUOTA_SPEC[service]?.category;36return IS_PAYG[category ?? ""] ?? false;37}3839const GOOGLE_AI_COLOR = "#ff4d4f";40const ANTHROPIC_COLOR = "#181818";41const OPENAI_COLOR = "#10a37f";42const MISTRALAI_COLOR = "#ff7000";43const XAI_COLOR = "#000000";4445const GPT_TURBO_128k: Spec = {46display: "OpenAI GPT-4 Turbo 128k",47color: OPENAI_COLOR,48category: "ai",49} as const;5051const GPT_TURBO_8K: Spec = {52...GPT_TURBO_128k,53display: "OpenAI GPT-4 Turbo",54} as const;5556const GPT_OMNI_128k: Spec = {57display: "OpenAI GPT-4o 128k",58color: OPENAI_COLOR,59category: "ai",60} as const;6162const GPT_OMNI_8K: Spec = {63...GPT_OMNI_128k,64display: "OpenAI GPT-4o",65} as const;6667const GPT_OMNI_MINI_128k: Spec = {68...GPT_OMNI_128k,69display: "OpenAI GPT-4o Mini 128k",70} as const;7172const GPT_OMNI_MINI_8K: Spec = {73...GPT_OMNI_MINI_128k,74display: "OpenAI GPT-4o Mini",75} as const;7677const GPT_41_8K: Spec = {78display: "OpenAI GPT-4.1",79color: OPENAI_COLOR,80category: "ai",81} as const;8283const GPT_41_MINI_8K: Spec = {84...GPT_41_8K,85display: "OpenAI GPT-4.1 Mini",86} as const;8788const GPT_O1_8K: Spec = {89...GPT_OMNI_128k,90display: "OpenAI o1",91} as const;9293const GPT_O1_MINI_8K: Spec = {94...GPT_O1_8K,95display: "OpenAI o1 mini",96} as const;9798const GPT_O3_8K: Spec = {99display: "OpenAI o3",100color: OPENAI_COLOR,101category: "ai",102} as const;103104const GPT_O3_128k: Spec = {105...GPT_O3_8K,106display: "OpenAI o3 128k",107} as const;108109const GPT_O4_MINI_8K: Spec = {110display: "OpenAI o4-mini",111color: OPENAI_COLOR,112category: "ai",113} as const;114115const GPT_O4_MINI_128k: Spec = {116...GPT_O4_MINI_8K,117display: "OpenAI o4-mini 128k",118} as const;119120const GPT_5_8K: Spec = {121display: "OpenAI GPT-5",122color: OPENAI_COLOR,123category: "ai",124} as const;125126const GPT_5_128k: Spec = {127...GPT_5_8K,128display: "OpenAI GPT-5 128k",129} as const;130131const GPT_5_2_8K: Spec = {132display: "OpenAI GPT-5.2",133color: OPENAI_COLOR,134category: "ai",135} as const;136137const GPT_5_2_128k: Spec = {138...GPT_5_2_8K,139display: "OpenAI GPT-5.2 128k",140} as const;141142const GPT_5_MINI_8K: Spec = {143display: "OpenAI GPT-5 Mini",144color: OPENAI_COLOR,145category: "ai",146} as const;147148const GPT_5_MINI_128k: Spec = {149...GPT_5_MINI_8K,150display: "OpenAI GPT-5 Mini 128k",151} as const;152153// NOTE: all-quotas-config.tsx will automatically filter out those, which are free or not selectable by the user154export const QUOTA_SPEC: QuotaSpec = {155credit: {156display: "Credit",157noSet: true,158color: "green",159category: "money",160description:161"Credit that was added to your account as a result of a manual or subscription payment (e.g., from a credit card)",162},163"auto-credit": {164display: "Automatic Credit",165noSet: true,166color: "green",167category: "money",168description:169"Credited that was automatically added to your account as a result of a payment because of your balance became low.",170},171refund: {172display: "Refund",173noSet: true,174color: "red",175category: "money",176description:177"Money that was refunded to your account as a result of a support request.",178},179"compute-server": {180display: "Compute Server",181color: "#2196f3",182category: "compute",183description: "Charge for creating or using a compute server.",184},185"compute-server-network-usage": {186display: "Network Data",187color: "#2196f3",188category: "compute",189description: "Charge due to network traffic out of a compute server.",190},191"compute-server-storage": {192display: "Cloud Storage",193color: "#fbbd05",194category: "compute",195description: "Charge due to storage of data on a cloud filesystem.",196},197license: {198display: "License",199color: "cyan",200noSet: true,201category: "license",202description: "Purchase of a license from the store.",203},204"edit-license": {205display: "Edit License",206color: "gold",207noSet: true,208category: "license",209description:210"Charge or credit resulting from changing a license, which includes you manually editing the license, or the license being edited to extend the validity date on subscription renewal.",211},212voucher: {213display: "Voucher",214color: "#00238b",215noSet: true,216category: "money",217description: "Charge for purchasing a voucher.",218},219// ATTN: LLMs comes below this line, the quotas above are the important ones to show first!220"openai-gpt-4": {221display: "OpenAI GPT-4",222color: OPENAI_COLOR,223category: "ai",224},225"openai-gpt-3.5-turbo": {226display: "OpenAI GPT-3.5",227color: OPENAI_COLOR,228category: "ai",229},230"openai-gpt-3.5-turbo-16k": {231display: "OpenAI GPT-3.5 16k",232color: OPENAI_COLOR,233category: "ai",234},235"openai-text-embedding-ada-002": {236display: "OpenAI Text Embedding Ada 002",237color: OPENAI_COLOR,238noSet: true, // because this model is not user visible yet239category: "ai",240},241"openai-gpt-4-32k": {242display: "OpenAI GPT-4 32k",243color: OPENAI_COLOR,244category: "ai",245},246"openai-gpt-4-turbo-preview": GPT_TURBO_128k, // the "preview" is over247"openai-gpt-4-turbo-preview-8k": GPT_TURBO_8K, // the "preview" is over248"openai-gpt-4-turbo": GPT_TURBO_128k,249"openai-gpt-4-turbo-8k": GPT_TURBO_8K,250"openai-gpt-4o": GPT_OMNI_128k,251"openai-gpt-4o-8k": GPT_OMNI_8K,252"openai-gpt-4o-mini": GPT_OMNI_MINI_128k,253"openai-gpt-4o-mini-8k": GPT_OMNI_MINI_8K,254"openai-gpt-4.1": GPT_41_8K,255"openai-gpt-4.1-mini": GPT_41_MINI_8K,256"openai-o1-mini-8k": GPT_O1_8K,257"openai-o1-8k": GPT_O1_MINI_8K,258"openai-o1-mini": GPT_O1_8K,259"openai-o1": GPT_O1_MINI_8K,260"openai-o3-8k": GPT_O3_8K,261"openai-o3": GPT_O3_128k,262"openai-o4-mini-8k": GPT_O4_MINI_8K,263"openai-o4-mini": GPT_O4_MINI_128k,264"openai-gpt-5-8k": GPT_5_8K,265"openai-gpt-5": GPT_5_128k,266"openai-gpt-5.2-8k": GPT_5_2_8K,267"openai-gpt-5.2": GPT_5_2_128k,268"openai-gpt-5-mini-8k": GPT_5_MINI_8K,269"openai-gpt-5-mini": GPT_5_MINI_128k,270"google-text-bison-001": {271display: "Google Palm 2 (Text)",272color: GOOGLE_AI_COLOR,273noSet: true, // deprecated, will be removed274category: "ai",275},276"google-chat-bison-001": {277display: "Google Palm 2 (Chat)",278color: GOOGLE_AI_COLOR,279noSet: true, // deprecated, will be removed280category: "ai",281},282"google-embedding-gecko-001": {283display: "Google Gecko (Embedding)",284color: GOOGLE_AI_COLOR,285noSet: true, // deprecated, will be removed286category: "ai",287},288"google-gemini-1.5-flash": {289display: "Google Gemini 1.5 Flash",290color: GOOGLE_AI_COLOR,291category: "ai",292},293"google-gemini-1.5-flash-8k": {294display: "Google Gemini 1.5 Flash",295color: GOOGLE_AI_COLOR,296category: "ai",297},298"google-gemini-pro": {299display: "Google Gemini 1.0 Pro",300color: GOOGLE_AI_COLOR,301category: "ai",302},303"google-gemini-1.0-ultra": {304display: "Google Gemini 1.0 Ultra",305color: GOOGLE_AI_COLOR,306category: "ai",307},308"google-gemini-1.5-pro-8k": {309display: LLM_USERNAMES["gemini-1.5-pro-8k"],310color: GOOGLE_AI_COLOR,311category: "ai",312},313"google-gemini-1.5-pro": {314display: LLM_USERNAMES["gemini-1.5-pro"],315color: GOOGLE_AI_COLOR,316category: "ai",317},318"google-gemini-2.0-flash-8k": {319display: LLM_USERNAMES["gemini-2.0-flash-8k"],320color: GOOGLE_AI_COLOR,321category: "ai",322},323"google-gemini-2.0-flash-lite-8k": {324display: LLM_USERNAMES["gemini-2.0-flash-lite-8k"],325color: GOOGLE_AI_COLOR,326category: "ai",327},328"google-gemini-2.5-flash-8k": {329display: LLM_USERNAMES["gemini-2.5-flash-8k"],330color: GOOGLE_AI_COLOR,331category: "ai",332},333"google-gemini-2.5-pro-8k": {334display: LLM_USERNAMES["gemini-2.5-pro-8k"],335color: GOOGLE_AI_COLOR,336category: "ai",337},338"google-gemini-3-flash-preview-16k": {339display: LLM_USERNAMES["gemini-3-flash-preview-16k"],340color: GOOGLE_AI_COLOR,341category: "ai",342},343"google-gemini-3-pro-preview-8k": {344display: LLM_USERNAMES["gemini-3-pro-preview-8k"],345color: GOOGLE_AI_COLOR,346category: "ai",347},348"xai-grok-4-1-fast-non-reasoning-16k": {349display: LLM_USERNAMES["grok-4-1-fast-non-reasoning-16k"],350color: XAI_COLOR,351category: "ai",352},353"xai-grok-4-1-fast-reasoning-16k": {354display: LLM_USERNAMES["grok-4-1-fast-reasoning-16k"],355color: XAI_COLOR,356category: "ai",357},358"xai-grok-code-fast-1-16k": {359display: LLM_USERNAMES["grok-code-fast-1-16k"],360color: XAI_COLOR,361category: "ai",362},363"anthropic-claude-3-opus": {364display: LLM_USERNAMES["claude-3-opus"],365color: ANTHROPIC_COLOR,366category: "ai",367},368"anthropic-claude-3-opus-8k": {369display: LLM_USERNAMES["claude-3-opus-8k"],370color: ANTHROPIC_COLOR,371category: "ai",372},373"anthropic-claude-3-sonnet": {374display: LLM_USERNAMES["claude-3-sonnet"],375color: ANTHROPIC_COLOR,376category: "ai",377},378"anthropic-claude-3-sonnet-4k": {379display: LLM_USERNAMES["claude-3-sonnet-4k"],380color: ANTHROPIC_COLOR,381category: "ai",382},383"anthropic-claude-3-5-sonnet": {384display: LLM_USERNAMES["claude-3-5-sonnet"],385color: ANTHROPIC_COLOR,386category: "ai",387},388"anthropic-claude-3-5-sonnet-4k": {389display: LLM_USERNAMES["claude-3-5-sonnet-4k"],390color: ANTHROPIC_COLOR,391category: "ai",392},393"anthropic-claude-3-haiku": {394display: LLM_USERNAMES["claude-3-haiku"],395color: ANTHROPIC_COLOR,396category: "ai",397},398"anthropic-claude-3-haiku-8k": {399display: LLM_USERNAMES["claude-3-haiku-8k"],400color: ANTHROPIC_COLOR,401category: "ai",402},403"anthropic-claude-3-5-haiku-8k": {404display: LLM_USERNAMES["claude-3-5-haiku-8k"],405color: ANTHROPIC_COLOR,406category: "ai",407},408"anthropic-claude-4-sonnet-8k": {409display: LLM_USERNAMES["claude-4-sonnet-8k"],410color: ANTHROPIC_COLOR,411category: "ai",412},413"anthropic-claude-4-opus-8k": {414display: LLM_USERNAMES["claude-4-opus-8k"],415color: ANTHROPIC_COLOR,416category: "ai",417},418"anthropic-claude-4-5-sonnet-8k": {419display: LLM_USERNAMES["claude-4-5-sonnet-8k"],420color: ANTHROPIC_COLOR,421category: "ai",422},423"anthropic-claude-4-6-sonnet-8k": {424display: LLM_USERNAMES["claude-4-6-sonnet-8k"],425color: ANTHROPIC_COLOR,426category: "ai",427},428"anthropic-claude-4-5-opus-8k": {429display: LLM_USERNAMES["claude-4-5-opus-8k"],430color: ANTHROPIC_COLOR,431category: "ai",432},433"anthropic-claude-4-6-opus-8k": {434display: LLM_USERNAMES["claude-4-6-opus-8k"],435color: ANTHROPIC_COLOR,436category: "ai",437},438"anthropic-claude-4-5-haiku-8k": {439display: LLM_USERNAMES["claude-4-5-haiku-8k"],440color: ANTHROPIC_COLOR,441category: "ai",442},443"mistralai-mistral-small-latest": {444display: LLM_USERNAMES["mistral-small-latest"],445color: MISTRALAI_COLOR, // the orange from their website446category: "ai",447},448"mistralai-mistral-medium-latest": {449display: LLM_USERNAMES["mistral-medium-latest"],450color: MISTRALAI_COLOR, // the orange from their website451category: "ai",452},453"mistralai-mistral-large-latest": {454display: LLM_USERNAMES["mistral-large-latest"],455color: MISTRALAI_COLOR, // the orange from their website456category: "ai",457},458"mistralai-devstral-medium-2507": {459display: LLM_USERNAMES["devstral-medium-2507"],460color: MISTRALAI_COLOR, // the orange from their website461category: "ai",462},463// "mistralai-magistral-medium-latest": {464// display: LLM_USERNAMES["magistral-medium-latest"],465// color: MISTRALAI_COLOR, // the orange from their website466// category: "ai",467// },468"project-upgrade": {469display: "Project Upgrade",470color: "#5bc0de",471category: "compute",472description:473"Charge resulting from using pay as you go upgrades to a project.",474},475} as const;476477// For pay-as-you-go project quota upgrades478export interface ProjectQuota {479cost?: number; // dollars per hour480enabled?: number;481cores?: number;482disk_quota?: number;483memory?: number;484mintime?: number;485network?: number;486member_host?: number;487always_running?: number;488}489490export const PROJECT_QUOTA_KEYS = new Set<string>([491"enabled",492"cost",493"cores",494"disk_quota",495"memory",496"mintime",497"network",498"member_host",499"always_running",500]);501502export function serviceToDisplay(service: Service): string {503return QUOTA_SPEC[service]?.display ?? service;504}505506Table({507name: "purchase_quotas",508fields: {509id: ID,510account_id: CREATED_BY,511service: {512title: "Service Category",513desc: "The service being charged for, e.g., openai-gpt-4, project-upgrade, etc.",514type: "string",515pg_type: "varchar(127)",516},517value: {518title: "Value",519desc: "The maximum amount that user can be charged for this service during one month billing period, in US dollars.",520type: "number", // actually comes back as string in queries.521pg_type: "REAL",522},523},524rules: {525desc: "Purchase Quotas",526primary_key: "id",527// make it fast to find all quotas for a given account528pg_indexes: ["account_id"],529// enforce that there is only one quota for each service for a given account530pg_unique_indexes: ["(account_id,service)"],531user_query: {532// set happens though v2 api only to enforce global quota533get: {534pg_where: [{ "account_id = $::UUID": "account_id" }],535fields: {536id: null,537account_id: null,538service: null,539value: null,540},541},542},543},544});545546Table({547name: "crm_purchase_quotas",548rules: {549virtual: "purchase_quotas",550primary_key: "id",551user_query: {552get: {553pg_where: [],554admin: true,555fields: {556id: null,557account_id: null,558service: null,559value: null,560},561},562set: {563admin: true,564fields: {565id: true,566account_id: true,567service: true,568value: true,569},570},571},572},573fields: schema.purchase_quotas.fields,574});575576577