Implement LLM-WP-0008: provider-scoped account balance CLI
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Add pluggable balance registry, OpenRouter credits/key limit client,
and llm-connect balance with one-shot --provider that does not change
library defaults.
This commit is contained in:
tegwick 2026-08-03 23:49:01 +02:00
parent c0d5c4ad08
commit ed7c632155
11 changed files with 783 additions and 14 deletions

View file

@ -10,7 +10,12 @@ from datetime import datetime
from pathlib import Path
from typing import Any
from llm_connect.balance import (
format_balance_human,
get_account_balance,
)
from llm_connect.costs import estimate_cost
from llm_connect.exceptions import LLMBalanceUnsupportedError, LLMConfigurationError, LLMError
from llm_connect.factory import create_adapter
from llm_connect.fx import resolve_fx_rate
from llm_connect.models import RunConfig
@ -106,6 +111,30 @@ def _build_parser() -> argparse.ArgumentParser:
spend_week.add_argument("--json", action="store_true", help="Emit JSON")
spend_week.set_defaults(func=_spend_week)
balance = commands.add_parser(
"balance",
help=(
"Show prepaid/account remaining for a backend "
"(default: current default provider; --provider is one-shot only)"
),
)
balance.add_argument(
"--provider",
default=None,
help=(
"Backend to query for this command only (does not change library defaults). "
"Omit to use the current default provider."
),
)
balance.add_argument("--json", action="store_true", help="Emit JSON")
balance.add_argument(
"--eur-per-usd",
type=float,
default=None,
help="Override FX rate (euros per one USD)",
)
balance.set_defaults(func=_balance_show)
return parser
@ -340,6 +369,27 @@ def _fmt_local(dt: datetime) -> str:
return dt.isoformat()
def _balance_show(args: argparse.Namespace) -> int:
try:
balance = get_account_balance(args.provider, fx=args.eur_per_usd)
except LLMBalanceUnsupportedError as exc:
print(str(exc), file=sys.stderr)
return 2
except LLMConfigurationError as exc:
print(str(exc), file=sys.stderr)
return 2
except LLMError as exc:
print(str(exc), file=sys.stderr)
return 1
if args.json:
print(json.dumps(balance.to_dict(), indent=2, sort_keys=True))
return 0
print(format_balance_human(balance))
return 0
def _classes_payload(classes: Iterable[ProblemClass]) -> dict[str, dict[str, Any]]:
return {
problem_class.name: {