Skip to content

Add LiteLLM Router model #1096

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Apr 23, 2025

Conversation

NishantBaheti
Copy link
Contributor

@NishantBaheti NishantBaheti commented Mar 28, 2025

Simply add a LiteLLM Router to the Model Pool

Motivation - When we talk about agentic frameworks LLM's are always a bottleneck in terms of API calls per minute. What if we simply add LiteLLM router in smolagents.

Guidelines - https://github.com/huggingface/smolagents/blob/main/CONTRIBUTING.md

Code Snippet -

import os
from smolagents import CodeAgent, DuckDuckGoSearchTool, LiteLLMRouter

os.environ["OPENAI_API_KEY"] = ""
os.environ["AWS_ACCESS_KEY_ID"] = ""
os.environ["AWS_SECRET_ACCESS_KEY"] = ""
os.environ["AWS_REGION"] = ""

llm_loadbalancer_model_list = [
    {
        "model_name": "model-group-1",
        "litellm_params": {
            "model": "gpt-4o-mini",
            "api_key": os.getenv("OPENAI_API_KEY"),
        },
    },
    {
        "model_name": "model-group-1",
        "litellm_params": {
            "model": "bedrock/anthropic.claude-3-sonnet-20240229-v1:0",
            "aws_access_key_id": os.getenv("AWS_ACCESS_KEY_ID"),
            "aws_secret_access_key": os.getenv("AWS_SECRET_ACCESS_KEY"),
            "aws_region_name": os.getenv("AWS_REGION"),
        },
    }
]


model = LiteLLMRouter(
    model_id="model-group-1",
    model_list=llm_loadbalancer_model_list,
    router_kwargs={"routing_strategy": "simple-shuffle"},
)
agent = CodeAgent(tools=[DuckDuckGoSearchTool()], model=model)

agent.run("How many seconds would it take for a leopard at full speed to run through Pont des Arts?")

@aymeric-roucher Let me know your thoughts. Excited to be a part of this community.

@luiztauffer
Copy link

+1 to that! definitely needed!

@NishantBaheti
Copy link
Contributor Author

@luiztauffer Idea is to give user the possibility to provide model's list from config like etcd etc and the group name of the models to choose from the list of models. Hence user can manage multiple models with different combinations in different use cases like text2sql, websearch, summarizes etc. I have put one example as well. model_id is actually the group of model that you want to use from the list. May be I should change the name to model group, just wanted to follow the same naming conventions. 😊

@luiztauffer
Copy link

@NishantBaheti yeah I agree. I missed that point at the beginning, but I got it right after trying your code =)
Which works, btw! I'm already using it in my projects, thanks for the contribution! I hope they review it soon.

@NishantBaheti
Copy link
Contributor Author

Hopefully, Felt like this should be here, it can help a lot of engineers trying to scale agents for a million users. BTW loved this framework, it was easy to use and easy to contribute.

@luiztauffer
Copy link

two suggestions:

  1. It looks like the method create_client must be defined by classes inheriting from ApiModel:
def create_client(self):
        """Create a client for the model."""
        try:
            from litellm import Router
        except ModuleNotFoundError:
            raise ModuleNotFoundError(
                "Please install 'litellm' extra to use LiteLLMModel: `pip install 'smolagents[litellm]'`"
            )
        self.router = Router(
            model_list=self._model_list,
            **self.router_config,
        )
  1. It would be useful to pass forward extra configurations for the litellm router:
router_config = {
    "routing_strategy": "simple-shuffle",
    "num_retries": 3,
    "retry_after": 10,
}
model = LiteLLMRouter(model_id="all_models", model_list=model_list, router_config=router_config)
class LiteLLMRouter(ApiModel):
    def __init__(
        self,
        model_id: str,
        model_list: List[Dict[Any, Any]],
        router_config: Optional[Dict[str, Any]] = None,
        ...

@NishantBaheti
Copy link
Contributor Author

@luiztauffer Good Suggestions. Added. Check now.

@NishantBaheti
Copy link
Contributor Author

@aymeric-roucher WDYT?

)
self.router: Router = Router(model_list=self._model_list, **self._router_kwargs)

def __call__(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since this __call__method is the same as LiteLLMModel, could you just have this LiteLLMRouter class inherit from LiteLLMModel , and only define init and create_client methods?

@aymeric-roucher
Copy link
Collaborator

@NishantBaheti this will be a great addition, thanks a lot! Just left some comments.

if flatten_messages_as_text is not None
else self.model_id.startswith(("ollama", "groq", "cerebras"))
)
self.create_client()
Copy link
Collaborator

Choose a reason for hiding this comment

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

You should modify the base create_client methods to accept kwargs, and pass it router_kwargs or {} : thus you can get rid of self._router_kwargs and self._model_list attributes.

@@ -905,6 +905,132 @@ def __call__(
return self.postprocess_message(first_message, tools_to_call_from)


class LiteLLMRouter(ApiModel):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Have this directly inherit from LiteLLMModel to get its call method.

@NishantBaheti
Copy link
Contributor Author

@aymeric-roucher Made some changes based on the review. Let me know

@NishantBaheti
Copy link
Contributor Author

Copy link
Member

@albertvillanova albertvillanova left a comment

Choose a reason for hiding this comment

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

Thanks for the update. I think there are still requested changes that haven't been addressed yet.

@NishantBaheti
Copy link
Contributor Author

@albertvillanova Thanks a lot for the response!!!! Yeah, I missed one. But it was a repeated one. As I've already mentioned in the comment LiteLLM model completion and router completion are two different methods and, according to my understanding, cannot be used interchangeably. Is there a better way available?

Copy link
Member

@albertvillanova albertvillanova left a comment

Choose a reason for hiding this comment

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

Thanks for the follow-up!

You are right that the completions may differ conceptually, but in this case, I believe basic OOP method overriding should be sufficient. You can have LiteLLMRouter inherit from LiteLLMModel, and simply override the create_client and __init__ methods as needed, while still leveraging the shared logic in __call__.

This would help reduce duplication and make the design more maintainable. Let me know if you run into any issues with that approach or if there is a specific part that does not fit this pattern.

Also, feel free to add your comments in the reviewer comments you have above (this link: #1096 (comment)), so we can follow the discussion: I cannot find your comment

As I've already mentioned in the comment

@NishantBaheti
Copy link
Contributor Author

@albertvillanova
Solved this now,

Old strucuture

  • ApiModel
    • LiteLLMModel
    • LiteLLMRouter

New Structure

  • ApiModel
    • LiteLLMModel
      • LiteLLMRouter

Please have a look and let me know.. I believe all the asks are completed :)

@HuggingFaceDocBuilderDev

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

Copy link
Member

@albertvillanova albertvillanova left a comment

Choose a reason for hiding this comment

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

Thanks for your contribution.

I made a few small adjustments to fix typos and align the code style with the rest of the codebase.

@albertvillanova albertvillanova changed the title adding LiteLLM Router (LLM Pooling) As Well Add LiteLLM Router model Apr 23, 2025
@albertvillanova albertvillanova merged commit 6923eb5 into huggingface:main Apr 23, 2025
4 checks passed
@NishantBaheti
Copy link
Contributor Author

@albertvillanova Yes, I got it. I understand we all love our codebase as it is our creation, like a painting. Even I want some things in certain ways, following a certain structure, and IT NEEDS TO BE PERFECT—the forbidden curse of perfection.
Anyway, I'm always happy to help. Again, kudos on the library—it's easy to read, easy to implement, and easy to contribute to. I have more feature suggestions, and might contribute in the future too. :)

@NishantBaheti NishantBaheti deleted the adding-litellmrouter branch April 23, 2025 09:56
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.

5 participants