fast-agent-mcp 0.1.11__py3-none-any.whl → 0.1.13__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.11.dist-info → fast_agent_mcp-0.1.13.dist-info}/METADATA +1 -1
- fast_agent_mcp-0.1.13.dist-info/RECORD +164 -0
- mcp_agent/agents/agent.py +37 -102
- mcp_agent/app.py +16 -27
- mcp_agent/cli/commands/bootstrap.py +22 -52
- mcp_agent/cli/commands/config.py +4 -4
- mcp_agent/cli/commands/setup.py +11 -26
- mcp_agent/cli/main.py +6 -9
- mcp_agent/cli/terminal.py +2 -2
- mcp_agent/config.py +1 -5
- mcp_agent/context.py +13 -26
- mcp_agent/context_dependent.py +3 -7
- mcp_agent/core/agent_app.py +46 -122
- mcp_agent/core/agent_types.py +29 -2
- mcp_agent/core/agent_utils.py +3 -5
- mcp_agent/core/decorators.py +6 -14
- mcp_agent/core/enhanced_prompt.py +25 -52
- mcp_agent/core/error_handling.py +1 -1
- mcp_agent/core/exceptions.py +8 -8
- mcp_agent/core/factory.py +30 -72
- mcp_agent/core/fastagent.py +48 -88
- mcp_agent/core/mcp_content.py +10 -19
- mcp_agent/core/prompt.py +8 -15
- mcp_agent/core/proxies.py +34 -25
- mcp_agent/core/request_params.py +46 -0
- mcp_agent/core/types.py +6 -6
- mcp_agent/core/validation.py +16 -16
- mcp_agent/executor/decorator_registry.py +11 -23
- mcp_agent/executor/executor.py +8 -17
- mcp_agent/executor/task_registry.py +2 -4
- mcp_agent/executor/temporal.py +28 -74
- mcp_agent/executor/workflow.py +3 -5
- mcp_agent/executor/workflow_signal.py +17 -29
- mcp_agent/human_input/handler.py +4 -9
- mcp_agent/human_input/types.py +2 -3
- mcp_agent/logging/events.py +1 -5
- mcp_agent/logging/json_serializer.py +7 -6
- mcp_agent/logging/listeners.py +20 -23
- mcp_agent/logging/logger.py +15 -17
- mcp_agent/logging/rich_progress.py +10 -8
- mcp_agent/logging/tracing.py +4 -6
- mcp_agent/logging/transport.py +24 -24
- mcp_agent/mcp/gen_client.py +4 -12
- mcp_agent/mcp/interfaces.py +107 -88
- mcp_agent/mcp/mcp_agent_client_session.py +11 -19
- mcp_agent/mcp/mcp_agent_server.py +8 -10
- mcp_agent/mcp/mcp_aggregator.py +49 -122
- mcp_agent/mcp/mcp_connection_manager.py +16 -37
- mcp_agent/mcp/prompt_message_multipart.py +12 -18
- mcp_agent/mcp/prompt_serialization.py +13 -38
- mcp_agent/mcp/prompts/prompt_load.py +99 -0
- mcp_agent/mcp/prompts/prompt_server.py +21 -128
- mcp_agent/mcp/prompts/prompt_template.py +20 -42
- mcp_agent/mcp/resource_utils.py +8 -17
- mcp_agent/mcp/sampling.py +62 -64
- mcp_agent/mcp/stdio.py +11 -8
- mcp_agent/mcp_server/__init__.py +1 -1
- mcp_agent/mcp_server/agent_server.py +10 -17
- mcp_agent/mcp_server_registry.py +13 -35
- mcp_agent/resources/examples/data-analysis/analysis-campaign.py +1 -1
- mcp_agent/resources/examples/data-analysis/analysis.py +1 -1
- mcp_agent/resources/examples/data-analysis/slides.py +110 -0
- mcp_agent/resources/examples/internal/agent.py +2 -1
- mcp_agent/resources/examples/internal/job.py +2 -1
- mcp_agent/resources/examples/internal/prompt_category.py +1 -1
- mcp_agent/resources/examples/internal/prompt_sizing.py +3 -5
- mcp_agent/resources/examples/internal/sizer.py +2 -1
- mcp_agent/resources/examples/internal/social.py +2 -1
- mcp_agent/resources/examples/mcp_researcher/researcher-eval.py +1 -1
- mcp_agent/resources/examples/prompting/__init__.py +1 -1
- mcp_agent/resources/examples/prompting/agent.py +2 -1
- mcp_agent/resources/examples/prompting/image_server.py +5 -11
- mcp_agent/resources/examples/researcher/researcher-eval.py +1 -1
- mcp_agent/resources/examples/researcher/researcher-imp.py +3 -4
- mcp_agent/resources/examples/researcher/researcher.py +2 -1
- mcp_agent/resources/examples/workflows/agent_build.py +2 -1
- mcp_agent/resources/examples/workflows/chaining.py +2 -1
- mcp_agent/resources/examples/workflows/evaluator.py +2 -1
- mcp_agent/resources/examples/workflows/human_input.py +2 -1
- mcp_agent/resources/examples/workflows/orchestrator.py +2 -1
- mcp_agent/resources/examples/workflows/parallel.py +2 -1
- mcp_agent/resources/examples/workflows/router.py +2 -1
- mcp_agent/resources/examples/workflows/sse.py +1 -1
- mcp_agent/telemetry/usage_tracking.py +2 -1
- mcp_agent/ui/console_display.py +17 -41
- mcp_agent/workflows/embedding/embedding_base.py +1 -4
- mcp_agent/workflows/embedding/embedding_cohere.py +2 -2
- mcp_agent/workflows/embedding/embedding_openai.py +4 -13
- mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +23 -57
- mcp_agent/workflows/intent_classifier/intent_classifier_base.py +5 -8
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding.py +7 -11
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding_cohere.py +4 -8
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding_openai.py +4 -8
- mcp_agent/workflows/intent_classifier/intent_classifier_llm.py +11 -22
- mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py +3 -3
- mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py +4 -6
- mcp_agent/workflows/llm/anthropic_utils.py +8 -29
- mcp_agent/workflows/llm/augmented_llm.py +94 -332
- mcp_agent/workflows/llm/augmented_llm_anthropic.py +43 -76
- mcp_agent/workflows/llm/augmented_llm_openai.py +46 -100
- mcp_agent/workflows/llm/augmented_llm_passthrough.py +42 -20
- mcp_agent/workflows/llm/augmented_llm_playback.py +8 -6
- mcp_agent/workflows/llm/memory.py +103 -0
- mcp_agent/workflows/llm/model_factory.py +9 -21
- mcp_agent/workflows/llm/openai_utils.py +1 -1
- mcp_agent/workflows/llm/prompt_utils.py +39 -27
- mcp_agent/workflows/llm/providers/multipart_converter_anthropic.py +246 -184
- mcp_agent/workflows/llm/providers/multipart_converter_openai.py +212 -202
- mcp_agent/workflows/llm/providers/openai_multipart.py +19 -61
- mcp_agent/workflows/llm/providers/sampling_converter_anthropic.py +11 -212
- mcp_agent/workflows/llm/providers/sampling_converter_openai.py +13 -215
- mcp_agent/workflows/llm/sampling_converter.py +117 -0
- mcp_agent/workflows/llm/sampling_format_converter.py +12 -29
- mcp_agent/workflows/orchestrator/orchestrator.py +24 -67
- mcp_agent/workflows/orchestrator/orchestrator_models.py +14 -40
- mcp_agent/workflows/parallel/fan_in.py +17 -47
- mcp_agent/workflows/parallel/fan_out.py +6 -12
- mcp_agent/workflows/parallel/parallel_llm.py +9 -26
- mcp_agent/workflows/router/router_base.py +29 -59
- mcp_agent/workflows/router/router_embedding.py +11 -25
- mcp_agent/workflows/router/router_embedding_cohere.py +2 -2
- mcp_agent/workflows/router/router_embedding_openai.py +2 -2
- mcp_agent/workflows/router/router_llm.py +12 -28
- mcp_agent/workflows/swarm/swarm.py +20 -48
- mcp_agent/workflows/swarm/swarm_anthropic.py +2 -2
- mcp_agent/workflows/swarm/swarm_openai.py +2 -2
- fast_agent_mcp-0.1.11.dist-info/RECORD +0 -160
- mcp_agent/workflows/llm/llm_selector.py +0 -345
- {fast_agent_mcp-0.1.11.dist-info → fast_agent_mcp-0.1.13.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.1.11.dist-info → fast_agent_mcp-0.1.13.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.1.11.dist-info → fast_agent_mcp-0.1.13.dist-info}/licenses/LICENSE +0 -0
@@ -1,8 +1,11 @@
|
|
1
|
-
from typing import Any, List, Optional, Type, Union
|
2
1
|
import json # Import at the module level
|
2
|
+
from typing import Any, List, Optional, Type, Union
|
3
|
+
|
3
4
|
from mcp import GetPromptResult
|
4
5
|
from mcp.types import PromptMessage
|
5
6
|
from pydantic_core import from_json
|
7
|
+
|
8
|
+
from mcp_agent.logging.logger import get_logger
|
6
9
|
from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
|
7
10
|
from mcp_agent.workflows.llm.augmented_llm import (
|
8
11
|
AugmentedLLM,
|
@@ -11,7 +14,6 @@ from mcp_agent.workflows.llm.augmented_llm import (
|
|
11
14
|
ModelT,
|
12
15
|
RequestParams,
|
13
16
|
)
|
14
|
-
from mcp_agent.logging.logger import get_logger
|
15
17
|
|
16
18
|
|
17
19
|
class PassthroughLLM(AugmentedLLM):
|
@@ -23,7 +25,7 @@ class PassthroughLLM(AugmentedLLM):
|
|
23
25
|
parallel workflow where no fan-in aggregation is needed.
|
24
26
|
"""
|
25
27
|
|
26
|
-
def __init__(self, name: str = "Passthrough", context=None, **kwargs):
|
28
|
+
def __init__(self, name: str = "Passthrough", context=None, **kwargs) -> None:
|
27
29
|
super().__init__(name=name, context=context, **kwargs)
|
28
30
|
self.provider = "fast-agent"
|
29
31
|
# Initialize logger - keep it simple without name reference
|
@@ -61,6 +63,9 @@ class PassthroughLLM(AugmentedLLM):
|
|
61
63
|
|
62
64
|
return str(message)
|
63
65
|
|
66
|
+
async def initialize(self) -> None:
|
67
|
+
pass
|
68
|
+
|
64
69
|
async def _call_tool_and_return_result(self, command: str) -> str:
|
65
70
|
"""
|
66
71
|
Call a tool based on the command and return its result as a string.
|
@@ -94,9 +99,7 @@ class PassthroughLLM(AugmentedLLM):
|
|
94
99
|
"""
|
95
100
|
parts = command.split(" ", 2)
|
96
101
|
if len(parts) < 2:
|
97
|
-
raise ValueError(
|
98
|
-
"Invalid format. Expected '***CALL_TOOL <tool_name> [arguments_json]'"
|
99
|
-
)
|
102
|
+
raise ValueError("Invalid format. Expected '***CALL_TOOL <tool_name> [arguments_json]'")
|
100
103
|
|
101
104
|
tool_name = parts[1].strip()
|
102
105
|
arguments = None
|
@@ -158,15 +161,9 @@ class PassthroughLLM(AugmentedLLM):
|
|
158
161
|
elif isinstance(message, str):
|
159
162
|
return response_model.model_validate(from_json(message, allow_partial=True))
|
160
163
|
|
161
|
-
async def generate_prompt(
|
162
|
-
self, prompt: "PromptMessageMultipart", request_params: RequestParams | None
|
163
|
-
) -> str:
|
164
|
+
async def generate_prompt(self, prompt: "PromptMessageMultipart", request_params: RequestParams | None) -> str:
|
164
165
|
# Check if this prompt contains a tool call command
|
165
|
-
if (
|
166
|
-
prompt.content
|
167
|
-
and prompt.content[0].text
|
168
|
-
and prompt.content[0].text.startswith("***CALL_TOOL ")
|
169
|
-
):
|
166
|
+
if prompt.content and prompt.content[0].text and prompt.content[0].text.startswith("***CALL_TOOL "):
|
170
167
|
return await self._call_tool_and_return_result(prompt.content[0].text)
|
171
168
|
|
172
169
|
# Process all parts of the PromptMessageMultipart
|
@@ -181,21 +178,40 @@ class PassthroughLLM(AugmentedLLM):
|
|
181
178
|
# Join all parts and process with generate_str
|
182
179
|
return await self.generate_str("\n".join(parts_text), request_params)
|
183
180
|
|
184
|
-
async def
|
185
|
-
self,
|
181
|
+
async def apply_prompt(
|
182
|
+
self,
|
183
|
+
multipart_messages: List["PromptMessageMultipart"],
|
184
|
+
request_params: Optional[RequestParams] = None,
|
186
185
|
) -> str:
|
186
|
+
"""
|
187
|
+
Apply a list of PromptMessageMultipart messages directly to the LLM.
|
188
|
+
In PassthroughLLM, this returns a concatenated string of all message content.
|
189
|
+
|
190
|
+
Args:
|
191
|
+
multipart_messages: List of PromptMessageMultipart objects
|
192
|
+
request_params: Optional parameters to configure the LLM request
|
193
|
+
|
194
|
+
Returns:
|
195
|
+
String representation of all message content concatenated together
|
196
|
+
"""
|
197
|
+
# Generate and concatenate result from all messages
|
198
|
+
result = ""
|
199
|
+
for prompt in multipart_messages:
|
200
|
+
result += await self.generate_prompt(prompt, request_params) + "\n"
|
201
|
+
|
202
|
+
return result
|
203
|
+
|
204
|
+
async def apply_prompt_template(self, prompt_result: GetPromptResult, prompt_name: str) -> str:
|
187
205
|
"""
|
188
206
|
Apply a prompt template by adding it to the conversation history.
|
189
|
-
|
190
|
-
generate an assistant response.
|
207
|
+
For PassthroughLLM, this returns all content concatenated together.
|
191
208
|
|
192
209
|
Args:
|
193
210
|
prompt_result: The GetPromptResult containing prompt messages
|
194
211
|
prompt_name: The name of the prompt being applied
|
195
212
|
|
196
213
|
Returns:
|
197
|
-
String representation of
|
198
|
-
or the last assistant message in the prompt
|
214
|
+
String representation of all message content concatenated together
|
199
215
|
"""
|
200
216
|
prompt_messages: List[PromptMessage] = prompt_result.messages
|
201
217
|
|
@@ -210,3 +226,9 @@ class PassthroughLLM(AugmentedLLM):
|
|
210
226
|
arguments=arguments,
|
211
227
|
)
|
212
228
|
self._messages = prompt_messages
|
229
|
+
|
230
|
+
# Convert prompt messages to multipart format
|
231
|
+
multipart_messages = PromptMessageMultipart.to_multipart(prompt_messages)
|
232
|
+
|
233
|
+
# Use apply_prompt to handle the multipart messages
|
234
|
+
return await self.apply_prompt(multipart_messages)
|
@@ -1,9 +1,13 @@
|
|
1
|
-
from typing import List, Optional, Union
|
1
|
+
from typing import TYPE_CHECKING, List, Optional, Union
|
2
|
+
|
2
3
|
from mcp import GetPromptResult
|
3
|
-
|
4
|
+
|
4
5
|
from mcp_agent.workflows.llm.augmented_llm import MessageParamT, RequestParams
|
5
6
|
from mcp_agent.workflows.llm.augmented_llm_passthrough import PassthroughLLM
|
6
7
|
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from mcp.types import PromptMessage
|
10
|
+
|
7
11
|
|
8
12
|
# TODO -- support tool calling
|
9
13
|
class PlaybackLLM(PassthroughLLM):
|
@@ -19,7 +23,7 @@ class PlaybackLLM(PassthroughLLM):
|
|
19
23
|
been played back, it returns a message indicating that messages are exhausted.
|
20
24
|
"""
|
21
25
|
|
22
|
-
def __init__(self, name: str = "Playback", **kwargs):
|
26
|
+
def __init__(self, name: str = "Playback", **kwargs) -> None:
|
23
27
|
super().__init__(name=name, **kwargs)
|
24
28
|
self._messages: List[PromptMessage] = []
|
25
29
|
self._current_index = 0
|
@@ -70,9 +74,7 @@ class PlaybackLLM(PassthroughLLM):
|
|
70
74
|
# If we get here, we've run out of assistant messages
|
71
75
|
return f"MESSAGES EXHAUSTED (list size {len(self._messages)})"
|
72
76
|
|
73
|
-
async def apply_prompt_template(
|
74
|
-
self, prompt_result: GetPromptResult, prompt_name: str
|
75
|
-
) -> str:
|
77
|
+
async def apply_prompt_template(self, prompt_result: GetPromptResult, prompt_name: str) -> str:
|
76
78
|
"""
|
77
79
|
Apply a prompt template by adding its messages to the playback queue.
|
78
80
|
|
@@ -0,0 +1,103 @@
|
|
1
|
+
from typing import Generic, List, Protocol, TypeVar
|
2
|
+
|
3
|
+
# Define our own type variable for implementation use
|
4
|
+
MessageParamT = TypeVar("MessageParamT")
|
5
|
+
|
6
|
+
|
7
|
+
class Memory(Protocol, Generic[MessageParamT]):
|
8
|
+
"""
|
9
|
+
Simple memory management for storing past interactions in-memory.
|
10
|
+
"""
|
11
|
+
|
12
|
+
# TODO: saqadri - add checkpointing and other advanced memory capabilities
|
13
|
+
|
14
|
+
def __init__(self) -> None: ...
|
15
|
+
|
16
|
+
def extend(self, messages: List[MessageParamT], is_prompt: bool = False) -> None: ...
|
17
|
+
|
18
|
+
def set(self, messages: List[MessageParamT], is_prompt: bool = False) -> None: ...
|
19
|
+
|
20
|
+
def append(self, message: MessageParamT, is_prompt: bool = False) -> None: ...
|
21
|
+
|
22
|
+
def get(self, include_history: bool = True) -> List[MessageParamT]: ...
|
23
|
+
|
24
|
+
def clear(self, clear_prompts: bool = False) -> None: ...
|
25
|
+
|
26
|
+
|
27
|
+
class SimpleMemory(Memory, Generic[MessageParamT]):
|
28
|
+
"""
|
29
|
+
Simple memory management for storing past interactions in-memory.
|
30
|
+
|
31
|
+
Maintains both prompt messages (which are always included) and
|
32
|
+
generated conversation history (which is included based on use_history setting).
|
33
|
+
"""
|
34
|
+
|
35
|
+
def __init__(self) -> None:
|
36
|
+
self.history: List[MessageParamT] = []
|
37
|
+
self.prompt_messages: List[MessageParamT] = [] # Always included
|
38
|
+
|
39
|
+
def extend(self, messages: List[MessageParamT], is_prompt: bool = False) -> None:
|
40
|
+
"""
|
41
|
+
Add multiple messages to history.
|
42
|
+
|
43
|
+
Args:
|
44
|
+
messages: Messages to add
|
45
|
+
is_prompt: If True, add to prompt_messages instead of regular history
|
46
|
+
"""
|
47
|
+
if is_prompt:
|
48
|
+
self.prompt_messages.extend(messages)
|
49
|
+
else:
|
50
|
+
self.history.extend(messages)
|
51
|
+
|
52
|
+
def set(self, messages: List[MessageParamT], is_prompt: bool = False) -> None:
|
53
|
+
"""
|
54
|
+
Replace messages in history.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
messages: Messages to set
|
58
|
+
is_prompt: If True, replace prompt_messages instead of regular history
|
59
|
+
"""
|
60
|
+
if is_prompt:
|
61
|
+
self.prompt_messages = messages.copy()
|
62
|
+
else:
|
63
|
+
self.history = messages.copy()
|
64
|
+
|
65
|
+
def append(self, message: MessageParamT, is_prompt: bool = False) -> None:
|
66
|
+
"""
|
67
|
+
Add a single message to history.
|
68
|
+
|
69
|
+
Args:
|
70
|
+
message: Message to add
|
71
|
+
is_prompt: If True, add to prompt_messages instead of regular history
|
72
|
+
"""
|
73
|
+
if is_prompt:
|
74
|
+
self.prompt_messages.append(message)
|
75
|
+
else:
|
76
|
+
self.history.append(message)
|
77
|
+
|
78
|
+
def get(self, include_history: bool = True) -> List[MessageParamT]:
|
79
|
+
"""
|
80
|
+
Get all messages in memory.
|
81
|
+
|
82
|
+
Args:
|
83
|
+
include_history: If True, include regular history messages
|
84
|
+
If False, only return prompt messages
|
85
|
+
|
86
|
+
Returns:
|
87
|
+
Combined list of prompt messages and optionally history messages
|
88
|
+
"""
|
89
|
+
if include_history:
|
90
|
+
return self.prompt_messages + self.history
|
91
|
+
else:
|
92
|
+
return self.prompt_messages.copy()
|
93
|
+
|
94
|
+
def clear(self, clear_prompts: bool = False) -> None:
|
95
|
+
"""
|
96
|
+
Clear history and optionally prompt messages.
|
97
|
+
|
98
|
+
Args:
|
99
|
+
clear_prompts: If True, also clear prompt messages
|
100
|
+
"""
|
101
|
+
self.history = []
|
102
|
+
if clear_prompts:
|
103
|
+
self.prompt_messages = []
|
@@ -1,16 +1,16 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
2
|
from enum import Enum, auto
|
3
|
-
from typing import
|
3
|
+
from typing import Callable, Dict, Optional, Type, Union
|
4
4
|
|
5
5
|
from mcp_agent.agents.agent import Agent
|
6
6
|
from mcp_agent.core.exceptions import ModelConfigError
|
7
|
+
from mcp_agent.core.request_params import RequestParams
|
8
|
+
from mcp_agent.mcp.interfaces import AugmentedLLMProtocol
|
7
9
|
from mcp_agent.workflows.llm.augmented_llm_anthropic import AnthropicAugmentedLLM
|
8
10
|
from mcp_agent.workflows.llm.augmented_llm_openai import OpenAIAugmentedLLM
|
9
|
-
from mcp_agent.workflows.llm.augmented_llm import RequestParams
|
10
11
|
from mcp_agent.workflows.llm.augmented_llm_passthrough import PassthroughLLM
|
11
12
|
from mcp_agent.workflows.llm.augmented_llm_playback import PlaybackLLM
|
12
13
|
|
13
|
-
|
14
14
|
# Type alias for LLM classes
|
15
15
|
LLMClass = Union[
|
16
16
|
Type[AnthropicAugmentedLLM],
|
@@ -145,14 +145,10 @@ class ModelFactory:
|
|
145
145
|
if provider is None:
|
146
146
|
raise ModelConfigError(f"Unknown model: {model_name}")
|
147
147
|
|
148
|
-
return ModelConfig(
|
149
|
-
provider=provider, model_name=model_name, reasoning_effort=reasoning_effort
|
150
|
-
)
|
148
|
+
return ModelConfig(provider=provider, model_name=model_name, reasoning_effort=reasoning_effort)
|
151
149
|
|
152
150
|
@classmethod
|
153
|
-
def create_factory(
|
154
|
-
cls, model_string: str, request_params: Optional[RequestParams] = None
|
155
|
-
) -> Callable[..., LLMClass]:
|
151
|
+
def create_factory(cls, model_string: str, request_params: Optional[RequestParams] = None) -> Callable[..., AugmentedLLMProtocol]:
|
156
152
|
"""
|
157
153
|
Creates a factory function that follows the attach_llm protocol.
|
158
154
|
|
@@ -173,23 +169,15 @@ class ModelFactory:
|
|
173
169
|
# Create a factory function matching the attach_llm protocol
|
174
170
|
def factory(agent: Agent, **kwargs) -> LLMClass:
|
175
171
|
# Create merged params with parsed model name
|
176
|
-
factory_params = (
|
177
|
-
|
178
|
-
)
|
179
|
-
factory_params.model = (
|
180
|
-
config.model_name
|
181
|
-
) # Use the parsed model name, not the alias
|
172
|
+
factory_params = request_params.model_copy() if request_params else RequestParams()
|
173
|
+
factory_params.model = config.model_name # Use the parsed model name, not the alias
|
182
174
|
|
183
175
|
# Merge with any provided default_request_params
|
184
176
|
if "default_request_params" in kwargs and kwargs["default_request_params"]:
|
185
177
|
params_dict = factory_params.model_dump()
|
186
|
-
params_dict.update(
|
187
|
-
kwargs["default_request_params"].model_dump(exclude_unset=True)
|
188
|
-
)
|
178
|
+
params_dict.update(kwargs["default_request_params"].model_dump(exclude_unset=True))
|
189
179
|
factory_params = RequestParams(**params_dict)
|
190
|
-
factory_params.model =
|
191
|
-
config.model_name
|
192
|
-
) # Ensure parsed model name isn't overwritten
|
180
|
+
factory_params.model = config.model_name # Ensure parsed model name isn't overwritten
|
193
181
|
|
194
182
|
# Forward all keyword arguments to LLM constructor
|
195
183
|
llm_args = {
|
@@ -5,7 +5,7 @@ This file provides backward compatibility with the existing API while
|
|
5
5
|
delegating to the proper implementations in the providers/ directory.
|
6
6
|
"""
|
7
7
|
|
8
|
-
from typing import
|
8
|
+
from typing import Any, Dict, Union
|
9
9
|
|
10
10
|
from openai.types.chat import (
|
11
11
|
ChatCompletionMessage,
|
@@ -5,8 +5,11 @@ XML formatting utilities for consistent prompt engineering across components.
|
|
5
5
|
from typing import Dict, List, Optional, Union
|
6
6
|
|
7
7
|
|
8
|
-
def format_xml_tag(
|
9
|
-
|
8
|
+
def format_xml_tag(
|
9
|
+
tag_name: str,
|
10
|
+
content: Optional[str] = None,
|
11
|
+
attributes: Optional[Dict[str, str]] = None,
|
12
|
+
) -> str:
|
10
13
|
"""
|
11
14
|
Format an XML tag with optional content and attributes.
|
12
15
|
Uses self-closing tag when content is None or empty.
|
@@ -23,56 +26,62 @@ def format_xml_tag(tag_name: str, content: Optional[str] = None,
|
|
23
26
|
attrs_str = ""
|
24
27
|
if attributes:
|
25
28
|
attrs_str = " " + " ".join(f'{k}="{v}"' for k, v in attributes.items())
|
26
|
-
|
29
|
+
|
27
30
|
# Use self-closing tag if no content
|
28
31
|
if content is None or content == "":
|
29
32
|
return f"<{tag_name}{attrs_str} />"
|
30
|
-
|
33
|
+
|
31
34
|
# Full tag with content
|
32
35
|
return f"<{tag_name}{attrs_str}>{content}</{tag_name}>"
|
33
36
|
|
34
37
|
|
35
|
-
def format_fastagent_tag(
|
36
|
-
|
38
|
+
def format_fastagent_tag(
|
39
|
+
tag_type: str,
|
40
|
+
content: Optional[str] = None,
|
41
|
+
attributes: Optional[Dict[str, str]] = None,
|
42
|
+
) -> str:
|
37
43
|
"""
|
38
44
|
Format a fastagent-namespaced XML tag with consistent formatting.
|
39
|
-
|
45
|
+
|
40
46
|
Args:
|
41
47
|
tag_type: Type of fastagent tag (without namespace prefix)
|
42
48
|
content: Content to include inside the tag
|
43
49
|
attributes: Dictionary of attribute name-value pairs
|
44
|
-
|
50
|
+
|
45
51
|
Returns:
|
46
52
|
Formatted fastagent XML tag as string
|
47
53
|
"""
|
48
54
|
return format_xml_tag(f"fastagent:{tag_type}", content, attributes)
|
49
55
|
|
50
56
|
|
51
|
-
def format_server_info(
|
52
|
-
|
57
|
+
def format_server_info(
|
58
|
+
server_name: str,
|
59
|
+
description: Optional[str] = None,
|
60
|
+
tools: Optional[List[Dict[str, str]]] = None,
|
61
|
+
) -> str:
|
53
62
|
"""
|
54
63
|
Format server information consistently across router and orchestrator modules.
|
55
|
-
|
64
|
+
|
56
65
|
Args:
|
57
66
|
server_name: Name of the server
|
58
67
|
description: Optional server description
|
59
68
|
tools: Optional list of tool dictionaries with 'name' and 'description' keys
|
60
|
-
|
69
|
+
|
61
70
|
Returns:
|
62
71
|
Formatted server XML as string
|
63
72
|
"""
|
64
73
|
# Use self-closing tag if no description or tools
|
65
74
|
if not description and not tools:
|
66
75
|
return format_fastagent_tag("server", None, {"name": server_name})
|
67
|
-
|
76
|
+
|
68
77
|
# Start building components
|
69
78
|
components = []
|
70
|
-
|
79
|
+
|
71
80
|
# Add description if present
|
72
81
|
if description:
|
73
82
|
desc_tag = format_fastagent_tag("description", description)
|
74
83
|
components.append(desc_tag)
|
75
|
-
|
84
|
+
|
76
85
|
# Add tools section if tools exist
|
77
86
|
if tools and len(tools) > 0:
|
78
87
|
tool_tags = []
|
@@ -81,41 +90,44 @@ def format_server_info(server_name: str, description: Optional[str] = None,
|
|
81
90
|
tool_desc = tool.get("description", "")
|
82
91
|
tool_tag = format_fastagent_tag("tool", tool_desc, {"name": tool_name})
|
83
92
|
tool_tags.append(tool_tag)
|
84
|
-
|
93
|
+
|
85
94
|
tools_content = "\n".join(tool_tags)
|
86
95
|
tools_tag = format_fastagent_tag("tools", f"\n{tools_content}\n")
|
87
96
|
components.append(tools_tag)
|
88
|
-
|
97
|
+
|
89
98
|
# Combine all components
|
90
99
|
server_content = "\n".join(components)
|
91
100
|
return format_fastagent_tag("server", f"\n{server_content}\n", {"name": server_name})
|
92
101
|
|
93
102
|
|
94
|
-
def format_agent_info(
|
95
|
-
|
103
|
+
def format_agent_info(
|
104
|
+
agent_name: str,
|
105
|
+
description: Optional[str] = None,
|
106
|
+
servers: Optional[List[Dict[str, Union[str, List[Dict[str, str]]]]]] = None,
|
107
|
+
) -> str:
|
96
108
|
"""
|
97
109
|
Format agent information consistently across router and orchestrator modules.
|
98
|
-
|
110
|
+
|
99
111
|
Args:
|
100
112
|
agent_name: Name of the agent
|
101
113
|
description: Optional agent description/instruction
|
102
114
|
servers: Optional list of server dictionaries with 'name', 'description', and 'tools' keys
|
103
|
-
|
115
|
+
|
104
116
|
Returns:
|
105
117
|
Formatted agent XML as string
|
106
118
|
"""
|
107
119
|
# Start building components
|
108
120
|
components = []
|
109
|
-
|
121
|
+
|
110
122
|
# Add description if present
|
111
123
|
if description:
|
112
124
|
desc_tag = format_fastagent_tag("description", description)
|
113
125
|
components.append(desc_tag)
|
114
|
-
|
126
|
+
|
115
127
|
# If no description or servers, use self-closing tag
|
116
128
|
if not description and not servers:
|
117
129
|
return format_fastagent_tag("agent", None, {"name": agent_name})
|
118
|
-
|
130
|
+
|
119
131
|
# If has servers, format them
|
120
132
|
if servers and len(servers) > 0:
|
121
133
|
server_tags = []
|
@@ -125,13 +137,13 @@ def format_agent_info(agent_name: str, description: Optional[str] = None,
|
|
125
137
|
server_tools = server.get("tools", [])
|
126
138
|
server_tag = format_server_info(server_name, server_desc, server_tools)
|
127
139
|
server_tags.append(server_tag)
|
128
|
-
|
140
|
+
|
129
141
|
# Only add servers section if we have servers
|
130
142
|
if server_tags:
|
131
143
|
servers_content = "\n".join(server_tags)
|
132
144
|
servers_tag = format_fastagent_tag("servers", f"\n{servers_content}\n")
|
133
145
|
components.append(servers_tag)
|
134
|
-
|
146
|
+
|
135
147
|
# Combine all components
|
136
148
|
agent_content = "\n".join(components)
|
137
|
-
return format_fastagent_tag("agent", f"\n{agent_content}\n", {"name": agent_name})
|
149
|
+
return format_fastagent_tag("agent", f"\n{agent_content}\n", {"name": agent_name})
|