Spaces:
Running
Running
| """ | |
| Template Validator for HuggingFace Space Generator | |
| This module provides comprehensive validation of the space template | |
| to ensure it contains all required components and valid Python syntax. | |
| Used during development and before deployment package generation. | |
| """ | |
| import ast | |
| from typing import Dict, List, Any | |
| def validate_template(template_string: str = None) -> bool: | |
| """ | |
| Comprehensive validation of the space template | |
| Args: | |
| template_string: The template to validate. If None, imports from space_template. | |
| Returns: | |
| bool: True if validation passes, raises ValueError if it fails | |
| """ | |
| # Import template if not provided | |
| if template_string is None: | |
| from space_template import SPACE_TEMPLATE | |
| template_string = SPACE_TEMPLATE | |
| # Required placeholders that must exist in the template | |
| required_placeholders = [ | |
| 'name', 'description', 'system_prompt', 'temperature', 'max_tokens', | |
| 'model', 'api_key_var', 'theme', 'grounding_urls', 'enable_dynamic_urls', | |
| 'enable_file_upload', 'examples', 'language' | |
| ] | |
| validation_results = { | |
| 'placeholder_check': {'passed': True, 'missing': [], 'errors': []}, | |
| 'syntax_check': {'passed': True, 'errors': []}, | |
| 'structure_check': {'passed': True, 'errors': []}, | |
| 'import_check': {'passed': True, 'errors': []}, | |
| 'class_check': {'passed': True, 'errors': []}, | |
| 'function_check': {'passed': True, 'errors': []} | |
| } | |
| # 1. Check for required placeholders | |
| print("🔍 Checking placeholders...") | |
| missing = [] | |
| for placeholder in required_placeholders: | |
| if f'{{{placeholder}}}' not in template_string: | |
| missing.append(placeholder) | |
| if missing: | |
| validation_results['placeholder_check']['passed'] = False | |
| validation_results['placeholder_check']['missing'] = missing | |
| validation_results['placeholder_check']['errors'].append( | |
| f"Missing required placeholders: {', '.join(missing)}" | |
| ) | |
| else: | |
| print("✅ All required placeholders found") | |
| # 2. Validate Python syntax with sample values | |
| print("🔍 Checking Python syntax...") | |
| sample_values = { | |
| 'name': repr("Test Space"), | |
| 'description': repr("Test Description"), | |
| 'system_prompt': repr("You are a helpful assistant."), | |
| 'temperature': '0.7', | |
| 'max_tokens': '2000', | |
| 'model': repr("openai/gpt-4o-mini"), | |
| 'api_key_var': repr("API_KEY"), | |
| 'theme': repr("Default"), | |
| 'grounding_urls': '[]', | |
| 'enable_dynamic_urls': 'False', | |
| 'enable_file_upload': 'False', | |
| 'examples': '[]', | |
| 'language': repr("English") | |
| } | |
| # Create test template with sample values | |
| test_template = template_string | |
| for key, value in sample_values.items(): | |
| test_template = test_template.replace(f'{{{key}}}', value) | |
| try: | |
| # Parse the template as Python code | |
| ast.parse(test_template) | |
| print("✅ Python syntax is valid") | |
| except SyntaxError as e: | |
| validation_results['syntax_check']['passed'] = False | |
| validation_results['syntax_check']['errors'].append( | |
| f"Syntax error at line {e.lineno}: {e.msg}" | |
| ) | |
| print(f"❌ Syntax error: {e}") | |
| # 3. Check for required imports | |
| print("🔍 Checking required imports...") | |
| required_imports = [ | |
| 'import gradio as gr', | |
| 'import requests', | |
| 'import json', | |
| 'from datetime import datetime' | |
| ] | |
| for imp in required_imports: | |
| if imp not in template_string: | |
| validation_results['import_check']['passed'] = False | |
| validation_results['import_check']['errors'].append(f"Missing import: {imp}") | |
| if validation_results['import_check']['passed']: | |
| print("✅ All required imports present") | |
| # 4. Check for required classes | |
| print("🔍 Checking required classes...") | |
| required_classes = ['ConfigurationManager'] # Only ConfigurationManager is a class in the template | |
| for cls in required_classes: | |
| if f'class {cls}' not in template_string: | |
| validation_results['class_check']['passed'] = False | |
| validation_results['class_check']['errors'].append(f"Missing class: {cls}") | |
| if validation_results['class_check']['passed']: | |
| print("✅ All required classes found") | |
| # 5. Check for required functions | |
| print("🔍 Checking required functions...") | |
| required_functions = [ | |
| 'def respond', # Main chat response function | |
| 'def process_file_upload', | |
| 'def fetch_url_content', | |
| 'def export_conversation', | |
| 'def create_interface' | |
| ] | |
| for func in required_functions: | |
| if func not in template_string: | |
| validation_results['function_check']['passed'] = False | |
| validation_results['function_check']['errors'].append( | |
| f"Missing function: {func.replace('def ', '')}" | |
| ) | |
| if validation_results['function_check']['passed']: | |
| print("✅ All required functions found") | |
| # 6. Structure validation | |
| print("🔍 Checking template structure...") | |
| # Check for proper configuration section | |
| if 'DEFAULT_CONFIG = {' not in template_string: | |
| validation_results['structure_check']['passed'] = False | |
| validation_results['structure_check']['errors'].append("Missing DEFAULT_CONFIG dictionary") | |
| # Check for Gradio app launch | |
| if 'demo.launch()' not in template_string and 'app.launch()' not in template_string: | |
| validation_results['structure_check']['passed'] = False | |
| validation_results['structure_check']['errors'].append("Missing Gradio app launch") | |
| # Check for main execution block (with double quotes in template) | |
| if 'if __name__ == "__main__":' not in template_string: | |
| validation_results['structure_check']['passed'] = False | |
| validation_results['structure_check']['errors'].append("Missing main execution block") | |
| if validation_results['structure_check']['passed']: | |
| print("✅ Template structure is valid") | |
| # Generate summary | |
| all_passed = all(check['passed'] for check in validation_results.values()) | |
| if all_passed: | |
| print("\n✅ Template validation PASSED - All checks successful!") | |
| return True | |
| else: | |
| print("\n❌ Template validation FAILED:") | |
| for check_name, result in validation_results.items(): | |
| if not result['passed']: | |
| print(f"\n {check_name.replace('_', ' ').title()}:") | |
| for error in result['errors']: | |
| print(f" - {error}") | |
| if result.get('missing'): | |
| print(f" Missing items: {', '.join(result['missing'])}") | |
| raise ValueError("Template validation failed. See details above.") | |
| return all_passed | |
| def validate_silently(template_string: str = None) -> tuple[bool, Dict[str, Any]]: | |
| """ | |
| Silent validation that returns results without printing | |
| Args: | |
| template_string: The template to validate. If None, imports from space_template. | |
| Returns: | |
| tuple: (success: bool, results: dict with validation details) | |
| """ | |
| # Import template if not provided | |
| if template_string is None: | |
| from space_template import SPACE_TEMPLATE | |
| template_string = SPACE_TEMPLATE | |
| # Required placeholders that must exist in the template | |
| required_placeholders = [ | |
| 'name', 'description', 'system_prompt', 'temperature', 'max_tokens', | |
| 'model', 'api_key_var', 'theme', 'grounding_urls', 'enable_dynamic_urls', | |
| 'enable_file_upload', 'examples', 'language' | |
| ] | |
| validation_results = { | |
| 'placeholder_check': {'passed': True, 'missing': [], 'errors': []}, | |
| 'syntax_check': {'passed': True, 'errors': []}, | |
| 'structure_check': {'passed': True, 'errors': []}, | |
| 'import_check': {'passed': True, 'errors': []}, | |
| 'class_check': {'passed': True, 'errors': []}, | |
| 'function_check': {'passed': True, 'errors': []} | |
| } | |
| # 1. Check for required placeholders | |
| missing = [] | |
| for placeholder in required_placeholders: | |
| if f'{{{placeholder}}}' not in template_string: | |
| missing.append(placeholder) | |
| if missing: | |
| validation_results['placeholder_check']['passed'] = False | |
| validation_results['placeholder_check']['missing'] = missing | |
| validation_results['placeholder_check']['errors'].append( | |
| f"Missing required placeholders: {', '.join(missing)}" | |
| ) | |
| # 2. Validate Python syntax with sample values | |
| sample_values = { | |
| 'name': repr("Test Space"), | |
| 'description': repr("Test Description"), | |
| 'system_prompt': repr("You are a helpful assistant."), | |
| 'temperature': '0.7', | |
| 'max_tokens': '2000', | |
| 'model': repr("openai/gpt-4o-mini"), | |
| 'api_key_var': repr("API_KEY"), | |
| 'theme': repr("Default"), | |
| 'grounding_urls': '[]', | |
| 'enable_dynamic_urls': 'False', | |
| 'enable_file_upload': 'False', | |
| 'examples': '[]', | |
| 'language': repr("English") | |
| } | |
| # Create test template with sample values | |
| test_template = template_string | |
| for key, value in sample_values.items(): | |
| test_template = test_template.replace(f'{{{key}}}', value) | |
| try: | |
| # Parse the template as Python code | |
| ast.parse(test_template) | |
| except SyntaxError as e: | |
| validation_results['syntax_check']['passed'] = False | |
| validation_results['syntax_check']['errors'].append( | |
| f"Syntax error at line {e.lineno}: {e.msg}" | |
| ) | |
| # 3. Check for required imports | |
| required_imports = [ | |
| 'import gradio as gr', | |
| 'import requests', | |
| 'import json', | |
| 'from datetime import datetime' | |
| ] | |
| for imp in required_imports: | |
| if imp not in template_string: | |
| validation_results['import_check']['passed'] = False | |
| validation_results['import_check']['errors'].append(f"Missing import: {imp}") | |
| # 4. Check for required classes | |
| required_classes = ['ConfigurationManager'] | |
| for cls in required_classes: | |
| if f'class {cls}' not in template_string: | |
| validation_results['class_check']['passed'] = False | |
| validation_results['class_check']['errors'].append(f"Missing class: {cls}") | |
| # 5. Check for required functions | |
| required_functions = [ | |
| 'def respond', | |
| 'def process_file_upload', | |
| 'def fetch_url_content', | |
| 'def export_conversation', | |
| 'def create_interface' | |
| ] | |
| for func in required_functions: | |
| if func not in template_string: | |
| validation_results['function_check']['passed'] = False | |
| validation_results['function_check']['errors'].append( | |
| f"Missing function: {func.replace('def ', '')}" | |
| ) | |
| # 6. Structure validation | |
| if 'DEFAULT_CONFIG = {' not in template_string: | |
| validation_results['structure_check']['passed'] = False | |
| validation_results['structure_check']['errors'].append("Missing DEFAULT_CONFIG dictionary") | |
| if 'demo.launch()' not in template_string and 'app.launch()' not in template_string: | |
| validation_results['structure_check']['passed'] = False | |
| validation_results['structure_check']['errors'].append("Missing Gradio app launch") | |
| if 'if __name__ == "__main__":' not in template_string: | |
| validation_results['structure_check']['passed'] = False | |
| validation_results['structure_check']['errors'].append("Missing main execution block") | |
| # Generate summary | |
| all_passed = all(check['passed'] for check in validation_results.values()) | |
| return all_passed, validation_results | |
| if __name__ == "__main__": | |
| # Run validation when module is executed directly | |
| validate_template() |