feat: marketplace analytics (#7998)
parent
126d070396
commit
3c12a398ae
|
@ -11,7 +11,10 @@ import {
|
||||||
ChevronUp,
|
ChevronUp,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
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 dynamic from "next/dynamic";
|
||||||
import { Node, Edge } from "@xyflow/react";
|
import { Node, Edge } from "@xyflow/react";
|
||||||
import MarketplaceAPI from "@/lib/marketplace-api";
|
import MarketplaceAPI from "@/lib/marketplace-api";
|
||||||
|
@ -32,6 +35,7 @@ const Background = dynamic(
|
||||||
|
|
||||||
import "@xyflow/react/dist/style.css";
|
import "@xyflow/react/dist/style.css";
|
||||||
import { beautifyString } from "@/lib/utils";
|
import { beautifyString } from "@/lib/utils";
|
||||||
|
import { makeAnalyticsEvent } from "./actions";
|
||||||
|
|
||||||
function convertGraphToReactFlow(graph: any): { nodes: Node[]; edges: Edge[] } {
|
function convertGraphToReactFlow(graph: any): { nodes: Node[]; edges: Edge[] } {
|
||||||
const nodes: Node[] = graph.nodes.map((node: any) => {
|
const nodes: Node[] = graph.nodes.map((node: any) => {
|
||||||
|
@ -96,8 +100,16 @@ async function installGraph(id: string): Promise<void> {
|
||||||
nodes: agent.graph.nodes,
|
nodes: agent.graph.nodes,
|
||||||
links: agent.graph.links,
|
links: agent.graph.links,
|
||||||
};
|
};
|
||||||
await serverAPI.createTemplate(data);
|
const result = await serverAPI.createTemplate(data);
|
||||||
console.log(`Agent installed successfully`);
|
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) {
|
} catch (error) {
|
||||||
console.error(`Error installing agent:`, error);
|
console.error(`Error installing agent:`, error);
|
||||||
throw 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,
|
AgentWithRank,
|
||||||
FeaturedAgentResponse,
|
FeaturedAgentResponse,
|
||||||
UniqueCategoriesResponse,
|
UniqueCategoriesResponse,
|
||||||
|
AnalyticsEvent,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
export default class MarketplaceAPI {
|
export default class MarketplaceAPI {
|
||||||
|
@ -193,6 +194,13 @@ export default class MarketplaceAPI {
|
||||||
return this._get("/admin/categories");
|
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) {
|
private async _get(path: string) {
|
||||||
return this._request("GET", path);
|
return this._request("GET", path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,3 +76,34 @@ export type AgentResponse = Agent;
|
||||||
export type UniqueCategoriesResponse = {
|
export type UniqueCategoriesResponse = {
|
||||||
unique_categories: string[];
|
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.agents
|
||||||
import market.routes.search
|
import market.routes.search
|
||||||
import market.routes.submissions
|
import market.routes.submissions
|
||||||
|
import market.routes.analytics
|
||||||
|
|
||||||
dotenv.load_dotenv()
|
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.search.router, tags=["search"])
|
||||||
app.include_router(market.routes.submissions.router, tags=["submissions"])
|
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.admin.router, prefix="/admin", tags=["admin"])
|
||||||
|
app.include_router(
|
||||||
|
market.routes.analytics.router, prefix="/analytics", tags=["analytics"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/health")
|
@app.get("/health")
|
||||||
|
|
|
@ -634,10 +634,30 @@ FROM (
|
||||||
model=market.model.CategoriesResponse,
|
model=market.model.CategoriesResponse,
|
||||||
)
|
)
|
||||||
if not categories:
|
if not categories:
|
||||||
raise AgentQueryError("No categories found")
|
return market.model.CategoriesResponse(unique_categories=[])
|
||||||
|
|
||||||
return categories
|
return categories
|
||||||
except prisma.errors.PrismaError as e:
|
except prisma.errors.PrismaError as e:
|
||||||
raise AgentQueryError(f"Database query failed: {str(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:
|
except Exception as e:
|
||||||
raise AgentQueryError(f"Unexpected error occurred: {str(e)}")
|
raise AgentQueryError(f"Unexpected error occurred: {str(e)}")
|
||||||
|
|
|
@ -4,6 +4,35 @@ import typing
|
||||||
import prisma.enums
|
import prisma.enums
|
||||||
import pydantic
|
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):
|
class AddAgentRequest(pydantic.BaseModel):
|
||||||
graph: dict[str, typing.Any]
|
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
|
graph Json
|
||||||
AnalyticsTracker AnalyticsTracker[]
|
AnalyticsTracker AnalyticsTracker[]
|
||||||
FeaturedAgent FeaturedAgent?
|
FeaturedAgent FeaturedAgent?
|
||||||
|
InstallTracker InstallTracker[]
|
||||||
|
|
||||||
@@id(name: "graphVersionId", [id, version])
|
@@id(name: "graphVersionId", [id, version])
|
||||||
}
|
}
|
||||||
|
@ -52,6 +53,22 @@ model AnalyticsTracker {
|
||||||
downloads Int
|
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 {
|
model FeaturedAgent {
|
||||||
id String @id @unique @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
id String @id @unique @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||||
agentId String @unique @db.Uuid
|
agentId String @unique @db.Uuid
|
||||||
|
|
Loading…
Reference in New Issue