Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/util/db-schema/purchase-quotas.ts
1447 views
1
import { CREATED_BY, ID } from "./crm";
2
import { SCHEMA as schema } from "./index";
3
import { LLM_USERNAMES } from "./llm-utils";
4
import type { Service } from "./purchases";
5
import { Table } from "./types";
6
7
export type { Service };
8
9
// Users will set their spend limits for these broad categories.
10
// TODO: right now there is a separate limit for each quota spec,
11
// which has got ridiculous.
12
const SERVICE_CATEGORIES = ["money", "compute", "license", "ai"];
13
type ServiceCategory = (typeof SERVICE_CATEGORIES)[number];
14
15
export interface Spec {
16
display: string; // what to show user to describe this service
17
noSet?: boolean; // if true, then no spend limits are set for this.
18
color: string;
19
category: ServiceCategory;
20
// tooltip more detailed description
21
description?: string;
22
}
23
24
export type QuotaSpec = Record<Service, Spec>;
25
26
// for each category of service, this says whether or not it is a pay as you go service,
27
// which can impact how spend options are determined.
28
const IS_PAYG: { [name: ServiceCategory]: boolean } = {
29
money: false,
30
compute: true,
31
license: false,
32
ai: true,
33
} as const;
34
35
export function isPaygService(service: Service): boolean {
36
const category = QUOTA_SPEC[service]?.category;
37
return IS_PAYG[category ?? ""] ?? false;
38
}
39
40
const GPT_TURBO_128k: Spec = {
41
display: "OpenAI GPT-4 Turbo 128k",
42
color: "#10a37f",
43
category: "ai",
44
} as const;
45
46
const GPT_TURBO_8K: Spec = {
47
...GPT_TURBO_128k,
48
display: "OpenAI GPT-4 Turbo",
49
} as const;
50
51
const GPT_OMNI_128k: Spec = {
52
display: "OpenAI GPT-4o 128k",
53
color: "#10a37f",
54
category: "ai",
55
} as const;
56
57
const GPT_OMNI_8K: Spec = {
58
...GPT_OMNI_128k,
59
display: "OpenAI GPT-4o",
60
} as const;
61
62
const GPT_OMNI_MINI_128k: Spec = {
63
...GPT_OMNI_128k,
64
display: "OpenAI GPT-4o Mini 128k",
65
} as const;
66
67
const GPT_OMNI_MINI_8K: Spec = {
68
...GPT_OMNI_MINI_128k,
69
display: "OpenAI GPT-4o Mini",
70
} as const;
71
72
const GPT_41_8K: Spec = {
73
display: "OpenAI GPT-4.1",
74
color: "#10a37f",
75
category: "ai",
76
} as const;
77
78
const GPT_41_MINI_8K: Spec = {
79
...GPT_41_8K,
80
display: "OpenAI GPT-4.1 Mini",
81
} as const;
82
83
const GPT_O1_8K: Spec = {
84
...GPT_OMNI_128k,
85
display: "OpenAI o1",
86
} as const;
87
88
const GPT_O1_MINI_8K: Spec = {
89
...GPT_O1_8K,
90
display: "OpenAI o1 mini",
91
} as const;
92
93
const GOOGLE_AI_COLOR = "#ff4d4f";
94
95
// NOTE: all-quotas-config.tsx will automatically filter out those, which are free or not selectable by the user
96
export const QUOTA_SPEC: QuotaSpec = {
97
credit: {
98
display: "Credit",
99
noSet: true,
100
color: "green",
101
category: "money",
102
description:
103
"Credit that was added to your account as a result of a manual or subscription payment (e.g., from a credit card)",
104
},
105
"auto-credit": {
106
display: "Automatic Credit",
107
noSet: true,
108
color: "green",
109
category: "money",
110
description:
111
"Credited that was automatically added to your account as a result of a payment because of your balance became low.",
112
},
113
refund: {
114
display: "Refund",
115
noSet: true,
116
color: "red",
117
category: "money",
118
description:
119
"Money that was refunded to your account as a result of a support request.",
120
},
121
"compute-server": {
122
display: "Compute Server",
123
color: "#2196f3",
124
category: "compute",
125
description: "Charge for creating or using a compute server.",
126
},
127
"compute-server-network-usage": {
128
display: "Network Data",
129
color: "#2196f3",
130
category: "compute",
131
description: "Charge due to network traffic out of a compute server.",
132
},
133
"compute-server-storage": {
134
display: "Cloud Storage",
135
color: "#fbbd05",
136
category: "compute",
137
description: "Charge due to storage of data on a cloud filesystem.",
138
},
139
license: {
140
display: "License",
141
color: "cyan",
142
noSet: true,
143
category: "license",
144
description: "Purchase of a license from the store.",
145
},
146
"edit-license": {
147
display: "Edit License",
148
color: "gold",
149
noSet: true,
150
category: "license",
151
description:
152
"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.",
153
},
154
voucher: {
155
display: "Voucher",
156
color: "#00238b",
157
noSet: true,
158
category: "money",
159
description: "Charge for purchasing a voucher.",
160
},
161
// ATTN: LLMs comes below this line, the quotas above are the important ones to show first!
162
"openai-gpt-4": { display: "OpenAI GPT-4", color: "#10a37f", category: "ai" },
163
"openai-gpt-3.5-turbo": {
164
display: "OpenAI GPT-3.5",
165
color: "#10a37f",
166
category: "ai",
167
},
168
"openai-gpt-3.5-turbo-16k": {
169
display: "OpenAI GPT-3.5 16k",
170
color: "#10a37f",
171
category: "ai",
172
},
173
"openai-text-embedding-ada-002": {
174
display: "OpenAI Text Embedding Ada 002",
175
color: "#10a37f",
176
noSet: true, // because this model is not user visible yet
177
category: "ai",
178
},
179
"openai-gpt-4-32k": {
180
display: "OpenAI GPT-4 32k",
181
color: "#10a37f",
182
category: "ai",
183
},
184
"openai-gpt-4-turbo-preview": GPT_TURBO_128k, // the "preview" is over
185
"openai-gpt-4-turbo-preview-8k": GPT_TURBO_8K, // the "preview" is over
186
"openai-gpt-4-turbo": GPT_TURBO_128k,
187
"openai-gpt-4-turbo-8k": GPT_TURBO_8K,
188
"openai-gpt-4o": GPT_OMNI_128k,
189
"openai-gpt-4o-8k": GPT_OMNI_8K,
190
"openai-gpt-4o-mini": GPT_OMNI_MINI_128k,
191
"openai-gpt-4o-mini-8k": GPT_OMNI_MINI_8K,
192
"openai-gpt-4.1": GPT_41_8K,
193
"openai-gpt-4.1-mini": GPT_41_MINI_8K,
194
"openai-o1-mini-8k": GPT_O1_8K,
195
"openai-o1-8k": GPT_O1_MINI_8K,
196
"openai-o1-mini": GPT_O1_8K,
197
"openai-o1": GPT_O1_MINI_8K,
198
"google-text-bison-001": {
199
display: "Google Palm 2 (Text)",
200
color: GOOGLE_AI_COLOR,
201
noSet: true, // deprecated, will be removed
202
category: "ai",
203
},
204
"google-chat-bison-001": {
205
display: "Google Palm 2 (Chat)",
206
color: GOOGLE_AI_COLOR,
207
noSet: true, // deprecated, will be removed
208
category: "ai",
209
},
210
"google-embedding-gecko-001": {
211
display: "Google Gecko (Embedding)",
212
color: GOOGLE_AI_COLOR,
213
noSet: true, // deprecated, will be removed
214
category: "ai",
215
},
216
"google-gemini-1.5-flash-8k": {
217
display: "Google Gemini 1.5 Flash",
218
color: GOOGLE_AI_COLOR,
219
category: "ai",
220
},
221
"google-gemini-pro": {
222
display: "Google Gemini 1.0 Pro",
223
color: GOOGLE_AI_COLOR,
224
category: "ai",
225
},
226
"google-gemini-1.0-ultra": {
227
display: "Google Gemini 1.0 Ultra",
228
color: GOOGLE_AI_COLOR,
229
category: "ai",
230
},
231
"google-gemini-1.5-pro-8k": {
232
display: LLM_USERNAMES["gemini-1.5-pro-8k"],
233
color: GOOGLE_AI_COLOR,
234
category: "ai",
235
},
236
"google-gemini-1.5-pro": {
237
display: LLM_USERNAMES["gemini-1.5-pro"],
238
color: GOOGLE_AI_COLOR,
239
category: "ai",
240
},
241
"google-gemini-2.0-flash-8k": {
242
display: LLM_USERNAMES["gemini-2.0-flash-8k"],
243
color: GOOGLE_AI_COLOR,
244
category: "ai",
245
},
246
"google-gemini-2.0-flash-lite-8k": {
247
display: LLM_USERNAMES["gemini-2.0-flash-lite-8k"],
248
color: GOOGLE_AI_COLOR,
249
category: "ai",
250
},
251
"anthropic-claude-3-opus": {
252
display: LLM_USERNAMES["claude-3-opus"],
253
color: "#181818",
254
category: "ai",
255
},
256
"anthropic-claude-3-opus-8k": {
257
display: LLM_USERNAMES["claude-3-opus-8k"],
258
color: "#181818",
259
category: "ai",
260
},
261
"anthropic-claude-3-sonnet": {
262
display: LLM_USERNAMES["claude-3-sonnet"],
263
color: "#181818",
264
category: "ai",
265
},
266
"anthropic-claude-3-sonnet-4k": {
267
display: LLM_USERNAMES["claude-3-sonnet-4k"],
268
color: "#181818",
269
category: "ai",
270
},
271
"anthropic-claude-3-5-sonnet": {
272
display: LLM_USERNAMES["claude-3-5-sonnet"],
273
color: "#181818",
274
category: "ai",
275
},
276
"anthropic-claude-3-5-sonnet-4k": {
277
display: LLM_USERNAMES["claude-3-5-sonnet-4k"],
278
color: "#181818",
279
category: "ai",
280
},
281
"anthropic-claude-3-haiku": {
282
display: LLM_USERNAMES["claude-3-haiku"],
283
color: "#181818",
284
category: "ai",
285
},
286
"anthropic-claude-3-haiku-8k": {
287
display: LLM_USERNAMES["claude-3-haiku-8k"],
288
color: "#181818",
289
category: "ai",
290
},
291
"mistralai-mistral-small-latest": {
292
display: LLM_USERNAMES["mistral-small-latest"],
293
color: "#ff7000", // the orange from their website
294
category: "ai",
295
},
296
"mistralai-mistral-medium-latest": {
297
display: LLM_USERNAMES["mistral-medium-latest"],
298
color: "#ff7000", // the orange from their website
299
category: "ai",
300
},
301
"mistralai-mistral-large-latest": {
302
display: LLM_USERNAMES["mistral-large-latest"],
303
color: "#ff7000", // the orange from their website
304
category: "ai",
305
},
306
"project-upgrade": {
307
display: "Project Upgrade",
308
color: "#5bc0de",
309
category: "compute",
310
description:
311
"Charge resulting from using pay as you go upgrades to a project.",
312
},
313
} as const;
314
315
// For pay-as-you-go project quota upgrades
316
export interface ProjectQuota {
317
cost?: number; // dollars per hour
318
enabled?: number;
319
cores?: number;
320
disk_quota?: number;
321
memory?: number;
322
mintime?: number;
323
network?: number;
324
member_host?: number;
325
always_running?: number;
326
}
327
328
export const PROJECT_QUOTA_KEYS = new Set<string>([
329
"enabled",
330
"cost",
331
"cores",
332
"disk_quota",
333
"memory",
334
"mintime",
335
"network",
336
"member_host",
337
"always_running",
338
]);
339
340
export function serviceToDisplay(service: Service): string {
341
return QUOTA_SPEC[service]?.display ?? service;
342
}
343
344
Table({
345
name: "purchase_quotas",
346
fields: {
347
id: ID,
348
account_id: CREATED_BY,
349
service: {
350
title: "Service Category",
351
desc: "The service being charged for, e.g., openai-gpt-4, project-upgrade, etc.",
352
type: "string",
353
pg_type: "varchar(127)",
354
},
355
value: {
356
title: "Value",
357
desc: "The maximum amount that user can be charged for this service during one month billing period, in US dollars.",
358
type: "number", // actually comes back as string in queries.
359
pg_type: "REAL",
360
},
361
},
362
rules: {
363
desc: "Purchase Quotas",
364
primary_key: "id",
365
// make it fast to find all quotas for a given account
366
pg_indexes: ["account_id"],
367
// enforce that there is only one quota for each service for a given account
368
pg_unique_indexes: ["(account_id,service)"],
369
user_query: {
370
// set happens though v2 api only to enforce global quota
371
get: {
372
pg_where: [{ "account_id = $::UUID": "account_id" }],
373
fields: {
374
id: null,
375
account_id: null,
376
service: null,
377
value: null,
378
},
379
},
380
},
381
},
382
});
383
384
Table({
385
name: "crm_purchase_quotas",
386
rules: {
387
virtual: "purchase_quotas",
388
primary_key: "id",
389
user_query: {
390
get: {
391
pg_where: [],
392
admin: true,
393
fields: {
394
id: null,
395
account_id: null,
396
service: null,
397
value: null,
398
},
399
},
400
set: {
401
admin: true,
402
fields: {
403
id: true,
404
account_id: true,
405
service: true,
406
value: true,
407
},
408
},
409
},
410
},
411
fields: schema.purchase_quotas.fields,
412
});
413
414