forked from retoor/devplacepy
feat: add issue_usage tracking and metrics for AI ticket enhancement and planning
Add `issue_usage` table with columns for user_uid, tokens, cost, and latency metrics, including a unique index on user_uid. Wire `accumulate_usage` into `enhance_ticket` and `generate_plan` to capture per-request AI usage, persist totals via `add_issue_usage` in both `IssueCreateService` and `PlanningReportService`, and expose aggregated usage as metric cards through `IssueTrackerService.collect_metrics`. Update planning API docs summary to reflect the new phased implementation document format.
This commit is contained in:
@@ -284,6 +284,45 @@ def parse_usage_headers(headers) -> Optional[dict]:
|
||||
}
|
||||
|
||||
|
||||
USAGE_FIELDS = (
|
||||
"calls",
|
||||
"prompt_tokens",
|
||||
"completion_tokens",
|
||||
"total_tokens",
|
||||
"cost_usd",
|
||||
"upstream_latency_ms",
|
||||
"total_latency_ms",
|
||||
)
|
||||
|
||||
|
||||
def new_usage_totals() -> dict:
|
||||
return {field: 0 for field in USAGE_FIELDS}
|
||||
|
||||
|
||||
def accumulate_usage(totals: Optional[dict], response) -> None:
|
||||
if totals is None:
|
||||
return
|
||||
parsed = parse_usage_headers(getattr(response, "headers", None))
|
||||
if not parsed:
|
||||
return
|
||||
for field in USAGE_FIELDS:
|
||||
totals[field] += parsed[field]
|
||||
|
||||
|
||||
def usage_metric_cards(usage: dict) -> list[dict]:
|
||||
return [
|
||||
{"label": "AI calls", "value": usage["calls"]},
|
||||
{"label": "Total tokens", "value": usage["total_tokens"]},
|
||||
{"label": "Prompt tokens", "value": usage["prompt_tokens"]},
|
||||
{"label": "Completion tokens", "value": usage["completion_tokens"]},
|
||||
{"label": "Total cost", "value": f"${usage['cost_usd']:.4f}"},
|
||||
{"label": "Avg tokens/call", "value": usage["avg_tokens"]},
|
||||
{"label": "Avg cost/call", "value": f"${usage['avg_cost_usd']:.6f}"},
|
||||
{"label": "Avg latency", "value": f"{usage['avg_upstream_latency_ms']:.0f}ms"},
|
||||
{"label": "Avg tokens/sec", "value": usage["avg_tokens_per_second"]},
|
||||
]
|
||||
|
||||
|
||||
class GatewayUsageLedger:
|
||||
def record(self, raw: dict, pricing: Pricing, context_map: dict) -> Optional[dict]:
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user