From 18e169aa519fa7cd77ed03a714e57c0ce6e40702 Mon Sep 17 00:00:00 2001 From: Swifty Date: Fri, 10 Oct 2025 11:33:28 +0200 Subject: [PATCH] feat(platform): Log Marketplace Search Terms (#11092) Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Reinier van der Leer --- .../backend/backend/server/v2/store/db.py | 26 +++++++++++--- .../migration.sql | 11 ++++++ autogpt_platform/backend/schema.prisma | 36 ++++++++++++------- 3 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 autogpt_platform/backend/migrations/20251007084233_log_search_terms/migration.sql diff --git a/autogpt_platform/backend/backend/server/v2/store/db.py b/autogpt_platform/backend/backend/server/v2/store/db.py index 43bd03488e..63a6ae772b 100644 --- a/autogpt_platform/backend/backend/server/v2/store/db.py +++ b/autogpt_platform/backend/backend/server/v2/store/db.py @@ -70,8 +70,7 @@ async def get_store_agents( logger.debug( f"Getting store agents. featured={featured}, creators={creators}, sorted_by={sorted_by}, search={search_query}, category={category}, page={page}" ) - sanitized_query = sanitize_query(search_query) - + search_term = sanitize_query(search_query) where_clause: prisma.types.StoreAgentWhereInput = {"is_available": True} if featured: where_clause["featured"] = featured @@ -80,10 +79,10 @@ async def get_store_agents( if category: where_clause["categories"] = {"has": category} - if sanitized_query: + if search_term: where_clause["OR"] = [ - {"agent_name": {"contains": sanitized_query, "mode": "insensitive"}}, - {"description": {"contains": sanitized_query, "mode": "insensitive"}}, + {"agent_name": {"contains": search_term, "mode": "insensitive"}}, + {"description": {"contains": search_term, "mode": "insensitive"}}, ] order_by = [] @@ -145,6 +144,23 @@ async def get_store_agents( raise backend.server.v2.store.exceptions.DatabaseError( "Failed to fetch store agents" ) from e + finally: + if search_term: + await log_search_term(search_query=search_term) + + +async def log_search_term(search_query: str): + """Log a search term to the database""" + + # Anonymize the data by preventing correlation with other logs + date = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0) + try: + await prisma.models.SearchTerms.prisma().create( + data={"searchTerm": search_query, "createdDate": date} + ) + except Exception as e: + # Fail silently here so that logging search terms doesn't break the app + logger.error(f"Error logging search term: {e}") async def get_store_agent_details( diff --git a/autogpt_platform/backend/migrations/20251007084233_log_search_terms/migration.sql b/autogpt_platform/backend/migrations/20251007084233_log_search_terms/migration.sql new file mode 100644 index 0000000000..1534fcabeb --- /dev/null +++ b/autogpt_platform/backend/migrations/20251007084233_log_search_terms/migration.sql @@ -0,0 +1,11 @@ +-- CreateTable +CREATE TABLE "SearchTerms" ( + "id" BIGSERIAL NOT NULL, + "createdDate" TIMESTAMP(3) NOT NULL, + "searchTerm" TEXT NOT NULL, + + CONSTRAINT "SearchTerms_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "SearchTerms_createdDate_idx" ON "SearchTerms"("createdDate"); diff --git a/autogpt_platform/backend/schema.prisma b/autogpt_platform/backend/schema.prisma index 0654ee93ec..4d1b84ba65 100644 --- a/autogpt_platform/backend/schema.prisma +++ b/autogpt_platform/backend/schema.prisma @@ -118,9 +118,9 @@ model AgentGraph { createdAt DateTime @default(now()) updatedAt DateTime? @updatedAt - name String? - description String? - instructions String? + name String? + description String? + instructions String? recommendedScheduleCron String? isActive Boolean @default(true) @@ -382,9 +382,9 @@ model AgentGraphExecution { stats Json? // Sharing fields - isShared Boolean @default(false) - shareToken String? @unique - sharedAt DateTime? + isShared Boolean @default(false) + shareToken String? @unique + sharedAt DateTime? @@index([agentGraphId, agentGraphVersion]) @@index([userId, isDeleted, createdAt]) @@ -545,7 +545,7 @@ model CreditTransaction { createdAt DateTime @default(now()) userId String - User User? @relation(fields: [userId], references: [id], onDelete: NoAction) + User User? @relation(fields: [userId], references: [id], onDelete: NoAction) amount Int type CreditTransactionType @@ -587,6 +587,16 @@ model CreditRefundRequest { //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// +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()) @@ -769,13 +779,13 @@ model StoreListingVersion { AgentGraph AgentGraph @relation(fields: [agentGraphId, agentGraphVersion], references: [id, version]) // Content fields - name String - subHeading String - videoUrl String? - imageUrls String[] - description String + name String + subHeading String + videoUrl String? + imageUrls String[] + description String instructions String? - categories String[] + categories String[] isFeatured Boolean @default(false)