omnia-tracing 0.1.0__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.
@@ -0,0 +1 @@
1
+ PY_PY=pypi-AgEIcHlwaS5vcmcCJDZlMDMxNDM5LTU2NjQtNDA0OS1iMDBmLTg3YTEyN2I1MGY5ZQACKlszLCIwYzc3YjA4Zi0yMjM1LTRiYWEtODEzMC1kOGNjYTc2MDY0MDAiXQAABiC-pMRm8OFpi3aPTC8WcA6RYye-O9lYrXl4X16aViCdbw
@@ -0,0 +1,6 @@
1
+ node_modules/
2
+ dist/
3
+ __pycache__/
4
+ *.egg-info/
5
+ .venv/
6
+ .pytest_cache/
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.5
2
+ Name: omnia-tracing
3
+ Version: 0.1.0
4
+ Summary: Omnia tracing — standard OpenTelemetry, curated. One install, one line; eject anytime, your spans don't change.
5
+ Project-URL: Repository, https://github.com/omnia-v/omnia-tracing
6
+ License-Expression: Apache-2.0
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.27
9
+ Requires-Dist: opentelemetry-instrumentation-anthropic>=0.30
10
+ Requires-Dist: opentelemetry-instrumentation-google-generativeai>=0.30
11
+ Requires-Dist: opentelemetry-instrumentation-langchain>=0.30
12
+ Requires-Dist: opentelemetry-instrumentation-openai>=0.30
13
+ Requires-Dist: opentelemetry-sdk>=1.27
14
+ Description-Content-Type: text/markdown
15
+
16
+ # omnia-tracing
17
+
18
+ Standard OpenTelemetry, curated. One install, one line; eject anytime — your spans don't change.
19
+
20
+ ```python
21
+ from omnia_tracing import setup
22
+
23
+ tracing = setup() # reads OMNIA_API_KEY and OMNIA_TAG
24
+ ```
25
+
26
+ See the [repository README](https://github.com/omnia-v/omnia-tracing) for the full story, and `docs/eject.md` for the identical setup in vanilla OpenTelemetry.
@@ -0,0 +1,11 @@
1
+ # omnia-tracing
2
+
3
+ Standard OpenTelemetry, curated. One install, one line; eject anytime — your spans don't change.
4
+
5
+ ```python
6
+ from omnia_tracing import setup
7
+
8
+ tracing = setup() # reads OMNIA_API_KEY and OMNIA_TAG
9
+ ```
10
+
11
+ See the [repository README](https://github.com/omnia-v/omnia-tracing) for the full story, and `docs/eject.md` for the identical setup in vanilla OpenTelemetry.
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "omnia-tracing"
7
+ version = "0.1.0"
8
+ description = "Omnia tracing — standard OpenTelemetry, curated. One install, one line; eject anytime, your spans don't change."
9
+ license = "Apache-2.0"
10
+ readme = "README.md"
11
+ requires-python = ">=3.10"
12
+ dependencies = [
13
+ "opentelemetry-sdk>=1.27",
14
+ "opentelemetry-exporter-otlp-proto-http>=1.27",
15
+ "opentelemetry-instrumentation-openai>=0.30",
16
+ "opentelemetry-instrumentation-anthropic>=0.30",
17
+ "opentelemetry-instrumentation-google-generativeai>=0.30",
18
+ "opentelemetry-instrumentation-langchain>=0.30",
19
+ ]
20
+
21
+ [project.urls]
22
+ Repository = "https://github.com/omnia-v/omnia-tracing"
23
+
24
+ [dependency-groups]
25
+ dev = ["pytest>=8"]
26
+
27
+ [tool.hatch.build.targets.wheel]
28
+ packages = ["src/omnia_tracing"]
@@ -0,0 +1,125 @@
1
+ """Omnia tracing — standard OpenTelemetry, curated.
2
+
3
+ One install, one line; eject anytime, your spans don't change. This package
4
+ contains NO instrumentation code of its own: it pins and configures standard,
5
+ ecosystem-maintained OpenTelemetry pieces. The identical setup without this
6
+ package is documented in docs/eject.md.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import os
12
+ from dataclasses import dataclass, field
13
+
14
+ DEFAULT_ENDPOINT = "https://gateway.omnia-voice.com/v1/traces"
15
+ TAG_ATTRIBUTE = "omnia.tag"
16
+
17
+ __all__ = ["setup", "resolve_config", "ResolvedConfig", "Tracing", "DEFAULT_ENDPOINT", "TAG_ATTRIBUTE"]
18
+
19
+
20
+ @dataclass
21
+ class ResolvedConfig:
22
+ endpoint: str
23
+ headers: dict[str, str]
24
+ service_name: str | None
25
+ resource_attributes: dict[str, str] = field(default_factory=dict)
26
+
27
+
28
+ def resolve_config(
29
+ api_key: str | None = None,
30
+ tag: str | None = None,
31
+ service_name: str | None = None,
32
+ endpoint: str | None = None,
33
+ env: dict[str, str] | None = None,
34
+ ) -> ResolvedConfig:
35
+ """Pure configuration assembly — unit-testable without starting a pipeline."""
36
+ e = os.environ if env is None else env
37
+ key = api_key or e.get("OMNIA_API_KEY")
38
+ if not key:
39
+ raise ValueError(
40
+ "omnia-tracing: no API key. Pass setup(api_key=...) or set OMNIA_API_KEY. "
41
+ "Refusing to start a tracer that exports nowhere."
42
+ )
43
+ resource_attributes: dict[str, str] = {}
44
+ resolved_tag = tag or e.get("OMNIA_TAG")
45
+ if resolved_tag:
46
+ resource_attributes[TAG_ATTRIBUTE] = resolved_tag
47
+ return ResolvedConfig(
48
+ endpoint=endpoint or e.get("OMNIA_OTLP_ENDPOINT") or DEFAULT_ENDPOINT,
49
+ headers={"Authorization": f"Bearer {key}"},
50
+ service_name=service_name or e.get("OTEL_SERVICE_NAME"),
51
+ resource_attributes=resource_attributes,
52
+ )
53
+
54
+
55
+ class Tracing:
56
+ """Handle returned by setup(): shutdown() flushes and stops.
57
+
58
+ `instrumented` names the libraries actually being traced — the ones both
59
+ installed in this environment and successfully instrumented."""
60
+
61
+ def __init__(self, provider, instrumented: list[str]) -> None:
62
+ self._provider = provider
63
+ self.instrumented = instrumented
64
+
65
+ def shutdown(self) -> None:
66
+ self._provider.shutdown()
67
+
68
+
69
+ def setup(
70
+ api_key: str | None = None,
71
+ tag: str | None = None,
72
+ service_name: str | None = None,
73
+ endpoint: str | None = None,
74
+ ) -> Tracing:
75
+ """Start standard OpenTelemetry tracing, exporting to Omnia.
76
+
77
+ Call ONCE, at startup, before constructing LLM clients. Instruments
78
+ OpenAI, Anthropic, Gemini and LangChain via the ecosystem's standard
79
+ instrumentation packages.
80
+ """
81
+ config = resolve_config(api_key, tag, service_name, endpoint)
82
+
83
+ # Imports live here, not module top: `import omnia_tracing` must stay
84
+ # side-effect free so resolve_config is usable (and testable) alone.
85
+ from opentelemetry.sdk.resources import Resource
86
+ from opentelemetry.sdk.trace import TracerProvider
87
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
88
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
89
+
90
+ attrs: dict[str, str] = dict(config.resource_attributes)
91
+ if config.service_name:
92
+ attrs["service.name"] = config.service_name
93
+ provider = TracerProvider(resource=Resource.create(attrs))
94
+ provider.add_span_processor(
95
+ BatchSpanProcessor(
96
+ OTLPSpanExporter(endpoint=config.endpoint, headers=config.headers)
97
+ )
98
+ )
99
+
100
+ # Each instrumentation package imports its TARGET library at import time,
101
+ # so instrument only what this environment actually has — an
102
+ # Anthropic-only app must not be forced to install `openai`.
103
+ candidates = (
104
+ ("openai", "opentelemetry.instrumentation.openai", "OpenAIInstrumentor"),
105
+ ("anthropic", "opentelemetry.instrumentation.anthropic", "AnthropicInstrumentor"),
106
+ (
107
+ "google-generativeai",
108
+ "opentelemetry.instrumentation.google_generativeai",
109
+ "GoogleGenerativeAiInstrumentor",
110
+ ),
111
+ ("langchain", "opentelemetry.instrumentation.langchain", "LangchainInstrumentor"),
112
+ )
113
+ instrumented: list[str] = []
114
+ import importlib
115
+
116
+ for name, module_path, class_name in candidates:
117
+ try:
118
+ instrumentor = getattr(importlib.import_module(module_path), class_name)()
119
+ except ImportError:
120
+ continue # target library not installed — nothing to trace
121
+ if not instrumentor.is_instrumented_by_opentelemetry:
122
+ instrumentor.instrument(tracer_provider=provider)
123
+ instrumented.append(name)
124
+
125
+ return Tracing(provider, instrumented)
@@ -0,0 +1,44 @@
1
+ import pytest
2
+
3
+ from omnia_tracing import DEFAULT_ENDPOINT, TAG_ATTRIBUTE, resolve_config
4
+
5
+
6
+ def test_refuses_without_api_key():
7
+ with pytest.raises(ValueError, match="OMNIA_API_KEY"):
8
+ resolve_config(env={})
9
+
10
+
11
+ def test_defaults_to_gateway_with_bearer():
12
+ c = resolve_config(api_key="sk_x", env={})
13
+ assert c.endpoint == DEFAULT_ENDPOINT
14
+ assert c.headers["Authorization"] == "Bearer sk_x"
15
+
16
+
17
+ def test_reads_env():
18
+ c = resolve_config(
19
+ env={
20
+ "OMNIA_API_KEY": "sk_env",
21
+ "OMNIA_TAG": "checkout-agent",
22
+ "OMNIA_OTLP_ENDPOINT": "https://other.example/v1/traces",
23
+ "OTEL_SERVICE_NAME": "svc",
24
+ }
25
+ )
26
+ assert c.headers["Authorization"] == "Bearer sk_env"
27
+ assert c.resource_attributes[TAG_ATTRIBUTE] == "checkout-agent"
28
+ assert c.endpoint == "https://other.example/v1/traces"
29
+ assert c.service_name == "svc"
30
+
31
+
32
+ def test_options_beat_env():
33
+ c = resolve_config(
34
+ api_key="sk_opt",
35
+ tag="t2",
36
+ env={"OMNIA_API_KEY": "sk_env", "OMNIA_TAG": "t1"},
37
+ )
38
+ assert c.headers["Authorization"] == "Bearer sk_opt"
39
+ assert c.resource_attributes[TAG_ATTRIBUTE] == "t2"
40
+
41
+
42
+ def test_no_tag_means_no_attribute():
43
+ c = resolve_config(api_key="k", env={})
44
+ assert c.resource_attributes == {}