fix: pin the eager-503 default error_code to not_ready (Worldtree b2)
worldtree-dev finalized the #331 503 turn-launch error_code as `not_ready` (re-pinned from internal_error; retryable, matching the /readyz 503 sense) and re-froze the OpenAPI at 2.2.0 documenting the 409/503 statuses our v0.18.3 mapping already handles. Tighten our fallback default from the placeholder `turn_launch_unavailable` to the canonical `not_ready` (the default only fires when the body omits error_code — the real code is surfaced verbatim regardless). +1 test, suite 510 green. NOTE: a full conversation-api-spec.md re-vendor to the b2 era is a separate, larger pin-refresh (ratatoskr vendors the markdown spec, not the OpenAPI JSON) — deferred, to bundle with the v1 coverage-audit / when personal is on b2.
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "ratatoskr"
|
name = "ratatoskr"
|
||||||
version = "0.18.3"
|
version = "0.18.4"
|
||||||
description = "Worldtree Conversation API debug TUI — multi-pane observability dashboard"
|
description = "Worldtree Conversation API debug TUI — multi-pane observability dashboard"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|||||||
@@ -241,7 +241,11 @@ class TurnLaunchUnavailable(SseConnectFailed):
|
|||||||
self.message = message
|
self.message = message
|
||||||
|
|
||||||
|
|
||||||
_EAGER_TURN_FAILURE_CODE = {409: "agent_not_available", 503: "turn_launch_unavailable"}
|
# Canonical error_codes (Worldtree #331 / v1.0.0b2): 409 -> agent_not_available,
|
||||||
|
# 503 -> not_ready (retryable; re-pinned from internal_error). Used only as a
|
||||||
|
# fallback default when the body omits error_code — the real code is surfaced
|
||||||
|
# verbatim from the {detail:{error_code,message}} envelope.
|
||||||
|
_EAGER_TURN_FAILURE_CODE = {409: "agent_not_available", 503: "not_ready"}
|
||||||
|
|
||||||
|
|
||||||
def _eager_failure_fields(body: bytes, status: int) -> tuple[str, str]:
|
def _eager_failure_fields(body: bytes, status: int) -> tuple[str, str]:
|
||||||
|
|||||||
@@ -479,6 +479,19 @@ class TestStreamTurn:
|
|||||||
_ = [e async for e in stream_turn(client, "s1", "hi")]
|
_ = [e async for e in stream_turn(client, "s1", "hi")]
|
||||||
assert exc.value.error_code == "agent_not_available"
|
assert exc.value.error_code == "agent_not_available"
|
||||||
|
|
||||||
|
@respx.mock
|
||||||
|
async def test_eager_503_non_json_body_defaults_not_ready(self) -> None:
|
||||||
|
"""b2: eager 503 with a non-JSON body -> TurnLaunchUnavailable with the
|
||||||
|
canonical default error_code `not_ready`."""
|
||||||
|
respx.post("https://w.example/sessions/s1/messages").mock(
|
||||||
|
return_value=httpx.Response(503, content=b"<html>nope</html>")
|
||||||
|
)
|
||||||
|
async with httpx.AsyncClient(base_url="https://w.example") as client:
|
||||||
|
with pytest.raises(TurnLaunchUnavailable) as exc:
|
||||||
|
_ = [e async for e in stream_turn(client, "s1", "hi")]
|
||||||
|
assert exc.value.error_code == "not_ready"
|
||||||
|
assert exc.value.retryable is True
|
||||||
|
|
||||||
@respx.mock
|
@respx.mock
|
||||||
async def test_no_text_aggregation(self) -> None:
|
async def test_no_text_aggregation(self) -> None:
|
||||||
"""no_text_aggregation: consumer yields each text event separately; no concat."""
|
"""no_text_aggregation: consumer yields each text event separately; no concat."""
|
||||||
|
|||||||
Reference in New Issue
Block a user