Skip to content

feat(amp): add model mapping support for routing unavailable models to alternatives#390

Merged
luispater merged 4 commits intorouter-for-me:devfrom
NguyenSiTrung:main
Dec 2, 2025
Merged

feat(amp): add model mapping support for routing unavailable models to alternatives#390
luispater merged 4 commits intorouter-for-me:devfrom
NguyenSiTrung:main

Conversation

@NguyenSiTrung
Copy link
Copy Markdown
Contributor

Summary

Add model mapping feature for Amp CLI that allows routing requests for unavailable models to alternative models that are available locally.

Features

  • Model Mapping Configuration: New amp-model-mappings config to define model routing rules (e.g., claude-opus-4.5claude-sonnet-4)
  • Hot Reload Support: Model mappings can be updated without server restart
  • Structured Logging: Clear routing decision logs showing local provider, mapping applied, or amp credits usage

Changes

Core Implementation (2cd5980)

  • Add AmpModelMapping config struct with from and to fields
  • Add ModelMapper interface and DefaultModelMapper implementation
  • Enhance FallbackHandler to apply model mappings before falling back to ampcode.com
  • Update config.example.yaml with documentation

Documentation (33a5656)

  • Add model mapping feature to README.md Amp CLI section
  • Add detailed Model Mapping Configuration section to amp-cli-integration.md
  • Update architecture diagram to show model mapping flow

Hot Reload Fix (3409f4e)

  • Store ampModule in Server struct for config update access
  • Call ampModule.OnConfigUpdated() during config reload
  • Watch config directory instead of file to handle atomic saves (vim, VSCode, etc.)

Example Configuration

amp-model-mappings:
  - from: "claude-opus-4.5"
    to: "claude-sonnet-4"
  - from: "gpt-4.5"
    to: "gemini-2.5-pro"

Files Changed

  • internal/api/modules/amp/model_mapping.go - New model mapper implementation
  • internal/api/modules/amp/model_mapping_test.go - Unit tests
  • internal/api/modules/amp/fallback_handlers.go - Apply mappings in routing
  • internal/api/modules/amp/amp.go - Hot reload support
  • internal/config/config.go - Config struct additions
  • internal/watcher/watcher.go - Fix atomic save detection
  • internal/api/server.go - Wire up amp module config updates
  • docs/amp-cli-integration.md - Documentation
  • config.example.yaml - Example configuration

Trung Nguyen and others added 4 commits November 29, 2025 12:44
…o alternatives

- Add AmpModelMapping config to route models like 'claude-opus-4.5' to 'claude-sonnet-4'
- Add ModelMapper interface and DefaultModelMapper implementation with hot-reload support
- Enhance FallbackHandler to apply model mappings before falling back to ampcode.com
- Add structured logging for routing decisions (local provider, mapping, amp credits)
- Update config.example.yaml with amp-model-mappings documentation
- Add model mapping feature to README.md Amp CLI section
- Add detailed Model Mapping Configuration section to amp-cli-integration.md
- Update architecture diagram to show model mapping flow
- Update Model Fallback Behavior to include mapping step
- Add Table of Contents entry for model mapping
- Store ampModule in Server struct to access it during config updates
- Call ampModule.OnConfigUpdated() in UpdateClients() for hot reload
- Watch config directory instead of file to handle atomic saves (vim, VSCode, etc.)
- Improve config file event detection with basename matching
- Add diagnostic logging for config reload tracing
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @NguyenSiTrung, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant feature to the Amp CLI integration: model mapping. It allows users to configure rules that automatically redirect requests for specific AI models (that might not be locally available) to alternative models that are. This mechanism helps users leverage their existing local provider subscriptions, reducing reliance on Amp credits and enhancing the proxy's adaptability to varying model availabilities.

Highlights

  • Model Mapping Configuration: Introduced amp-model-mappings in the configuration to define rules for routing unavailable models to local alternatives.
  • Hot Reload Support: Model mappings can be updated dynamically without requiring a server restart, improving operational flexibility.
  • Structured Routing Logs: Enhanced logging provides clear, structured information on routing decisions, including when a model mapping is applied or when fallback to ampcode.com occurs.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a robust model mapping feature for the Amp CLI integration, allowing requests for unavailable models to be routed to local alternatives. The implementation is thread-safe, supports hot-reloading of configurations, and includes comprehensive unit tests. Key improvements include structured logging for routing decisions and a fix for atomic config file saves, enhancing the overall reliability and maintainability of the system. The documentation has also been thoroughly updated to reflect these new capabilities.

Comment on lines +171 to +173
} else {
log.Warnf("amp model mapper not initialized, skipping model mapping update")
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The log.Warnf here suggests that m.modelMapper might be nil when OnConfigUpdated is called. While m.modelMapper is initialized in Register which uses sync.Once, it's important to ensure that Register is always called successfully before OnConfigUpdated can be invoked. If there's an edge case where OnConfigUpdated is triggered before Register completes or if Register fails, this warning would be relevant. Consider if there's a scenario where this could happen in normal operation or during module initialization, and if so, how to prevent it or handle it more gracefully.

providers = util.GetProviderName(mappedModel)

// Continue to handler with remapped model
goto handleRequest
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The use of goto handleRequest here, while functional, can sometimes make the control flow harder to follow and debug. In Go, goto is often discouraged for general control flow, although it has specific use cases like breaking out of nested loops or jumping to common error handling. For improved readability and maintainability, consider refactoring this logic to avoid goto, perhaps by encapsulating the common request handling in a separate function or restructuring the if/else blocks.

@luispater
Copy link
Copy Markdown
Collaborator

@ben-vargas Could you please review this PR when you have a moment?

@ben-vargas
Copy link
Copy Markdown
Collaborator

PR Review: feat(amp): add model mapping support

Great work on this feature! The model mapping implementation is solid, well-tested, and the documentation is thorough. I have two minor suggestions to consider:


1. Consider refactoring goto usage in fallback_handlers.go

The goto handleRequest pattern works correctly but can make control flow harder to follow. Consider extracting the model resolution logic into a helper method:

// resolveModel determines how to handle the requested model.
// Returns nil providers if the request was forwarded to the proxy.
func (fh *FallbackHandler) resolveModel(
    normalizedModel string,
    bodyBytes []byte,
    originalModelName string,
    requestPath string,
    c *gin.Context,
) (resolvedModel string, providers []string, updatedBody []byte, usedMapping bool) {
    // Check for local providers first
    providers = util.GetProviderName(normalizedModel)
    if len(providers) > 0 {
        return normalizedModel, providers, bodyBytes, false
    }

    // Try model mapping
    if fh.modelMapper != nil {
        if mappedModel := fh.modelMapper.MapModel(normalizedModel); mappedModel != "" {
            updatedBody = rewriteModelInBody(bodyBytes, mappedModel)
            c.Request.Body = io.NopCloser(bytes.NewReader(updatedBody))
            return mappedModel, util.GetProviderName(mappedModel), updatedBody, true
        }
    }

    // Forward to proxy if available
    if proxy := fh.getProxy(); proxy != nil {
        logAmpRouting(RouteTypeAmpCredits, originalModelName, "", "", requestPath)
        c.Request.Body = io.NopCloser(bytes.NewReader(bodyBytes))
        proxy.ServeHTTP(c.Writer, c.Request)
        return "", nil, bodyBytes, false  // nil signals request handled
    }

    logAmpRouting(RouteTypeNoProvider, originalModelName, "", "", requestPath)
    return normalizedModel, providers, bodyBytes, false
}

This makes the main WrapHandler simpler and the routing logic easier to test independently.


2. Consider reducing log level for expected startup state

In amp.go:171-172, the warning fires during normal startup if OnConfigUpdated is called before Register:

} else {
    log.Warnf("amp model mapper not initialized, skipping model mapping update")
}

This is expected during startup sequencing (watcher may trigger before routes are registered), so it could confuse users watching logs. Consider:

} else {
    log.Debugf("amp model mapper not yet initialized, skipping model mapping update")
}

Neither of these affects correctness - just suggestions for improved readability and log clarity. Overall this is a clean, well-documented implementation. 👍

@ben-vargas
Copy link
Copy Markdown
Collaborator

Overall I would generally guide users to use the amp.internal.model parameter to change/override the Amp CLI "smart" mode model but I can see the use case @NguyenSiTrung is trying to address, and there are some modes like "Oracle" which only supports openai models in the AMP CLI source code, so if you don't have a subscription/access to openai you might want to map this to another provider rather than using AMP credits/cost. I just don't know how consistent this output will be in actual use.

Ref on amp.internal.model: https://x.com/benvargas/status/1992772754211561779

@luispater
Copy link
Copy Markdown
Collaborator

Overall I would generally guide users to use the amp.internal.model parameter to change/override the Amp CLI "smart" mode model but I can see the use case @NguyenSiTrung is trying to address, and there are some modes like "Oracle" which only supports openai models in the AMP CLI source code, so if you don't have a subscription/access to openai you might want to map this to another provider rather than using AMP credits/cost. I just don't know how consistent this output will be in actual use.

Ref on amp.internal.model: https://x.com/benvargas/status/1992772754211561779

@ben-vargas Please approve or request changes on this PR.

I will merge it to the dev branch after it is approved.

If you feel this PR is unnecessary, please let me know and I can close it(maybe you can close it yourself, I think you have enough permission).

@ben-vargas
Copy link
Copy Markdown
Collaborator

@luispater - It looks like I have access to close it if needed. I'm going to dig into the Amp CLI minified source a bit more and decide whether this is something that makes sense to incorporate into CLIProxyAPI's Amp support or defer to client configuration... will update again later today.

@ben-vargas ben-vargas self-requested a review December 1, 2025 20:40
Copy link
Copy Markdown
Collaborator

@ben-vargas ben-vargas left a comment

Choose a reason for hiding this comment

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

Approved

Addressing Review Comments and Prior Concerns

Re: goto suggestion
The goto handleRequest pattern in fallback_handlers.go is a valid design choice here. This could be refactored into a helper function in a future cleanup PR if desired, but it's not a blocker.

Re: log level suggestion for uninitialized mapper warning
After tracing the initialization order, this warning cannot fire in production:

  1. NewServer() calls Register() synchronously, which initializes modelMapper via registerOnce.Do()
  2. OnConfigUpdated() is only called via watcher callbacks after the server is fully created
  3. The warning only appears in unit tests that test OnConfigUpdated() in isolation without calling Register() first

This is test noise, not a production concern. Could be addressed in a follow-up if desired.

Re: Feature overlap with amp.internal.model
While amp.internal.model can override the model for "smart" mode, this PR addresses a different use case: Amp CLI modes like Oracle (requires OpenAI) and Librarian (requires Anthropic) are hardcoded to specific providers. Users without those subscriptions currently fall back to Amp credits. This mapping feature allows transparent routing to alternative local providers, which is a valid cost-optimization use case.

@NguyenSiTrung
Copy link
Copy Markdown
Contributor Author

Hi @ben-vargas , Thanks for your review and very useful comments. However, amp.internal.model still has limitations because the model support list is limited to a specified list, where users may want to use another model for smart mode, which is not possible. In addition, as you have noticed, oracle and librarian cannot configure it. So I think this feature is necessary for user customization.

@luispater luispater changed the base branch from main to dev December 2, 2025 00:07
@luispater luispater merged commit 08a1d2e into router-for-me:dev Dec 2, 2025
2 checks passed
MuhsinunC pushed a commit to MuhsinunC/CLIProxyAPI that referenced this pull request Jan 24, 2026
feat(amp): add model mapping support for routing unavailable models to alternatives
CreatorMetaSky pushed a commit to AIxSpace/CLIProxyAPI that referenced this pull request Feb 11, 2026
feat(amp): add model mapping support for routing unavailable models to alternatives
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants