fix: make serialization errors in component update show up in the UI (#5332)

* feat: Add SerializationError exception for improved error handling during data serialization

- Introduced a new SerializationError class to handle errors when serializing data to JSON.
- The class provides detailed error messages based on the type of serialization issue encountered, including async function handling and unsupported object types.
- Enhances the robustness of the application by providing clearer feedback for serialization-related errors.

* fix: Improve error handling in custom component update to include SerializationError
This commit is contained in:
Gabriel Luiz Freitas Almeida 2024-12-18 15:36:55 -03:00 • committed by GitHub
commit 0c08bed713
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 68 additions and 14 deletions

View file

@ -7,16 +7,8 @@ from typing import TYPE_CHECKING, Annotated
from uuid import UUID from uuid import UUID
import sqlalchemy as sa import sqlalchemy as sa
from fastapi import ( from fastapi import APIRouter, BackgroundTasks, Body, Depends, HTTPException, Request, UploadFile, status
APIRouter, from fastapi.encoders import jsonable_encoder
BackgroundTasks,
Body,
Depends,
HTTPException,
Request,
UploadFile,
status,
)
from loguru import logger from loguru import logger
from sqlmodel import select from sqlmodel import select
@ -35,6 +27,7 @@ from langflow.api.v1.schemas import (
from langflow.custom.custom_component.component import Component from langflow.custom.custom_component.component import Component
from langflow.custom.utils import build_custom_component_template, get_instance_name, update_component_build_config from langflow.custom.utils import build_custom_component_template, get_instance_name, update_component_build_config
from langflow.exceptions.api import APIException, InvalidChatInputError from langflow.exceptions.api import APIException, InvalidChatInputError
from langflow.exceptions.serialization import SerializationError
from langflow.graph.graph.base import Graph from langflow.graph.graph.base import Graph
from langflow.graph.schema import RunOutputs from langflow.graph.schema import RunOutputs
from langflow.helpers.flow import get_flow_by_id_or_endpoint_name from langflow.helpers.flow import get_flow_by_id_or_endpoint_name
@ -46,9 +39,7 @@ from langflow.services.auth.utils import api_key_security, get_current_active_us
from langflow.services.cache.utils import save_uploaded_file from langflow.services.cache.utils import save_uploaded_file
from langflow.services.database.models.flow import Flow from langflow.services.database.models.flow import Flow
from langflow.services.database.models.flow.model import FlowRead from langflow.services.database.models.flow.model import FlowRead
from langflow.services.database.models.flow.utils import ( from langflow.services.database.models.flow.utils import get_all_webhook_components_in_flow
get_all_webhook_components_in_flow,
)
from langflow.services.database.models.user.model import User, UserRead from langflow.services.database.models.user.model import User, UserRead
from langflow.services.deps import get_session_service, get_settings_service, get_task_service, get_telemetry_service from langflow.services.deps import get_session_service, get_settings_service, get_task_service, get_telemetry_service
from langflow.services.settings.feature_flags import FEATURE_FLAGS from langflow.services.settings.feature_flags import FEATURE_FLAGS
@ -605,6 +596,9 @@ async def custom_component_update(
Returns: Returns:
dict: The updated custom component node. dict: The updated custom component node.
Raises:
HTTPException: If there's an error building or updating the component
SerializationError: If there's an error serializing the component to JSON
""" """
try: try:
component = Component(_code=code_request.code) component = Component(_code=code_request.code)
@ -649,7 +643,11 @@ async def custom_component_update(
except Exception as exc: except Exception as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc raise HTTPException(status_code=400, detail=str(exc)) from exc
return component_node
try:
return jsonable_encoder(component_node)
except Exception as exc:
raise SerializationError.from_exception(exc, data=component_node) from exc
@router.get("/config", response_model=ConfigResponse) @router.get("/config", response_model=ConfigResponse)

View file

@ -0,0 +1,56 @@
from typing import Any
from fastapi import HTTPException, status
class SerializationError(HTTPException):
"""Exception raised when there are errors serializing data to JSON."""
def __init__(
self,
detail: str,
original_error: Exception | None = None,
data: Any = None,
status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR,
) -> None:
super().__init__(status_code=status_code, detail=detail)
self.original_error = original_error
self.data = data
@classmethod
def from_exception(cls, exc: Exception, data: Any = None) -> "SerializationError":
"""Create a SerializationError from an existing exception."""
errors = exc.args[0] if exc.args else []
if isinstance(errors, list):
for error in errors:
if isinstance(error, TypeError):
if "'coroutine'" in str(error):
return cls(
detail=(
"The component contains async functions that need to be awaited. Please add 'await' "
"before any async function calls in your component code."
),
original_error=exc,
data=data,
)
if "vars()" in str(error):
return cls(
detail=(
"The component contains objects that cannot be converted to JSON. Please ensure all "
"properties and return values in your component are basic Python types like strings, "
"numbers, lists, or dictionaries."
),
original_error=exc,
data=data,
)
# Generic error for other cases
return cls(
detail=(
"The component returned invalid data. Please check that all values in your component (properties, "
"return values, etc.) are basic Python types that can be converted to JSON."
),
original_error=exc,
data=data,
)