FlameF0X commited on
Commit
e1a9cb7
·
verified ·
1 Parent(s): 3aa1bcf

Create api.py

Browse files
Files changed (1) hide show
  1. api.py +208 -0
api.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException, BackgroundTasks
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from pydantic import BaseModel
4
+ from typing import Dict, Any, Optional
5
+ import json
6
+ import os
7
+ from datetime import datetime
8
+ from huggingface_hub import HfApi, Repository
9
+ import hashlib
10
+ import uuid
11
+
12
+ # FastAPI app
13
+ app = FastAPI(title="ML Tracker API", version="1.0.0")
14
+
15
+ # CORS middleware
16
+ app.add_middleware(
17
+ CORSMiddleware,
18
+ allow_origins=["*"],
19
+ allow_credentials=True,
20
+ allow_methods=["*"],
21
+ allow_headers=["*"],
22
+ )
23
+
24
+ # Pydantic models
25
+ class LogRequest(BaseModel):
26
+ api_key: str
27
+ experiment: str
28
+ step: int
29
+ metrics: Dict[str, Any]
30
+ timestamp: Optional[float] = None
31
+ config: Optional[Dict[str, Any]] = None
32
+
33
+ class ExperimentResponse(BaseModel):
34
+ experiment: str
35
+ total_steps: int
36
+ created_at: str
37
+ last_updated: str
38
+ metrics: list
39
+
40
+ # In-memory storage (in production, use HF Hub or database)
41
+ experiments_db = {}
42
+ api_keys_db = {}
43
+
44
+ def verify_api_key(api_key: str) -> bool:
45
+ """Verify if the API key is valid"""
46
+ # In production, verify against HF Hub or database
47
+ return True # Simplified for demo
48
+
49
+ def get_user_from_api_key(api_key: str) -> str:
50
+ """Get username from API key"""
51
+ # In production, lookup from database
52
+ return hashlib.sha256(api_key.encode()).hexdigest()[:8]
53
+
54
+ @app.get("/")
55
+ async def root():
56
+ return {"message": "ML Tracker API - Free W&B Alternative", "version": "1.0.0"}
57
+
58
+ @app.post("/api/log")
59
+ async def log_metrics(request: LogRequest, background_tasks: BackgroundTasks):
60
+ """Log metrics for an experiment"""
61
+ try:
62
+ # Verify API key
63
+ if not verify_api_key(request.api_key):
64
+ raise HTTPException(status_code=401, detail="Invalid API key")
65
+
66
+ user_id = get_user_from_api_key(request.api_key)
67
+ experiment_key = f"{user_id}_{request.experiment}"
68
+
69
+ # Initialize experiment if not exists
70
+ if experiment_key not in experiments_db:
71
+ experiments_db[experiment_key] = {
72
+ "experiment": request.experiment,
73
+ "user_id": user_id,
74
+ "created_at": datetime.now().isoformat(),
75
+ "metrics": [],
76
+ "config": request.config or {}
77
+ }
78
+
79
+ # Add metrics
80
+ metric_entry = {
81
+ "step": request.step,
82
+ "timestamp": request.timestamp or datetime.now().timestamp(),
83
+ "metrics": request.metrics
84
+ }
85
+
86
+ experiments_db[experiment_key]["metrics"].append(metric_entry)
87
+ experiments_db[experiment_key]["last_updated"] = datetime.now().isoformat()
88
+
89
+ # Update config if provided
90
+ if request.config:
91
+ experiments_db[experiment_key]["config"].update(request.config)
92
+
93
+ # Background task to save to HF Hub (simplified)
94
+ background_tasks.add_task(save_to_hub, experiment_key)
95
+
96
+ return {
97
+ "status": "success",
98
+ "experiment": request.experiment,
99
+ "step": request.step,
100
+ "metrics_logged": len(request.metrics)
101
+ }
102
+
103
+ except Exception as e:
104
+ raise HTTPException(status_code=500, detail=f"Error logging metrics: {str(e)}")
105
+
106
+ @app.get("/api/experiments")
107
+ async def get_experiments(api_key: str):
108
+ """Get all experiments for a user"""
109
+ try:
110
+ if not verify_api_key(api_key):
111
+ raise HTTPException(status_code=401, detail="Invalid API key")
112
+
113
+ user_id = get_user_from_api_key(api_key)
114
+ user_experiments = []
115
+
116
+ for exp_key, exp_data in experiments_db.items():
117
+ if exp_data["user_id"] == user_id:
118
+ user_experiments.append({
119
+ "experiment": exp_data["experiment"],
120
+ "total_steps": len(exp_data["metrics"]),
121
+ "created_at": exp_data["created_at"],
122
+ "last_updated": exp_data.get("last_updated", exp_data["created_at"])
123
+ })
124
+
125
+ return {"experiments": user_experiments}
126
+
127
+ except Exception as e:
128
+ raise HTTPException(status_code=500, detail=f"Error fetching experiments: {str(e)}")
129
+
130
+ @app.get("/api/experiment/{experiment_name}")
131
+ async def get_experiment(experiment_name: str, api_key: str):
132
+ """Get detailed data for a specific experiment"""
133
+ try:
134
+ if not verify_api_key(api_key):
135
+ raise HTTPException(status_code=401, detail="Invalid API key")
136
+
137
+ user_id = get_user_from_api_key(api_key)
138
+ experiment_key = f"{user_id}_{experiment_name}"
139
+
140
+ if experiment_key not in experiments_db:
141
+ raise HTTPException(status_code=404, detail="Experiment not found")
142
+
143
+ exp_data = experiments_db[experiment_key]
144
+
145
+ return {
146
+ "experiment": exp_data["experiment"],
147
+ "created_at": exp_data["created_at"],
148
+ "last_updated": exp_data.get("last_updated", exp_data["created_at"]),
149
+ "total_steps": len(exp_data["metrics"]),
150
+ "config": exp_data["config"],
151
+ "metrics": exp_data["metrics"]
152
+ }
153
+
154
+ except Exception as e:
155
+ raise HTTPException(status_code=500, detail=f"Error fetching experiment: {str(e)}")
156
+
157
+ @app.delete("/api/experiment/{experiment_name}")
158
+ async def delete_experiment(experiment_name: str, api_key: str):
159
+ """Delete an experiment"""
160
+ try:
161
+ if not verify_api_key(api_key):
162
+ raise HTTPException(status_code=401, detail="Invalid API key")
163
+
164
+ user_id = get_user_from_api_key(api_key)
165
+ experiment_key = f"{user_id}_{experiment_name}"
166
+
167
+ if experiment_key not in experiments_db:
168
+ raise HTTPException(status_code=404, detail="Experiment not found")
169
+
170
+ del experiments_db[experiment_key]
171
+
172
+ return {"status": "success", "message": f"Experiment '{experiment_name}' deleted"}
173
+
174
+ except Exception as e:
175
+ raise HTTPException(status_code=500, detail=f"Error deleting experiment: {str(e)}")
176
+
177
+ async def save_to_hub(experiment_key: str):
178
+ """Save experiment data to HuggingFace Hub (background task)"""
179
+ try:
180
+ # In production, save to HF Hub datasets
181
+ # For demo, we'll just log
182
+ print(f"Saving experiment {experiment_key} to HF Hub...")
183
+
184
+ # Example HF Hub integration:
185
+ # api = HfApi(token=user_token)
186
+ # repo_id = f"{username}/ml-tracker-data"
187
+ # api.upload_file(
188
+ # path_or_fileobj=json.dumps(experiments_db[experiment_key]),
189
+ # path_in_repo=f"experiments/{experiment_key}.json",
190
+ # repo_id=repo_id,
191
+ # repo_type="dataset"
192
+ # )
193
+
194
+ except Exception as e:
195
+ print(f"Error saving to hub: {str(e)}")
196
+
197
+ @app.get("/api/health")
198
+ async def health_check():
199
+ """Health check endpoint"""
200
+ return {
201
+ "status": "healthy",
202
+ "timestamp": datetime.now().isoformat(),
203
+ "total_experiments": len(experiments_db)
204
+ }
205
+
206
+ if __name__ == "__main__":
207
+ import uvicorn
208
+ uvicorn.run(app, host="0.0.0.0", port=8000)