ai-pipeline-core 0.1.7__py3-none-any.whl → 0.1.10__py3-none-any.whl

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.
@@ -5,7 +5,7 @@ import jinja2
5
5
 
6
6
  from ai_pipeline_core.logging import get_pipeline_logger
7
7
 
8
- from .exceptions import PromptNotFoundError, PromptRenderError
8
+ from .exceptions import PromptError, PromptNotFoundError, PromptRenderError
9
9
 
10
10
  logger = get_pipeline_logger(__name__)
11
11
 
@@ -28,6 +28,12 @@ class PromptManager:
28
28
 
29
29
  # Start from the directory containing the calling file
30
30
  current_path = Path(current_dir).resolve()
31
+ if not current_path.exists():
32
+ raise PromptError(
33
+ f"PromptManager expected __file__ (a valid file path), "
34
+ f"but got {current_dir!r}. Did you pass __name__ instead?"
35
+ )
36
+
31
37
  if current_path.is_file():
32
38
  current_path = current_path.parent
33
39
 
@@ -1,15 +1,21 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import os
5
+ import sys
6
+ from contextlib import ExitStack
4
7
  from pathlib import Path
5
8
  from typing import Callable, Type, TypeVar, cast
6
9
 
7
10
  from lmnr import Laminar
11
+ from pydantic import ValidationError
8
12
  from pydantic_settings import CliPositionalArg, SettingsConfigDict
9
13
 
10
14
  from ai_pipeline_core.documents import DocumentList
11
15
  from ai_pipeline_core.flow.options import FlowOptions
12
16
  from ai_pipeline_core.logging import get_pipeline_logger, setup_logging
17
+ from ai_pipeline_core.prefect import disable_run_logger, prefect_test_harness
18
+ from ai_pipeline_core.settings import settings
13
19
 
14
20
  from .simple_runner import ConfigSequence, FlowSequence, run_pipelines, save_documents_to_directory
15
21
 
@@ -28,12 +34,18 @@ def _initialize_environment() -> None:
28
34
  logger.warning(f"Failed to initialize LMNR tracing: {e}")
29
35
 
30
36
 
37
+ def _running_under_pytest() -> bool: # NEW
38
+ """Return True when invoked by pytest (so fixtures will supply test contexts)."""
39
+ return "PYTEST_CURRENT_TEST" in os.environ or "pytest" in sys.modules
40
+
41
+
31
42
  def run_cli(
32
43
  *,
33
44
  flows: FlowSequence,
34
45
  flow_configs: ConfigSequence,
35
46
  options_cls: Type[TOptions],
36
47
  initializer: InitializerFunc = None,
48
+ trace_name: str | None = None,
37
49
  ) -> None:
38
50
  """
39
51
  Parse CLI+env into options, then run the pipeline.
@@ -43,13 +55,20 @@ def run_cli(
43
55
  - --start/--end: optional, 1-based step bounds
44
56
  - all other flags come from options_cls (fields & Field descriptions)
45
57
  """
58
+ # Check if no arguments provided before initialization
59
+ if len(sys.argv) == 1:
60
+ # Add --help to show usage
61
+ sys.argv.append("--help")
62
+
46
63
  _initialize_environment()
47
64
 
48
65
  class _RunnerOptions( # type: ignore[reportRedeclaration]
49
66
  options_cls,
50
67
  cli_parse_args=True,
51
68
  cli_kebab_case=True,
52
- cli_exit_on_error=False,
69
+ cli_exit_on_error=True, # Let it exit normally on error
70
+ cli_prog_name="ai-pipeline",
71
+ cli_use_class_docs_for_groups=True,
53
72
  ):
54
73
  working_directory: CliPositionalArg[Path]
55
74
  project_name: str | None = None
@@ -58,7 +77,49 @@ def run_cli(
58
77
 
59
78
  model_config = SettingsConfigDict(frozen=True, extra="ignore")
60
79
 
61
- opts = cast(FlowOptions, _RunnerOptions()) # type: ignore[reportCallIssue]
80
+ try:
81
+ opts = cast(FlowOptions, _RunnerOptions()) # type: ignore[reportCallIssue]
82
+ except ValidationError as e:
83
+ print("\nError: Invalid command line arguments\n", file=sys.stderr)
84
+ for error in e.errors():
85
+ field = " -> ".join(str(loc) for loc in error["loc"])
86
+ msg = error["msg"]
87
+ value = error.get("input", "")
88
+
89
+ # Format the field name nicely (convert from snake_case to kebab-case for CLI)
90
+ cli_field = field.replace("_", "-")
91
+
92
+ print(f" --{cli_field}: {msg}", file=sys.stderr)
93
+ if value:
94
+ print(f" Provided value: '{value}'", file=sys.stderr)
95
+
96
+ # Add helpful hints for common errors
97
+ if error["type"] == "float_parsing":
98
+ print(" Hint: Please provide a valid number (e.g., 0.7)", file=sys.stderr)
99
+ elif error["type"] == "int_parsing":
100
+ print(" Hint: Please provide a valid integer (e.g., 10)", file=sys.stderr)
101
+ elif error["type"] == "literal_error":
102
+ ctx = error.get("ctx", {})
103
+ expected = ctx.get("expected", "valid options")
104
+ print(f" Hint: Valid options are: {expected}", file=sys.stderr)
105
+ elif error["type"] in [
106
+ "less_than_equal",
107
+ "greater_than_equal",
108
+ "less_than",
109
+ "greater_than",
110
+ ]:
111
+ ctx = error.get("ctx", {})
112
+ if "le" in ctx:
113
+ print(f" Hint: Value must be ≤ {ctx['le']}", file=sys.stderr)
114
+ elif "ge" in ctx:
115
+ print(f" Hint: Value must be ≥ {ctx['ge']}", file=sys.stderr)
116
+ elif "lt" in ctx:
117
+ print(f" Hint: Value must be < {ctx['lt']}", file=sys.stderr)
118
+ elif "gt" in ctx:
119
+ print(f" Hint: Value must be > {ctx['gt']}", file=sys.stderr)
120
+
121
+ print("\nRun with --help to see all available options\n", file=sys.stderr)
122
+ sys.exit(1)
62
123
 
63
124
  wd: Path = cast(Path, getattr(opts, "working_directory"))
64
125
  wd.mkdir(parents=True, exist_ok=True)
@@ -82,14 +143,28 @@ def run_cli(
82
143
  if getattr(opts, "start", 1) == 1 and initial_documents:
83
144
  save_documents_to_directory(wd, initial_documents)
84
145
 
85
- asyncio.run(
86
- run_pipelines(
87
- project_name=project_name,
88
- output_dir=wd,
89
- flows=flows,
90
- flow_configs=flow_configs,
91
- flow_options=opts,
92
- start_step=getattr(opts, "start", 1),
93
- end_step=getattr(opts, "end", None),
146
+ # Setup context stack with optional test harness and tracing
147
+
148
+ with ExitStack() as stack:
149
+ if trace_name:
150
+ stack.enter_context(
151
+ Laminar.start_as_current_span(
152
+ name=f"{trace_name}-{project_name}", input=[opts.model_dump_json()]
153
+ )
154
+ )
155
+
156
+ if not settings.prefect_api_key and not _running_under_pytest():
157
+ stack.enter_context(prefect_test_harness())
158
+ stack.enter_context(disable_run_logger())
159
+
160
+ asyncio.run(
161
+ run_pipelines(
162
+ project_name=project_name,
163
+ output_dir=wd,
164
+ flows=flows,
165
+ flow_configs=flow_configs,
166
+ flow_options=opts,
167
+ start_step=getattr(opts, "start", 1),
168
+ end_step=getattr(opts, "end", None),
169
+ )
94
170
  )
95
- )
@@ -77,7 +77,9 @@ async def run_pipeline(
77
77
  ) -> DocumentList:
78
78
  """Execute a single pipeline flow."""
79
79
  if flow_name is None:
80
- flow_name = getattr(flow_func, "name", getattr(flow_func, "__name__", "flow"))
80
+ # For Prefect Flow objects, use their name attribute
81
+ # For regular functions, fall back to __name__
82
+ flow_name = getattr(flow_func, "name", None) or getattr(flow_func, "__name__", "flow")
81
83
 
82
84
  logger.info(f"Running Flow: {flow_name}")
83
85
 
@@ -126,7 +128,10 @@ async def run_pipelines(
126
128
  for i in range(start_index, end_index + 1):
127
129
  flow_func = flows[i]
128
130
  config = flow_configs[i]
129
- flow_name = getattr(flow_func, "name", getattr(flow_func, "__name__", f"flow_{i + 1}"))
131
+ # For Prefect Flow objects, use their name attribute; for functions, use __name__
132
+ flow_name = getattr(flow_func, "name", None) or getattr(
133
+ flow_func, "__name__", f"flow_{i + 1}"
134
+ )
130
135
 
131
136
  logger.info(f"--- [Step {i + 1}/{num_steps}] Running Flow: {flow_name} ---")
132
137
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ai-pipeline-core
3
- Version: 0.1.7
3
+ Version: 0.1.10
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
@@ -151,7 +151,7 @@ async def process_document(doc: Document):
151
151
  return response.parsed
152
152
  ```
153
153
 
154
- ### Enhanced Pipeline Decorators (New in v0.1.7)
154
+ ### Enhanced Pipeline Decorators
155
155
  ```python
156
156
  from ai_pipeline_core import pipeline_flow, pipeline_task
157
157
  from ai_pipeline_core.flow import FlowOptions
@@ -182,7 +182,7 @@ async def my_pipeline(
182
182
  return DocumentList(results)
183
183
  ```
184
184
 
185
- ### Simple Runner Utility (New in v0.1.7)
185
+ ### Simple Runner Utility
186
186
  ```python
187
187
  from ai_pipeline_core.simple_runner import run_cli, run_pipeline
188
188
  from ai_pipeline_core.flow import FlowOptions
@@ -206,7 +206,7 @@ async def main():
206
206
  )
207
207
  ```
208
208
 
209
- ### Clean Prefect Decorators (New in v0.1.7)
209
+ ### Clean Prefect Decorators
210
210
  ```python
211
211
  # Import clean Prefect decorators without tracing
212
212
  from ai_pipeline_core.prefect import flow, task
@@ -214,12 +214,12 @@ from ai_pipeline_core.prefect import flow, task
214
214
  # Or use pipeline decorators with tracing
215
215
  from ai_pipeline_core import pipeline_flow, pipeline_task
216
216
 
217
- @task # Clean Prefect task
217
+ @task # Clean Prefect task (supports both sync and async)
218
218
  def compute(x: int) -> int:
219
219
  return x * 2
220
220
 
221
- @pipeline_task(trace_level="always") # With tracing
222
- def compute_traced(x: int) -> int:
221
+ @pipeline_task(trace_level="always") # With tracing (async only)
222
+ async def compute_traced(x: int) -> int:
223
223
  return x * 2
224
224
  ```
225
225
 
@@ -246,12 +246,12 @@ docs = DocumentList([doc1, doc2])
246
246
  Managed AI interactions with built-in retry logic, cost tracking, and structured outputs.
247
247
 
248
248
  **Supported Models** (via LiteLLM proxy):
249
- - OpenAI: GPT-4, GPT-5 series
250
- - Anthropic: Claude 3 series
251
- - Google: Gemini 2.5 series
252
- - xAI: Grok models
253
- - Perplexity: Sonar models (with search capabilities)
254
- - And many more through LiteLLM compatibility
249
+ - OpenAI: gpt-5
250
+ - Anthropic: claude-4
251
+ - Google: gemini-2.5
252
+ - xAI: grok-3, grok-4
253
+ - Perplexity: sonar-pro-search
254
+ - And many more through LiteLLM compatibility. Every model from openrouter should work.
255
255
 
256
256
  ```python
257
257
  from ai_pipeline_core.llm import generate_structured, AIMessages, ModelOptions
@@ -328,13 +328,13 @@ ai_pipeline_core/
328
328
  │ └── model_options.py # Configuration models
329
329
  ├── flow/ # Prefect flow utilities
330
330
  │ ├── config.py # Type-safe flow configuration
331
- │ └── options.py # FlowOptions base class (v0.1.7)
332
- ├── simple_runner/ # Pipeline execution utilities (v0.1.7)
331
+ │ └── options.py # FlowOptions base class
332
+ ├── simple_runner/ # Pipeline execution utilities
333
333
  │ ├── cli.py # CLI interface
334
334
  │ └── simple_runner.py # Core runner logic
335
335
  ├── logging/ # Structured logging
336
- ├── pipeline.py # Enhanced decorators (v0.1.7)
337
- ├── prefect.py # Clean Prefect exports (v0.1.7)
336
+ ├── pipeline.py # Enhanced decorators
337
+ ├── prefect.py # Clean Prefect exports
338
338
  ├── tracing.py # Observability decorators
339
339
  └── settings.py # Centralized configuration
340
340
  ```
@@ -345,6 +345,7 @@ ai_pipeline_core/
345
345
  ```bash
346
346
  make test # Run all tests
347
347
  make test-cov # Run with coverage report
348
+ make test-showcase # Test the showcase.py CLI example
348
349
  pytest tests/test_documents.py::TestDocument::test_creation # Single test
349
350
  ```
350
351
 
@@ -481,6 +482,22 @@ For learning purposes, see [CLAUDE.md](CLAUDE.md) for our comprehensive coding s
481
482
 
482
483
  - [CLAUDE.md](CLAUDE.md) - Detailed coding standards and architecture guide
483
484
 
485
+ ## Examples
486
+
487
+ ### In This Repository
488
+ - [showcase.py](examples/showcase.py) - Complete example demonstrating all core features including the CLI runner
489
+ ```bash
490
+ # Run the showcase example with CLI
491
+ python examples/showcase.py ./output --temperature 0.7 --batch-size 5
492
+
493
+ # Show help
494
+ python examples/showcase.py --help
495
+ ```
496
+ - [showcase.jinja2](examples/showcase.jinja2) - Example Jinja2 prompt template
497
+
498
+ ### Real-World Application
499
+ - [AI Documentation Writer](https://github.com/bbarwik/ai-documentation-writer) - Production-ready example showing how to build sophisticated AI pipelines for automated documentation generation. See [examples/ai-documentation-writer.md](examples/ai-documentation-writer.md) for a detailed overview.
500
+
484
501
  ### dependencies_docs/ Directory
485
502
  > [!NOTE]
486
503
  > The `dependencies_docs/` directory contains guides for AI assistants (like Claude Code) on how to interact with the project's external dependencies and tooling, NOT user documentation for ai-pipeline-core itself. These files are excluded from repository listings to avoid confusion.
@@ -511,29 +528,9 @@ Built with:
511
528
  - [LiteLLM](https://litellm.ai/) - LLM proxy
512
529
  - [Pydantic](https://pydantic-docs.helpmanual.io/) - Data validation
513
530
 
514
- ## What's New in v0.1.7
515
-
516
- ### Major Additions
517
- - **Enhanced Pipeline Decorators**: New `pipeline_flow` and `pipeline_task` decorators combining Prefect functionality with automatic LMNR tracing
518
- - **FlowOptions Base Class**: Extensible configuration system for flows with type-safe inheritance
519
- - **Simple Runner Module**: CLI and programmatic utilities for easy pipeline execution
520
- - **Clean Prefect Exports**: Separate imports for Prefect decorators with and without tracing
521
- - **Expanded Exports**: All major components now accessible from top-level package import
522
-
523
- ### API Improvements
524
- - Better type inference for document flows with custom options
525
- - Support for custom FlowOptions inheritance in pipeline flows
526
- - Improved error messages for invalid flow signatures
527
- - Enhanced document utility functions (`canonical_name_key`, `sanitize_url`)
528
-
529
- ### Developer Experience
530
- - Simplified imports - most components available from `ai_pipeline_core` directly
531
- - Better separation of concerns between clean Prefect and traced pipeline decorators
532
- - More intuitive flow configuration with `FlowOptions` inheritance
533
-
534
531
  ## Stability Notice
535
532
 
536
- **Current Version**: 0.1.7
533
+ **Current Version**: 0.1.10
537
534
  **Status**: Internal Preview
538
535
  **API Stability**: Unstable - Breaking changes expected
539
536
  **Recommended Use**: Learning and reference only
@@ -1,25 +1,26 @@
1
- ai_pipeline_core/__init__.py,sha256=INcTtHr2TFY8bR0eCg7RwvIRYY6px8knCgjyIvSSKP4,1602
1
+ ai_pipeline_core/__init__.py,sha256=qKdAEzvFaIG3FgMh-90X825rcL4vqshSM2xI_WIeiq0,1711
2
2
  ai_pipeline_core/exceptions.py,sha256=_vW0Hbw2LGb5tcVvH0YzTKMff7QOPfCRr3w-w_zPyCE,968
3
- ai_pipeline_core/pipeline.py,sha256=GOrPC53j756Xhpg_CShnkAKxSdkC16XHEoPeIhkjLIA,16569
3
+ ai_pipeline_core/pipeline.py,sha256=f-pEDwrEhMLfcSEvPP2b74xb0WzFI05IQcl-NDFzH7w,16565
4
4
  ai_pipeline_core/prefect.py,sha256=VHYkkRcUmSpdwyWosOOxuExVCncIQgT6MypqGdjcYnM,241
5
- ai_pipeline_core/prompt_manager.py,sha256=XmNUdMIC0WrE9fF0LIcfozAKOGrlYwj8AfXvCndIH-o,4693
5
+ ai_pipeline_core/prompt_manager.py,sha256=e6i9xOpgmyj-0FoJQg4Y4ZgnYSOUbCARU4UYNk_rT-0,4938
6
6
  ai_pipeline_core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  ai_pipeline_core/settings.py,sha256=Zl2BPa6IHzh-B5V7cg5mtySr1dhWZQYYKxXz3BwrHlQ,615
8
8
  ai_pipeline_core/tracing.py,sha256=T-3fTyA37TejXxotkVzTNqL2a5nOfZ0bcHg9TClLvmg,9471
9
- ai_pipeline_core/documents/__init__.py,sha256=TLW8eOEmthfDHOTssXjyBlqhgrZe9ZIyxlkd0LBJ3_s,340
10
- ai_pipeline_core/documents/document.py,sha256=e3IBr0TThucBAaOHvdqv0X--iCcBrqh2jzFTyaOp7O0,12418
9
+ ai_pipeline_core/documents/__init__.py,sha256=a5fdAZxlIX5j-BEcawFnBN3jwyR1nHuvy5iw5G1MwC8,415
10
+ ai_pipeline_core/documents/document.py,sha256=3X1u78If77UqoHgl1uTiwqCc5QdQ9kbGmkL6LFoJVUM,16405
11
11
  ai_pipeline_core/documents/document_list.py,sha256=HOG_uZDazA9CJB7Lr_tNcDFzb5Ff9RUt0ELWQK_eYNM,4940
12
12
  ai_pipeline_core/documents/flow_document.py,sha256=qsV-2JYOMhkvAj7lW54ZNH_4QUclld9h06CoU59tWww,815
13
13
  ai_pipeline_core/documents/mime_type.py,sha256=sBhNRoBJQ35JoHWhJzBGpp00WFDfMdEX0JZKKkR7QH0,3371
14
14
  ai_pipeline_core/documents/task_document.py,sha256=WjHqtl1d60XFBBqewNRdz1OqBErGI0jRx15oQYCTHo8,907
15
+ ai_pipeline_core/documents/temporary_document.py,sha256=qaJYNza9EyvgBh7uch8Oic6DDk3QL0OEXGF-zuvhK_4,358
15
16
  ai_pipeline_core/documents/utils.py,sha256=BdE4taSl1vrBhxnFbOP5nDA7lXIcvY__AMRTHoaNb5M,2764
16
17
  ai_pipeline_core/flow/__init__.py,sha256=54DRfZnjXQVrimgtKEVEm5u5ErImx31cjK2PpBvHjU4,116
17
- ai_pipeline_core/flow/config.py,sha256=crbe_OvNE6qulIKv1D8yKoe8xrEsIlvICyxjhqHHBxQ,2266
18
+ ai_pipeline_core/flow/config.py,sha256=j2FP56gTNqNrxrtUnkfn-mUnIs1Cayy2ge4TzoDqj8E,3856
18
19
  ai_pipeline_core/flow/options.py,sha256=WygJEwjqOa14l23a_Hp36hJX-WgxHMq-YzSieC31Z4Y,701
19
20
  ai_pipeline_core/llm/__init__.py,sha256=3XVK-bSJdOe0s6KmmO7PDbsXHfjlcZEG1MVBmaz3EeU,442
20
21
  ai_pipeline_core/llm/ai_messages.py,sha256=DwJJe05BtYdnMZeHbBbyEbDCqrW63SRvprxptoJUCn4,4586
21
22
  ai_pipeline_core/llm/client.py,sha256=VMs1nQKCfoxbcvE2mypn5QF19u90Ua87-5IiZxWOj98,7784
22
- ai_pipeline_core/llm/model_options.py,sha256=TvAAlDFZN-TP9-J-RZBuU_dpSocskf6paaQMw1XY9UE,1321
23
+ ai_pipeline_core/llm/model_options.py,sha256=7O5y-qtYtmTXzIUS7vxKOQlRAM3TTggqHw2_dOnS_a8,1441
23
24
  ai_pipeline_core/llm/model_response.py,sha256=fIWueaemgo0cMruvToMZyKsRPzKwL6IlvUJN7DLG710,5558
24
25
  ai_pipeline_core/llm/model_types.py,sha256=rIwY6voT8-xdfsKPDC0Gkdl2iTp9Q2LuvWGSRU9Mp3k,342
25
26
  ai_pipeline_core/logging/__init__.py,sha256=DOO6ckgnMVXl29Sy7q6jhO-iW96h54pCHQDzgA2Pu6I,272
@@ -27,9 +28,9 @@ ai_pipeline_core/logging/logging.yml,sha256=YTW48keO_K5bkkb-KXGM7ZuaYKiquLsjsURe
27
28
  ai_pipeline_core/logging/logging_config.py,sha256=6MBz9nnVNvqiLDoyy9-R3sWkn6927Re5hdz4hwTptpI,4903
28
29
  ai_pipeline_core/logging/logging_mixin.py,sha256=RDaR2ju2-vKTJRzXGa0DquGPT8_UxahWjvKJnaD0IV8,7810
29
30
  ai_pipeline_core/simple_runner/__init__.py,sha256=OPbTCZvqpnYdwi1Knnkj-MpmD0Nvtg5O7UwIdAKz_AY,384
30
- ai_pipeline_core/simple_runner/cli.py,sha256=TjiSh7lr1VnTbO1jA2DuVzC2AA6V_5sA5Z8XSuldQmc,3054
31
- ai_pipeline_core/simple_runner/simple_runner.py,sha256=70BHT1iz-G368H2t4tsWAVni0jw2VkWVdnKICuVtLPw,5009
32
- ai_pipeline_core-0.1.7.dist-info/METADATA,sha256=2Pi815TCTBlKnTp2duTaUJiKaextafqZ5yfPZdD_--o,18361
33
- ai_pipeline_core-0.1.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
34
- ai_pipeline_core-0.1.7.dist-info/licenses/LICENSE,sha256=kKj8mfbdWwkyG3U6n7ztB3bAZlEwShTkAsvaY657i3I,1074
35
- ai_pipeline_core-0.1.7.dist-info/RECORD,,
31
+ ai_pipeline_core/simple_runner/cli.py,sha256=vRB10SiBFCHD9eqjqSDNZkXYrL3oIQsxNUrHi6L7hu4,6310
32
+ ai_pipeline_core/simple_runner/simple_runner.py,sha256=TVFFVWAt2pxIJdMqVgxXJ1wHn1Xf_-N1fSMVNQH-Hqo,5253
33
+ ai_pipeline_core-0.1.10.dist-info/METADATA,sha256=HGRwvKh5XDGW7ufmmZ3lIwqVnZVr3eb8hLxYFCXhEbc,18010
34
+ ai_pipeline_core-0.1.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
35
+ ai_pipeline_core-0.1.10.dist-info/licenses/LICENSE,sha256=kKj8mfbdWwkyG3U6n7ztB3bAZlEwShTkAsvaY657i3I,1074
36
+ ai_pipeline_core-0.1.10.dist-info/RECORD,,