diff --git a/actions/setup/js/handle_agent_failure.cjs b/actions/setup/js/handle_agent_failure.cjs index 35a3601050f..b0534df7efd 100644 --- a/actions/setup/js/handle_agent_failure.cjs +++ b/actions/setup/js/handle_agent_failure.cjs @@ -284,6 +284,7 @@ function buildFailureMatchCategories(options) { * @param {boolean} options.maxAICreditsExceeded * @param {boolean} options.hasAssignmentErrors * @param {boolean} options.http400ResponseError + * @param {boolean} options.unknownModelAICredits * @returns {string} */ function buildFailureIssueTitle(options) { @@ -294,6 +295,9 @@ function buildFailureIssueTitle(options) { // Keep HTTP 400 below AI-credits signals: quota/rate-limit indicates an account-level // budget state that should take precedence when both classes are detected. if (options.http400ResponseError) return `[aw] ${workflowName} hit HTTP 400 bad request`; + // Unknown model pricing is a configuration error that may also trigger a timeout; + // report it explicitly so the title is not misleadingly "timed out". + if (options.unknownModelAICredits) return `[aw] ${workflowName} has unknown model pricing`; if (options.hasAppTokenMintingFailed) return `[aw] ${workflowName} failed to mint GitHub App token`; if (options.hasLockdownCheckFailed) return `[aw] ${workflowName} failed lockdown check`; if (options.hasOAuthTokenCheckFailed) return `[aw] ${workflowName} has OAuth token misconfiguration`; @@ -3245,6 +3249,7 @@ async function main() { maxAICreditsExceeded, hasAssignmentErrors, http400ResponseError, + unknownModelAICredits, }); const failureCategories = buildFailureMatchCategories({ agentConclusion, diff --git a/actions/setup/js/handle_agent_failure.test.cjs b/actions/setup/js/handle_agent_failure.test.cjs index faf2ccc7882..f3cc1961ece 100644 --- a/actions/setup/js/handle_agent_failure.test.cjs +++ b/actions/setup/js/handle_agent_failure.test.cjs @@ -89,6 +89,7 @@ describe("handle_agent_failure", () => { maxAICreditsExceeded: false, hasAssignmentErrors: false, http400ResponseError: false, + unknownModelAICredits: false, }; const cases = [ @@ -96,6 +97,7 @@ describe("handle_agent_failure", () => { { flag: "maxAICreditsExceeded", expected: "[aw] Test Workflow exceeded max AI credits" }, { flag: "aiCreditsRateLimitError", expected: "[aw] Test Workflow hit AI credits rate limit" }, { flag: "http400ResponseError", expected: "[aw] Test Workflow hit HTTP 400 bad request" }, + { flag: "unknownModelAICredits", expected: "[aw] Test Workflow has unknown model pricing" }, { flag: "hasAppTokenMintingFailed", expected: "[aw] Test Workflow failed to mint GitHub App token" }, { flag: "hasLockdownCheckFailed", expected: "[aw] Test Workflow failed lockdown check" }, { flag: "hasStaleLockFileFailed", expected: "[aw] Test Workflow has stale lock file" }, @@ -116,6 +118,10 @@ describe("handle_agent_failure", () => { it("falls back to generic failed title when no specific condition matches", () => { expect(buildFailureIssueTitle(baseOptions)).toBe("[aw] Test Workflow failed"); }); + + it("prefers unknownModelAICredits over isTimedOut when both are true", () => { + expect(buildFailureIssueTitle({ ...baseOptions, unknownModelAICredits: true, isTimedOut: true })).toBe("[aw] Test Workflow has unknown model pricing"); + }); }); describe("detection caution placement in main()", () => { diff --git a/actions/setup/md/unknown_model_ai_credits.md b/actions/setup/md/unknown_model_ai_credits.md index 4423532963f..42a435f976a 100644 --- a/actions/setup/md/unknown_model_ai_credits.md +++ b/actions/setup/md/unknown_model_ai_credits.md @@ -22,22 +22,32 @@ models: --- ``` -**Option 2 — Use a model already in the built-in pricing table:** +**Option 2 — Add pricing for your model in the frontmatter:** -Switch to a model name that the AWF pricing system recognizes directly (e.g. `gpt-4.1`, `claude-sonnet-4-5`, `gemini-2.0-flash`). - -**Option 3 — Set a default AI credits price as a fallback:** - -Add `defaultAiCreditsPricing` to supply a price for any unrecognized models: +Use the `models.providers` field to supply per-token pricing for your custom model. Use the provider key that matches your engine (`github-copilot`, `anthropic`, `openai`, `google`): ```yaml --- model: my-custom-model max-ai-credits: 500 models: - my-custom-model: - defaultAiCreditsPricing: 3.0 + providers: + openai: # github-copilot | anthropic | openai | google + models: + my-custom-model: + cost: + input: "3.75e-06" # $3.75 per million input tokens (required) + output: "1.5e-05" # $15.00 per million output tokens (required) + cache_read: "3.75e-07" # $0.375 per million cached-read tokens (optional) + cache_write: "4.5e-06" # $4.50 per million cache-write tokens (optional) + reasoning: "1.5e-05" # $15.00 per million reasoning tokens (optional, defaults to output price) --- ``` +Use the provider key matching your engine: `github-copilot` (Copilot), `anthropic` (Claude), `openai` (Codex), or `google` (Gemini). Only `input` and `output` are required; the rest default to zero (or `output` for `reasoning`). + +**Option 3 — Use a model already in the built-in pricing table:** + +Switch to a model name that the AWF pricing system recognizes directly (e.g. `gpt-4.1`, `claude-sonnet-4-5`, `gemini-2.0-flash`). +