OpceanAI commited on
Commit
f428022
·
verified ·
1 Parent(s): 290369b

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +25 -4
app.py CHANGED
@@ -264,13 +264,19 @@ OpenAI-style REST API for [PASITA](https://huggingface.co/OpceanAI/PASITA), an 8
264
  from-scratch model that turns plain text into faithful Markdown. No API key is required.
265
  Works with the OpenAI SDK by pointing `base_url` at `/v1`. The Gradio API and the MCP
266
  server stay available at `?view=api`.
 
 
 
 
 
267
  """.strip()
268
 
269
 
270
- def _api_error(message: str, error_type: str = "invalid_request_error", status: int = 400, code: str | None = None) -> JSONResponse:
271
  return JSONResponse(
272
  status_code=status,
273
  content={"error": {"message": message, "type": error_type, "param": None, "code": code}},
 
274
  )
275
 
276
 
@@ -278,9 +284,9 @@ def _exception_to_error(exc: Exception) -> JSONResponse:
278
  message = f"{type(exc).__name__}: {exc}"
279
  text = message.lower()
280
  if "zerogpu" in text and any(k in text for k in ("quota", "exhausted", "insufficient", "limit")):
281
- return _api_error(str(exc), "rate_limit_error", status=429, code="gpu_quota_exceeded")
282
  if any(k in text for k in ("illegal duration", "task aborted", "queue is full", "queue full")):
283
- return _api_error(str(exc), "rate_limit_error", status=429, code="gpu_busy")
284
  return _api_error(message, "server_error", status=500)
285
 
286
 
@@ -311,6 +317,7 @@ class ChatCompletionRequest(BaseModel):
311
  model: str | None = "pasita-v1"
312
  messages: list[ChatMessage]
313
  stream: bool = False
 
314
  max_tokens: int | None = None
315
  candidates: int = 3
316
  structured_fallback: bool = True
@@ -370,6 +377,18 @@ class _LegacyConvertBodyMiddleware:
370
 
371
  server.add_middleware(_LegacyConvertBodyMiddleware)
372
 
 
 
 
 
 
 
 
 
 
 
 
 
373
 
374
  @server.exception_handler(fastapi.exceptions.RequestValidationError)
375
  async def validation_error_handler(request: fastapi.Request, exc: fastapi.exceptions.RequestValidationError):
@@ -486,6 +505,8 @@ def chat_completions(body: ChatCompletionRequest, request: fastapi.Request) -> d
486
  markdown = payload["markdown"]
487
 
488
  if body.stream:
 
 
489
  def sse():
490
  def chunk(delta, finish=None, usage=None):
491
  data = {
@@ -501,7 +522,7 @@ def chat_completions(body: ChatCompletionRequest, request: fastapi.Request) -> d
501
 
502
  yield chunk({"role": "assistant", "content": ""})
503
  yield chunk({"content": markdown})
504
- yield chunk({}, finish="stop", usage=payload["usage"])
505
  yield "data: [DONE]\n\n"
506
 
507
  return StreamingResponse(
 
264
  from-scratch model that turns plain text into faithful Markdown. No API key is required.
265
  Works with the OpenAI SDK by pointing `base_url` at `/v1`. The Gradio API and the MCP
266
  server stay available at `?view=api`.
267
+
268
+ Honored request fields: `model`, `messages`, `stream`, `stream_options.include_usage`,
269
+ `max_tokens` (upper bound), `candidates` (1-3), `structured_fallback`. Sampling
270
+ temperature is fixed at 0.3 because that is the measured recipe; other OpenAI fields
271
+ are accepted but ignored.
272
  """.strip()
273
 
274
 
275
+ def _api_error(message: str, error_type: str = "invalid_request_error", status: int = 400, code: str | None = None, headers: dict | None = None) -> JSONResponse:
276
  return JSONResponse(
277
  status_code=status,
278
  content={"error": {"message": message, "type": error_type, "param": None, "code": code}},
279
+ headers=headers,
280
  )
281
 
282
 
 
284
  message = f"{type(exc).__name__}: {exc}"
285
  text = message.lower()
286
  if "zerogpu" in text and any(k in text for k in ("quota", "exhausted", "insufficient", "limit")):
287
+ return _api_error(str(exc), "rate_limit_error", status=429, code="gpu_quota_exceeded", headers={"Retry-After": "60"})
288
  if any(k in text for k in ("illegal duration", "task aborted", "queue is full", "queue full")):
289
+ return _api_error(str(exc), "rate_limit_error", status=429, code="gpu_busy", headers={"Retry-After": "30"})
290
  return _api_error(message, "server_error", status=500)
291
 
292
 
 
317
  model: str | None = "pasita-v1"
318
  messages: list[ChatMessage]
319
  stream: bool = False
320
+ stream_options: dict | None = None
321
  max_tokens: int | None = None
322
  candidates: int = 3
323
  structured_fallback: bool = True
 
377
 
378
  server.add_middleware(_LegacyConvertBodyMiddleware)
379
 
380
+ try:
381
+ from fastapi.middleware.cors import CORSMiddleware
382
+
383
+ server.add_middleware(
384
+ CORSMiddleware,
385
+ allow_origins=["*"],
386
+ allow_methods=["GET", "POST", "OPTIONS"],
387
+ allow_headers=["*"],
388
+ )
389
+ except Exception:
390
+ pass
391
+
392
 
393
  @server.exception_handler(fastapi.exceptions.RequestValidationError)
394
  async def validation_error_handler(request: fastapi.Request, exc: fastapi.exceptions.RequestValidationError):
 
505
  markdown = payload["markdown"]
506
 
507
  if body.stream:
508
+ include_usage = bool((body.stream_options or {}).get("include_usage"))
509
+
510
  def sse():
511
  def chunk(delta, finish=None, usage=None):
512
  data = {
 
522
 
523
  yield chunk({"role": "assistant", "content": ""})
524
  yield chunk({"content": markdown})
525
+ yield chunk({}, finish="stop", usage=payload["usage"] if include_usage else None)
526
  yield "data: [DONE]\n\n"
527
 
528
  return StreamingResponse(