feat: marketplace analytics (#7998)

pull/8015/head^2
Nicholas Tindle 2024-09-06 14:22:24 -05:00 committed by GitHub
parent 126d070396
commit 3c12a398ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 179 additions and 4 deletions

View File

@ -11,7 +11,10 @@ import {
ChevronUp,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { AgentDetailResponse } from "@/lib/marketplace-api";
import {
AgentDetailResponse,
InstallationLocation,
} from "@/lib/marketplace-api";
import dynamic from "next/dynamic";
import { Node, Edge } from "@xyflow/react";
import MarketplaceAPI from "@/lib/marketplace-api";
@ -32,6 +35,7 @@ const Background = dynamic(
import "@xyflow/react/dist/style.css";
import { beautifyString } from "@/lib/utils";
import { makeAnalyticsEvent } from "./actions";
function convertGraphToReactFlow(graph: any): { nodes: Node[]; edges: Edge[] } {
const nodes: Node[] = graph.nodes.map((node: any) => {
@ -96,8 +100,16 @@ async function installGraph(id: string): Promise<void> {
nodes: agent.graph.nodes,
links: agent.graph.links,
};
await serverAPI.createTemplate(data);
console.log(`Agent installed successfully`);
const result = await serverAPI.createTemplate(data);
makeAnalyticsEvent({
event_name: "agent_installed_from_marketplace",
event_data: {
marketplace_agent_id: id,
installed_agent_id: result.id,
installation_location: InstallationLocation.CLOUD,
},
});
console.log(`Agent installed successfully`, result);
} catch (error) {
console.error(`Error installing agent:`, error);
throw error;

View File

@ -0,0 +1,9 @@
"use server";
import MarketplaceAPI, { AnalyticsEvent } from "@/lib/marketplace-api";
export async function makeAnalyticsEvent(event: AnalyticsEvent) {
const apiUrl = process.env.AGPT_SERVER_API_URL;
const api = new MarketplaceAPI();
await api.makeAnalyticsEvent(event);
}

View File

@ -8,6 +8,7 @@ import {
AgentWithRank,
FeaturedAgentResponse,
UniqueCategoriesResponse,
AnalyticsEvent,
} from "./types";
export default class MarketplaceAPI {
@ -193,6 +194,13 @@ export default class MarketplaceAPI {
return this._get("/admin/categories");
}
async makeAnalyticsEvent(event: AnalyticsEvent) {
if (event.event_name === "agent_installed_from_marketplace") {
return this._post("/analytics/agent-installed", event.event_data);
}
throw new Error("Invalid event name");
}
private async _get(path: string) {
return this._request("GET", path);
}

View File

@ -76,3 +76,34 @@ export type AgentResponse = Agent;
export type UniqueCategoriesResponse = {
unique_categories: string[];
};
export enum InstallationLocation {
LOCAL = "local",
CLOUD = "cloud",
}
export type AgentInstalledFromMarketplaceEventData = {
marketplace_agent_id: string;
installed_agent_id: string;
installation_location: InstallationLocation;
};
export type AgentInstalledFromTemplateEventData = {
template_id: string;
installed_agent_id: string;
installation_location: InstallationLocation;
};
export interface AgentInstalledFromMarketplaceEvent {
event_name: "agent_installed_from_marketplace";
event_data: AgentInstalledFromMarketplaceEventData;
}
export interface AgentInstalledFromTemplateEvent {
event_name: "agent_installed_from_template";
event_data: AgentInstalledFromTemplateEventData;
}
export type AnalyticsEvent =
| AgentInstalledFromMarketplaceEvent
| AgentInstalledFromTemplateEvent;

View File

@ -18,6 +18,7 @@ import market.routes.admin
import market.routes.agents
import market.routes.search
import market.routes.submissions
import market.routes.analytics
dotenv.load_dotenv()
@ -75,6 +76,9 @@ app.include_router(market.routes.agents.router, tags=["agents"])
app.include_router(market.routes.search.router, tags=["search"])
app.include_router(market.routes.submissions.router, tags=["submissions"])
app.include_router(market.routes.admin.router, prefix="/admin", tags=["admin"])
app.include_router(
market.routes.analytics.router, prefix="/analytics", tags=["analytics"]
)
@app.get("/health")

View File

@ -634,10 +634,30 @@ FROM (
model=market.model.CategoriesResponse,
)
if not categories:
raise AgentQueryError("No categories found")
return market.model.CategoriesResponse(unique_categories=[])
return categories
except prisma.errors.PrismaError as e:
raise AgentQueryError(f"Database query failed: {str(e)}")
except Exception as e:
# raise AgentQueryError(f"Unexpected error occurred: {str(e)}")
return market.model.CategoriesResponse(unique_categories=[])
async def create_agent_installed_event(
event_data: market.model.AgentInstalledFromMarketplaceEventData,
):
try:
await prisma.models.InstallTracker.prisma().create(
data={
"installedAgentId": event_data.installed_agent_id,
"marketplaceAgentId": event_data.marketplace_agent_id,
"installationLocation": prisma.enums.InstallationLocation(
event_data.installation_location.name
),
}
)
except prisma.errors.PrismaError as e:
raise AgentQueryError(f"Database query failed: {str(e)}")
except Exception as e:
raise AgentQueryError(f"Unexpected error occurred: {str(e)}")

View File

@ -4,6 +4,35 @@ import typing
import prisma.enums
import pydantic
from enum import Enum
from typing import Literal, Union
class InstallationLocation(str, Enum):
LOCAL = "local"
CLOUD = "cloud"
class AgentInstalledFromMarketplaceEventData(pydantic.BaseModel):
marketplace_agent_id: str
installed_agent_id: str
installation_location: InstallationLocation
class AgentInstalledFromTemplateEventData(pydantic.BaseModel):
template_id: str
installed_agent_id: str
installation_location: InstallationLocation
class AgentInstalledFromMarketplaceEvent(pydantic.BaseModel):
event_name: Literal["agent_installed_from_marketplace"]
event_data: AgentInstalledFromMarketplaceEventData
class AgentInstalledFromTemplateEvent(pydantic.BaseModel):
event_name: Literal["agent_installed_from_template"]
event_data: AgentInstalledFromTemplateEventData
AnalyticsEvent = Union[AgentInstalledFromMarketplaceEvent, AgentInstalledFromTemplateEvent]
class AnalyticsRequest(pydantic.BaseModel):
event: AnalyticsEvent
class AddAgentRequest(pydantic.BaseModel):
graph: dict[str, typing.Any]

View File

@ -0,0 +1,26 @@
import fastapi
import market.db
import market.model
router = fastapi.APIRouter()
@router.post("/agent-installed")
async def agent_installed_endpoint(
event_data: market.model.AgentInstalledFromMarketplaceEventData,
):
"""
Endpoint to track agent installation events from the marketplace.
Args:
event_data (market.model.AgentInstalledFromMarketplaceEventData): The event data.
"""
try:
await market.db.create_agent_installed_event(event_data)
except market.db.AgentQueryError as e:
raise fastapi.HTTPException(status_code=400, detail=str(e))
except Exception as e:
raise fastapi.HTTPException(
status_code=500, detail=f"An unexpected error occurred: {e}"
)

View File

@ -0,0 +1,19 @@
-- CreateEnum
CREATE TYPE "InstallationLocation" AS ENUM ('LOCAL', 'CLOUD');
-- CreateTable
CREATE TABLE "InstallTracker" (
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
"marketplaceAgentId" UUID NOT NULL,
"installedAgentId" UUID NOT NULL,
"installationLocation" "InstallationLocation" NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "InstallTracker_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "InstallTracker_marketplaceAgentId_installedAgentId_key" ON "InstallTracker"("marketplaceAgentId", "installedAgentId");
-- AddForeignKey
ALTER TABLE "InstallTracker" ADD CONSTRAINT "InstallTracker_marketplaceAgentId_fkey" FOREIGN KEY ("marketplaceAgentId") REFERENCES "Agents"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -40,6 +40,7 @@ model Agents {
graph Json
AnalyticsTracker AnalyticsTracker[]
FeaturedAgent FeaturedAgent?
InstallTracker InstallTracker[]
@@id(name: "graphVersionId", [id, version])
}
@ -52,6 +53,22 @@ model AnalyticsTracker {
downloads Int
}
enum InstallationLocation {
LOCAL
CLOUD
}
model InstallTracker {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
marketplaceAgentId String @db.Uuid
marketplaceAgent Agents @relation(fields: [marketplaceAgentId], references: [id])
installedAgentId String @db.Uuid
installationLocation InstallationLocation
createdAt DateTime @default(now())
@@unique([marketplaceAgentId, installedAgentId])
}
model FeaturedAgent {
id String @id @unique @default(dbgenerated("gen_random_uuid()")) @db.Uuid
agentId String @unique @db.Uuid