@@ -5,8 +5,10 @@ import {
55 PullModelRequest ,
66 PullModelResponse ,
77} from '@/types/model' ;
8- import { useState , useRef } from 'react' ;
8+ import { useState , useRef , useEffect } from 'react' ;
99import Spinner from '../Spinner' ;
10+ import { toast } from 'react-toastify' ;
11+ import Toast from '../Toast' ;
1012
1113interface ModelModalProps {
1214 loading : boolean ;
@@ -37,6 +39,18 @@ export default function ModelModal({ loading, setLoading }: ModelModalProps) {
3739 const pullModelNameRef = useRef < HTMLInputElement > ( null ) ;
3840 const deleteModelNameRef = useRef < HTMLInputElement > ( null ) ;
3941
42+ useEffect ( ( ) => {
43+ if ( error ) {
44+ toast . error ( error ) ;
45+ }
46+ } , [ error ] ) ;
47+
48+ useEffect ( ( ) => {
49+ if ( success ) {
50+ toast . success ( success ) ;
51+ }
52+ } , [ success ] ) ;
53+
4054 const clearValues = ( ) => {
4155 setSuccess ( null ) ;
4256 setError ( null ) ;
@@ -49,79 +63,88 @@ export default function ModelModal({ loading, setLoading }: ModelModalProps) {
4963 clearValues ( ) ;
5064 } ;
5165
52- // TODO: Fix stream request
5366 const sendStreamedRequest = async ( body : CreateModelRequest | PullModelRequest ) => {
5467 setLoading ( true ) ;
5568 clearValues ( ) ;
5669 try {
57- const initialResponse = await fetch ( `/api/models/${ activeTab } ` , {
58- method : 'POST' ,
59- headers : {
60- 'Content-Type' : 'application/json' ,
61- } ,
62- body : JSON . stringify ( body ) ,
63- } ) ;
70+ const initialResponse = await fetchRequest ( body ) ;
71+ const reader = initialResponse . body ?. getReader ( ) ;
72+ if ( ! reader ) throw new Error ( 'Failed to get reader from response body' ) ;
6473
65- if ( ! initialResponse . ok || ! initialResponse . body ) {
66- const data = await initialResponse . json ( ) ;
67- throw new Error ( data . error || `Failed to ${ activeTab } the model.` ) ;
68- }
74+ await processStream ( reader ) ;
75+ } catch ( error : unknown ) {
76+ handleError ( error ) ;
77+ } finally {
78+ setLoading ( false ) ;
79+ }
80+ } ;
81+
82+ const fetchRequest = async ( body : CreateModelRequest | PullModelRequest ) => {
83+ const response = await fetch ( `/api/models/${ activeTab } ` , {
84+ method : 'POST' ,
85+ headers : {
86+ 'Content-Type' : 'application/json' ,
87+ } ,
88+ body : JSON . stringify ( body ) ,
89+ } ) ;
90+
91+ if ( ! response . ok || ! response . body ) {
92+ const data = await response . json ( ) ;
93+ throw new Error ( data . error || `Failed to ${ activeTab } the model` ) ;
94+ }
95+
96+ return response ;
97+ } ;
6998
70- const reader = initialResponse . body . getReader ( ) ;
71- const decoder = new TextDecoder ( 'utf-8' ) ;
72- let buffer = '' ;
73- let done = false ;
74-
75- while ( ! done ) {
76- const { value, done : streamDone } = await reader . read ( ) ;
77- done = streamDone ;
78- buffer += decoder . decode ( value , { stream : true } ) ;
79-
80- const lines = buffer . split ( / \r ? \n / ) ;
81- buffer = lines . pop ( ) || '' ;
82-
83- for ( const line of lines ) {
84- if ( line . trim ( ) ) {
85- try {
86- const response : CreateModelResponse | PullModelResponse = JSON . parse ( line ) ;
87- setLogs ( ( prevLogs ) => [ ...prevLogs , response . status ] ) ;
88-
89- if ( response . status === 'success' ) {
90- setSuccess ( getSuccessMessage ( ) ) ;
91- done = true ;
92- break ;
93- }
94- } catch ( parseError : unknown ) {
95- if ( parseError instanceof Error ) {
96- setError ( parseError . message ) ;
97- done = true ;
98- }
99+ const processStream = async ( reader : ReadableStreamDefaultReader < Uint8Array > ) => {
100+ const decoder = new TextDecoder ( 'utf-8' ) ;
101+ let buffer = '' ;
102+ let done = false ;
103+
104+ while ( ! done ) {
105+ const { value, done : streamDone } = await reader . read ( ) ;
106+ done = streamDone ;
107+ buffer += decoder . decode ( value , { stream : true } ) ;
108+
109+ const lines = buffer . split ( / \r ? \n / ) ;
110+ buffer = lines . pop ( ) || '' ;
111+
112+ for ( const line of lines ) {
113+ if ( line . trim ( ) ) {
114+ try {
115+ const response : CreateModelResponse | PullModelResponse = JSON . parse ( line ) ;
116+ setLogs ( ( prevLogs ) => [ ...prevLogs , response . status ] ) ;
117+
118+ if ( response . status === 'success' ) {
119+ setSuccess ( getSuccessMessage ( ) ) ;
120+ done = true ;
121+ break ;
99122 }
123+ } catch ( parseError : unknown ) {
124+ handleError ( parseError ) ;
125+ done = true ;
100126 }
101127 }
102128 }
129+ }
103130
104- // Remaining data in the buffer
105- if ( buffer . trim ( ) ) {
106- try {
107- const response : CreateModelResponse | PullModelResponse = JSON . parse ( buffer ) ;
108- setLogs ( ( prevLogs ) => [ ...prevLogs , response . status ] ) ;
131+ if ( buffer . trim ( ) ) {
132+ try {
133+ const response : CreateModelResponse | PullModelResponse = JSON . parse ( buffer ) ;
134+ setLogs ( ( prevLogs ) => [ ...prevLogs , response . status ] ) ;
109135
110- if ( response . status === 'success' ) {
111- setSuccess ( getSuccessMessage ( ) ) ;
112- }
113- } catch ( parseError : unknown ) {
114- if ( parseError instanceof Error ) {
115- setError ( parseError . message ) ;
116- }
136+ if ( response . status === 'success' ) {
137+ setSuccess ( getSuccessMessage ( ) ) ;
117138 }
139+ } catch ( parseError : unknown ) {
140+ handleError ( parseError ) ;
118141 }
119- } catch ( error : unknown ) {
120- if ( error instanceof Error ) {
121- setError ( error . message ) ;
122- }
123- } finally {
124- setLoading ( false ) ;
142+ }
143+ } ;
144+
145+ const handleError = ( error : unknown ) => {
146+ if ( error instanceof Error ) {
147+ setError ( error . message ) ;
125148 }
126149 } ;
127150
@@ -194,13 +217,13 @@ export default function ModelModal({ loading, setLoading }: ModelModalProps) {
194217 const getText = ( ) => {
195218 switch ( activeTab ) {
196219 case 'create' :
197- return loading ? 'Creating' : 'Create Model' ;
220+ return loading ? 'Creating... ' : 'Create Model' ;
198221 case 'pull' :
199- return loading ? 'Pulling' : 'Pull Model' ;
222+ return loading ? 'Pulling... ' : 'Pull Model' ;
200223 case 'delete' :
201- return loading ? 'Deleting' : 'Delete Model' ;
224+ return loading ? 'Deleting... ' : 'Delete Model' ;
202225 default :
203- return loading ? 'Processing' : 'Submit' ;
226+ return loading ? 'Processing... ' : 'Submit' ;
204227 }
205228 } ;
206229 return loading ? (
@@ -321,18 +344,7 @@ export default function ModelModal({ loading, setLoading }: ModelModalProps) {
321344 </ ul >
322345 </ div >
323346 ) }
324-
325- { success && (
326- < div className = "mt-4" >
327- < p className = "text-sm text-green-500" > { success } </ p >
328- </ div >
329- ) }
330-
331- { error && (
332- < div className = "mt-4" >
333- < p className = "text-sm text-red-500" > { error } </ p >
334- </ div >
335- ) }
347+ < Toast />
336348 </ div >
337349 </ >
338350 ) ;
0 commit comments