feat(backend): Update schema for PAYG System (#8944)

First step for the PAYG System.

### Changes 🏗️

- Add `stripeCustomerId` to `User` model
- Rename model `UserBlockCredit` to `CreditTransaction`
- Rename model `UserBlockCreditType` to `CreditTransactionType`
- Update related code
- Add a migration

### 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:
Krzysztof Czerwinski 2024-12-11 16:52:13 +00:00 committed by GitHub
parent 6490b4e188
commit 3fd2b7ce4a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 32 additions and 23 deletions

View File

@ -2,9 +2,9 @@ from abc import ABC, abstractmethod
from datetime import datetime, timezone
from prisma import Json
from prisma.enums import UserBlockCreditType
from prisma.enums import CreditTransactionType
from prisma.errors import UniqueViolationError
from prisma.models import UserBlockCredit
from prisma.models import CreditTransaction
from backend.data.block import Block, BlockInput, get_block
from backend.data.block_cost_config import BLOCK_COSTS
@ -76,7 +76,7 @@ class UserCredit(UserCreditBase):
else cur_month.replace(year=cur_month.year + 1, month=1)
)
user_credit = await UserBlockCredit.prisma().group_by(
user_credit = await CreditTransaction.prisma().group_by(
by=["userId"],
sum={"amount": True},
where={
@ -93,10 +93,10 @@ class UserCredit(UserCreditBase):
key = f"MONTHLY-CREDIT-TOP-UP-{cur_month}"
try:
await UserBlockCredit.prisma().create(
await CreditTransaction.prisma().create(
data={
"amount": self.num_user_credits_refill,
"type": UserBlockCreditType.TOP_UP,
"type": CreditTransactionType.TOP_UP,
"userId": user_id,
"transactionKey": key,
"createdAt": self.time_now(),
@ -184,11 +184,11 @@ class UserCredit(UserCreditBase):
if validate_balance and user_credit < cost:
raise ValueError(f"Insufficient credit: {user_credit} < {cost}")
await UserBlockCredit.prisma().create(
await CreditTransaction.prisma().create(
data={
"userId": user_id,
"amount": -cost,
"type": UserBlockCreditType.USAGE,
"type": CreditTransactionType.USAGE,
"blockId": block.id,
"metadata": Json(
{
@ -202,11 +202,11 @@ class UserCredit(UserCreditBase):
return cost
async def top_up_credits(self, user_id: str, amount: int):
await UserBlockCredit.prisma().create(
await CreditTransaction.prisma().create(
data={
"userId": user_id,
"amount": amount,
"type": UserBlockCreditType.TOP_UP,
"type": CreditTransactionType.TOP_UP,
"createdAt": self.time_now(),
}
)

View File

@ -0,0 +1,8 @@
-- AlterTable
ALTER TABLE "User" ADD COLUMN "stripeCustomerId" TEXT;
-- AlterEnum
ALTER TYPE "UserBlockCreditType" RENAME TO "CreditTransactionType";
-- AlterTable
ALTER TABLE "UserBlockCredit" RENAME TO "CreditTransaction";

View File

@ -12,13 +12,14 @@ generator client {
// User model to mirror Auth provider users
model User {
id String @id // This should match the Supabase user ID
email String @unique
name String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
metadata Json @default("{}")
integrations String @default("")
id String @id // This should match the Supabase user ID
email String @unique
name String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
metadata Json @default("{}")
integrations String @default("")
stripeCustomerId String?
// Relations
AgentGraphs AgentGraph[]
@ -26,7 +27,7 @@ model User {
IntegrationWebhooks IntegrationWebhook[]
AnalyticsDetails AnalyticsDetails[]
AnalyticsMetrics AnalyticsMetrics[]
UserBlockCredit UserBlockCredit[]
CreditTransaction CreditTransaction[]
APIKeys APIKey[]
@@index([id])
@ -123,7 +124,7 @@ model AgentBlock {
// Prisma requires explicit back-references.
ReferencedByAgentNode AgentNode[]
UserBlockCredit UserBlockCredit[]
CreditTransaction CreditTransaction[]
}
// This model describes the status of an AgentGraphExecution or AgentNodeExecution.
@ -275,12 +276,12 @@ model AnalyticsMetrics {
@@index([userId])
}
enum UserBlockCreditType {
enum CreditTransactionType {
TOP_UP
USAGE
}
model UserBlockCredit {
model CreditTransaction {
transactionKey String @default(uuid())
createdAt DateTime @default(now())
@ -291,7 +292,7 @@ model UserBlockCredit {
block AgentBlock? @relation(fields: [blockId], references: [id])
amount Int
type UserBlockCreditType
type CreditTransactionType
isActive Boolean @default(true)
metadata Json?

View File

@ -1,7 +1,7 @@
from datetime import datetime
import pytest
from prisma.models import UserBlockCredit
from prisma.models import CreditTransaction
from backend.blocks.llm import AITextGeneratorBlock
from backend.data.credit import UserCredit
@ -82,7 +82,7 @@ async def test_block_credit_reset(server: SpinTestServer):
@pytest.mark.asyncio(scope="session")
async def test_credit_refill(server: SpinTestServer):
# Clear all transactions within the month
await UserBlockCredit.prisma().update_many(
await CreditTransaction.prisma().update_many(
where={
"userId": DEFAULT_USER_ID,
"createdAt": {