bandenamaj commited on
Commit
22cc708
·
verified ·
1 Parent(s): e8d575f

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +39 -29
app.py CHANGED
@@ -1,42 +1,52 @@
1
- from fastapi import FastAPI, HTTPException
2
  from fastapi.responses import StreamingResponse
3
  import requests
4
  import os
5
 
6
  app = FastAPI()
7
-
8
- # Optional: Add your HF_TOKEN to Space Secrets for private models
9
  HF_TOKEN = os.environ.get("HF_TOKEN")
 
10
 
11
- @app.get("/")
12
- def home():
13
- return {"status": "running", "message": "HF Storage Proxy"}
14
-
15
- @app.get("/resolve/{repo_id:path}/{filename:path}")
16
- async def proxy_resolve(repo_id: str, filename: str):
17
- """
18
- Proxies requests to huggingface.co/repo/resolve/main/file
19
- """
20
- hf_url = f"https://huggingface.co/{repo_id}/resolve/main/{filename}"
21
-
22
- headers = {}
23
- if HF_TOKEN:
24
- headers["Authorization"] = f"Bearer {HF_TOKEN}"
25
 
 
 
 
 
 
26
  try:
27
- # We use stream=True to handle large model files without loading into memory
28
- response = requests.get(hf_url, headers=headers, stream=True, allow_redirects=True)
29
- response.raise_for_status()
 
 
 
 
 
 
 
 
 
30
 
31
  return StreamingResponse(
32
- response.iter_content(chunk_size=1024*1024),
33
- media_type=response.headers.get("content-type"),
34
- headers={
35
- "Content-Disposition": response.headers.get("Content-Disposition", f'attachment; filename="{filename}"'),
36
- "Content-Length": response.headers.get("Content-Length")
37
- }
38
  )
39
- except requests.exceptions.HTTPError as e:
40
- raise HTTPException(status_code=e.response.status_code, detail=str(e))
41
  except Exception as e:
42
- raise HTTPException(status_code=500, detail=str(e))
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Request, Response
2
  from fastapi.responses import StreamingResponse
3
  import requests
4
  import os
5
 
6
  app = FastAPI()
 
 
7
  HF_TOKEN = os.environ.get("HF_TOKEN")
8
+ BASE_URL = "https://huggingface.co"
9
 
10
+ def get_proxy_response(path: str, query: str = "", method: str = "GET", headers: dict = {}, body: bytes = None):
11
+ url = f"{BASE_URL}/{path}"
12
+ if query:
13
+ url += f"?{query}"
 
 
 
 
 
 
 
 
 
 
14
 
15
+ # Filter headers to avoid conflicts
16
+ filtered_headers = {k: v for k, v in headers.items() if k.lower() not in ["host", "content-length", "connection"]}
17
+ if HF_TOKEN and "authorization" not in [k.lower() for k in filtered_headers.keys()]:
18
+ filtered_headers["Authorization"] = f"Bearer {HF_TOKEN}"
19
+
20
  try:
21
+ resp = requests.request(
22
+ method=method,
23
+ url=url,
24
+ headers=filtered_headers,
25
+ data=body,
26
+ stream=True,
27
+ allow_redirects=True
28
+ )
29
+
30
+ # Exclude headers that FastAPI/Uvicorn might handle or conflict with
31
+ excluded = ["content-encoding", "transfer-encoding", "connection", "keep-alive"]
32
+ resp_headers = {k: v for k, v in resp.headers.items() if k.lower() not in excluded}
33
 
34
  return StreamingResponse(
35
+ resp.iter_content(chunk_size=128*1024),
36
+ status_code=resp.status_code,
37
+ headers=resp_headers,
38
+ media_type=resp.headers.get("content-type")
 
 
39
  )
 
 
40
  except Exception as e:
41
+ return Response(content=str(e), status_code=500)
42
+
43
+ @app.get("/resolve/{repo_id:path}/{filename:path}")
44
+ async def old_resolve_compat(repo_id: str, filename: str):
45
+ """Keep compatibility with the previous /resolve structure."""
46
+ return get_proxy_response(f"{repo_id}/resolve/main/{filename}")
47
+
48
+ @app.api_route("/{path:path}", methods=["GET", "HEAD", "OPTIONS", "POST"])
49
+ async def catch_all(request: Request, path: str):
50
+ """Universal proxy to Hugging Face."""
51
+ body = await request.body() if request.method == "POST" else None
52
+ return get_proxy_response(path, request.url.query, request.method, dict(request.headers), body)