feat(web): add workflow inputs code template

This commit is contained in:
BennyKok 2023-12-21 17:35:48 +08:00
parent c2d67da7db
commit 91b8c4d702
5 changed files with 118 additions and 58 deletions

View File

@ -9,6 +9,7 @@ import {
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { TableCell, TableRow } from "@/components/ui/table"; import { TableCell, TableRow } from "@/components/ui/table";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { getInputsFromWorkflow } from "@/lib/getInputsFromWorkflow";
import { getRelativeTime } from "@/lib/getRelativeTime"; import { getRelativeTime } from "@/lib/getRelativeTime";
import type { findAllDeployments } from "@/server/findAllRuns"; import type { findAllDeployments } from "@/server/findAllRuns";
import { headers } from "next/headers"; import { headers } from "next/headers";
@ -29,28 +30,29 @@ curl --request GET \
`; `;
const jsTemplate = ` const jsTemplate = `
fetch('<URL>', { const { run_id } = await fetch('<URL>', {
method: 'POST', method: 'POST',
headers: {'Content-Type': 'application/json'}, headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + process.env.COMFY_DEPLOY_API_KEY,
},
body: JSON.stringify({ body: JSON.stringify({
deployment_id: '<ID>', deployment_id: '<ID>',
inputs: {}
}), }),
}) }).then(response => response.json())
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
`; `;
const jsTemplate_checkStatus = ` const jsTemplate_checkStatus = `
const run_id = '<RUN_ID>'; const run_id = '<RUN_ID>';
fetch('<URL>?run_id=' + run_id, { const output = fetch('<URL>?run_id=' + run_id, {
method: 'GET', method: 'GET',
headers: {'Content-Type': 'application/json'}, headers: {
}) 'Content-Type': 'application/json',
.then(response => response.json()) 'Authorization': 'Bearer ' + process.env.COMFY_DEPLOY_API_KEY,
.then(response => console.log(response)) },
.catch(err => console.error(err)); }).then(response => response.json())
`; `;
export function DeploymentDisplay({ export function DeploymentDisplay({
@ -63,6 +65,8 @@ export function DeploymentDisplay({
const protocol = headersList.get("x-forwarded-proto") || ""; const protocol = headersList.get("x-forwarded-proto") || "";
const domain = `${protocol}://${host}`; const domain = `${protocol}://${host}`;
const workflowInput = getInputsFromWorkflow(deployment.version);
return ( return (
<Dialog> <Dialog>
<DialogTrigger asChild className="appearance-none hover:cursor-pointer"> <DialogTrigger asChild className="appearance-none hover:cursor-pointer">
@ -79,7 +83,7 @@ export function DeploymentDisplay({
</TableCell> </TableCell>
</TableRow> </TableRow>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-w-xl"> <DialogContent className="max-w-2xl">
<DialogHeader> <DialogHeader>
<DialogTitle className="capitalize"> <DialogTitle className="capitalize">
{deployment.environment} Deployment {deployment.environment} Deployment
@ -95,7 +99,7 @@ export function DeploymentDisplay({
Trigger the workflow Trigger the workflow
<CodeBlock <CodeBlock
lang="js" lang="js"
code={formatCode(jsTemplate, deployment, domain)} code={formatCode(jsTemplate, deployment, domain, workflowInput)}
/> />
Check the status of the run, and retrieve the outputs Check the status of the run, and retrieve the outputs
<CodeBlock <CodeBlock
@ -122,8 +126,32 @@ export function DeploymentDisplay({
function formatCode( function formatCode(
codeTemplate: string, codeTemplate: string,
deployment: Awaited<ReturnType<typeof findAllDeployments>>[0], deployment: Awaited<ReturnType<typeof findAllDeployments>>[0],
domain: string domain: string,
inputs?: ReturnType<typeof getInputsFromWorkflow>
) { ) {
if (inputs && inputs.length > 0) {
codeTemplate = codeTemplate.replace(
"inputs: {}",
`inputs: ${JSON.stringify(
Object.fromEntries(
inputs.map((x) => {
return [x?.input_id, ""];
})
),
null,
2
)
.split("\n")
.map((line, index) => (index === 0 ? line : ` ${line}`)) // Add two spaces indentation except for the first line
.join("\n")}`
);
} else {
codeTemplate = codeTemplate.replace(
`
inputs: {}`,
""
);
}
return codeTemplate return codeTemplate
.replace("<URL>", `${domain ?? "http://localhost:3000"}/api/run`) .replace("<URL>", `${domain ?? "http://localhost:3000"}/api/run`)
.replace("<ID>", deployment.id); .replace("<ID>", deployment.id);

View File

@ -1,6 +1,8 @@
"use client"; "use client";
import { getInputsFromWorkflow } from "../lib/getInputsFromWorkflow";
import { callServerPromise } from "./callServerPromise"; import { callServerPromise } from "./callServerPromise";
import { customInputNodes } from "./customInputNodes";
import { LoadingIcon } from "@/components/LoadingIcon"; import { LoadingIcon } from "@/components/LoadingIcon";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@ -206,10 +208,16 @@ export function CreateDeploymentButton({
); );
} }
const customInputNodes: Record<string, string> = { export function getWorkflowVersionFromVersionIndex(
ComfyUIDeployExternalText: "string", workflow: Awaited<ReturnType<typeof findFirstTableWithVersion>>,
ComfyUIDeployExternalImage: "string - (public image url)", version: number
}; ) {
const workflow_version = workflow?.versions.find(
(x) => x.version === version
);
return workflow_version;
}
export function VersionDetails({ export function VersionDetails({
workflow, workflow,
@ -220,46 +228,46 @@ export function VersionDetails({
defaultValue: workflow?.versions[0].version ?? 1, defaultValue: workflow?.versions[0].version ?? 1,
...parseAsInteger, ...parseAsInteger,
}); });
const workflow_version = workflow?.versions.find( const workflow_version = getWorkflowVersionFromVersionIndex(
(x) => x.version === version workflow,
version
); );
const inputs = getInputsFromWorkflow(workflow_version);
return ( return (
<div className="mt-4"> <div className="mt-4">
Workflow Details Workflow Details
<div className="border rounded-lg p-2"> <div className="border rounded-lg p-2">
{workflow_version?.workflow_api && ( {inputs && (
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
{Object.entries(workflow_version.workflow_api).map( {inputs.map((value) => {
([key, value]) => { if (!value.class_type) return <> </>;
if (!value.class_type) return <> </>; const nodeType = customInputNodes[value.class_type];
const nodeType = customInputNodes[value.class_type]; if (nodeType) {
if (nodeType) { const input_id = value.input_id;
const input_id = value.inputs.input_id; const defaultValue = value.default_value;
const defaultValue = value.inputs.default_value; return (
return ( <div key={input_id}>
<div key={input_id}> <Tooltip>
<Tooltip> <TooltipTrigger>
<TooltipTrigger> <Badge variant="secondary">
<Badge variant="secondary"> <div>
<div> {input_id}
{input_id} {" : "}
{" : "} <span className="text-orange-500">{nodeType}</span>
{nodeType} </div>
</div> </Badge>
</Badge> {/* {nodeType}{" "} */}
{/* {nodeType}{" "} */} {/* <Button variant="outline">Hover</Button> */}
{/* <Button variant="outline">Hover</Button> */} </TooltipTrigger>
</TooltipTrigger> <TooltipContent>
<TooltipContent> Default Value: {defaultValue}
Default Value: {defaultValue} </TooltipContent>
</TooltipContent> </Tooltip>
</Tooltip> </div>
</div> );
);
}
return <></>;
} }
)} return <></>;
})}
</div> </div>
)} )}
</div> </div>

View File

@ -0,0 +1,4 @@
export const customInputNodes: Record<string, string> = {
ComfyUIDeployExternalText: "string",
ComfyUIDeployExternalImage: "string - (public image url)",
};

View File

@ -0,0 +1,24 @@
import type { getWorkflowVersionFromVersionIndex } from "../components/VersionSelect";
import { customInputNodes } from "@/components/customInputNodes";
export function getInputsFromWorkflow(
workflow_version: ReturnType<typeof getWorkflowVersionFromVersionIndex>
) {
if (!workflow_version || !workflow_version.workflow_api) return null;
return Object.entries(workflow_version.workflow_api)
.map(([_, value]) => {
if (!value.class_type) return undefined;
const nodeType = customInputNodes[value.class_type];
if (nodeType) {
const input_id = value.inputs.input_id as string;
const default_value = value.inputs.default_value as string;
return {
class_type: value.class_type,
input_id,
default_value,
};
}
return undefined;
})
.filter((item) => item !== undefined);
}

View File

@ -8,7 +8,7 @@ export async function findAllRuns(workflow_id: string) {
orderBy: desc(workflowRunsTable.created_at), orderBy: desc(workflowRunsTable.created_at),
limit: 10, limit: 10,
extras: { extras: {
"number": sql<number>`row_number() over (order by created_at)`.as("number"), number: sql<number>`row_number() over (order by created_at)`.as("number"),
}, },
with: { with: {
machine: { machine: {
@ -36,11 +36,7 @@ export async function findAllDeployments(workflow_id: string) {
name: true, name: true,
}, },
}, },
version: { version: true,
columns: {
version: true,
},
},
}, },
}); });
} }