a1c00l commited on
Commit
ef28c77
·
verified ·
1 Parent(s): c5073ea

Update src/aibom_generator/api.py

Browse files
Files changed (1) hide show
  1. src/aibom_generator/api.py +36 -166
src/aibom_generator/api.py CHANGED
@@ -1,15 +1,12 @@
1
  import os
2
  import json
3
  import logging
4
- from typing import Dict, List, Optional, Any, Union
5
- from datetime import datetime
6
- from pathlib import Path
7
-
8
- from fastapi import FastAPI, HTTPException, Depends, BackgroundTasks, Query, File, UploadFile, Form, Request
9
  from fastapi.responses import JSONResponse, FileResponse, HTMLResponse
10
  from fastapi.staticfiles import StaticFiles
11
  from fastapi.templating import Jinja2Templates
12
- from pydantic import BaseModel, Field
13
 
14
  # Configure logging
15
  logging.basicConfig(level=logging.INFO)
@@ -18,11 +15,11 @@ logger = logging.getLogger(__name__)
18
  # Define templates directory
19
  templates_dir = "templates"
20
 
21
- # Initialize templates with a simple path
22
  templates = Jinja2Templates(directory=templates_dir)
23
 
24
  # Create app
25
- app = FastAPI(title="AI SBOM Generator API")
26
 
27
  # Define output directory for generated AIBOMs
28
  OUTPUT_DIR = "/tmp/aibom_output"
@@ -34,90 +31,35 @@ os.makedirs(OUTPUT_DIR, exist_ok=True)
34
  app.mount("/output", StaticFiles(directory=OUTPUT_DIR), name="output")
35
 
36
  # Define models
37
- class GenerateRequest(BaseModel):
38
- model_id: str
39
- include_inference: bool = False
40
- use_best_practices: bool = True
41
-
42
- class GenerateWithReportRequest(BaseModel):
43
- model_id: str
44
- include_inference: bool = False
45
- use_best_practices: bool = True
46
-
47
- class BatchGenerateRequest(BaseModel):
48
- model_ids: List[str]
49
- include_inference: bool = False
50
- use_best_practices: bool = True
51
-
52
- class ModelScoreRequest(BaseModel):
53
- model_id: str
54
-
55
  class StatusResponse(BaseModel):
56
  status: str
57
  version: str
58
  generator_version: str
59
 
60
- class BatchJobResponse(BaseModel):
61
- job_id: str
62
- status: str
63
- model_ids: List[str]
64
- message: str
65
-
66
  # Startup event to ensure directories exist
67
  @app.on_event("startup")
68
  async def startup_event():
69
  """Create necessary directories on startup."""
70
  os.makedirs(OUTPUT_DIR, exist_ok=True)
71
  logger.info(f"Created output directory at {OUTPUT_DIR}")
 
72
 
73
  # Root route serves the UI
74
  @app.get("/", response_class=HTMLResponse)
75
  async def root(request: Request):
76
  """Serve the web UI interface as the default view."""
77
  try:
78
- # Check if templates directory exists
79
- if os.path.exists(templates_dir):
80
- logger.info(f"Templates directory exists")
81
- logger.info(f"Templates directory contents: {os.listdir(templates_dir)}")
82
- else:
83
- logger.warning(f"Templates directory does not exist: {templates_dir}")
84
-
85
- # Try to render the template
86
- try:
87
- return templates.TemplateResponse("index.html", {"request": request})
88
- except Exception as e:
89
- logger.error(f"Error rendering template: {str(e)}")
90
-
91
- # Try direct file reading as fallback
92
- try:
93
- with open(os.path.join(templates_dir, "index.html"), "r") as f:
94
- html_content = f.read()
95
- return HTMLResponse(content=html_content)
96
- except Exception as file_e:
97
- logger.error(f"Error reading file directly: {str(file_e)}")
98
-
99
- # Last resort: try absolute path
100
- try:
101
- with open("/app/templates/index.html", "r") as f:
102
- html_content = f.read()
103
- return HTMLResponse(content=html_content)
104
- except Exception as abs_e:
105
- logger.error(f"Error reading file from absolute path: {str(abs_e)}")
106
- raise HTTPException(status_code=500, detail=f"Error in UI endpoint: {str(e)}")
107
- except Exception as outer_e:
108
- logger.error(f"Outer error in UI endpoint: {str(outer_e)}")
109
- raise HTTPException(status_code=500, detail=f"Error in UI endpoint: {str(outer_e)}")
110
-
111
- # UI route for backward compatibility
112
- @app.get("/ui", response_class=HTMLResponse)
113
- async def ui(request: Request):
114
- """Serve the web UI interface (kept for backward compatibility)."""
115
- return await root(request)
116
 
117
  # Status endpoint
118
  @app.get("/status", response_model=StatusResponse)
119
  async def get_status():
120
  """Get the API status and version information."""
 
121
  return {
122
  "status": "operational",
123
  "version": "1.0.0",
@@ -134,31 +76,43 @@ async def generate_form(
134
  ):
135
  """Generate an AI SBOM from form data and render the result template."""
136
  try:
 
 
137
  # Import the generator here to avoid circular imports
138
  try:
139
  from src.aibom_generator.generator import AIBOMGenerator
140
- except ImportError:
 
 
141
  try:
142
  from aibom_generator.generator import AIBOMGenerator
143
- except ImportError:
 
 
144
  try:
145
  from generator import AIBOMGenerator
146
- except ImportError:
 
 
147
  raise ImportError("Could not import AIBOMGenerator. Please check your installation.")
148
 
149
  # Create generator instance
 
150
  generator = AIBOMGenerator()
151
 
152
  # Generate AIBOM
 
153
  aibom, enhancement_report = generator.generate(
154
  model_id=model_id,
155
  include_inference=include_inference,
156
  use_best_practices=use_best_practices
157
  )
 
158
 
159
  # Save AIBOM to file
160
  filename = f"{model_id.replace('/', '_')}_aibom.json"
161
  filepath = os.path.join(OUTPUT_DIR, filename)
 
162
  with open(filepath, "w") as f:
163
  json.dump(aibom, f, indent=2)
164
 
@@ -183,11 +137,14 @@ async def generate_form(
183
  completeness_score = None
184
  if hasattr(generator, 'get_completeness_score'):
185
  try:
 
186
  completeness_score = generator.get_completeness_score(model_id)
 
187
  except Exception as e:
188
  logger.error(f"Error getting completeness score: {str(e)}")
189
 
190
  # Render result template
 
191
  return templates.TemplateResponse(
192
  "result.html",
193
  {
@@ -201,7 +158,7 @@ async def generate_form(
201
  }
202
  )
203
  except Exception as e:
204
- logger.error(f"Error generating AI SBOM: {str(e)}")
205
  return templates.TemplateResponse(
206
  "error.html",
207
  {
@@ -210,106 +167,18 @@ async def generate_form(
210
  }
211
  )
212
 
213
- # JSON API endpoints
214
- @app.post("/api/generate")
215
- async def generate_api(request: GenerateRequest):
216
- """Generate an AI SBOM and return it as JSON."""
217
- try:
218
- # Import the generator here to avoid circular imports
219
- try:
220
- from src.aibom_generator.generator import AIBOMGenerator
221
- except ImportError:
222
- try:
223
- from aibom_generator.generator import AIBOMGenerator
224
- except ImportError:
225
- try:
226
- from generator import AIBOMGenerator
227
- except ImportError:
228
- raise ImportError("Could not import AIBOMGenerator. Please check your installation.")
229
-
230
- # Create generator instance
231
- generator = AIBOMGenerator()
232
-
233
- # Generate AIBOM
234
- aibom, _ = generator.generate(
235
- model_id=request.model_id,
236
- include_inference=request.include_inference,
237
- use_best_practices=request.use_best_practices
238
- )
239
-
240
- return aibom
241
- except Exception as e:
242
- raise HTTPException(status_code=500, detail=f"Error generating AI SBOM: {str(e)}")
243
-
244
- @app.post("/api/generate-with-report")
245
- async def generate_with_report_api(request: GenerateWithReportRequest):
246
- """Generate an AI SBOM with enhancement report and return both as JSON."""
247
- try:
248
- # Import the generator here to avoid circular imports
249
- try:
250
- from src.aibom_generator.generator import AIBOMGenerator
251
- except ImportError:
252
- try:
253
- from aibom_generator.generator import AIBOMGenerator
254
- except ImportError:
255
- try:
256
- from generator import AIBOMGenerator
257
- except ImportError:
258
- raise ImportError("Could not import AIBOMGenerator. Please check your installation.")
259
-
260
- # Create generator instance
261
- generator = AIBOMGenerator()
262
-
263
- # Generate AIBOM
264
- aibom, enhancement_report = generator.generate(
265
- model_id=request.model_id,
266
- include_inference=request.include_inference,
267
- use_best_practices=request.use_best_practices
268
- )
269
-
270
- return {
271
- "aibom": aibom,
272
- "enhancement_report": enhancement_report
273
- }
274
- except Exception as e:
275
- raise HTTPException(status_code=500, detail=f"Error generating AI SBOM: {str(e)}")
276
-
277
- @app.get("/api/model-score/{model_id}")
278
- async def model_score_api(model_id: str):
279
- """Get the completeness score for a model."""
280
- try:
281
- # Import the generator here to avoid circular imports
282
- try:
283
- from src.aibom_generator.generator import AIBOMGenerator
284
- except ImportError:
285
- try:
286
- from aibom_generator.generator import AIBOMGenerator
287
- except ImportError:
288
- try:
289
- from generator import AIBOMGenerator
290
- except ImportError:
291
- raise ImportError("Could not import AIBOMGenerator. Please check your installation.")
292
-
293
- # Create generator instance
294
- generator = AIBOMGenerator()
295
-
296
- # Get completeness score
297
- if hasattr(generator, 'get_completeness_score'):
298
- completeness_score = generator.get_completeness_score(model_id)
299
- return completeness_score
300
- else:
301
- raise HTTPException(status_code=501, detail="Completeness score calculation not implemented")
302
- except Exception as e:
303
- raise HTTPException(status_code=500, detail=f"Error getting model score: {str(e)}")
304
-
305
  @app.get("/download/{filename}")
306
  async def download_file(filename: str):
307
  """Download a generated AI SBOM file."""
308
  try:
 
309
  filepath = os.path.join(OUTPUT_DIR, filename)
310
  if not os.path.exists(filepath):
 
311
  raise HTTPException(status_code=404, detail=f"File {filename} not found")
312
 
 
313
  return FileResponse(
314
  filepath,
315
  media_type="application/json",
@@ -318,4 +187,5 @@ async def download_file(filename: str):
318
  except HTTPException:
319
  raise
320
  except Exception as e:
 
321
  raise HTTPException(status_code=500, detail=f"Error downloading file: {str(e)}")
 
1
  import os
2
  import json
3
  import logging
4
+ from typing import Dict, List, Optional, Any
5
+ from fastapi import FastAPI, HTTPException, Request, Form
 
 
 
6
  from fastapi.responses import JSONResponse, FileResponse, HTMLResponse
7
  from fastapi.staticfiles import StaticFiles
8
  from fastapi.templating import Jinja2Templates
9
+ from pydantic import BaseModel
10
 
11
  # Configure logging
12
  logging.basicConfig(level=logging.INFO)
 
15
  # Define templates directory
16
  templates_dir = "templates"
17
 
18
+ # Initialize templates
19
  templates = Jinja2Templates(directory=templates_dir)
20
 
21
  # Create app
22
+ app = FastAPI(title="AI SBOM Generator API - Simplified Version")
23
 
24
  # Define output directory for generated AIBOMs
25
  OUTPUT_DIR = "/tmp/aibom_output"
 
31
  app.mount("/output", StaticFiles(directory=OUTPUT_DIR), name="output")
32
 
33
  # Define models
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  class StatusResponse(BaseModel):
35
  status: str
36
  version: str
37
  generator_version: str
38
 
 
 
 
 
 
 
39
  # Startup event to ensure directories exist
40
  @app.on_event("startup")
41
  async def startup_event():
42
  """Create necessary directories on startup."""
43
  os.makedirs(OUTPUT_DIR, exist_ok=True)
44
  logger.info(f"Created output directory at {OUTPUT_DIR}")
45
+ logger.info(f"Registered routes: {[route.path for route in app.routes]}")
46
 
47
  # Root route serves the UI
48
  @app.get("/", response_class=HTMLResponse)
49
  async def root(request: Request):
50
  """Serve the web UI interface as the default view."""
51
  try:
52
+ logger.info("Attempting to render index.html template")
53
+ return templates.TemplateResponse("index.html", {"request": request})
54
+ except Exception as e:
55
+ logger.error(f"Error rendering template: {str(e)}")
56
+ raise HTTPException(status_code=500, detail=f"Error in UI endpoint: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
  # Status endpoint
59
  @app.get("/status", response_model=StatusResponse)
60
  async def get_status():
61
  """Get the API status and version information."""
62
+ logger.info("Status endpoint called")
63
  return {
64
  "status": "operational",
65
  "version": "1.0.0",
 
76
  ):
77
  """Generate an AI SBOM from form data and render the result template."""
78
  try:
79
+ logger.info(f"Generate form endpoint called with model_id: {model_id}")
80
+
81
  # Import the generator here to avoid circular imports
82
  try:
83
  from src.aibom_generator.generator import AIBOMGenerator
84
+ logger.info("Imported AIBOMGenerator from src.aibom_generator.generator")
85
+ except ImportError as e:
86
+ logger.warning(f"Import from src.aibom_generator.generator failed: {e}")
87
  try:
88
  from aibom_generator.generator import AIBOMGenerator
89
+ logger.info("Imported AIBOMGenerator from aibom_generator.generator")
90
+ except ImportError as e:
91
+ logger.warning(f"Import from aibom_generator.generator failed: {e}")
92
  try:
93
  from generator import AIBOMGenerator
94
+ logger.info("Imported AIBOMGenerator from generator")
95
+ except ImportError as e:
96
+ logger.error(f"All import attempts failed: {e}")
97
  raise ImportError("Could not import AIBOMGenerator. Please check your installation.")
98
 
99
  # Create generator instance
100
+ logger.info("Creating AIBOMGenerator instance")
101
  generator = AIBOMGenerator()
102
 
103
  # Generate AIBOM
104
+ logger.info(f"Generating AIBOM for model: {model_id}")
105
  aibom, enhancement_report = generator.generate(
106
  model_id=model_id,
107
  include_inference=include_inference,
108
  use_best_practices=use_best_practices
109
  )
110
+ logger.info("AIBOM generation successful")
111
 
112
  # Save AIBOM to file
113
  filename = f"{model_id.replace('/', '_')}_aibom.json"
114
  filepath = os.path.join(OUTPUT_DIR, filename)
115
+ logger.info(f"Saving AIBOM to: {filepath}")
116
  with open(filepath, "w") as f:
117
  json.dump(aibom, f, indent=2)
118
 
 
137
  completeness_score = None
138
  if hasattr(generator, 'get_completeness_score'):
139
  try:
140
+ logger.info("Getting completeness score")
141
  completeness_score = generator.get_completeness_score(model_id)
142
+ logger.info(f"Completeness score: {completeness_score}")
143
  except Exception as e:
144
  logger.error(f"Error getting completeness score: {str(e)}")
145
 
146
  # Render result template
147
+ logger.info("Rendering result.html template")
148
  return templates.TemplateResponse(
149
  "result.html",
150
  {
 
158
  }
159
  )
160
  except Exception as e:
161
+ logger.error(f"Error generating AI SBOM: {str(e)}", exc_info=True)
162
  return templates.TemplateResponse(
163
  "error.html",
164
  {
 
167
  }
168
  )
169
 
170
+ # Download endpoint
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  @app.get("/download/{filename}")
172
  async def download_file(filename: str):
173
  """Download a generated AI SBOM file."""
174
  try:
175
+ logger.info(f"Download endpoint called for file: {filename}")
176
  filepath = os.path.join(OUTPUT_DIR, filename)
177
  if not os.path.exists(filepath):
178
+ logger.error(f"File not found: {filepath}")
179
  raise HTTPException(status_code=404, detail=f"File {filename} not found")
180
 
181
+ logger.info(f"Returning file: {filepath}")
182
  return FileResponse(
183
  filepath,
184
  media_type="application/json",
 
187
  except HTTPException:
188
  raise
189
  except Exception as e:
190
+ logger.error(f"Error downloading file: {str(e)}", exc_info=True)
191
  raise HTTPException(status_code=500, detail=f"Error downloading file: {str(e)}")