feat: add cold start duration display

This commit is contained in:
BennyKok 2024-01-21 11:40:06 +08:00
parent 1d2497116d
commit ca1b05fff5
8 changed files with 1144 additions and 343 deletions

View File

@ -0,0 +1 @@
ALTER TABLE "comfyui_deploy"."workflow_runs" ADD COLUMN "started_at" timestamp;

View File

@ -0,0 +1,762 @@
{
"id": "1ca4fdb7-c0c4-4c39-8b47-f40282293da0",
"prevId": "db06ea66-92c2-4ebe-93c1-6cb8a90ccd8b",
"version": "5",
"dialect": "pg",
"tables": {
"api_keys": {
"name": "api_keys",
"schema": "comfyui_deploy",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"key": {
"name": "key",
"type": "text",
"primaryKey": false,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
"org_id": {
"name": "org_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"revoked": {
"name": "revoked",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"api_keys_user_id_users_id_fk": {
"name": "api_keys_user_id_users_id_fk",
"tableFrom": "api_keys",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"api_keys_key_unique": {
"name": "api_keys_key_unique",
"nullsNotDistinct": false,
"columns": [
"key"
]
}
}
},
"deployments": {
"name": "deployments",
"schema": "comfyui_deploy",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
"org_id": {
"name": "org_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"workflow_version_id": {
"name": "workflow_version_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"workflow_id": {
"name": "workflow_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"machine_id": {
"name": "machine_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false
},
"showcase_media": {
"name": "showcase_media",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"environment": {
"name": "environment",
"type": "deployment_environment",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"deployments_user_id_users_id_fk": {
"name": "deployments_user_id_users_id_fk",
"tableFrom": "deployments",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"deployments_workflow_version_id_workflow_versions_id_fk": {
"name": "deployments_workflow_version_id_workflow_versions_id_fk",
"tableFrom": "deployments",
"tableTo": "workflow_versions",
"columnsFrom": [
"workflow_version_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"deployments_workflow_id_workflows_id_fk": {
"name": "deployments_workflow_id_workflows_id_fk",
"tableFrom": "deployments",
"tableTo": "workflows",
"columnsFrom": [
"workflow_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"deployments_machine_id_machines_id_fk": {
"name": "deployments_machine_id_machines_id_fk",
"tableFrom": "deployments",
"tableTo": "machines",
"columnsFrom": [
"machine_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"machines": {
"name": "machines",
"schema": "comfyui_deploy",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"org_id": {
"name": "org_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"endpoint": {
"name": "endpoint",
"type": "text",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"disabled": {
"name": "disabled",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": false
},
"auth_token": {
"name": "auth_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"type": {
"name": "type",
"type": "machine_type",
"primaryKey": false,
"notNull": true,
"default": "'classic'"
},
"status": {
"name": "status",
"type": "machine_status",
"primaryKey": false,
"notNull": true,
"default": "'ready'"
},
"snapshot": {
"name": "snapshot",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"models": {
"name": "models",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"gpu": {
"name": "gpu",
"type": "machine_gpu",
"primaryKey": false,
"notNull": false
},
"build_machine_instance_id": {
"name": "build_machine_instance_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"build_log": {
"name": "build_log",
"type": "text",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"machines_user_id_users_id_fk": {
"name": "machines_user_id_users_id_fk",
"tableFrom": "machines",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"users": {
"name": "users",
"schema": "comfyui_deploy",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"username": {
"name": "username",
"type": "text",
"primaryKey": false,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"workflow_run_outputs": {
"name": "workflow_run_outputs",
"schema": "comfyui_deploy",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"run_id": {
"name": "run_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"data": {
"name": "data",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"workflow_run_outputs_run_id_workflow_runs_id_fk": {
"name": "workflow_run_outputs_run_id_workflow_runs_id_fk",
"tableFrom": "workflow_run_outputs",
"tableTo": "workflow_runs",
"columnsFrom": [
"run_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"workflow_runs": {
"name": "workflow_runs",
"schema": "comfyui_deploy",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"workflow_version_id": {
"name": "workflow_version_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"workflow_inputs": {
"name": "workflow_inputs",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"workflow_id": {
"name": "workflow_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"machine_id": {
"name": "machine_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"origin": {
"name": "origin",
"type": "workflow_run_origin",
"primaryKey": false,
"notNull": true,
"default": "'api'"
},
"status": {
"name": "status",
"type": "workflow_run_status",
"primaryKey": false,
"notNull": true,
"default": "'not-started'"
},
"ended_at": {
"name": "ended_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"started_at": {
"name": "started_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"workflow_runs_workflow_version_id_workflow_versions_id_fk": {
"name": "workflow_runs_workflow_version_id_workflow_versions_id_fk",
"tableFrom": "workflow_runs",
"tableTo": "workflow_versions",
"columnsFrom": [
"workflow_version_id"
],
"columnsTo": [
"id"
],
"onDelete": "set null",
"onUpdate": "no action"
},
"workflow_runs_workflow_id_workflows_id_fk": {
"name": "workflow_runs_workflow_id_workflows_id_fk",
"tableFrom": "workflow_runs",
"tableTo": "workflows",
"columnsFrom": [
"workflow_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"workflow_runs_machine_id_machines_id_fk": {
"name": "workflow_runs_machine_id_machines_id_fk",
"tableFrom": "workflow_runs",
"tableTo": "machines",
"columnsFrom": [
"machine_id"
],
"columnsTo": [
"id"
],
"onDelete": "set null",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"workflows": {
"name": "workflows",
"schema": "comfyui_deploy",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
"org_id": {
"name": "org_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"workflows_user_id_users_id_fk": {
"name": "workflows_user_id_users_id_fk",
"tableFrom": "workflows",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"workflow_versions": {
"name": "workflow_versions",
"schema": "comfyui_deploy",
"columns": {
"workflow_id": {
"name": "workflow_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"workflow": {
"name": "workflow",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"workflow_api": {
"name": "workflow_api",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"version": {
"name": "version",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"snapshot": {
"name": "snapshot",
"type": "jsonb",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"workflow_versions_workflow_id_workflows_id_fk": {
"name": "workflow_versions_workflow_id_workflows_id_fk",
"tableFrom": "workflow_versions",
"tableTo": "workflows",
"columnsFrom": [
"workflow_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {
"deployment_environment": {
"name": "deployment_environment",
"values": {
"staging": "staging",
"production": "production",
"public-share": "public-share"
}
},
"machine_gpu": {
"name": "machine_gpu",
"values": {
"T4": "T4",
"A10G": "A10G",
"A100": "A100"
}
},
"machine_status": {
"name": "machine_status",
"values": {
"ready": "ready",
"building": "building",
"error": "error"
}
},
"machine_type": {
"name": "machine_type",
"values": {
"classic": "classic",
"runpod-serverless": "runpod-serverless",
"modal-serverless": "modal-serverless",
"comfy-deploy-serverless": "comfy-deploy-serverless"
}
},
"workflow_run_origin": {
"name": "workflow_run_origin",
"values": {
"manual": "manual",
"api": "api",
"public-share": "public-share"
}
},
"workflow_run_status": {
"name": "workflow_run_status",
"values": {
"not-started": "not-started",
"running": "running",
"uploading": "uploading",
"success": "success",
"failed": "failed"
}
}
},
"schemas": {
"comfyui_deploy": "comfyui_deploy"
},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
}
}

View File

@ -218,6 +218,13 @@
"when": 1705716303820,
"tag": "0030_kind_doorman",
"breakpoints": true
},
{
"idx": 31,
"version": "5",
"when": 1705763980972,
"tag": "0031_fast_lyja",
"breakpoints": true
}
]
}

View File

@ -2,62 +2,73 @@ import { RunInputs } from "@/components/RunInputs";
import { RunOutputs } from "@/components/RunOutputs";
import { Badge } from "@/components/ui/badge";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { TableCell, TableRow } from "@/components/ui/table";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { getDuration, getRelativeTime } from "@/lib/getRelativeTime";
import { type findAllRuns } from "@/server/findAllRuns";
import { Suspense } from "react";
import { LiveStatus } from "./LiveStatus";
export async function RunDisplay({
run,
run,
}: {
run: Awaited<ReturnType<typeof findAllRuns>>[0];
run: Awaited<ReturnType<typeof findAllRuns>>[0];
}) {
return (
<Dialog>
<DialogTrigger asChild className="appearance-none hover:cursor-pointer">
<TableRow>
<TableCell>{run.number}</TableCell>
<TableCell className="font-medium truncate">
{run.machine?.name}
</TableCell>
<TableCell className="truncate">
{getRelativeTime(run.created_at)}
</TableCell>
<TableCell>{run.version?.version}</TableCell>
<TableCell>
<Badge variant="outline" className="truncate">
{run.origin}
</Badge>
</TableCell>
<TableCell className="truncate">
{getDuration(run.duration)}
</TableCell>
<LiveStatus run={run} />
</TableRow>
</DialogTrigger>
<DialogContent className="max-w-3xl">
<DialogHeader>
<DialogTitle>Run outputs</DialogTitle>
<DialogDescription>
You can view your run&apos;s outputs here
</DialogDescription>
</DialogHeader>
<div className="max-h-96 overflow-y-scroll">
<RunInputs run={run} />
<Suspense>
<RunOutputs run_id={run.id} />
</Suspense>
</div>
{/* <div className="max-h-96 overflow-y-scroll">{view}</div> */}
</DialogContent>
</Dialog>
);
return (
<Dialog>
<DialogTrigger asChild className="appearance-none hover:cursor-pointer">
<TableRow>
<TableCell>{run.number}</TableCell>
<TableCell className="font-medium truncate">
{run.machine?.name}
</TableCell>
<TableCell className="truncate">
{getRelativeTime(run.created_at)}
</TableCell>
<TableCell>{run.version?.version}</TableCell>
<TableCell>
<Badge variant="outline" className="truncate">
{run.origin}
</Badge>
</TableCell>
<TableCell className="truncate">
<Tooltip>
<TooltipTrigger>{getDuration(run.duration)}</TooltipTrigger>
<TooltipContent>
<div>Cold start: {getDuration(run.cold_start_duration)}</div>
<div>Run duration: {getDuration(run.run_duration)}</div>
</TooltipContent>
</Tooltip>
</TableCell>
<LiveStatus run={run} />
</TableRow>
</DialogTrigger>
<DialogContent className="max-w-3xl">
<DialogHeader>
<DialogTitle>Run outputs</DialogTitle>
<DialogDescription>
You can view your run&apos;s outputs here
</DialogDescription>
</DialogHeader>
<div className="max-h-96 overflow-y-scroll">
<RunInputs run={run} />
<Suspense>
<RunOutputs run_id={run.id} />
</Suspense>
</div>
{/* <div className="max-h-96 overflow-y-scroll">{view}</div> */}
</DialogContent>
</Dialog>
);
}

View File

@ -1,10 +1,3 @@
import {
findAllDeployments,
findAllRunsWithCounts,
} from "../server/findAllRuns";
import { DeploymentDisplay } from "./DeploymentDisplay";
import { PaginationControl } from "./PaginationControl";
import { RunDisplay } from "./RunDisplay";
import {
Table,
TableBody,
@ -15,6 +8,13 @@ import {
} from "@/components/ui/table";
import { parseAsInteger } from "next-usequerystate";
import { headers } from "next/headers";
import {
findAllDeployments,
findAllRunsWithCounts,
} from "../server/findAllRuns";
import { DeploymentDisplay } from "./DeploymentDisplay";
import { PaginationControl } from "./PaginationControl";
import { RunDisplay } from "./RunDisplay";
const itemPerPage = 6;
const pageParser = parseAsInteger.withDefault(1);
@ -33,40 +33,40 @@ export async function RunsTable(props: {
offset: (page - 1) * itemPerPage,
});
return (
<div>
<div className="overflow-auto h-fit w-full">
<Table className="">
{allRuns.length == 0 && (
<TableCaption>A list of your recent runs.</TableCaption>
)}
<TableHeader className="bg-background top-0 sticky">
<TableRow>
<TableHead className="truncate">Number</TableHead>
<TableHead className="truncate">Machine</TableHead>
<TableHead className="truncate">Time</TableHead>
<TableHead className="truncate">Version</TableHead>
<TableHead className="truncate">Origin</TableHead>
<TableHead className="truncate">Duration</TableHead>
<TableHead className="truncate">Live Status</TableHead>
<TableHead className="text-right">Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{allRuns.map((run) => (
<RunDisplay run={run} key={run.id} />
))}
</TableBody>
</Table>
</div>
<div>
<div className="overflow-auto h-fit w-full">
<Table className="">
{allRuns.length === 0 && (
<TableCaption>A list of your recent runs.</TableCaption>
)}
<TableHeader className="bg-background top-0 sticky">
<TableRow>
<TableHead className="truncate">Number</TableHead>
<TableHead className="truncate">Machine</TableHead>
<TableHead className="truncate">Time</TableHead>
<TableHead className="truncate">Version</TableHead>
<TableHead className="truncate">Origin</TableHead>
<TableHead className="truncate">Duration</TableHead>
<TableHead className="truncate">Live Status</TableHead>
<TableHead className="text-right">Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{allRuns.map((run) => (
<RunDisplay run={run} key={run.id} />
))}
</TableBody>
</Table>
</div>
{Math.ceil(total / itemPerPage) > 0 && (
<PaginationControl
totalPage={Math.ceil(total / itemPerPage)}
currentPage={page}
/>
)}
</div>
);
{Math.ceil(total / itemPerPage) > 0 && (
<PaginationControl
totalPage={Math.ceil(total / itemPerPage)}
currentPage={page}
/>
)}
</div>
);
}
export async function DeploymentsTable(props: { workflow_id: string }) {

View File

@ -1,13 +1,13 @@
import { relations, type InferSelectModel } from "drizzle-orm";
import { type InferSelectModel, relations } from "drizzle-orm";
import {
text,
pgSchema,
uuid,
integer,
timestamp,
jsonb,
pgEnum,
boolean,
boolean,
integer,
jsonb,
pgEnum,
pgSchema,
text,
timestamp,
uuid,
} from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
@ -130,29 +130,30 @@ export const machinesStatus = pgEnum("machine_status", [
// We still want to keep the workflow run record.
export const workflowRunsTable = dbSchema.table("workflow_runs", {
id: uuid("id").primaryKey().defaultRandom().notNull(),
// when workflow version deleted, still want to keep this record
workflow_version_id: uuid("workflow_version_id").references(
() => workflowVersionTable.id,
{
onDelete: "set null",
}
),
workflow_inputs:
jsonb("workflow_inputs").$type<Record<string, string | number>>(),
workflow_id: uuid("workflow_id")
.notNull()
.references(() => workflowTable.id, {
onDelete: "cascade",
}),
// when machine deleted, still want to keep this record
machine_id: uuid("machine_id").references(() => machinesTable.id, {
onDelete: "set null",
}),
origin: workflowRunOrigin("origin").notNull().default("api"),
status: workflowRunStatus("status").notNull().default("not-started"),
ended_at: timestamp("ended_at"),
created_at: timestamp("created_at").defaultNow().notNull(),
id: uuid("id").primaryKey().defaultRandom().notNull(),
// when workflow version deleted, still want to keep this record
workflow_version_id: uuid("workflow_version_id").references(
() => workflowVersionTable.id,
{
onDelete: "set null",
},
),
workflow_inputs:
jsonb("workflow_inputs").$type<Record<string, string | number>>(),
workflow_id: uuid("workflow_id")
.notNull()
.references(() => workflowTable.id, {
onDelete: "cascade",
}),
// when machine deleted, still want to keep this record
machine_id: uuid("machine_id").references(() => machinesTable.id, {
onDelete: "set null",
}),
origin: workflowRunOrigin("origin").notNull().default("api"),
status: workflowRunStatus("status").notNull().default("not-started"),
ended_at: timestamp("ended_at"),
created_at: timestamp("created_at").defaultNow().notNull(),
started_at: timestamp("started_at"),
});
export const workflowRunRelations = relations(

View File

@ -1,11 +1,10 @@
"use server";
import { withServerPromise } from "./withServerPromise";
import { db } from "@/db/db";
import type {
MachineType,
WorkflowRunOriginType,
WorkflowVersionType,
MachineType,
WorkflowRunOriginType,
WorkflowVersionType,
} from "@/db/schema";
import { machinesTable, workflowRunsTable } from "@/db/schema";
import type { APIKeyUserType } from "@/server/APIKeyBodyRequest";
@ -16,219 +15,229 @@ import { and, eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";
import "server-only";
import { v4 } from "uuid";
import { withServerPromise } from "./withServerPromise";
export const createRun = withServerPromise(
async ({
origin,
workflow_version_id,
machine_id,
inputs,
runOrigin,
apiUser,
}: {
origin: string;
workflow_version_id: string | WorkflowVersionType;
machine_id: string | MachineType;
inputs?: Record<string, string | number>;
runOrigin?: WorkflowRunOriginType;
apiUser?: APIKeyUserType;
}) => {
const machine =
typeof machine_id === "string"
? await db.query.machinesTable.findFirst({
where: and(
eq(machinesTable.id, machine_id),
eq(machinesTable.disabled, false)
),
})
: machine_id;
async ({
origin,
workflow_version_id,
machine_id,
inputs,
runOrigin,
apiUser,
}: {
origin: string;
workflow_version_id: string | WorkflowVersionType;
machine_id: string | MachineType;
inputs?: Record<string, string | number>;
runOrigin?: WorkflowRunOriginType;
apiUser?: APIKeyUserType;
}) => {
const machine =
typeof machine_id === "string"
? await db.query.machinesTable.findFirst({
where: and(
eq(machinesTable.id, machine_id),
eq(machinesTable.disabled, false),
),
})
: machine_id;
if (!machine) {
throw new Error("Machine not found");
}
if (!machine) {
throw new Error("Machine not found");
}
const workflow_version_data =
typeof workflow_version_id === "string"
? await db.query.workflowVersionTable.findFirst({
where: eq(workflowRunsTable.id, workflow_version_id),
with: {
workflow: {
columns: {
org_id: true,
user_id: true,
},
},
},
})
: workflow_version_id;
const workflow_version_data =
typeof workflow_version_id === "string"
? await db.query.workflowVersionTable.findFirst({
where: eq(workflowRunsTable.id, workflow_version_id),
with: {
workflow: {
columns: {
org_id: true,
user_id: true,
},
},
},
})
: workflow_version_id;
if (!workflow_version_data) {
throw new Error("Workflow version not found");
}
if (!workflow_version_data) {
throw new Error("Workflow version not found");
}
if (apiUser)
if (apiUser.org_id) {
// is org api call, check org only
if (apiUser.org_id != workflow_version_data.workflow.org_id) {
throw new Error("Workflow not found");
}
} else {
// is user api call, check user only
if (
apiUser.user_id != workflow_version_data.workflow.user_id &&
workflow_version_data.workflow.org_id == null
) {
throw new Error("Workflow not found");
}
}
if (apiUser)
if (apiUser.org_id) {
// is org api call, check org only
if (apiUser.org_id != workflow_version_data.workflow.org_id) {
throw new Error("Workflow not found");
}
} else {
// is user api call, check user only
if (
apiUser.user_id != workflow_version_data.workflow.user_id &&
workflow_version_data.workflow.org_id == null
) {
throw new Error("Workflow not found");
}
}
const workflow_api = workflow_version_data.workflow_api;
const workflow_api = workflow_version_data.workflow_api;
// Replace the inputs
if (inputs && workflow_api) {
for (const key in inputs) {
Object.entries(workflow_api).forEach(([_, node]) => {
if (node.inputs["input_id"] === key) {
node.inputs["input_id"] = inputs[key];
}
});
}
}
// Replace the inputs
if (inputs && workflow_api) {
for (const key in inputs) {
Object.entries(workflow_api).forEach(([_, node]) => {
if (node.inputs["input_id"] === key) {
node.inputs["input_id"] = inputs[key];
}
});
}
}
let prompt_id: string | undefined = undefined;
const shareData = {
workflow_api: workflow_api,
status_endpoint: `${origin}/api/update-run`,
file_upload_endpoint: `${origin}/api/file-upload`,
};
let prompt_id: string | undefined = undefined;
const shareData = {
workflow_api: workflow_api,
status_endpoint: `${origin}/api/update-run`,
file_upload_endpoint: `${origin}/api/file-upload`,
};
prompt_id = v4();
prompt_id = v4();
// Add to our db
const workflow_run = await db
.insert(workflowRunsTable)
.values({
id: prompt_id,
workflow_id: workflow_version_data.workflow_id,
workflow_version_id: workflow_version_data.id,
workflow_inputs: inputs,
machine_id: machine.id,
origin: runOrigin,
})
.returning();
// Add to our db
const workflow_run = await db
.insert(workflowRunsTable)
.values({
id: prompt_id,
workflow_id: workflow_version_data.workflow_id,
workflow_version_id: workflow_version_data.id,
workflow_inputs: inputs,
machine_id: machine.id,
origin: runOrigin,
})
.returning();
revalidatePath(`/${workflow_version_data.workflow_id}`);
revalidatePath(`/${workflow_version_data.workflow_id}`);
try {
switch (machine.type) {
case "comfy-deploy-serverless":
case "modal-serverless":
const _data = {
input: {
...shareData,
prompt_id: prompt_id,
},
};
try {
switch (machine.type) {
case "comfy-deploy-serverless":
case "modal-serverless":
const _data = {
input: {
...shareData,
prompt_id: prompt_id,
},
};
const ___result = await fetch(`${machine.endpoint}/run`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(_data),
cache: "no-store",
});
console.log(___result);
if (!___result.ok)
throw new Error(
`Error creating run, ${
___result.statusText
} ${await ___result.text()}`
);
console.log(_data, ___result);
break;
case "runpod-serverless":
const data = {
input: {
...shareData,
prompt_id: prompt_id,
},
};
const ___result = await fetch(`${machine.endpoint}/run`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(_data),
cache: "no-store",
});
console.log(___result);
if (!___result.ok)
throw new Error(
`Error creating run, ${
___result.statusText
} ${await ___result.text()}`,
);
console.log(_data, ___result);
break;
case "runpod-serverless":
const data = {
input: {
...shareData,
prompt_id: prompt_id,
},
};
if (
!machine.auth_token &&
!machine.endpoint.includes("localhost") &&
!machine.endpoint.includes("127.0.0.1")
) {
throw new Error("Machine auth token not found");
}
if (
!machine.auth_token &&
!machine.endpoint.includes("localhost") &&
!machine.endpoint.includes("127.0.0.1")
) {
throw new Error("Machine auth token not found");
}
const __result = await fetch(`${machine.endpoint}/run`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${machine.auth_token}`,
},
body: JSON.stringify(data),
cache: "no-store",
});
console.log(__result);
if (!__result.ok)
throw new Error(
`Error creating run, ${
__result.statusText
} ${await __result.text()}`
);
console.log(data, __result);
break;
case "classic":
const body = {
...shareData,
prompt_id: prompt_id,
};
// console.log(body);
const comfyui_endpoint = `${machine.endpoint}/comfyui-deploy/run`;
const _result = await fetch(comfyui_endpoint, {
method: "POST",
body: JSON.stringify(body),
cache: "no-store",
});
// console.log(_result);
const __result = await fetch(`${machine.endpoint}/run`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${machine.auth_token}`,
},
body: JSON.stringify(data),
cache: "no-store",
});
console.log(__result);
if (!__result.ok)
throw new Error(
`Error creating run, ${
__result.statusText
} ${await __result.text()}`,
);
console.log(data, __result);
break;
case "classic":
const body = {
...shareData,
prompt_id: prompt_id,
};
// console.log(body);
const comfyui_endpoint = `${machine.endpoint}/comfyui-deploy/run`;
const _result = await fetch(comfyui_endpoint, {
method: "POST",
body: JSON.stringify(body),
cache: "no-store",
});
// console.log(_result);
if (!_result.ok) {
let message = `Error creating run, ${_result.statusText}`;
try {
const result = await ComfyAPI_Run.parseAsync(
await _result.json()
);
message += ` ${result.node_errors}`;
} catch (error) {}
throw new Error(message);
}
// prompt_id = result.prompt_id;
break;
}
} catch (e) {
console.error(e);
await db
.update(workflowRunsTable)
.set({
status: "failed",
})
.where(eq(workflowRunsTable.id, workflow_run[0].id));
throw e;
}
if (!_result.ok) {
let message = `Error creating run, ${_result.statusText}`;
try {
const result = await ComfyAPI_Run.parseAsync(
await _result.json(),
);
message += ` ${result.node_errors}`;
} catch (error) {}
throw new Error(message);
}
// prompt_id = result.prompt_id;
break;
}
} catch (e) {
console.error(e);
await db
.update(workflowRunsTable)
.set({
status: "failed",
})
.where(eq(workflowRunsTable.id, workflow_run[0].id));
throw e;
}
return {
workflow_run_id: workflow_run[0].id,
message: "Successful workflow run",
};
}
// It successfully started, update the started_at time
await db
.update(workflowRunsTable)
.set({
started_at: new Date(),
})
.where(eq(workflowRunsTable.id, workflow_run[0].id));
return {
workflow_run_id: workflow_run[0].id,
message: "Successful workflow run",
};
},
);
export async function checkStatus(run_id: string) {
const { userId } = auth();
if (!userId) throw new Error("User not found");
const { userId } = auth();
if (!userId) throw new Error("User not found");
return await getRunsData(run_id);
return await getRunsData(run_id);
}

View File

@ -16,32 +16,42 @@ export async function findAllRuns({
offset = 0,
}: RunsSearchTypes) {
return await db.query.workflowRunsTable.findMany({
where: eq(workflowRunsTable.workflow_id, workflow_id),
orderBy: desc(workflowRunsTable.created_at),
offset: offset,
limit: limit,
extras: {
number: sql<number>`row_number() over (order by created_at)`.as("number"),
total: sql<number>`count(*) over ()`.as("total"),
duration:
sql<number>`(extract(epoch from ended_at) - extract(epoch from created_at))`.as(
"duration"
),
},
with: {
machine: {
columns: {
name: true,
endpoint: true,
},
},
version: {
columns: {
version: true,
},
},
},
});
where: eq(workflowRunsTable.workflow_id, workflow_id),
orderBy: desc(workflowRunsTable.created_at),
offset: offset,
limit: limit,
extras: {
number: sql<number>`row_number() over (order by created_at)`.as(
"number",
),
total: sql<number>`count(*) over ()`.as("total"),
duration:
sql<number>`(extract(epoch from ended_at) - extract(epoch from created_at))`.as(
"duration",
),
cold_start_duration:
sql<number>`(extract(epoch from started_at) - extract(epoch from created_at))`.as(
"cold_start_duration",
),
run_duration:
sql<number>`(extract(epoch from ended_at) - extract(epoch from started_at))`.as(
"run_duration",
),
},
with: {
machine: {
columns: {
name: true,
endpoint: true,
},
},
version: {
columns: {
version: true,
},
},
},
});
}
export async function findAllRunsWithCounts(props: RunsSearchTypes) {