Xmaster6y commited on
Commit
bfabd59
·
1 Parent(s): 0bc7129

initial setup

Browse files
.gitignore ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ target/
76
+
77
+ # Jupyter Notebook
78
+ .ipynb_checkpoints
79
+
80
+ # IPython
81
+ profile_default/
82
+ ipython_config.py
83
+
84
+ # pipenv
85
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
86
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
87
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
88
+ # install all needed dependencies.
89
+ #Pipfile.lock
90
+
91
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
92
+ __pypackages__/
93
+
94
+ # Celery stuff
95
+ celerybeat-schedule
96
+ celerybeat.pid
97
+
98
+ # SageMath parsed files
99
+ *.sage.py
100
+
101
+ # Environments
102
+ .env
103
+ .venv
104
+ env/
105
+ venv/
106
+ ENV/
107
+ env.bak/
108
+ venv.bak/
109
+
110
+ # Spyder project settings
111
+ .spyderproject
112
+ .spyproject
113
+
114
+ # Rope project settings
115
+ .ropeproject
116
+
117
+ # mkdocs documentation
118
+ /site
119
+
120
+ # mypy
121
+ .mypy_cache/
122
+ .dmypy.json
123
+ dmypy.json
124
+
125
+ # Pyre type checker
126
+ .pyre/
127
+
128
+ # Pickle files
129
+ *.pkl
130
+
131
+ # Various files
132
+ ignored
133
+ debug
134
+ *.zip
135
+ lc0
136
+ !bin/lc0
137
+ wandb
138
+ **/.DS_Store
139
+
140
+ *secret*
README.md CHANGED
@@ -1,12 +1,14 @@
1
  ---
2
  title: Lczerolens Backends Demo
3
- emoji: 📚
4
  colorFrom: gray
5
- colorTo: red
6
  sdk: gradio
7
  sdk_version: 5.28.0
8
  app_file: app.py
9
- pinned: false
 
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
  title: Lczerolens Backends Demo
3
+ emoji: 🔬
4
  colorFrom: gray
5
+ colorTo: green
6
  sdk: gradio
7
  sdk_version: 5.28.0
8
  app_file: app.py
9
+ pinned: true
10
+ license: mit
11
+ short_description: Demo lczerolens backends features.
12
  ---
13
 
14
+ See the documentation [here](https://lczerolens.readthedocs.io/).
app.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Gradio demo for lczerolens backends.
3
+ """
4
+
5
+ import gradio as gr
6
+
7
+ from demo.interfaces import convert
8
+
9
+ demo = gr.TabbedInterface(
10
+ [
11
+ convert.interface,
12
+ ],
13
+ [
14
+ "Convert",
15
+ ],
16
+ title="Lczero Backends Demo",
17
+ analytics_enabled=False,
18
+ )
19
+
20
+ if __name__ == "__main__":
21
+ demo.launch(
22
+ server_port=8000,
23
+ server_name="0.0.0.0",
24
+ )
demo/__init__.py ADDED
File without changes
demo/constants.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Constants for the demo.
3
+ """
4
+
5
+ import os
6
+
7
+ ONNX_MODEL_DIRECTORY = "demo/onnx-models"
8
+ LEELA_MODEL_DIRECTORY = "demo/leela-models"
9
+ FIGURE_DIRECTORY = "demo/figures"
10
+
11
+ ONNX_MODEL_NAMES = [
12
+ f for f in os.listdir(ONNX_MODEL_DIRECTORY)
13
+ if f.endswith(".onnx")
14
+ ]
15
+ LEELA_MODEL_NAMES = [
16
+ f for f in os.listdir(LEELA_MODEL_DIRECTORY)
17
+ if f.endswith(".pb.gz")
18
+ ]
demo/figures/.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ *
2
+ !.gitignore
demo/interfaces/__init__.py ADDED
File without changes
demo/interfaces/convert.py ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Gradio interface for converting models.
3
+ """
4
+
5
+ import os
6
+ import uuid
7
+ import re
8
+ import subprocess
9
+
10
+ import gradio as gr
11
+
12
+ from demo import constants, utils
13
+ from lczerolens import backends
14
+
15
+ def get_models_info(onnx=True, leela=True):
16
+ """
17
+ Get the names of the models in the model directory.
18
+ """
19
+ model_df = []
20
+ exp = r"(?P<n_filters>\d+)x(?P<n_blocks>\d+)"
21
+ if onnx:
22
+ for filename in os.listdir(constants.MODEL_DIRECTORY):
23
+ if filename.endswith(".onnx"):
24
+ match = re.search(exp, filename)
25
+ if match is None:
26
+ n_filters = -1
27
+ n_blocks = -1
28
+ else:
29
+ n_filters = int(match.group("n_filters"))
30
+ n_blocks = int(match.group("n_blocks"))
31
+ model_df.append(
32
+ [
33
+ filename,
34
+ "ONNX",
35
+ n_blocks,
36
+ n_filters,
37
+ ]
38
+ )
39
+ if leela:
40
+ for filename in os.listdir(constants.LEELA_MODEL_DIRECTORY):
41
+ if filename.endswith(".pb.gz"):
42
+ match = re.search(exp, filename)
43
+ if match is None:
44
+ n_filters = -1
45
+ n_blocks = -1
46
+ else:
47
+ n_filters = int(match.group("n_filters"))
48
+ n_blocks = int(match.group("n_blocks"))
49
+ model_df.append(
50
+ [
51
+ filename,
52
+ "LEELA",
53
+ n_blocks,
54
+ n_filters,
55
+ ]
56
+ )
57
+ return model_df
58
+
59
+
60
+ def save_model(tmp_file_path):
61
+ """
62
+ Save the model to the model directory.
63
+ """
64
+ popen = subprocess.Popen(
65
+ ["file", tmp_file_path],
66
+ stdout=subprocess.PIPE,
67
+ stderr=subprocess.PIPE,
68
+ )
69
+ popen.wait()
70
+ if popen.returncode != 0:
71
+ raise RuntimeError
72
+ file_desc = popen.stdout.read().decode("utf-8").split(tmp_file_path)[1].strip()
73
+ rename_match = re.search(r"was\s\"(?P<name>.+)\"", file_desc)
74
+ type_match = re.search(r"\:\s(?P<type>[a-zA-Z]+)", file_desc)
75
+ if rename_match is None or type_match is None:
76
+ raise RuntimeError
77
+ model_name = rename_match.group("name")
78
+ model_type = type_match.group("type")
79
+ if model_type != "gzip":
80
+ raise RuntimeError
81
+ os.rename(
82
+ tmp_file_path,
83
+ f"{constants.LEELA_MODEL_DIRECTORY}/{model_name}.gz",
84
+ )
85
+ try:
86
+ backends.describenet(
87
+ f"{constants.LEELA_MODEL_DIRECTORY}/{model_name}.gz",
88
+ )
89
+ except RuntimeError:
90
+ os.remove(f"{constants.LEELA_MODEL_DIRECTORY}/{model_name}.gz")
91
+ raise RuntimeError
92
+
93
+
94
+ def list_models():
95
+ """
96
+ List the models in the model directory.
97
+ """
98
+ models_info = utils.get_models_info()
99
+ return sorted([[model_info[0]] for model_info in models_info])
100
+
101
+
102
+ def on_select_model_df(
103
+ evt: gr.SelectData,
104
+ ):
105
+ """
106
+ When a model is selected, update the statement.
107
+ """
108
+ return evt.value
109
+
110
+
111
+ def convert_model(
112
+ model_name: str,
113
+ ):
114
+ """
115
+ Convert the model.
116
+ """
117
+ if model_name == "":
118
+ gr.Warning(
119
+ "Please select a model.",
120
+ )
121
+ return list_models(), ""
122
+ if model_name.endswith(".onnx"):
123
+ gr.Warning(
124
+ "ONNX conversion not implemented.",
125
+ )
126
+ return list_models(), ""
127
+ try:
128
+ backends.convert_to_onnx(
129
+ f"{constants.LEELA_MODEL_DIRECTORY}/{model_name}",
130
+ f"{constants.MODEL_DIRECTORY}/{model_name[:-6]}.onnx",
131
+ )
132
+ except RuntimeError:
133
+ gr.Warning(
134
+ f"Could not convert net at `{model_name}`.",
135
+ )
136
+ return list_models(), "Conversion failed"
137
+ return list_models(), "Conversion successful"
138
+
139
+
140
+ def upload_model(
141
+ model_file: gr.File,
142
+ ):
143
+ """
144
+ Convert the model.
145
+ """
146
+ if model_file is None:
147
+ gr.Warning(
148
+ "File not uploaded.",
149
+ )
150
+ return list_models()
151
+ try:
152
+ id = uuid.uuid4()
153
+ tmp_file_path = f"{constants.LEELA_MODEL_DIRECTORY}/{id}"
154
+ with open(
155
+ tmp_file_path,
156
+ "wb",
157
+ ) as f:
158
+ f.write(model_file)
159
+ utils.save_model(tmp_file_path)
160
+ except RuntimeError:
161
+ gr.Warning(
162
+ "Invalid file type.",
163
+ )
164
+ finally:
165
+ if os.path.exists(tmp_file_path):
166
+ os.remove(tmp_file_path)
167
+ return list_models()
168
+
169
+
170
+ def get_model_description(
171
+ model_name: str,
172
+ ):
173
+ """
174
+ Get the model description.
175
+ """
176
+ if model_name == "":
177
+ gr.Warning(
178
+ "Please select a model.",
179
+ )
180
+ return ""
181
+ if model_name.endswith(".onnx"):
182
+ gr.Warning(
183
+ "ONNX description not implemented.",
184
+ )
185
+ return ""
186
+ try:
187
+ description = backends.describenet(
188
+ f"{constants.LEELA_MODEL_DIRECTORY}/{model_name}",
189
+ )
190
+ except RuntimeError:
191
+ raise gr.Error(
192
+ f"Could not describe net at `{model_name}`.",
193
+ )
194
+ return description
195
+
196
+
197
+ def get_model_path(
198
+ model_name: str,
199
+ ):
200
+ """
201
+ Get the model path.
202
+ """
203
+ if model_name == "":
204
+ gr.Warning(
205
+ "Please select a model.",
206
+ )
207
+ return None
208
+ if model_name.endswith(".onnx"):
209
+ return f"{constants.MODEL_DIRECTORY}/{model_name}"
210
+ else:
211
+ return f"{constants.LEELA_MODEL_DIRECTORY}/{model_name}"
212
+
213
+
214
+ with gr.Blocks() as interface:
215
+ model_file = gr.File(type="binary")
216
+ upload_button = gr.Button(
217
+ value="Upload",
218
+ )
219
+ with gr.Row():
220
+ with gr.Column(scale=2):
221
+ model_df = gr.Dataframe(
222
+ headers=["Available models"],
223
+ datatype=["str"],
224
+ interactive=False,
225
+ type="array",
226
+ value=list_models,
227
+ )
228
+ with gr.Column(scale=1):
229
+ with gr.Row():
230
+ model_name = gr.Textbox(label="Selected model", lines=1, interactive=False, scale=7)
231
+ conversion_status = gr.Textbox(
232
+ label="Conversion status",
233
+ lines=1,
234
+ interactive=False,
235
+ )
236
+
237
+ convert_button = gr.Button(
238
+ value="Convert",
239
+ )
240
+ describe_button = gr.Button(
241
+ value="Describe model",
242
+ )
243
+ model_description = gr.Textbox(
244
+ label="Model description",
245
+ lines=1,
246
+ interactive=False,
247
+ )
248
+ download_button = gr.Button(
249
+ value="Get download link",
250
+ )
251
+ download_file = gr.File(
252
+ type="filepath",
253
+ label="Download link",
254
+ interactive=False,
255
+ )
256
+
257
+ model_df.select(
258
+ on_select_model_df,
259
+ None,
260
+ model_name,
261
+ )
262
+ upload_button.click(
263
+ upload_model,
264
+ model_file,
265
+ model_df,
266
+ )
267
+ convert_button.click(
268
+ convert_model,
269
+ model_name,
270
+ [model_df, conversion_status],
271
+ )
272
+ describe_button.click(
273
+ get_model_description,
274
+ model_name,
275
+ model_description,
276
+ )
277
+ download_button.click(
278
+ get_model_path,
279
+ model_name,
280
+ download_file,
281
+ )
demo/leela-models/.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ *
2
+ !.gitignore
demo/onnx-models/.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ *
2
+ !.gitignore
demo/utils.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import gradio as gr
3
+ import chess.svg
4
+ from typing import Optional
5
+
6
+ from lczerolens.board import LczeroBoard
7
+
8
+ from . import constants
9
+
10
+
11
+ def create_board_figure(
12
+ board: LczeroBoard,
13
+ *,
14
+ orientation: bool = chess.WHITE,
15
+ arrows: str = "",
16
+ square: str = "",
17
+ name: str = "board",
18
+ ):
19
+ try:
20
+ if arrows:
21
+ arrows_list = arrows.split(" ")
22
+ chess_arrows = []
23
+ for arrow in arrows_list:
24
+ from_square, to_square = arrow[:2], arrow[2:]
25
+ chess_arrows.append(
26
+ (
27
+ chess.parse_square(from_square),
28
+ chess.parse_square(to_square),
29
+ )
30
+ )
31
+ else:
32
+ chess_arrows = []
33
+ except ValueError:
34
+ chess_arrows = []
35
+ gr.Warning("Invalid arrows, using none.")
36
+
37
+ try:
38
+ color_dict = {chess.parse_square(square): "#FF0000"} if square else {}
39
+ except ValueError:
40
+ color_dict = {}
41
+ gr.Warning("Invalid square, using none.")
42
+
43
+ svg_board = chess.svg.board(
44
+ board,
45
+ size=350,
46
+ orientation=orientation,
47
+ arrows=chess_arrows,
48
+ fill=color_dict,
49
+ )
50
+ with open(f"{constants.FIGURE_DIRECTORY}/{name}.svg", "w") as f:
51
+ f.write(svg_board)
52
+ return f"{constants.FIGURE_DIRECTORY}/{name}.svg"
pyproject.toml ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "lczerolens-backends-demo"
3
+ version = "0.1.0"
4
+ description = "Demo lczerolens backends features."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ dependencies = [
8
+ "gdown>=5.2.0",
9
+ "gradio>=5.20.1",
10
+ "lczerolens>=0.3.1",
11
+ "lczero-bindings",
12
+ ]
13
+
14
+ [tool.uv.sources]
15
+ lczero-bindings = { git = "https://github.com/LeelaChessZero/lc0.git", rev = "dfcd1c314150b93125697491bef1221841443c18" }
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ lczerolens>=0.3.1
2
+ lczero-bindings @ git+https://github.com/LeelaChessZero/lc0.git
uv.lock ADDED
The diff for this file is too large to render. See raw diff