Skip to content

Commit 1a10507

Browse files
feat: handle workflowai error
1 parent eaa6227 commit 1a10507

3 files changed

Lines changed: 60 additions & 26 deletions

File tree

workflowai/core/client/api.py

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,15 @@ async def delete(self, path: str) -> None:
8989
response = await client.delete(path)
9090
await self.raise_for_status(response)
9191

92-
def _extract_error(self, data: Union[bytes, str], exception: Optional[Exception] = None) -> WorkflowAIError:
92+
def _extract_error(
93+
self,
94+
response: httpx.Response,
95+
data: Union[bytes, str],
96+
exception: Optional[Exception] = None,
97+
) -> WorkflowAIError:
9398
try:
9499
res = ErrorResponse.model_validate_json(data)
95-
return WorkflowAIError(res.error, task_run_id=res.task_run_id)
100+
return WorkflowAIError(error=res.error, task_run_id=res.task_run_id, response=response)
96101
except JSONDecodeError:
97102
raise WorkflowAIError(
98103
error=BaseError(
@@ -101,6 +106,7 @@ def _extract_error(self, data: Union[bytes, str], exception: Optional[Exception]
101106
"raw": str(data),
102107
},
103108
),
109+
response=response,
104110
) from exception
105111

106112
async def stream(
@@ -122,28 +128,8 @@ async def stream(
122128
for payload in split_chunks(chunk):
123129
yield returns.model_validate_json(payload)
124130
except ValidationError as e:
125-
raise self._extract_error(payload, e) from None
131+
raise self._extract_error(response, payload, e) from None
126132

127133
async def raise_for_status(self, response: httpx.Response):
128-
if response.status_code < 200 and response.status_code >= 300:
129-
try:
130-
response_json = response.json()
131-
r_error = response_json.get("error",{})
132-
error_message = response_json.get("detail", {}) or r_error.get("message", "Unknown Error")
133-
details = r_error.get("details", {})
134-
error_code = r_error.get("code", "unknown_error")
135-
status_code = r_error.get("status_code", response.status_code)
136-
except JSONDecodeError:
137-
error_message = "Unknown error"
138-
details = {"raw": response.content.decode()}
139-
error_code ="unknown_error"
140-
status_code = response.status_code
141-
142-
raise WorkflowAIError(
143-
error=BaseError(
144-
message=error_message,
145-
details=details,
146-
status_code=status_code,
147-
code=error_code,
148-
),
149-
) from None
134+
if response.status_code < 200 or response.status_code >= 300:
135+
raise WorkflowAIError.from_response(response) from None

workflowai/core/domain/errors.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
from json import JSONDecodeError
12
from typing import Any, Literal, Optional, Union
23

4+
from httpx import Response
35
from pydantic import BaseModel
46

57
ProviderErrorCode = Literal[
@@ -68,9 +70,39 @@ class ErrorResponse(BaseModel):
6870

6971

7072
class WorkflowAIError(Exception):
71-
def __init__(self, error: BaseError, task_run_id: Optional[str] = None):
73+
def __init__(self, response:Response, error: BaseError, task_run_id: Optional[str] = None):
7274
self.error = error
7375
self.task_run_id = task_run_id
76+
self.response = response
7477

7578
def __str__(self):
7679
return f"WorkflowAIError : [{self.error.code}] ({self.error.status_code}): [{self.error.message}]"
80+
81+
@classmethod
82+
def from_response(cls, response: Response):
83+
try:
84+
response_json = response.json()
85+
r_error = response_json.get("error",{})
86+
error_message = response_json.get("detail", {}) or r_error.get("message", "Unknown Error")
87+
details = r_error.get("details", {})
88+
error_code = r_error.get("code", "unknown_error")
89+
status_code = response.status_code
90+
task_run_id = r_error.get("task_run_id", None)
91+
except JSONDecodeError:
92+
error_message = "Unknown error"
93+
details = {"raw": response.content.decode()}
94+
error_code ="unknown_error"
95+
status_code = response.status_code
96+
task_run_id=None
97+
98+
return cls(
99+
response=response,
100+
error=BaseError(
101+
message=error_message,
102+
details=details,
103+
status_code=status_code,
104+
code=error_code,
105+
),
106+
task_run_id=task_run_id,
107+
)
108+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
from unittest.mock import Mock
3+
4+
from workflowai.core.domain.errors import WorkflowAIError
5+
6+
7+
def test_workflow_ai_error_404():
8+
response = Mock()
9+
response.status_code = 404
10+
response.json = Mock()
11+
response.json.return_value = {"error": {"message": "None", "status_code": 404, "code": "object_not_found"}}
12+
13+
error = WorkflowAIError.from_response(response)
14+
assert error.error.message == "None"
15+
assert error.error.status_code == 404
16+
assert error.error.code == "object_not_found"

0 commit comments

Comments
 (0)