Просмотр исходного кода

Revert "Revert "Adding More Ai Quota Tiers"" (#32433)

* Revert "Revert "Adding More Ai Quota Tiers (#32128)" (#32431)"

This reverts commit f0ea19b418da6096c84b42024aea643807347649.

* fix: dont block workbench from users who have access via WF

* fix: simplify workbench access rules and ensure wf premium users can use workbench

GitOrigin-RevId: 7927248eadd906d7f802d55fa5c6bc7df162b141
Jimmy Domagala-Tang 4 месяцев назад
Родитель
Сommit
72b7524094

+ 6 - 2
services/web/app/src/Features/Project/ProjectController.mjs

@@ -962,8 +962,12 @@ const _ProjectController = {
         symbolPaletteAvailable: Features.hasFeature('symbol-palette'),
         userRestrictions: Array.from(req.userRestrictions || []),
         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,

+ 6 - 8
services/web/app/src/Features/Subscription/FeaturesHelper.mjs

@@ -39,14 +39,12 @@ function mergeFeatures(featuresA, featuresB) {
         featuresB.compileTimeout || 0
       )
     } else if (key === 'aiUsageQuota') {
-      if (
-        features.aiUsageQuota === Settings.aiFeatures.unlimitedQuota ||
-        featuresB.aiUsageQuota === Settings.aiFeatures.unlimitedQuota
-      ) {
-        features.aiUsageQuota = Settings.aiFeatures.unlimitedQuota
-      } else {
-        features.aiUsageQuota = Settings.aiFeatures.freeTrialQuota
-      }
+      // 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
     } else {
       // Boolean keys, true is better
       features[key] = features[key] || featuresB[key]

+ 1 - 1
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.freeTrialQuota
+        : Settings.aiFeatures.freeQuota
       const mergedFeatures = FeaturesHelper.mergeFeatures(user.features, {
         aiUsageQuota: wfQuota,
       })

+ 2 - 1
services/web/app/views/project/editor/_meta.pug

@@ -25,7 +25,8 @@ 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-onAiFreeTrial" data-type="boolean" content=onAiFreeTrial)
+meta(name="ol-hasUnlimitedAi" data-type="boolean" content=hasUnlimitedAi)
+meta(name="ol-hasAiFreeTier" data-type="boolean" content=hasAiFreeTier)
 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)

+ 12 - 1
services/web/config/settings.defaults.js

@@ -425,10 +425,21 @@ module.exports = {
   },
 
   aiFeatures: {
-    freeTrialQuota: 'basic',
+    freeQuota: 'free',
+    standardQuota: 'standard',
+    basicQuota: 'basic',
     unlimitedQuota: 'unlimited',
   },
 
+  quotaGrants: {
+    ai: {
+      free: 0,
+      basic: 0,
+      standard: 0,
+      unlimited: 0,
+    },
+  },
+
   groupPlanModalOptions: {
     plan_codes: [],
     currencies: [],

+ 2 - 0
services/web/frontend/extracted-translations.json

@@ -713,6 +713,7 @@
   "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": "",
@@ -2145,6 +2146,7 @@
   "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": "",

+ 3 - 3
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 onAiFreeTrial = getMeta('ol-onAiFreeTrial')
+const hasUnlimitedAi = getMeta('ol-hasUnlimitedAi')
 
 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 !onAiFreeTrial, also remove null FF check
-  const hasAddOn = !onAiFreeTrial || Boolean(features?.aiErrorAssistant)
+  // 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)
 
   // error assist only needs usage quota
   const canUseErrorAssist = hasSuggestionsLeft

+ 2 - 2
services/web/frontend/js/shared/context/user-features-context.tsx

@@ -15,7 +15,7 @@ import getMeta from '@/utils/meta'
 
 export const UserFeaturesContext = createContext<User['features']>(undefined)
 
-const onAiFreeTrial = getMeta('ol-onAiFreeTrial')
+const hasUnlimitedAi = getMeta('ol-hasUnlimitedAi')
 
 export const UserFeaturesProvider: FC<React.PropsWithChildren> = ({
   children,
@@ -35,7 +35,7 @@ export const UserFeaturesProvider: FC<React.PropsWithChildren> = ({
   useEffect(() => {
     const listener = async ({ isPremium }: { isPremium: boolean }) => {
       // todo: quota clean-up: remove once we are transitioned off aiErrorAssistant naming
-      const hasPremiumQuota = !onAiFreeTrial
+      const hasPremiumQuota = hasUnlimitedAi
       const alreadyPremium =
         features?.aiErrorAssistant === isPremium ||
         hasPremiumQuota === isPremium

+ 2 - 1
services/web/frontend/js/utils/meta.ts

@@ -151,6 +151,7 @@ export interface Meta {
   'ol-groupSubscriptionsPendingEnrollment': PendingGroupSubscriptionEnrollment[]
   'ol-groupsAndEnterpriseBannerVariant': GroupsAndEnterpriseBannerVariant
   'ol-hasAiAssistViaWritefull': boolean
+  'ol-hasAiFreeTier': boolean
   'ol-hasGroupSSOFeature': boolean
   'ol-hasIndividualPaidSubscription': boolean
   'ol-hasManagedUsersFeature': boolean
@@ -159,6 +160,7 @@ 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': {
@@ -228,7 +230,6 @@ export interface Meta {
   'ol-notificationsInstitution': InstitutionType[]
   'ol-oauthProviders': OAuthProviders
   'ol-odcData': OnboardingFormData
-  'ol-onAiFreeTrial': boolean
   'ol-otMigrationStage': number
   'ol-overallThemes': OverallThemeMeta[]
   'ol-ownerIsManaged': boolean

+ 2 - 0
services/web/locales/en.json

@@ -916,6 +916,7 @@
   "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.",
@@ -2696,6 +2697,7 @@
   "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",

+ 11 - 1
services/web/test/unit/src/Institutions/InstitutionsFeatures.test.mjs

@@ -34,9 +34,19 @@ describe('InstitutionsFeatures', function () {
           quotaTierGranted: 'unlimited',
         },
         aiFeatures: {
-          freeTrialQuota: 'basic',
+          freeQuota: 'free',
+          standardQuota: 'standard',
+          basicQuota: 'basic',
           unlimitedQuota: 'unlimited',
         },
+        quotaGrants: {
+          ai: {
+            free: 5,
+            basic: 5,
+            standard: 10,
+            unlimited: 200,
+          },
+        },
       },
     }))
 

+ 11 - 1
services/web/test/unit/src/Subscription/FeaturesUpdater.test.mjs

@@ -77,9 +77,19 @@ describe('FeaturesUpdater', function () {
         quotaTierGranted: 'unlimited',
       },
       aiFeatures: {
-        freeTrialQuota: 'basic',
+        freeQuota: 'free',
+        standardQuota: 'standard',
+        basicQuota: 'basic',
         unlimitedQuota: 'unlimited',
       },
+      quotaGrants: {
+        ai: {
+          free: 5,
+          basic: 5,
+          standard: 10,
+          unlimited: 200,
+        },
+      },
     }
 
     ctx.ReferalFeatures = {

+ 11 - 1
services/web/test/unit/src/User/UserGetter.test.mjs

@@ -67,9 +67,19 @@ describe('UserGetter', function () {
       default: (ctx.settings = {
         reconfirmNotificationDays: 14,
         aiFeatures: {
-          freeTrialQuota: 'basic',
+          freeQuota: 'free',
+          standardQuota: 'standard',
+          basicQuota: 'basic',
           unlimitedQuota: 'unlimited',
         },
+        quotaGrants: {
+          ai: {
+            free: 5,
+            basic: 5,
+            standard: 10,
+            unlimited: 200,
+          },
+        },
       }),
     }))
 

+ 5 - 1
services/web/test/unit/src/infrastructure/AiFeatureUsageRateLimiter.test.mjs

@@ -61,12 +61,16 @@ describe('AiFeatureUsageRateLimiter', function () {
         quotaTierGranted: 'unlimited',
       },
       aiFeatures: {
-        freeTrialQuota: 'basic',
+        freeQuota: 'free',
+        standardQuota: 'standard',
+        basicQuota: 'basic',
         unlimitedQuota: 'unlimited',
       },
       quotaGrants: {
         ai: {
+          free: 5,
           basic: 5,
+          standard: 10,
           unlimited: 200,
         },
       },