Skip to content

refactor(token): use nullish coalescing - #861

Merged
steebchen merged 1 commit into
mainfrom
terragon/refactor-token-zero-values
Sep 16, 2025
Merged

steebchen merged 1 commit into
mainfrom
terragon/refactor-token-zero-values

Conversation

@steebchen

@steebchen steebchen commented Sep 16, 2025

Copy link
Copy Markdown
Member

Summary

  • Refactored token extraction logic in extractTokenUsage to use the nullish coalescing operator (??) instead of logical OR (||)
  • Ensures zero values for tokens are correctly handled and not replaced by null

Changes

Token Extraction Logic

  • Updated token assignments for Google AI Studio, Anthropic, and OpenAI providers:
    • Replaced || null with ?? null to correctly handle zero token counts
    • Updated total token calculations to use ?? 0 instead of || 0 for accurate summation

Test plan

  • Verified that token counts of zero are preserved and not converted to null
  • Confirmed total token calculations include zero values correctly
  • Ran existing tests to ensure no regressions in token usage extraction logic

🌿 Generated by Terry


ℹ️ Tag @terragon-labs to ask questions and address PR feedback

📎 Task: https://www.terragonlabs.com/task/8462a22d-8a2c-43d2-9427-a73697314921

Summary by CodeRabbit

  • Bug Fixes

    • Improved accuracy of token usage reporting across providers, preventing incorrect fallbacks and ensuring totals are calculated reliably.
    • More consistent handling of missing token metrics (e.g., prompt, completion, reasoning, cached), reducing ambiguous or misleading values.
  • Refactor

    • Standardized defaulting behavior for token metrics to provide clearer, predictable results without altering the public interface.

…r token counts

Replace all instances of logical OR (||) with nullish coalescing (??) when assigning token count variables in extractTokenUsage function. This ensures that zero values are correctly handled and only null or undefined values fall back to null or zero defaults.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Sep 16, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Updated extract-token-usage to use nullish coalescing (??) instead of logical OR (||) when assigning token metrics across provider branches (Google AI Studio, Anthropic, OpenAI/default). Adjusted total token calculations to sum nullable components where applicable. No public API/signature changes.

Changes

Cohort / File(s) Summary
Token usage extraction
apps/gateway/src/chat/tools/extract-token-usage.ts
Replaced

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "refactor(token): use nullish coalescing" is concise, follows conventional commit style, and accurately summarizes the primary change (switching token defaulting from
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch terragon/refactor-token-zero-values

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot changed the title Refactor token extraction to use nullish coalescing operator refactor(token): use nullish coalescing Sep 16, 2025
@steebchen
steebchen marked this pull request as ready for review September 16, 2025 16:01

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/gateway/src/chat/tools/extract-token-usage.ts (1)

27-42: Bug: totalTokens can be stale when completionTokens is estimated.

You compute totalTokens before the estimation branch; if estimation runs, totalTokens doesn’t include the estimated completionTokens.

Apply this diff to compute total after the possible estimation:

-        // Calculate total including reasoning tokens
-        totalTokens =
-          (promptTokens ?? 0) +
-          (completionTokens ?? 0) +
-          (reasoningTokens ?? 0);
-
         // If candidatesTokenCount is missing and we have content, estimate it
         if (completionTokens === null && fullContent) {
           const estimation = estimateTokens(
             provider,
             [],
             fullContent,
             null,
             null,
           );
           completionTokens = estimation.calculatedCompletionTokens;
         }
+        // Calculate total including reasoning tokens (after potential estimation)
+        totalTokens =
+          (promptTokens ?? 0) +
+          (completionTokens ?? 0) +
+          (reasoningTokens ?? 0);
🧹 Nitpick comments (4)
apps/gateway/src/chat/tools/extract-token-usage.ts (4)

47-51: Anthropic totals: confirm whether to include reasoning and cache tokens.

  • totalTokens currently excludes reasoningTokens; for parity with Google’s path you may want to include them.
  • cachedTokens only captures cache_read_input_tokens; decide if you also want cache_creation_input_tokens represented.

If inclusion is desired, minimally:

-        totalTokens = (promptTokens ?? 0) + (completionTokens ?? 0);
+        // Include reasoning tokens if present for parity
+        totalTokens =
+          (promptTokens ?? 0) +
+          (completionTokens ?? 0) +
+          (reasoningTokens ?? 0);

If you prefer to expose both cache components without breaking the shape, consider adding an extra optional field (e.g., cacheCreationTokens) in a separate change.


56-61: OpenAI: add a safe fallback when total_tokens is absent.

Some responses omit total_tokens; compute it from parts to avoid null totals.

         if (data.usage) {
           promptTokens = data.usage.prompt_tokens ?? null;
           completionTokens = data.usage.completion_tokens ?? null;
           totalTokens = data.usage.total_tokens ?? null;
           reasoningTokens = data.usage.reasoning_tokens ?? null;
           cachedTokens = data.usage.prompt_tokens_details?.cached_tokens ?? null;
+          // Fallback if provider omitted total
+          if (totalTokens == null) {
+            totalTokens =
+              (promptTokens ?? 0) +
+              (completionTokens ?? 0) +
+              (reasoningTokens ?? 0);
+          }
         }

13-17: Add explicit types to avoid implicit any/null widening.

Make intent clear and keep strict typing.

-  let promptTokens = null;
-  let completionTokens = null;
-  let totalTokens = null;
-  let reasoningTokens = null;
-  let cachedTokens = null;
+  let promptTokens: number | null = null;
+  let completionTokens: number | null = null;
+  let totalTokens: number | null = null;
+  let reasoningTokens: number | null = null;
+  let cachedTokens: number | null = null;

9-11: Avoid any for input payload; consider narrowing per provider.

Typing data as a discriminated union (per provider) or using type guards will prevent silent shape drift.

I can draft minimal types for Google AI Studio, Anthropic, and OpenAI usage blocks plus narrowers if you want.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de86938 and f9f0a3b.

📒 Files selected for processing (1)
  • apps/gateway/src/chat/tools/extract-token-usage.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use top-level import; never use require() or dynamic imports

Files:

  • apps/gateway/src/chat/tools/extract-token-usage.ts
{apps/{api,gateway}/src,packages/db}/**/*.ts?(x)

📄 CodeRabbit inference engine (CLAUDE.md)

Use Drizzle ORM with the latest object syntax

Files:

  • apps/gateway/src/chat/tools/extract-token-usage.ts
apps/{api,gateway}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

For read queries, use db().query.

.findMany() or db().query.
.findFirst()

Files:

  • apps/gateway/src/chat/tools/extract-token-usage.ts
{apps/api,apps/gateway,apps/ui,apps/docs,packages}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Always use top-level import; never use require() or dynamic imports (e.g., import(), next/dynamic)

Files:

  • apps/gateway/src/chat/tools/extract-token-usage.ts
{apps/api,apps/gateway,packages/db}/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

{apps/api,apps/gateway,packages/db}/**/*.ts: Use Drizzle ORM with the latest object syntax for database access
For reads, use db().query.

.findMany() or db().query.
.findFirst()

Files:

  • apps/gateway/src/chat/tools/extract-token-usage.ts
🔇 Additional comments (1)
apps/gateway/src/chat/tools/extract-token-usage.ts (1)

22-26: Good switch to nullish coalescing — preserves zero-token counts.

Using ?? avoids collapsing 0 to null and matches the PR goal. No issues here.

Also applies to: 28-30

@steebchen
steebchen added this pull request to the merge queue Sep 16, 2025
Merged via the queue into main with commit 1b78864 Sep 16, 2025
16 checks passed
@steebchen
steebchen deleted the terragon/refactor-token-zero-values branch September 16, 2025 16:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant