@@ -99,10 +99,9 @@ export class ModelsCliUsecases {
9999 }
100100
101101 async pullModel ( modelId : string ) {
102- if ( modelId . includes ( '/' ) ) {
102+ if ( modelId . includes ( '/' ) || modelId . includes ( ':' ) ) {
103103 await this . pullHuggingFaceModel ( modelId ) ;
104104 }
105-
106105 const bar = new SingleBar ( { } , Presets . shades_classic ) ;
107106 bar . start ( 100 , 0 ) ;
108107 const callback = ( progress : number ) => {
@@ -111,21 +110,41 @@ export class ModelsCliUsecases {
111110 await this . modelsUsecases . downloadModel ( modelId , callback ) ;
112111 }
113112
113+ /**
114+ * It's to pull model from HuggingFace repository
115+ * It could be a model from Jan's repo or other authors
116+ * @param modelId HuggingFace model id. e.g. "janhq/llama-3 or llama3:7b"
117+ */
114118 private async pullHuggingFaceModel ( modelId : string ) {
115- const data = await this . fetchHuggingFaceRepoData ( modelId ) ;
116- const { quantization } = await this . inquirerService . inquirer . prompt ( {
117- type : 'list' ,
118- name : 'quantization' ,
119- message : 'Select quantization' ,
120- choices : data . siblings
121- . map ( ( e ) => e . quantization )
122- . filter ( ( e ) => e != null ) ,
123- } ) ;
124-
125- const sibling = data . siblings
126- . filter ( ( e ) => ! ! e . quantization )
127- . find ( ( e : any ) => e . quantization === quantization ) ;
119+ let data : HuggingFaceRepoData ;
120+ if ( modelId . includes ( '/' ) )
121+ data = await this . fetchHuggingFaceRepoData ( modelId ) ;
122+ else data = await this . fetchJanRepoData ( modelId ) ;
123+
124+ let sibling ;
125+
126+ const listChoices = data . siblings
127+ . filter ( ( e ) => e . quantization != null )
128+ . map ( ( e ) => {
129+ return {
130+ name : e . quantization ,
131+ value : e . quantization ,
132+ } ;
133+ } ) ;
128134
135+ if ( listChoices . length > 1 ) {
136+ const { quantization } = await this . inquirerService . inquirer . prompt ( {
137+ type : 'list' ,
138+ name : 'quantization' ,
139+ message : 'Select quantization' ,
140+ choices : listChoices ,
141+ } ) ;
142+ sibling = data . siblings
143+ . filter ( ( e ) => ! ! e . quantization )
144+ . find ( ( e : any ) => e . quantization === quantization ) ;
145+ } else {
146+ sibling = data . siblings . find ( ( e ) => e . rfilename . includes ( '.gguf' ) ) ;
147+ }
129148 if ( ! sibling ) throw 'No expected quantization found' ;
130149
131150 let stopWord = '' ;
@@ -141,9 +160,7 @@ export class ModelsCliUsecases {
141160
142161 // @ts -expect-error "tokenizer.ggml.tokens"
143162 stopWord = metadata [ 'tokenizer.ggml.tokens' ] [ index ] ?? '' ;
144- } catch ( err ) {
145- console . log ( 'Failed to get stop word: ' , err ) ;
146- }
163+ } catch ( err ) { }
147164
148165 const stopWords : string [ ] = [ ] ;
149166 if ( stopWord . length > 0 ) {
@@ -163,6 +180,7 @@ export class ModelsCliUsecases {
163180 description : '' ,
164181 settings : {
165182 prompt_template : promptTemplate ,
183+ llama_model_path : sibling . rfilename ,
166184 } ,
167185 parameters : {
168186 stop : stopWords ,
@@ -209,8 +227,71 @@ export class ModelsCliUsecases {
209227 }
210228 }
211229
230+ /**
231+ * Fetch the model data from Jan's repo
232+ * @param modelId HuggingFace model id. e.g. "llama-3:7b"
233+ * @returns
234+ */
235+ private async fetchJanRepoData ( modelId : string ) {
236+ const repo = modelId . split ( ':' ) [ 0 ] ;
237+ const tree = modelId . split ( ':' ) [ 1 ] ;
238+ const url = this . getRepoModelsUrl ( `janhq/${ repo } ` , tree ) ;
239+ const res = await fetch ( url ) ;
240+ const response :
241+ | {
242+ path : string ;
243+ size : number ;
244+ } [ ]
245+ | { error : string } = await res . json ( ) ;
246+
247+ if ( 'error' in response && response . error != null ) {
248+ throw new Error ( response . error ) ;
249+ }
250+
251+ const data : HuggingFaceRepoData = {
252+ siblings : Array . isArray ( response )
253+ ? response . map ( ( e ) => {
254+ return {
255+ rfilename : e . path ,
256+ downloadUrl : `https://huggingface.co/janhq/${ repo } /resolve/${ tree } /${ e . path } ` ,
257+ fileSize : e . size ?? 0 ,
258+ } ;
259+ } )
260+ : [ ] ,
261+ tags : [ 'gguf' ] ,
262+ id : modelId ,
263+ modelId : modelId ,
264+ author : 'janhq' ,
265+ sha : '' ,
266+ downloads : 0 ,
267+ lastModified : '' ,
268+ private : false ,
269+ disabled : false ,
270+ gated : false ,
271+ pipeline_tag : 'text-generation' ,
272+ cardData : { } ,
273+ createdAt : '' ,
274+ } ;
275+
276+ AllQuantizations . forEach ( ( quantization ) => {
277+ data . siblings . forEach ( ( sibling : any ) => {
278+ if ( ! sibling . quantization && sibling . rfilename . includes ( quantization ) ) {
279+ sibling . quantization = quantization ;
280+ }
281+ } ) ;
282+ } ) ;
283+
284+ data . modelUrl = url ;
285+ return data ;
286+ }
287+
288+ /**
289+ * Fetches the model data from HuggingFace API
290+ * @param repoId HuggingFace model id. e.g. "janhq/llama-3"
291+ * @returns
292+ */
212293 private async fetchHuggingFaceRepoData ( repoId : string ) {
213- const sanitizedUrl = this . toHuggingFaceUrl ( repoId ) ;
294+ const sanitizedUrl = this . getRepoModelsUrl ( repoId ) ;
214295
215296 const res = await fetch ( sanitizedUrl ) ;
216297 const response = await res . json ( ) ;
@@ -245,24 +326,7 @@ export class ModelsCliUsecases {
245326 return data ;
246327 }
247328
248- private toHuggingFaceUrl ( repoId : string ) : string {
249- try {
250- const url = new URL ( `https://huggingface.co/${ repoId } ` ) ;
251- if ( url . host !== 'huggingface.co' ) {
252- throw `Invalid Hugging Face repo URL: ${ repoId } ` ;
253- }
254-
255- const paths = url . pathname . split ( '/' ) . filter ( ( e ) => e . trim ( ) . length > 0 ) ;
256- if ( paths . length < 2 ) {
257- throw `Invalid Hugging Face repo URL: ${ repoId } ` ;
258- }
259-
260- return `${ url . origin } /api/models/${ paths [ 0 ] } /${ paths [ 1 ] } ` ;
261- } catch ( err ) {
262- if ( repoId . startsWith ( 'https' ) ) {
263- throw new Error ( `Cannot parse url: ${ repoId } ` ) ;
264- }
265- throw err ;
266- }
329+ private getRepoModelsUrl ( repoId : string , tree ?: string ) : string {
330+ return `https://huggingface.co/api/models/${ repoId } ${ tree ? `/tree/${ tree } ` : '' } ` ;
267331 }
268332}
0 commit comments