Ver Fonte

Revert "[Web] Enable Quota System for AI Features (#31544)" (#31767)

This reverts commit 17763447965aae5777053b783d2601517bfe6b12.

GitOrigin-RevId: f6589bdbf0ac7e71313739e3e3f4fb5bedd48c22
Jimmy Domagala-Tang há 5 meses atrás
pai
commit
892047fcf6

+ 1 - 8
services/web/app/src/Features/Institutions/InstitutionsFeatures.mjs

@@ -15,14 +15,7 @@ async function _getInstitutionsAddons(userId) {
   const hasAssistBundle = affiliates.some(
     affiliate => affiliate?.institution?.writefullCommonsAccount === true
   )
-
-  // todo: seperate quota value depending on source of entitlement if needed
-  // todo: quota clean-up: remove aiErrorAssistant once migration finishes
-  const bundleFeatures = {
-    aiUsageQuota: Settings.writefull.quotaTierGranted,
-    aiErrorAssistant: true,
-  }
-  return hasAssistBundle ? bundleFeatures : {}
+  return hasAssistBundle ? { aiErrorAssistant: true } : {}
 }
 
 async function getInstitutionsFeatures(userId) {

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

@@ -952,8 +952,6 @@ const _ProjectController = {
         symbolPaletteAvailable: Features.hasFeature('symbol-palette'),
         userRestrictions: Array.from(req.userRestrictions || []),
         showAiFeatures: aiFeaturesAllowed && !aiFeaturesDisabled,
-        onAiFreeTrial:
-          user.features?.aiUsageQuota === Settings.aiFeatures?.freeTrialQuota,
         detachRole,
         metadata: { viewport: false },
         showUpgradePrompt,

+ 4 - 9
services/web/app/src/Features/Project/ProjectListController.mjs

@@ -510,10 +510,8 @@ async function projectListPage(req, res, next) {
     logger.error({ err: error }, 'Failed to get individual subscription')
   }
 
-  const aiBlocked =
-    Features.hasFeature('saas') && !(await _canUseAIAssist(user))
-  const hasAiAssist =
-    Features.hasFeature('saas') && (await _userHasAIAssist(user))
+  const aiBlocked = !(await _canUseAIAssist(user))
+  const hasAiAssist = await _userHasAIAssist(user)
 
   await SplitTestHandler.promises.getAssignment(
     req,
@@ -901,13 +899,11 @@ function _hasActiveFilter(filters) {
   )
 }
 
-// todo: quota clean-up: rename function and vars
 async function _userHasAIAssist(user) {
-  // Check if the user has a non free trial version of our AI features
-  if (user.features?.aiUsageQuota === Settings.aiFeatures.unlimitedQuota) {
+  // Check if the user has AI Assist enabled via Overleaf
+  if (user.features?.aiErrorAssistant) {
     return true
   }
-
   // Check if the user has AI Assist enabled via Writefull
   const { isPremium: hasAiAssistViaWritefull } =
     await UserGetter.promises.getWritefullData(user._id)
@@ -922,7 +918,6 @@ async function _userHasAIAssist(user) {
 // It does NOT determine if the user has AI Assist enabled
 async function _canUseAIAssist(user) {
   // Check if the assistant has been manually disabled by the user
-  // post https://github.com/overleaf/internal/pull/31273 we can rely on user.aiFeatures being populated
   if (user.aiFeatures?.enabled === false) {
     return false
   }

+ 0 - 16
services/web/app/src/Features/SplitTests/SplitTestHandler.mjs

@@ -306,20 +306,6 @@ async function getOneTimeAssignment(splitTestName) {
   }
 }
 
-/**
- * Checks if a feature flag is enabled for a specific user
- *
- * Retrieves the feature flag assignment for a user and determines if the assigned variant is 'enabled'
- *
- * @param {string} userId - The ID of the user to check the feature flag for
- * @param {string} splitTestName - The unique name of the feature flag
- * @returns {Promise<boolean>} True if the user's assigned variant is 'enabled', false otherwise
- */
-async function featureFlagEnabledForUser(userId, splitTestName) {
-  const { variant } = await getAssignmentForUser(userId, splitTestName)
-  return variant === 'enabled'
-}
-
 /**
  * Returns an array of valid variant names for the given split test, including default
  *
@@ -816,7 +802,6 @@ export default {
   getPercentile,
   getAssignment: callbackify(getAssignment),
   getAssignmentForUser: callbackify(getAssignmentForUser),
-  featureFlagEnabledForUser: callbackify(featureFlagEnabledForUser),
   getOneTimeAssignment: callbackify(getOneTimeAssignment),
   getActiveAssignmentsForUser: callbackify(getActiveAssignmentsForUser),
   hasUserBeenAssignedToVariant: callbackify(hasUserBeenAssignedToVariant),
@@ -825,7 +810,6 @@ export default {
   promises: {
     getAssignment,
     getAssignmentForUser,
-    featureFlagEnabledForUser,
     getOneTimeAssignment,
     getActiveAssignmentsForUser,
     hasUserBeenAssignedToVariant,

+ 0 - 9
services/web/app/src/Features/Subscription/FeaturesHelper.mjs

@@ -38,15 +38,6 @@ function mergeFeatures(featuresA, featuresB) {
         features.compileTimeout || 0,
         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
-      }
     } else {
       // Boolean keys, true is better
       features[key] = features[key] || featuresB[key]

+ 2 - 13
services/web/app/src/Features/Subscription/FeaturesUpdater.mjs

@@ -100,10 +100,7 @@ async function refreshFeatures(userId, reason) {
           },
           json: {
             userOverleafId: userId,
-            // todo: quota clean-up: collab with writefull to rename this, and check if still needed
-            hasAiAssist:
-              newFeatures.aiErrorAssistant ||
-              newFeatures.aiUsageQuota === Settings.aiFeatures.unlimitedQuota,
+            hasAiAssist: newFeatures.aiErrorAssistant,
           },
           method: 'POST',
         }
@@ -195,9 +192,6 @@ async function _getIndividualFeatures(userId) {
     featureSets.push(_subscriptionToFeatures(subscription))
   }
 
-  // todo: quota clean-up - remove
-  // if they are in the quota split test, we no longer look at the add-on, since every plan will now have the same quota
-  // standalone plan will receive correct state since their plan will provide the correct quota
   featureSets.push(_aiAddOnFeatures(subscription))
   return _.reduce(featureSets, FeaturesHelper.mergeFeatures, {})
 }
@@ -243,14 +237,9 @@ function _subscriptionToFeatures(subscription) {
   }
 }
 
-// todo: quota clean-up: remove post split test
 function _aiAddOnFeatures(subscription) {
   if (subscription?.addOns?.some(addOn => addOn.addOnCode === AI_ADD_ON_CODE)) {
-    return {
-      // allow both naming systems to work
-      aiErrorAssistant: true,
-      aiUsageQuota: Settings.aiFeatures.unlimitedQuota,
-    }
+    return { aiErrorAssistant: true }
   } else {
     return {}
   }

+ 2 - 0
services/web/app/src/infrastructure/FeatureUsageRateLimiter.mjs

@@ -87,6 +87,7 @@ export default class FeatureUsageRateLimiter {
         upsert: true,
       }
     ).exec()
+
     const featureUsage = featureUsages.features?.[this.featureName] ?? {}
     setRateLimitHeaders(res, featureUsage, allowance)
     this._checkRateLimit(featureUsage, allowance)
@@ -184,6 +185,7 @@ function setRateLimitHeaders(res, featureUsage, allowance) {
   const usage = featureUsage.usage ?? 0
   const refreshEpoch = periodStart.getTime() + PERIOD_IN_MILLISECONDS
   const secondsTillReset = Math.ceil((refreshEpoch - Date.now()) / 1000)
+
   if (!res.headersSent) {
     res.set('RateLimit-Limit', String(allowance))
     res.set('RateLimit-Remaining', String(Math.max(0, allowance - usage)))

+ 0 - 3
services/web/app/src/models/User.mjs

@@ -143,7 +143,6 @@ export const UserSchema = new Schema(
         type: Boolean,
         default: false,
       },
-      aiUsageQuota: { type: String, default: 'basic' },
     },
     featuresOverrides: [
       {
@@ -156,9 +155,7 @@ export const UserSchema = new Schema(
         expiresAt: { type: Date },
         note: { type: String },
         features: {
-          // todo: quota clean-up: remove aiErrorAssistant
           aiErrorAssistant: { type: Boolean },
-          aiUsageQuota: { type: String },
           collaborators: { type: Number },
           versioning: { type: Boolean },
           dropbox: { type: Boolean },

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

@@ -25,7 +25,6 @@ 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-detachRole" data-type="string" content=detachRole)
 meta(name="ol-imageNames" data-type="json" content=imageNames)
 meta(name="ol-languages" data-type="json" content=languages)

+ 0 - 5
services/web/config/settings.defaults.js

@@ -419,11 +419,6 @@ module.exports = {
     personal: defaultFeatures,
   },
 
-  aiFeatures: {
-    freeTrialQuota: 'basic',
-    unlimitedQuota: 'unlimited',
-  },
-
   groupPlanModalOptions: {
     plan_codes: [],
     currencies: [],

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

@@ -11,12 +11,9 @@ import { useUserContext } from './user-context'
 import { useReceiveUser } from '../hooks/user-channel/use-receive-user'
 import { getJSON } from '@/infrastructure/fetch-json'
 import { useEditorContext } from './editor-context'
-import getMeta from '@/utils/meta'
 
 export const UserFeaturesContext = createContext<User['features']>(undefined)
 
-const onAiFreeTrial = getMeta('ol-onAiFreeTrial')
-
 export const UserFeaturesProvider: FC<React.PropsWithChildren> = ({
   children,
 }) => {
@@ -34,12 +31,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 alreadyPremium =
-        features?.aiErrorAssistant === isPremium ||
-        hasPremiumQuota === isPremium
-      if (alreadyPremium) {
+      if (features?.aiErrorAssistant === isPremium) {
         // the user is premium on writefull and has the AI assist, no need to refresh the features
         return
       }

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

@@ -213,7 +213,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

+ 0 - 4
services/web/test/acceptance/config/settings.test.defaults.js

@@ -115,7 +115,6 @@ module.exports = {
       compileGroup: 'standard',
       trackChanges: false,
       symbolPalette: false,
-      aiUsageQuota: 'basic',
       aiErrorAssistant: false,
     },
     personal: {
@@ -133,7 +132,6 @@ module.exports = {
       compileGroup: 'standard',
       trackChanges: false,
       symbolPalette: false,
-      aiUsageQuota: 'basic',
       aiErrorAssistant: false,
     },
     collaborator: {
@@ -151,7 +149,6 @@ module.exports = {
       compileGroup: 'priority',
       trackChanges: true,
       symbolPalette: true,
-      aiUsageQuota: 'basic',
       aiErrorAssistant: false,
     },
     professional: {
@@ -169,7 +166,6 @@ module.exports = {
       compileGroup: 'priority',
       trackChanges: true,
       symbolPalette: true,
-      aiUsageQuota: 'basic',
       aiErrorAssistant: false,
     },
   }),

+ 2 - 17
services/web/test/unit/src/Institutions/InstitutionsFeatures.test.mjs

@@ -30,13 +30,6 @@ describe('InstitutionsFeatures', function () {
       default: {
         institutionPlanCode: ctx.institutionPlanCode,
         overleaf: {},
-        writefull: {
-          quotaTierGranted: 'unlimited',
-        },
-        aiFeatures: {
-          freeTrialQuota: 'basic',
-          unlimitedQuota: 'unlimited',
-        },
       },
     }))
 
@@ -59,18 +52,10 @@ describe('InstitutionsFeatures', function () {
     }
     ctx.testFeatures = { features: { institution: 'all' } }
     ctx.testFeaturesWithAiAddon = {
-      features: {
-        institution: 'all',
-        aiUsageQuota: 'unlimited',
-        aiErrorAssistant: true,
-      },
+      features: { institution: 'all', aiErrorAssistant: true },
     }
     ctx.testFeaturesWithNoAddon = {
-      features: {
-        institution: 'all',
-        aiUsageQuota: 'basic',
-        aiErrorAssistant: false,
-      },
+      features: { institution: 'all', aiErrorAssistant: false },
     }
   })
 

+ 0 - 1
services/web/test/unit/src/Project/ProjectListController.test.mjs

@@ -40,7 +40,6 @@ describe('ProjectListController', function () {
         theme: 'textmate',
         mode: 'none',
       },
-      aiFeatures: { enabled: false },
     }
     ctx.users = {
       'user-1': {

+ 0 - 7
services/web/test/unit/src/Subscription/FeaturesUpdater.test.mjs

@@ -74,11 +74,6 @@ describe('FeaturesUpdater', function () {
       },
       writefull: {
         overleafApiUrl: 'https://www.writefull.com',
-        quotaTierGranted: 'unlimited',
-      },
-      aiFeatures: {
-        freeTrialQuota: 'basic',
-        unlimitedQuota: 'unlimited',
       },
     }
 
@@ -328,7 +323,6 @@ describe('FeaturesUpdater', function () {
           default: 'features',
           individual: 'features',
           aiErrorAssistant: true,
-          aiUsageQuota: 'unlimited',
         })
       })
     })
@@ -347,7 +341,6 @@ describe('FeaturesUpdater', function () {
         expect(features).to.deep.equal({
           default: 'features',
           aiErrorAssistant: true,
-          aiUsageQuota: 'unlimited',
         })
       })
     })

+ 2 - 6
services/web/test/unit/src/User/UserGetter.test.mjs

@@ -66,10 +66,6 @@ describe('UserGetter', function () {
     vi.doMock('@overleaf/settings', () => ({
       default: (ctx.settings = {
         reconfirmNotificationDays: 14,
-        aiFeatures: {
-          freeTrialQuota: 'basic',
-          unlimitedQuota: 'unlimited',
-        },
       }),
     }))
 
@@ -1316,8 +1312,8 @@ describe('UserGetter', function () {
 
     it('should take into account features overrides from modules', async function (ctx) {
       // this case occurs when the user has bought the ai bundle on WF, which should include our error assistant
-      const bundleFeatures = { aiUsageQuota: 'unlimited' }
-      ctx.fakeUser.features = { aiUsageQuota: 'basic' }
+      const bundleFeatures = { aiErrorAssistant: true }
+      ctx.fakeUser.features = { aiErrorAssistant: false }
       ctx.Modules.promises.hooks.fire = sinon.stub().resolves([bundleFeatures])
       const features = await ctx.UserGetter.promises.getUserFeatures(
         ctx.fakeUser._id

+ 0 - 1
services/web/types/user.ts

@@ -10,7 +10,6 @@ export type UserId = Brand<string, 'UserId'>
 
 export type Features = {
   aiErrorAssistant?: boolean
-  aiUsageQuota?: string
   collaborators?: number
   compileGroup?: 'standard' | 'priority'
   compileTimeout?: number