fix(heartbeat): use lastIndexOf for nested provider prefixes

Handles model refs like "openrouter/anthropic/claude-opus-4" by
stripping all provider segments before matching the hardcoded pricing
table. Also adds nested prefix and free model regression tests.

Fixes #49823
Ref #3181
This commit is contained in:
amabito 2026-03-19 00:39:23 +09:00
parent 81e294118e
commit c2c261798d
2 changed files with 9 additions and 2 deletions

View File

@ -198,6 +198,12 @@ describe("config catalog lookup", () => {
expect(cost).toBeCloseTo(0.0025, 4); // gpt-4o = $2.5/M
});
it("matches hardcoded table with nested provider prefix", () => {
// "openrouter/anthropic/claude-opus-4" should strip to "claude-opus-4"
const cost = estimateRunCost("x".repeat(4000), "openrouter/anthropic/claude-opus-4");
expect(cost).toBeCloseTo(0.015, 3); // claude-opus-4 = $15/M
});
it("ignores catalog entry with negative cost.input", () => {
const cfg = {
models: {

View File

@ -1262,8 +1262,9 @@ function resolveInputPricePerToken(modelId: string, cfg?: OpenClawConfig): numbe
}
}
}
// Strip provider prefix (e.g. "openai/gpt-4o" -> "gpt-4o") for hardcoded table match.
const slash = modelId.indexOf("/");
// Strip provider prefix (e.g. "openai/gpt-4o" or "openrouter/anthropic/claude-opus-4"
// -> "gpt-4o" / "claude-opus-4") for hardcoded table match.
const slash = modelId.lastIndexOf("/");
const bareModel = slash !== -1 ? modelId.slice(slash + 1) : modelId;
const lower = bareModel.toLowerCase();
for (const [prefix, price] of SORTED_PRICING_ENTRIES) {