from flask import Flask, request, jsonify, Response import os import json from huggingface_hub import HfApi, create_repo, upload_file, login import random import string import logging import black # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Import documentation from assembler_docs.py from assembler_docs import DOCUMENTATION app = Flask(__name__) # Hugging Face API token (set in Space settings) HF_TOKEN = os.getenv("HF_TOKEN") if not HF_TOKEN: raise ValueError("HF_TOKEN not set. Add it in Space settings.") # Log in to Hugging Face Hub with the token try: login(token=HF_TOKEN) logger.info("Successfully logged in to Hugging Face Hub") except Exception as e: raise ValueError(f"Failed to log in to Hugging Face Hub: {str(e)}") hf_api = HfApi() 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}" def verify_organization_access(organization: str, token: str) -> bool: """Verify if the token has write access to the organization.""" try: # Try to list repositories or check organization membership repos = hf_api.list_repos(author=organization, token=token) logger.info(f"Verified access to organization {organization}") return True except Exception as e: logger.error(f"No write access to organization {organization}: {str(e)}") return False def build_space(repo_name,file_name,file_content,access_token=""): try: repo_path=user_+str(repo_) access_token=os.environ['HF_TOKEN'] if not access_token: return [{'role':'assistant','content': 'ENTER A HUGGINGFACE TOKEN'}] api=HfApi(endpoint="https://huggingface.co", token=access_token) repo_url = api.create_repo( repo_id=repo_path, repo_type="space", space_sdk="gradio", exist_ok=True, private=False, ) local_file_path=str(uuid.uuid4()) with open(file_name, 'w') as f: f.write(str(file_content).strip("''").strip('""').strip("{}").format()) f.close() # Upload a local file to the Space commit_message = "Adding file test: "+ str(file_name) api.upload_file(path_or_fileobj=file_name, path_in_repo=file_name.strip("./"), repo_id=repo_path, repo_type='space', commit_message=commit_message) print("File uploaded successfully.") # Commit changes commit_message += "\nInitial commit to the repository."+ f'{repo_path}/' + f'{file_name}' #api.commit_repo(space_id, message=commit_message) return [{'role':'assistant','content': commit_message+'\nCommit Success' }] except Exception as e: print("ERROR ",e) return [{'role':'assistant','content': 'There was an Error: ' + str(e)}] @app.route('/create-space', methods=['POST']) def create_hf_space(): try: # Parse JSON input data = request.get_json() if not data: return jsonify({"error": "No JSON data provided"}), 400 # 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"}), 400 # 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}"}), 400 # Organization namespace for new Spaces ORGANIZATION = "Space-Share" space_name = generate_space_name() full_repo_id = f"{ORGANIZATION}/{space_name}" # Verify token has access to Space-Share organization #if not verify_organization_access(ORGANIZATION, HF_TOKEN): # return jsonify({"error": f"No write access to organization {ORGANIZATION}. Check your HF_TOKEN permissions."}), 403 api=HfApi(endpoint="https://huggingface.co", token=HF_TOKEN) try: # Attempt to create the repository with explicit organization context repo_info = api.create_repo( repo_id=full_repo_id, repo_type="space", space_sdk=space_type, private=False, exist_ok=True # Allow creation even if the repo might exist ) logger.info(f"Repository created: {full_repo_id}") except Exception as e: return jsonify({"error": f"Failed to create repository {full_repo_id}: {str(e)}"}), 500 # Verify repository existence before uploading try: repo_exists = hf_api.repo_exists(repo_id=full_repo_id, repo_type="space") if not repo_exists: return jsonify({"error": f"Repository {full_repo_id} does not exist after creation attempt."}), 500 logger.info(f"Verified repository exists: {full_repo_id}") except Exception as e: return jsonify({"error": f"Failed to verify repository {full_repo_id}: {str(e)}"}), 500 # Handle multi-file uploads for filename, content in files.items(): #content_lines = content.split("\n") #content = "" #for line in content_lines: # content += line + "\n" # 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) os.system(f"black temp_{filename}") # Upload to the new Space try: api.upload_file( path_or_fileobj=f"temp_{filename}", path_in_repo=filename, repo_id=full_repo_id, repo_type="space", ) logger.info(f"Uploaded file: {filename}") 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: api.upload_file( path_or_fileobj="temp_requirements.txt", path_in_repo="requirements.txt", repo_id=full_repo_id, repo_type="space", token=HF_TOKEN ) logger.info("Uploaded requirements.txt") 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: api.upload_file( path_or_fileobj="temp_Dockerfile", path_in_repo="Dockerfile", repo_id=full_repo_id, repo_type="space", token=HF_TOKEN ) logger.info("Uploaded Dockerfile") 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/{full_repo_id}" 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"}), 400 except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/docs', methods=['GET']) 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)