mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 21:07:28 -08:00
feat: Expose license feature flags for free AI credits feature to frontend (no-changelog) (#12363)
This commit is contained in:
parent
ac4e042231
commit
7ea6c8b144
|
@ -163,6 +163,10 @@ export interface FrontendSettings {
|
||||||
pruneTime: number;
|
pruneTime: number;
|
||||||
licensePruneTime: number;
|
licensePruneTime: number;
|
||||||
};
|
};
|
||||||
|
aiCredits: {
|
||||||
|
enabled: boolean;
|
||||||
|
credits: number;
|
||||||
|
};
|
||||||
pruning?: {
|
pruning?: {
|
||||||
isEnabled: boolean;
|
isEnabled: boolean;
|
||||||
maxAge: number;
|
maxAge: number;
|
||||||
|
|
|
@ -93,6 +93,7 @@ export const LICENSE_FEATURES = {
|
||||||
AI_ASSISTANT: 'feat:aiAssistant',
|
AI_ASSISTANT: 'feat:aiAssistant',
|
||||||
ASK_AI: 'feat:askAi',
|
ASK_AI: 'feat:askAi',
|
||||||
COMMUNITY_NODES_CUSTOM_REGISTRY: 'feat:communityNodes:customRegistry',
|
COMMUNITY_NODES_CUSTOM_REGISTRY: 'feat:communityNodes:customRegistry',
|
||||||
|
AI_CREDITS: 'feat:aiCredits',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const LICENSE_QUOTAS = {
|
export const LICENSE_QUOTAS = {
|
||||||
|
@ -101,6 +102,7 @@ export const LICENSE_QUOTAS = {
|
||||||
USERS_LIMIT: 'quota:users',
|
USERS_LIMIT: 'quota:users',
|
||||||
WORKFLOW_HISTORY_PRUNE_LIMIT: 'quota:workflowHistoryPrune',
|
WORKFLOW_HISTORY_PRUNE_LIMIT: 'quota:workflowHistoryPrune',
|
||||||
TEAM_PROJECT_LIMIT: 'quota:maxTeamProjects',
|
TEAM_PROJECT_LIMIT: 'quota:maxTeamProjects',
|
||||||
|
AI_CREDITS: 'quota:aiCredits',
|
||||||
} as const;
|
} as const;
|
||||||
export const UNLIMITED_LICENSE_QUOTA = -1;
|
export const UNLIMITED_LICENSE_QUOTA = -1;
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,7 @@ export class E2EController {
|
||||||
[LICENSE_FEATURES.AI_ASSISTANT]: false,
|
[LICENSE_FEATURES.AI_ASSISTANT]: false,
|
||||||
[LICENSE_FEATURES.COMMUNITY_NODES_CUSTOM_REGISTRY]: false,
|
[LICENSE_FEATURES.COMMUNITY_NODES_CUSTOM_REGISTRY]: false,
|
||||||
[LICENSE_FEATURES.ASK_AI]: false,
|
[LICENSE_FEATURES.ASK_AI]: false,
|
||||||
|
[LICENSE_FEATURES.AI_CREDITS]: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
private numericFeatures: Record<NumericLicenseFeature, number> = {
|
private numericFeatures: Record<NumericLicenseFeature, number> = {
|
||||||
|
@ -108,6 +109,7 @@ export class E2EController {
|
||||||
[LICENSE_QUOTAS.USERS_LIMIT]: -1,
|
[LICENSE_QUOTAS.USERS_LIMIT]: -1,
|
||||||
[LICENSE_QUOTAS.WORKFLOW_HISTORY_PRUNE_LIMIT]: -1,
|
[LICENSE_QUOTAS.WORKFLOW_HISTORY_PRUNE_LIMIT]: -1,
|
||||||
[LICENSE_QUOTAS.TEAM_PROJECT_LIMIT]: 0,
|
[LICENSE_QUOTAS.TEAM_PROJECT_LIMIT]: 0,
|
||||||
|
[LICENSE_QUOTAS.AI_CREDITS]: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|
|
@ -255,6 +255,10 @@ export class License {
|
||||||
return this.isFeatureEnabled(LICENSE_FEATURES.ASK_AI);
|
return this.isFeatureEnabled(LICENSE_FEATURES.ASK_AI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isAiCreditsEnabled() {
|
||||||
|
return this.isFeatureEnabled(LICENSE_FEATURES.AI_CREDITS);
|
||||||
|
}
|
||||||
|
|
||||||
isAdvancedExecutionFiltersEnabled() {
|
isAdvancedExecutionFiltersEnabled() {
|
||||||
return this.isFeatureEnabled(LICENSE_FEATURES.ADVANCED_EXECUTION_FILTERS);
|
return this.isFeatureEnabled(LICENSE_FEATURES.ADVANCED_EXECUTION_FILTERS);
|
||||||
}
|
}
|
||||||
|
@ -365,6 +369,10 @@ export class License {
|
||||||
return this.getFeatureValue(LICENSE_QUOTAS.VARIABLES_LIMIT) ?? UNLIMITED_LICENSE_QUOTA;
|
return this.getFeatureValue(LICENSE_QUOTAS.VARIABLES_LIMIT) ?? UNLIMITED_LICENSE_QUOTA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAiCredits() {
|
||||||
|
return this.getFeatureValue(LICENSE_QUOTAS.AI_CREDITS) ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
getWorkflowHistoryPruneLimit() {
|
getWorkflowHistoryPruneLimit() {
|
||||||
return (
|
return (
|
||||||
this.getFeatureValue(LICENSE_QUOTAS.WORKFLOW_HISTORY_PRUNE_LIMIT) ?? UNLIMITED_LICENSE_QUOTA
|
this.getFeatureValue(LICENSE_QUOTAS.WORKFLOW_HISTORY_PRUNE_LIMIT) ?? UNLIMITED_LICENSE_QUOTA
|
||||||
|
|
|
@ -216,6 +216,10 @@ export class FrontendService {
|
||||||
askAi: {
|
askAi: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
|
aiCredits: {
|
||||||
|
enabled: false,
|
||||||
|
credits: 0,
|
||||||
|
},
|
||||||
workflowHistory: {
|
workflowHistory: {
|
||||||
pruneTime: -1,
|
pruneTime: -1,
|
||||||
licensePruneTime: -1,
|
licensePruneTime: -1,
|
||||||
|
@ -283,6 +287,7 @@ export class FrontendService {
|
||||||
const isS3Licensed = this.license.isBinaryDataS3Licensed();
|
const isS3Licensed = this.license.isBinaryDataS3Licensed();
|
||||||
const isAiAssistantEnabled = this.license.isAiAssistantEnabled();
|
const isAiAssistantEnabled = this.license.isAiAssistantEnabled();
|
||||||
const isAskAiEnabled = this.license.isAskAiEnabled();
|
const isAskAiEnabled = this.license.isAskAiEnabled();
|
||||||
|
const isAiCreditsEnabled = this.license.isAiCreditsEnabled();
|
||||||
|
|
||||||
this.settings.license.planName = this.license.getPlanName();
|
this.settings.license.planName = this.license.getPlanName();
|
||||||
this.settings.license.consumerId = this.license.getConsumerId();
|
this.settings.license.consumerId = this.license.getConsumerId();
|
||||||
|
@ -343,6 +348,11 @@ export class FrontendService {
|
||||||
this.settings.askAi.enabled = isAskAiEnabled;
|
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.mfa.enabled = config.get('mfa.enabled');
|
||||||
|
|
||||||
this.settings.executionMode = config.getEnv('executions.mode');
|
this.settings.executionMode = config.getEnv('executions.mode');
|
||||||
|
|
|
@ -125,6 +125,10 @@ export const defaultSettings: FrontendSettings = {
|
||||||
aiAssistant: {
|
aiAssistant: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
|
aiCredits: {
|
||||||
|
enabled: false,
|
||||||
|
credits: 0,
|
||||||
|
},
|
||||||
betaFeatures: [],
|
betaFeatures: [],
|
||||||
easyAIWorkflowOnboarded: false,
|
easyAIWorkflowOnboarded: false,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 type { AskAiRequest, ChatRequest, ReplaceCodeRequest } from '@/types/assistant.types';
|
||||||
import { makeRestApiRequest, streamRequest } from '@/utils/apiUtils';
|
import { makeRestApiRequest, streamRequest } from '@/utils/apiUtils';
|
||||||
import type { IDataObject } from 'n8n-workflow';
|
import type { IDataObject } from 'n8n-workflow';
|
||||||
|
@ -42,3 +42,12 @@ export async function generateCodeForPrompt(
|
||||||
forNode,
|
forNode,
|
||||||
} as IDataObject);
|
} as IDataObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function claimFreeAiCredits(
|
||||||
|
ctx: IRestApiContext,
|
||||||
|
{ projectId }: { projectId?: string },
|
||||||
|
): Promise<ICredentialsResponse> {
|
||||||
|
return await makeRestApiRequest(ctx, 'POST', '/ai/free-credits', {
|
||||||
|
projectId,
|
||||||
|
} as IDataObject);
|
||||||
|
}
|
||||||
|
|
|
@ -706,12 +706,19 @@ export const EASY_AI_WORKFLOW_EXPERIMENT = {
|
||||||
variant: 'variant',
|
variant: 'variant',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const AI_CREDITS_EXPERIMENT = {
|
||||||
|
name: '027_free_openai_calls',
|
||||||
|
control: 'control',
|
||||||
|
variant: 'variant',
|
||||||
|
};
|
||||||
|
|
||||||
export const EXPERIMENTS_TO_TRACK = [
|
export const EXPERIMENTS_TO_TRACK = [
|
||||||
TEMPLATE_CREDENTIAL_SETUP_EXPERIMENT,
|
TEMPLATE_CREDENTIAL_SETUP_EXPERIMENT,
|
||||||
CANVAS_AUTO_ADD_MANUAL_TRIGGER_EXPERIMENT.name,
|
CANVAS_AUTO_ADD_MANUAL_TRIGGER_EXPERIMENT.name,
|
||||||
AI_ASSISTANT_EXPERIMENT.name,
|
AI_ASSISTANT_EXPERIMENT.name,
|
||||||
CREDENTIAL_DOCS_EXPERIMENT.name,
|
CREDENTIAL_DOCS_EXPERIMENT.name,
|
||||||
EASY_AI_WORKFLOW_EXPERIMENT.name,
|
EASY_AI_WORKFLOW_EXPERIMENT.name,
|
||||||
|
AI_CREDITS_EXPERIMENT.name,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const WORKFLOW_EVALUATION_EXPERIMENT = '025_workflow_evaluation';
|
export const WORKFLOW_EVALUATION_EXPERIMENT = '025_workflow_evaluation';
|
||||||
|
|
|
@ -98,6 +98,10 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||||
|
|
||||||
const isCloudDeployment = computed(() => settings.value.deployment?.type === 'cloud');
|
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 isSmtpSetup = computed(() => userManagement.value.smtpSetup);
|
||||||
|
|
||||||
const isPersonalizationSurveyEnabled = computed(
|
const isPersonalizationSurveyEnabled = computed(
|
||||||
|
@ -425,6 +429,8 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||||
isCommunityPlan,
|
isCommunityPlan,
|
||||||
isAskAiEnabled,
|
isAskAiEnabled,
|
||||||
isCanvasV2Enabled,
|
isCanvasV2Enabled,
|
||||||
|
isAiCreditsEnabled,
|
||||||
|
aiCreditsQuota,
|
||||||
reset,
|
reset,
|
||||||
testLdapConnection,
|
testLdapConnection,
|
||||||
getLdapConfig,
|
getLdapConfig,
|
||||||
|
|
|
@ -74,6 +74,8 @@ export const useUsersStore = defineStore(STORES.USERS, () => {
|
||||||
|
|
||||||
const globalRoleName = computed(() => currentUser.value?.role ?? 'default');
|
const globalRoleName = computed(() => currentUser.value?.role ?? 'default');
|
||||||
|
|
||||||
|
const userClaimedAiCredits = computed(() => currentUser.value?.settings?.userClaimedAiCredits);
|
||||||
|
|
||||||
const isEasyAIWorkflowOnboardingDone = computed(() =>
|
const isEasyAIWorkflowOnboardingDone = computed(() =>
|
||||||
Boolean(currentUser.value?.settings?.easyAIWorkflowOnboarded),
|
Boolean(currentUser.value?.settings?.easyAIWorkflowOnboarded),
|
||||||
);
|
);
|
||||||
|
@ -388,6 +390,8 @@ export const useUsersStore = defineStore(STORES.USERS, () => {
|
||||||
mfaEnabled,
|
mfaEnabled,
|
||||||
globalRoleName,
|
globalRoleName,
|
||||||
personalizedNodeTypes,
|
personalizedNodeTypes,
|
||||||
|
userClaimedAiCredits,
|
||||||
|
isEasyAIWorkflowOnboardingDone,
|
||||||
addUsers,
|
addUsers,
|
||||||
loginWithCookie,
|
loginWithCookie,
|
||||||
initialize,
|
initialize,
|
||||||
|
@ -420,7 +424,6 @@ export const useUsersStore = defineStore(STORES.USERS, () => {
|
||||||
sendConfirmationEmail,
|
sendConfirmationEmail,
|
||||||
updateGlobalRole,
|
updateGlobalRole,
|
||||||
reset,
|
reset,
|
||||||
isEasyAIWorkflowOnboardingDone,
|
|
||||||
setEasyAIWorkflowOnboardingDone,
|
setEasyAIWorkflowOnboardingDone,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue