Alvaro Romo commited on
Commit
c2f297a
·
1 Parent(s): db0086f

Fixed logging messages and refactor code. Added log in private dataset

Browse files
.gitignore ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ venv/
2
+ .venv/
3
+ .mypy_cache/
4
+ .idea/
5
+ */__pycache__/*
6
+ .env
7
+ .ipynb_checkpoints
8
+ *ipynb
9
+ .vscode/
10
+ .DS_Store
11
+ .ruff_cache/
12
+ .python-version
13
+ .profile_app.python
14
+ *pstats
15
+ *.lock
16
+
17
+ user_request/
app.py CHANGED
@@ -1,122 +1,74 @@
1
- import streamlit as st
2
- import pandas as pd
3
- import re
4
- from datasets import load_dataset
5
- import src.check_validity as cv
6
- from src.submit import ModelSizeChecker
7
  import os
8
- from huggingface_hub import HfApi
9
-
10
- st.set_page_config(page_title="IVACE Leaderboard", layout="wide")
11
 
 
 
 
 
12
 
13
- def validate_model(model, precision, base_model, weight_type, use_chat_template):
14
- """
15
- Validate model with some checkers to assure tha can be evaluated
16
- :param model: hf model name
17
- :param precision: model parameters data type
18
- :param base_model: base model (if it is need it)
19
- :param weight_type:
20
- :param use_chat_template:
21
- :return:
22
- """
23
- API = HfApi()
24
 
25
- try:
26
- model_info = API.model_info(repo_id=model, revision="main")
27
- except Exception as e:
28
- return "Could not get your model information. Please fill it up properly."
29
-
30
- # TODO: think if it makes sense. Maybe we need to allow upload sumissions more than once
31
- # # Has it been submitted already?
32
- # model_key = f"{model}_{model_info.sha}_{precision}"
33
- # if model_key in requested_models:
34
- # return st.error(
35
- # f"The model '{model}' with revision '{model_info.sha}' and precision '{precision}' has already been submitted.")
36
-
37
- # Check model size early
38
- model_size, error_text = cv.get_model_size(model_info=model_info, precision=precision, base_model=base_model)
39
- if model_size is None:
40
- return error_text
41
-
42
- # Absolute size limit for float16 and bfloat16
43
- if precision in ["float16", "bfloat16"] and model_size > 100:
44
- error_message = f"Sadly, models larger than 100B parameters cannot be submitted in {precision} precision at this time. " \
45
- f"Your model size: {model_size:.2f}B parameters."
46
- return error_message
47
-
48
- # Precision-adjusted size limit for 8bit, 4bit, and GPTQ
49
- if precision in ["8bit", "4bit", "GPTQ"]:
50
- size_checker = ModelSizeChecker(model=model, precision=precision, model_size_in_b=model_size)
51
-
52
- if not size_checker.can_evaluate():
53
- precision_factor = size_checker.get_precision_factor()
54
- max_size = 140 * precision_factor
55
- error_message = f"Sadly, models this big ({model_size:.2f}B parameters) cannot be evaluated automatically " \
56
- f"at the moment on our cluster. The maximum size for {precision} precision is {max_size:.2f}B parameters."
57
- return error_message
58
-
59
- architecture = "?"
60
- # Is the model on the hub?
61
- if weight_type in ["Delta", "Adapter"]:
62
- base_model_on_hub, error, _ = cv.is_model_on_hub(
63
- model_name=base_model, revision="main", token=None, test_tokenizer=True
64
- )
65
- if not base_model_on_hub:
66
- return f'Base model "{base_model}" {error}'
67
- if not weight_type == "Adapter":
68
- model_on_hub, error, model_config = cv.is_model_on_hub(model_name=model, revision=model_info.sha,
69
- test_tokenizer=True)
70
- if not model_on_hub or model_config is None:
71
- return f'Model "{model}" {error}'
72
- if model_config is not None:
73
- architectures = getattr(model_config, "architectures", None)
74
- if architectures:
75
- architecture = ";".join(architectures)
76
-
77
- # Were the model card and license filled?
78
- try:
79
- model_info.cardData["license"]
80
- except Exception:
81
- return "Please select a license for your model"
82
-
83
- modelcard_OK, error_msg, model_card = cv.check_model_card(model)
84
- if not modelcard_OK:
85
- return error_msg
86
 
87
- # Check the chat template submission
88
- if use_chat_template:
89
- chat_template_valid, chat_template_error = cv.check_chat_template(model, "main")
90
- if not chat_template_valid:
91
- return chat_template_error
92
 
93
- return None
 
 
 
 
 
 
 
 
94
 
95
- # Function to send email
96
- def log_submission(model_name, description, user_contact):
97
- # todo: create email or log in dataset
98
- ...
99
 
100
- return True
 
 
 
 
 
 
 
101
 
102
 
103
- def get_url(html_content):
104
  match = re.search(r'href=["\'](https?://[^\s"\']+)', html_content)
105
  if match:
106
  url = match.group(1)
107
  return url
108
-
109
- return None
110
 
111
 
112
  @st.cache_data
113
- def load_data():
114
  try:
115
- columns = ["eval_name", "Model", "Type", "Average ⬆️", "IFEval", "MMLU-PRO", "GPQA", "MUSR", "CO₂ cost (kg)"]
116
- data = load_dataset("open-llm-leaderboard/contents")["train"].to_pandas().head(10)
117
- # print(data.columns)
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  data = data[columns]
119
- # TODO: check if from submit this is neede it
120
  data["Model"] = data["Model"].apply(get_url)
121
  data.sort_values(by="Average ⬆️", ascending=False, inplace=True)
122
  data.reset_index(drop=True, inplace=True)
@@ -126,11 +78,12 @@ def load_data():
126
  return pd.DataFrame()
127
 
128
 
 
129
  leaderboard_data = load_data()
130
- tabs = st.tabs(["Leaderboard", "Submit model"]) # , "Vote for next model"
131
 
132
  with tabs[0]:
133
- # logo
134
  cols_logo = st.columns(5, vertical_alignment="center")
135
  with cols_logo[2]:
136
  st.image("assets/images/hf-logo.png", use_container_width=True)
@@ -148,18 +101,16 @@ with tabs[0]:
148
  """,
149
  unsafe_allow_html=True,
150
  )
151
- leaderboard_cols = st.columns([0.1, 0.8, 0.1], vertical_alignment="center")
152
- with leaderboard_cols[1]:
153
- if not leaderboard_data.empty:
154
- st.data_editor(
155
- leaderboard_data,
156
- column_config={
157
- "Model": st.column_config.LinkColumn("Model")
158
- },
159
- hide_index=False,
160
- )
161
- else:
162
- st.write("No data found to display on leaderboard.")
163
 
164
  with tabs[1]:
165
  st.header("Submit model")
@@ -189,54 +140,84 @@ with tabs[1]:
189
  html_path = "assets/html"
190
  for filename in os.listdir(html_path):
191
  file_path = os.path.join(html_path, filename)
192
- with open(file_path, 'r', encoding='utf-8') as file:
193
  guide_info_list.append(file.read())
194
 
195
  # display adding number id
196
  for i, info_div in enumerate(guide_info_list):
197
- st.markdown(get_id_number(i+1) + info_div, unsafe_allow_html=True)
198
 
199
  with st.form("submit_model_form"):
200
- model_name = st.text_input("Model Name (format: user_name/model_name)",
201
- help="Your model should be public on the Hub and follow the username/model-id format (e.g. mistralai/Mistral-7B-v0.1).")
202
- description = st.text_area("Description", help="Add a description of the proposed model for the evaluation to help prioritize its evaluation")
203
- user_contact = st.text_input("Your Contact Email", help="User e-mail to contact when there are updates")
 
 
 
 
 
 
 
 
204
  precision_option = st.selectbox(
205
  "Choose precision format:",
206
  help="Size limits vary by precision: • FP16/BF16: up to 100B parameters • 8-bit: up to 280B parameters (2x) • 4-bit: up to 560B parameters (4x) Choose carefully as incorrect precision can cause evaluation errors.",
207
  options=["float16", "bfloat16", "8bit", "4bit", "GPTQ"],
208
- index=0
209
  )
210
  weight_type_option = st.selectbox(
211
  "Select what type of weights are being loaded from the checkpoint provided:",
212
  help="Original: Complete model weights in safetensors format Delta: Weight differences from base model (requires base model for size calculation) Adapter: Lightweight fine-tuning layers (requires base model for size calculation)",
213
  options=["Original", "Adapter", "Delta"],
214
- index=0
 
 
 
 
 
215
  )
216
- base_model_name = st.text_input("Base model",
217
- help="Required for delta weights or adapters. This information is used to identify the original model and calculate the total parameter count by combining base model and adapter/delta parameters.",
218
- value="")
219
  model_type = st.selectbox(
220
  "Choose model type:",
221
  help="🟢 Pretrained: Base models trained on text using masked modeling 🟩 Continuously Pretrained: Extended training on additional corpus 🔶 Fine-tuned: Domain-specific optimization 💬 Chat: Models using RLHF, DPO, or IFT for conversation 🤝 Merge: Combined weights without additional training",
222
- options=["🟢 Pretrained", "🟩 Continuously Pretrained", "🔶 Fine-tuned", "💬 Chat", "🤝 Merge"],
 
 
 
 
 
 
223
  )
224
  submit_button = st.form_submit_button("Submit Request")
225
 
226
  if submit_button:
227
  # validate model size, license, chat_templates
228
  use_chat_template = True if model_type == "💬 Chat" else False
229
- validation_error = validate_model(model_name, precision_option, base_model_name, weight_type_option, use_chat_template)
 
 
 
 
 
 
230
  if validation_error is not None:
231
  st.error(validation_error)
232
  elif not re.match(r"[^@]+@[^@]+\.[^@]+", user_contact):
233
  st.error("Invalid email address.")
234
  else:
235
- if log_submission(model_name, description, user_contact):
 
 
 
 
 
 
 
 
 
 
236
  st.success("Your request has been sent successfully.")
237
- else:
238
- st.error("Failed to send your request. Please try again later.")
239
-
240
- # with tabs[2]:
241
- # st.header("Vote for next model")
242
- # st.write("Esta sección estará disponible próximamente.")
 
1
+ import json
 
 
 
 
 
2
  import os
3
+ import re
4
+ import uuid
5
+ from pathlib import Path
6
 
7
+ import pandas as pd
8
+ import streamlit as st
9
+ from datasets import load_dataset
10
+ from huggingface_hub import CommitScheduler
11
 
12
+ from src.check_validity import validate_model
 
 
 
 
 
 
 
 
 
 
13
 
14
+ # define page config
15
+ st.set_page_config(page_title="IVACE Leaderboard", layout="wide")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
+ # setup scheduler to upload user requests
18
+ HF_TOKEN = os.environ.get("HF_TOKEN", None)
19
+ request_file = Path("user_request/") / f"data_{uuid.uuid4()}.json"
20
+ request_folder = request_file.parent
 
21
 
22
+ scheduler = CommitScheduler(
23
+ repo_id="iberbench/ivace-user-request",
24
+ repo_type="dataset",
25
+ private=True,
26
+ folder_path=request_folder,
27
+ token=HF_TOKEN,
28
+ path_in_repo="data",
29
+ every=10,
30
+ )
31
 
 
 
 
 
32
 
33
+ def log_submission(input_dict: dict) -> None:
34
+ """
35
+ Append input/outputs and user feedback to a JSON Lines file using a thread lock to avoid concurrent writes from different users.
36
+ """
37
+ with scheduler.lock:
38
+ with request_file.open("a") as f:
39
+ f.write(json.dumps(input_dict))
40
+ f.write("\n")
41
 
42
 
43
+ def get_url(html_content: str) -> str:
44
  match = re.search(r'href=["\'](https?://[^\s"\']+)', html_content)
45
  if match:
46
  url = match.group(1)
47
  return url
48
+ else:
49
+ raise ValueError("Url not found in the link")
50
 
51
 
52
  @st.cache_data
53
+ def load_data() -> pd.DataFrame:
54
  try:
55
+ columns = [
56
+ "eval_name",
57
+ "Model",
58
+ "Type",
59
+ "Average ⬆️",
60
+ "IFEval",
61
+ "MMLU-PRO",
62
+ "GPQA",
63
+ "MUSR",
64
+ "CO₂ cost (kg)",
65
+ ]
66
+ data = (
67
+ load_dataset("open-llm-leaderboard/contents")["train"]
68
+ .to_pandas()
69
+ .head(10)
70
+ )
71
  data = data[columns]
 
72
  data["Model"] = data["Model"].apply(get_url)
73
  data.sort_values(by="Average ⬆️", ascending=False, inplace=True)
74
  data.reset_index(drop=True, inplace=True)
 
78
  return pd.DataFrame()
79
 
80
 
81
+ # streamlit UI
82
  leaderboard_data = load_data()
83
+ tabs = st.tabs(["Leaderboard", "Submit model"])
84
 
85
  with tabs[0]:
86
+ # logo image
87
  cols_logo = st.columns(5, vertical_alignment="center")
88
  with cols_logo[2]:
89
  st.image("assets/images/hf-logo.png", use_container_width=True)
 
101
  """,
102
  unsafe_allow_html=True,
103
  )
104
+ # leaderboard_cols = st.columns([0.1, 0.8, 0.1], vertical_alignment="center")
105
+ # with leaderboard_cols[1]:
106
+ if not leaderboard_data.empty:
107
+ st.data_editor(
108
+ leaderboard_data,
109
+ column_config={"Model": st.column_config.LinkColumn("Model")},
110
+ hide_index=False,
111
+ )
112
+ else:
113
+ st.write("No data found to display on leaderboard.")
 
 
114
 
115
  with tabs[1]:
116
  st.header("Submit model")
 
140
  html_path = "assets/html"
141
  for filename in os.listdir(html_path):
142
  file_path = os.path.join(html_path, filename)
143
+ with open(file_path, "r", encoding="utf-8") as file:
144
  guide_info_list.append(file.read())
145
 
146
  # display adding number id
147
  for i, info_div in enumerate(guide_info_list):
148
+ st.markdown(get_id_number(i + 1) + info_div, unsafe_allow_html=True)
149
 
150
  with st.form("submit_model_form"):
151
+ model_name = st.text_input(
152
+ "Model Name (format: user_name/model_name)",
153
+ help="Your model should be public on the Hub and follow the username/model-id format (e.g. mistralai/Mistral-7B-v0.1).",
154
+ )
155
+ description = st.text_area(
156
+ "Description",
157
+ help="Add a description of the proposed model for the evaluation to help prioritize its evaluation",
158
+ )
159
+ user_contact = st.text_input(
160
+ "Your Contact Email",
161
+ help="User e-mail to contact when there are updates",
162
+ )
163
  precision_option = st.selectbox(
164
  "Choose precision format:",
165
  help="Size limits vary by precision: • FP16/BF16: up to 100B parameters • 8-bit: up to 280B parameters (2x) • 4-bit: up to 560B parameters (4x) Choose carefully as incorrect precision can cause evaluation errors.",
166
  options=["float16", "bfloat16", "8bit", "4bit", "GPTQ"],
167
+ index=0,
168
  )
169
  weight_type_option = st.selectbox(
170
  "Select what type of weights are being loaded from the checkpoint provided:",
171
  help="Original: Complete model weights in safetensors format Delta: Weight differences from base model (requires base model for size calculation) Adapter: Lightweight fine-tuning layers (requires base model for size calculation)",
172
  options=["Original", "Adapter", "Delta"],
173
+ index=0,
174
+ )
175
+ base_model_name = st.text_input(
176
+ "Base model",
177
+ help="Required for delta weights or adapters. This information is used to identify the original model and calculate the total parameter count by combining base model and adapter/delta parameters.",
178
+ value="",
179
  )
 
 
 
180
  model_type = st.selectbox(
181
  "Choose model type:",
182
  help="🟢 Pretrained: Base models trained on text using masked modeling 🟩 Continuously Pretrained: Extended training on additional corpus 🔶 Fine-tuned: Domain-specific optimization 💬 Chat: Models using RLHF, DPO, or IFT for conversation 🤝 Merge: Combined weights without additional training",
183
+ options=[
184
+ "🟢 Pretrained",
185
+ "🟩 Continuously Pretrained",
186
+ "🔶 Fine-tuned",
187
+ "💬 Chat",
188
+ "🤝 Merge",
189
+ ],
190
  )
191
  submit_button = st.form_submit_button("Submit Request")
192
 
193
  if submit_button:
194
  # validate model size, license, chat_templates
195
  use_chat_template = True if model_type == "💬 Chat" else False
196
+ validation_error = validate_model(
197
+ model_name,
198
+ precision_option,
199
+ base_model_name,
200
+ weight_type_option,
201
+ use_chat_template,
202
+ )
203
  if validation_error is not None:
204
  st.error(validation_error)
205
  elif not re.match(r"[^@]+@[^@]+\.[^@]+", user_contact):
206
  st.error("Invalid email address.")
207
  else:
208
+ input_dict = {
209
+ "model_name": model_name,
210
+ "description": description,
211
+ "user_contact": user_contact,
212
+ "precision_option": precision_option,
213
+ "weight_type_option": weight_type_option,
214
+ "base_model_name": base_model_name,
215
+ "model_type": model_type,
216
+ }
217
+ try:
218
+ log_submission(input_dict)
219
  st.success("Your request has been sent successfully.")
220
+ except Exception as e:
221
+ st.error(
222
+ f"Failed to send your request: {e}. Please try again later."
223
+ )
 
 
src/__pycache__/__init__.cpython-310.pyc DELETED
Binary file (156 Bytes)
 
src/__pycache__/__init__.cpython-38.pyc DELETED
Binary file (154 Bytes)
 
src/__pycache__/check_validity.cpython-310.pyc DELETED
Binary file (5.85 kB)
 
src/__pycache__/check_validity.cpython-38.pyc DELETED
Binary file (5.9 kB)
 
src/__pycache__/submit.cpython-310.pyc DELETED
Binary file (1.22 kB)
 
src/check_validity.py CHANGED
@@ -1,15 +1,19 @@
1
  import json
2
- import os
3
- import re
4
  import logging
5
- from collections import defaultdict
6
- from datetime import datetime, timedelta, timezone
7
 
8
  import huggingface_hub
9
- from huggingface_hub import ModelCard, hf_hub_download
10
- from huggingface_hub.hf_api import ModelInfo, get_safetensors_metadata, parse_safetensors_file_metadata
 
 
 
 
11
  from transformers import AutoConfig, AutoTokenizer
12
 
 
 
 
13
  # ht to @Wauplin, thank you for the snippet!
14
  # See https://huggingface.co/spaces/open-llm-leaderboard/open_llm_leaderboard/discussions/317
15
  def check_model_card(repo_id: str) -> tuple[bool, str]:
@@ -17,10 +21,16 @@ def check_model_card(repo_id: str) -> tuple[bool, str]:
17
  try:
18
  card = ModelCard.load(repo_id)
19
  except huggingface_hub.utils.EntryNotFoundError:
20
- return False, "Please add a model card to your model to explain how you trained/fine-tuned it.", None
 
 
 
 
21
 
22
  # Enforce license metadata
23
- if card.data.license is None and not ("license_name" in card.data and "license_link" in card.data):
 
 
24
  return (
25
  False,
26
  (
@@ -32,24 +42,44 @@ def check_model_card(repo_id: str) -> tuple[bool, str]:
32
 
33
  # Enforce card content
34
  if len(card.text) < 200:
35
- return False, "Please add a description to your model card, it is too short.", None
 
 
 
 
36
 
37
  return True, "", card
38
 
39
 
40
  def is_model_on_hub(
41
- model_name: str, revision: str, token: str | None = None, trust_remote_code: bool = False, test_tokenizer: bool = False,
 
 
 
 
42
  ) -> tuple[bool, str, AutoConfig]:
43
  try:
44
  config = AutoConfig.from_pretrained(
45
- model_name, revision=revision, trust_remote_code=trust_remote_code, token=token, force_download=True)
 
 
 
 
 
46
  if test_tokenizer:
47
  try:
48
- AutoTokenizer.from_pretrained(
49
- model_name, revision=revision, trust_remote_code=trust_remote_code, token=token,
 
 
 
50
  )
51
  except ValueError as e:
52
- return (False, f"uses a tokenizer which is not in a transformers release: {e}", None)
 
 
 
 
53
  except Exception:
54
  return (
55
  False,
@@ -74,27 +104,42 @@ def is_model_on_hub(
74
  except Exception as e:
75
  if "You are trying to access a gated repo." in str(e):
76
  return True, "uses a gated model.", None
77
- return False, f"was not found or misconfigured on the hub! Error raised was {e.args[0]}", None
 
 
 
 
78
 
79
 
80
- def get_model_size(model_info: ModelInfo, precision: str, base_model: str| None) -> tuple[float | None, str]:
 
 
81
  size_pattern = re.compile(r"(\d+\.)?\d+(b|m)")
82
  safetensors = None
83
  adapter_safetensors = None
84
  # hack way to check that model is adapter
85
- is_adapter = "adapter_config.json" in (s.rfilename for s in model_info.siblings)
 
 
86
 
87
  try:
88
  if is_adapter:
89
  if not base_model:
90
- return None, "Adapter model submission detected. Please ensure the base model information is provided."
 
 
 
91
 
92
- adapter_safetensors = parse_safetensors_file_metadata(model_info.id, "adapter_model.safetensors")
 
 
93
  safetensors = get_safetensors_metadata(base_model)
94
  else:
95
  safetensors = get_safetensors_metadata(model_info.id)
96
  except Exception as e:
97
- logging.warning(f"Failed to get safetensors metadata for model {model_info.id}: {e!s}")
 
 
98
 
99
  if safetensors is not None:
100
  model_size = sum(safetensors.parameter_count.values())
@@ -106,21 +151,32 @@ def get_model_size(model_info: ModelInfo, precision: str, base_model: str| None)
106
  size_match = re.search(size_pattern, model_info.id.lower())
107
  if size_match:
108
  model_size = size_match.group(0)
109
- model_size = round(float(model_size[:-1]) if model_size[-1] == "b" else float(model_size[:-1]) / 1e3, 3)
 
 
 
 
 
110
  else:
111
  return None, "Unknown model size"
112
  except AttributeError:
113
- logging.warning(f"Unable to parse model size from ID: {model_info.id}")
 
 
114
  return None, "Unknown model size"
115
 
116
- size_factor = 8 if (precision == "GPTQ" or "gptq" in model_info.id.lower()) else 1
 
 
117
  model_size = size_factor * model_size
118
 
119
  return model_size, ""
120
 
 
121
  def get_model_arch(model_info: ModelInfo):
122
  return model_info.config.get("architectures", "Unknown")
123
 
 
124
  def check_chat_template(model: str, revision: str) -> tuple[bool, str]:
125
  try:
126
  # Attempt to download only the tokenizer_config.json file
@@ -128,21 +184,28 @@ def check_chat_template(model: str, revision: str) -> tuple[bool, str]:
128
  repo_id=model,
129
  filename="tokenizer_config.json",
130
  revision=revision,
131
- repo_type="model"
132
  )
133
 
134
  # Read and parse the tokenizer_config.json file
135
- with open(config_file, 'r') as f:
136
  tokenizer_config = json.load(f)
137
 
138
  # Check if chat_template exists in the tokenizer configuration
139
- if 'chat_template' not in tokenizer_config:
140
- return False, f"The model {model} doesn't have a chat_template in its tokenizer_config.json. Please add a chat_template before submitting or submit without it."
 
 
 
141
 
142
  return True, ""
143
  except Exception as e:
144
- return False, f"Error checking chat_template for model {model}: {str(e)}"
145
-
 
 
 
 
146
  def get_model_tags(model_card, model: str):
147
  is_merge_from_metadata = False
148
  is_moe_from_metadata = False
@@ -152,22 +215,125 @@ def get_model_tags(model_card, model: str):
152
  return tags
153
  if model_card.data.tags:
154
  is_merge_from_metadata = any(
155
- [tag in model_card.data.tags for tag in ["merge", "moerge", "mergekit", "lazymergekit"]]
 
 
 
 
 
 
156
  )
157
- is_moe_from_metadata = any([tag in model_card.data.tags for tag in ["moe", "moerge"]])
158
 
159
  is_merge_from_model_card = any(
160
- keyword in model_card.text.lower() for keyword in ["merged model", "merge model", "moerge"]
 
161
  )
162
  if is_merge_from_model_card or is_merge_from_metadata:
163
  tags.append("merge")
164
- is_moe_from_model_card = any(keyword in model_card.text.lower() for keyword in ["moe", "mixtral"])
 
 
165
  # Hardcoding because of gating problem
166
  if "Qwen/Qwen1.5-32B" in model:
167
  is_moe_from_model_card = False
168
- is_moe_from_name = "moe" in model.lower().replace("/", "-").replace("_", "-").split("-")
 
 
169
  if is_moe_from_model_card or is_moe_from_name or is_moe_from_metadata:
170
  tags.append("moe")
171
 
172
  return tags
173
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import json
 
 
2
  import logging
3
+ import re
 
4
 
5
  import huggingface_hub
6
+ from huggingface_hub import HfApi, ModelCard, hf_hub_download
7
+ from huggingface_hub.hf_api import (
8
+ ModelInfo,
9
+ get_safetensors_metadata,
10
+ parse_safetensors_file_metadata,
11
+ )
12
  from transformers import AutoConfig, AutoTokenizer
13
 
14
+ from src.submit import ModelSizeChecker
15
+
16
+
17
  # ht to @Wauplin, thank you for the snippet!
18
  # See https://huggingface.co/spaces/open-llm-leaderboard/open_llm_leaderboard/discussions/317
19
  def check_model_card(repo_id: str) -> tuple[bool, str]:
 
21
  try:
22
  card = ModelCard.load(repo_id)
23
  except huggingface_hub.utils.EntryNotFoundError:
24
+ return (
25
+ False,
26
+ "Please add a model card to your model to explain how you trained/fine-tuned it.",
27
+ None,
28
+ )
29
 
30
  # Enforce license metadata
31
+ if card.data.license is None and not (
32
+ "license_name" in card.data and "license_link" in card.data
33
+ ):
34
  return (
35
  False,
36
  (
 
42
 
43
  # Enforce card content
44
  if len(card.text) < 200:
45
+ return (
46
+ False,
47
+ "Please add a description to your model card, it is too short.",
48
+ None,
49
+ )
50
 
51
  return True, "", card
52
 
53
 
54
  def is_model_on_hub(
55
+ model_name: str,
56
+ revision: str,
57
+ token: str | None = None,
58
+ trust_remote_code: bool = False,
59
+ test_tokenizer: bool = False,
60
  ) -> tuple[bool, str, AutoConfig]:
61
  try:
62
  config = AutoConfig.from_pretrained(
63
+ model_name,
64
+ revision=revision,
65
+ trust_remote_code=trust_remote_code,
66
+ token=token,
67
+ force_download=True,
68
+ )
69
  if test_tokenizer:
70
  try:
71
+ _ = AutoTokenizer.from_pretrained(
72
+ model_name,
73
+ revision=revision,
74
+ trust_remote_code=trust_remote_code,
75
+ token=token,
76
  )
77
  except ValueError as e:
78
+ return (
79
+ False,
80
+ f"uses a tokenizer which is not in a transformers release: {e}",
81
+ None,
82
+ )
83
  except Exception:
84
  return (
85
  False,
 
104
  except Exception as e:
105
  if "You are trying to access a gated repo." in str(e):
106
  return True, "uses a gated model.", None
107
+ return (
108
+ False,
109
+ f"was not found or misconfigured on the hub! Error raised was {e.args[0]}",
110
+ None,
111
+ )
112
 
113
 
114
+ def get_model_size(
115
+ model_info: ModelInfo, precision: str, base_model: str | None
116
+ ) -> tuple[float | None, str]:
117
  size_pattern = re.compile(r"(\d+\.)?\d+(b|m)")
118
  safetensors = None
119
  adapter_safetensors = None
120
  # hack way to check that model is adapter
121
+ is_adapter = "adapter_config.json" in (
122
+ s.rfilename for s in model_info.siblings
123
+ )
124
 
125
  try:
126
  if is_adapter:
127
  if not base_model:
128
+ return (
129
+ None,
130
+ "Adapter model submission detected. Please ensure the base model information is provided.",
131
+ )
132
 
133
+ adapter_safetensors = parse_safetensors_file_metadata(
134
+ model_info.id, "adapter_model.safetensors"
135
+ )
136
  safetensors = get_safetensors_metadata(base_model)
137
  else:
138
  safetensors = get_safetensors_metadata(model_info.id)
139
  except Exception as e:
140
+ logging.warning(
141
+ f"Failed to get safetensors metadata for model {model_info.id}: {e!s}"
142
+ )
143
 
144
  if safetensors is not None:
145
  model_size = sum(safetensors.parameter_count.values())
 
151
  size_match = re.search(size_pattern, model_info.id.lower())
152
  if size_match:
153
  model_size = size_match.group(0)
154
+ model_size = round(
155
+ float(model_size[:-1])
156
+ if model_size[-1] == "b"
157
+ else float(model_size[:-1]) / 1e3,
158
+ 3,
159
+ )
160
  else:
161
  return None, "Unknown model size"
162
  except AttributeError:
163
+ logging.warning(
164
+ f"Unable to parse model size from ID: {model_info.id}"
165
+ )
166
  return None, "Unknown model size"
167
 
168
+ size_factor = (
169
+ 8 if (precision == "GPTQ" or "gptq" in model_info.id.lower()) else 1
170
+ )
171
  model_size = size_factor * model_size
172
 
173
  return model_size, ""
174
 
175
+
176
  def get_model_arch(model_info: ModelInfo):
177
  return model_info.config.get("architectures", "Unknown")
178
 
179
+
180
  def check_chat_template(model: str, revision: str) -> tuple[bool, str]:
181
  try:
182
  # Attempt to download only the tokenizer_config.json file
 
184
  repo_id=model,
185
  filename="tokenizer_config.json",
186
  revision=revision,
187
+ repo_type="model",
188
  )
189
 
190
  # Read and parse the tokenizer_config.json file
191
+ with open(config_file, "r") as f:
192
  tokenizer_config = json.load(f)
193
 
194
  # Check if chat_template exists in the tokenizer configuration
195
+ if "chat_template" not in tokenizer_config:
196
+ return (
197
+ False,
198
+ f"The model {model} doesn't have a chat_template in its tokenizer_config.json. Please add a chat_template before submitting or submit without it.",
199
+ )
200
 
201
  return True, ""
202
  except Exception as e:
203
+ return (
204
+ False,
205
+ f"Error checking chat_template for model {model}: {str(e)}",
206
+ )
207
+
208
+
209
  def get_model_tags(model_card, model: str):
210
  is_merge_from_metadata = False
211
  is_moe_from_metadata = False
 
215
  return tags
216
  if model_card.data.tags:
217
  is_merge_from_metadata = any(
218
+ [
219
+ tag in model_card.data.tags
220
+ for tag in ["merge", "moerge", "mergekit", "lazymergekit"]
221
+ ]
222
+ )
223
+ is_moe_from_metadata = any(
224
+ [tag in model_card.data.tags for tag in ["moe", "moerge"]]
225
  )
 
226
 
227
  is_merge_from_model_card = any(
228
+ keyword in model_card.text.lower()
229
+ for keyword in ["merged model", "merge model", "moerge"]
230
  )
231
  if is_merge_from_model_card or is_merge_from_metadata:
232
  tags.append("merge")
233
+ is_moe_from_model_card = any(
234
+ keyword in model_card.text.lower() for keyword in ["moe", "mixtral"]
235
+ )
236
  # Hardcoding because of gating problem
237
  if "Qwen/Qwen1.5-32B" in model:
238
  is_moe_from_model_card = False
239
+ is_moe_from_name = "moe" in model.lower().replace("/", "-").replace(
240
+ "_", "-"
241
+ ).split("-")
242
  if is_moe_from_model_card or is_moe_from_name or is_moe_from_metadata:
243
  tags.append("moe")
244
 
245
  return tags
246
 
247
+
248
+ def validate_model(
249
+ model, precision, base_model, weight_type, use_chat_template
250
+ ):
251
+ """
252
+ Validate model with some checkers to assure tha can be evaluated
253
+ :param model: hf model name
254
+ :param precision: model parameters data type
255
+ :param base_model: base model (if it is need it)
256
+ :param weight_type:
257
+ :param use_chat_template:
258
+ :return:
259
+ """
260
+ API = HfApi()
261
+
262
+ try:
263
+ model_info = API.model_info(repo_id=model, revision="main")
264
+ except:
265
+ return (
266
+ "Could not get your model information. Please fill it up properly."
267
+ )
268
+
269
+ # Check model size early
270
+ model_size, error_text = get_model_size(
271
+ model_info=model_info, precision=precision, base_model=base_model
272
+ )
273
+ if model_size is None:
274
+ return error_text
275
+
276
+ # Absolute size limit for float16 and bfloat16
277
+ if precision in ["float16", "bfloat16"] and model_size > 100:
278
+ error_message = (
279
+ f"Sadly, models larger than 100B parameters cannot be submitted in {precision} precision at this time. "
280
+ f"Your model size: {model_size:.2f}B parameters."
281
+ )
282
+ return error_message
283
+
284
+ # Precision-adjusted size limit for 8bit, 4bit, and GPTQ
285
+ if precision in ["8bit", "4bit", "GPTQ"]:
286
+ size_checker = ModelSizeChecker(
287
+ model=model, precision=precision, model_size_in_b=model_size
288
+ )
289
+
290
+ if not size_checker.can_evaluate():
291
+ precision_factor = size_checker.get_precision_factor()
292
+ max_size = 140 * precision_factor
293
+ error_message = (
294
+ f"Sadly, models this big ({model_size:.2f}B parameters) cannot be evaluated automatically "
295
+ f"at the moment on our cluster. The maximum size for {precision} precision is {max_size:.2f}B parameters."
296
+ )
297
+ return error_message
298
+
299
+ architecture = "?"
300
+ # Is the model on the hub?
301
+ if weight_type in ["Delta", "Adapter"]:
302
+ base_model_on_hub, error, _ = is_model_on_hub(
303
+ model_name=base_model,
304
+ revision="main",
305
+ token=None,
306
+ test_tokenizer=True,
307
+ )
308
+ if not base_model_on_hub:
309
+ return f'Base model "{base_model}" {error}'
310
+ if not weight_type == "Adapter":
311
+ model_on_hub, error, model_config = is_model_on_hub(
312
+ model_name=model, revision=model_info.sha, test_tokenizer=True
313
+ )
314
+ if not model_on_hub or model_config is None:
315
+ return f'Model "{model}" {error}'
316
+ if model_config is not None:
317
+ architectures = getattr(model_config, "architectures", None)
318
+ if architectures:
319
+ architecture = ";".join(architectures)
320
+
321
+ # Were the model card and license filled?
322
+ try:
323
+ _ = model_info.cardData["license"]
324
+ except Exception:
325
+ return "Please select a license for your model"
326
+
327
+ modelcard_OK, error_msg, model_card = check_model_card(model)
328
+ if not modelcard_OK:
329
+ return error_msg
330
+
331
+ # Check the chat template submission
332
+ if use_chat_template:
333
+ chat_template_valid, chat_template_error = check_chat_template(
334
+ model, "main"
335
+ )
336
+ if not chat_template_valid:
337
+ return chat_template_error
338
+
339
+ return None
src/submit.py CHANGED
@@ -1,6 +1,8 @@
1
- from transformers import AutoConfig
2
  from dataclasses import dataclass
3
 
 
 
 
4
  @dataclass
5
  class ModelSizeChecker:
6
  model: str
 
 
1
  from dataclasses import dataclass
2
 
3
+ from transformers import AutoConfig
4
+
5
+
6
  @dataclass
7
  class ModelSizeChecker:
8
  model: str