From 085404daef2e82b280efd1b57d9d8634b3f3de78 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Jun 2026 16:56:18 +0000 Subject: [PATCH 1/3] Initial plan From cb1b90d204d3a26f85702d7ba691fa41bc6298aa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Jun 2026 17:29:49 +0000 Subject: [PATCH 2/3] fix(api-proxy): add embedding model pricing to fix unknown model error Embedding models (text-embedding-3-small, text-embedding-ada-002) were missing from the api-proxy AI credits pricing table. With maxAiCredits active and no defaultAiCreditsPricing fallback, these requests were rejected with HTTP 400, triggering the GH_AW_UNKNOWN_MODEL_AI_CREDITS flag that caused the Refactoring Opportunity Scanner to file issue #4935. Add pricing entries: - text-embedding-3-small: $0.02/M input, $0.00 output (no output tokens) - text-embedding-ada-002: $0.10/M input, $0.00 output - text-embedding-3-small-inference resolves via prefix-match Closes #4935 --- containers/api-proxy/ai-credits-pricing.js | 7 ++-- .../api-proxy/guards/ai-credits-guard.test.js | 32 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/containers/api-proxy/ai-credits-pricing.js b/containers/api-proxy/ai-credits-pricing.js index 752f16ee6..507328739 100644 --- a/containers/api-proxy/ai-credits-pricing.js +++ b/containers/api-proxy/ai-credits-pricing.js @@ -26,6 +26,9 @@ module.exports = Object.freeze({ 'gemini-3.1-flash': { input: 0.75, cachedInput: 0.075, cacheWrite: null, output: 4.50 }, 'gemini-3.1-pro': { input: 2.00, cachedInput: 0.20, cacheWrite: null, output: 12.00 }, 'gemini-3.5-flash': { input: 1.50, cachedInput: 0.15, cacheWrite: null, output: 9.00 }, - 'mai-code-1-flash': { input: 0.75, cachedInput: 0.075, cacheWrite: null, output: 4.50 }, - 'raptor-mini': { input: 0.25, cachedInput: 0.025, cacheWrite: null, output: 2.00 }, + 'mai-code-1-flash': { input: 0.75, cachedInput: 0.075, cacheWrite: null, output: 4.50 }, + 'raptor-mini': { input: 0.25, cachedInput: 0.025, cacheWrite: null, output: 2.00 }, + // Embedding models (output tokens are not produced; output cost is 0) + 'text-embedding-3-small': { input: 0.02, cachedInput: null, cacheWrite: null, output: 0.00 }, + 'text-embedding-ada-002': { input: 0.10, cachedInput: null, cacheWrite: null, output: 0.00 }, }); diff --git a/containers/api-proxy/guards/ai-credits-guard.test.js b/containers/api-proxy/guards/ai-credits-guard.test.js index 7290ebf50..6943f90ec 100644 --- a/containers/api-proxy/guards/ai-credits-guard.test.js +++ b/containers/api-proxy/guards/ai-credits-guard.test.js @@ -121,6 +121,38 @@ describe('ai-credits-guard', () => { })); }); + it('resolves text-embedding-3-small with input-only pricing', () => { + const usage = applyAiCreditsUsage({ input_tokens: 1_000_000, output_tokens: 0 }, 'text-embedding-3-small'); + // input: 1M × $0.02/M / 10_000 denominator = 2.0 AIC + expect(usage).not.toBeNull(); + expect(usage.aiCreditsThisResponse).toBeCloseTo(2.0, 5); + expect(usage.outputCreditsThisResponse).toBe(0); + }); + + it('resolves text-embedding-3-small-inference via prefix match', () => { + const usage = applyAiCreditsUsage({ input_tokens: 1_000_000, output_tokens: 0 }, 'text-embedding-3-small-inference'); + // Same pricing as text-embedding-3-small via prefix match + expect(usage).not.toBeNull(); + expect(usage.aiCreditsThisResponse).toBeCloseTo(2.0, 5); + }); + + it('resolves text-embedding-ada-002 with input-only pricing', () => { + const usage = applyAiCreditsUsage({ input_tokens: 1_000_000, output_tokens: 0 }, 'text-embedding-ada-002'); + // input: 1M × $0.10/M / 10_000 denominator = 10.0 AIC + expect(usage).not.toBeNull(); + expect(usage.aiCreditsThisResponse).toBeCloseTo(10.0, 5); + expect(usage.outputCreditsThisResponse).toBe(0); + }); + + it('does not reject embedding models when maxAiCredits is active', () => { + process.env.AWF_MAX_AI_CREDITS = '10'; + resetAiCreditsGuardForTests(); + + expect(checkUnknownModelRejection('text-embedding-3-small')).toBeNull(); + expect(checkUnknownModelRejection('text-embedding-3-small-inference')).toBeNull(); + expect(checkUnknownModelRejection('text-embedding-ada-002')).toBeNull(); + }); + it('uses bundled models.dev pricing for known catalog models', () => { const usage = applyAiCreditsUsage({ input_tokens: 100, From 1a37ec40d922bf14313b82c500c91d6db9536708 Mon Sep 17 00:00:00 2001 From: Landon Cox Date: Sun, 14 Jun 2026 10:43:24 -0700 Subject: [PATCH 3/3] Fix embedding pricing: use 0 instead of null for cachedInput, strengthen tests - Replace cachedInput: null with cachedInput: 0 for consistency with all other pricing entries (avoids null coercion fragility) - Use non-zero output_tokens in embedding tests to verify that output: 0.00 pricing is actually enforced Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- containers/api-proxy/ai-credits-pricing.js | 4 ++-- containers/api-proxy/guards/ai-credits-guard.test.js | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/containers/api-proxy/ai-credits-pricing.js b/containers/api-proxy/ai-credits-pricing.js index 507328739..a4540073f 100644 --- a/containers/api-proxy/ai-credits-pricing.js +++ b/containers/api-proxy/ai-credits-pricing.js @@ -29,6 +29,6 @@ module.exports = Object.freeze({ 'mai-code-1-flash': { input: 0.75, cachedInput: 0.075, cacheWrite: null, output: 4.50 }, 'raptor-mini': { input: 0.25, cachedInput: 0.025, cacheWrite: null, output: 2.00 }, // Embedding models (output tokens are not produced; output cost is 0) - 'text-embedding-3-small': { input: 0.02, cachedInput: null, cacheWrite: null, output: 0.00 }, - 'text-embedding-ada-002': { input: 0.10, cachedInput: null, cacheWrite: null, output: 0.00 }, + 'text-embedding-3-small': { input: 0.02, cachedInput: 0, cacheWrite: null, output: 0.00 }, + 'text-embedding-ada-002': { input: 0.10, cachedInput: 0, cacheWrite: null, output: 0.00 }, }); diff --git a/containers/api-proxy/guards/ai-credits-guard.test.js b/containers/api-proxy/guards/ai-credits-guard.test.js index 6943f90ec..6dc486ddb 100644 --- a/containers/api-proxy/guards/ai-credits-guard.test.js +++ b/containers/api-proxy/guards/ai-credits-guard.test.js @@ -122,8 +122,9 @@ describe('ai-credits-guard', () => { }); it('resolves text-embedding-3-small with input-only pricing', () => { - const usage = applyAiCreditsUsage({ input_tokens: 1_000_000, output_tokens: 0 }, 'text-embedding-3-small'); + const usage = applyAiCreditsUsage({ input_tokens: 1_000_000, output_tokens: 500 }, 'text-embedding-3-small'); // input: 1M × $0.02/M / 10_000 denominator = 2.0 AIC + // output: 500 tokens × $0.00/M = 0 AIC (embedding models produce no output cost) expect(usage).not.toBeNull(); expect(usage.aiCreditsThisResponse).toBeCloseTo(2.0, 5); expect(usage.outputCreditsThisResponse).toBe(0); @@ -137,8 +138,9 @@ describe('ai-credits-guard', () => { }); it('resolves text-embedding-ada-002 with input-only pricing', () => { - const usage = applyAiCreditsUsage({ input_tokens: 1_000_000, output_tokens: 0 }, 'text-embedding-ada-002'); + const usage = applyAiCreditsUsage({ input_tokens: 1_000_000, output_tokens: 1000 }, 'text-embedding-ada-002'); // input: 1M × $0.10/M / 10_000 denominator = 10.0 AIC + // output: 1000 tokens × $0.00/M = 0 AIC (embedding models produce no output cost) expect(usage).not.toBeNull(); expect(usage.aiCreditsThisResponse).toBeCloseTo(10.0, 5); expect(usage.outputCreditsThisResponse).toBe(0);