diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/tutorial.ts b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/tutorial.ts
index 1d862f2811..7adcfd9c1e 100644
--- a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/tutorial.ts
+++ b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/tutorial.ts
@@ -1,7 +1,7 @@
import Shepherd from "shepherd.js";
import "shepherd.js/dist/css/shepherd.css";
-import { sendGAEvent } from "@/services/analytics/google-analytics";
import { Key, storage } from "@/services/storage/local-storage";
+import { analytics } from "@/services/analytics";
export const startTutorial = (
emptyNodeList: (forceEmpty: boolean) => boolean,
@@ -555,7 +555,7 @@ export const startTutorial = (
"use client";
console.debug("sendTutorialStep");
- sendGAEvent("event", "tutorial_step_shown", { value: step.id });
+ analytics.sendGAEvent("event", "tutorial_step_shown", { value: step.id });
});
}
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/AgentRunsView/components/RunAgentModal/useAgentRunModal.ts b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/AgentRunsView/components/RunAgentModal/useAgentRunModal.ts
index 65fa2186fe..2e8fc02d97 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/AgentRunsView/components/RunAgentModal/useAgentRunModal.ts
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/AgentRunsView/components/RunAgentModal/useAgentRunModal.ts
@@ -17,6 +17,7 @@ import { GraphExecutionJobInfo } from "@/app/api/__generated__/models/graphExecu
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
import { useGetV1GetUserTimezone } from "@/app/api/__generated__/endpoints/auth/auth";
import { useOnboarding } from "@/providers/onboarding/onboarding-provider";
+import { analytics } from "@/services/analytics";
export type RunVariant =
| "manual"
@@ -78,6 +79,10 @@ export function useAgentRunModal(
agent.graph_id,
).queryKey,
});
+ analytics.sendDatafastEvent("run_agent", {
+ name: agent.name,
+ id: agent.graph_id,
+ });
setIsOpen(false);
}
},
@@ -105,6 +110,11 @@ export function useAgentRunModal(
agent.graph_id,
),
});
+ analytics.sendDatafastEvent("schedule_agent", {
+ name: agent.name,
+ id: agent.graph_id,
+ cronExpression: cronExpression,
+ });
setIsOpen(false);
}
},
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/agent-run-details-view.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/agent-run-details-view.tsx
index a5aebad54c..2fa24471a8 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/agent-run-details-view.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/agent-run-details-view.tsx
@@ -37,6 +37,7 @@ import { useToastOnFail } from "@/components/molecules/Toast/use-toast";
import { AgentRunStatus, agentRunStatusMap } from "./agent-run-status-chip";
import useCredits from "@/hooks/useCredits";
import { AgentRunOutputView } from "./agent-run-output-view";
+import { analytics } from "@/services/analytics";
export function AgentRunDetailsView({
agent,
@@ -131,7 +132,13 @@ export function AgentRunDetailsView({
run.inputs!,
run.credential_inputs!,
)
- .then(({ id }) => onRun(id))
+ .then(({ id }) => {
+ analytics.sendDatafastEvent("run_agent", {
+ name: graph.name,
+ id: graph.id,
+ });
+ onRun(id);
+ })
.catch(toastOnFail("execute agent preset"));
}
@@ -142,7 +149,13 @@ export function AgentRunDetailsView({
run.inputs!,
run.credential_inputs!,
)
- .then(({ id }) => onRun(id))
+ .then(({ id }) => {
+ analytics.sendDatafastEvent("run_agent", {
+ name: graph.name,
+ id: graph.id,
+ });
+ onRun(id);
+ })
.catch(toastOnFail("execute agent"));
}, [api, graph, run, onRun, toastOnFail]);
diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/agent-run-draft-view.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/agent-run-draft-view.tsx
index 87ceaf54bb..755b0ba5e1 100644
--- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/agent-run-draft-view.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/agent-run-draft-view.tsx
@@ -43,6 +43,7 @@ import {
import { AgentStatus, AgentStatusChip } from "./agent-status-chip";
import { useOnboarding } from "@/providers/onboarding/onboarding-provider";
+import { analytics } from "@/services/analytics";
export function AgentRunDraftView({
graph,
@@ -197,6 +198,12 @@ export function AgentRunDraftView({
}
// Mark run agent onboarding step as completed
completeOnboardingStep("MARKETPLACE_RUN_AGENT");
+
+ analytics.sendDatafastEvent("run_agent", {
+ name: graph.name,
+ id: graph.id,
+ });
+
if (runCount > 0) {
completeOnboardingStep("RE_RUN_AGENT");
}
@@ -373,6 +380,12 @@ export function AgentRunDraftView({
})
.catch(toastOnFail("set up agent run schedule"));
+ analytics.sendDatafastEvent("schedule_agent", {
+ name: graph.name,
+ id: graph.id,
+ cronExpression: cronExpression,
+ });
+
if (schedule && onCreateSchedule) onCreateSchedule(schedule);
},
[api, graph, inputValues, inputCredentials, onCreateSchedule, toastOnFail],
diff --git a/autogpt_platform/frontend/src/app/(platform)/marketplace/components/AgentInfo/AgentInfo.tsx b/autogpt_platform/frontend/src/app/(platform)/marketplace/components/AgentInfo/AgentInfo.tsx
index cd0572e836..836882c918 100644
--- a/autogpt_platform/frontend/src/app/(platform)/marketplace/components/AgentInfo/AgentInfo.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/marketplace/components/AgentInfo/AgentInfo.tsx
@@ -6,10 +6,10 @@ import Link from "next/link";
import { User } from "@supabase/supabase-js";
import { cn } from "@/lib/utils";
import { useAgentInfo } from "./useAgentInfo";
-import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
interface AgentInfoProps {
user: User | null;
+ agentId: string;
name: string;
creator: string;
shortDescription: string;
@@ -20,11 +20,12 @@ interface AgentInfoProps {
lastUpdated: string;
version: string;
storeListingVersionId: string;
- libraryAgent: LibraryAgent | undefined;
+ isAgentAddedToLibrary: boolean;
}
export const AgentInfo = ({
user,
+ agentId,
name,
creator,
shortDescription,
@@ -35,7 +36,7 @@ export const AgentInfo = ({
lastUpdated,
version,
storeListingVersionId,
- libraryAgent,
+ isAgentAddedToLibrary,
}: AgentInfoProps) => {
const {
handleDownload,
@@ -82,11 +83,15 @@ export const AgentInfo = ({
"transition-colors duration-200 hover:bg-violet-500 disabled:bg-zinc-400",
)}
data-testid={"agent-add-library-button"}
- onClick={handleLibraryAction}
disabled={isAddingAgentToLibrary}
+ onClick={() =>
+ handleLibraryAction({
+ isAddingAgentFirstTime: !isAgentAddedToLibrary,
+ })
+ }
>
- {libraryAgent ? "See runs" : "Add to library"}
+ {isAgentAddedToLibrary ? "See runs" : "Add to library"}
)}
@@ -96,7 +101,7 @@ export const AgentInfo = ({
"transition-colors duration-200 hover:bg-zinc-200/70 disabled:bg-zinc-200/40",
)}
data-testid={"agent-download-button"}
- onClick={handleDownload}
+ onClick={() => handleDownload(agentId, name)}
disabled={isDownloadingAgent}
>
diff --git a/autogpt_platform/frontend/src/app/(platform)/marketplace/components/AgentInfo/useAgentInfo.ts b/autogpt_platform/frontend/src/app/(platform)/marketplace/components/AgentInfo/useAgentInfo.ts
index 2b5a4029d3..3a3988575f 100644
--- a/autogpt_platform/frontend/src/app/(platform)/marketplace/components/AgentInfo/useAgentInfo.ts
+++ b/autogpt_platform/frontend/src/app/(platform)/marketplace/components/AgentInfo/useAgentInfo.ts
@@ -1,10 +1,11 @@
import { usePostV2AddMarketplaceAgent } from "@/app/api/__generated__/endpoints/library/library";
-import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
import { useToast } from "@/components/molecules/Toast/use-toast";
import { useRouter } from "next/navigation";
import * as Sentry from "@sentry/nextjs";
import { useGetV2DownloadAgentFile } from "@/app/api/__generated__/endpoints/store/store";
import { useOnboarding } from "@/providers/onboarding/onboarding-provider";
+import { analytics } from "@/services/analytics";
+import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
interface UseAgentInfoProps {
storeListingVersionId: string;
@@ -16,29 +17,9 @@ export const useAgentInfo = ({ storeListingVersionId }: UseAgentInfoProps) => {
const { completeStep } = useOnboarding();
const {
- mutate: addMarketplaceAgentToLibrary,
+ mutateAsync: addMarketplaceAgentToLibrary,
isPending: isAddingAgentToLibrary,
- } = usePostV2AddMarketplaceAgent({
- mutation: {
- onSuccess: ({ data }) => {
- completeStep("MARKETPLACE_ADD_AGENT");
- router.push(`/library/agents/${(data as LibraryAgent).id}`);
- toast({
- title: "Agent Added",
- description: "Redirecting to your library...",
- duration: 2000,
- });
- },
- onError: (error) => {
- Sentry.captureException(error);
- toast({
- title: "Error",
- description: "Failed to add agent to library. Please try again.",
- variant: "destructive",
- });
- },
- },
- });
+ } = usePostV2AddMarketplaceAgent();
const { refetch: downloadAgent, isFetching: isDownloadingAgent } =
useGetV2DownloadAgentFile(storeListingVersionId, {
@@ -50,13 +31,46 @@ export const useAgentInfo = ({ storeListingVersionId }: UseAgentInfoProps) => {
},
});
- const handleLibraryAction = async () => {
- addMarketplaceAgentToLibrary({
- data: { store_listing_version_id: storeListingVersionId },
- });
+ const handleLibraryAction = async ({
+ isAddingAgentFirstTime,
+ }: {
+ isAddingAgentFirstTime: boolean;
+ }) => {
+ try {
+ const { data: response } = await addMarketplaceAgentToLibrary({
+ data: { store_listing_version_id: storeListingVersionId },
+ });
+
+ const data = response as LibraryAgent;
+
+ if (isAddingAgentFirstTime) {
+ completeStep("MARKETPLACE_ADD_AGENT");
+
+ analytics.sendDatafastEvent("add_to_library", {
+ name: data.name,
+ id: data.id,
+ });
+ }
+
+ router.push(`/library/agents/${data.id}`);
+
+ toast({
+ title: "Agent Added",
+ description: "Redirecting to your library...",
+ duration: 2000,
+ });
+ } catch (error) {
+ Sentry.captureException(error);
+
+ toast({
+ title: "Error",
+ description: "Failed to add agent to library. Please try again.",
+ variant: "destructive",
+ });
+ }
};
- const handleDownload = async () => {
+ const handleDownload = async (agentId: string, agentName: string) => {
try {
const { data: file } = await downloadAgent();
@@ -74,6 +88,11 @@ export const useAgentInfo = ({ storeListingVersionId }: UseAgentInfoProps) => {
window.URL.revokeObjectURL(url);
+ analytics.sendDatafastEvent("download_agent", {
+ name: agentName,
+ id: agentId,
+ });
+
toast({
title: "Download Complete",
description: "Your agent has been successfully downloaded.",
diff --git a/autogpt_platform/frontend/src/app/(platform)/marketplace/components/MainAgentPage/MainAgentPage.tsx b/autogpt_platform/frontend/src/app/(platform)/marketplace/components/MainAgentPage/MainAgentPage.tsx
index b6090b79a0..67eb25b8b3 100644
--- a/autogpt_platform/frontend/src/app/(platform)/marketplace/components/MainAgentPage/MainAgentPage.tsx
+++ b/autogpt_platform/frontend/src/app/(platform)/marketplace/components/MainAgentPage/MainAgentPage.tsx
@@ -82,6 +82,7 @@ export const MainAgentPage = ({ params }: MainAgentPageProps) => {
{
lastUpdated={agent.last_updated.toISOString()}
version={agent.versions[agent.versions.length - 1]}
storeListingVersionId={agent.store_listing_version_id}
- libraryAgent={libraryAgent}
+ isAgentAddedToLibrary={Boolean(libraryAgent)}
/>
) {
const headersList = await headers();
const host = headersList.get("host") || "";
- const withAnalytics = environment.areAnalyticsEnabled(host);
return (
-
- {withAnalytics ? (
-
- ) : null}
= ({
- user,
- name,
- creator,
- shortDescription,
- longDescription,
- rating,
- runs,
- categories,
- lastUpdated,
- version,
- storeListingVersionId,
- libraryAgent,
-}) => {
- const router = useRouter();
- const api = useMemo(() => new BackendAPI(), []);
- const { toast } = useToast();
- const { completeStep } = useOnboarding();
- const [adding, setAdding] = useState(false);
- const [downloading, setDownloading] = useState(false);
-
- const libraryAction = useCallback(async () => {
- setAdding(true);
- if (libraryAgent) {
- toast({
- description: "Redirecting to your library...",
- duration: 2000,
- });
- // Redirect to the library agent page
- router.push(`/library/agents/${libraryAgent.id}`);
- return;
- }
- try {
- const newLibraryAgent = await api.addMarketplaceAgentToLibrary(
- storeListingVersionId,
- );
- completeStep("MARKETPLACE_ADD_AGENT");
- router.push(`/library/agents/${newLibraryAgent.id}`);
- toast({
- title: "Agent Added",
- description: "Redirecting to your library...",
- duration: 2000,
- });
- } catch (error) {
- console.error("Failed to add agent to library:", error);
- toast({
- title: "Error",
- description: "Failed to add agent to library. Please try again.",
- variant: "destructive",
- });
- }
- }, [toast, api, storeListingVersionId, completeStep, router]);
-
- const handleDownload = useCallback(async () => {
- const downloadAgent = async (): Promise => {
- setDownloading(true);
- try {
- const file = await api.downloadStoreAgent(storeListingVersionId);
-
- // Similar to Marketplace v1
- const jsonData = JSON.stringify(file, null, 2);
- // Create a Blob from the file content
- const blob = new Blob([jsonData], { type: "application/json" });
-
- // Create a temporary URL for the Blob
- const url = window.URL.createObjectURL(blob);
-
- // Create a temporary anchor element
- const a = document.createElement("a");
- a.href = url;
- a.download = `agent_${storeListingVersionId}.json`; // Set the filename
-
- // Append the anchor to the body, click it, and remove it
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
-
- // Revoke the temporary URL
- window.URL.revokeObjectURL(url);
-
- toast({
- title: "Download Complete",
- description: "Your agent has been successfully downloaded.",
- });
- } catch (error) {
- console.error(`Error downloading agent:`, error);
- toast({
- title: "Error",
- description: "Failed to download agent. Please try again.",
- variant: "destructive",
- });
- }
- };
- await downloadAgent();
- setDownloading(false);
- }, [setDownloading, api, storeListingVersionId, toast]);
-
- return (
-
- {/* Title */}
-
- {name}
-
-
- {/* Creator */}
-
-
- by
-
-
- {creator}
-
-
-
- {/* Short Description */}
-
- {shortDescription}
-
-
- {/* Buttons */}
-
- {user && (
-
- )}
-
-
-
- {/* Rating and Runs */}
-
-
-
- {rating.toFixed(1)}
-
-
{StarRatingIcons(rating)}
-
-
- {runs.toLocaleString()} runs
-
-
-
- {/* Separator */}
-
-
- {/* Description Section */}
-
-
- Description
-
-
- {longDescription}
-
-
-
- {/* Categories */}
-
-
- Categories
-
-
- {categories.map((category, index) => (
-
- {category}
-
- ))}
-
-
-
- {/* Version History */}
-
-
- Version history
-
-
- Last updated {lastUpdated}
-
-
- Version {version}
-
-
-
- );
-};
diff --git a/autogpt_platform/frontend/src/services/analytics/google-analytics.tsx b/autogpt_platform/frontend/src/services/analytics/google-analytics.tsx
deleted file mode 100644
index 58d5f996b5..0000000000
--- a/autogpt_platform/frontend/src/services/analytics/google-analytics.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * Modified copy of ga.tsx from @next/third-parties/google, with modified gtag.js source URL.
- * Original source file: https://github.com/vercel/next.js/blob/b304b45e3a6e3e79338568d76e28805e77c03ec9/packages/third-parties/src/google/ga.tsx
- */
-
-"use client";
-
-import type { GAParams } from "@/types/google";
-import Script from "next/script";
-import { useEffect } from "react";
-
-let currDataLayerName: string | undefined = undefined;
-
-export function GoogleAnalytics(props: GAParams) {
- const { gaId, debugMode, dataLayerName = "dataLayer", nonce } = props;
-
- if (currDataLayerName === undefined) {
- currDataLayerName = dataLayerName;
- }
-
- useEffect(() => {
- // Feature usage signal (same as original implementation)
- performance.mark("mark_feature_usage", {
- detail: {
- feature: "custom-ga",
- },
- });
- }, []);
-
- return (
- <>
-
-
- >
- );
-}
-
-export function sendGAEvent(...args: any[]) {
- if (currDataLayerName === undefined) {
- console.warn(`Custom GA: GA has not been initialized`);
- return;
- }
-
- const dataLayer = (window as any)[currDataLayerName];
- if (dataLayer) {
- dataLayer.push(...args);
- } else {
- console.warn(`Custom GA: dataLayer ${currDataLayerName} does not exist`);
- }
-}
diff --git a/autogpt_platform/frontend/src/services/analytics/index.tsx b/autogpt_platform/frontend/src/services/analytics/index.tsx
new file mode 100644
index 0000000000..6ea24a1122
--- /dev/null
+++ b/autogpt_platform/frontend/src/services/analytics/index.tsx
@@ -0,0 +1,114 @@
+/**
+ * Modified copy of ga.tsx from @next/third-parties/google, with modified gtag.js source URL.
+ * Original source file: https://github.com/vercel/next.js/blob/b304b45e3a6e3e79338568d76e28805e77c03ec9/packages/third-parties/src/google/ga.tsx
+ */
+
+"use client";
+
+import type { GAParams } from "@/types/google";
+import Script from "next/script";
+import { useEffect } from "react";
+import { environment } from "../environment";
+
+declare global {
+ interface Window {
+ datafast: (name: string, metadata: Record) => void;
+ [key: string]: unknown[] | ((...args: unknown[]) => void) | unknown;
+ }
+}
+
+let currDataLayerName: string | undefined = undefined;
+
+type SetupProps = {
+ ga: GAParams;
+ host: string;
+};
+
+export function SetupAnalytics(props: SetupProps) {
+ const { ga, host } = props;
+ const { gaId, debugMode, dataLayerName = "dataLayer", nonce } = ga;
+ const isProductionDomain = host.includes("platform.agpt.co");
+
+ // Datafa.st journey analytics only on production
+ const dataFastEnabled = isProductionDomain;
+ // We collect analytics too for open source developers running the platform locally
+ const googleAnalyticsEnabled = environment.isLocal() || isProductionDomain;
+
+ if (currDataLayerName === undefined) {
+ currDataLayerName = dataLayerName;
+ }
+
+ useEffect(() => {
+ if (!googleAnalyticsEnabled) return;
+
+ // Google Analytics: feature usage signal (same as original implementation)
+ performance.mark("mark_feature_usage", {
+ detail: {
+ feature: "custom-ga",
+ },
+ });
+ }, [googleAnalyticsEnabled]);
+
+ return (
+ <>
+ {/* Google Analytics */}
+ {googleAnalyticsEnabled ? (
+ <>
+
+ {/* Google Tag Manager */}
+
+ >
+ ) : null}
+ {/* Datafa.st */}
+ {dataFastEnabled ? (
+
+ ) : null}
+ >
+ );
+}
+
+export const analytics = {
+ sendGAEvent,
+ sendDatafastEvent,
+};
+
+function sendGAEvent(...args: unknown[]) {
+ if (typeof window === "undefined") return;
+ if (currDataLayerName === undefined) return;
+
+ const dataLayer = window[currDataLayerName];
+ if (!dataLayer) return;
+
+ if (Array.isArray(dataLayer)) {
+ dataLayer.push(...args);
+ } else {
+ console.warn(`Custom GA: dataLayer ${currDataLayerName} does not exist`);
+ }
+}
+
+function sendDatafastEvent(name: string, metadata: Record) {
+ if (typeof window === "undefined" || !window.datafast) return;
+ window.datafast(name, metadata);
+}
diff --git a/autogpt_platform/frontend/src/services/environment/index.ts b/autogpt_platform/frontend/src/services/environment/index.ts
index e84e7e2e8d..dc09090469 100644
--- a/autogpt_platform/frontend/src/services/environment/index.ts
+++ b/autogpt_platform/frontend/src/services/environment/index.ts
@@ -92,10 +92,6 @@ function areFeatureFlagsEnabled() {
return process.env.NEXT_PUBLIC_LAUNCHDARKLY_ENABLED === "enabled";
}
-function areAnalyticsEnabled(host: string) {
- return host.includes("platform.agpt.co");
-}
-
export const environment = {
// Generic
getEnvironmentStr,
@@ -115,6 +111,5 @@ export const environment = {
isCloud,
isLocal,
isCAPTCHAEnabled,
- areAnalyticsEnabled,
areFeatureFlagsEnabled,
};