OpenClaw Skills 模型使用成本统计(Model Usage)技能使用参考手册
2026-03-06 11:22 更新
概述
Model Usage 是用于从 CodexBar 的本地成本日志中获取按模型分类的使用成本的 OpenClaw 技能,该技能支持获取当前使用的模型(最近的每日记录)或所有模型的使用总结,适用于 Codex 和 Claude 模型,帮助用户统计和分析不同模型的使用成本和使用情况。
该技能可以帮助用户在 OpenClaw 中直接获取模型的使用成本数据,适合需要跟踪 AI 模型使用成本的场景,例如开发者、运维人员等,用户可以通过简单的命令行操作获取模型的使用总结,支持文本和 JSON 格式的输出。
技能信息
- 名称:model-usage
- 描述:通过 CodexBar CLI 本地成本使用情况来总结 Codex 或 Claude 的按模型使用情况,包括当前(最近使用的)模型或完整的模型使用明细。当需要从 codexbar 获取模型级别的使用 / 成本数据,或者需要从 codexbar 成本 JSON 中获取可脚本化的按模型总结时触发该技能。
- 版本:1.0.0
- 作者:steipete(Peter Steinberger)
- 依赖:
- 需要 macOS 系统(目前仅支持 darwin 系统,Linux 支持将在 CodexBar CLI 的 Linux 安装路径文档完成后添加)
- 需要安装 CodexBar CLI 工具
- 触发词:"模型使用成本"、"Codex 模型使用"、"Claude 模型成本"、"模型使用统计"、"CodexBar 成本分析"
👤 作者:Peter Steinberger
🦞 官方地址:https://clawhub.ai/steipete/model-usage
👉 Skills 下载地址:model-usage-1.0.0.zip
安装方法
通过 Brew 安装 CodexBar CLI
在你的 macOS 系统中通过 brew 安装 CodexBar CLI 工具:
brew install steipete/tap/codexbar
快速开始
- 通过 CodexBar CLI 获取成本 JSON 数据,或者直接传入 JSON 文件。
- 使用附带的脚本按模型总结使用情况:
python {baseDir}/scripts/model_usage.py --provider codex --mode current
python {baseDir}/scripts/model_usage.py --provider codex --mode all
python {baseDir}/scripts/model_usage.py --provider claude --mode all --format json --pretty
当前模型逻辑
- 使用包含
modelBreakdowns的最近每日记录行。 - 选择该行中成本最高的模型。
- 当缺少明细数据时,回退到
modelsUsed中的最后一个条目。 - 当需要指定特定模型时,可以使用
--model <name>参数进行覆盖。
输入方式
- 默认方式:直接运行
codexbar cost --format json --provider <codex|claude>获取数据。 - 文件或标准输入方式:
codexbar cost --provider codex --format json > /tmp/cost.json
python {baseDir}/scripts/model_usage.py --input /tmp/cost.json --mode all
cat /tmp/cost.json | python {baseDir}/scripts/model_usage.py --input - --mode current
输出格式
- 文本格式(默认)或 JSON 格式(使用
--format json --pretty参数)。 - 输出值为每个模型的成本数据;CodexBar 输出中未按模型拆分 token 数据。
CodexBar CLI 参考
安装
- 通过应用安装:进入应用偏好设置 -> 高级 -> 安装 CLI
- 通过仓库安装:运行
./bin/install-codexbar-cli.sh
命令
- 使用情况快照(支持网页 / CLI 数据源):
codexbar usage --format json --pretty
codexbar --provider all --format json
- 本地成本使用情况(仅支持 Codex + Claude):
codexbar cost --format json --pretty
codexbar cost --provider codex|claude --format json
成本 JSON 字段
返回的数据是一个数组(每个提供商对应一个条目),包含以下字段:
- provider, source, updatedAt
- sessionTokens, sessionCostUSD
- last30DaysTokens, last30DaysCostUSD
- daily[]: date, inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens, totalTokens, totalCost, modelsUsed, modelBreakdowns[]
- modelBreakdowns[]: modelName, cost
- totals: totalInputTokens, totalOutputTokens, cacheReadTokens, cacheCreationTokens, totalTokens, totalCost
注意事项
- 成本使用情况仅支持本地数据,它会读取以下路径下的 JSONL 日志:
- Codex:
~/.codex/sessions/**/*.jsonl - Claude:
~/.config/claude/projects/**/*.jsonl or ~/.claude/projects/**/*.jsonl
- Codex:
- 如果需要使用网页使用情况(非本地),请使用
codexbar usage命令(而非cost命令)。
工具代码说明
model_usage.py(模型使用成本总结脚本)
#!/usr/bin/env python3
"""
从CodexBar本地成本日志中按模型总结使用情况。
默认获取当前模型(最近的每日记录),或列出所有模型。
"""
from __future__ import annotations
import argparse
import json
import os
import subprocess
import sys
from dataclasses import dataclass
from datetime import date, datetime, timedelta
from typing import Any, Dict, Iterable, List, Optional, Tuple
def eprint(msg: str) -> None:
"""将消息打印到标准错误输出"""
print(msg, file=sys.stderr)
def run_codexbar_cost(provider: str) -> List[Dict[str, Any]]:
"""
运行codexbar cost命令获取成本数据
参数:
provider: 模型提供商,可选值为codex或claude
返回:
codexbar返回的JSON数据列表
"""
cmd = ["codexbar", "cost", "--format", "json", "--provider", provider]
try:
output = subprocess.check_output(cmd, text=True)
except FileNotFoundError:
raise RuntimeError("codexbar未在PATH中找到,请先安装CodexBar CLI。")
except subprocess.CalledProcessError as exc:
raise RuntimeError(f"codexbar cost命令执行失败(退出码 {exc.returncode})。")
try:
payload = json.loads(output)
except json.JSONDecodeError as exc:
raise RuntimeError(f"解析codexbar JSON输出失败: {exc}")
if not isinstance(payload, list):
raise RuntimeError("期望codexbar cost返回JSON数组。")
return payload
def load_payload(input_path: Optional[str], provider: str) -> Dict[str, Any]:
"""
加载成本数据负载
参数:
input_path: 输入文件路径,或"-"表示从标准输入读取
provider: 模型提供商,可选值为codex或claude
返回:
对应提供商的成本数据字典
"""
if input_path:
if input_path == "-":
raw = sys.stdin.read()
else:
with open(input_path, "r", encoding="utf-8") as handle:
raw = handle.read()
data = json.loads(raw)
else:
data = run_codexbar_cost(provider)
if isinstance(data, dict):
return data
if isinstance(data, list):
for entry in data:
if isinstance(entry, dict) and entry.get("provider") == provider:
return entry
raise RuntimeError(f"在codexbar数据中未找到提供商'{provider}'。")
raise RuntimeError("不支持的JSON输入格式。")
@dataclass
class ModelCost:
"""模型成本数据类"""
model: str
cost: float
def parse_daily_entries(payload: Dict[str, Any]) -> List[Dict[str, Any]]:
"""
解析每日记录条目
参数:
payload: 成本数据负载
返回:
有效的每日记录条目列表
"""
daily = payload.get("daily")
if not daily:
return []
if not isinstance(daily, list):
return []
return [entry for entry in daily if isinstance(entry, dict)]
def parse_date(value: str) -> Optional[date]:
"""
解析日期字符串
参数:
value: 日期字符串,格式为%Y-%m-%d
返回:
解析后的date对象,解析失败则返回None
"""
try:
return datetime.strptime(value, "%Y-%m-%d").date()
except Exception:
return None
def filter_by_days(entries: List[Dict[str, Any]], days: Optional[int]) -> List[Dict[str, Any]]:
"""
按天数过滤记录条目
参数:
entries: 每日记录条目列表
days: 保留最近的天数
返回:
过滤后的记录条目列表
"""
if not days:
return entries
cutoff = date.today() - timedelta(days=days - 1)
filtered: List[Dict[str, Any]] = []
for entry in entries:
day = entry.get("date")
if not isinstance(day, str):
continue
parsed = parse_date(day)
if parsed and parsed >= cutoff:
filtered.append(entry)
return filtered
def aggregate_costs(entries: Iterable[Dict[str, Any]]) -> Dict[str, float]:
"""
汇总所有模型的成本
参数:
entries: 每日记录条目列表
返回:
按模型名称汇总的成本字典
"""
totals: Dict[str, float] = {}
for entry in entries:
breakdowns = entry.get("modelBreakdowns")
if not breakdowns:
continue
if not isinstance(breakdowns, list):
continue
for item in breakdowns:
if not isinstance(item, dict):
continue
model = item.get("modelName")
cost = item.get("cost")
if not isinstance(model, str):
continue
if not isinstance(cost, (int, float)):
continue
totals[model] = totals.get(model, 0.0) + float(cost)
return totals
def pick_current_model(entries: List[Dict[str, Any]]) -> Tuple[Optional[str], Optional[str]]:
"""
选择当前使用的模型
参数:
entries: 每日记录条目列表
返回:
(当前模型名称, 模型使用日期)
"""
if not entries:
return None, None
sorted_entries = sorted(
entries,
key=lambda entry: entry.get("date") or "",
)
for entry in reversed(sorted_entries):
breakdowns = entry.get("modelBreakdowns")
if isinstance(breakdowns, list) and breakdowns:
scored: List[ModelCost] = []
for item in breakdowns:
if not isinstance(item, dict):
continue
model = item.get("modelName")
cost = item.get("cost")
if isinstance(model, str) and isinstance(cost, (int, float)):
scored.append(ModelCost(model=model, cost=float(cost)))
if scored:
scored.sort(key=lambda item: item.cost, reverse=True)
return scored[0].model, entry.get("date") if isinstance(entry.get("date"), str) else None
models_used = entry.get("modelsUsed")
if isinstance(models_used, list) and models_used:
last = models_used[-1]
if isinstance(last, str):
return last, entry.get("date") if isinstance(entry.get("date"), str) else None
return None, None
def usd(value: Optional[float]) -> str:
"""
将数值格式化为美元金额字符串
参数:
value: 金额数值
返回:
格式化后的美元金额字符串
"""
if value is None:
return "—"
return f"${value:,.2f}"
def latest_day_cost(entries: List[Dict[str, Any]], model: str) -> Tuple[Optional[str], Optional[float]]:
"""
获取模型最近一天的成本
参数:
entries: 每日记录条目列表
model: 模型名称
返回:
(最近成本日期, 最近成本金额)
"""
if not entries:
return None, None
sorted_entries = sorted(
entries,
key=lambda entry: entry.get("date") or "",
)
for entry in reversed(sorted_entries):
breakdowns = entry.get("modelBreakdowns")
if not isinstance(breakdowns, list):
continue
for item in breakdowns:
if not isinstance(item, dict):
continue
if item.get("modelName") == model:
cost = item.get("cost") if isinstance(item.get("cost"), (int, float)) else None
day = entry.get("date") if isinstance(entry.get("date"), str) else None
return day, float(cost) if cost is not None else None
return None, None
def render_text_current(
provider: str,
model: str,
latest_date: Optional[str],
total_cost: Optional[float],
latest_cost: Optional[float],
latest_cost_date: Optional[str],
entry_count: int,
) -> str:
"""
渲染当前模型的文本输出
参数:
provider: 模型提供商
model: 模型名称
latest_date: 模型最近使用日期
total_cost: 模型总使用成本
latest_cost: 模型最近一天使用成本
latest_cost_date: 模型最近一天使用日期
entry_count: 记录条目数量
返回:
格式化的文本输出
"""
lines = [f"提供商: {provider}", f"当前模型: {model}"]
if latest_date:
lines.append(f"模型最近使用日期: {latest_date}")
lines.append(f"总使用成本 (记录行数): {usd(total_cost)}")
if latest_cost_date:
lines.append(f"最近一天使用成本: {usd(latest_cost)} ({latest_cost_date})")
lines.append(f"每日记录行数: {entry_count}")
return "\n".join(lines)
def render_text_all(provider: str, totals: Dict[str, float]) -> str:
"""
渲染所有模型的文本输出
参数:
provider: 模型提供商
totals: 按模型汇总的成本字典
返回:
格式化的文本输出
"""
lines = [f"提供商: {provider}", "模型列表:"]
for model, cost in sorted(totals.items(), key=lambda item: item[1], reverse=True):
lines.append(f"- {model}: {usd(cost)}")
return "\n".join(lines)
def build_json_current(
provider: str,
model: str,
latest_date: Optional[str],
total_cost: Optional[float],
latest_cost: Optional[float],
latest_cost_date: Optional[str],
entry_count: int,
) -> Dict[str, Any]:
"""
构建当前模型的JSON输出
参数:
provider: 模型提供商
model: 模型名称
latest_date: 模型最近使用日期
total_cost: 模型总使用成本
latest_cost: 模型最近一天使用成本
latest_cost_date: 模型最近一天使用日期
entry_count: 记录条目数量
返回:
JSON格式的输出数据
"""
return {
"provider": provider,
"mode": "current",
"model": model,
"latestModelDate": latest_date,
"totalCostUSD": total_cost,
"latestDayCostUSD": latest_cost,
"latestDayCostDate": latest_cost_date,
"dailyRowCount": entry_count,
}
def build_json_all(provider: str, totals: Dict[str, float]) -> Dict[str, Any]:
"""
构建所有模型的JSON输出
参数:
provider: 模型提供商
totals: 按模型汇总的成本字典
返回:
JSON格式的输出数据
"""
return {
"provider": provider,
"mode": "all",
"models": [
{"model": model, "totalCostUSD": cost}
for model, cost in sorted(totals.items(), key=lambda item: item[1], reverse=True)
],
}
def main() -> int:
"""主函数"""
parser = argparse.ArgumentParser(description="从本地成本日志中总结CodexBar模型使用情况。")
parser.add_argument("--provider", choices=["codex", "claude"], default="codex")
parser.add_argument("--mode", choices=["current", "all"], default="current")
parser.add_argument("--model", help="指定要报告的模型名称,替代自动选择的当前模型。")
parser.add_argument("--input", help="codexbar成本JSON文件路径(或'-'表示从标准输入读取)。")
parser.add_argument("--days", type=int, help="限制为最近N天的记录(基于每日记录行)。")
parser.add_argument("--format", choices=["text", "json"], default="text")
parser.add_argument("--pretty", action="store_true", help="格式化输出JSON。")
args = parser.parse_args()
try:
payload = load_payload(args.input, args.provider)
except Exception as exc:
eprint(str(exc))
return 1
entries = parse_daily_entries(payload)
entries = filter_by_days(entries, args.days)
if args.mode == "current":
model = args.model
latest_date = None
if not model:
model, latest_date = pick_current_model(entries)
if not model:
eprint("在codexbar成本数据中未找到模型数据。")
return 2
totals = aggregate_costs(entries)
total_cost = totals.get(model)
latest_cost_date, latest_cost = latest_day_cost(entries, model)
if args.format == "json":
payload_out = build_json_current(
provider=args.provider,
model=model,
latest_date=latest_date,
total_cost=total_cost,
latest_cost=latest_cost,
latest_cost_date=latest_cost_date,
entry_count=len(entries),
)
indent = 2 if args.pretty else None
print(json.dumps(payload_out, indent=indent, sort_keys=args.pretty))
else:
print(
render_text_current(
provider=args.provider,
model=model,
latest_date=latest_date,
total_cost=total_cost,
latest_cost=latest_cost,
latest_cost_date=latest_cost_date,
entry_count=len(entries),
)
)
return 0
totals = aggregate_costs(entries)
if not totals:
eprint("在codexbar成本数据中未找到模型明细。")
return 2
if args.format == "json":
payload_out = build_json_all(provider=args.provider, totals=totals)
indent = 2 if args.pretty else None
print(json.dumps(payload_out, indent=indent, sort_keys=args.pretty))
else:
print(render_text_all(provider=args.provider, totals=totals))
return 0
if __name__ == "__main__":
raise SystemExit(main())
元数据信息
该技能的元数据信息如下:
{
"ownerId": "kn70pywhg0fyz996kpa8xj89s57yhv26",
"slug": "model-usage",
"version": "1.0.0",
"publishedAt": 1767667639455
}以上内容是否对您有帮助:

免费 AI IDE


更多建议: