@@ -33,7 +33,12 @@ import {
33
33
ModalVariant ,
34
34
ModalBody ,
35
35
ModalFooter ,
36
- ModalHeader
36
+ ModalHeader ,
37
+ Dropdown ,
38
+ DropdownItem ,
39
+ DropdownList ,
40
+ MenuToggle ,
41
+ MenuToggleElement
37
42
} from '@patternfly/react-core' ;
38
43
import {
39
44
ExternalLinkAltIcon ,
@@ -42,7 +47,8 @@ import {
42
47
CatalogIcon ,
43
48
PencilAltIcon ,
44
49
UploadIcon ,
45
- TrashIcon
50
+ TrashIcon ,
51
+ BalanceScaleIcon
46
52
} from '@patternfly/react-icons' ;
47
53
import { ExpandableSection } from '@patternfly/react-core/dist/esm/components/ExpandableSection/ExpandableSection' ;
48
54
import { v4 as uuidv4 } from 'uuid' ;
@@ -77,6 +83,18 @@ const DashboardNative: React.FunctionComponent = () => {
77
83
const [ isEditModalOpen , setIsEditModalOpen ] = React . useState ( false ) ;
78
84
const [ expandedFiles , setExpandedFiles ] = React . useState < Record < string , boolean > > ( { } ) ;
79
85
86
+ // State Variables for Evaluate Checkpoint
87
+ const [ isEvalModalOpen , setIsEvalModalOpen ] = React . useState < boolean > ( false ) ;
88
+ const [ checkpoints , setCheckpoints ] = React . useState < string [ ] > ( [ ] ) ;
89
+ const [ isCheckpointsLoading , setIsCheckpointsLoading ] = React . useState < boolean > ( false ) ;
90
+ const [ checkpointsError , setCheckpointsError ] = React . useState < string | null > ( null ) ;
91
+ const [ isDropdownOpen , setIsDropdownOpen ] = React . useState < boolean > ( false ) ;
92
+ const [ selectedCheckpoint , setSelectedCheckpoint ] = React . useState < string | null > ( null ) ;
93
+
94
+ // QnA eval result
95
+ const [ qnaEvalResult , setQnaEvalResult ] = React . useState < string > ( '' ) ;
96
+ const [ isQnaLoading , setIsQnaLoading ] = React . useState < boolean > ( false ) ;
97
+
80
98
const router = useRouter ( ) ;
81
99
82
100
// Fetch branches from the API route
@@ -285,6 +303,100 @@ const DashboardNative: React.FunctionComponent = () => {
285
303
} ) ) ;
286
304
} ;
287
305
306
+ const handleOpenEvalModal = ( ) => {
307
+ setIsEvalModalOpen ( true ) ;
308
+ fetchCheckpoints ( ) ;
309
+ } ;
310
+
311
+ const handleCloseEvalModal = ( ) => {
312
+ setIsEvalModalOpen ( false ) ;
313
+ setCheckpoints ( [ ] ) ;
314
+ setCheckpointsError ( null ) ;
315
+ setSelectedCheckpoint ( null ) ;
316
+ setQnaEvalResult ( '' ) ;
317
+ setIsQnaLoading ( false ) ;
318
+ } ;
319
+
320
+ // **New Function to Fetch Checkpoints from API Route**
321
+ const fetchCheckpoints = async ( ) => {
322
+ setIsCheckpointsLoading ( true ) ;
323
+ setCheckpointsError ( null ) ;
324
+ try {
325
+ const response = await fetch ( '/api/native/eval/checkpoints' ) ;
326
+ console . log ( 'Response status:' , response . status ) ;
327
+ const data = await response . json ( ) ;
328
+ console . log ( 'Checkpoints data:' , data ) ;
329
+
330
+ if ( response . ok ) {
331
+ // Assuming the API returns an array of checkpoints
332
+ if ( Array . isArray ( data ) && data . length > 0 ) {
333
+ setCheckpoints ( data ) ;
334
+ console . log ( 'Checkpoints set successfully:' , data ) ;
335
+ } else {
336
+ setCheckpoints ( [ ] ) ;
337
+ console . log ( 'No checkpoints returned from API.' ) ;
338
+ }
339
+ } else {
340
+ setCheckpointsError ( data . error || 'Failed to fetch checkpoints.' ) ;
341
+ console . error ( 'Error fetching checkpoints:' , data . error || 'Failed to fetch checkpoints.' ) ;
342
+ }
343
+ } catch ( error ) {
344
+ console . error ( 'Error fetching checkpoints:' , error ) ;
345
+ setCheckpointsError ( 'Unable to reach the checkpoints endpoint.' ) ;
346
+ } finally {
347
+ setIsCheckpointsLoading ( false ) ;
348
+ }
349
+ } ;
350
+
351
+ // Checkpoint select dropdown
352
+ const onDropdownToggle = ( isOpen : boolean ) => setIsDropdownOpen ( isOpen ) ;
353
+ const onSelectCheckpoint = ( event : React . MouseEvent < Element , MouseEvent > , selection : string ) => {
354
+ setSelectedCheckpoint ( selection ) ;
355
+ setIsDropdownOpen ( false ) ;
356
+ } ;
357
+
358
+ const handleEvaluateQnA = async ( ) => {
359
+ if ( ! selectedCheckpoint ) {
360
+ addDangerAlert ( 'Please select a checkpoint to evaluate.' ) ;
361
+ return ;
362
+ }
363
+
364
+ setIsQnaLoading ( true ) ;
365
+ setQnaEvalResult ( '' ) ;
366
+
367
+ // TODO: dynamically prepend the checkpoint path
368
+ const selectedModelDir = '/var/home/cloud-user/.local/share/instructlab/checkpoints/hf_format/' + selectedCheckpoint ;
369
+
370
+ console . log ( '[CLIENT] Sending to /api/native/eval/qna:' , selectedModelDir ) ;
371
+
372
+ try {
373
+ const res = await fetch ( '/api/native/eval/qna' , {
374
+ method : 'POST' ,
375
+ headers : { 'Content-Type' : 'application/json' } ,
376
+ body : JSON . stringify ( { selectedModelDir } )
377
+ } ) ;
378
+
379
+ const data = await res . json ( ) ;
380
+ console . log ( '[CLIENT] Response from /api/native/eval/qna:' , data ) ;
381
+
382
+ if ( ! res . ok ) {
383
+ addDangerAlert ( data . error || 'Failed to evaluate QnA.' ) ;
384
+ } else {
385
+ if ( data . result ) {
386
+ setQnaEvalResult ( data . result ) ;
387
+ addSuccessAlert ( 'QnA Evaluation succeeded!' ) ;
388
+ } else {
389
+ setQnaEvalResult ( 'Evaluation completed (no result field).' ) ;
390
+ }
391
+ }
392
+ } catch ( error ) {
393
+ console . error ( 'Error evaluating QnA:' , error ) ;
394
+ addDangerAlert ( 'Error evaluating QnA.' ) ;
395
+ } finally {
396
+ setIsQnaLoading ( false ) ;
397
+ }
398
+ } ;
399
+
288
400
return (
289
401
< div >
290
402
< PageBreadcrumb hasBodyWrapper = { false } >
@@ -397,6 +509,9 @@ const DashboardNative: React.FunctionComponent = () => {
397
509
< Tooltip aria = "none" aria-live = "polite" content = { < div > Delete</ div > } >
398
510
< Button icon = { < TrashIcon /> } variant = "plain" aria-label = "delete" onClick = { ( ) => handleDeleteContribution ( branch . name ) } />
399
511
</ Tooltip >
512
+ < Tooltip aria = "none" aria-live = "polite" content = { < div > Evaluate QnA Checkpoint</ div > } >
513
+ < Button icon = { < BalanceScaleIcon /> } variant = "plain" aria-label = "evaluate" onClick = { handleOpenEvalModal } />
514
+ </ Tooltip >
400
515
</ FlexItem >
401
516
</ Flex >
402
517
</ CardBody >
@@ -412,6 +527,84 @@ const DashboardNative: React.FunctionComponent = () => {
412
527
</ PageSection >
413
528
) }
414
529
530
+ { /* Evaluate Checkpoint Modal */ }
531
+ < Modal
532
+ variant = { ModalVariant . medium }
533
+ title = "Evaluate Checkpoint"
534
+ isOpen = { isEvalModalOpen }
535
+ onClose = { handleCloseEvalModal }
536
+ aria-labelledby = "evaluate-checkpoint-modal-title"
537
+ aria-describedby = "evaluate-checkpoint-modal-body"
538
+ >
539
+ < ModalHeader title = "Evaluate Checkpoint" />
540
+ < ModalBody id = "evaluate-checkpoint-modal-body" >
541
+ { isCheckpointsLoading ? (
542
+ < Spinner size = "lg" aria-label = "Loading checkpoints" />
543
+ ) : checkpointsError ? (
544
+ < Alert variant = "danger" title = { checkpointsError } isInline />
545
+ ) : (
546
+ < >
547
+ < div style = { { marginBottom : '1rem' } } >
548
+ < label style = { { display : 'block' , marginBottom : '0.4rem' } } > Select a Checkpoint:</ label >
549
+ < Dropdown
550
+ isOpen = { isDropdownOpen }
551
+ onSelect = { onSelectCheckpoint }
552
+ onOpenChange = { onDropdownToggle }
553
+ toggle = { ( toggleRef : React . Ref < MenuToggleElement > ) => (
554
+ < MenuToggle ref = { toggleRef } onClick = { ( ) => onDropdownToggle ( ! isDropdownOpen ) } isExpanded = { isDropdownOpen } >
555
+ { selectedCheckpoint || 'Select a Checkpoint' }
556
+ </ MenuToggle >
557
+ ) }
558
+ ouiaId = "EvaluateCheckpointDropdown"
559
+ shouldFocusToggleOnSelect
560
+ >
561
+ < DropdownList >
562
+ { checkpoints . length > 0 ? (
563
+ checkpoints . map ( ( checkpoint ) => (
564
+ < DropdownItem key = { checkpoint } value = { checkpoint } >
565
+ { checkpoint }
566
+ </ DropdownItem >
567
+ ) )
568
+ ) : (
569
+ < DropdownItem key = "no-checkpoints" isDisabled >
570
+ No checkpoints available
571
+ </ DropdownItem >
572
+ ) }
573
+ </ DropdownList >
574
+ </ Dropdown >
575
+ </ div >
576
+
577
+ { /* Display the evaluation result */ }
578
+ { qnaEvalResult && (
579
+ < div style = { { marginTop : '1rem' } } >
580
+ < b > Evaluation Output:</ b >
581
+ < pre
582
+ style = { {
583
+ marginTop : '0.5rem' ,
584
+ backgroundColor : '#f5f5f5' ,
585
+ padding : '1rem' ,
586
+ borderRadius : '4px' ,
587
+ maxHeight : '300px' ,
588
+ overflowY : 'auto'
589
+ } }
590
+ >
591
+ { qnaEvalResult }
592
+ </ pre >
593
+ </ div >
594
+ ) }
595
+ </ >
596
+ ) }
597
+ </ ModalBody >
598
+ < ModalFooter >
599
+ < Button key = "evaluateQnA" variant = "primary" onClick = { handleEvaluateQnA } isDisabled = { ! selectedCheckpoint || isQnaLoading } >
600
+ { isQnaLoading ? 'Evaluating...' : 'Evaluate' }
601
+ </ Button >
602
+ < Button key = "cancel" variant = "secondary" onClick = { handleCloseEvalModal } >
603
+ Cancel
604
+ </ Button >
605
+ </ ModalFooter >
606
+ </ Modal >
607
+
415
608
< Modal
416
609
variant = { ModalVariant . medium }
417
610
title = { `Files Contained in Branch: ${ diffData ?. branch } ` }
@@ -486,7 +679,7 @@ const DashboardNative: React.FunctionComponent = () => {
486
679
>
487
680
< ModalHeader title = "Deleting Contribution" labelId = "delete-contribution-modal-title" titleIconVariant = "warning" />
488
681
< ModalBody id = "delete-contribution-body-variant" >
489
- < p > are you sure you want to delete this contribution?</ p >
682
+ < p > Are you sure you want to delete this contribution?</ p >
490
683
</ ModalBody >
491
684
< ModalFooter >
492
685
< Button key = "confirm" variant = "primary" onClick = { ( ) => handleDeleteContributionConfirm ( ) } >
@@ -509,7 +702,7 @@ const DashboardNative: React.FunctionComponent = () => {
509
702
>
510
703
< ModalHeader title = "Publishing Contribution" labelId = "publish-contribution-modal-title" titleIconVariant = "warning" />
511
704
< ModalBody id = "publish-contribution-body-variant" >
512
- < p > are you sure you want to publish contribution to remote taxonomy repository present at : { taxonomyRepoDir } ?</ p >
705
+ < p > Are you sure you want to publish contribution to remote taxonomy repository present at : { taxonomyRepoDir } ?</ p >
513
706
</ ModalBody >
514
707
< ModalFooter >
515
708
< Button key = "confirm" variant = "primary" onClick = { ( ) => handlePublishContributionConfirm ( ) } >
0 commit comments