feat: bound AI-plan work to resource-control and stop zeroing missing tokens
Record the FIN-WP-0007 split after checking resource-control: they keep intelligence resource identity and provision-level class I metering; session tokens stay with State Hub; work-effectiveness is a reporting join. Missing token counts are now unknown, not zero.
This commit is contained in:
parent
004c041dc0
commit
09ef7fa967
10 changed files with 326 additions and 38 deletions
|
|
@ -13,14 +13,28 @@ from fin_hub.ingest._csv import parse_amount, pick, read_csv_rows
|
|||
class TokenSpendRow:
|
||||
provider: str
|
||||
model: str
|
||||
tokens_in: int
|
||||
tokens_out: int
|
||||
tokens_in: int | None
|
||||
tokens_out: int | None
|
||||
cost: float
|
||||
currency: str
|
||||
session_id: str | None
|
||||
recorded_at: datetime
|
||||
|
||||
|
||||
def parse_optional_token_count(value: str) -> int | None:
|
||||
"""Parse a token quantity. Empty is unknown; 0 is measured none."""
|
||||
|
||||
if not value:
|
||||
return None
|
||||
try:
|
||||
count = int(value)
|
||||
except ValueError as error:
|
||||
raise ValueError(f"token count must be an integer, got {value!r}") from error
|
||||
if count < 0:
|
||||
raise ValueError(f"token count cannot be negative: {count}")
|
||||
return count
|
||||
|
||||
|
||||
def parse_anthropic_billing_csv(path: Path, *, default_currency: str = "USD") -> list[TokenSpendRow]:
|
||||
rows: list[TokenSpendRow] = []
|
||||
for row in read_csv_rows(path):
|
||||
|
|
@ -28,8 +42,12 @@ def parse_anthropic_billing_csv(path: Path, *, default_currency: str = "USD") ->
|
|||
cost_raw = pick(row, "cost", "amount", "total_cost", "usage_cost_usd")
|
||||
if not model or not cost_raw:
|
||||
continue
|
||||
tokens_in = int(pick(row, "input_tokens", "tokens_in", "prompt_tokens") or "0")
|
||||
tokens_out = int(pick(row, "output_tokens", "tokens_out", "completion_tokens") or "0")
|
||||
tokens_in = parse_optional_token_count(
|
||||
pick(row, "input_tokens", "tokens_in", "prompt_tokens")
|
||||
)
|
||||
tokens_out = parse_optional_token_count(
|
||||
pick(row, "output_tokens", "tokens_out", "completion_tokens")
|
||||
)
|
||||
recorded_raw = pick(row, "date", "usage_date", "recorded_at", "timestamp")
|
||||
recorded_at = (
|
||||
datetime.fromisoformat(recorded_raw.replace("Z", "+00:00"))
|
||||
|
|
|
|||
|
|
@ -69,8 +69,8 @@ class TokenSpend(Base, TimestampMixin):
|
|||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
provider: Mapped[str] = mapped_column(String(32), nullable=False, index=True)
|
||||
model: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
tokens_in: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||
tokens_out: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||
tokens_in: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
||||
tokens_out: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
||||
cost: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
|
||||
session_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
||||
recorded_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue