mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-08 14:53:53 -05:00
## Summary This PR implements a comprehensive marketplace update notification system that allows users to discover and update to newer agent versions, along with enhanced publishing workflows and UI improvements. <img width="1500" height="533" alt="image" src="https://github.com/user-attachments/assets/ee331838-d712-4718-b231-1f9ec21bcd8e" /> <img width="600" height="610" alt="image" src="https://github.com/user-attachments/assets/b881a7b8-91a5-460d-a159-f64765b339f1" /> <img width="1500" height="416" alt="image" src="https://github.com/user-attachments/assets/a2d61904-2673-4e44-bcc5-c47d36af7a38" /> <img width="1500" height="1015" alt="image" src="https://github.com/user-attachments/assets/2dd978c7-20cc-4230-977e-9c62157b9f23" /> ## Core Features ### 🔔 Marketplace Update Notifications - **Update detection**: Automatically detects when marketplace has newer agent versions than user's local copy - **Creator notifications**: Shows banners for creators with unpublished changes ready to publish - **Non-creator support**: Enables regular users to discover and update to newer marketplace versions - **Version comparison**: Intelligent logic comparing `graph_version` vs marketplace listing versions ### 📋 Enhanced Publishing Workflow - **Builder integration**: Added "Publish to Marketplace" button directly in the builder actions - **Unified banner system**: Consistent `MarketplaceBanners` component across library and marketplace pages - **Streamlined UX**: Fixed layout issues, improved button placement and styling - **Modal improvements**: Fixed thumbnail loading race conditions and infinite loop bugs ### 📚 Version History & Changelog - **Inline version history**: Added version changelog directly to marketplace agent pages - **Version comparison**: Clear display of available versions with current version highlighting - **Update mechanism**: Direct updates using `graph_version` parameter for accuracy ## Technical Implementation ### Backend Changes - **Database schema**: Added `agentGraphVersions` and `agentGraphId` fields to `StoreAgent` model - **API enhancement**: Updated store endpoints to expose graph version data for version comparison - **Data migration**: Fixed agent version field naming from `version` to `agentGraphVersions` - **Model updates**: Enhanced `LibraryAgentUpdateRequest` with `graph_version` field ### Frontend Architecture - **`useMarketplaceUpdate` hook**: Centralized marketplace update detection and creator identification - **`MarketplaceBanners` component**: Unified banner system with proper vertical layout and styling - **`AgentVersionChangelog` component**: Version history display for marketplace pages - **`PublishToMarketplace` component**: Builder integration with modal workflow ### Key Bug Fixes - **Thumbnail loading**: Fixed race condition where images wouldn't load on first modal open - **Infinite loops**: Used refs to prevent circular dependencies in `useThumbnailImages` hook - **Layout issues**: Fixed banner placement, removed duplicate breadcrumbs, corrected vertical layout - **Field naming**: Fixed `agent_version` vs `version` field inconsistencies across APIs ## Files Changed ### Backend - `autogpt_platform/backend/backend/server/v2/store/` - Enhanced store API with graph version data - `autogpt_platform/backend/backend/server/v2/library/` - Updated library API models - `autogpt_platform/backend/migrations/` - Database migrations for version fields - `autogpt_platform/backend/schema.prisma` - Schema updates for graph versions ### Frontend - `src/app/(platform)/components/MarketplaceBanners/` - New unified banner component - `src/app/(platform)/library/agents/[id]/components/` - Enhanced library views with banners - `src/app/(platform)/build/components/BuilderActions/` - Added marketplace publish button - `src/app/(platform)/marketplace/components/AgentInfo/` - Added inline version history - `src/components/contextual/PublishAgentModal/` - Fixed thumbnail loading and modal workflow ## User Experience Impact - **Better discovery**: Users automatically notified of newer agent versions - **Streamlined publishing**: Direct publish access from builder interface - **Reduced friction**: Fixed UI bugs, improved loading states, consistent design - **Enhanced transparency**: Inline version history on marketplace pages - **Creator workflow**: Better notifications for creators with unpublished changes ## Testing - ✅ Update banners appear correctly when marketplace has newer versions - ✅ Creator banners show for users with unpublished changes - ✅ Version comparison logic works with graph_version vs marketplace versions - ✅ Publish button in builder opens modal correctly with pre-populated data - ✅ Thumbnail images load properly on first modal open without infinite loops - ✅ Database migrations completed successfully with version field fixes - ✅ All existing tests updated and passing with new schema changes 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Lluis Agusti <hi@llu.lu> Co-authored-by: Ubbe <hi@ubbe.dev> Co-authored-by: Reinier van der Leer <pwuts@agpt.co>
1097 lines
35 KiB
Plaintext
1097 lines
35 KiB
Plaintext
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
directUrl = env("DIRECT_URL")
|
|
}
|
|
|
|
generator client {
|
|
provider = "prisma-client-py"
|
|
recursive_type_depth = -1
|
|
interface = "asyncio"
|
|
previewFeatures = ["views", "fullTextSearch"]
|
|
partial_type_generator = "backend/data/partial_types.py"
|
|
}
|
|
|
|
// User model to mirror Auth provider users
|
|
model User {
|
|
id String @id // This should match the Supabase user ID
|
|
email String @unique
|
|
emailVerified Boolean @default(true)
|
|
name String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
metadata Json @default("{}")
|
|
integrations String @default("")
|
|
stripeCustomerId String?
|
|
topUpConfig Json?
|
|
|
|
maxEmailsPerDay Int @default(3)
|
|
notifyOnAgentRun Boolean @default(true)
|
|
notifyOnZeroBalance Boolean @default(true)
|
|
notifyOnLowBalance Boolean @default(true)
|
|
notifyOnBlockExecutionFailed Boolean @default(true)
|
|
notifyOnContinuousAgentError Boolean @default(true)
|
|
notifyOnDailySummary Boolean @default(true)
|
|
notifyOnWeeklySummary Boolean @default(true)
|
|
notifyOnMonthlySummary Boolean @default(true)
|
|
notifyOnAgentApproved Boolean @default(true)
|
|
notifyOnAgentRejected Boolean @default(true)
|
|
|
|
timezone String @default("not-set")
|
|
|
|
// Relations
|
|
|
|
AgentGraphs AgentGraph[]
|
|
AgentGraphExecutions AgentGraphExecution[]
|
|
AnalyticsDetails AnalyticsDetails[]
|
|
AnalyticsMetrics AnalyticsMetrics[]
|
|
CreditTransactions CreditTransaction[]
|
|
UserBalance UserBalance?
|
|
|
|
AgentPresets AgentPreset[]
|
|
LibraryAgents LibraryAgent[]
|
|
|
|
Profile Profile[]
|
|
UserOnboarding UserOnboarding?
|
|
BuilderSearchHistory BuilderSearchHistory[]
|
|
StoreListings StoreListing[]
|
|
StoreListingReviews StoreListingReview[]
|
|
StoreVersionsReviewed StoreListingVersion[]
|
|
APIKeys APIKey[]
|
|
IntegrationWebhooks IntegrationWebhook[]
|
|
NotificationBatches UserNotificationBatch[]
|
|
PendingHumanReviews PendingHumanReview[]
|
|
|
|
// OAuth Provider relations
|
|
OAuthApplications OAuthApplication[]
|
|
OAuthAuthorizationCodes OAuthAuthorizationCode[]
|
|
OAuthAccessTokens OAuthAccessToken[]
|
|
OAuthRefreshTokens OAuthRefreshToken[]
|
|
}
|
|
|
|
enum OnboardingStep {
|
|
// Introductory onboarding (Library)
|
|
WELCOME
|
|
USAGE_REASON
|
|
INTEGRATIONS
|
|
AGENT_CHOICE
|
|
AGENT_NEW_RUN
|
|
AGENT_INPUT
|
|
CONGRATS
|
|
// First Wins
|
|
GET_RESULTS
|
|
MARKETPLACE_VISIT
|
|
MARKETPLACE_ADD_AGENT
|
|
MARKETPLACE_RUN_AGENT
|
|
BUILDER_SAVE_AGENT
|
|
// Consistency Challenge
|
|
RE_RUN_AGENT
|
|
SCHEDULE_AGENT
|
|
RUN_AGENTS
|
|
RUN_3_DAYS
|
|
// The Pro Playground
|
|
TRIGGER_WEBHOOK
|
|
RUN_14_DAYS
|
|
RUN_AGENTS_100
|
|
// No longer rewarded but exist for analytical purposes
|
|
BUILDER_OPEN
|
|
BUILDER_RUN_AGENT
|
|
}
|
|
|
|
model UserOnboarding {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime? @updatedAt
|
|
|
|
completedSteps OnboardingStep[] @default([])
|
|
walletShown Boolean @default(false)
|
|
notified OnboardingStep[] @default([])
|
|
rewardedFor OnboardingStep[] @default([])
|
|
usageReason String?
|
|
integrations String[] @default([])
|
|
otherIntegrations String?
|
|
selectedStoreListingVersionId String?
|
|
agentInput Json?
|
|
onboardingAgentExecutionId String?
|
|
agentRuns Int @default(0)
|
|
lastRunAt DateTime?
|
|
consecutiveRunDays Int @default(0)
|
|
|
|
userId String @unique
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
model BuilderSearchHistory {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
searchQuery String
|
|
filter String[] @default([])
|
|
byCreator String[] @default([])
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
// This model describes the Agent Graph/Flow (Multi Agent System).
|
|
model AgentGraph {
|
|
id String @default(uuid())
|
|
version Int @default(1)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime? @updatedAt
|
|
|
|
name String?
|
|
description String?
|
|
instructions String?
|
|
recommendedScheduleCron String?
|
|
|
|
isActive Boolean @default(true)
|
|
|
|
// Link to User model
|
|
userId String
|
|
// FIX: Do not cascade delete the agent when the user is deleted
|
|
// This allows us to delete user data with deleting the agent which maybe in use by other users
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
forkedFromId String?
|
|
forkedFromVersion Int?
|
|
forkedFrom AgentGraph? @relation("AgentGraphForks", fields: [forkedFromId, forkedFromVersion], references: [id, version])
|
|
forks AgentGraph[] @relation("AgentGraphForks")
|
|
|
|
Nodes AgentNode[]
|
|
Executions AgentGraphExecution[]
|
|
|
|
Presets AgentPreset[]
|
|
LibraryAgents LibraryAgent[]
|
|
StoreListings StoreListing[]
|
|
StoreListingVersions StoreListingVersion[]
|
|
|
|
@@id(name: "graphVersionId", [id, version])
|
|
@@index([userId, isActive, id, version])
|
|
@@index([forkedFromId, forkedFromVersion])
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
//////////////// USER SPECIFIC DATA ////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
|
|
// An AgentPrest is an Agent + User Configuration of that agent.
|
|
// For example, if someone has created a weather agent and they want to set it up to
|
|
// Inform them of extreme weather warnings in Texas, the agent with the configuration to set it to
|
|
// monitor texas, along with the cron setup or webhook tiggers, is an AgentPreset
|
|
model AgentPreset {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
name String
|
|
description String
|
|
|
|
// For agents that can be triggered by webhooks or cronjob
|
|
// This bool allows us to disable a configured agent without deleting it
|
|
isActive Boolean @default(true)
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
agentGraphId String
|
|
agentGraphVersion Int
|
|
AgentGraph AgentGraph @relation(fields: [agentGraphId, agentGraphVersion], references: [id, version], onDelete: Restrict)
|
|
|
|
InputPresets AgentNodeExecutionInputOutput[] @relation("AgentPresetsInputData")
|
|
Executions AgentGraphExecution[]
|
|
|
|
// For webhook-triggered agents: reference to the webhook that triggers the agent
|
|
webhookId String?
|
|
Webhook IntegrationWebhook? @relation(fields: [webhookId], references: [id])
|
|
|
|
isDeleted Boolean @default(false)
|
|
|
|
@@index([userId])
|
|
@@index([agentGraphId, agentGraphVersion])
|
|
@@index([webhookId])
|
|
}
|
|
|
|
enum NotificationType {
|
|
AGENT_RUN
|
|
ZERO_BALANCE
|
|
LOW_BALANCE
|
|
BLOCK_EXECUTION_FAILED
|
|
CONTINUOUS_AGENT_ERROR
|
|
DAILY_SUMMARY
|
|
WEEKLY_SUMMARY
|
|
MONTHLY_SUMMARY
|
|
REFUND_REQUEST
|
|
REFUND_PROCESSED
|
|
AGENT_APPROVED
|
|
AGENT_REJECTED
|
|
}
|
|
|
|
model NotificationEvent {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
UserNotificationBatch UserNotificationBatch? @relation(fields: [userNotificationBatchId], references: [id])
|
|
userNotificationBatchId String?
|
|
|
|
type NotificationType
|
|
data Json
|
|
|
|
@@index([userNotificationBatchId])
|
|
}
|
|
|
|
model UserNotificationBatch {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
type NotificationType
|
|
|
|
Notifications NotificationEvent[]
|
|
|
|
// Each user can only have one batch of a notification type at a time
|
|
@@unique([userId, type])
|
|
}
|
|
|
|
// For the library page
|
|
// It is a user controlled list of agents, that they will see in their library
|
|
model LibraryAgent {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
imageUrl String?
|
|
|
|
agentGraphId String
|
|
agentGraphVersion Int
|
|
AgentGraph AgentGraph @relation(fields: [agentGraphId, agentGraphVersion], references: [id, version], onDelete: Restrict)
|
|
|
|
creatorId String?
|
|
Creator Profile? @relation(fields: [creatorId], references: [id])
|
|
|
|
useGraphIsActiveVersion Boolean @default(false)
|
|
|
|
isFavorite Boolean @default(false)
|
|
isCreatedByUser Boolean @default(false)
|
|
isArchived Boolean @default(false)
|
|
isDeleted Boolean @default(false)
|
|
|
|
settings Json @default("{}")
|
|
|
|
@@unique([userId, agentGraphId, agentGraphVersion])
|
|
@@index([agentGraphId, agentGraphVersion])
|
|
@@index([creatorId])
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
//////// AGENT DEFINITION AND EXECUTION TABLES ////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
|
|
// This model describes a single node in the Agent Graph/Flow (Multi Agent System).
|
|
model AgentNode {
|
|
id String @id @default(uuid())
|
|
|
|
agentBlockId String
|
|
AgentBlock AgentBlock @relation(fields: [agentBlockId], references: [id], onUpdate: Cascade)
|
|
|
|
agentGraphId String
|
|
agentGraphVersion Int @default(1)
|
|
AgentGraph AgentGraph @relation(fields: [agentGraphId, agentGraphVersion], references: [id, version], onDelete: Cascade)
|
|
|
|
// List of consumed input, that the parent node should provide.
|
|
Input AgentNodeLink[] @relation("AgentNodeSink")
|
|
|
|
// List of produced output, that the child node should be executed.
|
|
Output AgentNodeLink[] @relation("AgentNodeSource")
|
|
|
|
constantInput Json @default("{}")
|
|
|
|
// For webhook-triggered blocks: reference to the webhook that triggers the node
|
|
webhookId String?
|
|
Webhook IntegrationWebhook? @relation(fields: [webhookId], references: [id])
|
|
|
|
metadata Json @default("{}")
|
|
|
|
Executions AgentNodeExecution[]
|
|
|
|
@@index([agentGraphId, agentGraphVersion])
|
|
@@index([agentBlockId])
|
|
@@index([webhookId])
|
|
}
|
|
|
|
// This model describes the link between two AgentNodes.
|
|
model AgentNodeLink {
|
|
id String @id @default(uuid())
|
|
|
|
// Output of a node is connected to the source of the link.
|
|
agentNodeSourceId String
|
|
AgentNodeSource AgentNode @relation("AgentNodeSource", fields: [agentNodeSourceId], references: [id], onDelete: Cascade)
|
|
sourceName String
|
|
|
|
// Input of a node is connected to the sink of the link.
|
|
agentNodeSinkId String
|
|
AgentNodeSink AgentNode @relation("AgentNodeSink", fields: [agentNodeSinkId], references: [id], onDelete: Cascade)
|
|
sinkName String
|
|
|
|
// Default: the data coming from the source can only be consumed by the sink once, Static: input data will be reused.
|
|
isStatic Boolean @default(false)
|
|
|
|
@@index([agentNodeSourceId])
|
|
@@index([agentNodeSinkId])
|
|
}
|
|
|
|
// This model describes a component that will be executed by the AgentNode.
|
|
model AgentBlock {
|
|
id String @id @default(uuid())
|
|
name String @unique
|
|
|
|
// We allow a block to have multiple types of input & output.
|
|
// Serialized object-typed `jsonschema` with top-level properties as input/output name.
|
|
inputSchema String
|
|
outputSchema String
|
|
|
|
// Prisma requires explicit back-references.
|
|
ReferencedByAgentNode AgentNode[]
|
|
}
|
|
|
|
// This model describes the status of an AgentGraphExecution or AgentNodeExecution.
|
|
enum AgentExecutionStatus {
|
|
INCOMPLETE
|
|
QUEUED
|
|
RUNNING
|
|
COMPLETED
|
|
TERMINATED
|
|
FAILED
|
|
REVIEW
|
|
}
|
|
|
|
// This model describes the execution of an AgentGraph.
|
|
model AgentGraphExecution {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime? @updatedAt
|
|
startedAt DateTime?
|
|
|
|
isDeleted Boolean @default(false)
|
|
|
|
executionStatus AgentExecutionStatus @default(COMPLETED)
|
|
|
|
agentGraphId String
|
|
agentGraphVersion Int @default(1)
|
|
AgentGraph AgentGraph @relation(fields: [agentGraphId, agentGraphVersion], references: [id, version], onDelete: Cascade)
|
|
|
|
agentPresetId String?
|
|
AgentPreset AgentPreset? @relation(fields: [agentPresetId], references: [id])
|
|
|
|
inputs Json?
|
|
credentialInputs Json?
|
|
nodesInputMasks Json?
|
|
|
|
NodeExecutions AgentNodeExecution[]
|
|
|
|
// Link to User model -- Executed by this user
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
stats Json?
|
|
|
|
// Parent-child execution tracking for nested agent graphs
|
|
parentGraphExecutionId String?
|
|
ParentExecution AgentGraphExecution? @relation("ParentChildExecution", fields: [parentGraphExecutionId], references: [id], onDelete: SetNull)
|
|
ChildExecutions AgentGraphExecution[] @relation("ParentChildExecution")
|
|
|
|
// Sharing fields
|
|
isShared Boolean @default(false)
|
|
shareToken String? @unique
|
|
sharedAt DateTime?
|
|
|
|
PendingHumanReviews PendingHumanReview[]
|
|
|
|
@@index([agentGraphId, agentGraphVersion])
|
|
@@index([userId, isDeleted, createdAt])
|
|
@@index([createdAt])
|
|
@@index([agentPresetId])
|
|
@@index([shareToken])
|
|
@@index([parentGraphExecutionId])
|
|
}
|
|
|
|
// This model describes the execution of an AgentNode.
|
|
model AgentNodeExecution {
|
|
id String @id @default(uuid())
|
|
|
|
agentGraphExecutionId String
|
|
GraphExecution AgentGraphExecution @relation(fields: [agentGraphExecutionId], references: [id], onDelete: Cascade)
|
|
|
|
agentNodeId String
|
|
Node AgentNode @relation(fields: [agentNodeId], references: [id], onDelete: Cascade)
|
|
|
|
Input AgentNodeExecutionInputOutput[] @relation("AgentNodeExecutionInput")
|
|
Output AgentNodeExecutionInputOutput[] @relation("AgentNodeExecutionOutput")
|
|
|
|
executionStatus AgentExecutionStatus @default(COMPLETED)
|
|
executionData Json?
|
|
addedTime DateTime @default(now())
|
|
queuedTime DateTime?
|
|
startedTime DateTime?
|
|
endedTime DateTime?
|
|
|
|
stats Json?
|
|
|
|
PendingHumanReview PendingHumanReview?
|
|
|
|
@@index([agentGraphExecutionId, agentNodeId, executionStatus])
|
|
@@index([agentNodeId, executionStatus])
|
|
@@index([addedTime, queuedTime])
|
|
}
|
|
|
|
// This model describes the output of an AgentNodeExecution.
|
|
model AgentNodeExecutionInputOutput {
|
|
id String @id @default(uuid())
|
|
|
|
name String
|
|
data Json?
|
|
time DateTime @default(now())
|
|
|
|
// Prisma requires explicit back-references.
|
|
referencedByInputExecId String?
|
|
ReferencedByInputExec AgentNodeExecution? @relation("AgentNodeExecutionInput", fields: [referencedByInputExecId], references: [id], onDelete: Cascade)
|
|
referencedByOutputExecId String?
|
|
ReferencedByOutputExec AgentNodeExecution? @relation("AgentNodeExecutionOutput", fields: [referencedByOutputExecId], references: [id], onDelete: Cascade)
|
|
|
|
agentPresetId String?
|
|
AgentPreset AgentPreset? @relation("AgentPresetsInputData", fields: [agentPresetId], references: [id])
|
|
|
|
// Input and Output pin names are unique for each AgentNodeExecution.
|
|
@@unique([referencedByInputExecId, referencedByOutputExecId, name])
|
|
@@index([referencedByOutputExecId])
|
|
// Composite index for `upsert_execution_input`.
|
|
@@index([name, time])
|
|
@@index([agentPresetId])
|
|
}
|
|
|
|
model AgentNodeExecutionKeyValueData {
|
|
userId String
|
|
key String
|
|
agentNodeExecutionId String
|
|
data Json?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime? @updatedAt
|
|
|
|
@@id([userId, key])
|
|
}
|
|
|
|
enum ReviewStatus {
|
|
WAITING
|
|
APPROVED
|
|
REJECTED
|
|
}
|
|
|
|
// Pending human reviews for Human-in-the-loop blocks
|
|
model PendingHumanReview {
|
|
nodeExecId String @id
|
|
userId String
|
|
graphExecId String
|
|
graphId String
|
|
graphVersion Int
|
|
payload Json // The actual payload data to be reviewed
|
|
instructions String? // Instructions/message for the reviewer
|
|
editable Boolean @default(true) // Whether the reviewer can edit the data
|
|
status ReviewStatus @default(WAITING)
|
|
reviewMessage String? // Optional message from the reviewer
|
|
wasEdited Boolean? // Whether the data was modified during review
|
|
processed Boolean @default(false) // Whether the review result has been processed by the execution engine
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime? @updatedAt
|
|
reviewedAt DateTime?
|
|
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
NodeExecution AgentNodeExecution @relation(fields: [nodeExecId], references: [id], onDelete: Cascade)
|
|
GraphExecution AgentGraphExecution @relation(fields: [graphExecId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([nodeExecId]) // One pending review per node execution
|
|
@@index([userId, status])
|
|
@@index([graphExecId, status])
|
|
}
|
|
|
|
// Webhook that is registered with a provider and propagates to one or more nodes
|
|
model IntegrationWebhook {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime? @updatedAt
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Restrict) // Webhooks must be deregistered before deleting
|
|
|
|
provider String // e.g. 'github'
|
|
credentialsId String // relation to the credentials that the webhook was created with
|
|
webhookType String // e.g. 'repo'
|
|
resource String // e.g. 'Significant-Gravitas/AutoGPT'
|
|
events String[] // e.g. ['created', 'updated']
|
|
config Json
|
|
secret String // crypto string, used to verify payload authenticity
|
|
|
|
providerWebhookId String // Webhook ID assigned by the provider
|
|
|
|
AgentNodes AgentNode[]
|
|
AgentPresets AgentPreset[]
|
|
}
|
|
|
|
model AnalyticsDetails {
|
|
// PK uses gen_random_uuid() to allow the db inserts to happen outside of prisma
|
|
// typical uuid() inserts are handled by prisma
|
|
id String @id @default(dbgenerated("gen_random_uuid()"))
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
// Link to User model
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
// Analytics Categorical data used for filtering (indexable w and w/o userId)
|
|
type String
|
|
|
|
// Analytic Specific Data. We should use a union type here, but prisma doesn't support it.
|
|
data Json?
|
|
|
|
// Indexable field for any count based analytical measures like page order clicking, tutorial step completion, etc.
|
|
dataIndex String?
|
|
|
|
@@index([userId, type])
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////// METRICS TRACKING TABLES ////////////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
model AnalyticsMetrics {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Analytics Categorical data used for filtering (indexable w and w/o userId)
|
|
analyticMetric String
|
|
// Any numeric data that should be counted upon, summed, or otherwise aggregated.
|
|
value Float
|
|
// Any string data that should be used to identify the metric as distinct.
|
|
// ex: '/build' vs '/market'
|
|
dataString String?
|
|
|
|
// Link to User model
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
//////// ACCOUNTING AND CREDIT SYSTEM TABLES //////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
|
|
enum CreditTransactionType {
|
|
TOP_UP
|
|
USAGE
|
|
GRANT
|
|
REFUND
|
|
CARD_CHECK
|
|
}
|
|
|
|
model CreditTransaction {
|
|
transactionKey String @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
|
|
userId String
|
|
User User? @relation(fields: [userId], references: [id], onDelete: NoAction)
|
|
|
|
amount Int
|
|
type CreditTransactionType
|
|
|
|
runningBalance Int?
|
|
|
|
isActive Boolean @default(true)
|
|
metadata Json?
|
|
|
|
@@id(name: "creditTransactionIdentifier", [transactionKey, userId])
|
|
@@index([userId, createdAt])
|
|
}
|
|
|
|
enum CreditRefundRequestStatus {
|
|
PENDING
|
|
APPROVED
|
|
REJECTED
|
|
}
|
|
|
|
model CreditRefundRequest {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
userId String
|
|
transactionKey String
|
|
|
|
amount Int
|
|
reason String
|
|
result String?
|
|
status CreditRefundRequestStatus @default(PENDING)
|
|
|
|
@@index([userId, transactionKey])
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////// Store TABLES ///////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
|
|
model SearchTerms {
|
|
// User ID not being logged as this is anonymous analytics data
|
|
// Not using uuid as we want to minimise table size
|
|
id BigInt @id @default(autoincrement())
|
|
createdDate DateTime
|
|
searchTerm String
|
|
|
|
@@index([createdDate])
|
|
}
|
|
|
|
model Profile {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
// Only 1 of user or group can be set.
|
|
// The user this profile belongs to, if any.
|
|
userId String?
|
|
User User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
name String
|
|
username String @unique
|
|
description String
|
|
|
|
links String[]
|
|
|
|
avatarUrl String?
|
|
|
|
isFeatured Boolean @default(false)
|
|
|
|
LibraryAgents LibraryAgent[]
|
|
|
|
@@index([userId])
|
|
}
|
|
|
|
view Creator {
|
|
username String @unique
|
|
name String
|
|
avatar_url String
|
|
description String
|
|
|
|
top_categories String[]
|
|
links String[]
|
|
|
|
num_agents Int
|
|
agent_rating Float
|
|
agent_runs Int
|
|
is_featured Boolean
|
|
|
|
// Materialized views used (refreshed every 15 minutes via pg_cron):
|
|
// - mv_agent_run_counts - Pre-aggregated agent execution counts by agentGraphId
|
|
// * idx_mv_agent_run_counts (UNIQUE on agentGraphId) - Primary lookup
|
|
// - mv_review_stats - Pre-aggregated review statistics (count, avg rating) by storeListingId
|
|
// * idx_mv_review_stats (UNIQUE on storeListingId) - Primary lookup
|
|
// * idx_mv_review_stats_rating (avg_rating DESC) - Sort by rating performance
|
|
// * idx_mv_review_stats_count (review_count DESC) - Sort by review count performance
|
|
//
|
|
// Query strategy: Uses CTEs to efficiently aggregate creator statistics leveraging materialized views
|
|
}
|
|
|
|
view StoreAgent {
|
|
listing_id String @id
|
|
storeListingVersionId String
|
|
updated_at DateTime
|
|
|
|
slug String
|
|
agent_name String
|
|
agent_video String?
|
|
agent_output_demo String?
|
|
agent_image String[]
|
|
|
|
featured Boolean @default(false)
|
|
creator_username String?
|
|
creator_avatar String?
|
|
sub_heading String
|
|
description String
|
|
categories String[]
|
|
search Unsupported("tsvector")? @default(dbgenerated("''::tsvector"))
|
|
runs Int
|
|
rating Float
|
|
versions String[]
|
|
agentGraphVersions String[]
|
|
agentGraphId String
|
|
is_available Boolean @default(true)
|
|
useForOnboarding Boolean @default(false)
|
|
|
|
// Materialized views used (refreshed every 15 minutes via pg_cron):
|
|
// - mv_agent_run_counts - Pre-aggregated agent execution counts by agentGraphId
|
|
// * idx_mv_agent_run_counts (UNIQUE on agentGraphId) - Primary lookup
|
|
// - mv_review_stats - Pre-aggregated review statistics (count, avg rating) by storeListingId
|
|
// * idx_mv_review_stats (UNIQUE on storeListingId) - Primary lookup
|
|
// * idx_mv_review_stats_rating (avg_rating DESC) - Sort by rating performance
|
|
// * idx_mv_review_stats_count (review_count DESC) - Sort by review count performance
|
|
//
|
|
// Query strategy: Uses CTE for version aggregation and joins with materialized views for performance
|
|
}
|
|
|
|
view StoreSubmission {
|
|
listing_id String @id
|
|
user_id String
|
|
slug String
|
|
name String
|
|
sub_heading String
|
|
description String
|
|
image_urls String[]
|
|
date_submitted DateTime
|
|
status SubmissionStatus
|
|
runs Int
|
|
rating Float
|
|
agent_id String
|
|
agent_version Int
|
|
store_listing_version_id String
|
|
reviewer_id String?
|
|
review_comments String?
|
|
internal_comments String?
|
|
reviewed_at DateTime?
|
|
changes_summary String?
|
|
video_url String?
|
|
categories String[]
|
|
|
|
// Index or unique are not applied to views
|
|
}
|
|
|
|
// Note: This is actually a MATERIALIZED VIEW in the database
|
|
// Refreshed automatically every 15 minutes via pg_cron (with fallback to manual refresh)
|
|
view mv_agent_run_counts {
|
|
agentGraphId String @unique
|
|
run_count Int
|
|
|
|
// Pre-aggregated count of AgentGraphExecution records by agentGraphId
|
|
// Used by StoreAgent and Creator views for performance optimization
|
|
// Unique index created automatically on agentGraphId for fast lookups
|
|
// Refresh uses CONCURRENTLY to avoid blocking reads
|
|
}
|
|
|
|
// Note: This is actually a MATERIALIZED VIEW in the database
|
|
// Refreshed automatically every 15 minutes via pg_cron (with fallback to manual refresh)
|
|
view mv_review_stats {
|
|
storeListingId String @unique
|
|
review_count Int
|
|
avg_rating Float
|
|
|
|
// Pre-aggregated review statistics from StoreListingReview
|
|
// Includes count of reviews and average rating per StoreListing
|
|
// Only includes approved versions (submissionStatus = 'APPROVED') and non-deleted listings
|
|
// Used by StoreAgent view for performance optimization
|
|
// Unique index created automatically on storeListingId for fast lookups
|
|
// Refresh uses CONCURRENTLY to avoid blocking reads
|
|
}
|
|
|
|
model StoreListing {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
isDeleted Boolean @default(false)
|
|
// Whether any version has been approved and is available for display
|
|
hasApprovedVersion Boolean @default(false)
|
|
|
|
// URL-friendly identifier for this agent (moved from StoreListingVersion)
|
|
slug String
|
|
|
|
// Allow this agent to be used during onboarding
|
|
useForOnboarding Boolean @default(false)
|
|
|
|
// The currently active version that should be shown to users
|
|
activeVersionId String? @unique
|
|
ActiveVersion StoreListingVersion? @relation("ActiveVersion", fields: [activeVersionId], references: [id])
|
|
|
|
// The agent link here is only so we can do lookup on agentId
|
|
agentGraphId String
|
|
agentGraphVersion Int
|
|
AgentGraph AgentGraph @relation(fields: [agentGraphId, agentGraphVersion], references: [id, version], onDelete: Cascade)
|
|
|
|
owningUserId String
|
|
OwningUser User @relation(fields: [owningUserId], references: [id])
|
|
|
|
// Relations
|
|
Versions StoreListingVersion[] @relation("ListingVersions")
|
|
|
|
// Unique index on agentId to ensure only one listing per agent, regardless of number of versions the agent has.
|
|
@@unique([agentGraphId])
|
|
@@unique([owningUserId, slug])
|
|
// Used in the view query
|
|
@@index([isDeleted, hasApprovedVersion])
|
|
@@index([agentGraphId, agentGraphVersion])
|
|
}
|
|
|
|
model StoreListingVersion {
|
|
id String @id @default(uuid())
|
|
version Int @default(1)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
// The agent and version to be listed on the store
|
|
agentGraphId String
|
|
agentGraphVersion Int
|
|
AgentGraph AgentGraph @relation(fields: [agentGraphId, agentGraphVersion], references: [id, version])
|
|
|
|
// Content fields
|
|
name String
|
|
subHeading String
|
|
videoUrl String?
|
|
agentOutputDemoUrl String?
|
|
imageUrls String[]
|
|
description String
|
|
instructions String?
|
|
categories String[]
|
|
|
|
isFeatured Boolean @default(false)
|
|
|
|
isDeleted Boolean @default(false)
|
|
// Old versions can be made unavailable by the author if desired
|
|
isAvailable Boolean @default(true)
|
|
|
|
search Unsupported("tsvector")? @default(dbgenerated("''::tsvector"))
|
|
|
|
// Version workflow state
|
|
submissionStatus SubmissionStatus @default(DRAFT)
|
|
submittedAt DateTime?
|
|
|
|
// Relations
|
|
storeListingId String
|
|
StoreListing StoreListing @relation("ListingVersions", fields: [storeListingId], references: [id], onDelete: Cascade)
|
|
|
|
// This version might be the active version for a listing
|
|
ActiveFor StoreListing? @relation("ActiveVersion")
|
|
|
|
// Submission history
|
|
changesSummary String?
|
|
|
|
// Review information
|
|
reviewerId String?
|
|
Reviewer User? @relation(fields: [reviewerId], references: [id])
|
|
internalComments String? // Private notes for admin use only
|
|
reviewComments String? // Comments visible to creator
|
|
reviewedAt DateTime?
|
|
|
|
recommendedScheduleCron String? // cron expression like "0 9 * * *"
|
|
|
|
// Reviews for this specific version
|
|
Reviews StoreListingReview[]
|
|
|
|
@@unique([storeListingId, version])
|
|
@@index([storeListingId, submissionStatus, isAvailable])
|
|
@@index([submissionStatus])
|
|
@@index([reviewerId])
|
|
@@index([agentGraphId, agentGraphVersion]) // Non-unique index for efficient lookups
|
|
}
|
|
|
|
model StoreListingReview {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @default(now()) @updatedAt
|
|
|
|
storeListingVersionId String
|
|
StoreListingVersion StoreListingVersion @relation(fields: [storeListingVersionId], references: [id], onDelete: Cascade)
|
|
|
|
reviewByUserId String
|
|
ReviewByUser User @relation(fields: [reviewByUserId], references: [id])
|
|
|
|
score Int
|
|
comments String?
|
|
|
|
@@unique([storeListingVersionId, reviewByUserId])
|
|
@@index([reviewByUserId])
|
|
}
|
|
|
|
enum SubmissionStatus {
|
|
DRAFT // Being prepared, not yet submitted
|
|
PENDING // Submitted, awaiting review
|
|
APPROVED // Reviewed and approved
|
|
REJECTED // Reviewed and rejected
|
|
}
|
|
|
|
enum APIKeyPermission {
|
|
IDENTITY // Info about the authenticated user
|
|
EXECUTE_GRAPH // Can execute agent graphs
|
|
READ_GRAPH // Can get graph versions and details
|
|
EXECUTE_BLOCK // Can execute individual blocks
|
|
READ_BLOCK // Can get block information
|
|
READ_STORE // Can read store agents and creators
|
|
USE_TOOLS // Can use chat tools via external API
|
|
MANAGE_INTEGRATIONS // Can initiate OAuth flows and complete them
|
|
READ_INTEGRATIONS // Can list credentials and providers
|
|
DELETE_INTEGRATIONS // Can delete credentials
|
|
}
|
|
|
|
model APIKey {
|
|
id String @id @default(uuid())
|
|
name String
|
|
head String // First few chars for identification
|
|
tail String
|
|
hash String @unique
|
|
salt String? // null for legacy unsalted keys
|
|
|
|
status APIKeyStatus @default(ACTIVE)
|
|
permissions APIKeyPermission[]
|
|
|
|
createdAt DateTime @default(now())
|
|
lastUsedAt DateTime?
|
|
revokedAt DateTime?
|
|
|
|
description String?
|
|
|
|
// Relation to user
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([head, name])
|
|
@@index([userId, status])
|
|
}
|
|
|
|
model UserBalance {
|
|
userId String @id
|
|
balance Int @default(0)
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
}
|
|
|
|
enum APIKeyStatus {
|
|
ACTIVE
|
|
REVOKED
|
|
SUSPENDED
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////// OAUTH PROVIDER TABLES //////////////////
|
|
////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////
|
|
|
|
// OAuth2 applications that can access AutoGPT on behalf of users
|
|
model OAuthApplication {
|
|
id String @id @default(uuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Application metadata
|
|
name String
|
|
description String?
|
|
logoUrl String? // URL to app logo stored in GCS
|
|
clientId String @unique
|
|
clientSecret String // Hashed with Scrypt (same as API keys)
|
|
clientSecretSalt String // Salt for Scrypt hashing
|
|
|
|
// OAuth configuration
|
|
redirectUris String[] // Allowed callback URLs
|
|
grantTypes String[] @default(["authorization_code", "refresh_token"])
|
|
scopes APIKeyPermission[] // Which permissions the app can request
|
|
|
|
// Application management
|
|
ownerId String
|
|
Owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
|
|
isActive Boolean @default(true)
|
|
|
|
// Relations
|
|
AuthorizationCodes OAuthAuthorizationCode[]
|
|
AccessTokens OAuthAccessToken[]
|
|
RefreshTokens OAuthRefreshToken[]
|
|
|
|
@@index([clientId])
|
|
@@index([ownerId])
|
|
}
|
|
|
|
// Temporary authorization codes (10 min TTL)
|
|
model OAuthAuthorizationCode {
|
|
id String @id @default(uuid())
|
|
code String @unique
|
|
createdAt DateTime @default(now())
|
|
expiresAt DateTime // Now + 10 minutes
|
|
|
|
applicationId String
|
|
Application OAuthApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
scopes APIKeyPermission[]
|
|
redirectUri String // Must match one from application
|
|
|
|
// PKCE (Proof Key for Code Exchange) support
|
|
codeChallenge String?
|
|
codeChallengeMethod String? // "S256" or "plain"
|
|
|
|
usedAt DateTime? // Set when code is consumed
|
|
|
|
@@index([code])
|
|
@@index([applicationId, userId])
|
|
@@index([expiresAt]) // For cleanup
|
|
}
|
|
|
|
// Access tokens (1 hour TTL)
|
|
model OAuthAccessToken {
|
|
id String @id @default(uuid())
|
|
token String @unique // SHA256 hash of plaintext token
|
|
createdAt DateTime @default(now())
|
|
expiresAt DateTime // Now + 1 hour
|
|
|
|
applicationId String
|
|
Application OAuthApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
scopes APIKeyPermission[]
|
|
|
|
revokedAt DateTime? // Set when token is revoked
|
|
|
|
@@index([token]) // For token lookup
|
|
@@index([userId, applicationId])
|
|
@@index([expiresAt]) // For cleanup
|
|
}
|
|
|
|
// Refresh tokens (30 days TTL)
|
|
model OAuthRefreshToken {
|
|
id String @id @default(uuid())
|
|
token String @unique // SHA256 hash of plaintext token
|
|
createdAt DateTime @default(now())
|
|
expiresAt DateTime // Now + 30 days
|
|
|
|
applicationId String
|
|
Application OAuthApplication @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
|
|
|
userId String
|
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
scopes APIKeyPermission[]
|
|
|
|
revokedAt DateTime? // Set when token is revoked
|
|
|
|
@@index([token]) // For token lookup
|
|
@@index([userId, applicationId])
|
|
@@index([expiresAt]) // For cleanup
|
|
}
|