Skip to content

Commit 05fab00

Browse files
Copilotpelikhangithub-actions[bot]
authored
feat: redesign max-ai-credits exceeded failure template (#38164)
* chore: initial plan for max-ai-credits exceeded template redesign Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> * feat: redesign max-ai-credits exceeded template — inline metrics, no table, no run URL, suggested limit snippet Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Peli de Halleux <pelikhan@users.noreply.github.com>
1 parent 618b95a commit 05fab00

4 files changed

Lines changed: 64 additions & 31 deletions

actions/setup/js/handle_agent_failure.cjs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,20 +1477,22 @@ function buildAICreditsRateLimitErrorContext(hasAICreditsRateLimitError, aiCredi
14771477
const formattedMaxAICredits = Number.isFinite(numericMaxAICredits) && numericMaxAICredits > 0 ? formatAIC(numericMaxAICredits) : "";
14781478
const overage = Number.isFinite(numericAICredits) && Number.isFinite(numericMaxAICredits) && numericAICredits > numericMaxAICredits ? numericAICredits - numericMaxAICredits : NaN;
14791479
const formattedOverage = Number.isFinite(overage) && overage > 0 ? formatAIC(overage) : "";
1480-
const metricsRows = [];
1481-
if (formattedAICredits) {
1482-
metricsRows.push(`| AI credits used | \`${formattedAICredits}\` |`);
1483-
}
1484-
if (formattedMaxAICredits) {
1485-
metricsRows.push(`| Guardrail limit (\`max-ai-credits\`) | \`${formattedMaxAICredits}\` |`);
1486-
}
1487-
if (formattedOverage) {
1488-
metricsRows.push(`| Over the limit by | \`${formattedOverage}\` |`);
1489-
}
1490-
if (runUrl) {
1491-
metricsRows.push(`| Run | [View workflow run](${runUrl}) |`);
1480+
1481+
// Build inline metrics summary (no table, no run URL)
1482+
let metricsSummary = "";
1483+
if (formattedAICredits && formattedMaxAICredits) {
1484+
metricsSummary = ` Used \`${formattedAICredits}\` of \`${formattedMaxAICredits}\` max`;
1485+
if (formattedOverage) {
1486+
metricsSummary += ` (over by \`${formattedOverage}\`)`;
1487+
}
1488+
metricsSummary += ".";
1489+
} else if (formattedAICredits) {
1490+
metricsSummary = ` Used \`${formattedAICredits}\`.`;
14921491
}
1493-
const metricsTable = metricsRows.length > 0 ? `\n\n| Metric | Value |\n| --- | --- |\n${metricsRows.join("\n")}` : "";
1492+
1493+
// Suggest a new limit: 2x current max, or 2x actual usage if max is unknown, or a reasonable default
1494+
const baseForSuggestion = Number.isFinite(numericMaxAICredits) && numericMaxAICredits > 0 ? numericMaxAICredits : Number.isFinite(numericAICredits) && numericAICredits > 0 ? numericAICredits : 0;
1495+
const suggestedCredits = baseForSuggestion > 0 ? Math.ceil(baseForSuggestion * 2) : 2000;
14941496

14951497
const templateName = "ai_credits_rate_limit_error.md";
14961498
let templatePath = "";
@@ -1504,11 +1506,12 @@ function buildAICreditsRateLimitErrorContext(hasAICreditsRateLimitError, aiCredi
15041506
return (
15051507
"\n" +
15061508
renderTemplateFromFile(templatePath, {
1507-
metrics_table: metricsTable,
1509+
metrics_summary: metricsSummary,
1510+
suggested_credits: suggestedCredits,
15081511
})
15091512
);
15101513
} catch (error) {
1511-
throw new Error(`failed to render template at ${templatePath}: ${getErrorMessage(error)}; verify template syntax and required placeholders: metrics_table`);
1514+
throw new Error(`failed to render template at ${templatePath}: ${getErrorMessage(error)}; verify template syntax and required placeholders: metrics_summary, suggested_credits`);
15121515
}
15131516
}
15141517

actions/setup/js/handle_agent_failure_ai_credits_rate_limit.test.cjs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,23 @@ describe("handle_agent_failure AI Credits rate-limit context", () => {
1919
delete process.env.GH_AW_PROMPTS_DIR;
2020
});
2121

22-
it("shows actual usage, guardrail details, and collapsible optimization guidance", () => {
22+
it("shows inline usage and overage without table, no run URL, with suggested limit snippet", () => {
2323
const rendered = buildAICreditsRateLimitErrorContext(true, "17.329230000000003", "10.1", "https://github.com/octo/repo/actions/runs/123");
2424

2525
expect(rendered).toContain("AI Credits Budget Exceeded");
26-
expect(rendered).toContain("| AI credits used | `17.3` |");
27-
expect(rendered).toContain("| Guardrail limit (`max-ai-credits`) | `10.1` |");
28-
expect(rendered).toContain("| Over the limit by | `7.23` |");
29-
expect(rendered).toContain("| Run | [View workflow run](https://github.com/octo/repo/actions/runs/123) |");
26+
// inline metrics
27+
expect(rendered).toContain("Used `17.3` of `10.1` max (over by `7.23`)");
28+
// no table rows
29+
expect(rendered).not.toContain("| AI credits used |");
30+
expect(rendered).not.toContain("| Guardrail limit");
31+
expect(rendered).not.toContain("| Over the limit by |");
32+
// run URL not repeated
33+
expect(rendered).not.toContain("https://github.com/octo/repo/actions/runs/123");
34+
// suggested limit snippet (2x max = 20.2, ceiled to 21)
35+
expect(rendered).toContain("max-ai-credits: 21");
36+
// progressive disclosure
3037
expect(rendered).toContain("<details>");
38+
expect(rendered).toContain("<summary>Increase the limit</summary>");
3139
expect(rendered).toContain("<summary>Tips for reducing AI credit usage</summary>");
3240
expect(rendered).toContain("https://github.github.com/gh-aw/reference/cost-management/");
3341
expect(rendered).not.toContain("Consult the billing dashboards for accurate usage and charges.");

actions/setup/js/handle_agent_failure_max_ai_credits_exceeded.test.cjs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,36 +20,48 @@ describe("handle_agent_failure Max AI Credits exceeded context", () => {
2020
delete process.env.GH_AW_PROMPTS_DIR;
2121
});
2222

23-
it("shows budget exhaustion message with usage, limit, and overage details", () => {
23+
it("shows budget exhaustion message with inline usage, limit, and overage — no table, no run URL", () => {
2424
const rendered = buildAICreditsRateLimitErrorContext(true, "105000", "100000", "https://github.com/octo/repo/actions/runs/456");
2525

2626
expect(rendered).toContain("AI Credits Budget Exceeded");
2727
expect(rendered).toContain("hit the configured `max-ai-credits` guardrail");
28-
expect(rendered).toContain("| AI credits used |");
29-
expect(rendered).toContain("| Guardrail limit (`max-ai-credits`) |");
30-
expect(rendered).toContain("| Over the limit by |");
31-
expect(rendered).toContain("| Run | [View workflow run](https://github.com/octo/repo/actions/runs/456) |");
28+
// inline metrics — no table
29+
expect(rendered).toContain("Used `105K` of `100K` max (over by `5K`)");
30+
expect(rendered).not.toContain("| AI credits used |");
31+
expect(rendered).not.toContain("| Guardrail limit");
32+
expect(rendered).not.toContain("| Over the limit by |");
33+
// run URL not repeated
34+
expect(rendered).not.toContain("https://github.com/octo/repo/actions/runs/456");
35+
// suggested limit snippet
36+
expect(rendered).toContain("max-ai-credits: 200000");
37+
// progressive disclosure
3238
expect(rendered).toContain("<details>");
39+
expect(rendered).toContain("<summary>Increase the limit</summary>");
3340
expect(rendered).toContain("<summary>Tips for reducing AI credit usage</summary>");
3441
expect(rendered).toContain("https://github.github.com/gh-aw/reference/cost-management/");
3542
});
3643

37-
it("shows message without metrics rows when no credit data is available", () => {
44+
it("shows message without metrics when no credit data is available, still shows snippet with default limit", () => {
3845
const rendered = buildAICreditsRateLimitErrorContext(true, "", "", "");
3946

4047
expect(rendered).toContain("AI Credits Budget Exceeded");
48+
expect(rendered).not.toContain("Used `");
4149
expect(rendered).not.toContain("| AI credits used |");
4250
expect(rendered).not.toContain("| Guardrail limit");
4351
expect(rendered).not.toContain("| Run |");
52+
expect(rendered).toContain("max-ai-credits: 2000");
4453
});
4554

46-
it("does not show overage row when usage does not exceed limit", () => {
55+
it("does not show overage when usage does not exceed limit", () => {
4756
const rendered = buildAICreditsRateLimitErrorContext(true, "50000", "100000", "");
4857

4958
expect(rendered).toContain("AI Credits Budget Exceeded");
50-
expect(rendered).toContain("| AI credits used |");
51-
expect(rendered).toContain("| Guardrail limit (`max-ai-credits`) |");
59+
expect(rendered).toContain("Used `50K` of `100K` max");
60+
expect(rendered).not.toContain("over by");
61+
expect(rendered).not.toContain("| AI credits used |");
5262
expect(rendered).not.toContain("| Over the limit by |");
63+
// suggested limit is 2x max
64+
expect(rendered).toContain("max-ai-credits: 200000");
5365
});
5466

5567
it("returns empty string when max_ai_credits_exceeded is false", () => {

actions/setup/md/ai_credits_rate_limit_error.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
> [!WARNING]
22
> **AI Credits Budget Exceeded**
33
>
4-
> The workflow hit the configured `max-ai-credits` guardrail.{metrics_table}
4+
> The workflow hit the configured `max-ai-credits` guardrail.{metrics_summary}
5+
6+
<details>
7+
<summary>Increase the limit</summary>
8+
9+
Update `max-ai-credits` in your workflow frontmatter:
10+
11+
```yaml
12+
max-ai-credits: {suggested_credits}
13+
```
14+
15+
</details>
516
617
<details>
718
<summary>Tips for reducing AI credit usage</summary>
819
920
- Review the [cost optimization guidance](https://github.github.com/gh-aw/reference/cost-management/).
10-
- Increase the `max-ai-credits` limit in the workflow frontmatter if the task legitimately requires more credits.
1121
- Reduce unnecessary model or tool calls in the prompt.
1222
- Trim large inputs or excess context that does not change the outcome.
1323
- Split large tasks across smaller runs when possible.

0 commit comments

Comments
 (0)