ai-pipeline-core 0.4.7__py3-none-any.whl → 0.4.8__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.
- ai_pipeline_core/__init__.py +1 -1
- ai_pipeline_core/deployment/base.py +26 -2
- ai_pipeline_core/observability/_initialization.py +4 -3
- ai_pipeline_core/observability/tracing.py +39 -7
- {ai_pipeline_core-0.4.7.dist-info → ai_pipeline_core-0.4.8.dist-info}/METADATA +1 -1
- {ai_pipeline_core-0.4.7.dist-info → ai_pipeline_core-0.4.8.dist-info}/RECORD +8 -8
- {ai_pipeline_core-0.4.7.dist-info → ai_pipeline_core-0.4.8.dist-info}/WHEEL +0 -0
- {ai_pipeline_core-0.4.7.dist-info → ai_pipeline_core-0.4.8.dist-info}/licenses/LICENSE +0 -0
ai_pipeline_core/__init__.py
CHANGED
|
@@ -661,7 +661,10 @@ class PipelineDeployment(Generic[TOptions, TResult]):
|
|
|
661
661
|
except Exception as e:
|
|
662
662
|
logger.warning(f"Failed to initialize observability: {e}")
|
|
663
663
|
with contextlib.suppress(Exception):
|
|
664
|
-
Laminar
|
|
664
|
+
# Use canonical initializer to ensure consistent Laminar setup
|
|
665
|
+
from ai_pipeline_core.observability import tracing
|
|
666
|
+
|
|
667
|
+
tracing._initialise_laminar()
|
|
665
668
|
|
|
666
669
|
deployment = self
|
|
667
670
|
|
|
@@ -882,13 +885,34 @@ class PipelineDeployment(Generic[TOptions, TResult]):
|
|
|
882
885
|
options: FlowOptions,
|
|
883
886
|
context: DeploymentContext,
|
|
884
887
|
) -> DeploymentResult:
|
|
888
|
+
# Initialize observability for remote workers
|
|
889
|
+
try:
|
|
890
|
+
initialize_observability()
|
|
891
|
+
except Exception as e:
|
|
892
|
+
logger.warning(f"Failed to initialize observability: {e}")
|
|
893
|
+
with contextlib.suppress(Exception):
|
|
894
|
+
# Use canonical initializer to ensure consistent Laminar setup
|
|
895
|
+
from ai_pipeline_core.observability import tracing
|
|
896
|
+
|
|
897
|
+
tracing._initialise_laminar()
|
|
898
|
+
|
|
899
|
+
# Set session ID from Prefect flow run for trace grouping
|
|
900
|
+
flow_run_id = str(runtime.flow_run.get_id()) if runtime.flow_run else str(uuid4()) # pyright: ignore[reportAttributeAccessIssue, reportUnknownMemberType, reportUnknownArgumentType]
|
|
901
|
+
os.environ["LMNR_SESSION_ID"] = flow_run_id
|
|
902
|
+
|
|
885
903
|
store = create_document_store(
|
|
886
904
|
settings,
|
|
887
905
|
summary_generator=_build_summary_generator(),
|
|
888
906
|
)
|
|
889
907
|
set_document_store(store)
|
|
890
908
|
try:
|
|
891
|
-
|
|
909
|
+
# Create parent span to group all traces under a single deployment trace
|
|
910
|
+
with Laminar.start_as_current_span(
|
|
911
|
+
name=f"{deployment.name}-{project_name}",
|
|
912
|
+
input={"project_name": project_name, "options": options.model_dump()},
|
|
913
|
+
session_id=flow_run_id,
|
|
914
|
+
):
|
|
915
|
+
return await deployment.run(project_name, documents, cast(Any, options), context)
|
|
892
916
|
finally:
|
|
893
917
|
store.shutdown()
|
|
894
918
|
set_document_store(None)
|
|
@@ -8,7 +8,6 @@ import importlib
|
|
|
8
8
|
from typing import Any, Protocol
|
|
9
9
|
from uuid import UUID
|
|
10
10
|
|
|
11
|
-
from lmnr import Laminar
|
|
12
11
|
from opentelemetry import trace as otel_trace
|
|
13
12
|
from pydantic import BaseModel, ConfigDict
|
|
14
13
|
|
|
@@ -180,10 +179,12 @@ def initialize_observability(config: ObservabilityConfig | None = None) -> None:
|
|
|
180
179
|
if config is None:
|
|
181
180
|
config = _build_config_from_settings()
|
|
182
181
|
|
|
183
|
-
# 1. Laminar
|
|
182
|
+
# 1. Laminar - use canonical initializer from tracing module
|
|
184
183
|
if config.has_lmnr:
|
|
185
184
|
try:
|
|
186
|
-
|
|
185
|
+
from ai_pipeline_core.observability import tracing # noqa: PLC0415
|
|
186
|
+
|
|
187
|
+
tracing._initialise_laminar()
|
|
187
188
|
logger.info("Laminar initialized")
|
|
188
189
|
except Exception as e:
|
|
189
190
|
logger.warning(f"Laminar initialization failed: {e}")
|
|
@@ -10,6 +10,7 @@ import contextlib
|
|
|
10
10
|
import inspect
|
|
11
11
|
import json
|
|
12
12
|
import os
|
|
13
|
+
import threading
|
|
13
14
|
from collections.abc import Callable
|
|
14
15
|
from functools import wraps
|
|
15
16
|
from typing import Any, Literal, ParamSpec, TypeVar, cast, overload
|
|
@@ -220,19 +221,42 @@ class TraceInfo(BaseModel):
|
|
|
220
221
|
# ---------------------------------------------------------------------------
|
|
221
222
|
|
|
222
223
|
|
|
224
|
+
_laminar_initialized = False
|
|
225
|
+
_laminar_init_lock = threading.Lock()
|
|
226
|
+
|
|
227
|
+
|
|
223
228
|
def _initialise_laminar() -> None:
|
|
224
|
-
"""Initialize Laminar SDK with project configuration.
|
|
229
|
+
"""Initialize Laminar SDK with project configuration (lazy, once per process).
|
|
225
230
|
|
|
226
231
|
Sets up the Laminar observability client with the project API key
|
|
227
232
|
from settings. Disables automatic OpenAI instrumentation to avoid
|
|
228
233
|
conflicts with our custom tracing.
|
|
229
234
|
|
|
230
|
-
|
|
235
|
+
IMPORTANT: This is called lazily at first trace execution (not at decoration time)
|
|
236
|
+
to allow LMNR_SPAN_CONTEXT environment variable to be set before initialization.
|
|
237
|
+
Laminar reads LMNR_SPAN_CONTEXT during initialize() to establish parent context
|
|
238
|
+
for cross-process tracing.
|
|
239
|
+
|
|
240
|
+
Uses double-checked locking pattern for thread safety. The flag is set AFTER
|
|
241
|
+
successful initialization to prevent permanently disabled tracing on init failure.
|
|
231
242
|
"""
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
243
|
+
global _laminar_initialized # noqa: PLW0603
|
|
244
|
+
|
|
245
|
+
# Fast path: already initialized (no lock needed)
|
|
246
|
+
if _laminar_initialized:
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
with _laminar_init_lock:
|
|
250
|
+
# Double-check inside lock
|
|
251
|
+
if _laminar_initialized:
|
|
252
|
+
return
|
|
253
|
+
|
|
254
|
+
if settings.lmnr_project_api_key:
|
|
255
|
+
disabled = [Instruments.OPENAI] if Instruments.OPENAI else []
|
|
256
|
+
Laminar.initialize(project_api_key=settings.lmnr_project_api_key, disabled_instruments=disabled, export_timeout_seconds=15)
|
|
257
|
+
|
|
258
|
+
# Set flag AFTER successful initialization
|
|
259
|
+
_laminar_initialized = True
|
|
236
260
|
|
|
237
261
|
|
|
238
262
|
# Overload for calls like @trace(name="...", level="debug")
|
|
@@ -400,7 +424,9 @@ def trace( # noqa: UP047
|
|
|
400
424
|
return f
|
|
401
425
|
|
|
402
426
|
# --- Pre-computation (done once when the function is decorated) ---
|
|
403
|
-
_initialise_laminar()
|
|
427
|
+
# NOTE: _initialise_laminar() is NOT called here (at decoration/import time)
|
|
428
|
+
# to allow LMNR_SPAN_CONTEXT to be set before Laminar.initialize() runs.
|
|
429
|
+
# It's called lazily in the wrapper functions at first execution.
|
|
404
430
|
sig = inspect.signature(f)
|
|
405
431
|
is_coroutine = inspect.iscoroutinefunction(f)
|
|
406
432
|
observe_name = name or f.__name__
|
|
@@ -550,6 +576,9 @@ def trace( # noqa: UP047
|
|
|
550
576
|
Returns:
|
|
551
577
|
The result of the wrapped function.
|
|
552
578
|
"""
|
|
579
|
+
# Lazy initialization: called at first execution, not at decoration time.
|
|
580
|
+
# This allows LMNR_SPAN_CONTEXT to be set before Laminar.initialize().
|
|
581
|
+
_initialise_laminar()
|
|
553
582
|
observe_params = _prepare_and_get_observe_params(kwargs)
|
|
554
583
|
observed_func = bound_observe(**observe_params)(f)
|
|
555
584
|
return observed_func(*args, **kwargs)
|
|
@@ -561,6 +590,9 @@ def trace( # noqa: UP047
|
|
|
561
590
|
Returns:
|
|
562
591
|
The result of the wrapped function.
|
|
563
592
|
"""
|
|
593
|
+
# Lazy initialization: called at first execution, not at decoration time.
|
|
594
|
+
# This allows LMNR_SPAN_CONTEXT to be set before Laminar.initialize().
|
|
595
|
+
_initialise_laminar()
|
|
564
596
|
observe_params = _prepare_and_get_observe_params(kwargs)
|
|
565
597
|
observed_func = bound_observe(**observe_params)(f)
|
|
566
598
|
return await observed_func(*args, **kwargs) # pyright: ignore[reportGeneralTypeIssues, reportUnknownVariableType]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ai-pipeline-core
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.8
|
|
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,11 +1,11 @@
|
|
|
1
|
-
ai_pipeline_core/__init__.py,sha256=
|
|
1
|
+
ai_pipeline_core/__init__.py,sha256=aJwyMqt4ESan14iAS9guaHbDRk1F97PbOeHBvxShhD4,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=
|
|
8
|
+
ai_pipeline_core/deployment/base.py,sha256=JhaEYok3USOOGm9VrqayZTQD-i_VyOch7Gdv-jQfqsQ,38785
|
|
9
9
|
ai_pipeline_core/deployment/contract.py,sha256=a1qbHhneTGB27oSOUy79CUIhOIzOoq37M63XoIMzA4Y,1952
|
|
10
10
|
ai_pipeline_core/deployment/deploy.py,sha256=y5FxKMm7nGwkjzA74pTffO7A82MaDuajx6LHGTem8bI,24662
|
|
11
11
|
ai_pipeline_core/deployment/helpers.py,sha256=yVtGFUs4AFXkpLkiQ_ale0nXXt5btfWSb5PAbikQHNs,3312
|
|
@@ -48,10 +48,10 @@ ai_pipeline_core/logging/logging_config.py,sha256=JnTarGSSkpi7eqR7N13TLKeuwNCvZg
|
|
|
48
48
|
ai_pipeline_core/logging/logging_mixin.py,sha256=Jn3x0xvSwSjbAMfWELMOEfffWBB1u4IeIr7M2-55CJs,7191
|
|
49
49
|
ai_pipeline_core/observability/__init__.py,sha256=km2nIiY3aYH13s2m4nR91erQG4qKnGuvQkrKDdVW3bw,720
|
|
50
50
|
ai_pipeline_core/observability/_document_tracking.py,sha256=tXv6rbGIuxOYdq22aVbyn9Ve5EhYHPnrYCE-kj2NGXI,5428
|
|
51
|
-
ai_pipeline_core/observability/_initialization.py,sha256=
|
|
51
|
+
ai_pipeline_core/observability/_initialization.py,sha256=kg5erwWLyKi-kWl21A8jXZTod05LtP-vP0VOd2zhAzo,6790
|
|
52
52
|
ai_pipeline_core/observability/_logging_bridge.py,sha256=T3PpkgoI0YKN2vvBJEHzR5rFMFNHq9REHJs7PQX2VQk,2053
|
|
53
53
|
ai_pipeline_core/observability/_summary.py,sha256=GAZXzXVkwUcubSiGb5DgkHfO1gGwx6pYoDz6RUJmL5k,3390
|
|
54
|
-
ai_pipeline_core/observability/tracing.py,sha256=
|
|
54
|
+
ai_pipeline_core/observability/tracing.py,sha256=RZ702N392_nP4HZvxVWv6x3c6hDBZzkdNOtqPTqmFMs,28521
|
|
55
55
|
ai_pipeline_core/observability/_debug/__init__.py,sha256=V8pbgdQOx-7oFKQ_sNzAZ1-oq5c73P4kVjEClZDXe8k,942
|
|
56
56
|
ai_pipeline_core/observability/_debug/_auto_summary.py,sha256=LMvETvx_RPKF8srewCKwjigTiWs3KfDmQAYYSuVybIM,2687
|
|
57
57
|
ai_pipeline_core/observability/_debug/_config.py,sha256=CWfnK-F3knUuOQ34y_CjmU3l67J85NIZ3siftYhevc0,3367
|
|
@@ -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.
|
|
74
|
-
ai_pipeline_core-0.4.
|
|
75
|
-
ai_pipeline_core-0.4.
|
|
76
|
-
ai_pipeline_core-0.4.
|
|
73
|
+
ai_pipeline_core-0.4.8.dist-info/METADATA,sha256=Ftytzz5IBhleZK7ce8HbE4XMc8pcjdVdOe2oN-fpluA,29947
|
|
74
|
+
ai_pipeline_core-0.4.8.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
75
|
+
ai_pipeline_core-0.4.8.dist-info/licenses/LICENSE,sha256=kKj8mfbdWwkyG3U6n7ztB3bAZlEwShTkAsvaY657i3I,1074
|
|
76
|
+
ai_pipeline_core-0.4.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|