feat(server): first backend endpoint

This commit is contained in:
Nicholas Tindle
2024-09-09 19:55:29 -05:00
parent ef691359b7
commit 2bcf373f0f
6 changed files with 236 additions and 7 deletions

View File

@@ -1,3 +1,6 @@
{
"python.analysis.typeCheckingMode": "basic",
"python.testing.pytestArgs": ["test"],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}

View File

@@ -78,128 +78,160 @@ class AgentServer(AppService):
from .integrations import integrations_api_router
api_router.include_router(integrations_api_router, prefix="/integrations")
import autogpt_server.server.routers.analytics
api_router.include_router(
autogpt_server.server.routers.analytics.router,
prefix="/analytics",
tags=["analytics"],
dependencies=[Depends(auth_middleware)],
)
api_router.add_api_route(
path="/auth/user",
endpoint=self.get_or_create_user_route,
methods=["POST"],
tags=["auth"],
)
api_router.add_api_route(
path="/blocks",
endpoint=self.get_graph_blocks,
methods=["GET"],
tags=["blocks"],
)
api_router.add_api_route(
path="/blocks/{block_id}/execute",
endpoint=self.execute_graph_block,
methods=["POST"],
tags=["blocks"],
)
api_router.add_api_route(
path="/graphs",
endpoint=self.get_graphs,
methods=["GET"],
tags=["graphs"],
)
api_router.add_api_route(
path="/templates",
endpoint=self.get_templates,
methods=["GET"],
tags=["templates", "graphs"],
)
api_router.add_api_route(
path="/graphs",
endpoint=self.create_new_graph,
methods=["POST"],
tags=["graphs"],
)
api_router.add_api_route(
path="/templates",
endpoint=self.create_new_template,
methods=["POST"],
tags=["templates", "graphs"],
)
api_router.add_api_route(
path="/graphs/{graph_id}",
endpoint=self.get_graph,
methods=["GET"],
tags=["graphs"],
)
api_router.add_api_route(
path="/templates/{graph_id}",
endpoint=self.get_template,
methods=["GET"],
tags=["templates", "graphs"],
)
api_router.add_api_route(
path="/graphs/{graph_id}",
endpoint=self.update_graph,
methods=["PUT"],
tags=["graphs"],
)
api_router.add_api_route(
path="/templates/{graph_id}",
endpoint=self.update_graph,
methods=["PUT"],
tags=["templates", "graphs"],
)
api_router.add_api_route(
path="/graphs/{graph_id}/versions",
endpoint=self.get_graph_all_versions,
methods=["GET"],
tags=["graphs"],
)
api_router.add_api_route(
path="/templates/{graph_id}/versions",
endpoint=self.get_graph_all_versions,
methods=["GET"],
tags=["templates", "graphs"],
)
api_router.add_api_route(
path="/graphs/{graph_id}/versions/{version}",
endpoint=self.get_graph,
methods=["GET"],
tags=["graphs"],
)
api_router.add_api_route(
path="/graphs/{graph_id}/versions/active",
endpoint=self.set_graph_active_version,
methods=["PUT"],
tags=["graphs"],
)
api_router.add_api_route(
path="/graphs/{graph_id}/input_schema",
endpoint=self.get_graph_input_schema,
methods=["GET"],
tags=["graphs"],
)
api_router.add_api_route(
path="/graphs/{graph_id}/execute",
endpoint=self.execute_graph,
methods=["POST"],
tags=["graphs"],
)
api_router.add_api_route(
path="/graphs/{graph_id}/executions",
endpoint=self.list_graph_runs,
methods=["GET"],
tags=["graphs"],
)
api_router.add_api_route(
path="/graphs/{graph_id}/executions/{graph_exec_id}",
endpoint=self.get_graph_run_node_execution_results,
methods=["GET"],
tags=["graphs"],
)
api_router.add_api_route(
path="/graphs/{graph_id}/executions/{graph_exec_id}/stop",
endpoint=self.stop_graph_run,
methods=["POST"],
tags=["graphs"],
)
api_router.add_api_route(
path="/graphs/{graph_id}/schedules",
endpoint=self.create_schedule,
methods=["POST"],
tags=["graphs"],
)
api_router.add_api_route(
path="/graphs/{graph_id}/schedules",
endpoint=self.get_execution_schedules,
methods=["GET"],
tags=["graphs"],
)
api_router.add_api_route(
path="/graphs/schedules/{schedule_id}",
endpoint=self.update_schedule,
methods=["PUT"],
tags=["graphs"],
)
api_router.add_api_route(
path="/settings",
endpoint=self.update_configuration,
methods=["POST"],
tags=["settings"],
)
app.add_exception_handler(500, self.handle_internal_http_error)

View File

@@ -0,0 +1,61 @@
# Analytics API
from typing import Annotated
import typing
import typing_extensions
import fastapi
import prisma
import prisma.enums
import pydantic
from autogpt_server.server.utils import get_user_id
router = fastapi.APIRouter()
class UserData(pydantic.BaseModel):
user_id: str
email: str
name: str
username: str
class TutorialStepData(pydantic.BaseModel):
data: typing.Optional[dict]
@router.post(path="/log_new_user")
async def log_create_user(
user_id: Annotated[str, fastapi.Depends(get_user_id)],
user_data: Annotated[UserData, fastapi.Body(..., embed=True)],
):
"""
Log the user ID for analytics purposes.
"""
id = await prisma.models.AnalyticsDetails.prisma().create(
data={
"userId": user_id,
"type": prisma.enums.AnalyticsType.CREATE_USER,
"data": prisma.Json(user_data.model_dump_json()),
}
)
return id.id
@router.post(path="/log_tutorial_step")
async def log_tutorial_step(
user_id: Annotated[str, fastapi.Depends(get_user_id)],
step: Annotated[int, fastapi.Query(..., embed=True)],
data: Annotated[TutorialStepData, fastapi.Body(..., embed=True)],
):
"""
Log the tutorial step completed by the user for analytics purposes.
"""
id = await prisma.models.AnalyticsDetails.prisma().create(
data={
"userId": user_id,
"type": prisma.enums.AnalyticsType.TUTORIAL_STEP,
"data": prisma.Json(data.model_dump_json()),
"dataIndex": step,
}
)
return id.id

View File

@@ -0,0 +1,53 @@
-- CreateEnum
CREATE TYPE "AnalyticsType" AS ENUM ('CREATE_USER', 'TUTORIAL_STEP', 'WEB_PAGE', 'AGENT_GRAPH_EXECUTION', 'AGENT_NODE_EXECUTION');
-- CreateEnum
CREATE TYPE "AnalyticsMetric" AS ENUM ('PAGE_VIEW', 'TUTORIAL_STEP_COMPLETION', 'AGENT_GRAPH_EXECUTION', 'AGENT_NODE_EXECUTION');
-- CreateEnum
CREATE TYPE "AggregationType" AS ENUM ('COUNT', 'SUM', 'AVG', 'MAX', 'MIN', 'NO_AGGREGATION');
-- CreateTable
CREATE TABLE "AnalyticsDetails" (
"id" TEXT NOT NULL DEFAULT gen_random_uuid(),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"userId" TEXT NOT NULL,
"type" "AnalyticsType" NOT NULL,
"data" JSONB,
"dataIndex" INTEGER,
CONSTRAINT "AnalyticsDetails_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "AnalyticsMetrics" (
"id" TEXT NOT NULL DEFAULT gen_random_uuid(),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"analyticMetric" "AnalyticsMetric" NOT NULL,
"value" DOUBLE PRECISION NOT NULL,
"dataString" TEXT,
"aggregationType" "AggregationType" NOT NULL DEFAULT 'NO_AGGREGATION',
"userId" TEXT NOT NULL,
CONSTRAINT "AnalyticsMetrics_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "analyticsDetails" ON "AnalyticsDetails"("userId", "type");
-- CreateIndex
CREATE INDEX "AnalyticsDetails_type_idx" ON "AnalyticsDetails"("type");
-- CreateIndex
CREATE INDEX "analytics_metric_index" ON "AnalyticsMetrics"("analyticMetric", "userId", "dataString", "aggregationType");
-- CreateIndex
CREATE UNIQUE INDEX "AnalyticsMetrics_analyticMetric_userId_dataString_aggregati_key" ON "AnalyticsMetrics"("analyticMetric", "userId", "dataString", "aggregationType");
-- AddForeignKey
ALTER TABLE "AnalyticsDetails" ADD CONSTRAINT "AnalyticsDetails_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "AnalyticsMetrics" ADD CONSTRAINT "AnalyticsMetrics_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "AnalyticsMetrics" ADD COLUMN "aggregationCounter" INTEGER DEFAULT 1;

View File

@@ -22,6 +22,8 @@ model User {
AgentGraphs AgentGraph[]
AgentGraphExecutions AgentGraphExecution[]
AgentGraphExecutionSchedules AgentGraphExecutionSchedule[]
AnalyticsDetails AnalyticsDetails[]
AnalyticsMetrics AnalyticsMetrics[]
@@index([id])
@@index([email])
@@ -29,9 +31,9 @@ model User {
// This model describes the Agent Graph/Flow (Multi Agent System).
model AgentGraph {
id String @default(uuid())
version Int @default(1)
createdAt DateTime @default(now())
id String @default(uuid())
version Int @default(1)
createdAt DateTime @default(now())
updatedAt DateTime? @updatedAt
name String?
@@ -115,8 +117,8 @@ model AgentBlock {
// This model describes the execution of an AgentGraph.
model AgentGraphExecution {
id String @id @default(uuid())
createdAt DateTime @default(now())
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime? @updatedAt
agentGraphId String
@@ -178,8 +180,8 @@ model AgentNodeExecutionInputOutput {
// This model describes the recurring execution schedule of an Agent.
model AgentGraphExecutionSchedule {
id String @id
createdAt DateTime @default(now())
id String @id
createdAt DateTime @default(now())
updatedAt DateTime? @updatedAt
agentGraphId String
@@ -199,3 +201,79 @@ model AgentGraphExecutionSchedule {
@@index([isEnabled])
}
enum AnalyticsType {
CREATE_USER
TUTORIAL_STEP
WEB_PAGE
AGENT_GRAPH_EXECUTION
AGENT_NODE_EXECUTION
}
model AnalyticsDetails {
// PK uses gen_random_uuid() to allow the db inserts to happen outside of prisma
// typical uuid() inserts are handled by prisma
id String @id @default(dbgenerated("gen_random_uuid()"))
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
// Link to User model
userId String
user User @relation(fields: [userId], references: [id])
// Analytics Categorical data used for filtering (indexable w and w/o userId)
type AnalyticsType
// Analytic Specific Data. We should use a union type here, but prisma doesn't support it.
data Json?
// Indexable field for any count based analytically measures like page order clicking, tutorial step completion, etc.
dataIndex Int?
@@index([userId, type], name: "analyticsDetails")
@@index([type])
}
enum AnalyticsMetric {
PAGE_VIEW
TUTORIAL_STEP_COMPLETION
AGENT_GRAPH_EXECUTION
AGENT_NODE_EXECUTION
}
enum AggregationType {
COUNT
SUM
AVG
MAX
MIN
NO_AGGREGATION
}
model AnalyticsMetrics {
id String @id @default(dbgenerated("gen_random_uuid()"))
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Analytics Categorical data used for filtering (indexable w and w/o userId)
analyticMetric AnalyticsMetric
// Any numeric data that should be counted upon, summed, or otherwise aggregated.
value Float
// Any string data that should be used to identify the metric as distinct.
// ex: '/build' vs '/market'
dataString String?
// Data Aggregation Type
aggregationType AggregationType @default(NO_AGGREGATION)
// Aggregation Counter used for aggregation style events that beenefit from it. (AVG) (not self incrementing)
aggregationCounter Int? @default(1)
// Link to User model
userId String
user User @relation(fields: [userId], references: [id])
// Allows us to have unique but useful user level metrics.
@@unique([analyticMetric, userId, dataString, aggregationType])
@@index(fields: [analyticMetric, userId, dataString, aggregationType], name: "analytics_metric_index")
}