a1c00l commited on
Commit
3457287
·
verified ·
1 Parent(s): edbf608

Update src/aibom_generator/api.py

Browse files
Files changed (1) hide show
  1. src/aibom_generator/api.py +3 -327
src/aibom_generator/api.py CHANGED
@@ -1,9 +1,6 @@
1
  import os
2
  import json
3
  import logging
4
- import uuid
5
- import shutil
6
- import tempfile
7
  from typing import Dict, List, Optional, Any, Union
8
  from datetime import datetime
9
  from pathlib import Path
@@ -73,77 +70,6 @@ async def startup_event():
73
  os.makedirs(OUTPUT_DIR, exist_ok=True)
74
  logger.info(f"Created output directory at {OUTPUT_DIR}")
75
 
76
- # Helper function to ensure enhancement report has the expected structure
77
- def ensure_enhancement_report_structure(enhancement_report):
78
- """Ensure the enhancement report has all the expected fields for templates."""
79
- if enhancement_report is None:
80
- enhancement_report = {}
81
-
82
- # Ensure ai_enhanced exists
83
- if "ai_enhanced" not in enhancement_report:
84
- enhancement_report["ai_enhanced"] = False
85
-
86
- # Ensure ai_model exists
87
- if "ai_model" not in enhancement_report:
88
- enhancement_report["ai_model"] = "Default AI Model"
89
-
90
- # Ensure original_score exists and has completeness_score
91
- if "original_score" not in enhancement_report:
92
- enhancement_report["original_score"] = {"total_score": 0, "completeness_score": 0}
93
- elif "completeness_score" not in enhancement_report["original_score"]:
94
- enhancement_report["original_score"]["completeness_score"] = enhancement_report["original_score"].get("total_score", 0)
95
-
96
- # Ensure final_score exists and has completeness_score
97
- if "final_score" not in enhancement_report:
98
- enhancement_report["final_score"] = {"total_score": 0, "completeness_score": 0}
99
- elif "completeness_score" not in enhancement_report["final_score"]:
100
- enhancement_report["final_score"]["completeness_score"] = enhancement_report["final_score"].get("total_score", 0)
101
-
102
- # Ensure improvement exists
103
- if "improvement" not in enhancement_report:
104
- enhancement_report["improvement"] = 0
105
-
106
- return enhancement_report
107
-
108
- # Helper function to ensure completeness_score has the expected structure
109
- def ensure_completeness_score_structure(completeness_score):
110
- """Ensure the completeness_score has all the expected fields for templates."""
111
- if completeness_score is None:
112
- completeness_score = {}
113
-
114
- # Ensure total_score exists
115
- if "total_score" not in completeness_score:
116
- completeness_score["total_score"] = 0
117
-
118
- # Ensure completeness_score exists
119
- if "completeness_score" not in completeness_score:
120
- completeness_score["completeness_score"] = completeness_score.get("total_score", 0)
121
-
122
- # Ensure section_scores exists
123
- if "section_scores" not in completeness_score:
124
- completeness_score["section_scores"] = {}
125
-
126
- # Ensure max_scores exists
127
- if "max_scores" not in completeness_score:
128
- completeness_score["max_scores"] = {}
129
-
130
- # Ensure field_checklist exists
131
- if "field_checklist" not in completeness_score:
132
- completeness_score["field_checklist"] = {}
133
-
134
- # Ensure field_tiers exists
135
- if "field_tiers" not in completeness_score:
136
- completeness_score["field_tiers"] = {}
137
-
138
- # Ensure completeness_profile exists
139
- if "completeness_profile" not in completeness_score:
140
- completeness_score["completeness_profile"] = {
141
- "name": "Incomplete",
142
- "description": "This AI SBOM needs improvement to meet transparency requirements."
143
- }
144
-
145
- return completeness_score
146
-
147
  # Root route serves the UI
148
  @app.get("/", response_class=HTMLResponse)
149
  async def root(request: Request):
@@ -188,15 +114,14 @@ async def ui(request: Request):
188
  """Serve the web UI interface (kept for backward compatibility)."""
189
  return await root(request)
190
 
191
- # Status endpoint - both at root and /api path for compatibility
192
  @app.get("/status", response_model=StatusResponse)
193
- @app.get("/api/status", response_model=StatusResponse)
194
  async def get_status():
195
  """Get the API status and version information."""
196
  return {
197
  "status": "operational",
198
  "version": "1.0.0",
199
- "generator_version": "1.0.0",
200
  }
201
 
202
  # Form-based generate endpoint for web UI
@@ -254,23 +179,13 @@ async def generate_form(
254
  </script>
255
  """ % (download_url, filename)
256
 
257
- # Process enhancement report for template
258
- enhancement_report = ensure_enhancement_report_structure(enhancement_report)
259
- logger.info(f"Enhancement report structure: {enhancement_report}")
260
-
261
  # Get completeness score
262
  completeness_score = None
263
  if hasattr(generator, 'get_completeness_score'):
264
  try:
265
  completeness_score = generator.get_completeness_score(model_id)
266
- completeness_score = ensure_completeness_score_structure(completeness_score)
267
- logger.info(f"Completeness score structure: {completeness_score}")
268
  except Exception as e:
269
  logger.error(f"Error getting completeness score: {str(e)}")
270
- completeness_score = ensure_completeness_score_structure(None)
271
- else:
272
- # Create a default completeness score if the method doesn't exist
273
- completeness_score = ensure_completeness_score_structure(None)
274
 
275
  # Render result template
276
  return templates.TemplateResponse(
@@ -352,9 +267,6 @@ async def generate_with_report_api(request: GenerateWithReportRequest):
352
  use_best_practices=request.use_best_practices
353
  )
354
 
355
- # Process enhancement report for API response
356
- enhancement_report = ensure_enhancement_report_structure(enhancement_report)
357
-
358
  return {
359
  "aibom": aibom,
360
  "enhancement_report": enhancement_report
@@ -362,100 +274,6 @@ async def generate_with_report_api(request: GenerateWithReportRequest):
362
  except Exception as e:
363
  raise HTTPException(status_code=500, detail=f"Error generating AI SBOM: {str(e)}")
364
 
365
- @app.post("/api/batch-generate")
366
- async def batch_generate_api(
367
- request: BatchGenerateRequest,
368
- background_tasks: BackgroundTasks
369
- ):
370
- """Start a batch job to generate multiple AI SBOMs."""
371
- try:
372
- # Generate job ID
373
- job_id = str(uuid.uuid4())
374
-
375
- # Create job directory
376
- job_dir = os.path.join(OUTPUT_DIR, job_id)
377
- os.makedirs(job_dir, exist_ok=True)
378
-
379
- # Create job status file
380
- status_file = os.path.join(job_dir, "status.json")
381
- with open(status_file, "w") as f:
382
- json.dump({
383
- "job_id": job_id,
384
- "status": "pending",
385
- "model_ids": request.model_ids,
386
- "completed": 0,
387
- "total": len(request.model_ids),
388
- "results": {}
389
- }, f, indent=2)
390
-
391
- # Start background task
392
- background_tasks.add_task(
393
- process_batch_job,
394
- job_id=job_id,
395
- model_ids=request.model_ids,
396
- include_inference=request.include_inference,
397
- use_best_practices=request.use_best_practices
398
- )
399
-
400
- return {
401
- "job_id": job_id,
402
- "status": "pending",
403
- "model_ids": request.model_ids,
404
- "message": f"Batch job started. {len(request.model_ids)} models will be processed."
405
- }
406
- except Exception as e:
407
- raise HTTPException(status_code=500, detail=f"Error starting batch job: {str(e)}")
408
-
409
- @app.get("/api/batch-status/{job_id}")
410
- async def batch_status_api(job_id: str):
411
- """Get the status of a batch job."""
412
- try:
413
- # Check if job exists
414
- job_dir = os.path.join(OUTPUT_DIR, job_id)
415
- if not os.path.exists(job_dir):
416
- raise HTTPException(status_code=404, detail=f"Job {job_id} not found")
417
-
418
- # Read job status
419
- status_file = os.path.join(job_dir, "status.json")
420
- with open(status_file, "r") as f:
421
- status = json.load(f)
422
-
423
- return status
424
- except HTTPException:
425
- raise
426
- except Exception as e:
427
- raise HTTPException(status_code=500, detail=f"Error getting job status: {str(e)}")
428
-
429
- @app.get("/api/batch-results/{job_id}")
430
- async def batch_results_api(job_id: str):
431
- """Get the results of a completed batch job."""
432
- try:
433
- # Check if job exists
434
- job_dir = os.path.join(OUTPUT_DIR, job_id)
435
- if not os.path.exists(job_dir):
436
- raise HTTPException(status_code=404, detail=f"Job {job_id} not found")
437
-
438
- # Read job status
439
- status_file = os.path.join(job_dir, "status.json")
440
- with open(status_file, "r") as f:
441
- status = json.load(f)
442
-
443
- # Check if job is completed
444
- if status["status"] != "completed":
445
- raise HTTPException(status_code=400, detail=f"Job {job_id} is not completed yet")
446
-
447
- # Read results
448
- results = {}
449
- for model_id, result_file in status["results"].items():
450
- with open(os.path.join(job_dir, result_file), "r") as f:
451
- results[model_id] = json.load(f)
452
-
453
- return results
454
- except HTTPException:
455
- raise
456
- except Exception as e:
457
- raise HTTPException(status_code=500, detail=f"Error getting job results: {str(e)}")
458
-
459
  @app.get("/api/model-score/{model_id}")
460
  async def model_score_api(model_id: str):
461
  """Get the completeness score for a model."""
@@ -478,58 +296,12 @@ async def model_score_api(model_id: str):
478
  # Get completeness score
479
  if hasattr(generator, 'get_completeness_score'):
480
  completeness_score = generator.get_completeness_score(model_id)
481
- completeness_score = ensure_completeness_score_structure(completeness_score)
482
  return completeness_score
483
  else:
484
- # Create a default completeness score if the method doesn't exist
485
- completeness_score = ensure_completeness_score_structure(None)
486
- return completeness_score
487
  except Exception as e:
488
  raise HTTPException(status_code=500, detail=f"Error getting model score: {str(e)}")
489
 
490
- @app.post("/api/upload-model-card")
491
- async def upload_model_card_api(
492
- model_id: str = Form(...),
493
- model_card: UploadFile = File(...)
494
- ):
495
- """Upload a model card file for a model."""
496
- try:
497
- # Create temporary file
498
- with tempfile.NamedTemporaryFile(delete=False, suffix=".md") as temp_file:
499
- # Write uploaded file to temporary file
500
- shutil.copyfileobj(model_card.file, temp_file)
501
-
502
- # Import the generator here to avoid circular imports
503
- try:
504
- from src.aibom_generator.generator import AIBOMGenerator
505
- except ImportError:
506
- try:
507
- from aibom_generator.generator import AIBOMGenerator
508
- except ImportError:
509
- try:
510
- from generator import AIBOMGenerator
511
- except ImportError:
512
- raise ImportError("Could not import AIBOMGenerator. Please check your installation.")
513
-
514
- # Create generator instance
515
- generator = AIBOMGenerator()
516
-
517
- # Process model card
518
- if hasattr(generator, 'process_model_card'):
519
- result = generator.process_model_card(model_id, temp_file.name)
520
-
521
- # Clean up temporary file
522
- os.unlink(temp_file.name)
523
-
524
- return result
525
- else:
526
- # Clean up temporary file
527
- os.unlink(temp_file.name)
528
-
529
- raise HTTPException(status_code=501, detail="Model card processing not implemented")
530
- except Exception as e:
531
- raise HTTPException(status_code=500, detail=f"Error processing model card: {str(e)}")
532
-
533
  @app.get("/download/{filename}")
534
  async def download_file(filename: str):
535
  """Download a generated AI SBOM file."""
@@ -547,99 +319,3 @@ async def download_file(filename: str):
547
  raise
548
  except Exception as e:
549
  raise HTTPException(status_code=500, detail=f"Error downloading file: {str(e)}")
550
-
551
- # Background task for batch processing
552
- async def process_batch_job(
553
- job_id: str,
554
- model_ids: List[str],
555
- include_inference: bool,
556
- use_best_practices: bool
557
- ):
558
- """Process a batch job in the background."""
559
- try:
560
- # Import the generator here to avoid circular imports
561
- try:
562
- from src.aibom_generator.generator import AIBOMGenerator
563
- except ImportError:
564
- try:
565
- from aibom_generator.generator import AIBOMGenerator
566
- except ImportError:
567
- try:
568
- from generator import AIBOMGenerator
569
- except ImportError:
570
- raise ImportError("Could not import AIBOMGenerator. Please check your installation.")
571
-
572
- # Create generator instance
573
- generator = AIBOMGenerator()
574
-
575
- # Create job directory
576
- job_dir = os.path.join(OUTPUT_DIR, job_id)
577
-
578
- # Read job status
579
- status_file = os.path.join(job_dir, "status.json")
580
- with open(status_file, "r") as f:
581
- status = json.load(f)
582
-
583
- # Update status to running
584
- status["status"] = "running"
585
- with open(status_file, "w") as f:
586
- json.dump(status, f, indent=2)
587
-
588
- # Process each model
589
- for i, model_id in enumerate(model_ids):
590
- try:
591
- # Generate AIBOM
592
- aibom, enhancement_report = generator.generate(
593
- model_id=model_id,
594
- include_inference=include_inference,
595
- use_best_practices=use_best_practices
596
- )
597
-
598
- # Process enhancement report for API response
599
- enhancement_report = ensure_enhancement_report_structure(enhancement_report)
600
-
601
- # Save result to file
602
- result_file = f"{model_id.replace('/', '_')}_aibom.json"
603
- result_path = os.path.join(job_dir, result_file)
604
- with open(result_path, "w") as f:
605
- json.dump({
606
- "aibom": aibom,
607
- "enhancement_report": enhancement_report
608
- }, f, indent=2)
609
-
610
- # Update status
611
- status["completed"] += 1
612
- status["results"][model_id] = result_file
613
- with open(status_file, "w") as f:
614
- json.dump(status, f, indent=2)
615
- except Exception as e:
616
- # Log error
617
- logger.error(f"Error processing model {model_id}: {str(e)}")
618
-
619
- # Update status
620
- status["completed"] += 1
621
- status["results"][model_id] = f"Error: {str(e)}"
622
- with open(status_file, "w") as f:
623
- json.dump(status, f, indent=2)
624
-
625
- # Update status to completed
626
- status["status"] = "completed"
627
- with open(status_file, "w") as f:
628
- json.dump(status, f, indent=2)
629
- except Exception as e:
630
- # Log error
631
- logger.error(f"Error processing batch job {job_id}: {str(e)}")
632
-
633
- try:
634
- # Update status to failed
635
- status_file = os.path.join(OUTPUT_DIR, job_id, "status.json")
636
- with open(status_file, "r") as f:
637
- status = json.load(f)
638
-
639
- status["status"] = "failed"
640
- status["error"] = str(e)
641
-
642
- with open(status_file, "w") as f:
643
- json.dump(status, f, indent=2)
644
- except Exception as status_e:
645
- logger.error(f"Error updating job status: {str(status_e)}")
 
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
 
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):
 
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",
124
+ "generator_version": "0.1.0",
125
  }
126
 
127
  # Form-based generate endpoint for web UI
 
179
  </script>
180
  """ % (download_url, filename)
181
 
 
 
 
 
182
  # Get completeness score
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(
 
267
  use_best_practices=request.use_best_practices
268
  )
269
 
 
 
 
270
  return {
271
  "aibom": aibom,
272
  "enhancement_report": enhancement_report
 
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."""
 
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."""
 
319
  raise
320
  except Exception as e:
321
  raise HTTPException(status_code=500, detail=f"Error downloading file: {str(e)}")