yangzhitao commited on
Commit
d1fd905
·
1 Parent(s): ae66b87

feat: refactor schemas and routes for community submission and file upload

Browse files

- Introduced new schemas for community submission and file upload, enhancing the backend's capability to handle evaluation requests.
- Updated routes to include a new endpoint for uploading files and refactored the existing community submit functionality.
- Removed deprecated submit parameters and streamlined response data handling for improved clarity and maintainability.

Files changed (2) hide show
  1. src/backend/routes/hf.py +65 -33
  2. src/backend/schemas.py +92 -34
src/backend/routes/hf.py CHANGED
@@ -1,6 +1,7 @@
1
  import io
2
  from dataclasses import asdict
3
- from typing import TYPE_CHECKING, Annotated
 
4
 
5
  from fastapi import APIRouter, Depends
6
  from loguru import logger
@@ -8,12 +9,13 @@ from loguru import logger
8
  from src.backend.config import settings
9
  from src.backend.schemas import (
10
  CommitInfo,
 
11
  GetModelInfo_QueryParams,
12
  GetModelInfo_RespData,
13
  HfRepoUrl,
14
  ResponseData,
15
- Submit_Params,
16
- Submit_RespData,
17
  )
18
 
19
  if TYPE_CHECKING:
@@ -23,16 +25,53 @@ if TYPE_CHECKING:
23
  router = APIRouter(tags=["huggingface"])
24
 
25
 
26
- @router.post("/community/submit/")
27
- async def submit(params: Annotated[Submit_Params, Depends()]) -> ResponseData[Submit_RespData]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  """Submit a new evaluation request to the Hugging Face repository."""
29
  file_obj = io.BytesIO(params.content.encode("utf-8"))
30
- commit_info = settings.hf_api.upload_file(
31
  path_or_fileobj=file_obj,
32
  path_in_repo=params.path_in_repo,
 
33
  repo_id=settings.REQUESTS_REPO_ID,
34
  repo_type="dataset",
35
- commit_message=params.commit_message,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  )
37
  data_dict = asdict(commit_info)
38
  try:
@@ -44,35 +83,28 @@ async def submit(params: Annotated[Submit_Params, Depends()]) -> ResponseData[Su
44
  "repo_type": commit_info.repo_url.repo_type,
45
  "url": commit_info.repo_url.url,
46
  })
47
- return ResponseData(
48
- data=Submit_RespData.model_validate({
49
- **data_dict,
50
- "repo_url": repo_url,
51
- })
52
- )
53
  except Exception as e:
54
  msg = f"Failed to validate repo url: {e}"
55
  logger.warning(msg)
56
- return ResponseData(
57
- code=500,
58
- msg=msg,
59
- data=Submit_RespData.model_validate(data_dict),
60
- )
61
 
62
 
63
- # Get model info
64
- @router.get("/models/info/")
65
- async def get_model_info(
66
- params: Annotated[GetModelInfo_QueryParams, Depends()],
67
- ) -> ResponseData[GetModelInfo_RespData]:
68
- # Get model info
69
- model: ModelInfo = settings.hf_api.model_info(params.model_id, revision=params.revision or None)
70
- # Get model commit history
71
- commit_infos = settings.hf_api.list_repo_commits(repo_id=params.model_id, repo_type="model")
72
- commits = [CommitInfo.model_validate(asdict(c)) for c in commit_infos]
73
- # Response data
74
- data = GetModelInfo_RespData.model_validate({
75
- **asdict(model),
76
- "commits": commits or None,
77
- })
78
  return ResponseData(data=data)
 
1
  import io
2
  from dataclasses import asdict
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING, Annotated, BinaryIO, Literal
5
 
6
  from fastapi import APIRouter, Depends
7
  from loguru import logger
 
9
  from src.backend.config import settings
10
  from src.backend.schemas import (
11
  CommitInfo,
12
+ CommunitySubmit_Params,
13
  GetModelInfo_QueryParams,
14
  GetModelInfo_RespData,
15
  HfRepoUrl,
16
  ResponseData,
17
+ UploadFileContent_Params,
18
+ UploadFileContent_RespData,
19
  )
20
 
21
  if TYPE_CHECKING:
 
25
  router = APIRouter(tags=["huggingface"])
26
 
27
 
28
+ @router.get("/models/info/")
29
+ async def get_model_info(
30
+ params: Annotated[GetModelInfo_QueryParams, Depends()],
31
+ ) -> ResponseData[GetModelInfo_RespData]:
32
+ """Get model info with commit history."""
33
+ model: ModelInfo = settings.hf_api.model_info(params.model_id, revision=params.revision or None)
34
+ # Get model commit history
35
+ commit_infos = settings.hf_api.list_repo_commits(repo_id=params.model_id, repo_type="model")
36
+ commits = [CommitInfo.model_validate(asdict(c)) for c in commit_infos]
37
+ # Response data
38
+ data = GetModelInfo_RespData.model_validate({
39
+ **asdict(model),
40
+ "commits": commits or None,
41
+ })
42
+ return ResponseData(data=data)
43
+
44
+
45
+ @router.post("/upload-file/")
46
+ async def upload_file_content(
47
+ params: Annotated[UploadFileContent_Params, Depends()],
48
+ ) -> ResponseData[UploadFileContent_RespData]:
49
  """Submit a new evaluation request to the Hugging Face repository."""
50
  file_obj = io.BytesIO(params.content.encode("utf-8"))
51
+ data = await upload_file_content_handler(
52
  path_or_fileobj=file_obj,
53
  path_in_repo=params.path_in_repo,
54
+ commit_message=params.commit_message,
55
  repo_id=settings.REQUESTS_REPO_ID,
56
  repo_type="dataset",
57
+ )
58
+ return ResponseData(data=data)
59
+
60
+
61
+ async def upload_file_content_handler(
62
+ path_or_fileobj: str | Path | bytes | BinaryIO,
63
+ path_in_repo: str,
64
+ repo_id: str,
65
+ repo_type: Literal["model", "dataset", "space"] = "dataset",
66
+ commit_message: str | None = None,
67
+ ):
68
+ """Community submit handler."""
69
+ commit_info = settings.hf_api.upload_file(
70
+ path_or_fileobj=path_or_fileobj,
71
+ path_in_repo=path_in_repo,
72
+ repo_id=repo_id,
73
+ repo_type=repo_type,
74
+ commit_message=commit_message,
75
  )
76
  data_dict = asdict(commit_info)
77
  try:
 
83
  "repo_type": commit_info.repo_url.repo_type,
84
  "url": commit_info.repo_url.url,
85
  })
86
+ return UploadFileContent_RespData.model_validate({
87
+ **data_dict,
88
+ "repo_url": repo_url,
89
+ })
 
 
90
  except Exception as e:
91
  msg = f"Failed to validate repo url: {e}"
92
  logger.warning(msg)
93
+ return UploadFileContent_RespData.model_validate(data_dict)
 
 
 
 
94
 
95
 
96
+ @router.post("/community/submit/")
97
+ async def community_submit(
98
+ params: Annotated[CommunitySubmit_Params, Depends()],
99
+ ) -> ResponseData[UploadFileContent_RespData]:
100
+ """Submit a new evaluation request to the Hugging Face repository."""
101
+ file_obj = io.BytesIO(params.content.encode("utf-8"))
102
+ path_in_repo = f"leaderboard-submissions/{params.filename}"
103
+ data = await upload_file_content_handler(
104
+ path_or_fileobj=file_obj,
105
+ path_in_repo=path_in_repo,
106
+ commit_message=params.commit_message,
107
+ repo_id=settings.REQUESTS_REPO_ID,
108
+ repo_type="dataset",
109
+ )
 
110
  return ResponseData(data=data)
src/backend/schemas.py CHANGED
@@ -1,7 +1,8 @@
1
  from datetime import datetime
2
  from typing import Annotated, Any, Generic, Literal, TypeVar
3
 
4
- from pydantic import BaseModel, ConfigDict, Field
 
5
 
6
  T = TypeVar("T", bound=BaseModel)
7
 
@@ -12,39 +13,6 @@ class ResponseData(BaseModel, Generic[T]):
12
  data: T | None = None
13
 
14
 
15
- # --- Submit
16
- class Submit_Params(BaseModel):
17
- path_in_repo: str
18
- commit_message: str | None = None
19
-
20
- model_id: str
21
- model_sha: str = "main"
22
- model_dtype: str
23
-
24
- content: str
25
-
26
-
27
- class Submit_RespData(BaseModel):
28
- model_config = ConfigDict(extra="allow")
29
-
30
- commit_url: str
31
- commit_message: str
32
- commit_description: str
33
- oid: Annotated[str, Field(description="Commit hash id.")]
34
- repo_url: "HfRepoUrl | None" = None
35
-
36
-
37
- class HfRepoUrl(BaseModel):
38
- model_config = ConfigDict(extra="allow")
39
-
40
- endpoint: str
41
- namespace: str
42
- repo_name: str
43
- repo_id: str
44
- repo_type: Literal["model", "dataset", "space"]
45
- url: str
46
-
47
-
48
  # --- Get Model Info ---
49
  class GetModelInfo_QueryParams(BaseModel):
50
  """Parameters for model info query."""
@@ -93,3 +61,93 @@ class CommitInfo(BaseModel):
93
 
94
  formatted_title: str | None = None
95
  formatted_message: str | None = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from datetime import datetime
2
  from typing import Annotated, Any, Generic, Literal, TypeVar
3
 
4
+ from pydantic import BaseModel, ConfigDict, Field, computed_field
5
+ from pydantic_core import PydanticCustomError
6
 
7
  T = TypeVar("T", bound=BaseModel)
8
 
 
13
  data: T | None = None
14
 
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  # --- Get Model Info ---
17
  class GetModelInfo_QueryParams(BaseModel):
18
  """Parameters for model info query."""
 
61
 
62
  formatted_title: str | None = None
63
  formatted_message: str | None = None
64
+
65
+
66
+ # --- Upload File Content ---
67
+ class UploadFileContent_Params(BaseModel):
68
+ path_in_repo: str
69
+ commit_message: str | None = None
70
+
71
+ model_id: str
72
+ model_sha: str = "main"
73
+ model_dtype: str
74
+
75
+ content: str
76
+
77
+
78
+ class UploadFileContent_RespData(BaseModel):
79
+ model_config = ConfigDict(extra="allow")
80
+
81
+ commit_url: str
82
+ commit_message: str
83
+ commit_description: str
84
+ oid: Annotated[str, Field(description="Commit hash id.")]
85
+ repo_url: "HfRepoUrl | None" = None
86
+
87
+
88
+ class HfRepoUrl(BaseModel):
89
+ model_config = ConfigDict(extra="allow")
90
+
91
+ endpoint: str
92
+ namespace: str
93
+ repo_name: str
94
+ repo_id: str
95
+ repo_type: Literal["model", "dataset", "space"]
96
+ url: str
97
+
98
+
99
+ # --- Community Submit ---
100
+ class CommunitySubmit_Params(BaseModel):
101
+ username: Annotated[str, Field(description='The username of the user. e.g. "Qwen"')]
102
+
103
+ # Commit info
104
+ model_id: Annotated[str, Field(description='The model id. e.g. "Qwen/Qwen2.5-3B"')]
105
+ model_sha: Annotated[
106
+ str, Field(description='The model sha or "main". e.g. "3aab1f1954e9cc14eb9509a215f9e5ca08227a9b"')
107
+ ] = "main"
108
+ model_dtype: Literal[
109
+ # float types
110
+ "bfloat16",
111
+ "float32",
112
+ "float16",
113
+ "float64",
114
+ # int types
115
+ "int8",
116
+ "uint8",
117
+ "int16",
118
+ "uint15",
119
+ "int32",
120
+ "uint32",
121
+ "int64",
122
+ "uint64",
123
+ "bool",
124
+ # rare types
125
+ "complex32",
126
+ "complex64",
127
+ "complex128",
128
+ "float8_e4m3fn",
129
+ "float8_e5m2",
130
+ "float8_e4m3fnuz",
131
+ "float8_e5m2fnuz",
132
+ "float8_e8m0fnu",
133
+ "float4_e2m1fn_x2",
134
+ ]
135
+ content: Annotated[str, Field(description='The content of the file in JSON format to upload.')]
136
+ weight_type: Literal["Original"] | str = "Original"
137
+
138
+ @computed_field
139
+ @property
140
+ def filename(self) -> str:
141
+ """Filename of the file to upload."""
142
+ model_name = self.model_id.split("/")[-1]
143
+ if not model_name:
144
+ raise PydanticCustomError(
145
+ "model_id_invalid", "Model id {model_id!r} is invalid.", {"model_id": self.model_id}
146
+ )
147
+ if not self.username:
148
+ raise PydanticCustomError(
149
+ "username_invalid", "Username {username!r} is invalid.", {"username": self.username}
150
+ )
151
+ return f"{model_name}_eval_request_False_{self.model_dtype}_{self.weight_type}_{self.username}.json"
152
+
153
+ commit_message: str | None = None