Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
adjust for event
Browse files
ui/src/app/api/hf-jobs/route.ts
CHANGED
@@ -7,7 +7,7 @@ import { tmpdir } from 'os';
|
|
7 |
export async function POST(request: NextRequest) {
|
8 |
try {
|
9 |
const body = await request.json();
|
10 |
-
const { action, token, hardware, namespace, jobConfig, datasetRepo } = body;
|
11 |
|
12 |
switch (action) {
|
13 |
case 'checkStatus':
|
@@ -59,7 +59,12 @@ export async function POST(request: NextRequest) {
|
|
59 |
await writeFile(scriptPath, uvScript);
|
60 |
|
61 |
// Submit HF job using uv run
|
62 |
-
const jobId = await submitHFJobUV(
|
|
|
|
|
|
|
|
|
|
|
63 |
|
64 |
return NextResponse.json({
|
65 |
success: true,
|
@@ -597,7 +602,7 @@ def generate_model_card_readme(repo_id: str, config: dict, model_name: str, cura
|
|
597 |
tags.append("sd3")
|
598 |
|
599 |
# Add LoRA-specific tags
|
600 |
-
tags.extend(["lora", "diffusers", "template:sd-lora", "ai-toolkit"
|
601 |
|
602 |
# Generate widgets and gallery section from sample images
|
603 |
curated_samples = curated_samples or []
|
@@ -778,7 +783,7 @@ if __name__ == "__main__":
|
|
778 |
`;
|
779 |
}
|
780 |
|
781 |
-
async function submitHFJobUV(token: string, hardware: string, scriptPath: string): Promise<string> {
|
782 |
return new Promise((resolve, reject) => {
|
783 |
// Ensure token is available
|
784 |
if (!token) {
|
@@ -787,17 +792,25 @@ async function submitHFJobUV(token: string, hardware: string, scriptPath: string
|
|
787 |
}
|
788 |
|
789 |
console.log('Setting up environment with HF_TOKEN for job submission');
|
790 |
-
|
791 |
-
|
|
|
792 |
// Use hf jobs uv run command with timeout and detach to get job ID
|
793 |
-
const
|
794 |
'jobs', 'uv', 'run',
|
795 |
'--flavor', hardware,
|
796 |
'--timeout', '5h',
|
797 |
'--secrets', 'HF_TOKEN',
|
798 |
-
'--detach'
|
799 |
-
|
800 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
801 |
env: {
|
802 |
...process.env,
|
803 |
HF_TOKEN: token
|
|
|
7 |
export async function POST(request: NextRequest) {
|
8 |
try {
|
9 |
const body = await request.json();
|
10 |
+
const { action, token, hardware, namespace, jobConfig, datasetRepo, participateHackathon } = body;
|
11 |
|
12 |
switch (action) {
|
13 |
case 'checkStatus':
|
|
|
59 |
await writeFile(scriptPath, uvScript);
|
60 |
|
61 |
// Submit HF job using uv run
|
62 |
+
const jobId = await submitHFJobUV(
|
63 |
+
token,
|
64 |
+
hardware,
|
65 |
+
scriptPath,
|
66 |
+
participateHackathon ? 'lora-training-frenzi' : undefined
|
67 |
+
);
|
68 |
|
69 |
return NextResponse.json({
|
70 |
success: true,
|
|
|
602 |
tags.append("sd3")
|
603 |
|
604 |
# Add LoRA-specific tags
|
605 |
+
tags.extend(["lora", "diffusers", "template:sd-lora", "ai-toolkit"])
|
606 |
|
607 |
# Generate widgets and gallery section from sample images
|
608 |
curated_samples = curated_samples or []
|
|
|
783 |
`;
|
784 |
}
|
785 |
|
786 |
+
async function submitHFJobUV(token: string, hardware: string, scriptPath: string, namespaceOverride?: string): Promise<string> {
|
787 |
return new Promise((resolve, reject) => {
|
788 |
// Ensure token is available
|
789 |
if (!token) {
|
|
|
792 |
}
|
793 |
|
794 |
console.log('Setting up environment with HF_TOKEN for job submission');
|
795 |
+
const namespaceArgs = namespaceOverride ? ` --namespace ${namespaceOverride}` : '';
|
796 |
+
console.log(`Command: hf jobs uv run --flavor ${hardware} --timeout 5h --secrets HF_TOKEN --detach${namespaceArgs} ${scriptPath}`);
|
797 |
+
|
798 |
// Use hf jobs uv run command with timeout and detach to get job ID
|
799 |
+
const args = [
|
800 |
'jobs', 'uv', 'run',
|
801 |
'--flavor', hardware,
|
802 |
'--timeout', '5h',
|
803 |
'--secrets', 'HF_TOKEN',
|
804 |
+
'--detach'
|
805 |
+
];
|
806 |
+
|
807 |
+
if (namespaceOverride) {
|
808 |
+
args.push('--namespace', namespaceOverride);
|
809 |
+
}
|
810 |
+
|
811 |
+
args.push(scriptPath);
|
812 |
+
|
813 |
+
const childProcess = spawn('hf', args, {
|
814 |
env: {
|
815 |
...process.env,
|
816 |
HF_TOKEN: token
|
ui/src/app/dashboard/page.tsx
CHANGED
@@ -19,6 +19,13 @@ export default function Dashboard() {
|
|
19 |
<div className="flex-1" />
|
20 |
</TopBar>
|
21 |
<MainContent>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
<div className="border border-gray-800 rounded-xl bg-gray-900 p-6 flex flex-col gap-4">
|
23 |
<div>
|
24 |
<h2 className="text-xl font-semibold text-gray-100">
|
|
|
19 |
<div className="flex-1" />
|
20 |
</TopBar>
|
21 |
<MainContent>
|
22 |
+
<div className="mb-6">
|
23 |
+
<img
|
24 |
+
src="https://d112y698adiu2z.cloudfront.net/photos/production/challenge_photos/003/754/358/datas/full_width.png"
|
25 |
+
alt="FLUX.1 Kontext Dev Hackathon banner"
|
26 |
+
className="w-full rounded-lg border border-gray-800"
|
27 |
+
/>
|
28 |
+
</div>
|
29 |
<div className="border border-gray-800 rounded-xl bg-gray-900 p-6 flex flex-col gap-4">
|
30 |
<div>
|
31 |
<h2 className="text-xl font-semibold text-gray-100">
|
ui/src/app/jobs/new/SimpleJob.tsx
CHANGED
@@ -125,6 +125,16 @@ export default function SimpleJob({
|
|
125 |
return newQuantizationOptions;
|
126 |
}, [modelArch]);
|
127 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
128 |
return (
|
129 |
<>
|
130 |
<form onSubmit={handleSubmit} className="space-y-8">
|
@@ -956,6 +966,7 @@ export default function SimpleJob({
|
|
956 |
<div className="mt-8">
|
957 |
<HFJobsWorkflow
|
958 |
jobConfig={jobConfig}
|
|
|
959 |
onComplete={(jobId, localJobId) => {
|
960 |
console.log('HF Job submitted:', jobId, 'Local job ID:', localJobId);
|
961 |
if (onHFJobComplete) {
|
|
|
125 |
return newQuantizationOptions;
|
126 |
}, [modelArch]);
|
127 |
|
128 |
+
const isFluxKontextDev = useMemo(() => {
|
129 |
+
try {
|
130 |
+
const process = jobConfig?.config?.process?.[0];
|
131 |
+
const model = process?.model ?? {};
|
132 |
+
return model?.arch === 'flux_kontext' && model?.name_or_path === 'black-forest-labs/FLUX.1-Kontext-dev';
|
133 |
+
} catch (error) {
|
134 |
+
return false;
|
135 |
+
}
|
136 |
+
}, [jobConfig]);
|
137 |
+
|
138 |
return (
|
139 |
<>
|
140 |
<form onSubmit={handleSubmit} className="space-y-8">
|
|
|
966 |
<div className="mt-8">
|
967 |
<HFJobsWorkflow
|
968 |
jobConfig={jobConfig}
|
969 |
+
hackathonEligible={isFluxKontextDev}
|
970 |
onComplete={(jobId, localJobId) => {
|
971 |
console.log('HF Job submitted:', jobId, 'Local job ID:', localJobId);
|
972 |
if (onHFJobComplete) {
|
ui/src/components/HFJobsWorkflow.tsx
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
'use client';
|
2 |
|
3 |
-
import { useState } from 'react';
|
4 |
import { Button } from '@headlessui/react';
|
5 |
import { SelectInput, TextInput, Checkbox } from '@/components/formInputs';
|
6 |
import Card from '@/components/Card';
|
@@ -130,11 +130,12 @@ import { useAuth } from '@/contexts/AuthContext';
|
|
130 |
interface HFJobsWorkflowProps {
|
131 |
jobConfig: JobConfig;
|
132 |
onComplete: (jobId: string, localJobId?: string) => void;
|
|
|
133 |
}
|
134 |
|
135 |
type Step = 'validate' | 'upload' | 'submit' | 'complete';
|
136 |
|
137 |
-
export default function HFJobsWorkflow({ jobConfig, onComplete }: HFJobsWorkflowProps) {
|
138 |
const { settings } = useSettings();
|
139 |
const { token: authToken } = useAuth();
|
140 |
const [defaultNamespace, setDefaultNamespace] = useState('');
|
@@ -149,6 +150,13 @@ export default function HFJobsWorkflow({ jobConfig, onComplete }: HFJobsWorkflow
|
|
149 |
const [hardware, setHardware] = useState(settings.HF_JOBS_DEFAULT_HARDWARE || 'a100-large');
|
150 |
const [namespace, setNamespace] = useState(settings.HF_JOBS_NAMESPACE || '');
|
151 |
const [autoUpload, setAutoUpload] = useState(true);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
|
153 |
// Progress state
|
154 |
const [validationResult, setValidationResult] = useState<any>(null);
|
@@ -301,6 +309,7 @@ export default function HFJobsWorkflow({ jobConfig, onComplete }: HFJobsWorkflow
|
|
301 |
namespace: resolvedNamespace,
|
302 |
jobConfig,
|
303 |
datasetRepo,
|
|
|
304 |
});
|
305 |
|
306 |
if (response.data.success) {
|
@@ -385,6 +394,26 @@ export default function HFJobsWorkflow({ jobConfig, onComplete }: HFJobsWorkflow
|
|
385 |
return (
|
386 |
<Card title="Validate HF Token">
|
387 |
<div className="space-y-4">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
388 |
<p className="text-sm text-gray-400">
|
389 |
First, let's validate your Hugging Face token and get your username for dataset uploads.
|
390 |
</p>
|
|
|
1 |
'use client';
|
2 |
|
3 |
+
import { useEffect, useState } from 'react';
|
4 |
import { Button } from '@headlessui/react';
|
5 |
import { SelectInput, TextInput, Checkbox } from '@/components/formInputs';
|
6 |
import Card from '@/components/Card';
|
|
|
130 |
interface HFJobsWorkflowProps {
|
131 |
jobConfig: JobConfig;
|
132 |
onComplete: (jobId: string, localJobId?: string) => void;
|
133 |
+
hackathonEligible?: boolean;
|
134 |
}
|
135 |
|
136 |
type Step = 'validate' | 'upload' | 'submit' | 'complete';
|
137 |
|
138 |
+
export default function HFJobsWorkflow({ jobConfig, onComplete, hackathonEligible = false }: HFJobsWorkflowProps) {
|
139 |
const { settings } = useSettings();
|
140 |
const { token: authToken } = useAuth();
|
141 |
const [defaultNamespace, setDefaultNamespace] = useState('');
|
|
|
150 |
const [hardware, setHardware] = useState(settings.HF_JOBS_DEFAULT_HARDWARE || 'a100-large');
|
151 |
const [namespace, setNamespace] = useState(settings.HF_JOBS_NAMESPACE || '');
|
152 |
const [autoUpload, setAutoUpload] = useState(true);
|
153 |
+
const [participateHackathon, setParticipateHackathon] = useState(false);
|
154 |
+
|
155 |
+
useEffect(() => {
|
156 |
+
if (!hackathonEligible && participateHackathon) {
|
157 |
+
setParticipateHackathon(false);
|
158 |
+
}
|
159 |
+
}, [hackathonEligible, participateHackathon]);
|
160 |
|
161 |
// Progress state
|
162 |
const [validationResult, setValidationResult] = useState<any>(null);
|
|
|
309 |
namespace: resolvedNamespace,
|
310 |
jobConfig,
|
311 |
datasetRepo,
|
312 |
+
participateHackathon: hackathonEligible && participateHackathon,
|
313 |
});
|
314 |
|
315 |
if (response.data.success) {
|
|
|
394 |
return (
|
395 |
<Card title="Validate HF Token">
|
396 |
<div className="space-y-4">
|
397 |
+
{hackathonEligible && (
|
398 |
+
<Checkbox
|
399 |
+
label={
|
400 |
+
<span>
|
401 |
+
Participating in the free FLUX.1 Kontext Dev Hackathon? If so, please join this
|
402 |
+
<a
|
403 |
+
href="https://huggingface.co/organizations/lora-training-frenzi/share/kEyyVNQXBPWqmARdwHFVdIiFqqONHZPOtz"
|
404 |
+
target="_blank"
|
405 |
+
rel="noopener noreferrer"
|
406 |
+
className="text-blue-400 underline mx-1"
|
407 |
+
>
|
408 |
+
organization
|
409 |
+
</a>
|
410 |
+
before starting your job.
|
411 |
+
</span>
|
412 |
+
}
|
413 |
+
checked={participateHackathon}
|
414 |
+
onChange={value => setParticipateHackathon(value)}
|
415 |
+
/>
|
416 |
+
)}
|
417 |
<p className="text-sm text-gray-400">
|
418 |
First, let's validate your Hugging Face token and get your username for dataset uploads.
|
419 |
</p>
|