Files
kwcode/kaiwu/telemetry/client.py
Val-sss ca55970ad8 release: v1.6.2 版本号统一 + README/CHANGELOG/STATUS更新
- 版本号统一到1.6.2(pyproject/__init__/formatters/telemetry/server)
- README: badge 1.6.2, License Apache-2.0, 更新日志加v1.6.2条目
- CHANGELOG: 完整v1.6.2变更记录
- STATUS: 当前版本更新到v1.6.2

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 14:42:21 +08:00

106 lines
2.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
匿名遥测客户端。
默认关闭,用户 opt-in 后只上传行为元数据:
- error_type枚举值
- retry_count数字
- successbool
- model模型名称
绝不上传代码内容、文件路径、任务描述、用户身份信息。
"""
import hashlib
import hmac
import json
import logging
import threading
from pathlib import Path
import httpx
import yaml
logger = logging.getLogger(__name__)
TELEMETRY_URL = "https://llmbbs.com/api/v1/event"
CONFIG_PATH = Path.home() / ".kwcode" / "config.yaml"
try:
from importlib.metadata import version as _pkg_version
VERSION = _pkg_version("kwcode")
except Exception:
VERSION = "1.6.2"
_HMAC_SECRET = b"kwcode-telemetry-2026-v1"
class TelemetryClient:
"""
Fire-and-forget 匿名遥测。
读取 ~/.kwcode/config.yaml 的 telemetry_enabled 字段。
缺失该字段 = 关闭(向后兼容)。
"""
def __init__(self):
self._enabled = self._read_config()
def _read_config(self) -> bool:
try:
if CONFIG_PATH.exists():
config = yaml.safe_load(
CONFIG_PATH.read_text(encoding="utf-8")
) or {}
return bool(config.get("telemetry_enabled", False))
except Exception as e:
logger.debug("telemetry config read failed: %s", e)
return False
def is_enabled(self) -> bool:
return self._enabled
def reload(self):
"""重新读取配置(用于 enable/disable 后刷新)。"""
self._enabled = self._read_config()
def report(
self,
error_type: str,
retry_count: int,
success: bool,
model: str,
):
"""
Fire-and-forget 上传匿名统计。
daemon thread + 3s 超时,绝不阻塞主流程。
HMAC-SHA256 签名防伪造。
"""
if not self._enabled:
return
payload = {
"error_type": error_type or "unknown",
"retry_count": retry_count,
"success": success,
"model": model or "unknown",
"version": VERSION,
}
thread = threading.Thread(
target=self._upload,
args=(payload,),
daemon=True,
)
thread.start()
def _upload(self, payload: dict):
try:
body = json.dumps(payload, separators=(",", ":"), sort_keys=True).encode()
sig = hmac.new(_HMAC_SECRET, body, hashlib.sha256).hexdigest()
httpx.post(
TELEMETRY_URL,
content=body,
headers={
"Content-Type": "application/json",
"X-KWCode-Sig": sig,
},
timeout=3.0,
)
except Exception as e:
logger.debug("telemetry upload failed (non-blocking): %s", e)