File size: 6,583 Bytes
0345768
 
 
 
 
 
6460cc3
 
 
 
 
0345768
b775a96
4797320
7e0c7d3
 
0345768
4797320
 
 
 
 
 
 
 
b7b387b
4797320
 
 
 
b7b387b
0345768
4797320
7e0c7d3
4797320
7e0c7d3
4797320
7e0c7d3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bf5fb31
7e0c7d3
 
 
 
 
 
 
 
 
 
 
 
 
bf5fb31
7e0c7d3
 
 
 
 
 
 
 
 
 
4797320
 
7e0c7d3
 
 
bf5fb31
7e0c7d3
 
4797320
7e0c7d3
 
4797320
7e0c7d3
4797320
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bf5fb31
4797320
 
 
 
 
 
 
 
 
7e0c7d3
4797320
027ed4c
7e0c7d3
4797320
 
7e0c7d3
4797320
7e0c7d3
4797320
 
 
7e0c7d3
4797320
bf5fb31
7e0c7d3
 
 
 
 
 
 
 
 
4797320
027ed4c
4797320
0345768
 
4797320
0345768
 
 
 
 
 
 
 
 
4797320
0345768
 
 
 
 
 
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
from typing import List

import numpy as np
import requests
import gradio as gr

from huggingface_hub import (
    create_repo,
    get_full_repo_name,
    upload_file,
)


class SpaceBuilder:
    error_message = None
    url = None

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

        :param name_list: list of space names
        :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).launch()"
        )

    @classmethod
    def create_space(cls, names: str, space_name: str, hf_token: str) -> bool:
        """
        Creates the space.

        :param name_list: Input space name_list
        :param space_name: Target space_name
        :param hf_token: HuggingFace Write Token
        :return: True if success
        """
        name_list = names.split("\n")
        create_repo(name=space_name, token=hf_token, repo_type="space", space_sdk="gradio")
        repo = get_full_repo_name(model_id=space_name, token=hf_token)

        try:
            file_string = cls.file_as_a_string(name_list)
            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:
            cls.url = upload_file(
                path_or_fileobj="temp_file.txt",
                path_in_repo="app.py",
                repo_id=repo,
                repo_type="space",
                token=hf_token,
            )
            return True
        except Exception as ex:
            print(ex)
            cls.error_message = "An exception occurred during writing app.py to the target space"
            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 check is successful
        """
        name_list = names.split("\n")
        try:
            interfaces = [gr.Interface.load(name) for name in name_list]
        except Exception as ex:
            print(ex)
            cls.error_message = "One of the given models cannot be loaded to gradio, sorry for the inconvenience"
            return False
        if not cls.control_input_and_output_types(interfaces):
            cls.error_message = "Spaces have different input or output types, could not combine them!"
            return False
        else:
            return True

    @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
                return False
            interface_output_types = [
                type(output) for output in interface.output_components
            ]
            if not np.all(interface_output_types == first_output_types):
                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
        url = f"https://huggingface.co/spaces/{repo_name}"
        response = requests.get(url)

        if response.status_code == 200:
            cls.error_message = "The the username/space_name is already used for the given token's user"
            return False
        else:
            return False

    @staticmethod
    def build_space(space_names: str, hf_token: str, target_space_name: str, space_description: str) -> str:
        """
        Creates a space with given inputs
        :param space_names:
        :param hf_token:
        :param target_space_name:
        :param space_description:
        :return:
        """
        if space_names == "" or hf_token == "" or target_space_name == "" or space_description == "":
            return "Please fill all the inputs"
        if SpaceBuilder.check_space_name_availability(hf_token=hf_token, space_name=target_space_name):
            print("The space name is available")
            if SpaceBuilder.load_and_check_spaces(names=space_names):
                print("Loaded and checked input spaces")
                if SpaceBuilder.create_space(names=space_names, space_name=target_space_name, hf_token=hf_token):
                    return SpaceBuilder.url
                else:
                    return SpaceBuilder.error_message
            else:
                return SpaceBuilder.error_message
        else:
            return SpaceBuilder.error_message


iface = gr.Interface(
    fn=SpaceBuilder.build_space,
    inputs=[
        gr.inputs.Textbox(
            lines=4,
            placeholder=(
                f"Drop space links at each line, ie:"
                f"\nspaces/Norod78/ComicsHeroHD"
                f"\nspaces/Amrrs/gradio-sentiment-analyzer"
            ),
        ),
        gr.inputs.Textbox(lines=1, placeholder="HuggingFace Write Token"),
        gr.inputs.Textbox(lines=1, placeholder="Name for the space"),
        gr.inputs.Textbox(lines=1, placeholder="Description for the space"),
    ],
    outputs="text",
)
iface.launch()