Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Expose license feature flags for free AI credits feature to frontend (no-changelog) #12363

Merged
merged 5 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/@n8n/api-types/src/frontend-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ export interface FrontendSettings {
pruneTime: number;
licensePruneTime: number;
};
aiCredits: {
enabled: boolean;
credits: number;
};
pruning?: {
isEnabled: boolean;
maxAge: number;
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export const LICENSE_FEATURES = {
AI_ASSISTANT: 'feat:aiAssistant',
ASK_AI: 'feat:askAi',
COMMUNITY_NODES_CUSTOM_REGISTRY: 'feat:communityNodes:customRegistry',
AI_CREDITS: 'feat:aiCredits',
} as const;

export const LICENSE_QUOTAS = {
Expand All @@ -101,6 +102,7 @@ export const LICENSE_QUOTAS = {
USERS_LIMIT: 'quota:users',
WORKFLOW_HISTORY_PRUNE_LIMIT: 'quota:workflowHistoryPrune',
TEAM_PROJECT_LIMIT: 'quota:maxTeamProjects',
AI_CREDITS: 'quota:aiCredits',
} as const;
export const UNLIMITED_LICENSE_QUOTA = -1;

Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/controllers/e2e.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export class E2EController {
[LICENSE_FEATURES.AI_ASSISTANT]: false,
[LICENSE_FEATURES.COMMUNITY_NODES_CUSTOM_REGISTRY]: false,
[LICENSE_FEATURES.ASK_AI]: false,
[LICENSE_FEATURES.AI_CREDITS]: false,
};

private numericFeatures: Record<NumericLicenseFeature, number> = {
Expand All @@ -108,6 +109,7 @@ export class E2EController {
[LICENSE_QUOTAS.USERS_LIMIT]: -1,
[LICENSE_QUOTAS.WORKFLOW_HISTORY_PRUNE_LIMIT]: -1,
[LICENSE_QUOTAS.TEAM_PROJECT_LIMIT]: 0,
[LICENSE_QUOTAS.AI_CREDITS]: 0,
};

constructor(
Expand Down
8 changes: 8 additions & 0 deletions packages/cli/src/license.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@ export class License {
return this.isFeatureEnabled(LICENSE_FEATURES.ASK_AI);
}

isAiCreditsEnabled() {
return this.isFeatureEnabled(LICENSE_FEATURES.AI_CREDITS);
}

isAdvancedExecutionFiltersEnabled() {
return this.isFeatureEnabled(LICENSE_FEATURES.ADVANCED_EXECUTION_FILTERS);
}
Expand Down Expand Up @@ -365,6 +369,10 @@ export class License {
return this.getFeatureValue(LICENSE_QUOTAS.VARIABLES_LIMIT) ?? UNLIMITED_LICENSE_QUOTA;
}

getAiCredits() {
return this.getFeatureValue(LICENSE_QUOTAS.AI_CREDITS) ?? 0;
}

getWorkflowHistoryPruneLimit() {
return (
this.getFeatureValue(LICENSE_QUOTAS.WORKFLOW_HISTORY_PRUNE_LIMIT) ?? UNLIMITED_LICENSE_QUOTA
Expand Down
10 changes: 10 additions & 0 deletions packages/cli/src/services/frontend.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,10 @@ export class FrontendService {
askAi: {
enabled: false,
},
aiCredits: {
enabled: false,
credits: 0,
},
workflowHistory: {
pruneTime: -1,
licensePruneTime: -1,
Expand Down Expand Up @@ -283,6 +287,7 @@ export class FrontendService {
const isS3Licensed = this.license.isBinaryDataS3Licensed();
const isAiAssistantEnabled = this.license.isAiAssistantEnabled();
const isAskAiEnabled = this.license.isAskAiEnabled();
const isAiCreditsEnabled = this.license.isAiCreditsEnabled();

this.settings.license.planName = this.license.getPlanName();
this.settings.license.consumerId = this.license.getConsumerId();
Expand Down Expand Up @@ -343,6 +348,11 @@ export class FrontendService {
this.settings.askAi.enabled = isAskAiEnabled;
}

if (isAiCreditsEnabled) {
this.settings.aiCredits.enabled = isAiCreditsEnabled;
this.settings.aiCredits.credits = this.license.getAiCredits();
}

this.settings.mfa.enabled = config.get('mfa.enabled');

this.settings.executionMode = config.getEnv('executions.mode');
Expand Down
4 changes: 4 additions & 0 deletions packages/editor-ui/src/__tests__/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ export const defaultSettings: FrontendSettings = {
aiAssistant: {
enabled: false,
},
aiCredits: {
enabled: false,
credits: 0,
},
betaFeatures: [],
easyAIWorkflowOnboarded: false,
};
11 changes: 10 additions & 1 deletion packages/editor-ui/src/api/ai.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IRestApiContext } from '@/Interface';
import type { ICredentialsResponse, IRestApiContext } from '@/Interface';
import type { AskAiRequest, ChatRequest, ReplaceCodeRequest } from '@/types/assistant.types';
import { makeRestApiRequest, streamRequest } from '@/utils/apiUtils';
import type { IDataObject } from 'n8n-workflow';
Expand Down Expand Up @@ -42,3 +42,12 @@ export async function generateCodeForPrompt(
forNode,
} as IDataObject);
}

export async function claimFreeAiCredits(
ctx: IRestApiContext,
{ projectId }: { projectId?: string },
): Promise<ICredentialsResponse> {
return await makeRestApiRequest(ctx, 'POST', '/ai/free-credits', {
projectId,
} as IDataObject);
}
7 changes: 7 additions & 0 deletions packages/editor-ui/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -706,12 +706,19 @@ export const EASY_AI_WORKFLOW_EXPERIMENT = {
variant: 'variant',
};

export const AI_CREDITS_EXPERIMENT = {
name: '027_free_openai_calls',
control: 'control',
variant: 'variant',
};

export const EXPERIMENTS_TO_TRACK = [
TEMPLATE_CREDENTIAL_SETUP_EXPERIMENT,
CANVAS_AUTO_ADD_MANUAL_TRIGGER_EXPERIMENT.name,
AI_ASSISTANT_EXPERIMENT.name,
CREDENTIAL_DOCS_EXPERIMENT.name,
EASY_AI_WORKFLOW_EXPERIMENT.name,
AI_CREDITS_EXPERIMENT.name,
];

export const WORKFLOW_EVALUATION_EXPERIMENT = '025_workflow_evaluation';
Expand Down
6 changes: 6 additions & 0 deletions packages/editor-ui/src/stores/settings.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {

const isCloudDeployment = computed(() => settings.value.deployment?.type === 'cloud');

const isAiCreditsEnabled = computed(() => settings.value.aiCredits?.enabled);

const aiCreditsQuota = computed(() => settings.value.aiCredits?.credits);

const isSmtpSetup = computed(() => userManagement.value.smtpSetup);

const isPersonalizationSurveyEnabled = computed(
Expand Down Expand Up @@ -425,6 +429,8 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
isCommunityPlan,
isAskAiEnabled,
isCanvasV2Enabled,
isAiCreditsEnabled,
aiCreditsQuota,
reset,
testLdapConnection,
getLdapConfig,
Expand Down
5 changes: 4 additions & 1 deletion packages/editor-ui/src/stores/users.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ export const useUsersStore = defineStore(STORES.USERS, () => {

const globalRoleName = computed(() => currentUser.value?.role ?? 'default');

const userClaimedAiCredits = computed(() => currentUser.value?.settings?.userClaimedAiCredits);

const isEasyAIWorkflowOnboardingDone = computed(() =>
Boolean(currentUser.value?.settings?.easyAIWorkflowOnboarded),
);
Expand Down Expand Up @@ -388,6 +390,8 @@ export const useUsersStore = defineStore(STORES.USERS, () => {
mfaEnabled,
globalRoleName,
personalizedNodeTypes,
userClaimedAiCredits,
isEasyAIWorkflowOnboardingDone,
addUsers,
loginWithCookie,
initialize,
Expand Down Expand Up @@ -420,7 +424,6 @@ export const useUsersStore = defineStore(STORES.USERS, () => {
sendConfirmationEmail,
updateGlobalRole,
reset,
isEasyAIWorkflowOnboardingDone,
setEasyAIWorkflowOnboardingDone,
};
});
Loading