mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-09 07:08:09 -05:00
fix(backend): Make monthly top-up adjust the target balance instead of top-up the target amount (#9295)
To maintain the transaction running-balance integrity, the monthly top-up balance has to adjust the top-up amount to maintain the balance of the user to be at least X amount, instead of top-up the user balance with a fixed X amount. ### Changes 🏗️ * Add an additional query to sum the remaining un-snapshotted balance (if any). * Monthly top-up transaction set the amount target_amount - current_balance instead of fixed target_amount. ### Checklist 📋 #### For code changes: - [ ] I have clearly listed my changes in the PR description - [ ] I have made a test plan - [ ] I have tested my changes according to the test plan: <!-- Put your test plan here: --> - [ ] ... <details> <summary>Example test plan</summary> - [ ] Create from scratch and execute an agent with at least 3 blocks - [ ] Import an agent from file upload, and confirm it executes correctly - [ ] Upload agent to marketplace - [ ] Import an agent from marketplace and confirm it executes correctly - [ ] Edit an agent from monitor, and confirm it executes correctly </details> #### For configuration changes: - [ ] `.env.example` is updated or already compatible with my changes - [ ] `docker-compose.yml` is updated or already compatible with my changes - [ ] I have included a list of my configuration changes in the PR description (under **Changes**) <details> <summary>Examples of configuration changes</summary> - Changing ports - Adding new services that need to communicate with each other - Secrets or environment variable changes - New or infrastructure changes such as databases </details>
This commit is contained in:
@@ -110,24 +110,35 @@ class UserCreditBase(ABC):
|
||||
},
|
||||
order={"createdAt": "desc"},
|
||||
)
|
||||
if snapshot:
|
||||
return snapshot.runningBalance or 0, snapshot.createdAt
|
||||
datetime_min = datetime.min.replace(tzinfo=timezone.utc)
|
||||
snapshot_balance = snapshot.runningBalance or 0 if snapshot else 0
|
||||
snapshot_time = snapshot.createdAt if snapshot else datetime_min
|
||||
|
||||
# No snapshot: Manually calculate balance using current month's transactions.
|
||||
low_time = top_time.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||
# Get transactions after the snapshot, this should not exist, but just in case.
|
||||
transactions = await CreditTransaction.prisma().group_by(
|
||||
by=["userId"],
|
||||
sum={"amount": True},
|
||||
max={"createdAt": True},
|
||||
where={
|
||||
"userId": user_id,
|
||||
"createdAt": {"gte": low_time, "lte": top_time},
|
||||
"createdAt": {
|
||||
"gt": snapshot_time,
|
||||
"lte": top_time,
|
||||
},
|
||||
"isActive": True,
|
||||
},
|
||||
)
|
||||
transaction_balance = (
|
||||
transactions[0].get("_sum", {}).get("amount", 0) if transactions else 0
|
||||
transactions[0].get("_sum", {}).get("amount", 0) + snapshot_balance
|
||||
if transactions
|
||||
else snapshot_balance
|
||||
)
|
||||
return transaction_balance, datetime.min
|
||||
transaction_time = (
|
||||
transactions[0].get("_max", {}).get("createdAt", datetime_min)
|
||||
if transactions
|
||||
else snapshot_time
|
||||
)
|
||||
return transaction_balance, transaction_time
|
||||
|
||||
async def _enable_transaction(
|
||||
self, transaction_key: str, user_id: str, metadata: Json
|
||||
@@ -389,22 +400,15 @@ class BetaUserCredit(UserCredit):
|
||||
return balance
|
||||
|
||||
try:
|
||||
await CreditTransaction.prisma().create(
|
||||
data={
|
||||
"transactionKey": f"MONTHLY-CREDIT-TOP-UP-{cur_time}",
|
||||
"userId": user_id,
|
||||
"amount": self.num_user_credits_refill,
|
||||
"runningBalance": self.num_user_credits_refill,
|
||||
"type": CreditTransactionType.TOP_UP,
|
||||
"metadata": Json({}),
|
||||
"isActive": True,
|
||||
"createdAt": self.time_now(),
|
||||
}
|
||||
return await self._add_transaction(
|
||||
user_id=user_id,
|
||||
amount=max(self.num_user_credits_refill - balance, 0),
|
||||
transaction_type=CreditTransactionType.TOP_UP,
|
||||
transaction_key=f"MONTHLY-CREDIT-TOP-UP-{cur_time}",
|
||||
)
|
||||
except UniqueViolationError:
|
||||
pass # Already refilled this month
|
||||
|
||||
return self.num_user_credits_refill
|
||||
# Already refilled this month
|
||||
return (await self._get_credits(user_id))[0]
|
||||
|
||||
|
||||
class DisabledUserCredit(UserCreditBase):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import pytest
|
||||
from prisma.models import CreditTransaction
|
||||
@@ -83,17 +83,17 @@ async def test_block_credit_reset(server: SpinTestServer):
|
||||
month2 = 2
|
||||
|
||||
# set the calendar to month 2 but use current time from now
|
||||
user_credit.time_now = lambda: datetime.now().replace(month=month2)
|
||||
user_credit.time_now = lambda: datetime.now(timezone.utc).replace(month=month2)
|
||||
month2credit = await user_credit.get_credits(DEFAULT_USER_ID)
|
||||
|
||||
# Month 1 result should only affect month 1
|
||||
user_credit.time_now = lambda: datetime.now().replace(month=month1)
|
||||
user_credit.time_now = lambda: datetime.now(timezone.utc).replace(month=month1)
|
||||
month1credit = await user_credit.get_credits(DEFAULT_USER_ID)
|
||||
await user_credit.top_up_credits(DEFAULT_USER_ID, 100)
|
||||
assert await user_credit.get_credits(DEFAULT_USER_ID) == month1credit + 100
|
||||
|
||||
# Month 2 balance is unaffected
|
||||
user_credit.time_now = lambda: datetime.now().replace(month=month2)
|
||||
user_credit.time_now = lambda: datetime.now(timezone.utc).replace(month=month2)
|
||||
assert await user_credit.get_credits(DEFAULT_USER_ID) == month2credit
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user