feat: marketplace analytics (#7998)
parent
126d070396
commit
3c12a398ae
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)}")
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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}"
|
||||
)
|
|
@ -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;
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue