Metadata-Version: 2.4
Name: ennew-script-observability
Version: 0.2.0
Summary: ENNEW Script Observability SDK: structured reporting for Codex-executed Python scripts (Langfuse v4 backend)
License: Proprietary
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: langfuse<5,>=4.7

# ENNEW Script Observability SDK

面向 Codex Python skill 的结构化观测 SDK。业务代码用接近普通日志的方式写入，
SDK 自动完成 Trace/Session 关联、Langfuse 初始化、脱敏、异常栈与进程退出 flush。

## 最简用法

```python
from ennew_script_observability import log, skill_entry


@skill_entry
def main() -> None:
    log.input(doi="10.1000/x", provider="unpaywall")

    log.info("开始获取论文", event="paper.fetch.started", doi="10.1000/x")
    with log.step("provider.unpaywall", doi="10.1000/x") as step:
        result = fetch_from_unpaywall("10.1000/x")
        step.output(found=bool(result), status_code=200)

    log.result(success=True)
```

常规使用只需记住四件事：

- `@skill_entry`：装饰唯一入口，自动创建和结束根 Trace。
- `log.input(...)` / `log.result(...)`：脚本整体输入与结果。
- `log.info("消息", **字段)`：结构化日志；另有 `debug`、`warning`。
- `with log.step("稳定名称", **输入) as step`：需要耗时和输入输出配对的步骤。

错误也保持日志式写法：

```python
# 业务错误：自动关联当前 step，并更新根 Run 状态
log.error("资源不存在", code="PAPER-404", recoverable=False, doi=doi)

# 已捕获代码异常：在 except 块中调用，SDK 自动读取当前异常和 stacktrace
try:
    fetch()
except TimeoutError:
    log.exception("首次请求超时，准备重试", provider="arxiv", attempt=1)
```

## 固定 API

| API | 语义 | Langfuse 映射 |
|---|---|---|
| `@skill_entry` | 脚本生命周期 | 根 `tool` observation / 独立 Trace |
| `log.input(**fields)` | 脚本输入摘要，最多一次 | root input |
| `log.result(**fields)` | 脚本最终结果，最多一次 | root output |
| `log.step(name, kind="tool", **fields)` | 有耗时的业务步骤 | child observation |
| `step.output(**fields)` | 当前步骤输出 | child output |
| `log.debug/info/warning(message, event=..., **fields)` | 结构化业务日志 | event |
| `log.error(message, code=..., recoverable=..., **fields)` | 业务失败 | WARNING/ERROR event + 状态 |
| `log.exception(message, **fields)` | `except` 中的代码异常 | ERROR event + stacktrace |

`event` 可省略，SDK 会按日志级别使用稳定默认名。只有需要按业务事件单独筛选时才传。
`kind` 只在步骤确实属于 `retriever` 或 `generation` 时设置，默认 `tool`。

## Langfuse 自动初始化

SDK 与 Codex Langfuse 插件读取同一配置，优先级由低到高：

1. SDK 默认值；
2. `~/.codex/langfuse.json`；
3. `<cwd>/.codex/langfuse.json`；
4. 环境变量。

示例配置：

```json
{
  "enabled": true,
  "public_key": "pk-lf-...",
  "secret_key": "sk-lf-...",
  "base_url": "https://langfuse.example.com",
  "environment": "cloud-codex",
  "user_id": "codex-cloud",
  "tags": ["codex", "skill"],
  "metadata": {"deployment": "cloud"},
  "debug": false,
  "fail_on_error": false
}
```

环境变量同时支持插件专用名和 Langfuse 标准名；前者优先：

| 插件专用名 | 标准名 |
|---|---|
| `LANGFUSE_CODEX_PUBLIC_KEY` | `LANGFUSE_PUBLIC_KEY` |
| `LANGFUSE_CODEX_SECRET_KEY` | `LANGFUSE_SECRET_KEY` |
| `LANGFUSE_CODEX_BASE_URL` | `LANGFUSE_BASE_URL` |
| `LANGFUSE_CODEX_ENVIRONMENT` | `LANGFUSE_TRACING_ENVIRONMENT` |
| `LANGFUSE_CODEX_TAGS` | `LANGFUSE_TAGS` |
| `LANGFUSE_CODEX_METADATA` | `LANGFUSE_METADATA` |

业务脚本不需要读取配置文件、传凭据或创建 Langfuse 客户端。

## 执行上下文

执行层只需给子进程注入：

- `CODEX_THREAD_ID`：Langfuse `session_id`，云端使用时必需；
- `CODEX_TURN_ID` / `CODEX_CALL_ID`：推荐，写入 metadata；
- `ENNEW_SCRIPT_TRIGGER_SOURCE`：`skill` / `agents_md` / `manual`；
- `SKILL_NAME` / `SKILL_VERSION`：skill 标识。

`ENNEW_SCRIPT_OBSERVABILITY_STRICT=true` 或配置中的 `fail_on_error=true` 会在缺少
上下文、凭据或初始化失败时直接报错；默认模式则 fail-open，不影响业务脚本。

## 开发与验证

```bash
python3.12 -m venv .venv
.venv/bin/pip install -e . pytest
.venv/bin/python -m pytest tests/unit
.venv/bin/ennew-script-observability lint ../local-e2e/skills/ennew-observability-mock/scripts
```
