Wimmboo commited on
Commit
8fc20f8
·
1 Parent(s): 3b86f27

clean error messages, add model validation, add gemini-3.1-flash-lite

Browse files
Files changed (3) hide show
  1. .graphify_uncached.txt +15 -0
  2. proxy/client.py +20 -2
  3. proxy/router.py +11 -2
.graphify_uncached.txt ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ C:\Users\Rafandra Aydin\Documents\Wimmboo\OpenAIProxy\config.py
2
+ C:\Users\Rafandra Aydin\Documents\Wimmboo\OpenAIProxy\main.py
3
+ C:\Users\Rafandra Aydin\Documents\Wimmboo\OpenAIProxy\.opencode\opencode.json
4
+ C:\Users\Rafandra Aydin\Documents\Wimmboo\OpenAIProxy\.opencode\package.json
5
+ C:\Users\Rafandra Aydin\Documents\Wimmboo\OpenAIProxy\.opencode\plugins\graphify.js
6
+ C:\Users\Rafandra Aydin\Documents\Wimmboo\OpenAIProxy\proxy\auth.py
7
+ C:\Users\Rafandra Aydin\Documents\Wimmboo\OpenAIProxy\proxy\client.py
8
+ C:\Users\Rafandra Aydin\Documents\Wimmboo\OpenAIProxy\proxy\providers.py
9
+ C:\Users\Rafandra Aydin\Documents\Wimmboo\OpenAIProxy\proxy\router.py
10
+ C:\Users\Rafandra Aydin\Documents\Wimmboo\OpenAIProxy\proxy\__init__.py
11
+ C:\Users\Rafandra Aydin\Documents\Wimmboo\OpenAIProxy\DESIGN.md
12
+ C:\Users\Rafandra Aydin\Documents\Wimmboo\OpenAIProxy\PRODUCT.md
13
+ C:\Users\Rafandra Aydin\Documents\Wimmboo\OpenAIProxy\README.md
14
+ C:\Users\Rafandra Aydin\Documents\Wimmboo\OpenAIProxy\requirements.txt
15
+ C:\Users\Rafandra Aydin\Documents\Wimmboo\OpenAIProxy\.opencode\AGENTS.md
proxy/client.py CHANGED
@@ -52,6 +52,22 @@ async def _poll_nvidia(client: httpx.AsyncClient, provider: Provider, request_id
52
  raise HTTPException(status_code=504, detail=f"NVIDIA async job {request_id} timed out after polling")
53
 
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  async def _stream_response(provider: Provider, real_key: str, body: dict[str, Any]) -> AsyncIterator[bytes]:
56
  """Async generator that manages its own httpx client for streaming."""
57
  target_url = f"{provider.base_url}/v1/chat/completions"
@@ -60,7 +76,8 @@ async def _stream_response(provider: Provider, real_key: str, body: dict[str, An
60
  async with client.stream("POST", target_url, headers=headers, json=body) as upstream:
61
  if upstream.status_code >= 400:
62
  content = await upstream.aread()
63
- raise HTTPException(status_code=upstream.status_code, detail=content.decode("utf-8", errors="replace"))
 
64
  async for chunk in upstream.aiter_raw():
65
  yield chunk
66
 
@@ -91,7 +108,8 @@ async def forward_request(provider: Provider, real_key: str, request: Request, b
91
  if upstream.status_code >= 400:
92
  content = await upstream.aread()
93
  logger.warning("Upstream error from %s: %d %s", provider.name, upstream.status_code, content[:500])
94
- raise HTTPException(status_code=upstream.status_code, detail=content.decode("utf-8", errors="replace"))
 
95
 
96
  return {
97
  "status_code": upstream.status_code,
 
52
  raise HTTPException(status_code=504, detail=f"NVIDIA async job {request_id} timed out after polling")
53
 
54
 
55
+ def _upstream_error_message(status_code: int, content: bytes) -> str:
56
+ """Try to extract a readable message from a provider error response."""
57
+ text = content.decode("utf-8", errors="replace").strip()
58
+ if not text:
59
+ return f"Upstream provider returned HTTP {status_code}"
60
+ try:
61
+ data = json.loads(text)
62
+ if isinstance(data, dict):
63
+ if isinstance(data.get("error"), dict):
64
+ return data["error"].get("message") or json.dumps(data["error"])
65
+ return data.get("message") or data.get("error") or text
66
+ except Exception:
67
+ pass
68
+ return text
69
+
70
+
71
  async def _stream_response(provider: Provider, real_key: str, body: dict[str, Any]) -> AsyncIterator[bytes]:
72
  """Async generator that manages its own httpx client for streaming."""
73
  target_url = f"{provider.base_url}/v1/chat/completions"
 
76
  async with client.stream("POST", target_url, headers=headers, json=body) as upstream:
77
  if upstream.status_code >= 400:
78
  content = await upstream.aread()
79
+ detail = _upstream_error_message(upstream.status_code, content)
80
+ raise HTTPException(status_code=upstream.status_code, detail=detail)
81
  async for chunk in upstream.aiter_raw():
82
  yield chunk
83
 
 
108
  if upstream.status_code >= 400:
109
  content = await upstream.aread()
110
  logger.warning("Upstream error from %s: %d %s", provider.name, upstream.status_code, content[:500])
111
+ detail = _upstream_error_message(upstream.status_code, content)
112
+ raise HTTPException(status_code=upstream.status_code, detail=detail)
113
 
114
  return {
115
  "status_code": upstream.status_code,
proxy/router.py CHANGED
@@ -24,10 +24,11 @@ MODELS = {
24
  "moonshotai/kimi-k2.5",
25
  "google/gemma-4-31b-it",
26
  "google/gemma-4-26b-it",
27
- "google/gemma-3-27b-it",
28
  ],
29
  "google": [
 
30
  "gemini-2.5-pro-001",
 
31
  "gemini-2.0-flash-001",
32
  "gemini-2.0-flash-lite-001",
33
  "gemini-1.5-pro-001",
@@ -66,7 +67,15 @@ async def chat_completions(request: Request) -> Response:
66
  if not isinstance(body, dict):
67
  raise StarletteHTTPException(status_code=400, detail="Request body must be a JSON object")
68
 
69
- logger.info("Proxying request to provider=%s model=%s", provider.name, body.get("model", "default"))
 
 
 
 
 
 
 
 
70
 
71
  result = await forward_request(provider, real_key, request, body)
72
 
 
24
  "moonshotai/kimi-k2.5",
25
  "google/gemma-4-31b-it",
26
  "google/gemma-4-26b-it",
 
27
  ],
28
  "google": [
29
+ "gemini-3.1-flash-lite",
30
  "gemini-2.5-pro-001",
31
+ "gemini-2.5-flash-001",
32
  "gemini-2.0-flash-001",
33
  "gemini-2.0-flash-lite-001",
34
  "gemini-1.5-pro-001",
 
67
  if not isinstance(body, dict):
68
  raise StarletteHTTPException(status_code=400, detail="Request body must be a JSON object")
69
 
70
+ model = body.get("model")
71
+ allowed = MODELS.get(provider.name, [])
72
+ if model and model not in allowed:
73
+ raise StarletteHTTPException(
74
+ status_code=400,
75
+ detail=f"Model '{model}' is not available for provider '{provider.name}'. Available: {', '.join(allowed)}",
76
+ )
77
+
78
+ logger.info("Proxying request to provider=%s model=%s", provider.name, model or "default")
79
 
80
  result = await forward_request(provider, real_key, request, body)
81