Skip to content

Commit b1530cc

Browse files
committed
ENG-2922 Show token usage in agent progress at all verbosity levels
Display input/output/total tokens inline on each step line and aggregate totals in the completion summary. Also includes curl commands documenting backend token reporting inconsistencies. Made-with: Cursor
1 parent dd1b667 commit b1530cc

1 file changed

Lines changed: 52 additions & 3 deletions

File tree

aixplain/v2/agent_progress.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,9 @@ def _format_step_line(
437437
step_line += f" · API {api_calls:2d}"
438438
step_credits = step.get("used_credits") or step.get("usedCredits") or 0
439439
step_line += f" · ${step_credits:.6f}"
440+
token_text = self._format_token_usage_inline(step)
441+
if token_text:
442+
step_line += f" · ⊘ {token_text}"
440443

441444
step_line += f" · {agent_action_part}"
442445
return step_line
@@ -588,35 +591,65 @@ def _print_step_details(self, step: Dict, idx: int) -> None:
588591
else:
589592
print(f" {self._format_multiline(str(output_data))}")
590593

594+
def _print_token_usage(self, step: Dict) -> None:
595+
"""Print token usage for a step if available."""
596+
text = self._format_token_usage_inline(step)
597+
if text:
598+
print(f" ⊘ Tokens: {text}")
599+
600+
def _format_token_usage_inline(self, step: Dict) -> str:
601+
"""Format token usage as a compact inline string, or empty if unavailable."""
602+
input_tok = step.get("input_tokens")
603+
output_tok = step.get("output_tokens")
604+
total_tok = step.get("total_tokens")
605+
606+
if input_tok is None and output_tok is None and total_tok is None:
607+
return ""
608+
609+
parts = []
610+
if input_tok is not None:
611+
parts.append(f"in={input_tok}")
612+
if output_tok is not None:
613+
parts.append(f"out={output_tok}")
614+
if total_tok is not None:
615+
parts.append(f"total={total_tok}")
616+
return " · ".join(parts)
617+
591618
def _print_completion_message(self, status: str, steps: List[Dict]) -> None:
592619
"""Print final completion message with stats."""
593620
total_steps = len(steps) if steps else 0
594621
total_elapsed = (self._now() - self._total_start_time) if self._total_start_time else 0
595622

596623
prefix = "\n" if self._format == ProgressFormat.STATUS else ""
597624

625+
token_suffix = ""
626+
total_input = getattr(self, "_total_input_tokens", 0)
627+
total_output = getattr(self, "_total_output_tokens", 0)
628+
if total_input or total_output:
629+
token_suffix = f" · ⊘ {total_input}{total_output} tokens"
630+
598631
if status == "SUCCESS":
599632
print(
600633
f"{prefix}✓ Completed {total_steps} steps · "
601634
f"⏱ {self._format_elapsed(total_elapsed)} · "
602635
f"API {self._total_api_calls} · "
603-
f"${self._total_credits:.6f}"
636+
f"${self._total_credits:.6f}{token_suffix}"
604637
)
605638
elif status in {"FAILED", "ABORTED", "CANCELLED", "ERROR"}:
606639
print(
607640
f"{prefix}✗ Agent failed with status: {status} · "
608641
f"{total_steps} steps · "
609642
f"⏱ {self._format_elapsed(total_elapsed)} · "
610643
f"API {self._total_api_calls} · "
611-
f"${self._total_credits:.6f}"
644+
f"${self._total_credits:.6f}{token_suffix}"
612645
)
613646
else:
614647
print(
615648
f"{prefix}⏸ Stopped: reached max polling limit ({self.max_polls}) · "
616649
f"{total_steps} steps · "
617650
f"⏱ {self._format_elapsed(total_elapsed)} · "
618651
f"API {self._total_api_calls} · "
619-
f"${self._total_credits:.6f}"
652+
f"${self._total_credits:.6f}{token_suffix}"
620653
)
621654

622655
# =========================================================================
@@ -667,6 +700,8 @@ def _update_metrics(self, steps: List[Dict]) -> None:
667700
"""Update tracking metrics from steps data."""
668701
self._total_credits = 0.0
669702
self._total_api_calls = 0
703+
self._total_input_tokens = 0
704+
self._total_output_tokens = 0
670705
for idx, s in enumerate(steps):
671706
sid = s.get("_progress_id")
672707
if sid not in self._first_seen:
@@ -681,6 +716,20 @@ def _update_metrics(self, steps: List[Dict]) -> None:
681716
if api_calls:
682717
self._total_api_calls += int(api_calls)
683718

719+
input_tokens = s.get("input_tokens")
720+
if input_tokens is not None:
721+
try:
722+
self._total_input_tokens += int(input_tokens)
723+
except (ValueError, TypeError):
724+
pass
725+
726+
output_tokens = s.get("output_tokens")
727+
if output_tokens is not None:
728+
try:
729+
self._total_output_tokens += int(output_tokens)
730+
except (ValueError, TypeError):
731+
pass
732+
684733
def _display_logs_format(self, steps: List[Dict]) -> None:
685734
"""Handle display for LOGS format (event timeline)."""
686735
for idx, step in enumerate(steps):

0 commit comments

Comments
 (0)