a1c00l commited on
Commit
53be893
·
verified ·
1 Parent(s): 8bc8b6c

Update src/aibom_generator/api.py

Browse files
Files changed (1) hide show
  1. src/aibom_generator/api.py +93 -129
src/aibom_generator/api.py CHANGED
@@ -1,153 +1,117 @@
1
- import logging
2
  import os
3
- from typing import Dict, List, Optional, Any, Union
4
-
5
- from fastapi import FastAPI, HTTPException, BackgroundTasks
6
- from fastapi.middleware.cors import CORSMiddleware
 
 
7
  from pydantic import BaseModel
8
 
9
- from aibom_generator.generator import AIBOMGenerator
10
- from aibom_generator.utils import setup_logging, calculate_completeness_score
11
-
12
- # Set up logging
13
- setup_logging()
14
  logger = logging.getLogger(__name__)
15
 
16
- # Create FastAPI app
17
- app = FastAPI(
18
- title="AIBOM Generator API",
19
- description="API for generating AI Bills of Materials (AIBOMs) in CycloneDX format for Hugging Face models.",
20
- version="0.1.0",
21
- )
22
-
23
- # Add CORS middleware
24
- app.add_middleware(
25
- CORSMiddleware,
26
- allow_origins=["*"],
27
- allow_credentials=True,
28
- allow_methods=["*"],
29
- allow_headers=["*"],
30
- )
31
-
32
- # Create generator instance
33
- generator = AIBOMGenerator(
34
- hf_token=os.environ.get("HF_TOKEN"),
35
- inference_model_url=os.environ.get("AIBOM_INFERENCE_URL"),
36
- use_inference=os.environ.get("AIBOM_USE_INFERENCE", "true").lower() == "true",
37
- cache_dir=os.environ.get("AIBOM_CACHE_DIR"),
38
- )
39
 
 
 
40
 
41
- # Define request and response models
42
- class GenerateRequest(BaseModel):
43
- model_id: str
44
- include_inference: Optional[bool] = None
45
- completeness_threshold: Optional[int] = 0
46
 
 
 
47
 
48
- class GenerateResponse(BaseModel):
49
- aibom: Dict[str, Any]
50
- completeness_score: int
51
- model_id: str
52
-
53
 
 
54
  class StatusResponse(BaseModel):
55
  status: str
56
  version: str
 
57
 
 
 
 
 
 
58
 
59
- # Define API endpoints
60
- @app.get("/", response_model=StatusResponse)
61
- async def root():
62
- """Get API status."""
63
- return {
64
- "status": "ok",
65
- "version": "0.1.0",
66
- }
67
-
68
-
69
- @app.post("/generate", response_model=GenerateResponse)
70
- async def generate_aibom(request: GenerateRequest):
71
- """Generate an AIBOM for a Hugging Face model."""
72
  try:
73
- # Generate the AIBOM
74
- aibom = generator.generate_aibom(
75
- model_id=request.model_id,
76
- include_inference=request.include_inference,
77
- )
78
-
79
- # Calculate completeness score
80
- completeness_score = calculate_completeness_score(aibom)
81
-
82
- # Check if it meets the threshold
83
- if completeness_score < request.completeness_threshold:
84
- raise HTTPException(
85
- status_code=400,
86
- detail=f"AIBOM completeness score ({completeness_score}) is below threshold ({request.completeness_threshold})",
87
- )
88
-
89
- return {
90
- "aibom": aibom,
91
- "completeness_score": completeness_score,
92
- "model_id": request.model_id,
93
- }
94
  except Exception as e:
95
- logger.error(f"Error generating AIBOM: {e}")
96
- raise HTTPException(
97
- status_code=500,
98
- detail=f"Error generating AIBOM: {str(e)}",
99
- )
100
-
101
-
102
- @app.post("/generate/async")
103
- async def generate_aibom_async(
104
- request: GenerateRequest,
105
- background_tasks: BackgroundTasks,
106
- ):
107
- """Generate an AIBOM asynchronously for a Hugging Face model."""
108
- # Add to background tasks
109
- background_tasks.add_task(
110
- _generate_aibom_background,
111
- request.model_id,
112
- request.include_inference,
113
- request.completeness_threshold,
114
- )
115
-
116
- return {
117
- "status": "accepted",
118
- "message": f"AIBOM generation for {request.model_id} started in the background",
119
- }
120
-
121
-
122
- async def _generate_aibom_background(
123
- model_id: str,
124
- include_inference: Optional[bool] = None,
125
- completeness_threshold: Optional[int] = 0,
126
  ):
127
- """Generate an AIBOM in the background."""
128
  try:
129
- # Generate the AIBOM
 
 
 
130
  aibom = generator.generate_aibom(
131
  model_id=model_id,
132
  include_inference=include_inference,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  )
134
-
135
- # Calculate completeness score
136
- completeness_score = calculate_completeness_score(aibom)
137
-
138
- # TODO: Store the result or notify the user
139
- logger.info(f"Background AIBOM generation completed for {model_id}")
140
- logger.info(f"Completeness score: {completeness_score}")
141
  except Exception as e:
142
- logger.error(f"Error in background AIBOM generation for {model_id}: {e}")
143
-
144
-
145
- @app.get("/health")
146
- async def health():
147
- """Health check endpoint."""
148
- return {"status": "healthy"}
149
-
150
-
151
- if __name__ == "__main__":
152
- import uvicorn
153
- uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 5000)))
 
 
1
  import os
2
+ import json
3
+ import logging
4
+ from fastapi import FastAPI, HTTPException, Request, Form
5
+ from fastapi.responses import HTMLResponse
6
+ from fastapi.staticfiles import StaticFiles
7
+ from fastapi.templating import Jinja2Templates
8
  from pydantic import BaseModel
9
 
10
+ # Configure logging
11
+ logging.basicConfig(level=logging.INFO)
 
 
 
12
  logger = logging.getLogger(__name__)
13
 
14
+ # Define directories
15
+ templates_dir = "templates"
16
+ OUTPUT_DIR = "/tmp/aibom_output"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
+ # Initialize templates
19
+ templates = Jinja2Templates(directory=templates_dir)
20
 
21
+ # Create app
22
+ app = FastAPI(title="AI SBOM Generator API")
 
 
 
23
 
24
+ # Ensure output directory exists
25
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
26
 
27
+ # Mount output directory as static files
28
+ app.mount("/output", StaticFiles(directory=OUTPUT_DIR), name="output")
 
 
 
29
 
30
+ # Status response model
31
  class StatusResponse(BaseModel):
32
  status: str
33
  version: str
34
+ generator_version: str
35
 
36
+ @app.on_event("startup")
37
+ async def startup_event():
38
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
39
+ logger.info(f"Output directory ready at {OUTPUT_DIR}")
40
+ logger.info(f"Registered routes: {[route.path for route in app.routes]}")
41
 
42
+ @app.get("/", response_class=HTMLResponse)
43
+ async def root(request: Request):
 
 
 
 
 
 
 
 
 
 
 
44
  try:
45
+ return templates.TemplateResponse("index.html", {"request": request})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  except Exception as e:
47
+ logger.error(f"Error rendering template: {str(e)}")
48
+ raise HTTPException(status_code=500, detail=f"Template rendering error: {str(e)}")
49
+
50
+ @app.get("/status", response_model=StatusResponse)
51
+ async def get_status():
52
+ return StatusResponse(status="operational", version="1.0.0", generator_version="1.0.0")
53
+
54
+ @app.post("/generate", response_class=HTMLResponse)
55
+ async def generate_form(
56
+ request: Request,
57
+ model_id: str = Form(...),
58
+ include_inference: bool = Form(False),
59
+ use_best_practices: bool = Form(True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  ):
 
61
  try:
62
+ from src.aibom_generator.generator import AIBOMGenerator
63
+
64
+ generator = AIBOMGenerator()
65
+
66
  aibom = generator.generate_aibom(
67
  model_id=model_id,
68
  include_inference=include_inference,
69
+ use_best_practices=use_best_practices
70
+ )
71
+ enhancement_report = generator.get_enhancement_report()
72
+
73
+ filename = f"{model_id.replace('/', '_')}_aibom.json"
74
+ filepath = os.path.join(OUTPUT_DIR, filename)
75
+
76
+ with open(filepath, "w") as f:
77
+ json.dump(aibom, f, indent=2)
78
+
79
+ download_url = f"/output/{filename}"
80
+
81
+ download_script = f"""
82
+ <script>
83
+ function downloadJSON() {{
84
+ const a = document.createElement('a');
85
+ a.href = '{download_url}';
86
+ a.download = '{filename}';
87
+ document.body.appendChild(a);
88
+ a.click();
89
+ document.body.removeChild(a);
90
+ }}
91
+ </script>
92
+ """
93
+
94
+ completeness_score = None
95
+ if hasattr(generator, 'get_completeness_score'):
96
+ try:
97
+ completeness_score = generator.get_completeness_score(model_id)
98
+ except Exception as e:
99
+ logger.error(f"Completeness score error: {str(e)}")
100
+
101
+ return templates.TemplateResponse(
102
+ "result.html",
103
+ {
104
+ "request": request,
105
+ "model_id": model_id,
106
+ "aibom": aibom,
107
+ "enhancement_report": enhancement_report,
108
+ "completeness_score": completeness_score,
109
+ "download_url": download_url,
110
+ "download_script": download_script
111
+ }
112
  )
 
 
 
 
 
 
 
113
  except Exception as e:
114
+ logger.error(f"Error generating AI SBOM: {str(e)}")
115
+ return templates.TemplateResponse(
116
+ "error.html", {"request": request, "error": str(e)}
117
+ )