Skip to content

Commit 89f48ed

Browse files
OriNachumclaude
andcommitted
fix(sonar): S3776 — extract handle_post resolver/backend-loop helpers (behavior-preserving)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TJc5yvfweHP2AEccKNeaVd
2 parents a1d2f1b + 37f116f commit 89f48ed

1 file changed

Lines changed: 151 additions & 74 deletions

File tree

lobes/gateway/server.py

Lines changed: 151 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,132 @@ def _busy_body(requested_tier: str) -> bytes:
378378
).encode("utf-8")
379379

380380

381+
def _feasibility_response(table: RoutingTable, requested: str | None) -> GatewayResponse | None:
382+
"""404 ``role_infeasible`` iff ``requested``'s owning backend is declared
383+
hardware-infeasible by this deployment's per-machine profile (task t6);
384+
``None`` when there is no such gate to apply. Shared by both the
385+
tier-alias and plain-id resolution paths in :func:`handle_post` so the
386+
feasibility gate — which outranks pressure-shedding and is never bypassed
387+
by ``X-Lobes-Override`` — is checked identically in both.
388+
"""
389+
infeasible_name = infeasible_owner(table, requested)
390+
if infeasible_name is None:
391+
return None
392+
return GatewayResponse(
393+
status=404,
394+
headers=[("Content-Type", _CONTENT_TYPE_JSON)],
395+
body=_role_infeasible_body(requested, infeasible_name),
396+
)
397+
398+
399+
def _resolve_tier(
400+
table: RoutingTable,
401+
requested: str | None,
402+
pressure: dict[str, float],
403+
override: bool,
404+
) -> tuple[GatewayResponse | None, str | None, list[tuple[str, str]]]:
405+
"""The tier-alias branch of :func:`handle_post`: hardware feasibility gate,
406+
then pressure-aware busy shedding (#85), then the resolved served name.
407+
408+
Returns ``(early_response, served, tier_headers)``. When ``early_response``
409+
is not ``None`` the caller must return it immediately without dialing any
410+
backend; ``served``/``tier_headers`` are only meaningful otherwise.
411+
"""
412+
early = _feasibility_response(table, requested)
413+
if early is not None:
414+
return early, None, []
415+
decision = resolve_tier_request(requested, pressure, override, table)
416+
if decision["busy"]:
417+
busy_response = GatewayResponse(
418+
status=429,
419+
headers=[
420+
("Retry-After", str(BUSY_RETRY_AFTER_SECONDS)),
421+
("X-Lobes-Tier-Reason", "busy"),
422+
("Content-Type", _CONTENT_TYPE_JSON),
423+
],
424+
body=_busy_body(decision["requested_tier"]),
425+
)
426+
return busy_response, None, []
427+
served = decision["served_name"]
428+
tier_headers = [
429+
("X-Lobes-Tier", decision["served_tier"]),
430+
("X-Lobes-Tier-Reason", decision["reason"]),
431+
]
432+
return None, served, tier_headers
433+
434+
435+
def _resolve_plain_model(
436+
table: RoutingTable, requested: str | None
437+
) -> tuple[GatewayResponse | None, str | None]:
438+
"""The non-tier branch of :func:`handle_post`: unknown-id 404 (h23), then
439+
the hardware feasibility gate, then the resolved served name.
440+
441+
Returns ``(early_response, served)``; when ``early_response`` is not
442+
``None`` the caller must return it immediately.
443+
"""
444+
if is_unknown_model(table, requested):
445+
response = GatewayResponse(
446+
status=404,
447+
headers=[("Content-Type", _CONTENT_TYPE_JSON)],
448+
body=_model_not_found_body(requested),
449+
)
450+
return response, None
451+
early = _feasibility_response(table, requested)
452+
if early is not None:
453+
return early, None
454+
return None, resolve_model(table, requested)
455+
456+
457+
def _try_backends(
458+
ordered: list[Backend],
459+
cfg: ServerConfig,
460+
path: str,
461+
fwd_body: bytes,
462+
fwd_headers: list[tuple[str, str]],
463+
open_upstream: OpenUpstream,
464+
streaming: bool,
465+
tier_headers: list[tuple[str, str]],
466+
) -> tuple[GatewayResponse | None, list[str]]:
467+
"""Attempt each backend in ``ordered`` (in practice exactly one — no
468+
cross-backend failover, #91) and relay the first 2xx/4xx verbatim.
469+
470+
Returns ``(response, attempts)``: ``response`` is ``None`` iff every
471+
backend refused / timed out / 5xx'd, in which case the caller maps
472+
``attempts`` to the retryable 503.
473+
"""
474+
attempts: list[str] = []
475+
for backend in ordered:
476+
try:
477+
up = open_upstream(
478+
backend,
479+
path,
480+
fwd_body,
481+
fwd_headers,
482+
connect_timeout=cfg.connect_timeout,
483+
read_timeout=cfg.read_timeout,
484+
)
485+
except UpstreamError as exc:
486+
attempts.append(str(exc))
487+
continue
488+
if up.status >= 500:
489+
attempts.append(f"{backend.name}: HTTP {up.status}")
490+
up.close()
491+
continue
492+
# 2xx or 4xx → commit to the owner and relay verbatim. A 4xx is a genuine
493+
# CLIENT error: the owner is the only backend that could serve this model.
494+
return (
495+
GatewayResponse(
496+
status=up.status,
497+
headers=tier_headers + up.headers,
498+
upstream=up,
499+
streaming=streaming,
500+
attempts=attempts,
501+
),
502+
attempts,
503+
)
504+
return None, attempts
505+
506+
381507
def handle_post(
382508
table: RoutingTable,
383509
cfg: ServerConfig,
@@ -448,29 +574,9 @@ def handle_post(
448574
# infeasible role (e.g. "cortex") is rejected outright, never silently
449575
# re-routed to a different, feasible gear via the tier system's normal
450576
# upward-fallback substitution.
451-
infeasible_name = infeasible_owner(table, requested)
452-
if infeasible_name is not None:
453-
return GatewayResponse(
454-
status=404,
455-
headers=[("Content-Type", _CONTENT_TYPE_JSON)],
456-
body=_role_infeasible_body(requested, infeasible_name),
457-
)
458-
decision = resolve_tier_request(requested, pressure, override, table)
459-
if decision["busy"]:
460-
return GatewayResponse(
461-
status=429,
462-
headers=[
463-
("Retry-After", str(BUSY_RETRY_AFTER_SECONDS)),
464-
("X-Lobes-Tier-Reason", "busy"),
465-
("Content-Type", _CONTENT_TYPE_JSON),
466-
],
467-
body=_busy_body(decision["requested_tier"]),
468-
)
469-
served = decision["served_name"]
470-
tier_headers = [
471-
("X-Lobes-Tier", decision["served_tier"]),
472-
("X-Lobes-Tier-Reason", decision["reason"]),
473-
]
577+
early, served, tier_headers = _resolve_tier(table, requested, pressure, override)
578+
if early is not None:
579+
return early
474580
else:
475581
# h23 converse: an UNKNOWN non-empty id (never an alias, never a wired
476582
# backend's served name) must NOT be silently served under the default
@@ -480,32 +586,17 @@ def handle_post(
480586
# /v1/models list — so a wired-but-dead backend (dropped from /v1/models but
481587
# still in the table) is KNOWN and routes on to the retryable 503 below, not
482588
# a 404 (that distinction is what keeps issue #91 fixed). An UNSPECIFIED
483-
# (missing/blank) model is not unknown — it routes to default_model.
484-
if is_unknown_model(table, requested):
485-
return GatewayResponse(
486-
status=404,
487-
headers=[("Content-Type", _CONTENT_TYPE_JSON)],
488-
body=_model_not_found_body(requested),
489-
)
490-
# Hardware feasibility gate (task t6), mirroring the tier branch above:
491-
# AFTER the unknown-model check (a genuinely never-advertised id still
492-
# gets model_not_found, not this), but BEFORE resolving/dialing a
493-
# backend — so a known id/alias pinned to an infeasible backend (a
494-
# concrete served model id, a custom operator alias, or a tier alias
495-
# resolved via the static table when no PressureCache is wired) is
496-
# rejected outright, never silently served by a different gear.
497-
infeasible_name = infeasible_owner(table, requested)
498-
if infeasible_name is not None:
499-
return GatewayResponse(
500-
status=404,
501-
headers=[("Content-Type", _CONTENT_TYPE_JSON)],
502-
body=_role_infeasible_body(requested, infeasible_name),
503-
)
504-
served = resolve_model(table, requested)
589+
# (missing/blank) model is not unknown — it routes to default_model. The
590+
# hardware feasibility gate (task t6) mirrors the tier branch above: it
591+
# runs AFTER the unknown-model check (a genuinely never-advertised id
592+
# still gets model_not_found, not role_infeasible) but BEFORE
593+
# resolving/dialing a backend.
594+
early, served = _resolve_plain_model(table, requested)
595+
if early is not None:
596+
return early
505597
streaming = is_streaming(body)
506598
fwd_body = rewrite_model(body, served)
507599
fwd_headers = filter_headers(req_headers)
508-
attempts: list[str] = []
509600

510601
ordered = order_backends(table, served)
511602
if not ordered:
@@ -518,36 +609,22 @@ def handle_post(
518609
return GatewayResponse(
519610
status=502,
520611
headers=tier_headers + [("Content-Type", _CONTENT_TYPE_JSON)],
521-
body=_error_body("no backend owns the requested model", attempts),
522-
attempts=attempts,
612+
body=_error_body("no backend owns the requested model", []),
613+
attempts=[],
523614
)
524615

525-
for backend in ordered: # exactly one backend — no failover chain (#91)
526-
try:
527-
up = open_upstream(
528-
backend,
529-
path,
530-
fwd_body,
531-
fwd_headers,
532-
connect_timeout=cfg.connect_timeout,
533-
read_timeout=cfg.read_timeout,
534-
)
535-
except UpstreamError as exc:
536-
attempts.append(str(exc))
537-
continue
538-
if up.status >= 500:
539-
attempts.append(f"{backend.name}: HTTP {up.status}")
540-
up.close()
541-
continue
542-
# 2xx or 4xx → commit to the owner and relay verbatim. A 4xx is a genuine
543-
# CLIENT error: the owner is the only backend that could serve this model.
544-
return GatewayResponse(
545-
status=up.status,
546-
headers=tier_headers + up.headers,
547-
upstream=up,
548-
streaming=streaming,
549-
attempts=attempts,
550-
)
616+
response, attempts = _try_backends(
617+
ordered,
618+
cfg,
619+
path,
620+
fwd_body,
621+
fwd_headers,
622+
open_upstream,
623+
streaming,
624+
tier_headers,
625+
)
626+
if response is not None:
627+
return response
551628

552629
# The single owner refused / timed out / 5xx'd. With no failover (#91) it is
553630
# the ONLY backend that could serve `served`, so this is a TRANSIENT owner-down

0 commit comments

Comments
 (0)