ademarteau commited on
Commit
c3fc8d4
Β·
1 Parent(s): e7f1f53

fix: proxy HF Inference API through FastAPI to bypass HF Spaces CSP

Browse files
frontend/src/App.jsx CHANGED
@@ -177,16 +177,12 @@ function runOneSimulation(computeROP, demandSeries, envKey) {
177
  };
178
  }
179
 
180
- // ─── HF INFERENCE API ─────────────────────────────────────────────────────────
181
  async function callQwen(messages, modelId, hfToken) {
182
- const url = `https://api-inference.huggingface.co/models/${modelId}/v1/chat/completions`;
183
- const resp = await fetch(url, {
184
  method: "POST",
185
- headers: {
186
- "Content-Type": "application/json",
187
- ...(hfToken ? { Authorization: `Bearer ${hfToken}` } : {}),
188
- },
189
- body: JSON.stringify({ model: modelId, messages, max_tokens: 600, temperature: 0.7 }),
190
  });
191
  if (!resp.ok) throw new Error(`API error ${resp.status}: ${await resp.text()}`);
192
  const data = await resp.json();
 
177
  };
178
  }
179
 
180
+ // ─── HF INFERENCE API (proxied through FastAPI to avoid CSP on HF Spaces) ────
181
  async function callQwen(messages, modelId, hfToken) {
182
+ const resp = await fetch("/api/qwen", {
 
183
  method: "POST",
184
+ headers: { "Content-Type": "application/json" },
185
+ body: JSON.stringify({ model: modelId, messages, max_tokens: 600, temperature: 0.7, hf_token: hfToken || "" }),
 
 
 
186
  });
187
  if (!resp.ok) throw new Error(`API error ${resp.status}: ${await resp.text()}`);
188
  const data = await resp.json();
requirements-server.txt CHANGED
@@ -4,3 +4,4 @@ pydantic==2.12.5
4
  numpy==2.4.2
5
  ciw==3.2.7
6
  aiofiles==24.1.0
 
 
4
  numpy==2.4.2
5
  ciw==3.2.7
6
  aiofiles==24.1.0
7
+ httpx==0.28.1
server/inventory_env.py CHANGED
@@ -5,6 +5,7 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
5
  from dataclasses import dataclass, asdict
6
  from typing import List, Optional
7
  import numpy as np
 
8
  from fastapi import FastAPI, HTTPException
9
  from fastapi.staticfiles import StaticFiles
10
  from fastapi.responses import FileResponse
@@ -260,6 +261,30 @@ def state():
260
  )
261
 
262
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  # ── Serve React frontend (static files built by Dockerfile) ──────────────────
264
  _static_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "static")
265
  if os.path.isdir(_static_dir):
 
5
  from dataclasses import dataclass, asdict
6
  from typing import List, Optional
7
  import numpy as np
8
+ import httpx
9
  from fastapi import FastAPI, HTTPException
10
  from fastapi.staticfiles import StaticFiles
11
  from fastapi.responses import FileResponse
 
261
  )
262
 
263
 
264
+ # ── HF Inference API proxy (avoids browser CSP restrictions on HF Spaces) ────
265
+
266
+ class QwenRequest(BaseModel):
267
+ model: str
268
+ messages: list
269
+ max_tokens: int = 600
270
+ temperature: float = 0.7
271
+ hf_token: str = ""
272
+
273
+ @app.post("/api/qwen", include_in_schema=False)
274
+ async def qwen_proxy(req: QwenRequest):
275
+ token = req.hf_token or os.environ.get("HF_TOKEN", "")
276
+ headers = {"Content-Type": "application/json"}
277
+ if token:
278
+ headers["Authorization"] = f"Bearer {token}"
279
+ url = f"https://api-inference.huggingface.co/models/{req.model}/v1/chat/completions"
280
+ payload = {"model": req.model, "messages": req.messages, "max_tokens": req.max_tokens, "temperature": req.temperature}
281
+ async with httpx.AsyncClient(timeout=60.0) as client:
282
+ resp = await client.post(url, json=payload, headers=headers)
283
+ if resp.status_code != 200:
284
+ raise HTTPException(status_code=resp.status_code, detail=resp.text)
285
+ return resp.json()
286
+
287
+
288
  # ── Serve React frontend (static files built by Dockerfile) ──────────────────
289
  _static_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "static")
290
  if os.path.isdir(_static_dir):