praisonaiagents 0.0.128__tar.gz → 0.0.129__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/PKG-INFO +1 -1
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/agent/__init__.py +2 -1
- praisonaiagents-0.0.129/praisonaiagents/agent/router_agent.py +334 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/llm/__init__.py +11 -1
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/llm/llm.py +240 -57
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/llm/model_capabilities.py +20 -3
- praisonaiagents-0.0.129/praisonaiagents/llm/model_router.py +348 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/process/process.py +61 -55
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/task/task.py +17 -4
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents.egg-info/PKG-INFO +1 -1
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents.egg-info/SOURCES.txt +2 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/pyproject.toml +1 -1
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/README.md +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/__init__.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/agent/agent.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/agent/handoff.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/agent/image_agent.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/agents/__init__.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/agents/agents.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/agents/autoagents.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/approval.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/guardrails/__init__.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/guardrails/guardrail_result.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/guardrails/llm_guardrail.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/knowledge/__init__.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/knowledge/chunking.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/knowledge/knowledge.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/llm/openai_client.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/main.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/mcp/__init__.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/mcp/mcp.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/mcp/mcp_http_stream.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/mcp/mcp_sse.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/memory/__init__.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/memory/memory.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/process/__init__.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/session.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/task/__init__.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/telemetry/__init__.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/telemetry/integration.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/telemetry/telemetry.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/README.md +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/__init__.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/arxiv_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/calculator_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/csv_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/duckdb_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/duckduckgo_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/excel_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/file_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/json_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/newspaper_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/pandas_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/python_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/searxng_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/shell_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/spider_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/test.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/train/data/generatecot.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/wikipedia_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/xml_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/yaml_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents/tools/yfinance_tools.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents.egg-info/dependency_links.txt +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents.egg-info/requires.txt +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/praisonaiagents.egg-info/top_level.txt +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/setup.cfg +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/tests/test-graph-memory.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/tests/test.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/tests/test_fix_comprehensive.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/tests/test_handoff_compatibility.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/tests/test_http_stream_basic.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/tests/test_ollama_async_fix.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/tests/test_ollama_fix.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/tests/test_posthog_fixed.py +0 -0
- {praisonaiagents-0.0.128 → praisonaiagents-0.0.129}/tests/test_validation_feedback.py +0 -0
@@ -2,5 +2,6 @@
|
|
2
2
|
from .agent import Agent
|
3
3
|
from .image_agent import ImageAgent
|
4
4
|
from .handoff import Handoff, handoff, handoff_filters, RECOMMENDED_PROMPT_PREFIX, prompt_with_handoff_instructions
|
5
|
+
from .router_agent import RouterAgent
|
5
6
|
|
6
|
-
__all__ = ['Agent', 'ImageAgent', 'Handoff', 'handoff', 'handoff_filters', 'RECOMMENDED_PROMPT_PREFIX', 'prompt_with_handoff_instructions']
|
7
|
+
__all__ = ['Agent', 'ImageAgent', 'Handoff', 'handoff', 'handoff_filters', 'RECOMMENDED_PROMPT_PREFIX', 'prompt_with_handoff_instructions', 'RouterAgent']
|
@@ -0,0 +1,334 @@
|
|
1
|
+
"""
|
2
|
+
Router Agent that can use different LLM models based on task requirements.
|
3
|
+
|
4
|
+
This module extends the base Agent class to support multiple models and intelligent
|
5
|
+
model selection based on task characteristics.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import os
|
9
|
+
import logging
|
10
|
+
from typing import Dict, List, Optional, Any, Union
|
11
|
+
from .agent import Agent
|
12
|
+
from ..llm.model_router import ModelRouter
|
13
|
+
from ..llm import LLM
|
14
|
+
|
15
|
+
logger = logging.getLogger(__name__)
|
16
|
+
|
17
|
+
|
18
|
+
class RouterAgent(Agent):
|
19
|
+
"""
|
20
|
+
An enhanced agent that can dynamically select and use different LLM models
|
21
|
+
based on task requirements, optimizing for cost and performance.
|
22
|
+
"""
|
23
|
+
|
24
|
+
def __init__(
|
25
|
+
self,
|
26
|
+
models: Optional[Union[List[str], Dict[str, Any]]] = None,
|
27
|
+
model_router: Optional[ModelRouter] = None,
|
28
|
+
routing_strategy: str = "auto", # "auto", "manual", "cost-optimized", "performance-optimized"
|
29
|
+
primary_model: Optional[str] = None,
|
30
|
+
fallback_model: Optional[str] = None,
|
31
|
+
**kwargs
|
32
|
+
):
|
33
|
+
"""
|
34
|
+
Initialize a RouterAgent.
|
35
|
+
|
36
|
+
Args:
|
37
|
+
models: List of model names or dict mapping model names to configurations
|
38
|
+
model_router: Custom ModelRouter instance for model selection
|
39
|
+
routing_strategy: Strategy for model selection
|
40
|
+
primary_model: Primary model to use (overrides routing for simple tasks)
|
41
|
+
fallback_model: Fallback model if selected model fails
|
42
|
+
**kwargs: Additional arguments passed to parent Agent class
|
43
|
+
"""
|
44
|
+
# Initialize model router
|
45
|
+
self.model_router = model_router or ModelRouter()
|
46
|
+
self.routing_strategy = routing_strategy
|
47
|
+
self.fallback_model = fallback_model or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o-mini')
|
48
|
+
|
49
|
+
# Process models configuration
|
50
|
+
self.available_models = self._process_models_config(models)
|
51
|
+
|
52
|
+
# Set primary model for parent class initialization
|
53
|
+
if primary_model:
|
54
|
+
kwargs['llm'] = primary_model
|
55
|
+
elif self.available_models:
|
56
|
+
# Use the most cost-effective model as default
|
57
|
+
cheapest_model = min(
|
58
|
+
self.available_models.keys(),
|
59
|
+
key=lambda m: self.model_router.get_model_info(m).cost_per_1k_tokens
|
60
|
+
if self.model_router.get_model_info(m) else float('inf')
|
61
|
+
)
|
62
|
+
kwargs['llm'] = cheapest_model
|
63
|
+
|
64
|
+
# Store the original llm parameter for later use
|
65
|
+
self._llm_config = kwargs.get('llm')
|
66
|
+
|
67
|
+
# Store api_key and base_url for LLM initialization
|
68
|
+
self._base_url = kwargs.get('base_url')
|
69
|
+
self._api_key = kwargs.get('api_key')
|
70
|
+
|
71
|
+
# Initialize parent Agent class
|
72
|
+
super().__init__(**kwargs)
|
73
|
+
|
74
|
+
# Initialize LLM instances for each model
|
75
|
+
self._llm_instances: Dict[str, LLM] = {}
|
76
|
+
self._initialize_llm_instances()
|
77
|
+
|
78
|
+
# Track usage statistics
|
79
|
+
self.model_usage_stats = {model: {'calls': 0, 'tokens': 0, 'cost': 0.0}
|
80
|
+
for model in self.available_models}
|
81
|
+
|
82
|
+
def _process_models_config(self, models: Optional[Union[List[str], Dict[str, Any]]]) -> Dict[str, Any]:
|
83
|
+
"""Process the models configuration into a standardized format."""
|
84
|
+
if not models:
|
85
|
+
# Use default models from router
|
86
|
+
return {m.name: {} for m in self.model_router.models}
|
87
|
+
|
88
|
+
if isinstance(models, list):
|
89
|
+
# Simple list of model names
|
90
|
+
return {model: {} for model in models}
|
91
|
+
|
92
|
+
# Already a dict with model configurations
|
93
|
+
return models
|
94
|
+
|
95
|
+
def _initialize_llm_instances(self):
|
96
|
+
"""Initialize LLM instances for each available model."""
|
97
|
+
base_url = self._base_url
|
98
|
+
api_key = self._api_key
|
99
|
+
|
100
|
+
for model_name, config in self.available_models.items():
|
101
|
+
try:
|
102
|
+
# Merge base configuration with model-specific config
|
103
|
+
llm_config = {
|
104
|
+
'model': model_name,
|
105
|
+
'base_url': config.get('base_url', base_url),
|
106
|
+
'api_key': config.get('api_key', api_key),
|
107
|
+
'verbose': self.verbose,
|
108
|
+
'markdown': self.markdown,
|
109
|
+
'stream': self.stream
|
110
|
+
}
|
111
|
+
|
112
|
+
# Add any model-specific parameters
|
113
|
+
llm_config.update(config)
|
114
|
+
|
115
|
+
# Create LLM instance
|
116
|
+
self._llm_instances[model_name] = LLM(**llm_config)
|
117
|
+
logger.debug(f"Initialized LLM instance for model: {model_name}")
|
118
|
+
|
119
|
+
except Exception as e:
|
120
|
+
logger.warning(f"Failed to initialize LLM for model {model_name}: {e}")
|
121
|
+
|
122
|
+
def _select_model_for_task(
|
123
|
+
self,
|
124
|
+
task_description: str,
|
125
|
+
tools: Optional[List[Any]] = None,
|
126
|
+
context_size: Optional[int] = None
|
127
|
+
) -> str:
|
128
|
+
"""
|
129
|
+
Select the most appropriate model for a given task.
|
130
|
+
|
131
|
+
Args:
|
132
|
+
task_description: Description of the task
|
133
|
+
tools: Tools required for the task
|
134
|
+
context_size: Estimated context size
|
135
|
+
|
136
|
+
Returns:
|
137
|
+
Selected model name
|
138
|
+
"""
|
139
|
+
if self.routing_strategy == "manual":
|
140
|
+
# Use the configured primary model from llm_model property
|
141
|
+
llm_model = self.llm_model
|
142
|
+
if hasattr(llm_model, 'model'):
|
143
|
+
# If it's an LLM instance, get the model name
|
144
|
+
return llm_model.model
|
145
|
+
elif isinstance(llm_model, str):
|
146
|
+
# If it's a string, use it directly
|
147
|
+
return llm_model
|
148
|
+
# Fallback if no model is configured
|
149
|
+
return self.fallback_model
|
150
|
+
|
151
|
+
# Determine required capabilities
|
152
|
+
required_capabilities = []
|
153
|
+
if tools:
|
154
|
+
required_capabilities.append("function-calling")
|
155
|
+
|
156
|
+
# Determine budget consciousness based on strategy
|
157
|
+
budget_conscious = self.routing_strategy in ["auto", "cost-optimized"]
|
158
|
+
|
159
|
+
# Get tool names for analysis
|
160
|
+
tool_names = []
|
161
|
+
if tools:
|
162
|
+
tool_names = [t.__name__ if hasattr(t, '__name__') else str(t) for t in tools]
|
163
|
+
|
164
|
+
# Use router to select model
|
165
|
+
selected_model = self.model_router.select_model(
|
166
|
+
task_description=task_description,
|
167
|
+
required_capabilities=required_capabilities,
|
168
|
+
tools_required=tool_names,
|
169
|
+
context_size=context_size,
|
170
|
+
budget_conscious=budget_conscious
|
171
|
+
)
|
172
|
+
|
173
|
+
# Ensure selected model is available
|
174
|
+
if selected_model not in self._llm_instances:
|
175
|
+
logger.warning(f"Selected model {selected_model} not available, using fallback")
|
176
|
+
return self.fallback_model
|
177
|
+
|
178
|
+
return selected_model
|
179
|
+
|
180
|
+
def _execute_with_model(
|
181
|
+
self,
|
182
|
+
model_name: str,
|
183
|
+
prompt: str,
|
184
|
+
context: Optional[str] = None,
|
185
|
+
tools: Optional[List[Any]] = None,
|
186
|
+
**kwargs
|
187
|
+
) -> str:
|
188
|
+
"""
|
189
|
+
Execute a task with a specific model.
|
190
|
+
|
191
|
+
Args:
|
192
|
+
model_name: Name of the model to use
|
193
|
+
prompt: The prompt to send to the model
|
194
|
+
context: Additional context
|
195
|
+
tools: Tools to make available
|
196
|
+
**kwargs: Additional arguments for the LLM
|
197
|
+
|
198
|
+
Returns:
|
199
|
+
Model response
|
200
|
+
"""
|
201
|
+
llm_instance = self._llm_instances.get(model_name)
|
202
|
+
if not llm_instance:
|
203
|
+
logger.error(f"Model {model_name} not initialized, using fallback")
|
204
|
+
llm_instance = self._llm_instances.get(self.fallback_model)
|
205
|
+
model_name = self.fallback_model
|
206
|
+
|
207
|
+
if not llm_instance:
|
208
|
+
raise ValueError("No LLM instance available for execution")
|
209
|
+
|
210
|
+
# Prepare the full prompt
|
211
|
+
full_prompt = prompt
|
212
|
+
if context:
|
213
|
+
full_prompt = f"{context}\n\n{prompt}"
|
214
|
+
|
215
|
+
try:
|
216
|
+
# Execute with the selected model
|
217
|
+
response = llm_instance.get_response(
|
218
|
+
prompt=full_prompt,
|
219
|
+
system_prompt=self._build_system_prompt(),
|
220
|
+
tools=tools,
|
221
|
+
verbose=self.verbose,
|
222
|
+
markdown=self.markdown,
|
223
|
+
stream=self.stream,
|
224
|
+
agent_name=self.name,
|
225
|
+
agent_role=self.role,
|
226
|
+
agent_tools=[t.__name__ if hasattr(t, '__name__') else str(t) for t in (tools or [])],
|
227
|
+
execute_tool_fn=self.execute_tool if tools else None,
|
228
|
+
**kwargs
|
229
|
+
)
|
230
|
+
|
231
|
+
# Update usage statistics
|
232
|
+
self.model_usage_stats[model_name]['calls'] += 1
|
233
|
+
|
234
|
+
# TODO: Implement token tracking when LLM.get_response() is updated to return token usage
|
235
|
+
# The LLM response currently returns only text, but litellm provides usage info in:
|
236
|
+
# response.get("usage") with prompt_tokens, completion_tokens, and total_tokens
|
237
|
+
# This would require modifying the LLM class to return both text and metadata
|
238
|
+
|
239
|
+
return response
|
240
|
+
|
241
|
+
except Exception as e:
|
242
|
+
logger.error(f"Error executing with model {model_name}: {e}")
|
243
|
+
|
244
|
+
# Try fallback model if different
|
245
|
+
if model_name != self.fallback_model and self.fallback_model in self._llm_instances:
|
246
|
+
logger.info(f"Attempting with fallback model: {self.fallback_model}")
|
247
|
+
return self._execute_with_model(
|
248
|
+
self.fallback_model, prompt, context, tools, **kwargs
|
249
|
+
)
|
250
|
+
|
251
|
+
raise
|
252
|
+
|
253
|
+
def execute(
|
254
|
+
self,
|
255
|
+
task_description: str,
|
256
|
+
context: Optional[str] = None,
|
257
|
+
tools: Optional[List[Any]] = None,
|
258
|
+
**kwargs
|
259
|
+
) -> str:
|
260
|
+
"""
|
261
|
+
Execute a task with automatic model selection.
|
262
|
+
|
263
|
+
This method overrides the parent Agent's execute method to add
|
264
|
+
intelligent model selection.
|
265
|
+
|
266
|
+
Args:
|
267
|
+
task_description: Description of the task to execute
|
268
|
+
context: Optional context for the task
|
269
|
+
tools: Optional tools to use
|
270
|
+
**kwargs: Additional arguments
|
271
|
+
|
272
|
+
Returns:
|
273
|
+
Task execution result
|
274
|
+
"""
|
275
|
+
# Estimate context size in tokens (rough estimate: ~4 chars per token)
|
276
|
+
# This is a simplified heuristic; actual tokenization varies by model
|
277
|
+
text_length = len(task_description) + (len(context) if context else 0)
|
278
|
+
context_size = text_length // 4 # Approximate token count
|
279
|
+
|
280
|
+
# Select the best model for this task
|
281
|
+
selected_model = self._select_model_for_task(
|
282
|
+
task_description=task_description,
|
283
|
+
tools=tools,
|
284
|
+
context_size=context_size
|
285
|
+
)
|
286
|
+
|
287
|
+
logger.info(f"RouterAgent '{self.name}' selected model: {selected_model} for task")
|
288
|
+
|
289
|
+
# Execute with the selected model
|
290
|
+
return self._execute_with_model(
|
291
|
+
model_name=selected_model,
|
292
|
+
prompt=task_description,
|
293
|
+
context=context,
|
294
|
+
tools=tools,
|
295
|
+
**kwargs
|
296
|
+
)
|
297
|
+
|
298
|
+
def get_usage_report(self) -> Dict[str, Any]:
|
299
|
+
"""
|
300
|
+
Get a report of model usage statistics.
|
301
|
+
|
302
|
+
Returns:
|
303
|
+
Dictionary containing usage statistics and cost estimates
|
304
|
+
"""
|
305
|
+
total_cost = 0.0
|
306
|
+
report = {
|
307
|
+
'agent_name': self.name,
|
308
|
+
'routing_strategy': self.routing_strategy,
|
309
|
+
'model_usage': {}
|
310
|
+
}
|
311
|
+
|
312
|
+
for model, stats in self.model_usage_stats.items():
|
313
|
+
model_info = self.model_router.get_model_info(model)
|
314
|
+
if model_info and stats['tokens'] > 0:
|
315
|
+
cost = self.model_router.estimate_cost(model, stats['tokens'])
|
316
|
+
stats['cost'] = cost
|
317
|
+
total_cost += cost
|
318
|
+
|
319
|
+
report['model_usage'][model] = stats
|
320
|
+
|
321
|
+
report['total_cost_estimate'] = total_cost
|
322
|
+
report['total_calls'] = sum(s['calls'] for s in self.model_usage_stats.values())
|
323
|
+
|
324
|
+
return report
|
325
|
+
|
326
|
+
def _build_system_prompt(self) -> str:
|
327
|
+
"""Build system prompt (inherited from parent but can be customized)."""
|
328
|
+
base_prompt = super()._build_system_prompt()
|
329
|
+
|
330
|
+
# Add multi-model context if needed
|
331
|
+
if self.routing_strategy == "auto":
|
332
|
+
base_prompt += "\n\nNote: You are part of a multi-model system. Focus on your specific task."
|
333
|
+
|
334
|
+
return base_prompt
|
@@ -33,6 +33,12 @@ from .model_capabilities import (
|
|
33
33
|
supports_structured_outputs,
|
34
34
|
supports_streaming_with_tools
|
35
35
|
)
|
36
|
+
from .model_router import (
|
37
|
+
ModelRouter,
|
38
|
+
ModelProfile,
|
39
|
+
TaskComplexity,
|
40
|
+
create_routing_agent
|
41
|
+
)
|
36
42
|
|
37
43
|
# Ensure telemetry is disabled after import as well
|
38
44
|
try:
|
@@ -55,5 +61,9 @@ __all__ = [
|
|
55
61
|
"ToolCall",
|
56
62
|
"process_stream_chunks",
|
57
63
|
"supports_structured_outputs",
|
58
|
-
"supports_streaming_with_tools"
|
64
|
+
"supports_streaming_with_tools",
|
65
|
+
"ModelRouter",
|
66
|
+
"ModelProfile",
|
67
|
+
"TaskComplexity",
|
68
|
+
"create_routing_agent"
|
59
69
|
]
|