fast-agent-mcp 0.1.6__py3-none-any.whl → 0.1.8__py3-none-any.whl
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.
- {fast_agent_mcp-0.1.6.dist-info → fast_agent_mcp-0.1.8.dist-info}/METADATA +12 -6
- {fast_agent_mcp-0.1.6.dist-info → fast_agent_mcp-0.1.8.dist-info}/RECORD +23 -22
- mcp_agent/core/agent_app.py +38 -24
- mcp_agent/core/decorators.py +3 -2
- mcp_agent/core/enhanced_prompt.py +106 -20
- mcp_agent/core/factory.py +28 -66
- mcp_agent/human_input/handler.py +4 -1
- mcp_agent/mcp/mcp_aggregator.py +16 -12
- mcp_agent/resources/examples/researcher/researcher-eval.py +1 -1
- mcp_agent/resources/examples/researcher/researcher.py +1 -1
- mcp_agent/resources/examples/workflows/orchestrator.py +5 -4
- mcp_agent/resources/examples/workflows/router.py +0 -2
- mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +57 -87
- mcp_agent/workflows/llm/augmented_llm.py +25 -84
- mcp_agent/workflows/llm/augmented_llm_anthropic.py +8 -30
- mcp_agent/workflows/llm/augmented_llm_openai.py +34 -40
- mcp_agent/workflows/llm/augmented_llm_passthrough.py +61 -0
- mcp_agent/workflows/llm/model_factory.py +5 -3
- mcp_agent/workflows/orchestrator/orchestrator.py +62 -153
- mcp_agent/workflows/router/router_llm.py +18 -24
- {fast_agent_mcp-0.1.6.dist-info → fast_agent_mcp-0.1.8.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.1.6.dist-info → fast_agent_mcp-0.1.8.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.1.6.dist-info → fast_agent_mcp-0.1.8.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
from typing import Any, List, Optional, Type, Union
|
2
|
+
|
3
|
+
from pydantic_core import from_json
|
4
|
+
from mcp_agent.workflows.llm.augmented_llm import (
|
5
|
+
AugmentedLLM,
|
6
|
+
MessageParamT,
|
7
|
+
MessageT,
|
8
|
+
ModelT,
|
9
|
+
RequestParams,
|
10
|
+
)
|
11
|
+
|
12
|
+
|
13
|
+
class PassthroughLLM(AugmentedLLM):
|
14
|
+
"""
|
15
|
+
A specialized LLM implementation that simply passes through input messages without modification.
|
16
|
+
|
17
|
+
This is useful for cases where you need an object with the AugmentedLLM interface
|
18
|
+
but want to preserve the original message without any processing, such as in a
|
19
|
+
parallel workflow where no fan-in aggregation is needed.
|
20
|
+
"""
|
21
|
+
|
22
|
+
def __init__(self, name: str = "Passthrough", context=None, **kwargs):
|
23
|
+
super().__init__(name=name, context=context, **kwargs)
|
24
|
+
|
25
|
+
async def generate(
|
26
|
+
self,
|
27
|
+
message: Union[str, MessageParamT, List[MessageParamT]],
|
28
|
+
request_params: Optional[RequestParams] = None,
|
29
|
+
) -> Union[List[MessageT], Any]:
|
30
|
+
"""Simply return the input message as is."""
|
31
|
+
# Return in the format expected by the caller
|
32
|
+
return [message] if isinstance(message, list) else message
|
33
|
+
|
34
|
+
async def generate_str(
|
35
|
+
self,
|
36
|
+
message: Union[str, MessageParamT, List[MessageParamT]],
|
37
|
+
request_params: Optional[RequestParams] = None,
|
38
|
+
) -> str:
|
39
|
+
"""Return the input message as a string."""
|
40
|
+
self.show_user_message(message, model="fastagent-passthrough", chat_turn=0)
|
41
|
+
await self.show_assistant_message(message, title="ASSISTANT/PASSTHROUGH")
|
42
|
+
|
43
|
+
return str(message)
|
44
|
+
|
45
|
+
async def generate_structured(
|
46
|
+
self,
|
47
|
+
message: Union[str, MessageParamT, List[MessageParamT]],
|
48
|
+
response_model: Type[ModelT],
|
49
|
+
request_params: Optional[RequestParams] = None,
|
50
|
+
) -> ModelT:
|
51
|
+
"""
|
52
|
+
Return the input message as the requested model type.
|
53
|
+
This is a best-effort implementation - it may fail if the
|
54
|
+
message cannot be converted to the requested model.
|
55
|
+
"""
|
56
|
+
if isinstance(message, response_model):
|
57
|
+
return message
|
58
|
+
elif isinstance(message, dict):
|
59
|
+
return response_model(**message)
|
60
|
+
elif isinstance(message, str):
|
61
|
+
return response_model.model_validate(from_json(message, allow_partial=True))
|
@@ -7,7 +7,7 @@ from mcp_agent.core.exceptions import ModelConfigError
|
|
7
7
|
from mcp_agent.workflows.llm.augmented_llm_anthropic import AnthropicAugmentedLLM
|
8
8
|
from mcp_agent.workflows.llm.augmented_llm_openai import OpenAIAugmentedLLM
|
9
9
|
from mcp_agent.workflows.llm.augmented_llm import RequestParams
|
10
|
-
from mcp_agent.workflows.llm.
|
10
|
+
from mcp_agent.workflows.llm.augmented_llm_passthrough import PassthroughLLM
|
11
11
|
|
12
12
|
# Type alias for LLM classes
|
13
13
|
LLMClass = Union[Type[AnthropicAugmentedLLM], Type[OpenAIAugmentedLLM]]
|
@@ -18,7 +18,7 @@ class Provider(Enum):
|
|
18
18
|
|
19
19
|
ANTHROPIC = auto()
|
20
20
|
OPENAI = auto()
|
21
|
-
|
21
|
+
FAST_AGENT = auto()
|
22
22
|
|
23
23
|
|
24
24
|
class ReasoningEffort(Enum):
|
@@ -45,6 +45,7 @@ class ModelFactory:
|
|
45
45
|
PROVIDER_MAP = {
|
46
46
|
"anthropic": Provider.ANTHROPIC,
|
47
47
|
"openai": Provider.OPENAI,
|
48
|
+
"fast-agent": Provider.FAST_AGENT,
|
48
49
|
}
|
49
50
|
|
50
51
|
# Mapping of effort strings to enum values
|
@@ -59,6 +60,7 @@ class ModelFactory:
|
|
59
60
|
# TODO -- bring model parameter configuration here
|
60
61
|
# Mapping of model names to their default providers
|
61
62
|
DEFAULT_PROVIDERS = {
|
63
|
+
"passthrough": Provider.FAST_AGENT,
|
62
64
|
"gpt-4o": Provider.OPENAI,
|
63
65
|
"gpt-4o-mini": Provider.OPENAI,
|
64
66
|
"o1-mini": Provider.OPENAI,
|
@@ -93,7 +95,7 @@ class ModelFactory:
|
|
93
95
|
PROVIDER_CLASSES: Dict[Provider, LLMClass] = {
|
94
96
|
Provider.ANTHROPIC: AnthropicAugmentedLLM,
|
95
97
|
Provider.OPENAI: OpenAIAugmentedLLM,
|
96
|
-
Provider.
|
98
|
+
Provider.FAST_AGENT: PassthroughLLM,
|
97
99
|
}
|
98
100
|
|
99
101
|
@classmethod
|
@@ -6,8 +6,8 @@ from typing import (
|
|
6
6
|
List,
|
7
7
|
Literal,
|
8
8
|
Optional,
|
9
|
-
Type,
|
10
9
|
TYPE_CHECKING,
|
10
|
+
Type,
|
11
11
|
)
|
12
12
|
|
13
13
|
from mcp_agent.agents.agent import Agent
|
@@ -132,21 +132,7 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
132
132
|
# Store agents by name - COMPLETE REWRITE OF AGENT STORAGE
|
133
133
|
self.agents = {}
|
134
134
|
for agent in available_agents:
|
135
|
-
# Fix: Remove all special handling of agent names and store them exactly as they are
|
136
135
|
agent_name = agent.name
|
137
|
-
|
138
|
-
# Verify if the name is actually "None" (string) or None (NoneType)
|
139
|
-
if agent_name == "None":
|
140
|
-
# Try to get a better name from config if available
|
141
|
-
if hasattr(agent, "config") and agent.config and agent.config.name:
|
142
|
-
agent_name = agent.config.name
|
143
|
-
elif agent_name is None:
|
144
|
-
# Try to get a better name from config if available
|
145
|
-
if hasattr(agent, "config") and agent.config and agent.config.name:
|
146
|
-
agent_name = agent.config.name
|
147
|
-
else:
|
148
|
-
agent_name = f"unnamed_agent_{len(self.agents)}"
|
149
|
-
|
150
136
|
self.logger.info(f"Adding agent '{agent_name}' to orchestrator")
|
151
137
|
self.agents[agent_name] = agent
|
152
138
|
|
@@ -169,7 +155,7 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
169
155
|
) -> str:
|
170
156
|
"""Request an LLM generation and return the string representation of the result"""
|
171
157
|
params = self.get_request_params(request_params)
|
172
|
-
|
158
|
+
|
173
159
|
result = await self.generate(
|
174
160
|
message=message,
|
175
161
|
request_params=params,
|
@@ -183,30 +169,7 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
183
169
|
response_model: Type[ModelT],
|
184
170
|
request_params: RequestParams | None = None,
|
185
171
|
) -> ModelT:
|
186
|
-
|
187
|
-
import json
|
188
|
-
from pydantic import ValidationError
|
189
|
-
|
190
|
-
params = self.get_request_params(request_params)
|
191
|
-
result_str = await self.generate_str(message=message, request_params=params)
|
192
|
-
|
193
|
-
try:
|
194
|
-
# Directly parse JSON and create model instance
|
195
|
-
parsed_data = json.loads(result_str)
|
196
|
-
return response_model(**parsed_data)
|
197
|
-
except (json.JSONDecodeError, ValidationError) as e:
|
198
|
-
# Log the error and fall back to the original method if direct parsing fails
|
199
|
-
self.logger.error(
|
200
|
-
f"Direct JSON parsing failed: {str(e)}. Falling back to standard method."
|
201
|
-
)
|
202
|
-
self.logger.debug(f"Failed JSON content: {result_str}")
|
203
|
-
|
204
|
-
# Use AugmentedLLM's structured output handling as fallback
|
205
|
-
return await super().generate_structured(
|
206
|
-
message=result_str,
|
207
|
-
response_model=response_model,
|
208
|
-
request_params=params,
|
209
|
-
)
|
172
|
+
return None
|
210
173
|
|
211
174
|
async def execute(
|
212
175
|
self, objective: str, request_params: RequestParams | None = None
|
@@ -299,9 +262,11 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
299
262
|
plan_result.add_step_result(step_result)
|
300
263
|
total_steps_executed += 1
|
301
264
|
|
302
|
-
# Check
|
303
|
-
if
|
304
|
-
plan_result
|
265
|
+
# Check if we need to break from the main loop due to hitting max_steps
|
266
|
+
if (
|
267
|
+
hasattr(plan_result, "max_steps_reached")
|
268
|
+
and plan_result.max_steps_reached
|
269
|
+
):
|
305
270
|
break
|
306
271
|
|
307
272
|
logger.debug(
|
@@ -317,21 +282,38 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
317
282
|
|
318
283
|
iterations += 1
|
319
284
|
|
320
|
-
# If we
|
321
|
-
|
322
|
-
|
323
|
-
)
|
285
|
+
# If we reach here, either:
|
286
|
+
# 1. We hit iteration limit without completing
|
287
|
+
# 2. We hit max_steps limit without completing
|
288
|
+
# 3. We detected diminishing returns (plan with 0-1 steps after multiple iterations)
|
324
289
|
|
325
|
-
#
|
326
|
-
|
290
|
+
# Check if we hit iteration limits without completing
|
291
|
+
if iterations >= params.max_iterations and not plan_result.is_complete:
|
292
|
+
self.logger.warning(
|
293
|
+
f"Failed to complete in {params.max_iterations} iterations."
|
294
|
+
)
|
295
|
+
# Mark that we hit the iteration limit
|
296
|
+
plan_result.max_iterations_reached = True
|
327
297
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
298
|
+
# Use the incomplete template when we've hit iteration limits
|
299
|
+
synthesis_prompt = SYNTHESIZE_INCOMPLETE_PLAN_TEMPLATE.format(
|
300
|
+
plan_result=format_plan_result(plan_result),
|
301
|
+
max_iterations=params.max_iterations,
|
302
|
+
)
|
303
|
+
else:
|
304
|
+
# Either plan is complete or we had diminishing returns (which we mark as complete)
|
305
|
+
if not plan_result.is_complete:
|
306
|
+
self.logger.info(
|
307
|
+
"Plan terminated due to diminishing returns, marking as complete"
|
308
|
+
)
|
309
|
+
plan_result.is_complete = True
|
310
|
+
|
311
|
+
# Use standard template for complete plans
|
312
|
+
synthesis_prompt = SYNTHESIZE_PLAN_PROMPT_TEMPLATE.format(
|
313
|
+
plan_result=format_plan_result(plan_result)
|
314
|
+
)
|
333
315
|
|
334
|
-
# Generate
|
316
|
+
# Generate the final synthesis with the appropriate template
|
335
317
|
plan_result.result = await self.planner.generate_str(
|
336
318
|
message=synthesis_prompt,
|
337
319
|
request_params=params.model_copy(update={"max_iterations": 1}),
|
@@ -359,8 +341,6 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
359
341
|
# Make sure we're using a valid agent name
|
360
342
|
agent = self.agents.get(task.agent)
|
361
343
|
if not agent:
|
362
|
-
# Log a more prominent error - this is a serious problem that shouldn't happen
|
363
|
-
# with the improved prompt
|
364
344
|
self.logger.error(
|
365
345
|
f"AGENT VALIDATION ERROR: No agent found matching '{task.agent}'. Available agents: {list(self.agents.keys())}"
|
366
346
|
)
|
@@ -431,18 +411,10 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
431
411
|
request_params: RequestParams | None = None,
|
432
412
|
) -> Plan:
|
433
413
|
"""Generate full plan considering previous results"""
|
434
|
-
import json
|
435
|
-
from pydantic import ValidationError
|
436
|
-
from mcp_agent.workflows.orchestrator.orchestrator_models import (
|
437
|
-
Plan,
|
438
|
-
Step,
|
439
|
-
AgentTask,
|
440
|
-
)
|
441
414
|
|
442
415
|
params = self.get_request_params(request_params)
|
443
416
|
params = params.model_copy(update={"use_history": False})
|
444
417
|
|
445
|
-
# Format agents without numeric prefixes for cleaner XML
|
446
418
|
agent_formats = []
|
447
419
|
for agent_name in self.agents.keys():
|
448
420
|
formatted = self._format_agent_info(agent_name)
|
@@ -452,7 +424,7 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
452
424
|
|
453
425
|
# Create clear plan status indicator for the template
|
454
426
|
plan_status = "Plan Status: Not Started"
|
455
|
-
if
|
427
|
+
if plan_result.is_complete:
|
456
428
|
plan_status = (
|
457
429
|
"Plan Status: Complete"
|
458
430
|
if plan_result.is_complete
|
@@ -478,49 +450,30 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
478
450
|
)
|
479
451
|
|
480
452
|
# Get raw JSON response from LLM
|
481
|
-
|
453
|
+
return await self.planner.generate_structured(
|
482
454
|
message=prompt,
|
483
455
|
request_params=params,
|
456
|
+
response_model=Plan,
|
484
457
|
)
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
steps.append(step)
|
505
|
-
|
506
|
-
# Create final Plan
|
507
|
-
plan = Plan(steps=steps, is_complete=data.get("is_complete", False))
|
508
|
-
|
509
|
-
return plan
|
510
|
-
|
511
|
-
except (json.JSONDecodeError, ValidationError, KeyError) as e:
|
512
|
-
# Log detailed error and fall back to the original method as last resort
|
513
|
-
self.logger.error(f"Error parsing plan JSON: {str(e)}")
|
514
|
-
self.logger.debug(f"Failed JSON content: {result_str}")
|
515
|
-
|
516
|
-
# Use the normal structured parsing as fallback
|
517
|
-
plan = await self.planner.generate_structured(
|
518
|
-
message=result_str,
|
519
|
-
response_model=Plan,
|
520
|
-
request_params=params,
|
521
|
-
)
|
522
|
-
|
523
|
-
return plan
|
458
|
+
# return data
|
459
|
+
|
460
|
+
# steps = []
|
461
|
+
# for step_data in data.steps:
|
462
|
+
# tasks = []
|
463
|
+
# for task_data in step_data.tasks:
|
464
|
+
# task = AgentTask(
|
465
|
+
# description=task_data.description,
|
466
|
+
# agent=task_data.agent,
|
467
|
+
# )
|
468
|
+
# tasks.append(task)
|
469
|
+
|
470
|
+
# # Create Step with the exact task objects we created
|
471
|
+
# step = Step(description=step_data.description, tasks=tasks)
|
472
|
+
# steps.append(step)
|
473
|
+
|
474
|
+
# # Create final Plan
|
475
|
+
# plan = Plan(steps=steps, is_complete=data.is_complete)
|
476
|
+
# return plan
|
524
477
|
|
525
478
|
async def _get_next_step(
|
526
479
|
self,
|
@@ -529,12 +482,6 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
529
482
|
request_params: RequestParams | None = None,
|
530
483
|
) -> NextStep:
|
531
484
|
"""Generate just the next needed step"""
|
532
|
-
import json
|
533
|
-
from pydantic import ValidationError
|
534
|
-
from mcp_agent.workflows.orchestrator.orchestrator_models import (
|
535
|
-
NextStep,
|
536
|
-
AgentTask,
|
537
|
-
)
|
538
485
|
|
539
486
|
params = self.get_request_params(request_params)
|
540
487
|
params = params.model_copy(update={"use_history": False})
|
@@ -547,7 +494,7 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
547
494
|
|
548
495
|
# Create clear plan status indicator for the template
|
549
496
|
plan_status = "Plan Status: Not Started"
|
550
|
-
if
|
497
|
+
if plan_result:
|
551
498
|
plan_status = (
|
552
499
|
"Plan Status: Complete"
|
553
500
|
if plan_result.is_complete
|
@@ -569,48 +516,10 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
569
516
|
)
|
570
517
|
|
571
518
|
# Get raw JSON response from LLM
|
572
|
-
|
573
|
-
message=prompt,
|
574
|
-
request_params=params,
|
519
|
+
return await self.planner.generate_structured(
|
520
|
+
message=prompt, request_params=params, response_model=NextStep
|
575
521
|
)
|
576
522
|
|
577
|
-
try:
|
578
|
-
# Parse JSON directly
|
579
|
-
data = json.loads(result_str)
|
580
|
-
|
581
|
-
# Create task objects manually to preserve exact agent names
|
582
|
-
tasks = []
|
583
|
-
for task_data in data.get("tasks", []):
|
584
|
-
# Preserve the exact agent name as specified in the JSON
|
585
|
-
task = AgentTask(
|
586
|
-
description=task_data.get("description", ""),
|
587
|
-
agent=task_data.get("agent", ""),
|
588
|
-
)
|
589
|
-
tasks.append(task)
|
590
|
-
|
591
|
-
# Create step with manually constructed tasks
|
592
|
-
next_step = NextStep(
|
593
|
-
description=data.get("description", ""),
|
594
|
-
tasks=tasks,
|
595
|
-
is_complete=data.get("is_complete", False),
|
596
|
-
)
|
597
|
-
|
598
|
-
return next_step
|
599
|
-
|
600
|
-
except (json.JSONDecodeError, ValidationError, KeyError) as e:
|
601
|
-
# Log detailed error and fall back to the original method
|
602
|
-
self.logger.error(f"Error parsing next step JSON: {str(e)}")
|
603
|
-
self.logger.debug(f"Failed JSON content: {result_str}")
|
604
|
-
|
605
|
-
# Use the normal structured parsing as fallback
|
606
|
-
next_step = await self.planner.generate_structured(
|
607
|
-
message=result_str,
|
608
|
-
response_model=NextStep,
|
609
|
-
request_params=params,
|
610
|
-
)
|
611
|
-
|
612
|
-
return next_step
|
613
|
-
|
614
523
|
def _format_server_info(self, server_name: str) -> str:
|
615
524
|
"""Format server information for display to planners using XML tags"""
|
616
525
|
from mcp_agent.workflows.llm.prompt_utils import format_server_info
|
@@ -13,7 +13,8 @@ if TYPE_CHECKING:
|
|
13
13
|
|
14
14
|
logger = get_logger(__name__)
|
15
15
|
|
16
|
-
|
16
|
+
# TODO -- reinstate function/server routing
|
17
|
+
# TODO -- Generate the Example Schema from the Pydantic Model
|
17
18
|
DEFAULT_ROUTING_INSTRUCTION = """
|
18
19
|
You are a highly accurate request router that directs incoming requests to the most appropriate category.
|
19
20
|
A category is a specialized destination, such as a Function, an MCP Server (a collection of tools/functions), or an Agent (a collection of servers).
|
@@ -34,7 +35,7 @@ Your task is to analyze the request and determine the most appropriate categorie
|
|
34
35
|
- Whether the request might benefit from multiple categories (up to {top_k})
|
35
36
|
|
36
37
|
<fastagent:instruction>
|
37
|
-
Respond in JSON format:
|
38
|
+
Respond in JSON format. NEVER include Code Fences:
|
38
39
|
{{
|
39
40
|
"categories": [
|
40
41
|
{{
|
@@ -65,37 +66,31 @@ Follow these guidelines:
|
|
65
66
|
"""
|
66
67
|
|
67
68
|
|
68
|
-
class
|
69
|
-
"""
|
69
|
+
class ConfidenceRating(BaseModel):
|
70
|
+
"""Base class for models with confidence ratings and reasoning"""
|
70
71
|
|
71
|
-
confidence: Literal["high", "medium", "low"]
|
72
72
|
"""The confidence level of the routing decision."""
|
73
|
+
confidence: Literal["high", "medium", "low"]
|
74
|
+
"""A brief explanation of the routing decision."""
|
75
|
+
reasoning: str | None = None # Make nullable to support both use cases
|
73
76
|
|
74
|
-
reasoning: str | None = None
|
75
|
-
"""
|
76
|
-
A brief explanation of the routing decision.
|
77
|
-
This is optional and may only be provided if the router is an LLM
|
78
|
-
"""
|
79
|
-
|
80
|
-
|
81
|
-
class StructuredResponseCategory(BaseModel):
|
82
|
-
"""A class that represents a single category returned by an LLM router"""
|
83
77
|
|
84
|
-
|
78
|
+
# Used for LLM output parsing
|
79
|
+
class StructuredResponseCategory(ConfidenceRating):
|
85
80
|
"""The name of the category (i.e. MCP server, Agent or function) to route the input to."""
|
86
81
|
|
87
|
-
|
88
|
-
"""The confidence level of the routing decision."""
|
89
|
-
|
90
|
-
reasoning: str | None = None
|
91
|
-
"""A brief explanation of the routing decision."""
|
82
|
+
category: str # Category name for lookup
|
92
83
|
|
93
84
|
|
94
85
|
class StructuredResponse(BaseModel):
|
95
|
-
"""A class that represents the structured response of an LLM router"""
|
96
|
-
|
97
86
|
categories: List[StructuredResponseCategory]
|
98
|
-
|
87
|
+
|
88
|
+
|
89
|
+
# Used for final router output
|
90
|
+
class LLMRouterResult(RouterResult[ResultT], ConfidenceRating):
|
91
|
+
# Inherits 'result' from RouterResult
|
92
|
+
# Inherits 'confidence' and 'reasoning' from ConfidenceRating
|
93
|
+
pass
|
99
94
|
|
100
95
|
|
101
96
|
class LLMRouter(Router):
|
@@ -282,7 +277,6 @@ class LLMRouter(Router):
|
|
282
277
|
for r in response.categories:
|
283
278
|
router_category = self.categories.get(r.category)
|
284
279
|
if not router_category:
|
285
|
-
# Skip invalid categories
|
286
280
|
# TODO: log or raise an error
|
287
281
|
continue
|
288
282
|
|
File without changes
|
File without changes
|
File without changes
|