Welcome back, future AI architect! In this exciting chapter, we’re going to put all the pieces together and build something truly practical and engaging: a multi-LLM chatbot. This isn’t just any chatbot; it’s one that can intelligently switch between different Large Language Model (LLM) providers using any-llm, leveraging their unique strengths and capabilities.
By the end of this chapter, you’ll have a functional Python chatbot that demonstrates dynamic LLM provider selection, manages conversation history, and incorporates robust error handling. This hands-on project will solidify your understanding of any-llm’s core features and prepare you for real-world AI application development. Ready to bring your multi-LLM vision to life? Let’s dive in!
Before we begin, ensure you’re comfortable with the concepts covered in previous chapters, especially:
any-llminstallation and basic usage.- Configuring and authenticating with multiple LLM providers (e.g., OpenAI, Mistral, Ollama).
- Understanding the
completionAPI and its parameters. - Basic error handling with
try-exceptblocks.
The Vision: A Smart, Adaptable Chatbot
Imagine a chatbot that knows when to use a super-fast, cost-effective model for simple questions and when to switch to a more powerful, nuanced model for complex reasoning tasks. That’s the power we’re unlocking with any-llm. Our chatbot will simulate this intelligence by:
- Taking user input.
- Maintaining conversation history.
- Deciding which LLM provider to use based on a simple strategy (for now, we’ll make it explicit).
- Calling the chosen LLM via
any-llm. - Displaying the response.
Let’s visualize this flow:
This diagram illustrates the continuous loop of our chatbot: taking input, processing it, interacting with an LLM, and responding, all while intelligently picking the right tool for the job.
Step-by-Step Implementation: Building Our Chatbot
We’ll build our chatbot incrementally, adding features one by one.
1. Project Setup and Initial File
First, let’s create a new Python file for our chatbot. Let’s call it multi_llm_chatbot.py.
touch multi_llm_chatbot.py
Now, open multi_llm_chatbot.py in your favorite code editor.
2. Importing any-llm and Setting Up Providers
Before we write any chat logic, we need to import any_llm and ensure our environment is ready. Remember, any-llm-sdk (current stable version 1.x.x as of 2025-12-30) needs to be installed with your desired providers.
If you haven’t already, install it:
pip install any-llm-sdk[openai,mistral,ollama]
(You can choose other providers like anthropic if you prefer, just ensure you have the necessary API keys or local Ollama server running.)
Next, ensure your API keys are set as environment variables. For example:
export OPENAI_API_KEY="YOUR_OPENAI_KEY"
export MISTRAL_API_KEY="YOUR_MISTRAL_KEY"
# For Ollama, you usually don't need a key if running locally, but ensure it's active.
Now, in multi_llm_chatbot.py, let’s import the necessary components:
# multi_llm_chatbot.py
import os
from any_llm import completion, LLMCompletionError
# Define our available LLM providers and their default models
# In a real application, you might load these from a config file.
LLM_PROVIDERS = {
"openai": {
"model": "gpt-4o-2025-12-30", # Assuming latest model for Dec 2025
"api_key_env": "OPENAI_API_KEY"
},
"mistral": {
"model": "mistral-large-2025-12-30", # Assuming latest model for Dec 2025
"api_key_env": "MISTRAL_API_KEY"
},
"ollama": {
"model": "llama3:8b", # Example Ollama model, ensure it's pulled locally
"api_key_env": None # Ollama typically doesn't use an API key env var
}
}
# This will store our conversation history
chat_history = []
print("Welcome to the Multi-LLM Chatbot! Type 'quit' to exit.")
print("You can switch providers by typing 'switch to openai', 'switch to mistral', or 'switch to ollama'.")
print(f"Current default provider: {list(LLM_PROVIDERS.keys())[0]}") # Just pick the first one as default
Explanation:
import os: Needed to check environment variables for API keys.from any_llm import completion, LLMCompletionError: We import thecompletionfunction to interact with LLMs andLLMCompletionErrorfor specific error handling.LLM_PROVIDERS: A dictionary to map simple provider names (like “openai”) to their specific configuration (model name, and the environment variable where its API key is stored, if applicable). This makes it easy to manage and switch.chat_history: An empty list to store our conversation. This is crucial for maintaining context in a chatbot.- Initial
printstatements: Friendly welcome messages and instructions for the user.
3. The Main Chat Loop
Now, let’s create the interactive loop where the user can type messages.
# ... (previous code) ...
def get_current_provider_config(provider_name):
"""Retrieves the configuration for a given provider, checking API keys."""
config = LLM_PROVIDERS.get(provider_name)
if not config:
print(f"Error: Provider '{provider_name}' not recognized.")
return None
# Check for API key if required
if config["api_key_env"] and not os.getenv(config["api_key_env"]):
print(f"Warning: {provider_name.upper()} API key not found. Please set the '{config['api_key_env']}' environment variable.")
return None
return config
# Initialize current provider
current_provider_name = list(LLM_PROVIDERS.keys())[0] # Default to the first one
current_provider_config = get_current_provider_config(current_provider_name)
while True:
user_input = input("\nYou: ")
if user_input.lower() == 'quit':
print("Goodbye!")
break
# Add user message to history
chat_history.append({"role": "user", "content": user_input})
# For now, let's just echo the input
print(f"Bot (using {current_provider_name}): Thinking...")
# We'll replace this "thinking" part with actual LLM calls next!
Explanation:
get_current_provider_config: A helper function to safely retrieve a provider’s configuration and perform a quick check for its API key. This helps preventany-llmerrors later.current_provider_nameandcurrent_provider_config: Variables to keep track of which LLM we’re currently using.while True:: This creates an infinite loop, typical for chatbots, until the user types ‘quit’.input("\nYou: "): Prompts the user for input.chat_history.append(...): Every user message is added to ourchat_history. This is vital for conversational context.
4. Integrating any-llm for Responses
Now, let’s replace our placeholder “Thinking…” with an actual call to any_llm.completion.
# ... (previous code) ...
# Initialize current provider
current_provider_name = list(LLM_PROVIDERS.keys())[0] # Default to the first one
current_provider_config = get_current_provider_config(current_provider_name)
while True:
user_input = input("\nYou: ")
if user_input.lower() == 'quit':
print("Goodbye!")
break
# Check for provider switching commands
if user_input.lower().startswith("switch to "):
requested_provider = user_input.lower().replace("switch to ", "").strip()
if requested_provider in LLM_PROVIDERS:
new_config = get_current_provider_config(requested_provider)
if new_config:
current_provider_name = requested_provider
current_provider_config = new_config
print(f"Bot: Switched to {current_provider_name} provider.")
continue # Skip LLM call for this command
else:
print(f"Bot: Could not switch to {requested_provider}. Check configuration or API key.")
continue
else:
print(f"Bot: Unknown provider '{requested_provider}'. Available: {', '.join(LLM_PROVIDERS.keys())}.")
continue # Skip LLM call for this command
# Add user message to history
chat_history.append({"role": "user", "content": user_input})
# Ensure we have a valid provider config before attempting a call
if not current_provider_config:
print("Bot: No valid LLM provider configured. Please set up API keys or switch to a working provider.")
# Remove the last user message as it wasn't processed by an LLM
chat_history.pop()
continue
print(f"Bot (using {current_provider_name}): Thinking...")
try:
# Make the completion call
# We pass the full chat_history to maintain context
response = completion(
messages=chat_history,
provider=current_provider_name,
model=current_provider_config["model"],
temperature=0.7, # A bit creative, but not too wild
max_tokens=150 # Limit response length
)
bot_response_content = response.content
print(f"Bot: {bot_response_content}")
# Add bot's response to history
chat_history.append({"role": "assistant", "content": bot_response_content})
except LLMCompletionError as e:
print(f"Bot Error (from {current_provider_name}): {e}")
print("Please try again or switch to a different provider.")
# Remove the last user message from history if the LLM call failed
chat_history.pop()
except Exception as e:
print(f"An unexpected error occurred: {e}")
print("Please try again or switch to a different provider.")
chat_history.pop()
Explanation:
- Provider Switching Logic: We added a
if user_input.lower().startswith("switch to "):block. This allows the user to explicitly tell the chatbot to use a different LLM. We updatecurrent_provider_nameandcurrent_provider_configaccordingly. - Pre-call Check:
if not current_provider_config:ensures we don’t try to callcompletionif the selected provider isn’t properly configured (e.g., missing API key). completion(...)call:messages=chat_history: This is crucial!any-llmexpects a list of message objects ({"role": "user", "content": "..."}or{"role": "assistant", "content": "..."}) to maintain conversation context. By passing the fullchat_history, the LLM sees the entire conversation.provider=current_provider_name: Tellsany-llmwhich LLM service to use.model=current_provider_config["model"]: Specifies the exact model within that service.temperature=0.7,max_tokens=150: Standard LLM parameters to control creativity and response length.
- Adding Bot Response to History: After receiving a response, we add it to
chat_historywith{"role": "assistant", "content": bot_response_content}. This ensures the next turn of the conversation includes the bot’s previous statement. - Error Handling:
try...except LLMCompletionError as e:: Catches specific errors fromany-llm(e.g., invalid model, rate limits, API key issues). We print the error and inform the user.except Exception as e:: A general catch-all for any other unexpected Python errors.chat_history.pop(): If an LLM call fails, we remove the user’s last message fromchat_history. Why? Because the LLM didn’t successfully process it, so including it in the next turn would be misleading or could lead to repeated errors.
5. Running Your Multi-LLM Chatbot!
Save your multi_llm_chatbot.py file. Now, open your terminal, ensure your API keys are set as environment variables, and run:
python multi_llm_chatbot.py
Try it out!
- Ask a simple question.
- Then try
switch to mistral(orswitch to openai, etc.). - Ask another question and observe if the response style changes.
- Ask a follow-up question to see if the conversation history is maintained.
- Finally, type
quitto exit.
This is your first multi-LLM chatbot! Pretty neat, right?
Mini-Challenge: Enhance Provider Switching
Our current provider switching is explicit (the user types “switch to”). Let’s make it a bit smarter.
Challenge: Modify the chatbot so that if the user asks a question containing the word “code” or “programming”, it automatically switches to a provider that you designate as “good for code” (e.g., OpenAI’s latest model or a specific Mistral model), if it’s not already using it. If the user then asks a general question, it should revert to a “general purpose” provider (e.g., a cheaper one).
Hint:
- You’ll need a variable to store your “current strategy” or “preferred provider” based on the last user input.
- Use
if "code" in user_input.lower() or "programming" in user_input.lower():to detect keywords. - You might want to add a
default_provider_namethat it reverts to.
What to Observe/Learn:
This challenge helps you understand how to implement basic “routing” logic based on user intent, a common pattern in advanced AI agents. You’ll see how any-llm makes it trivial to swap out the underlying model without changing your core completion call.
Common Pitfalls & Troubleshooting
Missing API Keys:
- Symptom:
LLMCompletionError: Missing API key for provider 'openai', or similar. - Fix: Ensure you’ve correctly set the
OPENAI_API_KEY,MISTRAL_API_KEY, etc., environment variables in the same terminal session before running the script. Rememberexportcommands are session-specific. - Best Practice: Never hardcode API keys directly in your script!
- Symptom:
Incorrect Model Name or Provider:
- Symptom:
LLMCompletionError: Invalid model 'gpt-4.0-turbo'orLLMCompletionError: Provider 'unknown_llm' not found. - Fix: Double-check the
modelnames in yourLLM_PROVIDERSdictionary against the official documentation for each provider. Ensure theprovidername matches whatany-llmexpects (e.g.,openai,mistral,ollama).
- Symptom:
Ollama Not Running/Model Not Pulled:
- Symptom: If using Ollama,
LLMCompletionError: Could not connect to Ollama serverorLLMCompletionError: Model 'llama3:8b' not found locally. - Fix: Ensure the Ollama desktop application or server is running. If the model isn’t found, run
ollama pull llama3:8b(or your chosen model) in your terminal.
- Symptom: If using Ollama,
Conversation History Issues:
- Symptom: The chatbot seems to forget previous turns, or responses are repetitive.
- Fix: Verify that
chat_historyis correctly updated with both user and assistant messages ({"role": "user", ...}and{"role": "assistant", ...}) and that the entirechat_historylist is passed to themessagesparameter of thecompletionfunction on every call.
Summary
Congratulations! You’ve successfully built a multi-LLM chatbot using any-llm. Here are the key takeaways from this hands-on project:
- Unified Interface:
any-llmprovides a seamless way to interact with diverse LLM providers using a singlecompletionfunction. - Dynamic Provider Switching: You learned to implement logic that allows your application to switch between LLMs based on user commands or predefined strategies.
- Conversation Management: Maintaining
chat_historyis crucial for building stateful, conversational AI applications. - Robustness with Error Handling: Incorporating
try-exceptblocks, especially forLLMCompletionError, makes your application more resilient to external API issues. - Practical Application: This project demonstrates how
any-llmsimplifies the integration of powerful LLMs into real-world applications.
What’s Next?
In the next chapter, we’ll delve deeper into advanced any-llm features, including how to leverage different output types (like structured JSON), explore embedding generation, and discuss more sophisticated production patterns for building scalable and reliable AI systems. You’re well on your way to becoming an any-llm master!
References
- mozilla-ai/any-llm GitHub Repository
- Introducing any-llm: A unified API to access any LLM provider - Mozilla.ai Blog
- OpenAI API Documentation
- Mistral AI Documentation
- Ollama GitHub Repository
This page is AI-assisted and reviewed. It references official documentation and recognized resources where relevant.