patdev commited on
Commit
0a5ad86
·
verified ·
1 Parent(s): 03c3d60

Upload anthropic_proxy.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. anthropic_proxy.py +47 -6
anthropic_proxy.py CHANGED
@@ -25,6 +25,18 @@ UPSTREAM = os.environ.get("VL_UPSTREAM", "http://127.0.0.1:8080")
25
  MODEL = os.environ.get("VL_SERVED_NAME", "qwen")
26
  TIMEOUT = float(os.environ.get("VL_TIMEOUT", "1800"))
27
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  app = FastAPI(title="anthropic-bridge")
29
  _client = httpx.AsyncClient(base_url=UPSTREAM, timeout=TIMEOUT)
30
 
@@ -311,17 +323,46 @@ async def health():
311
 
312
 
313
  @app.get("/v1/models")
314
- async def models():
315
- """Relais plutot que reponse figee : les clients et les bancs lisent
316
- `max_model_len` ici, et une reponse inventee le ferait disparaitre."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  try:
318
  r = await _client.get("/v1/models", timeout=10)
319
  if r.status_code == 200:
320
- return JSONResponse(r.json())
321
  except Exception:
322
  pass
323
- return JSONResponse({"data": [{"id": MODEL, "type": "model",
324
- "display_name": MODEL}]})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
 
326
  # ------------------------------------------------------------------ passe-plat
327
  # Le pod n'expose qu'un port. Le pont le prend (c'est l'API que Claude Code
 
25
  MODEL = os.environ.get("VL_SERVED_NAME", "qwen")
26
  TIMEOUT = float(os.environ.get("VL_TIMEOUT", "1800"))
27
 
28
+ # Claude Code refuse tout identifiant de modele qui ne commence pas par
29
+ # "claude-" : il valide le nom avant d'emettre la requete. On expose donc des
30
+ # alias conformes, et on ignore le nom recu pour router vers l'unique modele
31
+ # reellement charge -- le client choisit une etiquette, pas un moteur.
32
+ ALIASES = [
33
+ "claude-kimi-k3",
34
+ "claude-kimi-k3-linear",
35
+ "claude-qwen3-coder",
36
+ "claude-sonnet-4-5", # alias de compatibilite : certains clients
37
+ "claude-3-5-haiku", # codent en dur un modele "rapide" et un "lent"
38
+ ]
39
+
40
  app = FastAPI(title="anthropic-bridge")
41
  _client = httpx.AsyncClient(base_url=UPSTREAM, timeout=TIMEOUT)
42
 
 
323
 
324
 
325
  @app.get("/v1/models")
326
+ async def models(request: Request):
327
+ """Deux protocoles sur la meme route.
328
+
329
+ Un client Anthropic attend `{"data":[{"type":"model","id":...}]}` avec des
330
+ identifiants en "claude-*". Un client OpenAI ou un banc attend la reponse
331
+ de vLLM, dont il lit `max_model_len`. On distingue sur l'en-tete
332
+ `anthropic-version`, et on renvoie a chacun ce qu'il sait lire.
333
+ """
334
+ if "anthropic-version" not in request.headers:
335
+ try:
336
+ r = await _client.get("/v1/models", timeout=10)
337
+ if r.status_code == 200:
338
+ return JSONResponse(r.json())
339
+ except Exception:
340
+ pass
341
+
342
+ ctx = None
343
  try:
344
  r = await _client.get("/v1/models", timeout=10)
345
  if r.status_code == 200:
346
+ ctx = (r.json().get("data") or [{}])[0].get("max_model_len")
347
  except Exception:
348
  pass
349
+
350
+ data = [{
351
+ "type": "model",
352
+ "id": a,
353
+ "display_name": f"{a} ({MODEL})",
354
+ "created_at": "2026-01-01T00:00:00Z",
355
+ **({"context_window": ctx} if ctx else {}),
356
+ } for a in ALIASES]
357
+ return JSONResponse({"data": data, "has_more": False,
358
+ "first_id": data[0]["id"], "last_id": data[-1]["id"]})
359
+
360
+
361
+ @app.get("/v1/models/{model_id}")
362
+ async def model_detail(model_id: str):
363
+ return JSONResponse({"type": "model", "id": model_id,
364
+ "display_name": f"{model_id} ({MODEL})",
365
+ "created_at": "2026-01-01T00:00:00Z"})
366
 
367
  # ------------------------------------------------------------------ passe-plat
368
  # Le pod n'expose qu'un port. Le pont le prend (c'est l'API que Claude Code