Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions internal/registry/model_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -742,9 +742,33 @@ func (r *ModelRegistry) ClientSupportsModel(clientID, modelID string) bool {
}
}

// Fallback: strip known model variant suffixes (e.g. "-customtools").
// These variants share the same provider as the base model.
if stripped := stripModelVariantSuffix(modelID); stripped != modelID {
for _, id := range models {
if strings.EqualFold(strings.TrimSpace(id), stripped) {
return true
}
}
}

return false
}

// knownModelVariantSuffixes lists suffixes that Google appends to model names
// for specialised fine-tune variants (e.g. "-customtools" for tool-calling).
// These variants share the same provider/executor as the base model.
var knownModelVariantSuffixes = []string{"-customtools"}

func stripModelVariantSuffix(model string) string {
for _, suffix := range knownModelVariantSuffixes {
if strings.HasSuffix(model, suffix) {
return strings.TrimSuffix(model, suffix)
}
}
return model
}
Comment on lines +758 to +770
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This helper function and the knownModelVariantSuffixes variable are also defined in sdk/api/handlers/handlers.go. To avoid code duplication and improve maintainability, they should be moved to a shared package. The internal/thinking/suffix.go file seems like a suitable location, as it already handles model name suffix parsing. You would then need to add an import for internal/thinking in this file and use the shared function.


// GetAvailableModels returns all models that have at least one available client
// Parameters:
// - handlerType: The handler type to filter models for (e.g., "openai", "claude", "gemini")
Expand Down
27 changes: 27 additions & 0 deletions sdk/api/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,19 @@ func (h *BaseAPIHandler) getRequestDetails(modelName string) (providers []string
providers = util.GetProviderName(resolvedModelName)
}

// Fallback: strip known model variant suffixes (e.g. "-customtools") for
// provider lookup. The suffix is also stripped from resolvedModelName
// because CPA routes gemini-cli through cloudcode-pa/v1internal (OAuth)
// which does not accept variant suffixes (returns 404).
// The behavioral difference is minor: -customtools only changes tool
// selection priority (prefers custom tools over bash), not model capabilities.
if len(providers) == 0 {
if stripped := stripModelVariantSuffix(baseModel); stripped != baseModel {
providers = util.GetProviderName(stripped)
resolvedModelName = stripModelVariantSuffix(resolvedModelName)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

There's a potential bug here. The stripModelVariantSuffix function uses strings.HasSuffix to find and remove the variant suffix. If resolvedModelName contains a thinking suffix (e.g., (8192)), strings.HasSuffix will fail to find the variant suffix (e.g., -customtools), and resolvedModelName will not be stripped as intended. This would cause issues with providers that don't accept variant suffixes.

A more robust way to strip the variant suffix while preserving the thinking suffix would be to replace the baseModel part within resolvedModelName with the stripped version.

Suggested change
resolvedModelName = stripModelVariantSuffix(resolvedModelName)
resolvedModelName = strings.Replace(resolvedModelName, baseModel, stripped, 1)

}
}

if len(providers) == 0 {
return nil, "", &interfaces.ErrorMessage{StatusCode: http.StatusBadGateway, Error: fmt.Errorf("unknown provider for model %s", modelName)}
}
Expand All @@ -810,6 +823,20 @@ func (h *BaseAPIHandler) getRequestDetails(modelName string) (providers []string
return providers, resolvedModelName, nil
}

// knownModelVariantSuffixes lists suffixes that Google appends to model names
// for specialised fine-tune variants (e.g. "-customtools" for tool-calling).
// These variants share the same provider/executor as the base model.
var knownModelVariantSuffixes = []string{"-customtools"}

func stripModelVariantSuffix(model string) string {
for _, suffix := range knownModelVariantSuffixes {
if strings.HasSuffix(model, suffix) {
return strings.TrimSuffix(model, suffix)
}
}
return model
}
Comment on lines +826 to +838
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This helper function and the knownModelVariantSuffixes variable are duplicated from internal/registry/model_registry.go. To avoid code duplication and improve maintainability, they should be moved to a shared package like internal/thinking/suffix.go. Since this file already imports the thinking package, you can remove this duplicated code and call the function from there.


func cloneBytes(src []byte) []byte {
if len(src) == 0 {
return nil
Expand Down