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:
parent
ae5860e431
commit
0c08bed713
2 changed files with 68 additions and 14 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
56
src/backend/base/langflow/exceptions/serialization.py
Normal file
56
src/backend/base/langflow/exceptions/serialization.py
Normal 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,
|
||||||
|
)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue