Skip to content

Commit a0682f3

Browse files
committed
83 feature request implement image generation workload (#126) (#835)
1 parent fbb2d4d commit a0682f3

File tree

40 files changed

+4323
-13
lines changed

40 files changed

+4323
-13
lines changed

usecases/ai/edge-ai-demo-studio/frontend/next.config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
EMBEDDING_PORT,
99
SPEECH_TO_TEXT_PORT,
1010
TEXT_TO_SPEECH_PORT,
11+
IMAGE_GENERATION_PORT,
1112
} from '@/lib/constants'
1213

1314
const nextConfig: NextConfig = {
@@ -17,6 +18,9 @@ const nextConfig: NextConfig = {
1718
outputFileTracingIncludes: {
1819
'/': ['./node_modules/@libsql/win32-x64-msvc/**/*'],
1920
},
21+
experimental: {
22+
proxyTimeout: 1000 * 120, // 120 seconds
23+
},
2024
webpack: (config, { isServer }) => {
2125
if (!isServer) {
2226
config.externals = [
@@ -53,6 +57,10 @@ const nextConfig: NextConfig = {
5357
source: '/api/lipsync/:slug*',
5458
destination: `http://localhost:${LIPSYNC_PORT}/:slug*`,
5559
},
60+
{
61+
source: '/api/images/v1/:slug*',
62+
destination: `http://localhost:${IMAGE_GENERATION_PORT}/v3/images/:slug*`,
63+
},
5664
]
5765
},
5866
}

usecases/ai/edge-ai-demo-studio/frontend/package-lock.json

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

usecases/ai/edge-ai-demo-studio/frontend/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"@radix-ui/react-dialog": "^1.1.14",
2222
"@radix-ui/react-label": "^2.1.7",
2323
"@radix-ui/react-popover": "^1.1.15",
24+
"@radix-ui/react-progress": "^1.1.7",
2425
"@radix-ui/react-scroll-area": "^1.2.10",
2526
"@radix-ui/react-select": "^2.2.5",
2627
"@radix-ui/react-separator": "^1.1.7",
@@ -97,4 +98,4 @@
9798
}
9899
}
99100
}
100-
}
101+
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// Copyright (C) 2025 Intel Corporation
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
'use client'
5+
6+
import {
7+
DocumentationTemplate,
8+
DocumentationProps,
9+
} from '@/components/workloads/documentation'
10+
import Logs from '@/components/workloads/log'
11+
import { imageGenerationEndpoints } from '@/components/workloads/image-generation/api'
12+
import ImageGenerationDemo from '@/components/workloads/image-generation/demo'
13+
import ImageGenerationDocumentation from '@/components/workloads/image-generation/documentation'
14+
import WorkloadComponent from '@/components/workloads/workload'
15+
import {
16+
useCreateWorkload,
17+
useGetWorkloadByType,
18+
useUpdateWorkload,
19+
} from '@/hooks/use-workload'
20+
import { useMemo } from 'react'
21+
22+
import useDisclosure from '@/hooks/use-disclosure'
23+
import {
24+
Model,
25+
SettingsModal,
26+
} from '@/components/workloads/image-generation/settings'
27+
import { Button } from '@/components/ui/button'
28+
import { Settings } from 'lucide-react'
29+
import {
30+
IMAGE_GENERATION_WORKLOAD,
31+
IMAGE_GENERATION_TYPE,
32+
IMAGE_GENERATION_DESCRIPTION,
33+
IMAGE_GENERATION_MODELS,
34+
} from '@/lib/workloads/image-generation'
35+
import Endpoint from '@/components/workloads/endpoint'
36+
import { IMAGE_GENERATION_PORT } from '@/lib/constants'
37+
38+
const TYPE = IMAGE_GENERATION_TYPE
39+
const DESCRIPTION = IMAGE_GENERATION_DESCRIPTION
40+
41+
export default function ImageGenerationPage() {
42+
const { data: workload, isLoading } = useGetWorkloadByType('image-generation')
43+
const { isOpen, onClose, onOpen } = useDisclosure()
44+
45+
const updateWorkload = useUpdateWorkload()
46+
const createWorkload = useCreateWorkload()
47+
48+
const modelName = useMemo(() => {
49+
const model = workload?.model ?? IMAGE_GENERATION_MODELS[0].value
50+
return model
51+
}, [workload?.model])
52+
53+
const data: DocumentationProps = {
54+
overview: (
55+
<ImageGenerationDocumentation
56+
port={workload?.port ?? IMAGE_GENERATION_PORT}
57+
model={modelName}
58+
/>
59+
),
60+
endpoints: (
61+
<Endpoint
62+
apis={imageGenerationEndpoints}
63+
port={workload?.port ?? IMAGE_GENERATION_PORT}
64+
/>
65+
),
66+
}
67+
68+
const updateSettings = (device: string, model: Model) => {
69+
return new Promise((resolve) => {
70+
if (!workload) {
71+
createWorkload.mutate(
72+
{
73+
...IMAGE_GENERATION_WORKLOAD,
74+
device,
75+
model: model.value,
76+
status: 'inactive',
77+
},
78+
{
79+
onSuccess: () => resolve(true),
80+
onError: () => resolve(true),
81+
},
82+
)
83+
} else if (workload && !isLoading) {
84+
updateWorkload.mutateAsync(
85+
{
86+
id: workload?.id || 0,
87+
data: {
88+
device,
89+
model: model.value,
90+
status: workload?.status === 'active' ? 'restart' : 'inactive',
91+
},
92+
},
93+
{
94+
onSuccess: () => resolve(true),
95+
onError: () => resolve(true),
96+
},
97+
)
98+
} else {
99+
resolve(true)
100+
}
101+
})
102+
}
103+
104+
const SettingsButton = () => {
105+
return (
106+
<Button
107+
variant="secondary"
108+
size="icon"
109+
className="size-8"
110+
onClick={onOpen}
111+
>
112+
<Settings />
113+
</Button>
114+
)
115+
}
116+
117+
return (
118+
<>
119+
<SettingsModal
120+
task="Image Generation"
121+
isOpen={isOpen}
122+
onClose={onClose}
123+
availableModels={IMAGE_GENERATION_MODELS}
124+
updateSettings={updateSettings}
125+
selectedDevice={workload?.device || IMAGE_GENERATION_WORKLOAD.device}
126+
selectedModel={workload?.model || IMAGE_GENERATION_WORKLOAD.model}
127+
/>
128+
<WorkloadComponent
129+
title="Image Generation"
130+
settingsButton={<SettingsButton />}
131+
workload={workload}
132+
description={DESCRIPTION}
133+
workloadType={TYPE}
134+
demoElement={
135+
<ImageGenerationDemo
136+
disabled={!workload || workload.status !== 'active'}
137+
selectedModel={workload?.model || IMAGE_GENERATION_WORKLOAD.model}
138+
/>
139+
}
140+
docsElement={<DocumentationTemplate data={data} />}
141+
logsElement={<Logs name={`${workload?.name}_${workload?.id}`} />}
142+
isLoading={isLoading}
143+
/>
144+
</>
145+
)
146+
}

usecases/ai/edge-ai-demo-studio/frontend/src/collections/Workloads/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export const Workloads: CollectionConfig = {
2525
{ label: 'Text Generation', value: 'text-generation' },
2626
{ label: 'Text-To-Speech', value: 'text-to-speech' },
2727
{ label: 'Lipsync', value: 'lipsync' },
28+
{ label: 'Image Generation', value: 'image-generation' },
2829
],
2930
required: true,
3031
},
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (C) 2025 Intel Corporation
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
'use client'
5+
6+
import * as React from 'react'
7+
import * as ProgressPrimitive from '@radix-ui/react-progress'
8+
9+
import { cn } from '@/lib/utils'
10+
11+
function Progress({
12+
className,
13+
value,
14+
...props
15+
}: React.ComponentProps<typeof ProgressPrimitive.Root>) {
16+
return (
17+
<ProgressPrimitive.Root
18+
data-slot="progress"
19+
className={cn(
20+
'bg-primary/20 relative h-2 w-full overflow-hidden rounded-full',
21+
className,
22+
)}
23+
{...props}
24+
>
25+
<ProgressPrimitive.Indicator
26+
data-slot="progress-indicator"
27+
className="bg-primary h-full w-full flex-1 transition-all"
28+
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
29+
/>
30+
</ProgressPrimitive.Root>
31+
)
32+
}
33+
34+
export { Progress }

usecases/ai/edge-ai-demo-studio/frontend/src/components/workloads/endpoint.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface EndpointProps {
2424
exampleResponse: string
2525
queryParams?: string[]
2626
formData?: string[]
27+
output?: string
2728
}
2829

2930
export interface Parameter {
@@ -87,19 +88,21 @@ export default function Endpoint({
8788
(api.headers ? ' \\' : ''),
8889
api.headers
8990
? ` -H "${api.headers}"` +
90-
(api.formData || api.body ? ' \\' : '')
91+
(api.formData || api.body || api.output ? ' \\' : '')
9192
: null,
9293
api.formData
9394
? api.formData
9495
.map(
9596
(param, index) =>
9697
` -F "${param}"${index < api.formData!.length - 1 ? ' \\' : ''}`,
9798
)
98-
.join('\n')
99+
.join('\n') + (api.output ? ' \\' : '')
99100
: null,
100101
api.body
101-
? ` -d ${`'${JSON.stringify(JSON.parse(api.body), null, 4)}'`.split("}'").join(" }'")}`
102+
? ` -d ${`'${JSON.stringify(JSON.parse(api.body), null, 4)}'`.split("}'").join(" }'")}` +
103+
(api.output ? ' \\' : '')
102104
: null,
105+
api.output ? ` -o ${api.output}` : null,
103106
]
104107
.filter(Boolean)
105108
.join('\n')

0 commit comments

Comments
 (0)