File size: 6,510 Bytes
1e5eb40
1579e31
 
 
 
 
67e6ea6
18d0b22
 
 
1579e31
 
 
67e6ea6
 
 
 
1579e31
 
 
 
 
 
67e6ea6
 
 
 
 
 
 
 
 
 
 
 
 
 
1579e31
 
 
 
18d0b22
b257fdf
67e6ea6
1579e31
 
67e6ea6
 
 
 
1579e31
 
 
67e6ea6
18d0b22
90bdafc
 
 
 
18d0b22
90bdafc
67e6ea6
18d0b22
90bdafc
 
 
67e6ea6
18d0b22
 
90bdafc
18d0b22
 
46001b0
 
 
 
 
 
67e6ea6
46001b0
 
 
 
 
18d0b22
90bdafc
 
 
 
 
 
 
 
18d0b22
90bdafc
46001b0
 
 
 
 
 
67e6ea6
46001b0
 
 
 
 
90bdafc
18d0b22
90bdafc
 
 
 
 
 
 
 
 
 
46001b0
 
 
 
 
 
67e6ea6
46001b0
 
 
 
90bdafc
18d0b22
90bdafc
 
 
18d0b22
 
 
 
 
 
 
e9dd265
 
46001b0
 
 
 
 
 
67e6ea6
46001b0
 
 
 
8cc42bf
18d0b22
 
90bdafc
 
 
 
 
1579e31
1890cbb
67e6ea6
1890cbb
 
18d0b22
b257fdf
67e6ea6
1e5eb40
 
 
18d0b22
 
c08b935
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
from flask import Flask, request, jsonify, Response
import os
import json
from huggingface_hub import HfApi, create_repo, upload_file
import random
import string
from flask_httpauth import HTTPTokenAuth

# Import documentation from assembler_docs.py
from assembler_docs import DOCUMENTATION

app = Flask(__name__)

# Set up HTTP Token Authentication
auth = HTTPTokenAuth(scheme='Bearer')

# Hugging Face API token (set in Space settings or provided in request)
HF_TOKEN = os.getenv("HF_TOKEN")
if not HF_TOKEN:
    raise ValueError("HF_TOKEN not set. Add it in Space settings.")

hf_api = HfApi()

# Authentication function to validate the token
@auth.verify_token
def verify_token(token):
    if not token:
        return False
    try:
        # Attempt to verify the token by checking if it can access the user's profile or Space-Share
        user_info = hf_api.whoami(token=token)
        # Optionally, check if the token has write access to Space-Share
        # This is a basic check; you might want to add more specific permission checks
        return bool(user_info and "Space-Share" in user_info.get('orgs', [])) or token == HF_TOKEN
    except Exception:
        return False

def generate_space_name():
    """Generate a unique Space name."""
    random_suffix = ''.join(random.choices(string.ascii_lowercase + string.digits, k=6))
    return f"GeneratedSpace-{random_suffix}"

@app.route('/create-space', methods=['POST'])
@auth.login_required
def create_hf_space():
    try:
        # Use the authenticated token for Hugging Face operations
        token = auth.current_user()
        hf_api = HfApi(token=token)

        # Parse JSON input
        data = request.get_json()
        if not data:
            return jsonify({"error": "No JSON data provided"}), 401

        # Extract parameters
        space_type = data.get("space_type", "gradio")  # Default to gradio if not specified
        files = data.get("files", {})  # Dictionary of filename: content
        params = data.get("parameters", {})  # Optional parameters

        if not files:
            return jsonify({"error": "No files provided in JSON"}), 401

        # Validate space_type
        valid_space_types = ["gradio", "static", "docker", "streamlit"]
        if space_type not in valid_space_types:
            return jsonify({"error": f"Invalid space_type. Must be one of {valid_space_types}"}), 401

        # Create a unique Space name and repo under Space-Share namespace
        space_name = generate_space_name()
        full_repo_id = f"Space-Share/{space_name}"

        try:
            # Attempt to create the repository
            repo_info = create_repo(
                repo_id=space_name,
                repo_type="space",
                space_sdk=space_type,
                token=token,
                private=False,
                exist_ok=True  # Allow creation even if the repo might exist (rare case)
            )
        except Exception as e:
            return jsonify({"error": f"Failed to create repository: {str(e)}"}), 500

        # Handle multi-file uploads
        for filename, content in files.items():
            # Write content to a temporary file
            with open(f"temp_{filename}", "w") as f:
                if filename.endswith(".py"):
                    # Inject parameters into Python files if present
                    content = f"PARAMS = {json.dumps(params)}\n\n{content}"
                f.write(content)

            # Upload to the new Space
            try:
                upload_file(
                    path_or_fileobj=f"temp_{filename}",
                    path_in_repo=filename,
                    repo_id=full_repo_id,
                    repo_type="space",
                    token=token
                )
            except Exception as e:
                os.remove(f"temp_{filename}")
                return jsonify({"error": f"Failed to upload file {filename}: {str(e)}"}), 500

            os.remove(f"temp_{filename}")

        # Add requirements.txt if not provided (basic defaults)
        if "requirements.txt" not in files:
            default_requirements = {
                "gradio": "gradio",
                "static": "",
                "docker": "flask",  # Example; adjust based on needs
                "streamlit": "streamlit"
            }.get(space_type, "")
            with open("temp_requirements.txt", "w") as f:
                f.write(default_requirements)
            try:
                upload_file(
                    path_or_fileobj="temp_requirements.txt",
                    path_in_repo="requirements.txt",
                    repo_id=full_repo_id,
                    repo_type="space",
                    token=token
                )
            except Exception as e:
                os.remove("temp_requirements.txt")
                return jsonify({"error": f"Failed to upload requirements.txt: {str(e)}"}), 500
            os.remove("temp_requirements.txt")

        # Special handling for Docker Spaces
        if space_type == "docker" and "Dockerfile" not in files:
            default_dockerfile = """
FROM python:3.10-slim
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
EXPOSE 7860
CMD ["python", "app.py"]
"""
            with open("temp_Dockerfile", "w") as f:
                f.write(default_dockerfile)
            try:
                upload_file(
                    path_or_fileobj="temp_Dockerfile",
                    path_in_repo="Dockerfile",
                    repo_id=full_repo_id,
                    repo_type="space",
                    token=token
                )
            except Exception as e:
                os.remove("temp_Dockerfile")
                return jsonify({"error": f"Failed to upload Dockerfile: {str(e)}"}), 500
            os.remove("temp_Dockerfile")

        space_url = f"https://huggingface.co/spaces/Space-Share/{space_name}"
        return jsonify({
            "message": "New Space created",
            "url": space_url,
            "note": "It may take a few minutes to build and deploy."
        }), 200

    except json.JSONDecodeError:
        return jsonify({"error": "Invalid JSON format"}), 401
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/docs', methods=['GET'])
@auth.login_required
def get_docs():
    """Return the API documentation as plain text."""
    return Response(DOCUMENTATION, mimetype='text/plain'), 200

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=7860)