8000
Skip to content

feat: implement effective tokens computation and display in action JavaScript#24132

Merged
pelikhan merged 8 commits intomainfrom
copilot/implement-effective-tokens-computation
Apr 2, 2026
Merged

feat: implement effective tokens computation and display in action JavaScript#24132
pelikhan merged 8 commits intomainfrom
copilot/implement-effective-tokens-computation

Conversation

Copy link
Copy Markdown
Contributor
Copilot AI commented Apr 2, 2026

Summary

Implements the Effective Tokens (ET) specification in the action JavaScript layer per the effective-tokens-specification.md.

Changes

New files

  • actions/setup/js/model_multipliers.json — Source of truth for per-model ET multipliers in the JavaScript actions layer (same data as pkg/cli/data/model_multipliers.json)
  • actions/setup/js/effective_tokens.cjs — ET computation module implementing the spec formula:
    • Reads GH_AW_MODEL_MULTIPLIERS env var (set by setup.sh)
    • computeBaseWeightedTokens() — applies per-token-class weights
    • computeEffectiveTokens() — returns exact real-valued ET (rounding deferred to presentation)
    • formatET(n) — compact number formatter: <1K exact, 1K–999K as N.NK, ≥1M as N.NM
    • Falls back to multiplier 1.0 for unknown models
    • Caches both successful and unsuccessful parses of GH_AW_MODEL_MULTIPLIERS to avoid redundant re-parsing
  • actions/setup/js/effective_tokens.test.cjs — Spec-compliant tests covering T-ET-001 through T-ET-031 (including the Appendix A worked example) plus formatET tests

Modified files

  • actions/setup/setup.sh — Exports GH_AW_MODEL_MULTIPLIERS to $GITHUB_ENV (EOF delimiter multiline form) so all subsequent workflow steps have access to the multiplier JSON
  • actions/setup/js/parse_mcp_gateway_log.cjs:
    • parseTokenUsageJsonl() now computes effectiveTokens per model and totalEffectiveTokens
    • generateTokenUsageSummary() adds an ET column to the per-model table (compact format) and a ● N footer line (with cache efficiency when available)
    • Adds a weight disclosure line (<sub>ET weights: input=… · …</sub>) after the footer per ET spec requirements
    • writeStepSummaryWithTokenUsage() exports GH_AW_EFFECTIVE_TOKENS via core.exportVariable for use in generated footers
  • actions/setup/js/parse_mcp_gateway_log.test.cjs — Updated existing column-header test + added ET-specific tests
  • actions/setup/js/messages_footer.cjs:
    • FooterContext typedef extended with optional effectiveTokens field
    • getFooterMessage() appends · ● N (compact format, no "ET" label) to the default footer when effectiveTokens is available
    • generateFooterWithMessages() reads GH_AW_EFFECTIVE_TOKENS env var and passes it as effectiveTokens in the footer context

How it works

setup action
  → copies model_multipliers.json to $RUNNER_TEMP/gh-aw/actions/
  → exports GH_AW_MODEL_MULTIPLIERS to $GITHUB_ENV

agent job
  → firewall proxy writes token-usage.jsonl

"Parse MCP Gateway logs" step (parse_mcp_gateway_log.cjs)
  → reads token-usage.jsonl
  → computes ET per model using GH_AW_MODEL_MULTIPLIERS
  → adds ET column to step summary table (compact format)
  → adds "● 12.3K" footer line to step summary
  → discloses token class weights used for computation
  → exports GH_AW_EFFECTIVE_TOKENS=12345 to $GITHUB_ENV

safe-outputs job (messages_footer.cjs)
  → reads GH_AW_EFFECTIVE_TOKENS
  → appends "· ● 12.3K" to GitHub comment/issue/PR footer

Example output

Step summary token usage table:

### 📊 Token Usage
| Model | Input | Output | Cache Read | Cache Write | ET | Requests | Duration |
|-------|------:|-------:|-----------:|------------:|---:|---------:|---------:|
| claude-sonnet-4.5 | 1,000 | 500 | 200 | 100 | 3.5K | 5 | 1.2s |
| **Total** | **1,000** | **500** | **200** | **100** | **3.5K** | **5** | **1.2s** |

_● 3.5K · Cache efficiency: 16.7%_
<sub>ET weights: input=1 · cached_input=0.1 · output=4 · reasoning=4 · cache_write=1</sub>

Generated footer in GitHub comments:

> Generated by [my-workflow](url) for issue #42 · ● 3.5K · [◷](history-url)

Copilot AI and others added 2 commits April 2, 2026 14:38
…vaScript

- Add model_multipliers.json to actions/setup/js/ (source of truth for JS)
- Update setup.sh to export GH_AW_MODEL_MULTIPLIERS env var via $GITHUB_ENV
- Create effective_tokens.cjs with ET computation per the spec (Section 4.3/4.4)
- Create effective_tokens.test.cjs with 34 spec-compliant tests (T-ET-001 to T-ET-031)
- Update parse_mcp_gateway_log.cjs: ET column in token usage table, ● ET footer line
- Export GH_AW_EFFECTIVE_TOKENS via core.exportVariable for use in generated footers
- Update messages_footer.cjs: show ● N ET in generated footer when env var available
- Update parse_mcp_gateway_log.test.cjs with ET-specific tests

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/903d1209-5bf0-420c-b226-645a542280e8

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
@pelikhan
Copy link
Copy Markdown
Collaborator
pelikhan commented Apr 2, 2026

…ve_tokens.cjs

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/27496c5e-92fe-42e5-bec0-7f9134f64bd8

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author
Copilot AI commented Apr 2, 2026

@copilot fix https://github.com/github/gh-aw/actions/runs/23906295588/job/69715986122?pr=24132

Fixed in 6934acf — the TypeScript error was Type '{}' is not assignable to type 'Record<string, number>' on the multipliers object literal in effective_tokens.cjs. Added a /** @type {Record<string, number>} */ JSDoc annotation to satisfy the type checker.

@pelikhan pelikhan marked this pull request as ready for review April 2, 2026 14:56
Copilot AI review requested due to automatic review settings April 2, 2026 14:56
Copy link
Copy Markdown
Contributor
Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements Effective Tokens (ET) computation in the JavaScript actions layer and surfaces ET in both step summaries and generated workflow footers.

Changes:

  • Add an ET computation module driven by GH_AW_MODEL_MULTIPLIERS plus a per-model multipliers JSON source of truth.
  • Extend MCP gateway token-usage parsing to compute per-model and total ET and display it in the step summary (table column + “● N ET” footer line), exporting GH_AW_EFFECTIVE_TOKENS.
  • Update footer generation to append “● N ET” when GH_AW_EFFECTIVE_TOKENS is available, and expand/adjust test coverage accordingly.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
actions/setup/setup.sh Exports GH_AW_MODEL_MULTIPLIERS (multiline) to make multipliers available to later JS steps.
actions/setup/js/model_multipliers.json Adds JS-layer source of truth for token class weights + per-model multipliers.
actions/setup/js/effective_tokens.cjs Introduces ET computation (weights + model multiplier with longest-prefix matching).
actions/setup/js/effective_tokens.test.cjs Adds unit tests covering spec cases and edge conditions for ET computation.
actions/setup/js/parse_mcp_gateway_log.cjs Computes and displays ET in token usage summaries; exports GH_AW_EFFECTIVE_TOKENS.
actions/setup/js/parse_mcp_gateway_log.test.cjs Updates/expands tests for ET column and footer behavior.
actions/setup/js/messages_footer.cjs Reads GH_AW_EFFECTIVE_TOKENS and appends “● N ET” to the default footer.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +64 to +67
// Ensure zero-valued weights fall back to defaults
const defaults = defaultTokenClassWeights();
for (const key of Object.keys(defaults)) {
if (!weights[key]) {
Copy link
Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The fallback loop uses a falsy check (if (!weights[key])) which treats an explicit 0 override as “missing” and replaces it with the default. That prevents intentionally disabling a token class via a zero weight; use a null/undefined check (or Number.isFinite) instead so 0 remains a valid override.

Suggested change
// Ensure zero-valued weights fall back to defaults
const defaults = defaultTokenClassWeights();
for (const key of Object.keys(defaults)) {
if (!weights[key]) {
// Ensure missing or invalid weights fall back to defaults, but preserve explicit 0 overrides
const defaults = defaultTokenClassWeights();
for (const key of Object.keys(defaults)) {
const value = weights[key];
if (value == null || !Number.isFinite(value)) {

Copilot uses AI. Check for mistakes.
Comment on lines +134 to +139
/**
* Computes the base weighted token count for a single invocation.
*
* Formula (ET specification Section 4.3):
* base = (w_in × I) + (w_cache × C) + (w_out × O) + (w_reason × R) + (w_cache_write × W)
*
Copy link
Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

This JSDoc attributes the cache_write term to “ET specification Section 4.3”, but the spec’s base_weighted_tokens formula does not include cache_write (it’s an extension). Update the comment to distinguish spec vs extension to avoid misleading readers.

Copilot uses AI. Check for mistakes.
Comment on lines +48 to +56
function getMultipliersData() {
if (_parsedMultipliers !== null) {
return _parsedMultipliers;
}

const raw = process.env.GH_AW_MODEL_MULTIPLIERS;
if (!raw || !raw.trim()) {
return null;
}
Copy link
Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

getMultipliersData() only memoizes successful parses; when the env var is missing/invalid JSON it returns null without updating the cache, so repeated calls will repeatedly re-check / re-parse (and re-throw internally for malformed JSON). Consider using a separate “uninitialized” sentinel so you can cache the null result as well.

Copilot uses AI. Check for mistakes.
Comment on lines +173 to +175
const m = getModelMultiplier(model);
return Math.round(base * m);
}
Copy link
Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

computeEffectiveTokens() rounds ET to an integer via Math.round. The ET spec defines ET as m × base_weighted_tokens (a real-valued computation) and does not specify rounding; rounding here can lose precision when cached_input weights introduce fractions (e.g., 0.1 × C). Consider returning the exact number and rounding only at presentation boundaries (step summary/footer).

Copilot uses AI. Check for mistakes.
Comment on lines 117 to 121
/**
* Generates a markdown summary section for token usage data.
* @param {{totalInputTokens: number, totalOutputTokens: number, totalCacheReadTokens: number, totalCacheWriteTokens: number, totalRequests: number, totalDurationMs: number, cacheEfficiency: number, byModel: Object} | null} summary
* Includes an Effective Tokens (ET) column per model and a ● ET summary line.
* @param {{totalInputTokens: number, totalOutputTokens: number, totalCacheReadTokens: number, totalCacheWriteTokens: number, totalRequests: number, totalDurationMs: number, cacheEfficiency: number, totalEffectiveTokens: number, byModel: Object} | null} summary
* @returns {string} Markdown section, or empty string if no data
Copy link
Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The ET spec requires disclosing the weights used in reported output when presenting ET. The step summary currently adds an ET column and “● N ET” footer, but doesn’t disclose the token class weights / reference version (even if defaults). Consider adding a short note (e.g., after the table/footer) indicating the weights/multiplier version or reference model used to compute ET.

Copilot uses AI. Check for mistakes.
@pelikhan
Copy link
Copy Markdown
Collaborator
pelikhan commented Apr 2, 2026

@copilot review comments

@pelikhan
Copy link
Copy Markdown
Collaborator
pelikhan commented Apr 2, 2026

@copilot fix https://github.com/github/gh-aw/actions/runs/23906779102/job/69717689600?pr=24132

…teway_log.cjs

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/24bf8638-6e21-4d13-a457-004b078537f3

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author
Copilot AI commented Apr 2, 2026

@copilot review comments

Addressed all 5 review comments in 9245c2f:

  1. Falsy weight check — replaced !weights[key] with value == null || !Number.isFinite(value) so explicit 0 overrides are preserved
  2. cache_write JSDoc — updated comment to "base spec Section 4.3 + cache_write implementation extension" with an explicit note
  3. Null memoization — switched sentinel from null to undefined; all early-exit paths now set _parsedMultipliers = null before returning, so invalid/missing env vars are cached and not re-parsed on every call
  4. Remove rounding from computeEffectiveTokens — function now returns the exact real value; Math.round() is applied at presentation in generateTokenUsageSummary (table cells, footer) and when exporting GH_AW_EFFECTIVE_TOKENS
  5. ET weight disclosure — added a <sub>ET weights: input=… · cached_input=… · output=… · …</sub> line after the ET footer in the step summary, populated from getTokenClassWeights() so it reflects actual values (defaults or overrides)

@copilot fix https://github.com/github/gh-aw/actions/runs/23906779102/job/69717689600?pr=24132

Fixed in 9245c2f — same TypeScript error as before (merge commit reverted the previous fix). The multipliers literal is now correctly typed as /** @type {Record<string, number>} */ ({}) with parentheses, and TypeScript check passes cleanly.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/repos/github/gh-aw/contents/.github%2Fworkflows%2Faudit-workflows.md
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --experimental-import-meta-resolve --require /home/REDACTED/work/gh-aw/gh-aw/actions/setup/js/node_modules/vitest/suppress-warnings.cjs --conditions node --conditions development /home/REDACTED/work/gh-aw/gh-aw/actions/setup/js/node_modules/vitest/dist/workers/forks.js git _modules/.bin/gi--is-ancestor git ls-r�� origin refs/heads/fallback-branch ode_modules/.bin/node -bare-hV6lff main 0/x64/lib/node_m-e git (http block)
  • invalid.example.invalid
    • Triggering command: /usr/lib/git-core/git-remote-https /usr/lib/git-core/git-remote-https origin https://invalid.example.invalid/nonexistent-repo.git git comm�� -m lure test commit k/node_modules/.bin/git user.name Test User ode-gyp-bin/git git conf�� user.email test@test.com /git -b feature-branch es/.bin/git git (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@pelikhan
Copy link
Copy Markdown
Collaborator
pelikhan commented Apr 2, 2026

@copilot

  • add a rendering function that pretifies the ET numbers (which are likely high).
  • remove NET from footer, just the symbol.

Copy link
Copy Markdown
Contributor Author
Copilot AI commented Apr 2, 2026

@copilot

  • add a rendering function that pretifies the ET numbers (which are likely high)....

Done in 1ca3f2f:

  • formatET(n) added to effective_tokens.cjs and exported: values under 1K shown as-is, 1K–999K as N.NK (e.g. 12.3K, 450K), ≥1M as N.NM (e.g. 1.2M). Used in both the step summary table cells/footer and the GitHub comment footer. 4 new tests added.
  • Footer· ● 12,345 ET· ● 12.3K (no "ET" label, compact number)

Example footer: > Generated by [my-workflow](url) for issue #42 · ● 12.3K · [◷](url)

@pelikhan pelikhan merged commit e95d91a into main Apr 2, 2026
82 of 83 checks passed
@pelikhan pelikhan deleted the copilot/implement-effective-tokens-computation branch April 2, 2026 16:50
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.

3 participants

0