Metadata-Version: 2.4
Name: llm-budget-guard
Version: 0.2.3
Summary: Cross-vendor LLM quota / budget self-check CLI. Stable 0/1/2/3 exit-code contract lets any AI agent gate its work before burning through budget. Supports MiniMax Token Plan, OpenRouter credits, self-hosted LiteLLM proxy spend, and self-logged JSONL (any LLM vendor).
Project-URL: Homepage, https://github.com/me-speaker/token_manager
Project-URL: Repository, https://github.com/me-speaker/token_manager
Project-URL: Issues, https://github.com/me-speaker/token_manager/issues
Project-URL: Changelog, https://github.com/me-speaker/token_manager/blob/main/CHANGELOG.md
Author-email: speaker <noreply@example.com>
License: MIT
License-File: LICENSE
Keywords: ai-agent,budget,claude-code,cli,litellm,llm,minimax,monitoring,openrouter,quota
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: System :: Monitoring
Classifier: Topic :: Utilities
Requires-Python: >=3.10
Requires-Dist: click>=8.1
Requires-Dist: httpx>=0.27
Requires-Dist: plyer>=2.1
Requires-Dist: pydantic-settings>=2.2
Requires-Dist: pydantic>=2.6
Provides-Extra: dev
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

# llm-budget-guard

[![CI](https://github.com/me-speaker/token_manager/actions/workflows/ci.yml/badge.svg)](https://github.com/me-speaker/token_manager/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/llm-budget-guard)](https://pypi.org/project/llm-budget-guard/)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
[![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org)

**让 AI agent 跑任务前自己知道 budget 还剩多少——还没烧光就已经停手。**

跨厂商 LLM quota / budget 自检 CLI：把 MiniMax、OpenRouter、LiteLLM proxy、或自记 JSONL 用一个稳定的 0/1/2/3 退出码契约接进任何 AI agent 循环。

> **Why this exists** — Claude Code / Cursor Agent / Codex 跑长任务时，agent **不知道当前 quota 还剩多少**，直到 API 调用失败浪费一小时才知情。这个工具把"quota 自检"做成 30 行 bash 的 PreToolUse hook，让 agent 在任务前自己判断是不是该继续。

```bash
$ llm-budget-guard status --warn-pct 60 --critical-pct 85
[minimax] 使用率: 56.0% (WARN≥60.0% / CRITICAL≥85.0%)
$ echo $?
0

$ # 把阈值调低 — 自动触发
$ llm-budget-guard status --warn-pct 30 --critical-pct 50
[minimax] 使用率: 56.0% (WARN≥30.0% / CRITICAL≥50.0%)
[CRITICAL] 已用 56.0%,超过严重阈值 50.0%
$ echo $?
3
```

## 4 个 Provider (一个 CLI 吃 4 套数据源)

| 命令 | 数据源 | 凭据 |
|---|---|---|
| `llm-budget-guard --provider minimax` | MiniMax Token Plan 订阅 (5h 滚动 + 周窗口) | `SUBSCRIPTION_KEY` |
| `llm-budget-guard --provider openrouter` | OpenRouter credits 余额 | `OPENROUTER_API_KEY` |
| `llm-budget-guard --provider litellm` | 自部署 LiteLLM proxy 月度 spend | `LITELLM_PROXY_URL` (+ optional `LITELLM_API_KEY` / `LITELLM_BUDGET_USD`) |
| `llm-budget-guard --provider local` | 自记 JSONL 用量 (任何厂商都行) | `LOCAL_JSONL_PATH` (+ `LOCAL_BUDGET_USD` 或 `LOCAL_BUDGET_TOKENS`) |

切换 provider：
```bash
# 不同 provider 用不同凭据, 但退出码契约相同
llm-budget-guard status --provider openrouter --warn-pct 70 --critical-pct 90
llm-budget-guard loop   --provider local     --interval 600 --webhook https://...
```

## 给 Claude Code 装上"quota 自检开关"

```bash
# ~/.claude/hooks/PreToolUse.sh
#!/usr/bin/env bash
case "$(llm-budget-guard status --provider minimax --warn-pct 60 --critical-pct 85)" in
  0) exit 0 ;;                                       # 健康，放行
  2) echo "⚠️ Token WARN, 降低并发" >&2; exit 0 ;;     # 警告，减速继续
  3) echo "🛑 Token CRITICAL, 等 5h 窗口重置" >&2; exit 1 ;;  # 严重，暂停任务
  *) echo "⚫ llm-budget-guard 调用失败" >&2; exit 0 ;;  # 探测失败, 降级手动
esac
```

从此每次 Claude Code 动手前都自己先问一句"还剩多少"。Cursor / Codex / 自研脚本同理——任何调 LLM 的 cron 都能挂上。

## 特性

- **跨厂商统一接口** ✅: 一个 `QuotaSnapshot` 模型 4 个 provider 适配；切换 `--provider` 即可
- **稳定的退出码契约** ✅: `0` 健康 / `1` 调用失败 / `2` WARN / `3` CRITICAL — 接 cron / CI / agent loop 直接读 `$?`
- **权威字段优先**: 用 `*_remaining_percent` 字段而不是 `total_count`, 规避 MiniMax Token Plan Plus 年付的 GitHub issue #48 total=0 bug
- **4 种告警通道**: stderr 日志 / 桌面 toast / 共享 flag 文件 (`~/.token_manager/warning.flag`) / webhook (Slack / 飞书 / Bark)
- **JSONL 历史**: `~/.token_manager/history.jsonl` 自动 rotation (50MB × 3 备份), `pandas.read_json(lines=True)` 直接分析趋势
- **OpenClaw skill**: 自带 `skills/token-monitor/`, 让任何 agent 工具直接调用

## 命令一览

| 命令 | 说明 | 退出码 |
|---|---|---|
| `llm-budget-guard providers` | 列出所有 provider + 它们需要的凭据 | 0 |
| `llm-budget-guard once` | 探测一次, 人类可读 | 0/1 |
| `llm-budget-guard once --json` | 同上, JSON 输出 | 0/1 |
| `llm-budget-guard status --warn-pct 80 --critical-pct 95` | 健康检查 | 0/1/2/3 |
| `llm-budget-guard loop --interval 300 --webhook URL` | 循环探测 + 写历史 + 超阈值告警 | 0 (Ctrl-C 退出) |
| `llm-budget-guard history --tail 20` | 最近 20 条历史 | 0 |
| `llm-budget-guard check` | 读 shared warning flag | 0 |

## 安装

```bash
pip install llm-budget-guard
```

或 latest from GitHub:
```bash
git clone https://github.com/me-speaker/token_manager.git
cd token_manager
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
```

### 配 `.env`

```bash
cp .env.example ~/.config/llm-budget-guard/.env  # or any .env in cwd
chmod 600 ~/.config/llm-budget-guard/.env
```

按你用的 provider 填一行 (其他可以空着):
```bash
# 默认 minimax
SUBSCRIPTION_KEY=***
# 国内用户加: TOKEN_PLAN_BASE_URL=https://api.minimaxi.com

# 或切到 OpenRouter
# OPENROUTER_API_KEY=sk-or-***

# 或切到自部署 LiteLLM proxy
# LITELLM_PROXY_URL=http://localhost:4000
# LITELLM_BUDGET_USD=100.0

# 或完全自记 (任何 LLM 厂商都行)
# LOCAL_JSONL_PATH=/var/log/llm_usage.jsonl
# LOCAL_BUDGET_USD=50.0
```

跑 `llm-budget-guard providers` 看完整 provider 列表 + 它们的 `.env` 字段。

## 项目结构

```
token_manager/                    # ← Python module 名 (历史遗留)
├── __init__.py                   # 公开 API (Provider / QuotaSnapshot / make_provider)
├── provider.py                   # Provider 协议 + QuotaSnapshot / QuotaWindow
├── providers/                    # 4 个 adapter, 横向扩展
│   ├── minimax.py                # 双 schema 归一化, 5h 滚动 + 周窗口
│   ├── openrouter.py             # GET /credits 单窗口
│   ├── litellm.py                # GET /spend/logs 月度聚合
│   └── local_jsonl.py            # tail 自记日志
├── cli.py                        # 7 子命令: providers / init / once / status / loop / history / check
├── monitor.py                    # 持续循环 + 双阈值判定
├── history.py                    # JSONL 落盘 + rotation
├── alerts.py                     # Notifier 协议 + 4 通道实现
├── config.py                     # Settings (pydantic-settings, 跨厂商凭据)
├── exceptions.py                 # AuthError / APIError / TransportError
└── models.py                     # minimax 内部数据模型 (其他 provider 不依赖)
tests/                            # 101 测试, 覆盖 4 provider 双 schema
main.py                           # 项目入口
skills/token-monitor/             # OpenClaw / Claude Code skill 套件
docs/                             # local-jsonl-format.md + show-hn-draft + wechat-article
.github/workflows/ci.yml          # ruff + pytest (3.10~3.12) + PyPI Trusted Publishing
```

## ⚙️ 双阈值 `status` 退出码契约

```bash
$ llm-budget-guard status --provider minimax --warn-pct 50 --critical-pct 80
[minimax] 使用率: 56.0% (WARN≥50.0% / CRITICAL≥80.0%)
[WARN] 已用 56.0%, 超过警告阈值 50.0%
$ echo $?  # → 2
```

| exit | 含义 | agent 决策 |
|---|---|---|
| **0** | 健康 (用率 < warn) | 继续 |
| **1** | 调用失败 (auth / 网络 / 上游 schema 错) | 降级手动 |
| **2** | WARN (用率 ≥ warn) | 降速继续 |
| **3** | CRITICAL (用率 ≥ critical) | 暂停任务等窗口重置 |

## 开发

```bash
# 跑测试
pytest                                           # 101 测试
pytest --cov=token_manager --cov-report=term-missing   # 覆盖率

# Lint
ruff check .

# 本地构建
pip install build
python -m build
ls dist/

# 发版 (PyPI Trusted Publishing 自动)
git tag -a v0.X.Y -m "..."   # 注意: tag 之前确保 [project].version = "0.X.Y"
git push --tags              # CI 自动 publish
```

修改 `client.py` / `models.py` 时,务必先用 dummy key 跑一次 `python main.py once`, 观察真实输出 (`pytest-httpx` mock 抓不到真实 schema 偏差)。

## 安全

⚠️ 任何 provider 凭据都是真账户凭证:

- **MiniMax Subscription Key**: 能消耗整个 Token Plan 配额
- **OpenRouter API Key**: 能消耗账户 credits
- **LiteLLM Master Key**: 看用量, 小心泄漏
- **local JSONL 路径**: 可能含内部用量数据

通用规则:
- 用 `.env` 管理, `chmod 600`, **永远不要** commit 到 git
- **不要**贴到 IM / issue / PR / 公开聊天
- 怀疑泄露立刻去对应控制台"**重置 Key**", 旧 key 立即作废

## 相关文档

- `docs/local-jsonl-format.md` — `local` provider JSONL 数据格式
- `docs/wechat-article.md` — 公众号文章草稿
- `docs/show-hn-draft.md` — Show HN 帖子草稿 (英文 + 知乎/V2EX 中文版)

## License

MIT — see [LICENSE](./LICENSE).
