Files
mini-openclaw-v2/backend/api/tokens.py
T
qiaoqichaoandClaude Code d56c699ce6 chore(v2): 从兴邦预审v2白名单抽取独立可运行基座
按 docs/v2/05-implementation-plan.md §1 候选迁移表与 claude_use/T00 白名单,
从 55e2cdb 抽取 224 个文件(2.48 MB)到独立目录 F:/mini-OpenClaw-v2。

- 排除:凭据(.env/server-deploy.conf/旧DELIVERY.md)、运行数据(xingbang_data 等
  4 个 SQLite)、依赖(venv/node_modules 符号链接)、归档(_backup*/test-evidence/
  ori_file)、Office 临时文件
- 目标目录独立 git 仓库,初始恢复点;Gitea 远程与分支待 OQ-06 Q2 定案
- 未部署、未启动服务、未调用模型

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-29 12:58:26 +08:00

69 lines
2.1 KiB
Python

"""GET/POST /api/tokens — Token counting for sessions and files."""
from pathlib import Path
from typing import Any
import tiktoken
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from graph.session_manager import session_manager
from graph.prompt_builder import build_system_prompt
router = APIRouter()
BASE_DIR = Path(__file__).resolve().parent.parent
# Cache the encoder instance
_encoder = tiktoken.get_encoding("cl100k_base")
def _count_tokens(text: str) -> int:
"""Count tokens using cl100k_base encoding."""
return len(_encoder.encode(text))
@router.get("/tokens/session/{session_id}")
async def get_session_token_count(session_id: str) -> dict[str, Any]:
"""Count tokens in a session: system prompt + all messages."""
system_prompt = build_system_prompt(BASE_DIR)
system_tokens = _count_tokens(system_prompt)
messages = session_manager.load_session(session_id)
message_tokens = 0
for msg in messages:
message_tokens += _count_tokens(msg.get("content", ""))
return {
"system_tokens": system_tokens,
"message_tokens": message_tokens,
"total_tokens": system_tokens + message_tokens,
}
class FileTokenRequest(BaseModel):
paths: list[str]
@router.post("/tokens/files")
async def get_file_token_counts(request: FileTokenRequest) -> dict[str, Any]:
"""Count tokens for a list of files."""
results: list[dict[str, Any]] = []
for rel_path in request.paths:
normalized = rel_path.replace("\\", "/").lstrip("./")
full_path = (BASE_DIR / normalized).resolve()
if not str(full_path).startswith(str(BASE_DIR)):
results.append({"path": rel_path, "tokens": 0})
continue
if not full_path.exists():
results.append({"path": rel_path, "tokens": 0})
continue
try:
content = full_path.read_text(encoding="utf-8")
tokens = _count_tokens(content)
results.append({"path": rel_path, "tokens": tokens})
except Exception:
results.append({"path": rel_path, "tokens": 0})
return {"files": results}