Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions managed-chatkit/backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ async def create_session(request: Request) -> JSONResponse:

payload = parse_json(upstream)
if not upstream.is_success:
message = None
if isinstance(payload, Mapping):
message = payload.get("error")
message = message or upstream.reason_phrase or "Failed to create session"
message = resolve_error_message(
payload,
fallback=upstream.reason_phrase or "Failed to create session",
)
return respond({"error": message}, upstream.status_code, cookie_value)

client_secret = None
Expand All @@ -93,6 +93,29 @@ async def create_session(request: Request) -> JSONResponse:
)


def resolve_error_message(payload: Mapping[str, Any], fallback: str) -> str:
"""Return a human-readable error string from an upstream payload."""
raw_error = payload.get("error")

if isinstance(raw_error, str) and raw_error.strip():
return raw_error.strip()

if isinstance(raw_error, Mapping):
message = raw_error.get("message")
if isinstance(message, str) and message.strip():
return message.strip()

code = raw_error.get("code")
if isinstance(code, str) and code.strip():
return code.strip()

top_level_message = payload.get("message")
if isinstance(top_level_message, str) and top_level_message.strip():
return top_level_message.strip()

return fallback


def respond(
payload: Mapping[str, Any], status_code: int, cookie_value: str | None = None
) -> JSONResponse:
Expand Down
54 changes: 54 additions & 0 deletions managed-chatkit/backend/tests/test_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from __future__ import annotations

from fastapi.testclient import TestClient
import httpx

from app import main


def test_resolve_error_message_prefers_nested_error_message() -> None:
payload = {"error": {"message": "Workflow id is invalid"}}

message = main.resolve_error_message(payload, fallback="Fallback")

assert message == "Workflow id is invalid"


def test_resolve_error_message_uses_top_level_error_string() -> None:
payload = {"error": "Rate limit exceeded"}

message = main.resolve_error_message(payload, fallback="Fallback")

assert message == "Rate limit exceeded"


def test_create_session_flattens_nested_upstream_error(monkeypatch) -> None:
class StubAsyncClient:
def __init__(self, *args, **kwargs) -> None:
pass

async def __aenter__(self) -> "StubAsyncClient":
return self

async def __aexit__(self, exc_type, exc, tb) -> bool:
return False

async def post(self, *args, **kwargs) -> httpx.Response:
request = httpx.Request(
"POST", "https://api.openai.com/v1/chatkit/sessions"
)
return httpx.Response(
status_code=400,
json={"error": {"message": "Workflow not found"}},
request=request,
)

monkeypatch.setenv("OPENAI_API_KEY", "test-key")
monkeypatch.setattr(main.httpx, "AsyncClient", StubAsyncClient)

client = TestClient(main.app)
response = client.post("/api/create-session", json={"workflow": {"id": "wf_123"}})

assert response.status_code == 400
assert response.json() == {"error": "Workflow not found"}
assert "chatkit_session_id=" in response.headers.get("set-cookie", "")