File size: 10,433 Bytes
0345768
 
 
 
 
8ca56b3
fdd6860
0345768
6460cc3
 
 
 
 
0345768
b775a96
4797320
e4bb5d4
7e0c7d3
0345768
3e317b0
 
 
 
 
6677ed7
3e317b0
 
 
 
 
 
d85840f
b8ac61b
3e317b0
 
4797320
1ec80ac
4797320
 
 
 
8ca56b3
 
4797320
 
b7b387b
4797320
8ca56b3
4797320
8ec7dbf
b7b387b
0345768
4797320
c500ff5
 
 
4797320
 
 
 
 
 
c500ff5
 
 
4797320
 
 
 
c500ff5
 
 
4797320
 
 
8805249
4797320
 
 
 
 
8805249
4797320
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bf5fb31
4797320
 
b219560
 
 
 
9377dea
b219560
 
b0b5c7c
b219560
 
8f8e04c
b219560
4797320
 
8805249
 
 
 
 
 
6677ed7
8805249
 
 
 
6677ed7
8805249
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41930b6
8805249
41930b6
8805249
41930b6
 
8805249
 
 
 
 
41930b6
8805249
41930b6
8805249
 
 
 
41930b6
8805249
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
027ed4c
c500ff5
ba3ba40
c500ff5
4797320
8f8e04c
 
ba3ba40
1ec80ac
 
 
 
4797320
 
c500ff5
9c0f9b6
0437553
1ec80ac
 
c500ff5
4797320
fdd6860
e1b4bc9
8f8e04c
 
9c0f9b6
8f8e04c
9c0f9b6
027ed4c
f6b0264
 
 
4797320
0345768
8f8e04c
 
8317a79
8f8e04c
 
 
 
 
 
b8ac61b
ba3ba40
428b978
3fc3e48
8f8e04c
0345768
8f8e04c
276e364
 
 
8f8e04c
ee62d83
c48f5d1
6ed66b8
8530d2f
7a1deb2
 
 
8530d2f
8f8e04c
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
from typing import List

import numpy as np
import requests
import gradio as gr
import time
import os

from huggingface_hub import (
    create_repo,
    get_full_repo_name,
    upload_file,
)


class SpaceBuilder:
    error_message =  None
    url = None

    @classmethod
    def split_space_names(cls, names: str) -> List[str]:
        """
        Splits and filters the given space_names.

        :param names: space names
        :return: Name List
        """
        name_list = names.split("\n")
        filtered_list = []
        for name in name_list:
            if not (name == "" or name.isspace()):
                name = name.replace(" ", "")
                filtered_list.append(name)
        return filtered_list

    @classmethod
    def file_as_a_string(cls, name_list: List[str], title: str, description: str) -> str:
        """
        Returns the file that is going to be created in the new space as string.

        :param name_list: list of space names
        :param title: title
        :param description: description
        :return: file as a string
        """
        return (
            f"import gradio as gr"
            f"\nname_list = {name_list}"
            f"\ninterfaces = [gr.Interface.load(name) for name in name_list]"
            f"\ngr.mix.Parallel(*interfaces, title=\"{title}\", description=\"{description}\").launch()"
        )

    @classmethod
    def control_input_and_output_types(
        cls, interface_list: List["gr.Interface"]
    ) -> bool:
        """
        Controls whether if input and output types of the given interfaces are the same.

        :param interface_list: list of interfaces
        :return: True if all input and output types are the same
        """
        first_input_types = [
            type(input) for input in interface_list[0].input_components
        ]
        first_output_types = [
            type(output) for output in interface_list[0].output_components
        ]
        for interface in interface_list:
            interface_input_types = [
                type(input) for input in interface.input_components
            ]
            if not np.all(
                interface_input_types == first_input_types
            ):  # Vectorize the comparison and don't use double for loop
                cls.error_message = "Provided space input types are different"
                return False
            interface_output_types = [
                type(output) for output in interface.output_components
            ]
            if not np.all(interface_output_types == first_output_types):
                cls.error_message = "Provided space output types are different"
                return False

        return True

    @classmethod
    def check_space_name_availability(cls, hf_token: str, space_name: str) -> bool:
        """
        Check whether if the space_name is currently used.

        :param hf_token: hugging_face token
        :param space_name:
        :return: True if the space_name is available
        """
        try:
            repo_name = get_full_repo_name(model_id=space_name, token=hf_token)
        except Exception as ex:
            print(ex)
            cls.error_message = "You have given an incorrect HuggingFace token"
            return False
        try:
            url = f"https://huggingface.co/spaces/{repo_name}"
            response = requests.get(url)
            if response.status_code == 200:
                cls.error_message = f"The {repo_name} is already used."
                return False
            else:
                print(f"The space name {repo_name} is available")
                return True
        except Exception as ex:
            print(ex)
            cls.error_message = "Can not send a request to https://huggingface.co"
            return False

    @classmethod
    def load_and_check_spaces(cls, names: str) -> bool:
        """
        Loads given space inputs as interfaces and checks whether if they are loadable.

        :param names: Input space names
        :return: True if check is successful
        """
        name_list = cls.split_space_names(names)

        try:
            # We could gather these interfaces in parallel if gradio was supporting async gathering. It will probably be possible after the migration to the FastAPI is completed.
            interfaces = [gr.Interface.load(name) for name in name_list]
        except Exception as ex:
            print(ex)
            cls.error_message = (
                f"One of the given space cannot be loaded to gradio, sorry for the inconvenience. "
                f"\nPlease use different input space names!"
            )
            return False
        if not cls.control_input_and_output_types(interfaces):
            return False
        else:
            print("Loaded and checked input spaces, great it works!")
            return True

    @classmethod
    def create_space(cls, input_space_names: str, target_space_name: str, hf_token: str, title: str, description: str) -> bool:
        """
        Creates the target space with the given space names.

        :param input_space_names: Input space name_list
        :param target_space_name: Target space_name
        :param hf_token: HuggingFace Write Token
        :param title: Target Interface Title
        :param description: Target Interface Description
        :return: True if success
        """
        name_list = cls.split_space_names(input_space_names)
        try:
            create_repo(name=target_space_name, token=hf_token, repo_type="space", space_sdk="gradio")
        except Exception as ex:
            print(ex)
            cls.error_message = "Please provide a correct space name as Only regular characters and '-', '_', '.' accepted. '--' and '..' are forbidden. '-' and '.' cannot start or end the name."
            return False
        repo_name = get_full_repo_name(model_id=target_space_name, token=hf_token)

        try:
            file_string = cls.file_as_a_string(name_list, title, description)
            temp_file = open("temp_file.txt", "w")
            temp_file.write(file_string)
            temp_file.close()
        except Exception as ex:
            print(ex)
            cls.error_message = "An exception occurred during temporary file writing"
            return False
        try:
            file_url = upload_file(
                path_or_fileobj="temp_file.txt",
                path_in_repo="app.py",
                repo_id=repo_name,
                repo_type="space",
                token=hf_token,
            )
            cls.url = f"https://huggingface.co/spaces/{repo_name}"
            return True
        except Exception as ex:
            print(ex)
            cls.error_message = (
                "An exception occurred during writing app.py to the target space"
            )
            return False

    @staticmethod
    def build_space(
        model_or_space_names: str, hf_token: str, target_space_name: str, interface_title: str, interface_description: str
    ) -> str:
        """
        Creates a space with given input spaces.

        :param model_or_space_names: Multiple model or space names split with new lines
        :param hf_token: HuggingFace token
        :param target_space_name: Target Space Name
        :param interface_title: Target Interface Title
        :param interface_description: Target Interface Description
        :return:
        """
        if (
            model_or_space_names== "" or model_or_space_names.isspace()
            or target_space_name == "" or target_space_name.isspace()
            or interface_title == "" or interface_title.isspace()
            or interface_description == "" or interface_description.isspace()
        ):
            return "Please fill all the inputs"
        if hf_token == "" or hf_token.isspace():
            hf_token = os.environ['HF_SELF_TOKEN']
        if not SpaceBuilder.check_space_name_availability(hf_token=hf_token, space_name=target_space_name):
            return SpaceBuilder.error_message
        if not SpaceBuilder.load_and_check_spaces(names=model_or_space_names):
            return SpaceBuilder.error_message
        if not SpaceBuilder.create_space(input_space_names=model_or_space_names, target_space_name=target_space_name, hf_token=hf_token, title=interface_title, description=interface_description):
            return SpaceBuilder.error_message
        
        url = SpaceBuilder.url
        return f"<a href={url}>{url}</a>"



if __name__ == "__main__":
    print(f"Gradio Version: {gr.__version__}")
    iface = gr.Interface(
        fn=SpaceBuilder.build_space,
        inputs=[
            gr.inputs.Textbox(
                lines=4,
                placeholder=(
                    f"Drop model and space links at each line and I will create a new space comparing them. Usage examples:"
                    f"\nspaces/onnx/GPT-2"
                    f"\nmodels/gpt2-large"
                    f"\nmodels/gpt2"
                ),
            ),
            gr.inputs.Textbox(lines=1, placeholder="HuggingFace Write Token"),
            gr.inputs.Textbox(lines=1, placeholder="Name for the target space, ie. space-building-space"),
            gr.inputs.Textbox(lines=1, placeholder="Title for the target space interface, ie. Title"),
            gr.inputs.Textbox(lines=1, placeholder="Description for the target space interface, ie. Description"),
        ],
        title="Model Comparator Space Builder",
        description="Welcome onboard 🤗, I can create a comparative space which will compare the models and/or spaces you provide to me. You can get your HF Write Token from [here](https://huggingface.co/settings/tokens). If you leave HF Token input empty, the space will release under the author's account, [farukozderim](https://huggingface.co/farukozderim). Finally, you can publish spaces as a clone of other spaces if you provide just a single model or space. Have fun :)",
        outputs=gr.outputs.HTML(label="URL"),
        examples= [
            ["spaces/onnx/GPT-2 \nmodels/gpt2-large \nmodels/EleutherAI/gpt-j-6B", "", "comparison-space", "example-title", "example-description"],
            ["spaces/onnx/GPT-2", "", "duplicate-space", "example-title", "example-description"],
            ["models/EleutherAI/gpt-j-6B", "", "space-from-a-model", "example-title", "example-description"]
       ],
    )
    iface.launch()