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.
Metadata
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 | Provenance | Actions |
|---|---|---|---|---|
| dynamic_agent_switcher-0.1.1.tar.gz | 21.2 kB | Without provenance | Details for dynamic_agent_switcher-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| dynamic_agent_switcher-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for dynamic_agent_switcher-0.1.1-py3-none-any.whl |
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
|
Security for dynamic-agent-switcher 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.