diff --git a/alembic/versions/comparison_token_cols.py b/alembic/versions/comparison_token_cols.py new file mode 100644 index 0000000..84a9953 --- /dev/null +++ b/alembic/versions/comparison_token_cols.py @@ -0,0 +1,37 @@ +"""add input_tokens / output_tokens to comparison_record + +比价记录增加本次 LLM 累计 token(server 从 llm_calls[].usage 累加), +供 admin 比价记录页展示 token 数与估算成本。 + +Revision ID: comparison_token_cols +Revises: feedback_review_fields +Create Date: 2026-06-23 00:40:00.000000 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = "comparison_token_cols" +down_revision: Union[str, Sequence[str], None] = "feedback_review_fields" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.add_column( + "comparison_record", + sa.Column("input_tokens", sa.Integer(), nullable=True), + ) + op.add_column( + "comparison_record", + sa.Column("output_tokens", sa.Integer(), nullable=True), + ) + + +def downgrade() -> None: + op.drop_column("comparison_record", "output_tokens") + op.drop_column("comparison_record", "input_tokens") diff --git a/app/admin/schemas/comparison.py b/app/admin/schemas/comparison.py index 2fef36d..19b93bb 100644 --- a/app/admin/schemas/comparison.py +++ b/app/admin/schemas/comparison.py @@ -33,6 +33,8 @@ class AdminComparisonListItem(BaseModel): step_count: int | None = None llm_call_count: int | None = None retry_count: int | None = None + input_tokens: int | None = None # Σ usage.prompt_tokens(server 派生) + output_tokens: int | None = None # Σ usage.completion_tokens(server 派生) device_model: str | None = None rom_vendor: str | None = None rom_name: str | None = None diff --git a/app/api/v1/compare_record.py b/app/api/v1/compare_record.py index d4b1a03..b3f52b6 100644 --- a/app/api/v1/compare_record.py +++ b/app/api/v1/compare_record.py @@ -77,8 +77,15 @@ def _backfill_llm_calls(record_id: int, trace_id: str) -> None: rec.llm_calls = calls rec.llm_call_count = len(calls) rec.retry_count = sum(1 for c in calls if c.get("error")) + # token 累加(usage 已被 pricebot llm_client 归一为 prompt/completion_tokens; + # error 的调用 usage 可能为 None,or {} 兜底) + rec.input_tokens = sum((c.get("usage") or {}).get("prompt_tokens") or 0 for c in calls) + rec.output_tokens = sum((c.get("usage") or {}).get("completion_tokens") or 0 for c in calls) db.commit() - logger.info("backfill llm_calls trace=%s n=%d", trace_id, len(calls)) + logger.info( + "backfill llm_calls trace=%s n=%d in_tok=%d out_tok=%d", + trace_id, len(calls), rec.input_tokens, rec.output_tokens, + ) except Exception as e: # noqa: BLE001 best-effort logger.warning("backfill llm_calls failed trace=%s: %s", trace_id, e) finally: diff --git a/app/models/comparison.py b/app/models/comparison.py index 6924510..a8b24d9 100644 --- a/app/models/comparison.py +++ b/app/models/comparison.py @@ -124,6 +124,9 @@ class ComparisonRecord(Base): step_count: Mapped[int | None] = mapped_column(Integer, nullable=True) # 客户端 agent loop 总步数 llm_call_count: Mapped[int | None] = mapped_column(Integer, nullable=True) # 本次 LLM 调用次数(server 从 llm_calls 算) retry_count: Mapped[int | None] = mapped_column(Integer, nullable=True) # LLM 失败重试次数(server 从 llm_calls error 算) + # 本次 LLM 累计 token(server 从 llm_calls[].usage 累加;旧记录/未采集为 None) + input_tokens: Mapped[int | None] = mapped_column(Integer, nullable=True) # Σ usage.prompt_tokens + output_tokens: Mapped[int | None] = mapped_column(Integer, nullable=True) # Σ usage.completion_tokens # 每次 LLM 调用明细 [{scene,model,input_messages,output,usage,latency_ms,error}]; # server 收上报后按 trace_id 同机拉 pricebot 落库(见 compare_record 端点)。旧记录/未采集为 None。 llm_calls: Mapped[list | None] = mapped_column(_JSON, nullable=True)