Skip to content

Commit f81886a

Browse files
authored
Merge pull request Pipelex#112 from Pipelex/release/v0.4.5
### Changed - **Test structure overhaul**: Reorganized test directory structure for better organization: - Tests now separated into `unit/`, `integration/`, and `e2e/` directories - Created `tests/cases/` package for pure test data and constants - Created `tests/helpers/` package for test utilities - Cleaned up test imports and removed empty `__init__.py` files - **Class registry refactoring**: Updated kajson from 0.1.6 to 0.2.0, adapted to changes in [Kajson](https://github.com/Pipelex/kajson)'s class registry with new `ClassRegistryUtils` (better separation of concerns) - **Dependency updates**: - Added pytest-mock to dev dependencies for improved unit testing ### Added - **Coverage commands**: New Makefile targets for test coverage analysis: - `make cov`: Run tests with coverage report - `make cov-missing` (or `make cm`): Show coverage with missing lines - **Test configuration**: Set `xfail_strict = true` in pytest config for stricter test failure handling - **Pydantic validation errors**: Enhanced error formatting to properly handle model_type errors ### Fixed - **External links**: Removed broken Markdown target="_blank" syntax from MANIFESTO.md links - **Variable naming consistency**: Fixed redundant naming in OpenAI config (openai_openai_config → openai_config) - **Makefile optimization**: Removed parallel test execution (`-n auto`) from codex-tests, works better now ### Tests - **Unit tests added**: New comprehensive unit tests for: - `ClassRegistryUtils` - `FuncRegistry` - `ModuleInspector` - File finding utilities
2 parents 3ec87b7 + 926bef6 commit f81886a

101 files changed

Lines changed: 1234 additions & 288 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cursor/rules/pytest.mdc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ These rules apply when writing unit tests.
1010

1111
- Name test files with `test_` prefix
1212
- Use descriptive names that match the functionality being tested
13-
- Place test files in the appropriate subdirectory of `tests/`:
14-
- `tests/tools/` for tests related to sub-package `pipelex.tools`
15-
- `tests/pipelex/` for tests related to `pipelex`and its sub-packages
16-
- `tests/pipelex/cogt/` for tests related to sub-package `pipelex.cogt`
17-
- More precisely, for `pipelex` and `pipelex.cogt` the async tests are placed inside subdirectories named `cogt_asynch` and `pipelex_asynch`
13+
- Place test files in the appropriate test category directory:
14+
- `tests/unit/` - for unit tests that test individual functions/classes in isolation
15+
- `tests/integration/` - for integration tests that test component interactions
16+
- `tests/e2e/` - for end-to-end tests that test complete workflows
17+
- `tests/test_pipelines/` - for test pipeline definitions (TOML files and their structuring python files)
1818
- Fixtures are defined in conftest.py modules at different levels of the hierarchy, their scope is handled by pytest
1919
- Test data is placed inside test_data.py at different levels of the hierarchy, they must be imported with package paths from the root like `tests.pipelex.test_data`. Their content is all constants, regrouped inside classes to keep things tidy.
2020
- Always put test inside Test classes.
@@ -55,7 +55,7 @@ class TestFooBar:
5555
# Test implementation
5656
```
5757

58-
Sometimes it can be convenient to access the test's name in its body, for instance to include into a job_id. To achieve that, add the argument `request: FixtureRequest` into the signature and then you can get th test name using `cast(str, request.node.originalname), # type: ignore`.
58+
Sometimes it can be convenient to access the test's name in its body, for instance to include into a job_id. To achieve that, add the argument `request: FixtureRequest` into the signature and then you can get the test name using `cast(str, request.node.originalname), # type: ignore`.
5959

6060
# Pipe tests
6161

.cursor/rules/standards.mdc

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This document outlines the coding standards and quality control procedures that
1414
Before finalizing a task, you must run the following command to check for linting issues, type errors, and code quality problems:
1515

1616
```bash
17+
make fix-unused-imports
1718
make check
1819
```
1920

@@ -34,20 +35,16 @@ We have several make commands for running tests:
3435
```
3536
Use this for quick test runs that don't require LLM or image generation.
3637

37-
2. `make ti`: Runs all tests with these markers:
38-
```
39-
inference and not imgg
40-
```
41-
Use this for testing LLM functionality without image generation.
42-
43-
3. To run specific tests:
38+
2. To run specific tests:
4439
```bash
4540
make tp TEST=TestClassName
4641
# or
4742
make tp TEST=test_function_name
4843
```
4944
It matches names, so `TEST=test_function_name` is going to run all test with the function name that STARTS with `test_function_name`.
5045

46+
Note: never run `make ti`, `make test-inference`, `make to`, `make test-ocr`, `make tg`, or `make test-imgg`: these all use inference which is costly.
47+
5148
## Important Project Directories
5249

5350
### Pipelines Directory

CHANGELOG.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,36 @@
11
# Changelog
22

3+
## [v0.4.5] - 2025-06-23
4+
5+
### Changed
6+
- **Test structure overhaul**: Reorganized test directory structure for better organization:
7+
- Tests now separated into `unit/`, `integration/`, and `e2e/` directories
8+
- Created `tests/cases/` package for pure test data and constants
9+
- Created `tests/helpers/` package for test utilities
10+
- Cleaned up test imports and removed empty `__init__.py` files
11+
- **Class registry refactoring**: Updated kajson from 0.1.6 to 0.2.0, adapted to changes in [Kajson](https://github.com/Pipelex/kajson)'s class registry with new `ClassRegistryUtils` (better separation of concerns)
12+
- **Dependency updates**:
13+
- Added pytest-mock to dev dependencies for improved unit testing
14+
15+
### Added
16+
- **Coverage commands**: New Makefile targets for test coverage analysis:
17+
- `make cov`: Run tests with coverage report
18+
- `make cov-missing` (or `make cm`): Show coverage with missing lines
19+
- **Test configuration**: Set `xfail_strict = true` in pytest config for stricter test failure handling
20+
- **Pydantic validation errors**: Enhanced error formatting to properly handle model_type errors
21+
22+
### Fixed
23+
- **External links**: Removed broken Markdown target="_blank" syntax from MANIFESTO.md links
24+
- **Variable naming consistency**: Fixed redundant naming in OpenAI config (openai_openai_config → openai_config)
25+
- **Makefile optimization**: Removed parallel test execution (`-n auto`) from codex-tests, works better now
26+
27+
### Tests
28+
- **Unit tests added**: New comprehensive unit tests for:
29+
- `ClassRegistryUtils`
30+
- `FuncRegistry`
31+
- `ModuleInspector`
32+
- File finding utilities
33+
334
## [v0.4.4] - 2025-06-20
435

536
### Fixed

MANIFESTO.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
### This is our manifesto, this is why we build [Pipelex](https://pipelex.com){:target="_blank"}.
1+
### This is our manifesto, this is why we built [Pipelex](https://pipelex.com).
22

3-
First published on our [blog](https://www.pipelex.com/post/repeatable-ai-workflows-knowledge-pipelines){:target="_blank"} on June 3, 2025.
3+
First published on our [blog](https://www.pipelex.com/post/repeatable-ai-workflows-knowledge-pipelines) on June 3, 2025.
44

55
---
66
# The Knowledge Pipeline Manifesto
@@ -54,7 +54,7 @@ We've seen this movie before. Docker transformed deployment by giving us a simpl
5454

5555
Today's AI workflows are ready for the same breakthrough.
5656

57-
This is why we built [**Pipelex**](https://github.com/Pipelex/pipelex){:target="_blank"} as an open-source standard for repeatable AI workflows. Not just another tool, but a foundation for the community to build on.
57+
This is why we built [**Pipelex**](https://github.com/Pipelex/pipelex) as an open-source standard for repeatable AI workflows. Not just another tool, but a foundation for the community to build on.
5858

5959
The manifesto is simple: **When you discover a method that works, capture it as a knowledge pipeline. When you build a pipeline that roars, share it with the world!**
6060

Makefile

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ cleanall: cleanderived cleanenv cleanlibraries
207207
codex-tests: env
208208
$(call PRINT_TITLE,"Unit testing for Codex")
209209
@echo "• Running unit tests for Codex (excluding inference and codex_disabled)"
210-
$(VENV_PYTEST) -n auto --exitfirst --quiet -m "(dry_runnable or not inference) and not (needs_output or pipelex_api or codex_disabled)" || [ $$? = 5 ]
210+
$(VENV_PYTEST) --exitfirst -m "(dry_runnable or not inference) and not (needs_output or pipelex_api or codex_disabled)" || [ $$? = 5 ]
211211

212212
gha-tests: env
213213
$(call PRINT_TITLE,"Unit testing for github actions")
@@ -318,6 +318,27 @@ test-pipelex-api: env
318318
ta: test-pipelex-api
319319
@echo "> done: ta = test-pipelex-api"
320320

321+
cov: env
322+
$(call PRINT_TITLE,"Unit testing with coverage")
323+
@echo "• Running unit tests with coverage"
324+
@if [ -n "$(TEST)" ]; then \
325+
$(VENV_PYTEST) --cov=$(if $(PKG),$(PKG),pipelex) -k "$(TEST)" $(if $(filter 2,$(VERBOSE)),-vv,$(if $(filter 3,$(VERBOSE)),-vvv,-v)); \
326+
else \
327+
$(VENV_PYTEST) --cov=$(if $(PKG),$(PKG),pipelex) $(if $(filter 2,$(VERBOSE)),-vv,$(if $(filter 3,$(VERBOSE)),-vvv,-v)); \
328+
fi
329+
330+
cov-missing: env
331+
$(call PRINT_TITLE,"Unit testing with coverage and missing lines")
332+
@echo "• Running unit tests with coverage and missing lines"
333+
@if [ -n "$(TEST)" ]; then \
334+
$(VENV_PYTEST) --cov=$(if $(PKG),$(PKG),pipelex) --cov-report=term-missing -k "$(TEST)" $(if $(filter 2,$(VERBOSE)),-vv,$(if $(filter 3,$(VERBOSE)),-vvv,-v)); \
335+
else \
336+
$(VENV_PYTEST) --cov=$(if $(PKG),$(PKG),pipelex) --cov-report=term-missing $(if $(filter 2,$(VERBOSE)),-vv,$(if $(filter 3,$(VERBOSE)),-vvv,-v)); \
337+
fi
338+
339+
cm: cov-missing
340+
@echo "> done: cm = cov-missing"
341+
321342
############################################################################################
322343
############################ Linting ############################
323344
############################################################################################

docs/pages/build-reliable-ai-workflows-with-pipelex/pipe-operators/PipeLLM.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ Analyze the document page shown in the image and explain how it relates to the p
133133
2. **Fixed Multiple Outputs**: Use `nb_output = N` (where N is a positive integer) when you need exactly N outputs. For example, `nb_output = 3` will try to generate 3 results. The parameter `_nb_output` will be available in the prompt template, e.g. "Give me the names of $_nb_output flowers".
134134

135135
3. **Variable Multiple Outputs**: Use `multiple_output = true` when you need a variable-length list where the LLM determines how many outputs to generate based on the content and context.
136-
| `output_multiplicity` | string or integer | Defines the number of outputs. Use `"list"` for a variable-length list, or an integer (e.g., `3`) for a fixed-size list. | No |
137136

138137
## Examples
139138

pipelex/core/stuff_factory.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Any, Dict, List, Optional, Tuple
22

33
import shortuuid
4-
from pydantic import BaseModel, Field
4+
from pydantic import BaseModel, Field, ValidationError
55

66
from pipelex.config import get_config
77
from pipelex.core.concept import Concept
@@ -11,6 +11,7 @@
1111
from pipelex.core.stuff_content import StuffContent, StuffContentInitableFromStr
1212
from pipelex.exceptions import ConceptError, PipelexError
1313
from pipelex.hub import get_class_registry, get_required_concept
14+
from pipelex.tools.typing.pydantic_utils import format_pydantic_validation_error
1415

1516

1617
class StuffFactoryError(PipelexError):
@@ -148,14 +149,22 @@ def make_multiple_stuff_from_str(cls, str_stuff_and_concepts_dict: Dict[str, Tup
148149
return result
149150

150151
@classmethod
151-
def combine_stuffs(cls, concept_code: str, stuff_contents: Dict[str, StuffContent], name: Optional[str] = None) -> Stuff:
152+
def combine_stuffs(
153+
cls,
154+
concept_code: str,
155+
stuff_contents: Dict[str, StuffContent],
156+
name: Optional[str] = None,
157+
) -> Stuff:
152158
"""
153159
Combine a dictionary of stuffs into a single stuff.
154160
"""
155161
the_concept = get_required_concept(concept_code=concept_code)
156162
the_subclass_name = the_concept.structure_class_name
157163
the_subclass = get_class_registry().get_required_subclass(name=the_subclass_name, base_class=StuffContent)
158-
the_stuff_content = the_subclass.model_validate(obj=stuff_contents)
164+
try:
165+
the_stuff_content = the_subclass.model_validate(obj=stuff_contents)
166+
except ValidationError as exc:
167+
raise StuffFactoryError(f"Error combining stuffs: {format_pydantic_validation_error(exc=exc)}") from exc
159168
return cls.make_stuff(
160169
concept_str=concept_code,
161170
content=the_stuff_content,

pipelex/libraries/library_manager.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
StaticValidationError,
2525
)
2626
from pipelex.libraries.library_config import LibraryConfig
27+
from pipelex.tools.class_registry_utils import ClassRegistryUtils
2728
from pipelex.tools.misc.file_utils import find_files_in_dir
2829
from pipelex.tools.misc.json_utils import deep_update
2930
from pipelex.tools.misc.toml_utils import load_toml_from_path
@@ -74,13 +75,13 @@ def teardown(self) -> None:
7475
def load_libraries(self):
7576
log.debug("LibraryManager loading separate libraries")
7677

77-
KajsonManager.get_class_registry().register_classes_in_folder(
78+
ClassRegistryUtils.register_classes_in_folder(
7879
folder_path=LibraryConfig.loaded_pipelines_path,
7980
)
8081
library_paths = [LibraryConfig.loaded_pipelines_path]
8182
if runtime_manager.is_unit_testing:
8283
log.debug("Registering test pipeline structures for unit testing")
83-
KajsonManager.get_class_registry().register_classes_in_folder(
84+
ClassRegistryUtils.register_classes_in_folder(
8485
folder_path=LibraryConfig.test_pipelines_path,
8586
)
8687
library_paths += [LibraryConfig.test_pipelines_path]
@@ -117,6 +118,7 @@ def _load_combo_libraries(self, library_paths: List[str]):
117118
pattern="*.toml",
118119
is_recursive=True,
119120
)
121+
log.debug(f"Searching for TOML files in {libraries_path}, found '{found_file_paths}'")
120122
if not found_file_paths:
121123
log.warning(f"No TOML files found in library path: {libraries_path}")
122124
toml_file_paths.extend(found_file_paths)

pipelex/pipe_operators/pipe_llm_factory.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def make_pipe_from_blueprint(
131131
user_images=user_images or None,
132132
)
133133

134-
llm_settings = LLMSettingChoices(
134+
llm_choices = LLMSettingChoices(
135135
for_text=pipe_blueprint.llm,
136136
for_object=pipe_blueprint.llm_to_structure,
137137
for_object_direct=pipe_blueprint.llm_to_structure_direct,
@@ -152,7 +152,7 @@ def make_pipe_from_blueprint(
152152
inputs=PipeInputSpec(root=pipe_blueprint.inputs or {}),
153153
output_concept_code=pipe_blueprint.output,
154154
pipe_llm_prompt=pipe_llm_prompt,
155-
llm_choices=llm_settings,
155+
llm_choices=llm_choices,
156156
structuring_method=pipe_blueprint.structuring_method,
157157
prompt_template_to_structure=pipe_blueprint.prompt_template_to_structure,
158158
system_prompt_to_structure=pipe_blueprint.system_prompt_to_structure,

pipelex/plugins/openai/openai_factory.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ def make_openai_client(cls, llm_platform: LLMPlatform) -> openai.AsyncClient:
5050
base_url=endpoint,
5151
)
5252
case LLMPlatform.OPENAI:
53-
openai_openai_config = get_config().plugins.openai_config
54-
api_key = openai_openai_config.get_api_key(secrets_provider=get_secrets_provider())
53+
openai_config = get_config().plugins.openai_config
54+
api_key = openai_config.get_api_key(secrets_provider=get_secrets_provider())
5555
the_client = openai.AsyncOpenAI(api_key=api_key)
5656
case LLMPlatform.VERTEXAI:
5757
vertexai_config = get_config().plugins.vertexai_config

0 commit comments

Comments
 (0)