mingx 0.1.2
OpenTelemetry trace Python SDK with GenAI semantic conventions, LangChain/LangGraph callbacks, and method-level @traced decorator
Requires Python: >=3.10

# mingx

基于 OpenTelemetry 的 Trace Python SDK，具备：

- **OpenTelemetry GenAI 语义规范**：所有上报 Span 符合 GenAI 约定
- **可扩展适配器**：支持 LangChain、LangGraph，后续可扩展 CrewAI、Google ADK 等
- **默认输入/输出**：适配器默认记录所有节点的入参（输入）与返回值（输出），无返回值记空
- **手动上报**：原生 OTEL 风格 `with get_tracer().start_as_current_span("name") as span:` + `span_input` / `span_output` 按需添加输入或输出，支持自定义属性/Event
- **LangChain & LangGraph**：通过 Callback 上报（非自动插桩）
- **方法级跟踪**：通过 `@traced` 装饰器为任意方法自动建 Span，默认记录入参（输入）与返回值（输出）

## 安装

```bash
uv add mingx
# 或: pip install mingx
```

示例与可选依赖：

```bash
uv add mingx[examples]
```

## 快速开始

### 配置 Tracer 与装饰器

```python
from mingx import configure_tracer_provider, traced

# OTLP over gRPC（默认，端口 4317）
configure_tracer_provider("my-app", otlp_endpoint="http://localhost:4317")

# OTLP over HTTP/Protobuf（端口 4318）
configure_tracer_provider("my-app", otlp_endpoint="http://localhost:4318", protocol="http")

# 设置 API Key 与默认属性（workspace_id、user_id）
# 鉴权支持 Bearer（默认）或 Basic，通过 auth_scheme 选择
configure_tracer_provider(
    "my-app",
    otlp_endpoint="https://your-collector.example.com",
    protocol="http",
    api_key="your-api-key",
    auth_scheme="bearer",   # 默认 "bearer"；Basic 时传 auth_scheme="basic"（api_key 可为 base64(user:pass)）
    workspace_id="ws-001",
    user_id="user-123",
)
# LangChain/LangGraph 等有节点先后顺序时，若希望按完成顺序上送 span，可设 use_batch_processor=False
# configure_tracer_provider(..., use_batch_processor=False)

@traced  # 默认 record_io=True，会记录函数入参与返回值
def my_function():
    ...

# 指定 span 名称；作为入口调用时，该 name 即为 root span name
@traced(name="custom_span", span_type="model", attributes={"key": "value"})
def another():
    ...
```

### LangChain / LangGraph 使用 Callback

```python
from mingx import get_langchain_callback

handler = get_langchain_callback()
chain.invoke({"input": "..."}, config={"callbacks": [handler]})
# 或 LangGraph：
graph.invoke({"messages": [...]}, config={"callbacks": [handler]})
```

**指定 Root Span 名称**：创建 handler 时传入 `root_span_name`，仅对根 run 生效。

```python
handler = get_langchain_callback(root_span_name="my_agent")
graph.invoke(..., config={"callbacks": [handler]})
```

### 手动上报（span_input / span_output）

```python
from mingx import get_tracer, span_input, span_output

with get_tracer().start_as_current_span("call_model") as span:
    span_input({"prompt": "..."})   # 可选：attributes=..., event_attributes=...
    result = call_model()
    span_output(result)
```

详见下文「输入/输出记录」中的「手动上报输入/输出」。

## 默认属性与 Baggage

| 属性名 | 说明 | 设置方式 |
|--------|------|----------|
| `span_id` | Span ID | 默认属性，无需设置 |
| `trace_id` | Trace ID | 默认属性，无需设置 |
| `span_status` | span状态 | OK（成功）、UNSET（未上传，默认状态）、ERROR（错误）默认属性，无需设置 |
| `span_code` | span状态码 | UNSET = 0、OK = 1、ERROR = 2，默认属性，无需设置 |
| `reference_parent_span_id` | 父级spanID | 默认属性，无需设置 |
| `start_time` | 开始时间 | 默认属性，无需设置 |
| `end_time` | 结束时间 | 默认属性，无需设置 |
| `duration` | 时间差 | 默认属性，无需设置 |
| `operation_name` | span name | `@traced(name="custom_span")`方式指定 |
| `mingx.workspace_id` | 工作空间 ID | `configure_tracer_provider(..., workspace_id=...)` 或 Baggage |
| `mingx.user.id` | 用户 ID | `configure_tracer_provider(..., user_id=...)` 或 Baggage |
| `mingx.span_type` | Span 节点类型 | **不在初始化时设置**：由 `@traced(span_type=...)` 或适配器按调用类型自动设置 |

**span_type** 可选值（可扩展）：`model`（大模型）、`retriever`（知识库召回）、`tool`（Tool Call）、`chain`、`agent` 等。  
- 使用 **@traced** 时：`@traced(span_type="model")` 或 `@traced(span_type=SPAN_TYPE_TOOL)` 手动指定。  
- 使用 **LangChain/LangGraph 适配器** 时：根据回调自动设置（LLM→model、Tool→tool、Retriever→retriever、Chain→chain）。

**Baggage**：跨服务/跨请求时可在入口设置 Baggage，SDK 会优先从 Baggage 读取上述键并写入 Span。`use_baggage=True`（默认）启用。

```python
from opentelemetry.baggage import set_baggage

set_baggage("mingx.workspace_id", "ws-001")
set_baggage("mingx.user.id", "user-123")
# span_type 也可通过 Baggage 覆盖（如上游已设置）
set_baggage("mingx.span_type", "model")
```

## 输入/输出记录（Event 优先）

**默认行为**：适配器会**默认记录所有节点**的入参（输入）与返回值（输出）；无返回值时记空即可。  
模型的输入/输出按 **OpenTelemetry GenAI** 语义记录，与 Traceloop 等 SDK 一致：**优先使用 Span Event**，其次可选 Span 属性。

| 方式 | 说明 |
|------|------|
| **Event（推荐）** | 在 Span 上打两条 Event：`gen_ai.input`（属性 `gen_ai.input.messages`）、`gen_ai.output`（属性 `gen_ai.output.messages`）。Span 本体保持精简，后端可对 Event 单独采样、脱敏或存储。 |
| **Attribute** | 将输入/输出写入 Span 属性 `gen_ai.input.messages`、`gen_ai.output.messages`。适合小 payload 或需要与 Span 一起索引的场景。 |
| **none** | 不记录输入/输出，仅记录元数据（model、token 等）。 |

**配置**（LangChain 适配器）：

```python
from mingx import get_langchain_callback

# 默认已开启输入/输出（trace_content=True），所有节点入参、返回值都会记录
handler = get_langchain_callback()

# 如需关闭或改用属性记录
handler = get_langchain_callback(
    trace_content=False,               # 关闭输入/输出
    record_input_output_as="events",   # "events" | "attributes" | "none"
    max_content_length=2000,           # 可选：单条 content 截断长度
)
```

### 手动上报输入/输出（原生 OTEL 风格）

使用与 OpenTelemetry 一致的方式：`with get_tracer().start_as_current_span("name") as span:` 创建 Span，在块内按需调用 `span_input` 或 `span_output`，只需添加输入或输出其一即可。支持自定义 Span 属性或 Event 属性。

```python
from mingx import get_tracer, span_input, span_output

with get_tracer().start_as_current_span("call_model") as span:
    span_input({"prompt": "..."})   # 只添加输入
    result = call_model()
    span_output(result)             # 只添加输出

# 自定义属性或 Event 属性
span_input({"prompt": "..."}, attributes={"my.key": "value"})
span_output(result, event_attributes={"custom": "meta"})
```

- **span_input(data, ...)**：向当前 Span 添加输入（入参），可序列化 dict/list/str 等。
- **span_output(data, ...)**：向当前 Span 添加输出（返回值）；无返回值可不调用或传 `None`。
- **attributes**：自定义 Span 属性（写入当前 Span）。
- **event_attributes**：自定义 Event 属性（合并到 gen_ai.input / gen_ai.output 的 Event）。
- 可只调用其一（只添加输入或只添加输出）。
- 若未在 `with` 块内，可传 `span=span`：`span_input(data, span=span)` / `span_output(data, span=span)`。
- 可选参数：`record_as="events"`（默认）| `"attributes"` | `"none"`，`max_length` 截断长度。

**Event 名称与属性键**：

| Event 名 | 属性键 | 说明 |
|----------|--------|------|
| `gen_ai.input` | `gen_ai.input.messages` | 输入消息列表（JSON 数组，每项含 role、content） |
| `gen_ai.output` | `gen_ai.output.messages` | 输出消息列表（同上） |
| `gen_ai.system_instructions` | `gen_ai.system_instructions` | 可选，系统指令 |

手动上报时使用 `span_input` / `span_output`（见上文）；LLM 消息结构可调用 `mingx.genai.record_llm_input_output(span, input_messages, output_messages, record_as="events")`。

## Span 类型与属性说明

所有 Span 均可携带以下**通用属性**（来自 `configure_tracer_provider` 或 Baggage）：

| 属性名 | 类型 | 说明 |
|--------|------|------|
| `mingx.workspace_id` | string | 工作空间 ID |
| `mingx.user.id` | string | 用户 ID |
| `mingx.span_type` | string | Span 节点类型（见下表） |

以下为 **mingx.span_type** 的取值及每种类型对应的属性说明。

---

### 所有 span_type 取值

| span_type | 说明 | 典型来源 |
|-----------|------|----------|
| `model` | 大模型调用（LLM/推理） | LangChain 适配器 `on_llm_start`、`@traced(span_type="model")` |
| `retriever` | 知识库/检索召回 | LangChain 适配器 `on_retriever_start`、`@traced(span_type="retriever")` |
| `tool` | Tool Call 执行 | LangChain 适配器 `on_tool_start`、`@traced(span_type="tool")` |
| `chain` | 链/组合节点 | LangChain 适配器 `on_chain_start`、`@traced(span_type="chain")` |
| `agent` | Agent 节点 | `@traced(span_type="agent")` 或 Baggage |
| `custom` | 自定义类型（适配器未识别时的默认值） | 适配器在 span_type 为空时自动填充 |

---

### 各类型对应属性

#### 1. `model`（大模型节点）

| 属性/Event | 类型 | 说明 |
|------------|------|------|
| `mingx.span_type` | string | `"model"` |
| `gen_ai.operation.name` | string | 操作名，如 `"chat"` |
| `gen_ai.provider.name` | string | 厂商，如 `"openai"`, `"anthropic"`, `"gcp.vertex_ai"` 等 |
| `gen_ai.request.model` | string | 请求模型名 |
| `gen_ai.response.model` | string | 可选，响应实际使用的模型 |
| `gen_ai.response.id` | string | 可选，响应 ID |
| `gen_ai.response.finish_reasons` | string[] | 可选，结束原因 |
| `gen_ai.usage.input_tokens` | int | 可选，输入 token 数 |
| `gen_ai.usage.output_tokens` | int | 可选，输出 token 数 |
| **输入/输出** | **Event（推荐）或 Attribute** | 见上文「输入/输出记录」；`gen_ai.input` / `gen_ai.output` Event 或 `gen_ai.input.messages` / `gen_ai.output.messages` 属性 |
| `lc.run_id` | string | LangChain run_id（适配器时） |
| `error.type` | string | 出错时错误类型 |

#### 2. `retriever`（知识库召回节点）

| 属性名 | 类型 | 说明 |
|--------|------|------|
| `mingx.span_type` | string | `"retriever"` |
| `gen_ai.operation.name` | string | `"retriever"` |
| **输入/输出** | **Event 或 Attribute** | 适配器默认记录 query / documents（`trace_content=True`）；也可用 `span_input` / `span_output` 手动记录 |
| `lc.run_id` | string | LangChain run_id（适配器时） |
| `error.type` | string | 出错时错误类型 |

#### 3. `tool`（Tool Call 节点）

| 属性名 | 类型 | 说明 |
|--------|------|------|
| `mingx.span_type` | string | `"tool"` |
| `gen_ai.operation.name` | string | `"execute_tool"` |
| `gen_ai.tool.name` | string | 工具名称 |
| `gen_ai.tool.description` | string | 可选，工具描述 |
| `gen_ai.tool.call.id` | string | 可选，工具调用 ID |
| `gen_ai.tool.type` | string | 可选，工具类型 |
| **输入/输出** | **Event 或 Attribute** | 适配器默认记录入参/出参（`trace_content=True`）；支持 `attributes` / `event_attributes` 自定义属性 |
| `lc.run_id` | string | LangChain run_id（适配器时） |
| `error.type` | string | 出错时错误类型 |

#### 4. `chain`（链/组合节点）

| 属性名 | 类型 | 说明 |
|--------|------|------|
| `mingx.span_type` | string | `"chain"` |
| `gen_ai.operation.name` | string | `"invoke_agent"` |
| **输入/输出** | **Event 或 Attribute** | 适配器默认记录 inputs / outputs（`trace_content=True`） |
| `lc.run_id` | string | LangChain run_id（适配器时） |
| `error.type` | string | 出错时错误类型 |

#### 5. `agent`（Agent 节点）

| 属性名 | 类型 | 说明 |
|--------|------|------|
| `mingx.span_type` | string | `"agent"` |
| 其他 | — | 由 `@traced(span_type="agent", attributes={...})` 或 GenAI 语义层按需添加 |

#### 6. `custom`（自定义类型）

| 属性名 | 类型 | 说明 |
|--------|------|------|
| `mingx.span_type` | string | `"custom"` |
| 其他 | — | 适配器未识别类型时自动使用；也可通过 `@traced(span_type="custom", attributes={...})` 扩展 |

---

代码中可使用常量（来自 `mingx`）：

```python
from mingx import (
    SPAN_TYPE_MODEL,
    SPAN_TYPE_RETRIEVER,
    SPAN_TYPE_TOOL,
    SPAN_TYPE_CHAIN,
    SPAN_TYPE_AGENT,
    SPAN_TYPE_CUSTOM,
)

@traced(span_type=SPAN_TYPE_MODEL)
def call_llm(): ...
```

## GenAI 规范

Span 遵循 [OpenTelemetry GenAI Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/)。可选：设置环境变量 `OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental` 以使用最新实验属性。

## 项目结构

- `mingx._trace`：Tracer 与 TracerProvider 配置
- `mingx.genai`：GenAI 语义层（Span 名、属性、输入/输出 Event）
- `mingx.adapters`：框架适配器（LangChain，后续 CrewAI/ADK）
- `mingx.decorator`：`@traced` 方法级 Span
- `mingx._default_attributes`：默认属性与 Baggage 处理

## 打包

项目使用 [Hatchling](https://hatch.pypa.io/) 作为构建后端（见 `pyproject.toml`）。

**构建 wheel 与 sdist（推荐用 uv）：**

```bash
# 安装 uv 后，在项目根目录执行
uv build
```

产物在 `dist/` 目录：`mingx-<version>-py3-none-any.whl` 与 `mingx-<version>.tar.gz`。

**使用 pip 构建：**

```bash
pip install build
python -m build
```

**本地可编辑安装（开发时）：**

```bash
uv pip install -e .
# 或
pip install -e .
```

**发布到 PyPI（示例）：**

```bash
uv build
uv publish
# 或使用 twine
pip install twine
twine upload dist/*
```

## 测试

```bash
uv sync --extra dev
uv run pytest tests/ -v
```

## License

见仓库说明。