Skip to content
Merged
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
10 changes: 9 additions & 1 deletion .cursor/rules/pipelex.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -443,4 +443,12 @@ pretty_print(gantt_chart, title="Gantt Chart")
get_pipeline_tracker().output_flowchart()
```

ALWAYS RUN `pipelex validate` when you are finished writing pipelines: This checks for errors. If there are errors, iterate until it works.
# Error Handling & Validation

## Validation Error Handling

- Use `StuffContentValidationError` for handling content validation failures
- Use `format_pydantic_validation_error` to format validation errors consistently

ALWAYS RUN `pipelex validate` when you are finished writing pipelines: This checks for errors. If there are errors, iterate until it works.

5 changes: 5 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ This python >=3.10 code is in the `pipelex` directory.
- Add trailing commas to multi-line lists, dicts, function arguments, and tuples with >2 items (helps with cleaner diffs and prevents syntax errors when adding items)
- All imports inside this repo's packages must be absolute package paths from the root

## Error Handling & Validation

- Use `format_pydantic_validation_error` for consistent error formatting when catching ValidationError
- Implement `StuffContentValidationError` for content validation failures

## Linting & checking

- Run `make lint` -> it runs `ruff check . --fix` to enforce all our linting rules
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## [v0.6.3] - 2025-07-18

### Changed
- Enhanced `Stuff.content_as()` method with improved type validation logic - now attempts model validation when `isinstance` check fails

## [v0.6.2] - 2025-07-18

### Added
Expand Down
6 changes: 6 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ This python >=3.10 code is in the `pipelex` directory.
- Add trailing commas to multi-line lists, dicts, function arguments, and tuples with >2 items (helps with cleaner diffs and prevents syntax errors when adding items)
- All imports inside this repo's packages must be absolute package paths from the root

## Error Handling & Validation

- Use `StuffContentValidationError` for type validation failures
- Apply `format_pydantic_validation_error` for formatting validation errors consistently


## Test file structure

- Name test files with `test_` prefix
Expand Down
28 changes: 22 additions & 6 deletions pipelex/core/stuff.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Any, Dict, Optional, Type, Union

from pydantic import ConfigDict
from pydantic import ConfigDict, ValidationError
from typing_extensions import override

from pipelex import log
Expand All @@ -18,9 +18,9 @@
TextAndImagesContent,
TextContent,
)
from pipelex.exceptions import StuffError
from pipelex.exceptions import StuffContentValidationError, StuffError
from pipelex.tools.misc.string_utils import pascal_case_to_snake_case
from pipelex.tools.typing.pydantic_utils import CustomBaseModel
from pipelex.tools.typing.pydantic_utils import CustomBaseModel, format_pydantic_validation_error


class Stuff(CustomBaseModel):
Expand Down Expand Up @@ -106,9 +106,25 @@ def is_number(self) -> bool:

def content_as(self, content_type: Type[StuffContentType]) -> StuffContentType:
"""Get content with proper typing if it's of the expected type."""
if not isinstance(self.content, content_type):
raise TypeError(f"Content is of type '{type(self.content)}', instead of the expected '{content_type}'")
return self.content
# First try the direct isinstance check for performance
if isinstance(self.content, content_type):
return self.content

# If isinstance failed, try model validation approach
try:
# Check if class names match (quick filter before attempting validation)
if type(self.content).__name__ == content_type.__name__:
content_dict = self.content.smart_dump()
validated_content = content_type.model_validate(content_dict)
log.debug(f"Model validation passed: converted {type(self.content).__name__} to {content_type.__name__}")
return validated_content
except ValidationError as exc:
formatted_error = format_pydantic_validation_error(exc)
raise StuffContentValidationError(
original_type=type(self.content).__name__, target_type=content_type.__name__, validation_error=formatted_error
) from exc

raise TypeError(f"Content is of type '{type(self.content)}', instead of the expected '{content_type}'")

def as_list_content(self) -> ListContent: # pyright: ignore[reportMissingTypeArgument, reportUnknownParameterType]
"""Get content as ListContent with items of any type."""
Expand Down
10 changes: 10 additions & 0 deletions pipelex/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,16 @@ class StuffError(PipelexError):
pass


class StuffContentValidationError(StuffError):
"""Raised when content validation fails during type conversion."""

def __init__(self, original_type: str, target_type: str, validation_error: str):
self.original_type = original_type
self.target_type = target_type
self.validation_error = validation_error
super().__init__(f"Failed to validate content from {original_type} to {target_type}: {validation_error}")


class PipeExecutionError(PipelexError):
pass

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pipelex"
version = "0.6.2"
version = "0.6.3"
description = "Pipelex is an open-source dev tool based on a simple declarative language that lets you define replicable, structured, composable LLM pipelines."
authors = [{ name = "Evotis S.A.S.", email = "evotis@pipelex.com" }]
maintainers = [{ name = "Pipelex staff", email = "oss@pipelex.com" }]
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.