3
3
import sys
4
4
5
5
import click
6
+ import httpx
6
7
import ollama
7
- import requests
8
- from bs4 import BeautifulSoup
8
+ from bs4 import BeautifulSoup , SoupStrainer
9
9
10
- from ollama_manager .utils import get_session , handle_interaction , make_request
10
+ from ollama_manager .utils import coro , handle_interaction
11
11
12
12
13
13
def extract_quantization (text ):
@@ -92,11 +92,8 @@ def format_bytes(size_bytes: int) -> str:
92
92
return f"{ scaled_size :.2f} { _SUFFIXES [magnitude ]} "
93
93
94
94
95
- def list_remote_model_tags (model_name : str , session : requests .Session ):
96
- response = make_request (
97
- session = session ,
98
- url = f"https://ollama.com/library/{ model_name } /tags" ,
99
- )
95
+ async def list_remote_model_tags (model_name : str , client : httpx .AsyncClient ):
96
+ response = await client .get (f"https://ollama.com/library/{ model_name } /tags" )
100
97
soup = BeautifulSoup (response .text , "html.parser" )
101
98
102
99
# Find the span with the specific attribute
@@ -136,21 +133,21 @@ def list_remote_model_tags(model_name: str, session: requests.Session):
136
133
return results
137
134
138
135
139
- def list_remote_models (session : requests . Session ) -> list [str ] | None :
140
- response = make_request ( session = session , url = "https://ollama.com/search" )
136
+ async def list_remote_models (client : httpx . AsyncClient ) -> list [str ] | None :
137
+ response = await client . get ( url = "https://ollama.com/search" )
141
138
142
- soup = BeautifulSoup (response .text , "html.parser" )
143
- # Find the span with the specific attribute
144
- # @NOTE: This might change with website updates.
139
+ title_strainer = SoupStrainer ("span" , attrs = {"x-test-search-response-title" : True })
140
+ soup = BeautifulSoup (response .text , "html.parser" , parse_only = title_strainer )
145
141
elements = soup .find_all ("span" , attrs = {"x-test-search-response-title" : True })
142
+
146
143
if not elements :
147
144
return None
148
145
149
146
return [element .text .strip () for element in elements ]
150
147
151
148
152
- def list_hugging_face_models (
153
- session : requests . Session , limit : int , query : str
149
+ async def list_hugging_face_models (
150
+ client : httpx . AsyncClient , limit : int , query : str
154
151
) -> list [dict [str , str ]]:
155
152
BASE_API_ENDPOINT = "https://huggingface.co/api/models"
156
153
params = {
@@ -162,7 +159,7 @@ def list_hugging_face_models(
162
159
"config" : False ,
163
160
"search" : query ,
164
161
}
165
- res = make_request ( session , url = BASE_API_ENDPOINT , params = params )
162
+ res = await client . get ( url = BASE_API_ENDPOINT , params = params )
166
163
hf_response = res .json ()
167
164
payload = []
168
165
@@ -175,9 +172,12 @@ def list_hugging_face_models(
175
172
return payload
176
173
177
174
178
- def list_hugging_face_model_quantization (session : requests .Session , model_name : str ):
179
- API_ENDPOINT = f"https://huggingface.co/api/models/{ model_name } ?blobs=true"
180
- res = make_request (session = session , url = API_ENDPOINT )
175
+ async def list_hugging_face_model_quantization (
176
+ client : httpx .AsyncClient , model_name : str
177
+ ):
178
+ res = await client .get (
179
+ url = f"https://huggingface.co/api/models/{ model_name } ?blobs=true"
180
+ )
181
181
hf_response = res .json ()
182
182
payload = []
183
183
files = hf_response .get ("siblings" )
@@ -217,75 +217,81 @@ def list_hugging_face_model_quantization(session: requests.Session, model_name:
217
217
type = int ,
218
218
default = 20 ,
219
219
)
220
- def pull_model (hugging_face : bool , query : str , limit : int ):
220
+ @coro
221
+ async def pull_model (hugging_face : bool , query : str , limit : int ):
221
222
"""
222
223
Pull models from Ollama library:
223
224
224
225
https://ollama.dev/search
225
226
"""
226
- session = get_session ()
227
- if hugging_face :
228
- if not query :
229
- query = input ("🤗 hf search: " )
230
- models = list_hugging_face_models (session , limit , query )
231
- else :
232
- models = list_remote_models (session )
233
-
234
- if not models :
235
- print ("❌ No models selected for download" )
236
- sys .exit (0 )
237
-
238
- model_selection = handle_interaction (
239
- models , title = "📦 Select remote Ollama model\s:\n " , multi_select = False
240
- )
241
- if model_selection :
227
+ print ("Pulling Model...." )
228
+ async with httpx .AsyncClient () as client :
242
229
if hugging_face :
243
- model_tags = list_hugging_face_model_quantization (
244
- session = session , model_name = model_selection [ 0 ]
245
- )
230
+ if not query :
231
+ query = input ( "🤗 hf search: " )
232
+ models = await list_hugging_face_models ( client , limit , query )
246
233
else :
247
- model_tags = list_remote_model_tags (
248
- model_name = model_selection [0 ], session = session
249
- )
250
- if not model_tags :
251
- print (f"❌ Failed fetching tags for: { model_selection } . Please try again." )
252
- sys .exit (1 )
234
+ models = await list_remote_models (client )
253
235
254
- max_length = max (len (f"{ model_selection } :{ tag ['title' ]} " ) for tag in model_tags )
236
+ if not models :
237
+ print ("❌ No models selected for download" )
238
+ sys .exit (0 )
255
239
256
- if hugging_face :
257
- model_name_with_tags = [
258
- f"{ tag ['title' ]:<{max_length }} { tag ['size' ]:<{max_length }} { tag ['updated' ]} "
259
- for tag in model_tags
260
- ]
261
- else :
262
- model_name_with_tags = [
263
- f"{ model_selection [0 ]} :{ tag ['title' ]:<{max_length + 5 }} { tag ['size' ]:<{max_length + 5 }} { tag ['updated' ]} "
264
- for tag in model_tags
265
- ]
266
- selected_model_with_tag = handle_interaction (
267
- model_name_with_tags , title = "🔖 Select tag/quantization:\n "
240
+ model_selection = handle_interaction (
241
+ models , title = "📦 Select remote Ollama model\s:\n " , multi_select = False
268
242
)
269
- if not selected_model_with_tag :
270
- print ("No tag selected for the model" )
271
- sys .exit (1 )
243
+ if model_selection :
244
+ if hugging_face :
245
+ model_tags = await list_hugging_face_model_quantization (
246
+ client = client , model_name = model_selection [0 ]
247
+ )
248
+ else :
249
+ model_tags = await list_remote_model_tags (
250
+ model_name = model_selection [0 ], client = client
251
+ )
252
+ if not model_tags :
253
+ print (
254
+ f"❌ Failed fetching tags for: { model_selection } . Please try again."
255
+ )
256
+ sys .exit (1 )
257
+
258
+ max_length = max (
259
+ len (f"{ model_selection } :{ tag ['title' ]} " ) for tag in model_tags
260
+ )
272
261
273
- if hugging_face :
274
- final_model = (
275
- f"hf.co/{ model_selection [0 ]} :{ model_name_with_tags [0 ]} " .split ()[0 ]
262
+ if hugging_face :
263
+ model_name_with_tags = [
264
+ f"{ tag ['title' ]:<{max_length }} { tag ['size' ]:<{max_length }} { tag ['updated' ]} "
265
+ for tag in model_tags
266
+ ]
267
+ else :
268
+ model_name_with_tags = [
269
+ f"{ model_selection [0 ]} :{ tag ['title' ]:<{max_length + 5 }} { tag ['size' ]:<{max_length + 5 }} { tag ['updated' ]} "
270
+ for tag in model_tags
271
+ ]
272
+ selected_model_with_tag = handle_interaction (
273
+ model_name_with_tags , title = "🔖 Select tag/quantization:\n "
276
274
)
277
- else :
278
- final_model = selected_model_with_tag [0 ].split ()[0 ]
279
- print (f">>> Pulling model: { final_model } " )
280
- try :
281
- response = ollama .pull (final_model , stream = True )
282
- screen_padding = 100
283
-
284
- for data in response :
285
- out = f"Status: { data .get ('status' )} | Completed: { format_bytes (data .get ('completed' ))} /{ format_bytes (data .get ('total' ))} "
286
- print (f"{ out :<{screen_padding }} " , end = "\r " , flush = True )
287
-
288
- print (f'\r { " " * screen_padding } \r ' ) # Clear screen
289
- print (f"✅ { final_model } model is ready for use!\n \n >>> olm run\n " )
290
- except Exception as e :
291
- print (f"❌ Failed downloading { final_model } \n { str (e )} " )
275
+ if not selected_model_with_tag :
276
+ print ("No tag selected for the model" )
277
+ sys .exit (1 )
278
+
279
+ if hugging_face :
280
+ final_model = (
281
+ f"hf.co/{ model_selection [0 ]} :{ model_name_with_tags [0 ]} " .split ()[0 ]
282
+ )
283
+ else :
284
+ final_model = selected_model_with_tag [0 ].split ()[0 ]
285
+ print (f">>> Pulling model: { final_model } " )
286
+ try :
287
+ response = ollama .pull (final_model , stream = True )
288
+ screen_padding = 100
289
+
290
+ for data in response :
291
+ out = f"Status: { data .get ('status' )} | Completed: { format_bytes (data .get ('completed' ))} /{ format_bytes (data .get ('total' ))} "
292
+ print (f"{ out :<{screen_padding }} " , end = "\r " , flush = True )
293
+
294
+ print (f'\r { " " * screen_padding } \r ' ) # Clear screen
295
+ print (f"✅ { final_model } model is ready for use!\n \n >>> olm run\n " )
296
+ except Exception as e :
297
+ print (f"❌ Failed downloading { final_model } \n { str (e )} " )
0 commit comments