mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Billing for formula assistant
Summary: Adding limits for AI calls and connecting those limits with a Stripe Account. - New table in homedb called `limits` - All calls to the AI are not routed through DocApi and measured. - All products now contain a special key `assistantLimit`, with a default value 0 - Limit is reset every time the subscription has changed its period - The billing page is updated with two new options that describe the AI plan - There is a new popup that allows the user to upgrade to a higher plan - Tiers are read directly from the Stripe product with a volume pricing model Test Plan: Updated and added Reviewers: georgegevoian, paulfitz Reviewed By: georgegevoian Subscribers: dsagal Differential Revision: https://phab.getgrist.com/D3907
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import {ActionGroup} from 'app/common/ActionGroup';
|
||||
import {AssistanceRequest, AssistanceResponse} from 'app/common/AssistancePrompts';
|
||||
import {BulkAddRecord, CellValue, TableDataAction, UserAction} from 'app/common/DocActions';
|
||||
import {FormulaProperties} from 'app/common/GranularAccessClause';
|
||||
import {UIRowId} from 'app/common/TableData';
|
||||
@@ -320,11 +319,6 @@ export interface ActiveDocAPI {
|
||||
*/
|
||||
getFormulaError(tableId: string, colId: string, rowId: number): Promise<CellValue>;
|
||||
|
||||
/**
|
||||
* Generates a formula code based on the AI suggestions, it also modifies the column and sets it type to a formula.
|
||||
*/
|
||||
getAssistance(request: AssistanceRequest): Promise<AssistanceResponse>;
|
||||
|
||||
/**
|
||||
* Fetch content at a url.
|
||||
*/
|
||||
|
||||
@@ -2,15 +2,17 @@
|
||||
* A tip for fixing an error.
|
||||
*/
|
||||
export interface ApiTip {
|
||||
action: 'add-members' | 'upgrade' |'ask-for-help';
|
||||
action: 'add-members' | 'upgrade' | 'ask-for-help' | 'manage';
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type LimitType = 'collaborators' | 'docs' | 'workspaces' | 'assistant';
|
||||
|
||||
/**
|
||||
* Documentation of a limit relevant to an API error.
|
||||
*/
|
||||
export interface ApiLimit {
|
||||
quantity: 'collaborators' | 'docs' | 'workspaces'; // what are we counting
|
||||
quantity: LimitType; // what are we counting
|
||||
subquantity?: string; // a nuance to what we are counting
|
||||
maximum: number; // maximum allowed
|
||||
value: number; // current value of quantity for user
|
||||
|
||||
@@ -43,6 +43,16 @@ export interface IBillingPlan {
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
export interface ILimitTier {
|
||||
name?: string;
|
||||
volume: number;
|
||||
price: number;
|
||||
flatFee: number;
|
||||
type: string;
|
||||
planId: string;
|
||||
interval: string; // probably 'month'|'year';
|
||||
}
|
||||
|
||||
// Utility type that requires all properties to be non-nullish.
|
||||
// type NonNullableProperties<T> = { [P in keyof T]: Required<NonNullable<T[P]>>; };
|
||||
|
||||
@@ -69,6 +79,7 @@ export interface IBillingDiscount {
|
||||
export interface IBillingSubscription {
|
||||
// All standard plan options.
|
||||
plans: IBillingPlan[];
|
||||
tiers: ILimitTier[];
|
||||
// Index in the plans array of the plan currently in effect.
|
||||
planIndex: number;
|
||||
// Index in the plans array of the plan to be in effect after the current period end.
|
||||
@@ -111,6 +122,14 @@ export interface IBillingSubscription {
|
||||
lastInvoiceUrl?: string; // URL of the Stripe-hosted page with the last invoice.
|
||||
lastChargeError?: string; // The last charge error, if any, to show in case of a bad status.
|
||||
lastChargeTime?: number; // The time of the last charge attempt.
|
||||
limit?: ILimit|null;
|
||||
}
|
||||
|
||||
export interface ILimit {
|
||||
limitValue: number;
|
||||
currentUsage: number;
|
||||
type: string; // Limit type, for now only assistant is supported.
|
||||
price: number; // If this is 0, it means it is a free plan.
|
||||
}
|
||||
|
||||
export interface IBillingOrgSettings {
|
||||
@@ -139,6 +158,7 @@ export interface BillingAPI {
|
||||
downgradePlan(planName: string): Promise<void>;
|
||||
renewPlan(): string;
|
||||
customerPortal(): string;
|
||||
updateAssistantPlan(tier: number): Promise<void>;
|
||||
}
|
||||
|
||||
export class BillingAPIImpl extends BaseAPI implements BillingAPI {
|
||||
@@ -230,6 +250,13 @@ export class BillingAPIImpl extends BaseAPI implements BillingAPI {
|
||||
return `${this._url}/api/billing/renew`;
|
||||
}
|
||||
|
||||
public async updateAssistantPlan(tier: number): Promise<void> {
|
||||
await this.request(`${this._url}/api/billing/upgrade-assistant`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ tier })
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current org has active subscription for a Stripe plan.
|
||||
*/
|
||||
|
||||
@@ -58,6 +58,11 @@ export interface Features {
|
||||
// for attached files in a document
|
||||
|
||||
gracePeriodDays?: number; // Duration of the grace period in days, before entering delete-only mode
|
||||
|
||||
baseMaxAssistantCalls?: number; // Maximum number of AI assistant calls. Defaults to 0 if not set, use -1 to indicate
|
||||
// unbound limit. This is total limit, not per month or per day, it is used as a seed
|
||||
// value for the limits table. To create a per-month limit, there must be a separate
|
||||
// task that resets the usage in the limits table.
|
||||
}
|
||||
|
||||
// Check whether it is possible to add members at the org level. There's no flag
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {ActionSummary} from 'app/common/ActionSummary';
|
||||
import {ApplyUAResult, ForkResult, PermissionDataWithExtraUsers, QueryFilters} from 'app/common/ActiveDocAPI';
|
||||
import {AssistanceRequest, AssistanceResponse} from 'app/common/AssistancePrompts';
|
||||
import {BaseAPI, IOptions} from 'app/common/BaseAPI';
|
||||
import {BillingAPI, BillingAPIImpl} from 'app/common/BillingAPI';
|
||||
import {BrowserSettings} from 'app/common/BrowserSettings';
|
||||
@@ -462,6 +463,8 @@ export interface DocAPI {
|
||||
// Update webhook
|
||||
updateWebhook(webhook: WebhookUpdate): Promise<void>;
|
||||
flushWebhooks(): Promise<void>;
|
||||
|
||||
getAssistance(params: AssistanceRequest): Promise<AssistanceResponse>;
|
||||
}
|
||||
|
||||
// Operations that are supported by a doc worker.
|
||||
@@ -1012,6 +1015,13 @@ export class DocAPIImpl extends BaseAPI implements DocAPI {
|
||||
return response.data[0];
|
||||
}
|
||||
|
||||
public async getAssistance(params: AssistanceRequest): Promise<AssistanceResponse> {
|
||||
return await this.requestJson(`${this._url}/assistant`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
}
|
||||
|
||||
private _getRecords(tableId: string, endpoint: 'data' | 'records', options?: GetRowsParams): Promise<any> {
|
||||
const url = new URL(`${this._url}/tables/${tableId}/${endpoint}`);
|
||||
if (options?.filters) {
|
||||
|
||||
Reference in New Issue
Block a user