Skip to content

Commit 03f6c99

Browse files
Copilotpelikhan
andauthored
Add token table rendering to core.info in parse_token_usage.cjs
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
1 parent 8e7d1c9 commit 03f6c99

2 files changed

Lines changed: 51 additions & 0 deletions

File tree

actions/setup/js/parse_token_usage.cjs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,26 @@ function buildStepSummarySection(title, markdown) {
102102
return `### ${title}\n\n<details>\n<summary>Per-request AI credits and token totals</summary>\n\n${markdown}</details>\n\n`;
103103
}
104104

105+
/**
106+
* Renders the token usage markdown table as plain text for core.info output.
107+
* Strips markdown table separators, pipes, and bold markers so the table is
108+
* readable in the raw step log.
109+
* @param {string} title
110+
* @param {string} markdown
111+
* @returns {string}
112+
*/
113+
function renderTokenTableAsPlainText(title, markdown) {
114+
const plainText = markdown
115+
.replace(/^\|-+.*-+\|$/gm, "") // Remove table separator lines
116+
.replace(/^\|/gm, "") // Remove leading pipe from table rows
117+
.replace(/\|$/gm, "") // Remove trailing pipe from table rows
118+
.replace(/\s*\|\s*/g, " | ") // Normalize remaining pipes to spaced separators
119+
.replace(/\*\*(.*?)\*\*/g, "$1") // Remove bold markers
120+
.replace(/\n{3,}/g, "\n\n") // Collapse excess blank lines
121+
.trim();
122+
return `${title}\n\n${plainText}`;
123+
}
124+
105125
/**
106126
* Appends the token usage section to GITHUB_STEP_SUMMARY when available.
107127
* Falls back to the Actions summary API when the summary path is unavailable.
@@ -142,6 +162,7 @@ async function main() {
142162
}
143163
const markdown = generateTokenUsageSummary(summary);
144164
if (markdown.length > 0) {
165+
core.info(renderTokenTableAsPlainText(getSummaryTitle(), markdown));
145166
await appendStepSummarySection(getSummaryTitle(), markdown);
146167
}
147168

@@ -199,6 +220,7 @@ if (typeof module !== "undefined" && module.exports) {
199220
getSummaryTitle,
200221
buildStepSummarySection,
201222
appendStepSummarySection,
223+
renderTokenTableAsPlainText,
202224
TOKEN_USAGE_AUDIT_PATH,
203225
TOKEN_USAGE_PATH,
204226
TOKEN_USAGE_PATHS,

actions/setup/js/parse_token_usage.test.cjs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const {
1212
readDedupedTokenUsage,
1313
getSummaryTitle,
1414
buildStepSummarySection,
15+
renderTokenTableAsPlainText,
1516
TOKEN_USAGE_AUDIT_PATH,
1617
TOKEN_USAGE_PATH,
1718
TOKEN_USAGE_PATHS,
@@ -194,6 +195,9 @@ describe("parse_token_usage", () => {
194195
expect(mockCore.summary.addRaw).toHaveBeenCalledWith(expect.stringContaining("| Alias |"), true);
195196
expect(mockCore.summary.write).toHaveBeenCalled();
196197
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("Token usage summary appended"));
198+
// Token table should also be rendered to core.info
199+
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("Token Usage"));
200+
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("Alias"));
197201
});
198202

199203
test("uses custom summary title when configured", async () => {
@@ -536,5 +540,30 @@ describe("parse_token_usage", () => {
536540
expect(section).toContain("<details>");
537541
expect(section).toContain("<summary>Per-request AI credits and token totals</summary>");
538542
});
543+
544+
test("renderTokenTableAsPlainText strips table separator lines and pipes", () => {
545+
const markdown = ["| # | Alias | Input | Output |", "|--:|-------|------:|-------:|", "| 1 | sonnet46 | 100 | 200 |", "| **Total** | | **100** | **200** |", "", "Legend: `Alias` is the model shorthand.", ""].join("\n");
546+
547+
const result = renderTokenTableAsPlainText("Token Usage", markdown);
548+
549+
expect(result).toContain("Token Usage");
550+
// separator line is removed
551+
expect(result).not.toMatch(/\|--/);
552+
// leading/trailing pipes are stripped
553+
expect(result).not.toMatch(/^\|/m);
554+
expect(result).not.toMatch(/\|$/m);
555+
// bold markers are removed
556+
expect(result).not.toContain("**");
557+
// data is preserved
558+
expect(result).toContain("sonnet46");
559+
expect(result).toContain("100");
560+
expect(result).toContain("200");
561+
expect(result).toContain("Legend:");
562+
});
563+
564+
test("renderTokenTableAsPlainText prefixes output with title", () => {
565+
const result = renderTokenTableAsPlainText("My Token Usage", "| A |\n|---|\n| 1 |");
566+
expect(result.startsWith("My Token Usage")).toBe(true);
567+
});
539568
});
540569
});

0 commit comments

Comments
 (0)