feat(web): add workflow inputs code template
This commit is contained in:
parent
c2d67da7db
commit
91b8c4d702
@ -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);
|
||||||
|
@ -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>
|
||||||
|
4
web/src/components/customInputNodes.tsx
Normal file
4
web/src/components/customInputNodes.tsx
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export const customInputNodes: Record<string, string> = {
|
||||||
|
ComfyUIDeployExternalText: "string",
|
||||||
|
ComfyUIDeployExternalImage: "string - (public image url)",
|
||||||
|
};
|
24
web/src/lib/getInputsFromWorkflow.tsx
Normal file
24
web/src/lib/getInputsFromWorkflow.tsx
Normal 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);
|
||||||
|
}
|
@ -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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user