davanstrien HF staff commited on
Commit
fd1a723
·
1 Parent(s): 042bfdf
Files changed (2) hide show
  1. app.py +321 -65
  2. requirements.txt +145 -1
app.py CHANGED
@@ -1,9 +1,10 @@
1
  import datetime
2
  import os
 
3
  from dataclasses import asdict, dataclass
4
  from functools import lru_cache
5
  from json import JSONDecodeError
6
- from typing import List, Optional, Union
7
  from huggingface_hub.utils import GatedRepoError
8
  import gradio as gr
9
  from requests.exceptions import HTTPError
@@ -16,9 +17,18 @@ from huggingface_hub import (
16
  logging,
17
  model_info,
18
  )
 
 
 
19
  from huggingface_hub.utils import EntryNotFoundError, disable_progress_bars
20
  import httpx
21
  import orjson
 
 
 
 
 
 
22
 
23
  disable_progress_bars()
24
 
@@ -26,6 +36,10 @@ logging.set_verbosity_error()
26
 
27
  token = os.getenv("HF_TOKEN")
28
 
 
 
 
 
29
 
30
  def get_model_labels(model):
31
  try:
@@ -54,11 +68,11 @@ def _get_engagement_stats(hub_id):
54
 
55
  def _try_load_model_card(hub_id):
56
  try:
57
- url = hf_hub_url(repo_id=hub_id, filename="README.md")
 
 
58
  card_text = httpx.get(url).text
59
  length = len(card_text)
60
- # card_text = ModelCard.load(hub_id, token=token).text
61
- # length = len(card_text)
62
  except EntryNotFoundError:
63
  card_text = None
64
  length = None
@@ -97,7 +111,6 @@ class ModelMetadata:
97
  model_card_length: Optional[int] = None
98
 
99
  @classmethod
100
- @lru_cache()
101
  def from_hub(cls, hub_id):
102
  try:
103
  model = model_info(hub_id)
@@ -109,10 +122,6 @@ class ModelMetadata:
109
  library_name = model.library_name
110
  except AttributeError:
111
  library_name = None
112
- # try:
113
- # tags = model.tags
114
- # except AttributeError:
115
- # tags = None
116
  try:
117
  pipeline_tag = model.pipeline_tag
118
  except AttributeError:
@@ -236,7 +245,7 @@ ALL_PIPELINES = {
236
  def generate_task_scores_dict():
237
  task_scores = {}
238
  for task in ALL_PIPELINES:
239
- task_dict = COMMON_SCORES.copy()
240
  if task in TASK_TYPES_WITH_LANGUAGES:
241
  task_dict = {
242
  **task_dict,
@@ -271,81 +280,328 @@ def generate_task_scores_dict():
271
  return task_scores
272
 
273
 
274
- SCORES = generate_task_scores_dict()
 
 
 
 
 
 
275
 
276
 
277
- # cache = Cache("/data/")
 
278
 
279
 
280
- # @cache.memoize(expire=60 * 60 * 24 * 3) # expires after 3 days
281
  def _basic_check(hub_id):
282
- try:
283
- data = ModelMetadata.from_hub(hub_id)
284
- score = 0
285
- if task := data.pipeline_tag:
286
- task_scores = SCORES[task]
287
- to_fix = {}
288
- data_dict = asdict(data)
289
- for k, v in task_scores.items():
290
- if k.startswith("_"):
291
- continue
292
- if data_dict[k] is None:
293
- to_fix[k] = task_scores[k]["missing_recommendation"].replace(
294
- "HUB_ID", hub_id
295
- )
296
- if data_dict[k] is not None:
297
- score += v["score"]
298
- max_score = task_scores["_max_score"]
299
- score = score / max_score
300
- (
301
- f"Your model's metadata score is {round(score*100)}% based on suggested"
302
- f" metadata for {task}. \n"
 
 
 
 
 
 
303
  )
304
- # recommendations = []
305
- if to_fix:
306
- recommendations = (
307
- "Here are some suggestions to improve your model's metadata for"
308
- f" {task}: \n"
 
 
 
 
 
 
 
309
  )
310
- for v in to_fix.values():
311
- recommendations += f"\n- {v}"
312
- data_dict["score"] = score
313
- data_dict["recommendations"] = recommendations
314
- return orjson.dumps(data_dict)
315
-
316
- except Exception as e:
317
- print(e)
318
- return None
319
 
320
 
321
  def basic_check(hub_id):
322
  return _basic_check(hub_id)
323
 
324
 
325
- # print("caching models...")
326
- # print("getting top 5,000 models")
327
- # models = list_models(sort="downloads", direction=-1, limit=5_000)
328
- # model_ids = [model.modelId for model in models]
329
- # print("calculating metadata scores...")
330
- # thread_map(basic_check, model_ids)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
331
 
332
- with gr.Blocks() as demo:
333
- gr.Markdown(
334
- """
335
- # Model Metadata Checker
336
 
337
- This app will check your model's metadata for a few common issues."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
  with gr.Row():
340
- text = gr.Text(label="Model ID")
341
- button = gr.Button(label="Check", type="submit")
342
- with gr.Row():
343
- gr.Markdown("Results")
344
- markdown = gr.JSON()
345
- button.click(_basic_check, text, markdown)
 
 
 
 
 
 
 
 
 
 
 
 
346
 
347
  demo.queue(concurrency_count=32)
348
  demo.launch()
349
 
350
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
  # gr.Interface(fn=basic_check, inputs="text", outputs="markdown").launch(debug=True)
 
1
  import datetime
2
  import os
3
+ import copy
4
  from dataclasses import asdict, dataclass
5
  from functools import lru_cache
6
  from json import JSONDecodeError
7
+ from typing import Any, Dict, List, Optional, Union
8
  from huggingface_hub.utils import GatedRepoError
9
  import gradio as gr
10
  from requests.exceptions import HTTPError
 
17
  logging,
18
  model_info,
19
  )
20
+ from tqdm.auto import tqdm
21
+ from tqdm.contrib.concurrent import thread_map
22
+ import backoff
23
  from huggingface_hub.utils import EntryNotFoundError, disable_progress_bars
24
  import httpx
25
  import orjson
26
+ import httpx
27
+ from functools import lru_cache
28
+
29
+ from sys import platform
30
+
31
+ CACHE_DIR = "./cache" if platform == "darwin" else "/data/"
32
 
33
  disable_progress_bars()
34
 
 
36
 
37
  token = os.getenv("HF_TOKEN")
38
 
39
+ cache = Cache(
40
+ CACHE_DIR, eviction_policy="least-frequently-used", size_limit=4e9
41
+ ) # 4gb in bytes
42
+
43
 
44
  def get_model_labels(model):
45
  try:
 
68
 
69
  def _try_load_model_card(hub_id):
70
  try:
71
+ url = hf_hub_url(
72
+ repo_id=hub_id, filename="README.md"
73
+ ) # We grab card this way rather than via client library to improve performance
74
  card_text = httpx.get(url).text
75
  length = len(card_text)
 
 
76
  except EntryNotFoundError:
77
  card_text = None
78
  length = None
 
111
  model_card_length: Optional[int] = None
112
 
113
  @classmethod
 
114
  def from_hub(cls, hub_id):
115
  try:
116
  model = model_info(hub_id)
 
122
  library_name = model.library_name
123
  except AttributeError:
124
  library_name = None
 
 
 
 
125
  try:
126
  pipeline_tag = model.pipeline_tag
127
  except AttributeError:
 
245
  def generate_task_scores_dict():
246
  task_scores = {}
247
  for task in ALL_PIPELINES:
248
+ task_dict = copy.deepcopy(COMMON_SCORES)
249
  if task in TASK_TYPES_WITH_LANGUAGES:
250
  task_dict = {
251
  **task_dict,
 
280
  return task_scores
281
 
282
 
283
+ @lru_cache(maxsize=None)
284
+ def generate_common_scores():
285
+ GENERIC_SCORES = copy.deepcopy(COMMON_SCORES)
286
+ GENERIC_SCORES["_max_score"] = sum(
287
+ value["score"] for value in GENERIC_SCORES.values()
288
+ )
289
+ return GENERIC_SCORES
290
 
291
 
292
+ SCORES = generate_task_scores_dict()
293
+ GENERIC_SCORES = generate_common_scores()
294
 
295
 
296
+ @cache.memoize(expire=60 * 60 * 24 * 3) # expires after 3 days
297
  def _basic_check(hub_id):
298
+ data = ModelMetadata.from_hub(hub_id)
299
+ score = 0
300
+ if data is None:
301
+ return None
302
+ to_fix = {}
303
+ if task := data.pipeline_tag:
304
+ task_scores = SCORES[task]
305
+ data_dict = asdict(data)
306
+ for k, v in task_scores.items():
307
+ if k.startswith("_"):
308
+ continue
309
+ if data_dict[k] is None:
310
+ to_fix[k] = task_scores[k]["missing_recommendation"].replace(
311
+ "HUB_ID", hub_id
312
+ )
313
+ if data_dict[k] is not None:
314
+ score += v["score"]
315
+ max_score = task_scores["_max_score"]
316
+ score = score / max_score
317
+ (
318
+ f"Your model's metadata score is {round(score*100)}% based on suggested"
319
+ f" metadata for {task}. \n"
320
+ )
321
+ if to_fix:
322
+ recommendations = (
323
+ "Here are some suggestions to improve your model's metadata for"
324
+ f" {task}: \n"
325
  )
326
+ for v in to_fix.values():
327
+ recommendations += f"\n- {v}"
328
+ data_dict["recommendations"] = recommendations
329
+ data_dict["score"] = score * 100
330
+ else:
331
+ data_dict = asdict(data)
332
+ for k, v in GENERIC_SCORES.items():
333
+ if k.startswith("_"):
334
+ continue
335
+ if data_dict[k] is None:
336
+ to_fix[k] = GENERIC_SCORES[k]["missing_recommendation"].replace(
337
+ "HUB_ID", hub_id
338
  )
339
+ if data_dict[k] is not None:
340
+ score += v["score"]
341
+ score = score / GENERIC_SCORES["_max_score"]
342
+ data_dict["score"] = max(
343
+ 0, (score / 2) * 100
344
+ ) # TODO currently setting a manual penalty for not having a task
345
+
346
+ return orjson.dumps(data_dict)
 
347
 
348
 
349
  def basic_check(hub_id):
350
  return _basic_check(hub_id)
351
 
352
 
353
+ def create_query_url(query, skip=0):
354
+ return f"https://huggingface.co/api/search/full-text?q={query}&limit=100&skip={skip}&type=model"
355
+
356
+
357
+ @cache.memoize(expire=60 * 60 * 24 * 3) # expires after 3 days
358
+ def get_results(query) -> Dict[Any, Any]:
359
+ url = create_query_url(query)
360
+ r = httpx.get(url)
361
+ return r.json()
362
+
363
+
364
+ # result = {
365
+ # "repoId": "621ffdc036468d709f175eb5",
366
+ # "repoOwnerId": "60d099234330bad169e611f0",
367
+ # "isPrivate": False,
368
+ # "type": "model",
369
+ # "likes": 0,
370
+ # "isReadmeFile": True,
371
+ # "readmeStartLine": 8,
372
+ # "updatedAt": 1687806057107,
373
+ # "repoName": "hate_speech_en",
374
+ # "repoOwner": "IMSyPP",
375
+ # "tags": "pytorch, bert, text-classification, en, transformers, license:mit, has_space",
376
+ # "name": "IMSyPP/hate_speech_en",
377
+ # "fileName": "README.md",
378
+ # "formatted": {
379
+ # "repoName": [{"text": "hate_speech_en", "type": "text"}],
380
+ # "repoOwner": [{"text": "IMSyPP", "type": "text"}],
381
+ # "fileContent": [
382
+ # {"text": "\n# ", "type": "text"},
383
+ # {"text": "Hate", "type": "highlight"},
384
+ # {"text": " ", "type": "text"},
385
+ # {"text": "Speech", "type": "highlight"},
386
+ # {
387
+ # "text": " Classifier for Social Media Content in English Language\n\nA monolingual model for ",
388
+ # "type": "text",
389
+ # },
390
+ # {"text": "hate", "type": "highlight"},
391
+ # {"text": " ", "type": "text"},
392
+ # {"text": "speech", "type": "highlight"},
393
+ # {
394
+ # "text": " classification of social media content in English language. The model was trained on 103190 YouTube comments and tested on an independent test set of 20554 YouTube comments. It is based on English BERT base pre-trained language model.\n\n## Tokenizer\n\nDuring training the text was preprocessed using the original English BERT base tokenizer. We suggest the same tokenizer is used for inference.\n\n## Model output\n\nThe model classifies each input into one of four distinct classes:\n* 0 - acceptable\n* 1 - inappropriate\n* 2 - offensive\n* 3 - violent",
395
+ # "type": "text",
396
+ # },
397
+ # ],
398
+ # "tags": [
399
+ # {
400
+ # "text": "pytorch, bert, text-classification, en, transformers, license:mit, has_space",
401
+ # "type": "text",
402
+ # }
403
+ # ],
404
+ # "name": [{"text": "IMSyPP/hate_speech_en", "type": "text"}],
405
+ # "fileName": [{"text": "README.md", "type": "text"}],
406
+ # },
407
+ # "authorData": {
408
+ # "avatarUrl": "https://aeiljuispo.cloudimg.io/v7/https://s3.amazonaws.com/moonup/production/uploads/1624284535629-60d08803565dd1d0867f7a37.png?w=200&h=200&f=face",
409
+ # "fullname": "IMSyPP EU REC AG project 875263 - Innovative Monitoring Systems and Prevention Policies of Online Hate Speech",
410
+ # "name": "IMSyPP",
411
+ # "type": "org",
412
+ # "isHf": False,
413
+ # },
414
+ # }
415
+
416
+
417
+ @backoff.on_exception(
418
+ backoff.expo,
419
+ Exception,
420
+ max_time=2,
421
+ raise_on_giveup=False,
422
+ )
423
+ def parse_single_result(result):
424
+ name, filename = result["name"], result["fileName"]
425
+ search_result_file_url = hf_hub_url(name, filename)
426
+ repo_hub_url = f"https://huggingface.co/{name}"
427
+ score = _basic_check(name)
428
+ if score is None:
429
+ return None
430
+ score = orjson.loads(score)
431
+ return {
432
+ "name": name,
433
+ "search_result_file_url": search_result_file_url,
434
+ "repo_hub_url": repo_hub_url,
435
+ "metadata_score": score["score"],
436
+ "model_card_length": score["model_card_length"],
437
+ "is_licensed": bool(score["license"]),
438
+ # "metadata_report": score
439
+ }
440
+
441
+
442
+ def filter_search_results(
443
+ results: List[Dict[Any, Any]], min_score=None, min_model_card_length=None
444
+ ): # TODO make code more intuitive
445
+ results = thread_map(parse_single_result, results)
446
+ for i, parsed_result in tqdm(enumerate(results)):
447
+ # parsed_result = parse_single_result(result)
448
+ if parsed_result is None:
449
+ continue
450
+ if (
451
+ min_score is None
452
+ and min_model_card_length is not None
453
+ and parsed_result["model_card_length"] > min_model_card_length
454
+ or min_score is None
455
+ and min_model_card_length is None
456
+ ):
457
+ yield parsed_result
458
+ elif min_score is not None:
459
+ if parsed_result["metadata_score"] <= min_score:
460
+ continue
461
+ if (
462
+ min_model_card_length is not None
463
+ and parsed_result["model_card_length"] > min_model_card_length
464
+ or min_model_card_length is None
465
+ ):
466
+ parsed_result["original_position"] = i
467
+ yield parsed_result
468
+
469
+
470
+ def sort_search_results(filtered_search_results):
471
+ return sorted(
472
+ list(filtered_search_results),
473
+ key=lambda x: (x["metadata_score"], x["original_position"]),
474
+ reverse=True,
475
+ )
476
 
 
 
 
 
477
 
478
+ def find_context(text, query, window_size):
479
+ # Split the text into words
480
+ words = text.split()
481
+ # Find the index of the query token
482
+ try:
483
+ index = words.index(query)
484
+ # Get the start and end indices of the context window
485
+ start = max(0, index - window_size)
486
+ end = min(len(words), index + window_size + 1)
487
+
488
+ return " ".join(words[start:end])
489
+ except ValueError:
490
+ return " ".join(words[:window_size])
491
+
492
+
493
+ # single_result[
494
+ # "text"
495
+ # ] = "lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
496
+
497
+ # results = [single_result] * 3
498
+
499
+
500
+ def create_markdown(results):
501
+ rows = []
502
+ for result in results:
503
+ row = f"""# [{result['name']}]({result['repo_hub_url']})
504
+ | Metadata Quality Score | Model card length | Licensed |
505
+ |------------------------|-------------------|----------|
506
+ | {result['metadata_score']:.0f}% | {result['model_card_length']} | {"&#9989;" if result['is_licensed'] else "&#10060;"} |
507
+ \n
508
+ *{result['text']}*
509
+
510
+ <hr>
511
+ \n"""
512
+ rows.append(row)
513
+ return "\n".join(rows)
514
+
515
+
516
+ def get_result_card_snippet(result):
517
+ try:
518
+ result_text = httpx.get(result["search_result_file_url"]).text
519
+ result["text"] = find_context(result_text, query, 100)
520
+ except httpx.ConnectError:
521
+ result["text"] = "Could not load model card"
522
+ return result
523
+
524
+
525
+ def _search_hub(
526
+ query: str,
527
+ min_score: Optional[int] = None,
528
+ min_model_card_length: Optional[int] = None,
529
+ ):
530
+ results = get_results(query)
531
+ print(f"Found {len(results['hits'])} results")
532
+ results = results["hits"]
533
+ number_original_results = len(results)
534
+ filtered_results = filter_search_results(
535
+ results, min_score=min_score, min_model_card_length=min_model_card_length
536
  )
537
+ filtered_results = sort_search_results(filtered_results)
538
+ # final_results = []
539
+ # for result in filtered_results:
540
+ # result_text = httpx.get(result["search_result_file_url"]).text
541
+ # result["text"] = find_context(result_text, query, 100)
542
+
543
+ # final_results.append(result)
544
+ final_results = thread_map(get_result_card_snippet, filtered_results)
545
+ percent_of_original = round(
546
+ len(final_results) / number_original_results * 100, ndigits=0
547
+ )
548
+ filtered_vs_og = f"""
549
+ | Number of original results | Number of results after filtering | Percentage of results after filtering |
550
+ | -------------------------- | --------------------------------- | -------------------------------------------- |
551
+ | {number_original_results} | {len(final_results)} | {percent_of_original}% |
552
+
553
+ """
554
+ print(final_results)
555
+ return filtered_vs_og, create_markdown(final_results)
556
+
557
+
558
+ def search_hub(query: str, min_score=None, min_model_card_length=None):
559
+ return _search_hub(query, min_score, min_model_card_length)
560
+
561
+
562
+ with gr.Blocks() as demo:
563
+ gr.Markdown("# &#129303; Hub model search with metadata quality filters")
564
  with gr.Row():
565
+ with gr.Column():
566
+ query = gr.Textbox("x-ray", label="Search query")
567
+ with gr.Column():
568
+ button = gr.Button("Search")
569
+ with gr.Row():
570
+ # gr.Checkbox(False, label="Must have licence?")
571
+ mim_model_card_length = gr.Number(
572
+ None, label="Minimum model card length"
573
+ )
574
+ min_metadata_score = gr.Slider(0, label="Minimum metadata score")
575
+ filter_results = gr.Markdown("Filter results vs original search")
576
+ results_markdown = gr.Markdown("Search results")
577
+
578
+ button.click(
579
+ search_hub,
580
+ [query, min_metadata_score, mim_model_card_length],
581
+ [filter_results, results_markdown],
582
+ )
583
 
584
  demo.queue(concurrency_count=32)
585
  demo.launch()
586
 
587
 
588
+ # with gr.Blocks() as demo:
589
+ # gr.Markdown(
590
+ # """
591
+ # # Model Metadata Checker
592
+
593
+ # This app will check your model's metadata for a few common issues."""
594
+ # )
595
+ # with gr.Row():
596
+ # text = gr.Text(label="Model ID")
597
+ # button = gr.Button(label="Check", type="submit")
598
+ # with gr.Row():
599
+ # gr.Markdown("Results")
600
+ # markdown = gr.JSON()
601
+ # button.click(_basic_check, text, markdown)
602
+
603
+ # demo.queue(concurrency_count=32)
604
+ # demo.launch()
605
+
606
+
607
  # gr.Interface(fn=basic_check, inputs="text", outputs="markdown").launch(debug=True)
requirements.txt CHANGED
@@ -1,2 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  diskcache==5.6.1
2
- orjson
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # This file is autogenerated by pip-compile with Python 3.11
3
+ # by the following command:
4
+ #
5
+ # pip-compile --resolver=backtracking
6
+ #
7
+ anyio==3.7.0
8
+ # via httpcore
9
+ appnope==0.1.3
10
+ # via
11
+ # ipykernel
12
+ # ipython
13
+ asttokens==2.2.1
14
+ # via stack-data
15
+ backcall==0.2.0
16
+ # via ipython
17
+ backoff==2.2.1
18
+ # via -r requirements.in
19
+ certifi==2023.5.7
20
+ # via
21
+ # httpcore
22
+ # httpx
23
+ # requests
24
+ charset-normalizer==3.1.0
25
+ # via requests
26
+ comm==0.1.3
27
+ # via ipykernel
28
+ debugpy==1.6.7
29
+ # via ipykernel
30
+ decorator==5.1.1
31
+ # via ipython
32
  diskcache==5.6.1
33
+ # via -r requirements.in
34
+ executing==1.2.0
35
+ # via stack-data
36
+ filelock==3.12.2
37
+ # via huggingface-hub
38
+ fsspec==2023.6.0
39
+ # via huggingface-hub
40
+ h11==0.14.0
41
+ # via httpcore
42
+ httpcore==0.17.2
43
+ # via httpx
44
+ httpx==0.24.1
45
+ # via -r requirements.in
46
+ huggingface-hub==0.15.1
47
+ # via -r requirements.in
48
+ idna==3.4
49
+ # via
50
+ # anyio
51
+ # httpx
52
+ # requests
53
+ ipykernel==6.23.3
54
+ # via ipywidgets
55
+ ipython==8.14.0
56
+ # via
57
+ # ipykernel
58
+ # ipywidgets
59
+ ipywidgets==8.0.6
60
+ # via -r requirements.in
61
+ jedi==0.18.2
62
+ # via ipython
63
+ jupyter-client==8.3.0
64
+ # via ipykernel
65
+ jupyter-core==5.3.1
66
+ # via
67
+ # ipykernel
68
+ # jupyter-client
69
+ jupyterlab-widgets==3.0.7
70
+ # via ipywidgets
71
+ matplotlib-inline==0.1.6
72
+ # via
73
+ # ipykernel
74
+ # ipython
75
+ nest-asyncio==1.5.6
76
+ # via ipykernel
77
+ orjson==3.9.1
78
+ # via -r requirements.in
79
+ packaging==23.1
80
+ # via
81
+ # huggingface-hub
82
+ # ipykernel
83
+ parso==0.8.3
84
+ # via jedi
85
+ pexpect==4.8.0
86
+ # via ipython
87
+ pickleshare==0.7.5
88
+ # via ipython
89
+ platformdirs==3.8.0
90
+ # via jupyter-core
91
+ prompt-toolkit==3.0.38
92
+ # via ipython
93
+ psutil==5.9.5
94
+ # via ipykernel
95
+ ptyprocess==0.7.0
96
+ # via pexpect
97
+ pure-eval==0.2.2
98
+ # via stack-data
99
+ pygments==2.15.1
100
+ # via ipython
101
+ python-dateutil==2.8.2
102
+ # via jupyter-client
103
+ pyyaml==6.0
104
+ # via huggingface-hub
105
+ pyzmq==25.1.0
106
+ # via
107
+ # ipykernel
108
+ # jupyter-client
109
+ requests==2.31.0
110
+ # via huggingface-hub
111
+ six==1.16.0
112
+ # via
113
+ # asttokens
114
+ # python-dateutil
115
+ sniffio==1.3.0
116
+ # via
117
+ # anyio
118
+ # httpcore
119
+ # httpx
120
+ stack-data==0.6.2
121
+ # via ipython
122
+ toolz==0.12.0
123
+ # via -r requirements.in
124
+ tornado==6.3.2
125
+ # via
126
+ # ipykernel
127
+ # jupyter-client
128
+ tqdm==4.65.0
129
+ # via huggingface-hub
130
+ traitlets==5.9.0
131
+ # via
132
+ # comm
133
+ # ipykernel
134
+ # ipython
135
+ # ipywidgets
136
+ # jupyter-client
137
+ # jupyter-core
138
+ # matplotlib-inline
139
+ typing-extensions==4.7.0
140
+ # via huggingface-hub
141
+ urllib3==2.0.3
142
+ # via requests
143
+ wcwidth==0.2.6
144
+ # via prompt-toolkit
145
+ widgetsnbextension==4.0.7
146
+ # via ipywidgets