Metadata-Version: 2.4
Name: tencentcloud-agentobs-sdk-langchain
Version: 0.0.1
Summary: CLS observability SDK for LangChain/LangGraph — 6-layer span hierarchy, direct upload to Tencent Cloud CLS
Author: Tencent Cloud CLS Team
License: Apache-2.0
Project-URL: Homepage, https://cloud.tencent.com/product/cls
Project-URL: Documentation, https://cloud.tencent.com/document/product/614
Project-URL: Repository, https://github.com/TencentCloud/cls-sdk-langchain
Keywords: tencent,cls,langchain,langgraph,opentelemetry,observability,llm
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: langchain-core>=0.2.0
Requires-Dist: opentelemetry-api>=1.20.0
Requires-Dist: opentelemetry-sdk>=1.20.0
Requires-Dist: opentelemetry-instrumentation>=0.40b0
Requires-Dist: wrapt>=1.14.0
Requires-Dist: tencentcloud-cls-sdk-python>=1.0.8
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Dynamic: license-file

# tencentcloud-agentobs-sdk-langchain

[中文文档](README.zh-CN.md)

Observability SDK for **LangChain / LangGraph**: automatically intercepts
LangChain callback and LangGraph node lifecycle events, converts them into OTel
spans conforming to the Tencent Cloud CLS GenAI Trace specification, and uploads
directly to **Tencent Cloud CLS**.

- **Zero intrusion**: one `setup()` call (or `LangChainInstrumentor().instrument()`) completes instrumentation — no changes to business code
- **Faithful trace tree**: each `graph.invoke()` produces a complete trace with an `entry → agent → step → chat / tool` hierarchy that mirrors the framework's real call structure
- **Concurrency safe**: parent-child relationships are derived from the framework's `parent_run_id` — parallel tool calls and nested sub-agents just work
- **Compliance ready**: three content capture modes (`full` / `truncate` / `off`) for strict data-residency requirements
- **Full metrics**: token usage, finish_reason, tool error classification, ReAct round tracking

---

## Installation

```bash
pip install tencentcloud-agentobs-sdk-langchain
```

Runtime dependencies are installed automatically. The key ones:

- `langchain-core > 0.1.0` — the target framework being instrumented
- `tencentcloud-cls-sdk-python >= 1.0.8` — CLS upload client

For LangGraph agents you additionally need `langgraph`, and for LLM calls a
provider integration such as `langchain-openai`.

Requires **Python >= 3.9**.

---

## Quick Start

A single `setup()` handles all initialization:

```python
import os
from tencentcloud_agentobs_sdk_langchain import setup, CLSConfig

# 1. Configure your LLM provider (managed by LangChain, not this SDK)
os.environ["OPENAI_API_KEY"] = "sk-xxxx"

# 2. Enable CLS observability (must be called before running any agent)
# Option A: all from environment variables
setup()

# Option B: explicit CLSConfig (unset fields fall back to env vars)
setup(CLSConfig(
    endpoint="ap-guangzhou.cls.tencentcs.com",
    topic_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    secret_id="your_secret_id",
    secret_key="your_secret_key",
))

# 3. Use LangChain / LangGraph as usual
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent

@tool
def get_weather(city: str) -> str:
    """Look up the weather for a city."""
    return {"Beijing": "Sunny, 26C"}.get(city, "Sunny, 25C")

agent = create_react_agent(ChatOpenAI(model="gpt-4o-mini"), tools=[get_weather])
result = agent.invoke({"messages": [("user", "What's the weather in Beijing?")]})
print(result["messages"][-1].content)
```

`setup()` creates a `TracerProvider`, attaches the `CLSCloudExporter`, registers
instrumentation, and returns the provider (useful if you need to attach
additional processors).

### Manual wiring (advanced)

```python
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from tencentcloud_agentobs_sdk_langchain import LangChainInstrumentor
from tencentcloud_agentobs_sdk_langchain.cls_cloud_exporter import CLSCloudExporter

provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(CLSCloudExporter()))
LangChainInstrumentor().instrument(tracer_provider=provider)
```

---

## Configuration

All fields resolve with the priority: **explicit `CLSConfig` value > environment variable > built-in default**.

| Field | Env var | Default | Meaning |
|---|---|---|---|
| `endpoint` | `CLS_ENDPOINT` | — | CLS API endpoint (required) |
| `topic_id` | `CLS_TOPIC_ID` | — | CLS log topic ID (required) |
| `secret_id` | `CLS_SECRET_ID` | — | Tencent Cloud SecretId (required) |
| `secret_key` | `CLS_SECRET_KEY` | — | Tencent Cloud SecretKey (required) |
| `service_name` | `CLS_SERVICE_NAME` / `OTEL_SERVICE_NAME` | `langchain-app` | Service / application name |
| `content_mode` | `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT` | `full` | `full` / `truncate` / `off` |
| `batch_size` | `CLS_BATCH_SIZE` | `32` | Spans per upload batch (clamped 1–1000) |
| `debug` | `CLS_DEBUG` | `false` | Verbose SDK logging |
| `local_dump` | `CLS_LOCAL_DUMP` | `false` | Also write each uploaded batch to a local jsonl |
| `local_dump_file` | `CLS_LOCAL_DUMP_FILE` | `cls_spans.jsonl` | Local dump file path |
| `user_id` | `CLS_USER_ID` | — | End-user ID written to `gen_ai.user.id` |

SDK logs are written to `cls_sdk.log` (configurable via `CLS_SDK_LOG_FILE` /
`CLS_SDK_LOG_LEVEL`), using a rotating file handler.

---

## Span hierarchy

For a LangGraph `create_react_agent`, one `invoke()` produces:

```
entry  enter_application
└ agent  invoke_agent
  ├ step  react round_1
  │  ├ chain  call_model
  │  ├ chain  RunnableSequence
  │  │  ├ chain  Prompt
  │  │  └ chat   <model>
  │  ├ chain  should_continue
  │  └ tool   execute_tool <name>        (one per parallel tool call)
  └ step  react round_2
     └ ...
```

The tree faithfully reflects LangChain's real callback nesting: `chat` is nested
under its `RunnableSequence`, tool executions attach directly under the current
step, and each entry into the LangGraph `agent` node opens a new ReAct `step`.

---

## Examples

See [`example.py`](example.py) for a minimal end-to-end quick start
(LangGraph ReAct agent + `setup()`).

---

## License

Apache-2.0. See [LICENSE](LICENSE).
