diff --git a/services/web/app/src/Features/Project/ProjectController.mjs b/services/web/app/src/Features/Project/ProjectController.mjs index f57963d15b..2fe0c388c2 100644 --- a/services/web/app/src/Features/Project/ProjectController.mjs +++ b/services/web/app/src/Features/Project/ProjectController.mjs @@ -964,12 +964,6 @@ const _ProjectController = { showAiFeatures, onAiFreeTrial: fullFeatureSet?.aiUsageQuota === Settings.aiFeatures?.freeTrialQuota, - // default to free tier if they dont have a quota - hasAiFreeTier: - fullFeatureSet?.aiUsageQuota === Settings.aiFeatures?.freeQuota || - !fullFeatureSet?.aiUsageQuota, - hasUnlimitedAi: - fullFeatureSet?.aiUsageQuota === Settings.aiFeatures?.unlimitedQuota, detachRole, metadata: { viewport: false }, showUpgradePrompt, diff --git a/services/web/app/src/Features/Subscription/FeaturesHelper.mjs b/services/web/app/src/Features/Subscription/FeaturesHelper.mjs index bd1fb7e8b2..312d71898c 100644 --- a/services/web/app/src/Features/Subscription/FeaturesHelper.mjs +++ b/services/web/app/src/Features/Subscription/FeaturesHelper.mjs @@ -39,12 +39,14 @@ function mergeFeatures(featuresA, featuresB) { featuresB.compileTimeout || 0 ) } else if (key === 'aiUsageQuota') { - // later entries have higher precedence - const QUOTA_TIER_LIST = ['free', 'basic', 'standard', 'unlimited'] - const quotaA = QUOTA_TIER_LIST.indexOf(features.aiUsageQuota) - const quotaB = QUOTA_TIER_LIST.indexOf(featuresB.aiUsageQuota) - features.aiUsageQuota = - quotaA > quotaB ? features.aiUsageQuota : featuresB.aiUsageQuota + if ( + features.aiUsageQuota === Settings.aiFeatures.unlimitedQuota || + featuresB.aiUsageQuota === Settings.aiFeatures.unlimitedQuota + ) { + features.aiUsageQuota = Settings.aiFeatures.unlimitedQuota + } else { + features.aiUsageQuota = Settings.aiFeatures.freeTrialQuota + } } else { // Boolean keys, true is better features[key] = features[key] || featuresB[key] diff --git a/services/web/app/src/infrastructure/rate-limiters/AiFeatureUsageRateLimiter.mjs b/services/web/app/src/infrastructure/rate-limiters/AiFeatureUsageRateLimiter.mjs index 9b8c4e9133..b1af6b3ae0 100644 --- a/services/web/app/src/infrastructure/rate-limiters/AiFeatureUsageRateLimiter.mjs +++ b/services/web/app/src/infrastructure/rate-limiters/AiFeatureUsageRateLimiter.mjs @@ -30,7 +30,7 @@ class AiFeatureUsageRateLimiter extends FeatureUsageRateLimiter { if (inQuotaSplitTest) { const wfQuota = user.writefull?.isPremium ? Settings.writefull.quotaTierGranted - : Settings.aiFeatures.freeQuota + : Settings.aiFeatures.freeTrialQuota const mergedFeatures = FeaturesHelper.mergeFeatures(user.features, { aiUsageQuota: wfQuota, }) diff --git a/services/web/app/views/project/editor/_meta.pug b/services/web/app/views/project/editor/_meta.pug index 7f93e96ccf..c6e303b87b 100644 --- a/services/web/app/views/project/editor/_meta.pug +++ b/services/web/app/views/project/editor/_meta.pug @@ -25,8 +25,7 @@ meta(name="ol-debugPdfDetach" data-type="boolean" content=debugPdfDetach) meta(name="ol-showSymbolPalette" data-type="boolean" content=showSymbolPalette) meta(name="ol-symbolPaletteAvailable" data-type="boolean" content=symbolPaletteAvailable) meta(name="ol-showAiFeatures" data-type="boolean" content=showAiFeatures) -meta(name="ol-hasUnlimitedAi" data-type="boolean" content=hasUnlimitedAi) -meta(name="ol-hasAiFreeTier" data-type="boolean" content=hasAiFreeTier) +meta(name="ol-onAiFreeTrial" data-type="boolean" content=onAiFreeTrial) meta(name="ol-detachRole" data-type="string" content=detachRole) meta(name="ol-imageNames" data-type="json" content=imageNames) meta(name="ol-languages" data-type="json" content=languages) diff --git a/services/web/config/settings.defaults.js b/services/web/config/settings.defaults.js index 7c4039b1e9..eee3905ca2 100644 --- a/services/web/config/settings.defaults.js +++ b/services/web/config/settings.defaults.js @@ -425,21 +425,10 @@ module.exports = { }, aiFeatures: { - freeQuota: 'free', - standardQuota: 'standard', - basicQuota: 'basic', + freeTrialQuota: 'basic', unlimitedQuota: 'unlimited', }, - quotaGrants: { - ai: { - free: 0, - basic: 0, - standard: 0, - unlimited: 0, - }, - }, - groupPlanModalOptions: { plan_codes: [], currencies: [], diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index c282135fc7..f517d0314b 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -710,7 +710,6 @@ "get_most_subscription_by_checking_overleaf_ai_writefull": "", "get_real_time_track_changes": "", "get_unlimited_ai": "", - "get_your_hands_on_the_ultimate_research_writing_ai_assistant": "", "git": "", "git_authentication_token": "", "git_authentication_token_create_modal_info_1": "", @@ -2140,7 +2139,6 @@ "upgrade_to_add_more_collaborators_and_access_collaboration_features": "", "upgrade_to_add_more_collaborators_and_more": "", "upgrade_to_get_feature": "", - "upgrade_to_get_started": "", "upgrade_to_review": "", "upgrade_your_subscription": "", "upload": "", diff --git a/services/web/frontend/js/shared/components/ai-paywall-notification.tsx b/services/web/frontend/js/shared/components/ai-paywall-notification.tsx index f590a3a936..e5a8d0cc5a 100644 --- a/services/web/frontend/js/shared/components/ai-paywall-notification.tsx +++ b/services/web/frontend/js/shared/components/ai-paywall-notification.tsx @@ -7,7 +7,7 @@ import { formatSecondsToHoursAndMinutes } from '@/shared/utils/time' import { useFeatureFlag } from '@/shared/context/split-test-context' import getMeta from '@/utils/meta' -const hasUnlimitedAi = getMeta('ol-hasUnlimitedAi') +const onAiFreeTrial = getMeta('ol-onAiFreeTrial') type aiFeatureLocations = 'errorAssist' | 'workbench' @@ -33,8 +33,8 @@ function AiPaywallNotification({ return null } - // todo: quota clean-up: remove once we are transitioned off aiErrorAssistant naming and replace with just hasUnlimitedAi, also remove null FF check - const hasAddOn = hasUnlimitedAi || Boolean(features?.aiErrorAssistant) + // todo: quota clean-up: remove once we are transitioned off aiErrorAssistant naming and replace with just !onAiFreeTrial, also remove null FF check + const hasAddOn = !onAiFreeTrial || Boolean(features?.aiErrorAssistant) // error assist only needs usage quota const canUseErrorAssist = hasSuggestionsLeft diff --git a/services/web/frontend/js/shared/context/user-features-context.tsx b/services/web/frontend/js/shared/context/user-features-context.tsx index 2c7c32ca2f..151ca3357a 100644 --- a/services/web/frontend/js/shared/context/user-features-context.tsx +++ b/services/web/frontend/js/shared/context/user-features-context.tsx @@ -15,7 +15,7 @@ import getMeta from '@/utils/meta' export const UserFeaturesContext = createContext(undefined) -const hasUnlimitedAi = getMeta('ol-hasUnlimitedAi') +const onAiFreeTrial = getMeta('ol-onAiFreeTrial') export const UserFeaturesProvider: FC = ({ children, @@ -35,7 +35,7 @@ export const UserFeaturesProvider: FC = ({ useEffect(() => { const listener = async ({ isPremium }: { isPremium: boolean }) => { // todo: quota clean-up: remove once we are transitioned off aiErrorAssistant naming - const hasPremiumQuota = hasUnlimitedAi + const hasPremiumQuota = !onAiFreeTrial const alreadyPremium = features?.aiErrorAssistant === isPremium || hasPremiumQuota === isPremium diff --git a/services/web/frontend/js/utils/meta.ts b/services/web/frontend/js/utils/meta.ts index 5b24623f01..6b0b077ed3 100644 --- a/services/web/frontend/js/utils/meta.ts +++ b/services/web/frontend/js/utils/meta.ts @@ -152,7 +152,6 @@ export interface Meta { 'ol-groupSubscriptionsPendingEnrollment': PendingGroupSubscriptionEnrollment[] 'ol-groupsAndEnterpriseBannerVariant': GroupsAndEnterpriseBannerVariant 'ol-hasAiAssistViaWritefull': boolean - 'ol-hasAiFreeTier': boolean 'ol-hasGroupSSOFeature': boolean 'ol-hasIndividualPaidSubscription': boolean 'ol-hasManagedUsersFeature': boolean @@ -161,7 +160,6 @@ export interface Meta { 'ol-hasSplitTestWriteAccess': boolean 'ol-hasSubscription': boolean 'ol-hasTrackChangesFeature': boolean - 'ol-hasUnlimitedAi': boolean 'ol-hasWriteAccess': boolean 'ol-hideLinkingWidgets': boolean // CI only 'ol-historyBlobStats': { @@ -223,6 +221,7 @@ export interface Meta { 'ol-notificationsInstitution': InstitutionType[] 'ol-oauthProviders': OAuthProviders 'ol-odcData': OnboardingFormData + 'ol-onAiFreeTrial': boolean 'ol-otMigrationStage': number 'ol-overallThemes': OverallThemeMeta[] 'ol-ownerIsManaged': boolean diff --git a/services/web/locales/en.json b/services/web/locales/en.json index 8c56b6f412..af664c184d 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -913,7 +913,6 @@ "get_real_time_track_changes": "Get real-time track changes", "get_the_best_overleaf_experience": "Get the best Overleaf experience", "get_unlimited_ai": "Get unlimited use of AI features", - "get_your_hands_on_the_ultimate_research_writing_ai_assistant": "Get your hands on the ultimate research writing AI assistant.", "git": "Git", "git_authentication_token": "Git authentication token", "git_authentication_token_create_modal_info_1": "This is your Git authentication token. You should enter this when prompted for a password.", @@ -2692,7 +2691,6 @@ "upgrade_to_add_more_collaborators_and_more": "Upgrade to add more collaborators and access features like Overleaf AI, track changes, and full project history.", "upgrade_to_get_feature": "Upgrade to get __feature__, plus:", "upgrade_to_get_more_from_overleaf": "Upgrade to get more from Overleaf", - "upgrade_to_get_started": "Upgrade to get started", "upgrade_to_review": "Upgrade to Review", "upgrade_your_subscription": "Upgrade your subscription", "upload": "Upload", diff --git a/services/web/test/unit/src/Institutions/InstitutionsFeatures.test.mjs b/services/web/test/unit/src/Institutions/InstitutionsFeatures.test.mjs index 4479d71a7b..5c7264d0ee 100644 --- a/services/web/test/unit/src/Institutions/InstitutionsFeatures.test.mjs +++ b/services/web/test/unit/src/Institutions/InstitutionsFeatures.test.mjs @@ -34,19 +34,9 @@ describe('InstitutionsFeatures', function () { quotaTierGranted: 'unlimited', }, aiFeatures: { - freeQuota: 'free', - standardQuota: 'standard', - basicQuota: 'basic', + freeTrialQuota: 'basic', unlimitedQuota: 'unlimited', }, - quotaGrants: { - ai: { - free: 5, - basic: 5, - standard: 10, - unlimited: 200, - }, - }, }, })) diff --git a/services/web/test/unit/src/Subscription/FeaturesUpdater.test.mjs b/services/web/test/unit/src/Subscription/FeaturesUpdater.test.mjs index 926308eba1..e126cd58e0 100644 --- a/services/web/test/unit/src/Subscription/FeaturesUpdater.test.mjs +++ b/services/web/test/unit/src/Subscription/FeaturesUpdater.test.mjs @@ -77,19 +77,9 @@ describe('FeaturesUpdater', function () { quotaTierGranted: 'unlimited', }, aiFeatures: { - freeQuota: 'free', - standardQuota: 'standard', - basicQuota: 'basic', + freeTrialQuota: 'basic', unlimitedQuota: 'unlimited', }, - quotaGrants: { - ai: { - free: 5, - basic: 5, - standard: 10, - unlimited: 200, - }, - }, } ctx.ReferalFeatures = { diff --git a/services/web/test/unit/src/User/UserGetter.test.mjs b/services/web/test/unit/src/User/UserGetter.test.mjs index 8943d90deb..8e738643f6 100644 --- a/services/web/test/unit/src/User/UserGetter.test.mjs +++ b/services/web/test/unit/src/User/UserGetter.test.mjs @@ -67,19 +67,9 @@ describe('UserGetter', function () { default: (ctx.settings = { reconfirmNotificationDays: 14, aiFeatures: { - freeQuota: 'free', - standardQuota: 'standard', - basicQuota: 'basic', + freeTrialQuota: 'basic', unlimitedQuota: 'unlimited', }, - quotaGrants: { - ai: { - free: 5, - basic: 5, - standard: 10, - unlimited: 200, - }, - }, }), })) diff --git a/services/web/test/unit/src/infrastructure/AiFeatureUsageRateLimiter.test.mjs b/services/web/test/unit/src/infrastructure/AiFeatureUsageRateLimiter.test.mjs index 95758b45d2..27c61113c9 100644 --- a/services/web/test/unit/src/infrastructure/AiFeatureUsageRateLimiter.test.mjs +++ b/services/web/test/unit/src/infrastructure/AiFeatureUsageRateLimiter.test.mjs @@ -61,16 +61,12 @@ describe('AiFeatureUsageRateLimiter', function () { quotaTierGranted: 'unlimited', }, aiFeatures: { - freeQuota: 'free', - standardQuota: 'standard', - basicQuota: 'basic', + freeTrialQuota: 'basic', unlimitedQuota: 'unlimited', }, quotaGrants: { ai: { - free: 5, basic: 5, - standard: 10, unlimited: 200, }, },