Skip to content

Commit 14f4f0c

Browse files
MCBoarder289Copilot
authored andcommitted
fix(copilot): restrict model normalization to claude-prefixed names
GPT model IDs use dots in the pricing catalog (e.g. gpt-5.4) so applying strings.ReplaceAll universally would convert gpt-5.4 to gpt-5-4 and cause pricing lookup misses. Restrict the dot-to-hyphen substitution in normalizeCopilotModel to names that begin with 'claude-', which are the only Copilot-emitted IDs that need normalization. All other model names pass through unchanged. Add TestNormalizeCopilotModel table test covering claude variants, gpt-5.4/gpt-5.5, gpt-4o, o3-mini, and the empty string. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 746815f commit 14f4f0c

2 files changed

Lines changed: 32 additions & 6 deletions

File tree

internal/parser/copilot.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -290,13 +290,17 @@ func formatCopilotToolCalls(
290290
}
291291

292292
// normalizeCopilotModel converts the model identifier used in
293-
// Copilot session events (dots in version numbers, e.g.
294-
// "claude-sonnet-4.6") to the form used in the pricing catalog
295-
// (hyphens, e.g. "claude-sonnet-4-6"). Only dots that follow a
296-
// digit and precede a digit are replaced to avoid mangling
297-
// non-version dots in other model names.
293+
// Copilot session events to the form used in the pricing catalog.
294+
// Claude model IDs use dots in version numbers in Copilot events
295+
// (e.g. "claude-sonnet-4.6") but hyphens in the pricing catalog
296+
// (e.g. "claude-sonnet-4-6"). Other model families such as GPT
297+
// already use dots in the catalog (e.g. "gpt-5.4"), so only
298+
// claude-prefixed names are normalized.
298299
func normalizeCopilotModel(model string) string {
299-
return strings.ReplaceAll(model, ".", "-")
300+
if strings.HasPrefix(model, "claude-") {
301+
return strings.ReplaceAll(model, ".", "-")
302+
}
303+
return model
300304
}
301305

302306
// readCopilotWorkspaceName reads the session name from the

internal/parser/copilot_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,28 @@ func TestParseCopilotSession_ModelReset(t *testing.T) {
506506
assertEqual(t, "", msgs[3].Model, "msgs[3].Model (reset)")
507507
}
508508

509+
func TestNormalizeCopilotModel(t *testing.T) {
510+
tests := []struct {
511+
input string
512+
want string
513+
}{
514+
{"claude-sonnet-4.6", "claude-sonnet-4-6"},
515+
{"claude-haiku-4.5", "claude-haiku-4-5"},
516+
{"claude-opus-4.7", "claude-opus-4-7"},
517+
// GPT models use dots in the pricing catalog and must not be changed.
518+
{"gpt-5.4", "gpt-5.4"},
519+
{"gpt-5.5", "gpt-5.5"},
520+
{"gpt-4o", "gpt-4o"},
521+
{"o3-mini", "o3-mini"},
522+
{"", ""},
523+
}
524+
for _, tc := range tests {
525+
t.Run(tc.input, func(t *testing.T) {
526+
assert.Equal(t, tc.want, normalizeCopilotModel(tc.input))
527+
})
528+
}
529+
}
530+
509531
func TestSessionIDFromPath(t *testing.T) {
510532
tests := []struct {
511533
path string

0 commit comments

Comments
 (0)