mingx 0.1.0__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.
- mingx/__init__.py +50 -0
- mingx/_default_attributes.py +76 -0
- mingx/_trace.py +147 -0
- mingx/adapters/__init__.py +21 -0
- mingx/adapters/base.py +77 -0
- mingx/adapters/langchain.py +646 -0
- mingx/decorator.py +185 -0
- mingx/genai/__init__.py +99 -0
- mingx/genai/attributes.py +176 -0
- mingx/genai/io.py +439 -0
- mingx/genai/span_attributes.py +172 -0
- mingx/genai/spans.py +175 -0
- mingx-0.1.0.dist-info/METADATA +373 -0
- mingx-0.1.0.dist-info/RECORD +15 -0
- mingx-0.1.0.dist-info/WHEEL +4 -0
mingx/__init__.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""
|
|
2
|
+
mingx: OpenTelemetry trace Python SDK with GenAI semantic conventions.
|
|
3
|
+
|
|
4
|
+
- configure_tracer_provider / get_tracer: Tracer setup
|
|
5
|
+
- traced: Method-level @traced decorator
|
|
6
|
+
- OpenTelemetryCallbackHandler / get_langchain_callback: LangChain/LangGraph callbacks
|
|
7
|
+
- span_input / span_output: 手动向 Span 添加输入或输出,支持自定义属性/Event(原生 OTEL 风格)
|
|
8
|
+
- genai: GenAI span names and attributes (OTEL compliant)
|
|
9
|
+
- 默认属性: mingx.workspace_id, mingx.span_type, mingx.user.id(支持 Baggage 传递)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from mingx._trace import configure_tracer_provider, get_tracer, OTLPProtocol, AuthScheme
|
|
13
|
+
from mingx.decorator import traced
|
|
14
|
+
from mingx.adapters.langchain import (
|
|
15
|
+
OpenTelemetryCallbackHandler,
|
|
16
|
+
get_langchain_callback,
|
|
17
|
+
)
|
|
18
|
+
from mingx.genai.io import span_input, span_output
|
|
19
|
+
from mingx._default_attributes import (
|
|
20
|
+
MINGX_WORKSPACE_ID,
|
|
21
|
+
MINGX_SPAN_TYPE,
|
|
22
|
+
MINGX_USER_ID,
|
|
23
|
+
SPAN_TYPE_AGENT,
|
|
24
|
+
SPAN_TYPE_CHAIN,
|
|
25
|
+
SPAN_TYPE_CUSTOM,
|
|
26
|
+
SPAN_TYPE_MODEL,
|
|
27
|
+
SPAN_TYPE_RETRIEVER,
|
|
28
|
+
SPAN_TYPE_TOOL,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"configure_tracer_provider",
|
|
33
|
+
"get_tracer",
|
|
34
|
+
"OTLPProtocol",
|
|
35
|
+
"AuthScheme",
|
|
36
|
+
"traced",
|
|
37
|
+
"OpenTelemetryCallbackHandler",
|
|
38
|
+
"get_langchain_callback",
|
|
39
|
+
"span_input",
|
|
40
|
+
"span_output",
|
|
41
|
+
"MINGX_WORKSPACE_ID",
|
|
42
|
+
"MINGX_SPAN_TYPE",
|
|
43
|
+
"MINGX_USER_ID",
|
|
44
|
+
"SPAN_TYPE_AGENT",
|
|
45
|
+
"SPAN_TYPE_CHAIN",
|
|
46
|
+
"SPAN_TYPE_CUSTOM",
|
|
47
|
+
"SPAN_TYPE_MODEL",
|
|
48
|
+
"SPAN_TYPE_RETRIEVER",
|
|
49
|
+
"SPAN_TYPE_TOOL",
|
|
50
|
+
]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
默认 Span 属性及 Baggage 支持。
|
|
3
|
+
|
|
4
|
+
通过 SpanProcessor 为每个 Span 添加 mingx.workspace_id、mingx.user.id(来自配置或 Baggage);
|
|
5
|
+
mingx.span_type 由调用方设置:@traced(span_type=...) 或适配器根据回调类型(model/tool/retriever/chain)自动设置。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
11
|
+
|
|
12
|
+
from opentelemetry import context
|
|
13
|
+
from opentelemetry.baggage import get_all as get_baggage_all
|
|
14
|
+
from opentelemetry.sdk.trace import ReadableSpan, Span, SpanProcessor
|
|
15
|
+
|
|
16
|
+
# 默认属性键名
|
|
17
|
+
MINGX_WORKSPACE_ID = "mingx.workspace_id"
|
|
18
|
+
MINGX_SPAN_TYPE = "mingx.span_type"
|
|
19
|
+
MINGX_USER_ID = "mingx.user.id"
|
|
20
|
+
|
|
21
|
+
# span_type 可选值(可扩展,由装饰器或适配器设置)
|
|
22
|
+
SPAN_TYPE_MODEL = "model"
|
|
23
|
+
SPAN_TYPE_RETRIEVER = "retriever"
|
|
24
|
+
SPAN_TYPE_TOOL = "tool"
|
|
25
|
+
SPAN_TYPE_CHAIN = "chain"
|
|
26
|
+
SPAN_TYPE_AGENT = "agent"
|
|
27
|
+
SPAN_TYPE_CUSTOM = "custom" # 适配器方式下 span_type 为空时的默认值,表示自定义类型
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class DefaultAttributesSpanProcessor(SpanProcessor):
|
|
31
|
+
"""
|
|
32
|
+
为每个 Span 添加默认属性:workspace_id、user_id 来自配置或 Baggage。
|
|
33
|
+
span_type 不由此处设置,由 @traced(span_type=...) 或适配器按调用类型设置。
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
workspace_id: Optional[str] = None,
|
|
39
|
+
user_id: Optional[str] = None,
|
|
40
|
+
extra_attributes: Optional[Dict[str, Any]] = None,
|
|
41
|
+
use_baggage: bool = True,
|
|
42
|
+
) -> None:
|
|
43
|
+
self._defaults: Dict[str, Any] = {}
|
|
44
|
+
if workspace_id is not None:
|
|
45
|
+
self._defaults[MINGX_WORKSPACE_ID] = workspace_id
|
|
46
|
+
if user_id is not None:
|
|
47
|
+
self._defaults[MINGX_USER_ID] = user_id
|
|
48
|
+
if extra_attributes:
|
|
49
|
+
self._defaults.update(extra_attributes)
|
|
50
|
+
self._use_baggage = use_baggage
|
|
51
|
+
|
|
52
|
+
def on_start(
|
|
53
|
+
self,
|
|
54
|
+
span: Span,
|
|
55
|
+
parent_context: Optional[context.Context] = None,
|
|
56
|
+
) -> None:
|
|
57
|
+
attrs: Dict[str, Any] = dict(self._defaults)
|
|
58
|
+
if self._use_baggage and parent_context is not None:
|
|
59
|
+
baggage = get_baggage_all(parent_context)
|
|
60
|
+
if baggage:
|
|
61
|
+
for key in (MINGX_WORKSPACE_ID, MINGX_SPAN_TYPE, MINGX_USER_ID):
|
|
62
|
+
val = baggage.get(key)
|
|
63
|
+
if val is not None:
|
|
64
|
+
attrs[key] = str(val) if not isinstance(val, str) else val
|
|
65
|
+
for k, v in attrs.items():
|
|
66
|
+
if v is not None:
|
|
67
|
+
span.set_attribute(k, v)
|
|
68
|
+
|
|
69
|
+
def on_end(self, span: ReadableSpan) -> None:
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
def shutdown(self) -> None:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
76
|
+
return True
|
mingx/_trace.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tracer and TracerProvider configuration for mingx.
|
|
3
|
+
|
|
4
|
+
Uses the global TracerProvider when set; provides optional one-shot
|
|
5
|
+
configure_tracer_provider for applications that do not configure OTel themselves.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, Dict, Literal, Optional
|
|
11
|
+
|
|
12
|
+
from opentelemetry import trace
|
|
13
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
14
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor, SpanExporter
|
|
15
|
+
from opentelemetry.trace import Tracer
|
|
16
|
+
|
|
17
|
+
from mingx._default_attributes import DefaultAttributesSpanProcessor
|
|
18
|
+
|
|
19
|
+
OTLPProtocol = Literal["grpc", "http"]
|
|
20
|
+
AuthScheme = Literal["bearer", "basic"]
|
|
21
|
+
|
|
22
|
+
# Package version for get_tracer; keep in sync with pyproject.toml or use importlib.metadata
|
|
23
|
+
__all__ = ["get_tracer", "configure_tracer_provider", "OTLPProtocol", "AuthScheme"]
|
|
24
|
+
|
|
25
|
+
_PACKAGE_NAME = "mingx"
|
|
26
|
+
_PACKAGE_VERSION = "0.1.0"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_tracer(
|
|
30
|
+
name: str = _PACKAGE_NAME,
|
|
31
|
+
version: Optional[str] = None,
|
|
32
|
+
) -> Tracer:
|
|
33
|
+
"""
|
|
34
|
+
Return a Tracer from the global TracerProvider.
|
|
35
|
+
|
|
36
|
+
Uses opentelemetry.trace.get_tracer() so that whatever Provider is
|
|
37
|
+
set globally (by the app or by configure_tracer_provider) is used.
|
|
38
|
+
"""
|
|
39
|
+
return trace.get_tracer(name or _PACKAGE_NAME, version or _PACKAGE_VERSION)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def configure_tracer_provider(
|
|
43
|
+
service_name: str,
|
|
44
|
+
otlp_endpoint: Optional[str] = None,
|
|
45
|
+
exporter: Optional[SpanExporter] = None,
|
|
46
|
+
protocol: OTLPProtocol = "grpc",
|
|
47
|
+
api_key: Optional[str] = None,
|
|
48
|
+
auth_scheme: AuthScheme = "bearer",
|
|
49
|
+
workspace_id: Optional[str] = None,
|
|
50
|
+
user_id: Optional[str] = None,
|
|
51
|
+
extra_attributes: Optional[Dict[str, Any]] = None,
|
|
52
|
+
use_baggage: bool = True,
|
|
53
|
+
) -> TracerProvider:
|
|
54
|
+
"""
|
|
55
|
+
Create and set the global TracerProvider with BatchSpanProcessor and OTLP export.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
service_name: Used as service.name in the resource.
|
|
59
|
+
otlp_endpoint: OTLP endpoint URL.
|
|
60
|
+
- For gRPC: e.g. http://localhost:4317 (default port 4317).
|
|
61
|
+
- For HTTP: e.g. http://localhost:4318 (default port 4318).
|
|
62
|
+
If None, uses env OTEL_EXPORTER_OTLP_ENDPOINT.
|
|
63
|
+
exporter: Optional pre-built SpanExporter. If provided, otlp_endpoint,
|
|
64
|
+
protocol and api_key are ignored.
|
|
65
|
+
protocol: "grpc" (OTLP over gRPC, protobuf) or "http" (OTLP over HTTP, protobuf).
|
|
66
|
+
Default "grpc".
|
|
67
|
+
api_key: Optional API key/token for collector auth; sent in Authorization header.
|
|
68
|
+
If value already starts with "Bearer " or "Basic ", used as-is; otherwise
|
|
69
|
+
prefixed according to auth_scheme.
|
|
70
|
+
auth_scheme: "bearer" (default) or "basic". Used only when api_key does not
|
|
71
|
+
already start with Bearer/Basic. Basic 常用于用户名:密码的 base64 编码。
|
|
72
|
+
workspace_id: Default mingx.workspace_id (工作空间) on every span.
|
|
73
|
+
user_id: Default mingx.user.id (用户 id) on every span.
|
|
74
|
+
extra_attributes: Optional dict of extra default attributes on every span.
|
|
75
|
+
use_baggage: If True (default), read mingx.workspace_id, mingx.span_type,
|
|
76
|
+
mingx.user.id from Baggage and overlay on span (Baggage overrides defaults).
|
|
77
|
+
span_type 不在初始化时设置,由 @traced(span_type=...) 或适配器按调用类型设置。
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
The TracerProvider that is now set as global.
|
|
81
|
+
"""
|
|
82
|
+
from opentelemetry.sdk.resources import Resource
|
|
83
|
+
|
|
84
|
+
resource = Resource.create({"service.name": service_name})
|
|
85
|
+
provider = TracerProvider(resource=resource)
|
|
86
|
+
|
|
87
|
+
# Default attributes (and optional Baggage); span_type 由装饰器/适配器设置
|
|
88
|
+
default_processor = DefaultAttributesSpanProcessor(
|
|
89
|
+
workspace_id=workspace_id,
|
|
90
|
+
user_id=user_id,
|
|
91
|
+
extra_attributes=extra_attributes,
|
|
92
|
+
use_baggage=use_baggage,
|
|
93
|
+
)
|
|
94
|
+
provider.add_span_processor(default_processor)
|
|
95
|
+
|
|
96
|
+
if exporter is None:
|
|
97
|
+
exporter = _create_otlp_exporter(
|
|
98
|
+
protocol=protocol,
|
|
99
|
+
endpoint=otlp_endpoint,
|
|
100
|
+
api_key=api_key,
|
|
101
|
+
auth_scheme=auth_scheme,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
provider.add_span_processor(BatchSpanProcessor(exporter))
|
|
105
|
+
trace.set_tracer_provider(provider)
|
|
106
|
+
return provider
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _create_otlp_exporter(
|
|
110
|
+
protocol: OTLPProtocol = "grpc",
|
|
111
|
+
endpoint: Optional[str] = None,
|
|
112
|
+
api_key: Optional[str] = None,
|
|
113
|
+
auth_scheme: AuthScheme = "bearer",
|
|
114
|
+
) -> SpanExporter:
|
|
115
|
+
"""Create OTLP span exporter by protocol (grpc or http/protobuf)."""
|
|
116
|
+
headers = None
|
|
117
|
+
if api_key:
|
|
118
|
+
token = api_key.strip()
|
|
119
|
+
lower = token.lower()
|
|
120
|
+
if not (lower.startswith("bearer ") or lower.startswith("basic ")):
|
|
121
|
+
if auth_scheme == "basic":
|
|
122
|
+
token = f"Basic {token}"
|
|
123
|
+
else:
|
|
124
|
+
token = f"Bearer {token}"
|
|
125
|
+
headers = [("authorization", token)]
|
|
126
|
+
|
|
127
|
+
if protocol == "http":
|
|
128
|
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
|
|
129
|
+
OTLPSpanExporter as OTLPSpanExporterHttp,
|
|
130
|
+
)
|
|
131
|
+
kwargs: Dict[str, Any] = {}
|
|
132
|
+
if endpoint:
|
|
133
|
+
kwargs["endpoint"] = endpoint
|
|
134
|
+
if headers:
|
|
135
|
+
kwargs["headers"] = dict(headers)
|
|
136
|
+
return OTLPSpanExporterHttp(**kwargs)
|
|
137
|
+
# gRPC (default)
|
|
138
|
+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
|
|
139
|
+
OTLPSpanExporter as OTLPSpanExporterGrpc,
|
|
140
|
+
)
|
|
141
|
+
kwargs = {}
|
|
142
|
+
if endpoint:
|
|
143
|
+
kwargs["endpoint"] = endpoint
|
|
144
|
+
kwargs["insecure"] = True
|
|
145
|
+
if headers:
|
|
146
|
+
kwargs["headers"] = headers
|
|
147
|
+
return OTLPSpanExporterGrpc(**kwargs)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Framework adapters for LLM/Agent frameworks."""
|
|
2
|
+
|
|
3
|
+
from mingx.adapters.base import (
|
|
4
|
+
BaseFrameworkTraceAdapter,
|
|
5
|
+
FrameworkTraceAdapter,
|
|
6
|
+
SUPPORTED_FRAMEWORKS,
|
|
7
|
+
PLANNED_FRAMEWORKS,
|
|
8
|
+
)
|
|
9
|
+
from mingx.adapters.langchain import (
|
|
10
|
+
OpenTelemetryCallbackHandler,
|
|
11
|
+
get_langchain_callback,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"FrameworkTraceAdapter",
|
|
16
|
+
"BaseFrameworkTraceAdapter",
|
|
17
|
+
"SUPPORTED_FRAMEWORKS",
|
|
18
|
+
"PLANNED_FRAMEWORKS",
|
|
19
|
+
"OpenTelemetryCallbackHandler",
|
|
20
|
+
"get_langchain_callback",
|
|
21
|
+
]
|
mingx/adapters/base.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Framework trace adapter abstraction.
|
|
3
|
+
|
|
4
|
+
Adapters translate framework-specific events (e.g. LangChain on_llm_start,
|
|
5
|
+
CrewAI task start) into GenAI semantic layer calls (span names, attributes).
|
|
6
|
+
New frameworks (CrewAI, Google ADK) implement this protocol and register;
|
|
7
|
+
no changes to core or other adapters.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from abc import ABC, abstractmethod
|
|
13
|
+
from typing import Any, Dict, Optional, Protocol, runtime_checkable
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@runtime_checkable
|
|
17
|
+
class FrameworkTraceAdapter(Protocol):
|
|
18
|
+
"""
|
|
19
|
+
Protocol for framework trace adapters.
|
|
20
|
+
|
|
21
|
+
Implementations start/end spans and set GenAI attributes via mingx.genai
|
|
22
|
+
(spans / attributes), not by writing gen_ai.* strings directly.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def start_span(self, name: str, attributes: Optional[Dict[str, Any]] = None) -> Any:
|
|
26
|
+
"""
|
|
27
|
+
Start a span with the given name and attributes.
|
|
28
|
+
|
|
29
|
+
Returns a handle (e.g. context token or span) that is passed to end_span.
|
|
30
|
+
"""
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
def end_span(self, handle: Any, *, status: Optional[Any] = None) -> None:
|
|
34
|
+
"""End the span identified by handle. Optionally set status (e.g. error)."""
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
def set_attributes(self, handle: Any, attributes: Dict[str, Any]) -> None:
|
|
38
|
+
"""Set additional attributes on the span (e.g. on_*_end data)."""
|
|
39
|
+
...
|
|
40
|
+
|
|
41
|
+
def record_exception(self, handle: Any, exception: BaseException) -> None:
|
|
42
|
+
"""Record an exception on the span and set error status."""
|
|
43
|
+
...
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class BaseFrameworkTraceAdapter(ABC):
|
|
47
|
+
"""
|
|
48
|
+
Abstract base for framework adapters.
|
|
49
|
+
|
|
50
|
+
Adapters that cannot use a simple Protocol (e.g. need to hold tracer or
|
|
51
|
+
run_id -> span mapping) can subclass this and implement the same contract.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def start_span(self, name: str, attributes: Optional[Dict[str, Any]] = None) -> Any:
|
|
56
|
+
"""Start a span; return handle for end_span / set_attributes."""
|
|
57
|
+
...
|
|
58
|
+
|
|
59
|
+
@abstractmethod
|
|
60
|
+
def end_span(self, handle: Any, *, status: Optional[Any] = None) -> None:
|
|
61
|
+
"""End the span."""
|
|
62
|
+
...
|
|
63
|
+
|
|
64
|
+
@abstractmethod
|
|
65
|
+
def set_attributes(self, handle: Any, attributes: Dict[str, Any]) -> None:
|
|
66
|
+
"""Set attributes on the span."""
|
|
67
|
+
...
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
def record_exception(self, handle: Any, exception: BaseException) -> None:
|
|
71
|
+
"""Record exception on the span."""
|
|
72
|
+
...
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# Optional: registry for "supported frameworks" for docs
|
|
76
|
+
SUPPORTED_FRAMEWORKS = ["langchain"]
|
|
77
|
+
PLANNED_FRAMEWORKS = ["crewai", "google_adk"]
|