milwright commited on
Commit
9bf60ea
Β·
1 Parent(s): cabc83c

add missing template_validator.py to fix deployment error

Browse files
Files changed (1) hide show
  1. template_validator.py +320 -0
template_validator.py ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Template Validator for HuggingFace Space Generator
3
+
4
+ This module provides comprehensive validation of the space template
5
+ to ensure it contains all required components and valid Python syntax.
6
+ Used during development and before deployment package generation.
7
+ """
8
+
9
+ import ast
10
+ from typing import Dict, List, Any
11
+
12
+
13
+ def validate_template(template_string: str = None) -> bool:
14
+ """
15
+ Comprehensive validation of the space template
16
+
17
+ Args:
18
+ template_string: The template to validate. If None, imports from space_template.
19
+
20
+ Returns:
21
+ bool: True if validation passes, raises ValueError if it fails
22
+ """
23
+
24
+ # Import template if not provided
25
+ if template_string is None:
26
+ from space_template import SPACE_TEMPLATE
27
+ template_string = SPACE_TEMPLATE
28
+
29
+ # Required placeholders that must exist in the template
30
+ required_placeholders = [
31
+ 'name', 'description', 'system_prompt', 'temperature', 'max_tokens',
32
+ 'model', 'api_key_var', 'theme', 'grounding_urls', 'enable_dynamic_urls',
33
+ 'enable_file_upload', 'examples', 'language'
34
+ ]
35
+
36
+ validation_results = {
37
+ 'placeholder_check': {'passed': True, 'missing': [], 'errors': []},
38
+ 'syntax_check': {'passed': True, 'errors': []},
39
+ 'structure_check': {'passed': True, 'errors': []},
40
+ 'import_check': {'passed': True, 'errors': []},
41
+ 'class_check': {'passed': True, 'errors': []},
42
+ 'function_check': {'passed': True, 'errors': []}
43
+ }
44
+
45
+ # 1. Check for required placeholders
46
+ print("πŸ” Checking placeholders...")
47
+ missing = []
48
+ for placeholder in required_placeholders:
49
+ if f'{{{placeholder}}}' not in template_string:
50
+ missing.append(placeholder)
51
+
52
+ if missing:
53
+ validation_results['placeholder_check']['passed'] = False
54
+ validation_results['placeholder_check']['missing'] = missing
55
+ validation_results['placeholder_check']['errors'].append(
56
+ f"Missing required placeholders: {', '.join(missing)}"
57
+ )
58
+ else:
59
+ print("βœ… All required placeholders found")
60
+
61
+ # 2. Validate Python syntax with sample values
62
+ print("πŸ” Checking Python syntax...")
63
+ sample_values = {
64
+ 'name': repr("Test Space"),
65
+ 'description': repr("Test Description"),
66
+ 'system_prompt': repr("You are a helpful assistant."),
67
+ 'temperature': '0.7',
68
+ 'max_tokens': '2000',
69
+ 'model': repr("openai/gpt-4o-mini"),
70
+ 'api_key_var': repr("API_KEY"),
71
+ 'theme': repr("Default"),
72
+ 'grounding_urls': '[]',
73
+ 'enable_dynamic_urls': 'False',
74
+ 'enable_file_upload': 'False',
75
+ 'examples': '[]',
76
+ 'language': repr("English")
77
+ }
78
+
79
+ # Create test template with sample values
80
+ test_template = template_string
81
+ for key, value in sample_values.items():
82
+ test_template = test_template.replace(f'{{{key}}}', value)
83
+
84
+ try:
85
+ # Parse the template as Python code
86
+ ast.parse(test_template)
87
+ print("βœ… Python syntax is valid")
88
+ except SyntaxError as e:
89
+ validation_results['syntax_check']['passed'] = False
90
+ validation_results['syntax_check']['errors'].append(
91
+ f"Syntax error at line {e.lineno}: {e.msg}"
92
+ )
93
+ print(f"❌ Syntax error: {e}")
94
+
95
+ # 3. Check for required imports
96
+ print("πŸ” Checking required imports...")
97
+ required_imports = [
98
+ 'import gradio as gr',
99
+ 'import requests',
100
+ 'import json',
101
+ 'from datetime import datetime'
102
+ ]
103
+
104
+ for imp in required_imports:
105
+ if imp not in template_string:
106
+ validation_results['import_check']['passed'] = False
107
+ validation_results['import_check']['errors'].append(f"Missing import: {imp}")
108
+
109
+ if validation_results['import_check']['passed']:
110
+ print("βœ… All required imports present")
111
+
112
+ # 4. Check for required classes
113
+ print("πŸ” Checking required classes...")
114
+ required_classes = ['ConfigurationManager'] # Only ConfigurationManager is a class in the template
115
+
116
+ for cls in required_classes:
117
+ if f'class {cls}' not in template_string:
118
+ validation_results['class_check']['passed'] = False
119
+ validation_results['class_check']['errors'].append(f"Missing class: {cls}")
120
+
121
+ if validation_results['class_check']['passed']:
122
+ print("βœ… All required classes found")
123
+
124
+ # 5. Check for required functions
125
+ print("πŸ” Checking required functions...")
126
+ required_functions = [
127
+ 'def respond', # Main chat response function
128
+ 'def process_file_upload',
129
+ 'def fetch_url_content',
130
+ 'def export_conversation',
131
+ 'def create_interface'
132
+ ]
133
+
134
+ for func in required_functions:
135
+ if func not in template_string:
136
+ validation_results['function_check']['passed'] = False
137
+ validation_results['function_check']['errors'].append(
138
+ f"Missing function: {func.replace('def ', '')}"
139
+ )
140
+
141
+ if validation_results['function_check']['passed']:
142
+ print("βœ… All required functions found")
143
+
144
+ # 6. Structure validation
145
+ print("πŸ” Checking template structure...")
146
+
147
+ # Check for proper configuration section
148
+ if 'DEFAULT_CONFIG = {' not in template_string:
149
+ validation_results['structure_check']['passed'] = False
150
+ validation_results['structure_check']['errors'].append("Missing DEFAULT_CONFIG dictionary")
151
+
152
+ # Check for Gradio app launch
153
+ if 'demo.launch()' not in template_string and 'app.launch()' not in template_string:
154
+ validation_results['structure_check']['passed'] = False
155
+ validation_results['structure_check']['errors'].append("Missing Gradio app launch")
156
+
157
+ # Check for main execution block (with double quotes in template)
158
+ if 'if __name__ == "__main__":' not in template_string:
159
+ validation_results['structure_check']['passed'] = False
160
+ validation_results['structure_check']['errors'].append("Missing main execution block")
161
+
162
+ if validation_results['structure_check']['passed']:
163
+ print("βœ… Template structure is valid")
164
+
165
+ # Generate summary
166
+ all_passed = all(check['passed'] for check in validation_results.values())
167
+
168
+ if all_passed:
169
+ print("\nβœ… Template validation PASSED - All checks successful!")
170
+ return True
171
+ else:
172
+ print("\n❌ Template validation FAILED:")
173
+ for check_name, result in validation_results.items():
174
+ if not result['passed']:
175
+ print(f"\n {check_name.replace('_', ' ').title()}:")
176
+ for error in result['errors']:
177
+ print(f" - {error}")
178
+ if result.get('missing'):
179
+ print(f" Missing items: {', '.join(result['missing'])}")
180
+
181
+ raise ValueError("Template validation failed. See details above.")
182
+
183
+ return all_passed
184
+
185
+
186
+ def validate_silently(template_string: str = None) -> tuple[bool, Dict[str, Any]]:
187
+ """
188
+ Silent validation that returns results without printing
189
+
190
+ Args:
191
+ template_string: The template to validate. If None, imports from space_template.
192
+
193
+ Returns:
194
+ tuple: (success: bool, results: dict with validation details)
195
+ """
196
+
197
+ # Import template if not provided
198
+ if template_string is None:
199
+ from space_template import SPACE_TEMPLATE
200
+ template_string = SPACE_TEMPLATE
201
+
202
+ # Required placeholders that must exist in the template
203
+ required_placeholders = [
204
+ 'name', 'description', 'system_prompt', 'temperature', 'max_tokens',
205
+ 'model', 'api_key_var', 'theme', 'grounding_urls', 'enable_dynamic_urls',
206
+ 'enable_file_upload', 'examples', 'language'
207
+ ]
208
+
209
+ validation_results = {
210
+ 'placeholder_check': {'passed': True, 'missing': [], 'errors': []},
211
+ 'syntax_check': {'passed': True, 'errors': []},
212
+ 'structure_check': {'passed': True, 'errors': []},
213
+ 'import_check': {'passed': True, 'errors': []},
214
+ 'class_check': {'passed': True, 'errors': []},
215
+ 'function_check': {'passed': True, 'errors': []}
216
+ }
217
+
218
+ # 1. Check for required placeholders
219
+ missing = []
220
+ for placeholder in required_placeholders:
221
+ if f'{{{placeholder}}}' not in template_string:
222
+ missing.append(placeholder)
223
+
224
+ if missing:
225
+ validation_results['placeholder_check']['passed'] = False
226
+ validation_results['placeholder_check']['missing'] = missing
227
+ validation_results['placeholder_check']['errors'].append(
228
+ f"Missing required placeholders: {', '.join(missing)}"
229
+ )
230
+
231
+ # 2. Validate Python syntax with sample values
232
+ sample_values = {
233
+ 'name': repr("Test Space"),
234
+ 'description': repr("Test Description"),
235
+ 'system_prompt': repr("You are a helpful assistant."),
236
+ 'temperature': '0.7',
237
+ 'max_tokens': '2000',
238
+ 'model': repr("openai/gpt-4o-mini"),
239
+ 'api_key_var': repr("API_KEY"),
240
+ 'theme': repr("Default"),
241
+ 'grounding_urls': '[]',
242
+ 'enable_dynamic_urls': 'False',
243
+ 'enable_file_upload': 'False',
244
+ 'examples': '[]',
245
+ 'language': repr("English")
246
+ }
247
+
248
+ # Create test template with sample values
249
+ test_template = template_string
250
+ for key, value in sample_values.items():
251
+ test_template = test_template.replace(f'{{{key}}}', value)
252
+
253
+ try:
254
+ # Parse the template as Python code
255
+ ast.parse(test_template)
256
+ except SyntaxError as e:
257
+ validation_results['syntax_check']['passed'] = False
258
+ validation_results['syntax_check']['errors'].append(
259
+ f"Syntax error at line {e.lineno}: {e.msg}"
260
+ )
261
+
262
+ # 3. Check for required imports
263
+ required_imports = [
264
+ 'import gradio as gr',
265
+ 'import requests',
266
+ 'import json',
267
+ 'from datetime import datetime'
268
+ ]
269
+
270
+ for imp in required_imports:
271
+ if imp not in template_string:
272
+ validation_results['import_check']['passed'] = False
273
+ validation_results['import_check']['errors'].append(f"Missing import: {imp}")
274
+
275
+ # 4. Check for required classes
276
+ required_classes = ['ConfigurationManager']
277
+
278
+ for cls in required_classes:
279
+ if f'class {cls}' not in template_string:
280
+ validation_results['class_check']['passed'] = False
281
+ validation_results['class_check']['errors'].append(f"Missing class: {cls}")
282
+
283
+ # 5. Check for required functions
284
+ required_functions = [
285
+ 'def respond',
286
+ 'def process_file_upload',
287
+ 'def fetch_url_content',
288
+ 'def export_conversation',
289
+ 'def create_interface'
290
+ ]
291
+
292
+ for func in required_functions:
293
+ if func not in template_string:
294
+ validation_results['function_check']['passed'] = False
295
+ validation_results['function_check']['errors'].append(
296
+ f"Missing function: {func.replace('def ', '')}"
297
+ )
298
+
299
+ # 6. Structure validation
300
+ if 'DEFAULT_CONFIG = {' not in template_string:
301
+ validation_results['structure_check']['passed'] = False
302
+ validation_results['structure_check']['errors'].append("Missing DEFAULT_CONFIG dictionary")
303
+
304
+ if 'demo.launch()' not in template_string and 'app.launch()' not in template_string:
305
+ validation_results['structure_check']['passed'] = False
306
+ validation_results['structure_check']['errors'].append("Missing Gradio app launch")
307
+
308
+ if 'if __name__ == "__main__":' not in template_string:
309
+ validation_results['structure_check']['passed'] = False
310
+ validation_results['structure_check']['errors'].append("Missing main execution block")
311
+
312
+ # Generate summary
313
+ all_passed = all(check['passed'] for check in validation_results.values())
314
+
315
+ return all_passed, validation_results
316
+
317
+
318
+ if __name__ == "__main__":
319
+ # Run validation when module is executed directly
320
+ validate_template()