ai-pipeline-core 0.4.3__py3-none-any.whl → 0.4.4__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.
@@ -64,7 +64,7 @@ from .prompt_manager import PromptManager
64
64
  from .settings import Settings
65
65
  from .testing import disable_run_logger, prefect_test_harness
66
66
 
67
- __version__ = "0.4.3"
67
+ __version__ = "0.4.4"
68
68
 
69
69
  __all__ = [
70
70
  "AIMessageType",
@@ -821,7 +821,31 @@ class PipelineDeployment(Generic[TOptions, TResult]):
821
821
  else:
822
822
  current_docs = initial_documents or []
823
823
 
824
- await flow_fn(project_name, current_docs, options)
824
+ # Set up intra-flow progress context so progress_update() works inside flows
825
+ flow_minutes = tuple(getattr(f, "estimated_minutes", 1) for f in self.flows)
826
+ completed_mins = sum(flow_minutes[: max(step - 1, 0)])
827
+ progress_queue: asyncio.Queue[ProgressRun | None] = asyncio.Queue()
828
+ wh_url = context.progress_webhook_url or ""
829
+ worker = asyncio.create_task(webhook_worker(progress_queue, wh_url)) if wh_url else None
830
+
831
+ with flow_context(
832
+ webhook_url=wh_url,
833
+ project_name=project_name,
834
+ run_id=str(run_uuid) if run_uuid else "",
835
+ flow_run_id=str(run_uuid) if run_uuid else "",
836
+ flow_name=flow_name,
837
+ step=step,
838
+ total_steps=total_steps,
839
+ flow_minutes=flow_minutes,
840
+ completed_minutes=completed_mins,
841
+ queue=progress_queue,
842
+ ):
843
+ try:
844
+ await flow_fn(project_name, current_docs, options)
845
+ finally:
846
+ progress_queue.put_nowait(None)
847
+ if worker:
848
+ await worker
825
849
 
826
850
  # Build result from all documents in store
827
851
  if store:
@@ -852,12 +876,6 @@ class PipelineDeployment(Generic[TOptions, TResult]):
852
876
  """
853
877
  deployment = self
854
878
 
855
- @flow(
856
- name=self.name,
857
- flow_run_name=f"{self.name}-{{project_name}}",
858
- persist_result=True,
859
- result_serializer="json",
860
- )
861
879
  async def _deployment_flow(
862
880
  project_name: str,
863
881
  documents: list[Document],
@@ -875,7 +893,16 @@ class PipelineDeployment(Generic[TOptions, TResult]):
875
893
  store.shutdown()
876
894
  set_document_store(None)
877
895
 
878
- return _deployment_flow
896
+ # Patch annotations so Prefect generates the parameter schema from the concrete types
897
+ _deployment_flow.__annotations__["options"] = self.options_type
898
+ _deployment_flow.__annotations__["return"] = self.result_type
899
+
900
+ return flow(
901
+ name=self.name,
902
+ flow_run_name=f"{self.name}-{{project_name}}",
903
+ persist_result=True,
904
+ result_serializer="json",
905
+ )(_deployment_flow)
879
906
 
880
907
 
881
908
  __all__ = [
@@ -9,6 +9,8 @@ from dataclasses import dataclass
9
9
  from datetime import UTC, datetime
10
10
  from uuid import UUID
11
11
 
12
+ from prefect import get_client
13
+
12
14
  from ai_pipeline_core.logging import get_pipeline_logger
13
15
 
14
16
  from .contract import ProgressRun
@@ -38,9 +40,14 @@ _context: ContextVar[ProgressContext | None] = ContextVar("progress_context", de
38
40
 
39
41
 
40
42
  async def update(fraction: float, message: str = "") -> None:
41
- """Report intra-flow progress (0.0-1.0). No-op without context."""
43
+ """Report intra-flow progress (0.0-1.0). No-op without context.
44
+
45
+ Sends webhook payload (if webhook_url configured) AND updates Prefect
46
+ flow run labels (if flow_run_id available) so both push and poll consumers
47
+ see progress, and staleness detection stays current.
48
+ """
42
49
  ctx = _context.get()
43
- if ctx is None or not ctx.webhook_url:
50
+ if ctx is None:
44
51
  return
45
52
 
46
53
  fraction = max(0.0, min(1.0, fraction))
@@ -50,22 +57,43 @@ async def update(fraction: float, message: str = "") -> None:
50
57
  else:
51
58
  overall = fraction
52
59
  overall = round(max(0.0, min(1.0, overall)), 4)
53
-
54
- payload = ProgressRun(
55
- flow_run_id=UUID(ctx.flow_run_id) if ctx.flow_run_id else UUID(int=0),
56
- project_name=ctx.project_name,
57
- state="RUNNING",
58
- timestamp=datetime.now(UTC),
59
- step=ctx.step,
60
- total_steps=ctx.total_steps,
61
- flow_name=ctx.flow_name,
62
- status="progress",
63
- progress=overall,
64
- step_progress=round(fraction, 4),
65
- message=message,
66
- )
67
-
68
- ctx.queue.put_nowait(payload)
60
+ step_progress = round(fraction, 4)
61
+
62
+ # Enqueue webhook payload for async delivery
63
+ if ctx.webhook_url:
64
+ payload = ProgressRun(
65
+ flow_run_id=UUID(ctx.flow_run_id) if ctx.flow_run_id else UUID(int=0),
66
+ project_name=ctx.project_name,
67
+ state="RUNNING",
68
+ timestamp=datetime.now(UTC),
69
+ step=ctx.step,
70
+ total_steps=ctx.total_steps,
71
+ flow_name=ctx.flow_name,
72
+ status="progress",
73
+ progress=overall,
74
+ step_progress=step_progress,
75
+ message=message,
76
+ )
77
+ ctx.queue.put_nowait(payload)
78
+
79
+ # Update Prefect labels so polling consumers and staleness detection stay current
80
+ if ctx.flow_run_id:
81
+ try:
82
+ async with get_client() as client:
83
+ await client.update_flow_run_labels(
84
+ flow_run_id=UUID(ctx.flow_run_id),
85
+ labels={
86
+ "progress.step": ctx.step,
87
+ "progress.total_steps": ctx.total_steps,
88
+ "progress.flow_name": ctx.flow_name,
89
+ "progress.status": "progress",
90
+ "progress.progress": overall,
91
+ "progress.step_progress": step_progress,
92
+ "progress.message": message,
93
+ },
94
+ )
95
+ except Exception as e:
96
+ logger.warning(f"Progress label update failed: {e}")
69
97
 
70
98
 
71
99
  async def webhook_worker(
@@ -258,10 +258,10 @@ def _model_name_to_openrouter_model(model: ModelName) -> str:
258
258
  Returns:
259
259
  OpenRouter model name.
260
260
  """
261
- if model == "gemini-3-flash-search":
262
- return "google/gemini-3-flash:online"
263
261
  if model == "sonar-pro-search":
264
262
  return "perplexity/sonar-pro-search"
263
+ if model.endswith("-search"):
264
+ model = model.replace("-search", ":online")
265
265
  if model.startswith("gemini"):
266
266
  return f"google/{model}"
267
267
  elif model.startswith("gpt"):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ai-pipeline-core
3
- Version: 0.4.3
3
+ Version: 0.4.4
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
@@ -1,15 +1,15 @@
1
- ai_pipeline_core/__init__.py,sha256=YxuYpXNT3kliHsZSnbbwCZxtDAdTgT5TkKp-ONSl-q4,3270
1
+ ai_pipeline_core/__init__.py,sha256=VcU67cNpAB6EJf2V_FxUx4ZFosbpb6IzFFwpJauHJzk,3270
2
2
  ai_pipeline_core/exceptions.py,sha256=csAl7vq6xjSFBF8-UM9WZODCbhsOdOG5zH6IbA8iteM,1280
3
3
  ai_pipeline_core/prompt_manager.py,sha256=3wFkL5rrjtUT1cLInkgyhS8hKnO4MeD1cdXAEuLhgoE,9459
4
4
  ai_pipeline_core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  ai_pipeline_core/settings.py,sha256=BUz8JEFfJQrdE4rNOhQWwxnTrfekLjWkoy-3wDZQ7PY,5142
6
6
  ai_pipeline_core/testing.py,sha256=jIRrLxNvTwdamucfJoHET2qMeRhhMZV9uEJXO5vAfis,279
7
7
  ai_pipeline_core/deployment/__init__.py,sha256=wTkVK6gcEQvqBajFMTAuodRONpN25yHbR1jtcumf0WQ,900
8
- ai_pipeline_core/deployment/base.py,sha256=4fY9JXLW5PMtn7QFSt8OVRlpKSFTRQ2YnmvB_cZr8H0,36025
8
+ ai_pipeline_core/deployment/base.py,sha256=bGSnDdrw6cLM_TItAiwptnwApbw5wkoIGY9pnwDvOTQ,37485
9
9
  ai_pipeline_core/deployment/contract.py,sha256=a1qbHhneTGB27oSOUy79CUIhOIzOoq37M63XoIMzA4Y,1952
10
10
  ai_pipeline_core/deployment/deploy.py,sha256=TCF4fH5f-K1ADODZHEyf-x7PJzDbv4qtWxlpoCe_mTs,22909
11
11
  ai_pipeline_core/deployment/helpers.py,sha256=yVtGFUs4AFXkpLkiQ_ale0nXXt5btfWSb5PAbikQHNs,3312
12
- ai_pipeline_core/deployment/progress.py,sha256=5tVD9nW0N-b8Z2BxazcWCWHFpLu6pJ-eqPmRyj68X6Y,3591
12
+ ai_pipeline_core/deployment/progress.py,sha256=rO2g8VIh7EpzxzGGAroXEpveWoWZkk66jkDW22BY4j8,4827
13
13
  ai_pipeline_core/deployment/remote.py,sha256=tOexisKEeeBoHLGYZWqcjr2H-nqqYc6kvoDL72AW78w,4661
14
14
  ai_pipeline_core/docs_generator/__init__.py,sha256=JbWbk-Lw5GgWrCMRuw8zvKNTZY2jXv7XqoMiBYudvRI,1255
15
15
  ai_pipeline_core/docs_generator/__main__.py,sha256=CH4agiM2suFJ63MhTg5m0GuXdc40z-6o4ojR72JQWVA,145
@@ -38,7 +38,7 @@ ai_pipeline_core/images/__init__.py,sha256=Hc2QKR27Q2Q-h5nH-EbzfxdE3dHArBm-st5_x
38
38
  ai_pipeline_core/images/_processing.py,sha256=MrCuPGsyyEl9UlXYIPhZs0wN8CPTMZmejV2Lo2wyCZk,4362
39
39
  ai_pipeline_core/llm/__init__.py,sha256=oyRvYD5DLDl7JIRTBUaiVz6jUC5dLLujkMNFpfRp2zc,795
40
40
  ai_pipeline_core/llm/ai_messages.py,sha256=Ycmntk5d6NUFqVVsnNR_IDwJUFuHYEH7CPvmmDfYaJI,17424
41
- ai_pipeline_core/llm/client.py,sha256=CjxOiniuy5CEsA_Xz0KPLCBthbnUfC43fTpuDcqkIUM,30276
41
+ ai_pipeline_core/llm/client.py,sha256=N8eH9bY2rF28U5kGK0HQ3ibKvphcipSMLVVxtxtut8Y,30275
42
42
  ai_pipeline_core/llm/model_options.py,sha256=hg8xR0RJdJKp8QJNA4EbLnfFsnkE4HnxD85aYxc--hM,9164
43
43
  ai_pipeline_core/llm/model_response.py,sha256=Ml9wcssSssqibReJxCc9EQu488pz69Cmq_XNBs_xmak,12219
44
44
  ai_pipeline_core/llm/model_types.py,sha256=qHoUPPEkHu9B4kJ5xcIC09fk72v667ZxvzigxtgLpVo,2174
@@ -70,7 +70,7 @@ ai_pipeline_core/observability/_tracking/_writer.py,sha256=xZjwYyIxDzzzPxqkKjYAY
70
70
  ai_pipeline_core/pipeline/__init__.py,sha256=uMv1jwSyq8Ym8Hbn5097twBJLdwN1iMeqnVM4EWyrhA,282
71
71
  ai_pipeline_core/pipeline/decorators.py,sha256=CDJAeOjGLt5Ewc0Jc9zEuwLZwKyutOv89LSRS9dcXmI,37456
72
72
  ai_pipeline_core/pipeline/options.py,sha256=KF4FcT085-IwX8r649v0a9ua5xnApM0qG2wJHWbq39A,438
73
- ai_pipeline_core-0.4.3.dist-info/METADATA,sha256=j3lgsgzMN1D_4twQ3WqCzzaY1yfQThSKOxwMfZILk50,29947
74
- ai_pipeline_core-0.4.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
75
- ai_pipeline_core-0.4.3.dist-info/licenses/LICENSE,sha256=kKj8mfbdWwkyG3U6n7ztB3bAZlEwShTkAsvaY657i3I,1074
76
- ai_pipeline_core-0.4.3.dist-info/RECORD,,
73
+ ai_pipeline_core-0.4.4.dist-info/METADATA,sha256=DWL9spC2_pILAtrr6BARsTIjqWk3-VJO6WNyTqdEqyQ,29947
74
+ ai_pipeline_core-0.4.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
75
+ ai_pipeline_core-0.4.4.dist-info/licenses/LICENSE,sha256=kKj8mfbdWwkyG3U6n7ztB3bAZlEwShTkAsvaY657i3I,1074
76
+ ai_pipeline_core-0.4.4.dist-info/RECORD,,