Skip to content

Commit 9633b61

Browse files
Fix dark mode issues
Signed-off-by: Jeffrey Phillips <jephilli@redhat.com>
1 parent a17bf19 commit 9633b61

File tree

7 files changed

+689
-637
lines changed

7 files changed

+689
-637
lines changed

src/app/experimental/fine-tune/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import * as React from 'react';
55
import '@patternfly/react-core/dist/styles/base.css';
66
import { AppLayout, FeaturePages } from '@/components/AppLayout';
7-
import FineTuning from '@/components/Experimental/FineTuning';
7+
import FineTuning from '@/components/Experimental/FineTuning/FineTuningJobs';
88

99
const FineTune: React.FunctionComponent = () => {
1010
return (

src/components/Contribute/Knowledge/KnowledgeSeedExamples/KnowledgeSeedExamples.scss

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@
88
border-style: solid;
99
&.pf-m-info {
1010
background-color: var(--pf-t--color--blue--10);
11+
--pf-v6-c-alert--m-info__icon--Color: var(--pf-t--color--black);
12+
--pf-v6-c-alert--m-info__title--Color: var(--pf-t--color--black);
1113
}
1214
&.pf-m-warning {
1315
background-color: #fdf7e7;
16+
--pf-v6-c-alert--m-warning__title--Color: var(--pf-t--color--black);
1417
}
1518
&.pf-m-danger {
1619
background-color: #faeae8;
20+
--pf-v6-c-alert--m-danger__title--Color: var(--pf-t--color--black);
1721
}
1822
}
1923
}
@@ -32,15 +36,15 @@
3236
padding-bottom: 1rem;
3337

3438
&:not(:focus-visible) {
35-
outline: var(--pf-t--global--background--color--primary--default) auto 1px;
39+
outline: transparent auto 0;
3640
}
3741
}
3842
}
3943
&__text-help {
4044
position: absolute;
4145
bottom: calc(var(--pf-t--global--spacer--xs) + 3px);
4246
left: var(--pf-t--global--spacer--md);
43-
background-color: var(--pf-t--global--background--color--primary--default);
47+
background-color: transparent;
4448
width: calc(100% - 2 * var(--pf-t--global--spacer--lg));
4549
}
4650
}
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
// src/components/Experimental/FineTuning/index.tsx
2+
'use client';
3+
4+
import React from 'react';
5+
import {
6+
Modal,
7+
Form,
8+
FormGroup,
9+
Dropdown,
10+
DropdownItem,
11+
DropdownList,
12+
MenuToggle,
13+
MenuToggleElement,
14+
NumberInput,
15+
Button,
16+
Alert,
17+
ModalHeader,
18+
ModalBody,
19+
ModalFooter,
20+
Flex,
21+
FlexItem
22+
} from '@patternfly/react-core';
23+
import { Model, Branch, Job } from '@/components/Experimental/FineTuning/types';
24+
25+
interface Props {
26+
models: Model[];
27+
branches: Branch[];
28+
onClose: (newJob?: Job) => void;
29+
}
30+
31+
const AddFineTuningJobModal: React.FC<Props> = ({ models, branches, onClose }) => {
32+
const [errorMessage, setErrorMessage] = React.useState<string>(
33+
!models.length || !branches.length ? 'No data available for creating fine tuning jobs.' : ''
34+
);
35+
36+
const [selectedModel, setSelectedModel] = React.useState<string>('');
37+
const [selectedBranch, setSelectedBranch] = React.useState<string>('');
38+
const [selectedEpochs, setSelectedEpochs] = React.useState<number | ''>('');
39+
40+
const [isModelDropdownOpen, setIsModelDropdownOpen] = React.useState<boolean>(false);
41+
const [isBranchDropdownOpen, setIsBranchDropdownOpen] = React.useState<boolean>(false);
42+
43+
const isValid = !!selectedBranch && !!selectedModel && selectedEpochs;
44+
45+
const handleGenerateClick = async () => {
46+
if (!selectedModel || !selectedBranch) {
47+
setErrorMessage('Please select both a model and a branch.');
48+
return;
49+
}
50+
if (!selectedEpochs) {
51+
setErrorMessage('Please enter the number of epochs.');
52+
return;
53+
}
54+
try {
55+
const response = await fetch('/api/fine-tune/data/generate', {
56+
method: 'POST',
57+
headers: { 'Content-Type': 'application/json' },
58+
body: JSON.stringify({ modelName: selectedModel, branchName: selectedBranch, epochs: selectedEpochs }), // Include epochs
59+
cache: 'no-cache'
60+
});
61+
const result = await response.json();
62+
if (response.ok) {
63+
const newJob: Job = {
64+
job_id: result.job_id,
65+
status: 'running',
66+
type: result.job_id.startsWith('g-') ? 'generate' : result.job_id.startsWith('p-') ? 'pipeline' : 'train',
67+
start_time: new Date().toISOString()
68+
};
69+
onClose(newJob);
70+
} else {
71+
setErrorMessage(result.error || 'Failed to start generate job');
72+
}
73+
} catch (error) {
74+
console.error('Error starting generate job:', error);
75+
setErrorMessage('Error starting generate job');
76+
}
77+
};
78+
79+
const handleTrainClick = async () => {
80+
if (!selectedModel || !selectedBranch) {
81+
setErrorMessage('Please select both a model and a branch.');
82+
return;
83+
}
84+
if (!selectedEpochs) {
85+
setErrorMessage('Please enter the number of epochs.');
86+
return;
87+
}
88+
89+
try {
90+
console.log('Sending train request with:', {
91+
modelName: selectedModel,
92+
branchName: selectedBranch,
93+
epochs: selectedEpochs
94+
});
95+
96+
const response = await fetch('/api/fine-tune/model/train', {
97+
method: 'POST',
98+
headers: { 'Content-Type': 'application/json' },
99+
body: JSON.stringify({
100+
modelName: selectedModel,
101+
branchName: selectedBranch,
102+
epochs: selectedEpochs
103+
}),
104+
cache: 'no-cache'
105+
});
106+
const result = await response.json();
107+
if (response.ok) {
108+
const newJob: Job = {
109+
job_id: result.job_id,
110+
status: 'running',
111+
type: result.job_id.startsWith('g-') ? 'generate' : result.job_id.startsWith('p-') ? 'pipeline' : 'train',
112+
start_time: new Date().toISOString()
113+
};
114+
onClose(newJob);
115+
} else {
116+
setErrorMessage(result.error || 'Failed to start train job');
117+
}
118+
} catch (error) {
119+
console.error('Error starting train job:', error);
120+
setErrorMessage('Error starting train job');
121+
}
122+
};
123+
124+
const handlePipelineClick = async () => {
125+
if (!selectedModel || !selectedBranch) {
126+
setErrorMessage('Please select both a model and a branch.');
127+
return;
128+
}
129+
if (!selectedEpochs) {
130+
setErrorMessage('Please enter the number of epochs.');
131+
return;
132+
}
133+
134+
try {
135+
console.debug('Sending pipeline generate-train request with:', {
136+
modelName: selectedModel,
137+
branchName: selectedBranch,
138+
epochs: selectedEpochs
139+
});
140+
const response = await fetch('/api/fine-tune/pipeline/generate-train', {
141+
method: 'POST',
142+
headers: { 'Content-Type': 'application/json' },
143+
body: JSON.stringify({ modelName: selectedModel, branchName: selectedBranch, epochs: selectedEpochs }), // Include epochs
144+
cache: 'no-cache'
145+
});
146+
const result = await response.json();
147+
if (response.ok && result.pipeline_job_id) {
148+
// Add the new job to the job list
149+
const newJob: Job = {
150+
job_id: result.pipeline_job_id,
151+
status: 'running',
152+
type: 'pipeline',
153+
branch: selectedBranch,
154+
start_time: new Date().toISOString()
155+
};
156+
onClose(newJob);
157+
console.debug('New pipeline job added:', newJob);
158+
} else {
159+
setErrorMessage(result.error || 'Failed to start generate-train pipeline');
160+
console.warn('Pipeline action failed:', result.error);
161+
}
162+
} catch (error) {
163+
console.error('Error starting generate-train pipeline job:', error);
164+
setErrorMessage('Error starting generate-train pipeline job');
165+
}
166+
};
167+
168+
return (
169+
<Modal isOpen onClose={() => onClose()} variant="small">
170+
<ModalHeader title="Create fine tuning job" />
171+
<ModalBody>
172+
<Flex direction={{ default: 'column' }} gap={{ default: 'gapMd' }}>
173+
{errorMessage ? (
174+
<FlexItem>
175+
<Alert className="pf-v6-u-mt-md" variant="danger" title={errorMessage} isInline />
176+
</FlexItem>
177+
) : null}
178+
<FlexItem>
179+
<Form>
180+
<FormGroup label="Select Git Branch" isRequired fieldId="branch-select">
181+
<Dropdown
182+
isOpen={isBranchDropdownOpen}
183+
onSelect={(_event, value) => {
184+
setSelectedBranch(value as string);
185+
setIsBranchDropdownOpen(false);
186+
}}
187+
onOpenChange={(isOpen) => setIsBranchDropdownOpen(isOpen)}
188+
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
189+
<MenuToggle ref={toggleRef} onClick={() => setIsBranchDropdownOpen(!isBranchDropdownOpen)} isExpanded={isBranchDropdownOpen}>
190+
{selectedBranch || 'Select a branch'}
191+
</MenuToggle>
192+
)}
193+
>
194+
<DropdownList>
195+
{branches.map((branch) => (
196+
<DropdownItem key={branch.name} value={branch.name}>
197+
{branch.name}
198+
</DropdownItem>
199+
))}
200+
</DropdownList>
201+
</Dropdown>
202+
</FormGroup>
203+
204+
<FormGroup label="Select Base Model" isRequired fieldId="model-select">
205+
<Dropdown
206+
isOpen={isModelDropdownOpen}
207+
onSelect={(_event, value) => {
208+
setSelectedModel(value as string);
209+
setIsModelDropdownOpen(false);
210+
}}
211+
onOpenChange={(isOpen) => setIsModelDropdownOpen(isOpen)}
212+
toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
213+
<MenuToggle ref={toggleRef} onClick={() => setIsModelDropdownOpen(!isModelDropdownOpen)} isExpanded={isModelDropdownOpen}>
214+
{selectedModel || 'Select a model'}
215+
</MenuToggle>
216+
)}
217+
>
218+
<DropdownList>
219+
{models.map((model) => (
220+
<DropdownItem key={model.name} value={model.name}>
221+
{model.name}
222+
</DropdownItem>
223+
))}
224+
</DropdownList>
225+
</Dropdown>
226+
</FormGroup>
227+
228+
{/* New FormGroup for Epoch Selection using NumberInput */}
229+
<FormGroup label="Number of Epochs" isRequired fieldId="epochs-input">
230+
<NumberInput
231+
value={selectedEpochs || ''}
232+
onMinus={() => {
233+
const newValue = typeof selectedEpochs === 'number' ? selectedEpochs - 1 : 0;
234+
setSelectedEpochs(newValue >= 1 ? newValue : 1); // Ensure minimum of 1
235+
}}
236+
onChange={(event: React.FormEvent<HTMLInputElement>) => {
237+
const value = (event.target as HTMLInputElement).value;
238+
const parsedValue = value === '' ? '' : Number(value);
239+
if (parsedValue === '' || (Number.isInteger(parsedValue) && parsedValue > 0)) {
240+
setSelectedEpochs(parsedValue);
241+
}
242+
}}
243+
onPlus={() => {
244+
const newValue = typeof selectedEpochs === 'number' ? selectedEpochs + 1 : 1;
245+
setSelectedEpochs(newValue);
246+
}}
247+
inputName="epochs"
248+
inputAriaLabel="Number of Epochs"
249+
minusBtnAriaLabel="decrease number of epochs"
250+
plusBtnAriaLabel="increase number of epochs"
251+
min={1}
252+
/>
253+
</FormGroup>
254+
</Form>
255+
</FlexItem>
256+
</Flex>
257+
</ModalBody>
258+
<ModalFooter>
259+
<Button variant="secondary" isDisabled={!isValid} onClick={handleGenerateClick}>
260+
Generate
261+
</Button>
262+
<Button variant="secondary" isDisabled={!isValid} onClick={handleTrainClick}>
263+
Train
264+
</Button>
265+
<Button variant="secondary" isDisabled={!isValid} onClick={handlePipelineClick}>
266+
Generate &amp; Train
267+
</Button>
268+
<Button variant="link" onClick={() => onClose()}>
269+
Cancel
270+
</Button>
271+
</ModalFooter>
272+
</Modal>
273+
);
274+
};
275+
276+
export default AddFineTuningJobModal;

0 commit comments

Comments
 (0)