Dynamic Agent Switcher
A Python package that allows you to dynamically switch between different AI models during agent execution. Perfect for handling rate limits, distributing API calls, and implementing custom switching logic.
Features
- Flexible Switching Conditions: Switch models based on rate limits, tool call counts, response content, time limits, error counts, or custom functions
- Multiple AI Providers: Support for OpenAI, Gemini, Groq, and other providers
- Easy Integration: Drop-in replacement for existing
pydantic_ai.Agentinstances - Configurable Strategies: Round-robin, rate-limit-based, random, weighted, or fallback selection
- Real-time Context Tracking: Monitor execution context and make informed switching decisions
- Extensible: Create custom switching conditions for your specific needs
Installation
pip install dynamic_agent_switcher
Quick Start
Basic Usage
from dynamic_agent_switcher import (
DynamicAgentWrapper,
ModelConfig,
SwitchStrategy,
ConditionType
)
# Define your models
model_configs = [
ModelConfig(
name="openai_gpt4",
provider="openai",
model_name="gpt-4",
api_key="your-openai-key",
max_requests_per_minute=50
),
ModelConfig(
name="gemini_pro",
provider="gemini",
model_name="gemini-1.5-pro",
api_key="your-gemini-key",
max_requests_per_minute=60
)
]
# Create dynamic agent
agent = DynamicAgentWrapper(
model_configs=model_configs,
system_prompt="You are a helpful AI assistant.",
strategy=SwitchStrategy.RATE_LIMIT_BASED
)
# Use like a regular agent
result = await agent.run("Generate a story about a robot.")
Custom Switching Conditions
# Switch after 3 tool calls
tool_call_condition = {
"type": ConditionType.TOOL_CALL_COUNT.value,
"name": "tool_call_limit",
"parameters": {"max_calls": 3}
}
# Switch when the count_words tool reports > 500 words (preferred)
tool_word_count_condition = {
"type": ConditionType.TOOL_RESPONSE.value,
"name": "word_limit_via_tool",
"parameters": {
"tool_name": "count_words", # optional: apply only to this tool
"logic": "ANY", # ANY | ALL | NONE
"response_conditions": [
{"type": "greater_than", "path": "word_count", "value": 500}
]
}
}
# Note: If you want to measure raw model output length instead of tool output,
# you can still use AI_RESPONSE_CONTENT. However, for generic, reusable logic,
# prefer TOOL_RESPONSE so it works with your explicit tool outputs.
# Switch after 2 minutes
time_condition = {
"type": ConditionType.TIME_BASED.value,
"name": "time_limit",
"parameters": {"max_duration_seconds": 120}
}
agent = DynamicAgentWrapper(
model_configs=model_configs,
conditions=[tool_call_condition, tool_word_count_condition, time_condition]
)
Tool Response Based Switching
# Switch when a specific tool returns certain values
tool_response_condition = {
"type": ConditionType.TOOL_RESPONSE.value,
"name": "validation_failed",
"parameters": {
"tool_name": "validate_html",
"response_conditions": [
{"type": "contains", "value": "invalid"},
{"type": "equals", "value": False}
]
}
}
Custom Function Conditions
def custom_switch_logic(context):
"""Custom logic to determine when to switch models."""
error_count = context.get("error_count", 0)
current_model = context.get("current_model")
# Switch if we've had 2 errors with the same model
if error_count >= 2:
return True
# Switch if current model is OpenAI and it's peak hours
if current_model == "openai_gpt4" and is_peak_hours():
return True
return False
custom_condition = {
"type": ConditionType.CUSTOM_FUNCTION.value,
"name": "custom_logic",
"parameters": {
"function": custom_switch_logic,
"reason": "Custom business logic"
}
}
Combination Conditions
# Switch if ANY of these conditions are met
combination_condition = {
"type": ConditionType.COMBINATION.value,
"name": "complex_logic",
"operator": "OR", # AND, OR, XOR
"parameters": {
"conditions": [
{
"type": ConditionType.RATE_LIMIT.value,
"name": "rate_limit"
},
{
"type": ConditionType.TOOL_CALL_COUNT.value,
"name": "too_many_tools",
"parameters": {"max_calls": 10}
},
{
"type": ConditionType.TIME_BASED.value,
"name": "timeout",
"parameters": {"max_duration_seconds": 600}
}
]
}
}
Integration with Existing Code
Replace Existing Agents
from dynamic_agent_switcher import replace_agent_with_dynamic
# Your existing agent
original_agent = Agent(
openai_model,
system_prompt="...",
tools=[count_words, validate_html]
)
# Replace with dynamic version
dynamic_agent = replace_agent_with_dynamic(
original_agent=original_agent,
model_configs=model_configs,
conditions=conditions
)
# Use exactly the same way
result = await dynamic_agent.run(prompt)
Available Condition Types
| Condition Type | Description | Parameters |
|---|---|---|
RATE_LIMIT |
Switch when rate limit errors occur | None |
TOOL_CALL_COUNT |
Switch after N tool calls | max_calls: int |
TOOL_RESPONSE |
Switch based on tool response | tool_name: str, response_conditions: List |
AI_RESPONSE_STATUS |
Switch based on AI response status | status_codes: List, error_patterns: List |
AI_RESPONSE_CONTENT |
Switch based on response content | content_conditions: List |
TIME_BASED |
Switch after time limit | max_duration_seconds: int |
ERROR_COUNT |
Switch after N errors | max_errors: int |
CUSTOM_FUNCTION |
Switch based on custom function | function: Callable, reason: str |
COMBINATION |
Combine multiple conditions | conditions: List, operator: str |
Content Conditions
For AI_RESPONSE_CONTENT and TOOL_RESPONSE conditions, you can use:
contains: Check if response contains textequals: Check if response equals valueregex: Check if response matches regex patterngreater_than: Check if numeric value is greaterless_than: Check if numeric value is lesslength_greater_than: Check if string length is greaterlength_less_than: Check if string length is lessword_count_greater_than: Check if word count is greaterword_count_less_than: Check if word count is less
Monitoring and Debugging
# Get current status
status = agent.get_status()
print(f"Current model: {status['current_model']}")
print(f"Available models: {status['available_models']}")
print(f"Active conditions: {status['conditions']}")
print(f"Execution context: {status['execution_context']}")
# Update context manually
agent.update_context(
tool_call_count=5,
last_tool_response={"word_count": 750}
)
# Reset context
agent.reset_context()
# Add/remove conditions dynamically
agent.add_condition(new_condition)
agent.remove_condition("condition_name")
Advanced Usage
Custom Model Selection Strategy
def custom_model_selector(available_models, context):
"""Custom logic for model selection."""
if context.get("task_type") == "creative":
return "gemini_pro" # Better for creative tasks
elif context.get("task_type") == "analytical":
return "openai_gpt4" # Better for analysis
else:
return random.choice(available_models)
# Use with custom strategy
agent = DynamicAgentWrapper(
model_configs=model_configs,
strategy=custom_model_selector
)
Context-Aware Switching
# Update context based on your application logic
agent.update_context(
task_type="creative",
user_preference="fast",
current_topic="artificial_intelligence"
)
# Your custom condition can use this context
def context_aware_condition(context):
if context.get("task_type") == "creative" and context.get("current_model") == "openai_gpt4":
return True # Switch to Gemini for creative tasks
return False
Examples
See usage_examples.py for comprehensive examples including:
- Basic usage with rate limit handling
- Tool call count based switching
- Response content based switching
- Time-based switching
- Custom function conditions
- Combination conditions
- Tool response based switching
- Monitoring and debugging
- Integration with existing code
- Advanced context-aware switching
License
MIT License - see LICENSE file for details.
Author
Sumit Paul - sumit.18.paul@gmail.com
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Release files for dynamic-agent-switcher 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| dynamic_agent_switcher-0.1.1.tar.gz | 21.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dynamic_agent_switcher-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 39.6 kB
Release files / dynamic_agent_switcher-0.1.1.tar.gz
| Download URL | dynamic_agent_switcher-0.1.1.tar.gz |
|---|---|
| Size | 21.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d918fc76cc1f8d14537fe35dc079b8419cfa476bf75ff7e438b36690e941d777
|
|
BLAKE2b-256 checksum How to use checksums |
e419f48864179d05e443461b23dcff0a05329d1d7c6a710184657b7d627a7880
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.9.6
|
Release files / dynamic_agent_switcher-0.1.1-py3-none-any.whl
| Download URL | dynamic_agent_switcher-0.1.1-py3-none-any.whl |
|---|---|
| Size | 18.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1921e3b3e231b2133fe895fe68eada86b3f1641bf8acce6014d3d2bc2cdeeb24
|
|
BLAKE2b-256 checksum How to use checksums |
50eaf7584683db5216891f1cf68999b5e5970e6d954212b976ba4802bdd0bd33
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.9.6
|