ai-pipeline-core 0.1.11__tar.gz → 0.1.12__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/PKG-INFO +1 -1
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/__init__.py +1 -1
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/documents/document.py +15 -6
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/flow/config.py +5 -5
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/pipeline.py +58 -3
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/tracing.py +21 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/pyproject.toml +2 -2
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/.gitignore +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/LICENSE +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/README.md +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/documents/__init__.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/documents/document_list.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/documents/flow_document.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/documents/mime_type.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/documents/task_document.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/documents/temporary_document.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/documents/utils.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/exceptions.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/flow/__init__.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/flow/options.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/llm/__init__.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/llm/ai_messages.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/llm/client.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/llm/model_options.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/llm/model_response.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/llm/model_types.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/logging/__init__.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/logging/logging.yml +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/logging/logging_config.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/logging/logging_mixin.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/prefect.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/prompt_manager.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/py.typed +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/settings.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/simple_runner/__init__.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/simple_runner/cli.py +0 -0
- {ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/simple_runner/simple_runner.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ai-pipeline-core
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.12
|
|
4
4
|
Summary: Core utilities for AI-powered processing pipelines using prefect
|
|
5
5
|
Project-URL: Homepage, https://github.com/bbarwik/ai-pipeline-core
|
|
6
6
|
Project-URL: Repository, https://github.com/bbarwik/ai-pipeline-core
|
|
@@ -156,7 +156,7 @@ class Document(BaseModel, ABC):
|
|
|
156
156
|
DESCRIPTION_EXTENSION: ClassVar[str] = ".description.md"
|
|
157
157
|
"""File extension for description files."""
|
|
158
158
|
|
|
159
|
-
MARKDOWN_LIST_SEPARATOR: ClassVar[str] = "\n\n
|
|
159
|
+
MARKDOWN_LIST_SEPARATOR: ClassVar[str] = "\n\n-----------------\n\n"
|
|
160
160
|
"""Separator for markdown list items."""
|
|
161
161
|
|
|
162
162
|
def __init_subclass__(cls, **kwargs: Any) -> None:
|
|
@@ -254,7 +254,8 @@ class Document(BaseModel, ABC):
|
|
|
254
254
|
- bytes: Used directly without conversion
|
|
255
255
|
- str: Encoded to UTF-8 bytes
|
|
256
256
|
- dict[str, Any]: Serialized to JSON (.json) or YAML (.yaml/.yml)
|
|
257
|
-
- list[str]: Joined with separator for .md
|
|
257
|
+
- list[str]: Joined with separator for .md (validates no items
|
|
258
|
+
contain separator), else JSON/YAML
|
|
258
259
|
- list[BaseModel]: Serialized to JSON or YAML based on extension
|
|
259
260
|
- BaseModel: Serialized to JSON or YAML based on extension
|
|
260
261
|
description: Optional description - USUALLY OMIT THIS (defaults to None).
|
|
@@ -264,7 +265,8 @@ class Document(BaseModel, ABC):
|
|
|
264
265
|
New Document instance with content converted to bytes
|
|
265
266
|
|
|
266
267
|
Raises:
|
|
267
|
-
ValueError: If content type is not supported for the file extension
|
|
268
|
+
ValueError: If content type is not supported for the file extension,
|
|
269
|
+
or if markdown list items contain the separator
|
|
268
270
|
DocumentNameError: If filename violates validation rules
|
|
269
271
|
DocumentSizeError: If content exceeds MAX_CONTENT_SIZE
|
|
270
272
|
|
|
@@ -573,7 +575,7 @@ class Document(BaseModel, ABC):
|
|
|
573
575
|
2. str → UTF-8 encoding
|
|
574
576
|
3. dict/BaseModel + .json → JSON serialization (indented)
|
|
575
577
|
4. dict/BaseModel + .yaml/.yml → YAML serialization
|
|
576
|
-
5. list[str] + .md → Join with markdown separator
|
|
578
|
+
5. list[str] + .md → Join with markdown separator (validates no items contain separator)
|
|
577
579
|
6. list[Any] + .json/.yaml → JSON/YAML array
|
|
578
580
|
7. int/float/bool + .json → JSON primitive
|
|
579
581
|
|
|
@@ -622,6 +624,13 @@ class Document(BaseModel, ABC):
|
|
|
622
624
|
if name_lower.endswith(".md"):
|
|
623
625
|
# For markdown files, join with separator
|
|
624
626
|
if all(isinstance(item, str) for item in v):
|
|
627
|
+
# Check that no string contains the separator
|
|
628
|
+
for item in v:
|
|
629
|
+
if cls.MARKDOWN_LIST_SEPARATOR in item:
|
|
630
|
+
raise ValueError(
|
|
631
|
+
f"Markdown list item cannot contain the separator "
|
|
632
|
+
f"'{cls.MARKDOWN_LIST_SEPARATOR}' as it will mess up formatting"
|
|
633
|
+
)
|
|
625
634
|
v = cls.MARKDOWN_LIST_SEPARATOR.join(v).encode("utf-8")
|
|
626
635
|
else:
|
|
627
636
|
raise ValueError(
|
|
@@ -1060,7 +1069,7 @@ class Document(BaseModel, ABC):
|
|
|
1060
1069
|
|
|
1061
1070
|
@public
|
|
1062
1071
|
|
|
1063
|
-
Splits text content using markdown separator ("\n\n
|
|
1072
|
+
Splits text content using markdown separator ("\n\n-----------------\n\n").
|
|
1064
1073
|
Designed for markdown documents with multiple sections.
|
|
1065
1074
|
|
|
1066
1075
|
Returns:
|
|
@@ -1076,7 +1085,7 @@ class Document(BaseModel, ABC):
|
|
|
1076
1085
|
>>> doc.as_markdown_list() # Returns original sections
|
|
1077
1086
|
|
|
1078
1087
|
>>> # Manual creation with separator
|
|
1079
|
-
>>> content = "Part 1\n\n
|
|
1088
|
+
>>> content = "Part 1\n\n-----------------\n\nPart 2\n\n-----------------\n\nPart 3"
|
|
1080
1089
|
>>> doc2 = MyDocument(name="parts.md", content=content.encode())
|
|
1081
1090
|
>>> doc2.as_markdown_list() # ['Part 1', 'Part 2', 'Part 3']
|
|
1082
1091
|
"""
|
|
@@ -11,7 +11,7 @@ Best Practice:
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
from abc import ABC
|
|
14
|
-
from typing import Any, ClassVar
|
|
14
|
+
from typing import Any, ClassVar, Iterable
|
|
15
15
|
|
|
16
16
|
from ai_pipeline_core.documents import DocumentList, FlowDocument
|
|
17
17
|
from ai_pipeline_core.exceptions import DocumentValidationError
|
|
@@ -267,7 +267,7 @@ class FlowConfig(ABC):
|
|
|
267
267
|
|
|
268
268
|
@classmethod
|
|
269
269
|
def create_and_validate_output(
|
|
270
|
-
cls, output: FlowDocument |
|
|
270
|
+
cls, output: FlowDocument | Iterable[FlowDocument] | DocumentList
|
|
271
271
|
) -> DocumentList:
|
|
272
272
|
"""Create and validate flow output documents.
|
|
273
273
|
|
|
@@ -280,7 +280,7 @@ class FlowConfig(ABC):
|
|
|
280
280
|
and validates it matches the expected OUTPUT_DOCUMENT_TYPE.
|
|
281
281
|
|
|
282
282
|
Args:
|
|
283
|
-
output: Single document,
|
|
283
|
+
output: Single document, iterable of documents, or DocumentList.
|
|
284
284
|
|
|
285
285
|
Returns:
|
|
286
286
|
Validated DocumentList containing the output documents.
|
|
@@ -308,7 +308,7 @@ class FlowConfig(ABC):
|
|
|
308
308
|
elif isinstance(output, DocumentList):
|
|
309
309
|
documents = output
|
|
310
310
|
else:
|
|
311
|
-
|
|
312
|
-
documents = DocumentList(output) # type: ignore[arg-type]
|
|
311
|
+
# Handle any iterable of FlowDocuments
|
|
312
|
+
documents = DocumentList(list(output)) # type: ignore[arg-type]
|
|
313
313
|
cls.validate_output_documents(documents)
|
|
314
314
|
return documents
|
|
@@ -177,6 +177,38 @@ def _callable_name(obj: Any, fallback: str) -> str:
|
|
|
177
177
|
return fallback
|
|
178
178
|
|
|
179
179
|
|
|
180
|
+
def _is_already_traced(func: Callable[..., Any]) -> bool:
|
|
181
|
+
"""Check if a function has already been wrapped by the trace decorator.
|
|
182
|
+
|
|
183
|
+
This checks both for the explicit __is_traced__ marker and walks
|
|
184
|
+
the __wrapped__ chain to detect nested trace decorations.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
func: Function to check for existing trace decoration.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
True if the function is already traced, False otherwise.
|
|
191
|
+
"""
|
|
192
|
+
# Check for explicit marker
|
|
193
|
+
if hasattr(func, "__is_traced__") and func.__is_traced__: # type: ignore[attr-defined]
|
|
194
|
+
return True
|
|
195
|
+
|
|
196
|
+
# Walk the __wrapped__ chain to detect nested traces
|
|
197
|
+
current = func
|
|
198
|
+
depth = 0
|
|
199
|
+
max_depth = 10 # Prevent infinite loops
|
|
200
|
+
|
|
201
|
+
while hasattr(current, "__wrapped__") and depth < max_depth:
|
|
202
|
+
wrapped = current.__wrapped__ # type: ignore[attr-defined]
|
|
203
|
+
# Check if the wrapped function has the trace marker
|
|
204
|
+
if hasattr(wrapped, "__is_traced__") and wrapped.__is_traced__: # type: ignore[attr-defined]
|
|
205
|
+
return True
|
|
206
|
+
current = wrapped
|
|
207
|
+
depth += 1
|
|
208
|
+
|
|
209
|
+
return False
|
|
210
|
+
|
|
211
|
+
|
|
180
212
|
# --------------------------------------------------------------------------- #
|
|
181
213
|
# @pipeline_task — async-only, traced, returns Prefect's Task object
|
|
182
214
|
# --------------------------------------------------------------------------- #
|
|
@@ -264,6 +296,9 @@ def pipeline_task(
|
|
|
264
296
|
Wraps an async function with both Prefect task functionality and
|
|
265
297
|
LMNR tracing. The function MUST be async (declared with 'async def').
|
|
266
298
|
|
|
299
|
+
IMPORTANT: Never combine with @trace decorator - this includes tracing automatically.
|
|
300
|
+
The framework will raise TypeError if you try to use both decorators together.
|
|
301
|
+
|
|
267
302
|
Best Practice - Use Defaults:
|
|
268
303
|
For 90% of use cases, use this decorator WITHOUT any parameters.
|
|
269
304
|
Only specify parameters when you have EXPLICIT requirements.
|
|
@@ -354,13 +389,21 @@ def pipeline_task(
|
|
|
354
389
|
Wrapped task with tracing and Prefect functionality.
|
|
355
390
|
|
|
356
391
|
Raises:
|
|
357
|
-
TypeError: If function is not async.
|
|
392
|
+
TypeError: If function is not async or already traced.
|
|
358
393
|
"""
|
|
359
394
|
if not inspect.iscoroutinefunction(fn):
|
|
360
395
|
raise TypeError(
|
|
361
396
|
f"@pipeline_task target '{_callable_name(fn, 'task')}' must be 'async def'"
|
|
362
397
|
)
|
|
363
398
|
|
|
399
|
+
# Check if function is already traced
|
|
400
|
+
if _is_already_traced(fn):
|
|
401
|
+
raise TypeError(
|
|
402
|
+
f"@pipeline_task target '{_callable_name(fn, 'task')}' is already decorated "
|
|
403
|
+
f"with @trace. Remove the @trace decorator - @pipeline_task includes "
|
|
404
|
+
f"tracing automatically."
|
|
405
|
+
)
|
|
406
|
+
|
|
364
407
|
fname = _callable_name(fn, "task")
|
|
365
408
|
traced_fn = trace(
|
|
366
409
|
level=trace_level,
|
|
@@ -482,6 +525,9 @@ def pipeline_flow(
|
|
|
482
525
|
Wraps an async function as a Prefect flow with tracing and type safety.
|
|
483
526
|
The decorated function MUST be async and follow the required signature.
|
|
484
527
|
|
|
528
|
+
IMPORTANT: Never combine with @trace decorator - this includes tracing automatically.
|
|
529
|
+
The framework will raise TypeError if you try to use both decorators together.
|
|
530
|
+
|
|
485
531
|
Best Practice - Use Defaults:
|
|
486
532
|
For 90% of use cases, use this decorator WITHOUT any parameters.
|
|
487
533
|
Only specify parameters when you have EXPLICIT requirements.
|
|
@@ -590,13 +636,22 @@ def pipeline_flow(
|
|
|
590
636
|
Wrapped flow with tracing and Prefect functionality.
|
|
591
637
|
|
|
592
638
|
Raises:
|
|
593
|
-
TypeError: If function is not async, doesn't have
|
|
594
|
-
parameters, or doesn't return DocumentList.
|
|
639
|
+
TypeError: If function is not async, already traced, doesn't have
|
|
640
|
+
required parameters, or doesn't return DocumentList.
|
|
595
641
|
"""
|
|
596
642
|
fname = _callable_name(fn, "flow")
|
|
597
643
|
|
|
598
644
|
if not inspect.iscoroutinefunction(fn):
|
|
599
645
|
raise TypeError(f"@pipeline_flow '{fname}' must be declared with 'async def'")
|
|
646
|
+
|
|
647
|
+
# Check if function is already traced
|
|
648
|
+
if _is_already_traced(fn):
|
|
649
|
+
raise TypeError(
|
|
650
|
+
f"@pipeline_flow target '{fname}' is already decorated "
|
|
651
|
+
f"with @trace. Remove the @trace decorator - @pipeline_flow includes "
|
|
652
|
+
f"tracing automatically."
|
|
653
|
+
)
|
|
654
|
+
|
|
600
655
|
if len(inspect.signature(fn).parameters) < 3:
|
|
601
656
|
raise TypeError(
|
|
602
657
|
f"@pipeline_flow '{fname}' must accept "
|
|
@@ -336,7 +336,25 @@ def trace(
|
|
|
336
336
|
|
|
337
337
|
Returns:
|
|
338
338
|
Wrapped function with LMNR observability.
|
|
339
|
+
|
|
340
|
+
Raises:
|
|
341
|
+
TypeError: If function is already decorated with @pipeline_task or @pipeline_flow.
|
|
339
342
|
"""
|
|
343
|
+
# Check if this is already a traced pipeline_task or pipeline_flow
|
|
344
|
+
# This happens when @trace is applied after @pipeline_task/@pipeline_flow
|
|
345
|
+
if hasattr(f, "__is_traced__") and f.__is_traced__: # type: ignore[attr-defined]
|
|
346
|
+
# Check if it's a Prefect Task or Flow object (they have specific attributes)
|
|
347
|
+
# Prefect objects have certain attributes that regular functions don't
|
|
348
|
+
is_prefect_task = hasattr(f, "fn") and hasattr(f, "submit") and hasattr(f, "map")
|
|
349
|
+
is_prefect_flow = hasattr(f, "fn") and hasattr(f, "serve")
|
|
350
|
+
if is_prefect_task or is_prefect_flow:
|
|
351
|
+
fname = getattr(f, "__name__", "function")
|
|
352
|
+
raise TypeError(
|
|
353
|
+
f"Function '{fname}' is already decorated with @pipeline_task or "
|
|
354
|
+
f"@pipeline_flow. Remove the @trace decorator - pipeline decorators "
|
|
355
|
+
f"include tracing automatically."
|
|
356
|
+
)
|
|
357
|
+
|
|
340
358
|
# Handle 'debug' level logic - only trace when LMNR_DEBUG is "true"
|
|
341
359
|
if level == "debug" and os.getenv("LMNR_DEBUG", "").lower() != "true":
|
|
342
360
|
return f
|
|
@@ -437,6 +455,9 @@ def trace(
|
|
|
437
455
|
|
|
438
456
|
wrapper = async_wrapper if is_coroutine else sync_wrapper
|
|
439
457
|
|
|
458
|
+
# Mark function as traced for detection by pipeline decorators
|
|
459
|
+
wrapper.__is_traced__ = True # type: ignore[attr-defined]
|
|
460
|
+
|
|
440
461
|
# Preserve the original function signature
|
|
441
462
|
try:
|
|
442
463
|
wrapper.__signature__ = sig # type: ignore[attr-defined]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ai-pipeline-core"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.12"
|
|
4
4
|
description = "Core utilities for AI-powered processing pipelines using prefect"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = {text = "MIT"}
|
|
@@ -175,7 +175,7 @@ reportIncompatibleVariableOverride = "error"
|
|
|
175
175
|
reportMissingParameterType = "warning"
|
|
176
176
|
|
|
177
177
|
[tool.bumpversion]
|
|
178
|
-
current_version = "0.1.
|
|
178
|
+
current_version = "0.1.12"
|
|
179
179
|
commit = true
|
|
180
180
|
tag = true
|
|
181
181
|
tag_name = "v{new_version}"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/documents/document_list.py
RENAMED
|
File without changes
|
{ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/documents/flow_document.py
RENAMED
|
File without changes
|
|
File without changes
|
{ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/documents/task_document.py
RENAMED
|
File without changes
|
{ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/documents/temporary_document.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/logging/logging_config.py
RENAMED
|
File without changes
|
{ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/logging/logging_mixin.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/simple_runner/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{ai_pipeline_core-0.1.11 → ai_pipeline_core-0.1.12}/ai_pipeline_core/simple_runner/simple_runner.py
RENAMED
|
File without changes
|