fast-agent-mcp 0.2.46__py3-none-any.whl → 0.2.48__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.
Potentially problematic release.
This version of fast-agent-mcp might be problematic. Click here for more details.
- {fast_agent_mcp-0.2.46.dist-info → fast_agent_mcp-0.2.48.dist-info}/METADATA +12 -12
- {fast_agent_mcp-0.2.46.dist-info → fast_agent_mcp-0.2.48.dist-info}/RECORD +30 -27
- mcp_agent/agents/workflow/iterative_planner.py +572 -0
- mcp_agent/agents/workflow/orchestrator_agent.py +3 -3
- mcp_agent/agents/workflow/orchestrator_models.py +6 -6
- mcp_agent/cli/commands/go.py +17 -3
- mcp_agent/cli/main.py +2 -2
- mcp_agent/config.py +14 -0
- mcp_agent/core/agent_types.py +1 -0
- mcp_agent/core/direct_decorators.py +54 -0
- mcp_agent/core/direct_factory.py +42 -15
- mcp_agent/core/fastagent.py +4 -0
- mcp_agent/core/mermaid_utils.py +170 -0
- mcp_agent/llm/model_database.py +7 -7
- mcp_agent/llm/model_factory.py +5 -3
- mcp_agent/llm/provider_types.py +1 -0
- mcp_agent/llm/providers/augmented_llm_aliyun.py +1 -1
- mcp_agent/llm/providers/augmented_llm_anthropic.py +1 -1
- mcp_agent/llm/providers/augmented_llm_deepseek.py +4 -2
- mcp_agent/llm/providers/augmented_llm_google_oai.py +1 -1
- mcp_agent/llm/providers/augmented_llm_groq.py +30 -0
- mcp_agent/llm/providers/augmented_llm_openai.py +4 -1
- mcp_agent/llm/providers/augmented_llm_openrouter.py +1 -1
- mcp_agent/llm/providers/augmented_llm_tensorzero.py +1 -1
- mcp_agent/llm/providers/augmented_llm_xai.py +1 -1
- mcp_agent/resources/examples/workflows/orchestrator.py +5 -2
- mcp_agent/ui/console_display.py +104 -39
- {fast_agent_mcp-0.2.46.dist-info → fast_agent_mcp-0.2.48.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.2.46.dist-info → fast_agent_mcp-0.2.48.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.2.46.dist-info → fast_agent_mcp-0.2.48.dist-info}/licenses/LICENSE +0 -0
mcp_agent/cli/commands/go.py
CHANGED
|
@@ -19,7 +19,7 @@ app = typer.Typer(
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
async def _run_agent(
|
|
22
|
-
name: str = "
|
|
22
|
+
name: str = "fast-agent cli",
|
|
23
23
|
instruction: str = "You are a helpful AI Agent.",
|
|
24
24
|
config_path: Optional[str] = None,
|
|
25
25
|
server_list: Optional[List[str]] = None,
|
|
@@ -28,6 +28,7 @@ async def _run_agent(
|
|
|
28
28
|
prompt_file: Optional[str] = None,
|
|
29
29
|
url_servers: Optional[Dict[str, Dict[str, str]]] = None,
|
|
30
30
|
stdio_servers: Optional[Dict[str, Dict[str, str]]] = None,
|
|
31
|
+
agent_name: Optional[str] = "agent",
|
|
31
32
|
) -> None:
|
|
32
33
|
"""Async implementation to run an interactive agent."""
|
|
33
34
|
from pathlib import Path
|
|
@@ -35,6 +36,7 @@ async def _run_agent(
|
|
|
35
36
|
from mcp_agent.mcp.prompts.prompt_load import load_prompt_multipart
|
|
36
37
|
|
|
37
38
|
# Create the FastAgent instance
|
|
39
|
+
|
|
38
40
|
fast_kwargs = {
|
|
39
41
|
"name": name,
|
|
40
42
|
"config_path": config_path,
|
|
@@ -103,6 +105,8 @@ async def _run_agent(
|
|
|
103
105
|
# Single model - use original behavior
|
|
104
106
|
# Define the agent with specified parameters
|
|
105
107
|
agent_kwargs = {"instruction": instruction}
|
|
108
|
+
if agent_name:
|
|
109
|
+
agent_kwargs["name"] = agent_name
|
|
106
110
|
if server_list:
|
|
107
111
|
agent_kwargs["servers"] = server_list
|
|
108
112
|
if model:
|
|
@@ -117,7 +121,7 @@ async def _run_agent(
|
|
|
117
121
|
print(response)
|
|
118
122
|
elif prompt_file:
|
|
119
123
|
prompt = load_prompt_multipart(Path(prompt_file))
|
|
120
|
-
response = await agent.
|
|
124
|
+
response = await agent.generate(prompt)
|
|
121
125
|
# Print the response text and exit
|
|
122
126
|
print(response.last_text())
|
|
123
127
|
else:
|
|
@@ -138,6 +142,7 @@ def run_async_agent(
|
|
|
138
142
|
message: Optional[str] = None,
|
|
139
143
|
prompt_file: Optional[str] = None,
|
|
140
144
|
stdio_commands: Optional[List[str]] = None,
|
|
145
|
+
agent_name: Optional[str] = None,
|
|
141
146
|
):
|
|
142
147
|
"""Run the async agent function with proper loop handling."""
|
|
143
148
|
server_list = servers.split(",") if servers else None
|
|
@@ -237,6 +242,7 @@ def run_async_agent(
|
|
|
237
242
|
prompt_file=prompt_file,
|
|
238
243
|
url_servers=url_servers,
|
|
239
244
|
stdio_servers=stdio_servers,
|
|
245
|
+
agent_name=agent_name,
|
|
240
246
|
)
|
|
241
247
|
)
|
|
242
248
|
finally:
|
|
@@ -258,7 +264,7 @@ def run_async_agent(
|
|
|
258
264
|
@app.callback(invoke_without_command=True, no_args_is_help=False)
|
|
259
265
|
def go(
|
|
260
266
|
ctx: typer.Context,
|
|
261
|
-
name: str = typer.Option("
|
|
267
|
+
name: str = typer.Option("fast-agent", "--name", help="Name for the agent"),
|
|
262
268
|
instruction: Optional[str] = typer.Option(
|
|
263
269
|
None, "--instruction", "-i", help="Path to file or URL containing instruction for the agent"
|
|
264
270
|
),
|
|
@@ -338,6 +344,8 @@ def go(
|
|
|
338
344
|
|
|
339
345
|
# Resolve instruction from file/URL or use default
|
|
340
346
|
resolved_instruction = "You are a helpful AI Agent." # Default
|
|
347
|
+
agent_name = "agent"
|
|
348
|
+
|
|
341
349
|
if instruction:
|
|
342
350
|
try:
|
|
343
351
|
from pathlib import Path
|
|
@@ -352,6 +360,11 @@ def go(
|
|
|
352
360
|
else:
|
|
353
361
|
# Treat as file path
|
|
354
362
|
resolved_instruction = _resolve_instruction(Path(instruction))
|
|
363
|
+
# Extract filename without extension to use as agent name
|
|
364
|
+
instruction_path = Path(instruction)
|
|
365
|
+
if instruction_path.exists() and instruction_path.is_file():
|
|
366
|
+
# Get filename without extension
|
|
367
|
+
agent_name = instruction_path.stem
|
|
355
368
|
except Exception as e:
|
|
356
369
|
typer.echo(f"Error loading instruction from {instruction}: {e}", err=True)
|
|
357
370
|
raise typer.Exit(1)
|
|
@@ -367,4 +380,5 @@ def go(
|
|
|
367
380
|
message=message,
|
|
368
381
|
prompt_file=prompt_file,
|
|
369
382
|
stdio_commands=stdio_commands,
|
|
383
|
+
agent_name=agent_name,
|
|
370
384
|
)
|
mcp_agent/cli/main.py
CHANGED
|
@@ -8,7 +8,7 @@ from mcp_agent.cli.commands import check_config, go, quickstart, setup
|
|
|
8
8
|
from mcp_agent.cli.terminal import Application
|
|
9
9
|
|
|
10
10
|
app = typer.Typer(
|
|
11
|
-
help="
|
|
11
|
+
help="fast-agent - Build effective agents using Model Context Protocol",
|
|
12
12
|
add_completion=False, # We'll add this later when we have more commands
|
|
13
13
|
)
|
|
14
14
|
|
|
@@ -60,7 +60,7 @@ def main(
|
|
|
60
60
|
color: bool = typer.Option(True, "--color/--no-color", help="Enable/disable color output"),
|
|
61
61
|
version: bool = typer.Option(False, "--version", help="Show version and exit"),
|
|
62
62
|
) -> None:
|
|
63
|
-
"""
|
|
63
|
+
"""fast-agent - Build effective agents using Model Context Protocol (MCP).
|
|
64
64
|
|
|
65
65
|
Use --help with any command for detailed usage information.
|
|
66
66
|
"""
|
mcp_agent/config.py
CHANGED
|
@@ -224,6 +224,17 @@ class AzureSettings(BaseModel):
|
|
|
224
224
|
model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
|
|
225
225
|
|
|
226
226
|
|
|
227
|
+
class GroqSettings(BaseModel):
|
|
228
|
+
"""
|
|
229
|
+
Settings for using xAI Grok models in the fast-agent application.
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
api_key: str | None = None
|
|
233
|
+
base_url: str | None = "https://api.groq.com/openai/v1"
|
|
234
|
+
|
|
235
|
+
model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
|
|
236
|
+
|
|
237
|
+
|
|
227
238
|
class OpenTelemetrySettings(BaseModel):
|
|
228
239
|
"""
|
|
229
240
|
OTEL settings for the fast-agent application.
|
|
@@ -441,6 +452,9 @@ class Settings(BaseSettings):
|
|
|
441
452
|
huggingface: HuggingFaceSettings | None = None
|
|
442
453
|
"""Settings for HuggingFace authentication (used for MCP connections)"""
|
|
443
454
|
|
|
455
|
+
groq: GroqSettings | None = None
|
|
456
|
+
"""Settings for using the Groq provider in the fast-agent application"""
|
|
457
|
+
|
|
444
458
|
logger: LoggerSettings | None = LoggerSettings()
|
|
445
459
|
"""Logger settings for the fast-agent application"""
|
|
446
460
|
|
mcp_agent/core/agent_types.py
CHANGED
|
@@ -25,6 +25,7 @@ from mcp.client.session import ElicitationFnT
|
|
|
25
25
|
from pydantic import AnyUrl
|
|
26
26
|
|
|
27
27
|
from mcp_agent.agents.agent import AgentConfig
|
|
28
|
+
from mcp_agent.agents.workflow.iterative_planner import ITERATIVE_PLAN_SYSTEM_PROMPT_TEMPLATE
|
|
28
29
|
from mcp_agent.agents.workflow.router_agent import (
|
|
29
30
|
ROUTING_SYSTEM_INSTRUCTION,
|
|
30
31
|
)
|
|
@@ -457,6 +458,59 @@ def orchestrator(
|
|
|
457
458
|
)
|
|
458
459
|
|
|
459
460
|
|
|
461
|
+
def iterative_planner(
|
|
462
|
+
self,
|
|
463
|
+
name: str,
|
|
464
|
+
*,
|
|
465
|
+
agents: List[str],
|
|
466
|
+
instruction: str | Path | AnyUrl = ITERATIVE_PLAN_SYSTEM_PROMPT_TEMPLATE,
|
|
467
|
+
model: Optional[str] = None,
|
|
468
|
+
request_params: RequestParams | None = None,
|
|
469
|
+
plan_iterations: int = -1,
|
|
470
|
+
default: bool = False,
|
|
471
|
+
api_key: str | None = None,
|
|
472
|
+
) -> Callable[[AgentCallable[P, R]], DecoratedOrchestratorProtocol[P, R]]:
|
|
473
|
+
"""
|
|
474
|
+
Decorator to create and register an orchestrator agent with type-safe signature.
|
|
475
|
+
|
|
476
|
+
Args:
|
|
477
|
+
name: Name of the orchestrator
|
|
478
|
+
agents: List of agent names this orchestrator can use
|
|
479
|
+
instruction: Base instruction for the orchestrator
|
|
480
|
+
model: Model specification string
|
|
481
|
+
use_history: Whether to maintain conversation history
|
|
482
|
+
request_params: Additional request parameters for the LLM
|
|
483
|
+
human_input: Whether to enable human input capabilities
|
|
484
|
+
plan_type: Planning approach - "full" or "iterative"
|
|
485
|
+
plan_iterations: Maximum number of planning iterations (0 for unlimited)
|
|
486
|
+
default: Whether to mark this as the default agent
|
|
487
|
+
|
|
488
|
+
Returns:
|
|
489
|
+
A decorator that registers the orchestrator with proper type annotations
|
|
490
|
+
"""
|
|
491
|
+
|
|
492
|
+
# Create final request params with plan_iterations
|
|
493
|
+
resolved_instruction = _resolve_instruction(instruction)
|
|
494
|
+
|
|
495
|
+
return cast(
|
|
496
|
+
"Callable[[AgentCallable[P, R]], DecoratedOrchestratorProtocol[P, R]]",
|
|
497
|
+
_decorator_impl(
|
|
498
|
+
self,
|
|
499
|
+
AgentType.ITERATIVE_PLANNER,
|
|
500
|
+
name=name,
|
|
501
|
+
instruction=resolved_instruction,
|
|
502
|
+
servers=[], # Orchestrators don't connect to servers directly
|
|
503
|
+
model=model,
|
|
504
|
+
use_history=False,
|
|
505
|
+
request_params=request_params,
|
|
506
|
+
child_agents=agents,
|
|
507
|
+
plan_iterations=plan_iterations,
|
|
508
|
+
default=default,
|
|
509
|
+
api_key=api_key,
|
|
510
|
+
),
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
|
|
460
514
|
def router(
|
|
461
515
|
self,
|
|
462
516
|
name: str,
|
mcp_agent/core/direct_factory.py
CHANGED
|
@@ -10,6 +10,7 @@ from mcp_agent.agents.workflow.evaluator_optimizer import (
|
|
|
10
10
|
EvaluatorOptimizerAgent,
|
|
11
11
|
QualityRating,
|
|
12
12
|
)
|
|
13
|
+
from mcp_agent.agents.workflow.iterative_planner import IterativePlanner
|
|
13
14
|
from mcp_agent.agents.workflow.orchestrator_agent import OrchestratorAgent
|
|
14
15
|
from mcp_agent.agents.workflow.parallel_agent import ParallelAgent
|
|
15
16
|
from mcp_agent.agents.workflow.router_agent import RouterAgent
|
|
@@ -21,9 +22,10 @@ from mcp_agent.event_progress import ProgressAction
|
|
|
21
22
|
from mcp_agent.llm.augmented_llm import RequestParams
|
|
22
23
|
from mcp_agent.llm.model_factory import ModelFactory
|
|
23
24
|
from mcp_agent.logging.logger import get_logger
|
|
25
|
+
from mcp_agent.mcp.interfaces import AgentProtocol
|
|
24
26
|
|
|
25
27
|
# Type aliases for improved readability and IDE support
|
|
26
|
-
AgentDict = Dict[str,
|
|
28
|
+
AgentDict = Dict[str, AgentProtocol]
|
|
27
29
|
AgentConfigDict = Dict[str, Dict[str, Any]]
|
|
28
30
|
T = TypeVar("T") # For generic types
|
|
29
31
|
|
|
@@ -153,7 +155,7 @@ async def create_agents_by_type(
|
|
|
153
155
|
await agent.attach_llm(
|
|
154
156
|
llm_factory,
|
|
155
157
|
request_params=config.default_request_params,
|
|
156
|
-
api_key=config.api_key
|
|
158
|
+
api_key=config.api_key,
|
|
157
159
|
)
|
|
158
160
|
result_agents[name] = agent
|
|
159
161
|
|
|
@@ -172,11 +174,11 @@ async def create_agents_by_type(
|
|
|
172
174
|
await agent.attach_llm(
|
|
173
175
|
llm_factory,
|
|
174
176
|
request_params=config.default_request_params,
|
|
175
|
-
api_key=config.api_key
|
|
177
|
+
api_key=config.api_key,
|
|
176
178
|
)
|
|
177
179
|
result_agents[name] = agent
|
|
178
180
|
|
|
179
|
-
elif agent_type == AgentType.ORCHESTRATOR:
|
|
181
|
+
elif agent_type == AgentType.ORCHESTRATOR or agent_type == AgentType.ITERATIVE_PLANNER:
|
|
180
182
|
# Get base params configured with model settings
|
|
181
183
|
base_params = (
|
|
182
184
|
config.default_request_params.model_copy()
|
|
@@ -193,24 +195,35 @@ async def create_agents_by_type(
|
|
|
193
195
|
agent = active_agents[agent_name]
|
|
194
196
|
child_agents.append(agent)
|
|
195
197
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
198
|
+
if AgentType.ORCHESTRATOR == agent_type:
|
|
199
|
+
# Create the orchestrator
|
|
200
|
+
orchestrator = OrchestratorAgent(
|
|
201
|
+
config=config,
|
|
202
|
+
context=app_instance.context,
|
|
203
|
+
agents=child_agents,
|
|
204
|
+
plan_iterations=agent_data.get("plan_iterations", 5),
|
|
205
|
+
plan_type=agent_data.get("plan_type", "full"),
|
|
206
|
+
)
|
|
207
|
+
else:
|
|
208
|
+
orchestrator = IterativePlanner(
|
|
209
|
+
config=config,
|
|
210
|
+
context=app_instance.context,
|
|
211
|
+
agents=child_agents,
|
|
212
|
+
plan_iterations=agent_data.get("plan_iterations", 5),
|
|
213
|
+
plan_type=agent_data.get("plan_type", "full"),
|
|
214
|
+
)
|
|
204
215
|
|
|
205
216
|
# Initialize the orchestrator
|
|
206
217
|
await orchestrator.initialize()
|
|
207
218
|
|
|
208
219
|
# Attach LLM to the orchestrator
|
|
209
220
|
llm_factory = model_factory_func(model=config.model)
|
|
221
|
+
|
|
222
|
+
# print("************", config.default_request_params.instruction)
|
|
210
223
|
await orchestrator.attach_llm(
|
|
211
224
|
llm_factory,
|
|
212
225
|
request_params=config.default_request_params,
|
|
213
|
-
api_key=config.api_key
|
|
226
|
+
api_key=config.api_key,
|
|
214
227
|
)
|
|
215
228
|
|
|
216
229
|
result_agents[name] = orchestrator
|
|
@@ -274,7 +287,7 @@ async def create_agents_by_type(
|
|
|
274
287
|
await router.attach_llm(
|
|
275
288
|
llm_factory,
|
|
276
289
|
request_params=config.default_request_params,
|
|
277
|
-
api_key=config.api_key
|
|
290
|
+
api_key=config.api_key,
|
|
278
291
|
)
|
|
279
292
|
result_agents[name] = router
|
|
280
293
|
|
|
@@ -461,7 +474,6 @@ async def create_agents_in_dependency_order(
|
|
|
461
474
|
)
|
|
462
475
|
active_agents.update(evaluator_agents)
|
|
463
476
|
|
|
464
|
-
# Create orchestrator agents last since they might depend on other agents
|
|
465
477
|
if AgentType.ORCHESTRATOR.value in [agents_dict[name]["type"] for name in group]:
|
|
466
478
|
orchestrator_agents = await create_agents_by_type(
|
|
467
479
|
app_instance,
|
|
@@ -476,6 +488,21 @@ async def create_agents_in_dependency_order(
|
|
|
476
488
|
)
|
|
477
489
|
active_agents.update(orchestrator_agents)
|
|
478
490
|
|
|
491
|
+
# Create orchestrator2 agents last since they might depend on other agents
|
|
492
|
+
if AgentType.ITERATIVE_PLANNER.value in [agents_dict[name]["type"] for name in group]:
|
|
493
|
+
orchestrator2_agents = await create_agents_by_type(
|
|
494
|
+
app_instance,
|
|
495
|
+
{
|
|
496
|
+
name: agents_dict[name]
|
|
497
|
+
for name in group
|
|
498
|
+
if agents_dict[name]["type"] == AgentType.ITERATIVE_PLANNER.value
|
|
499
|
+
},
|
|
500
|
+
AgentType.ITERATIVE_PLANNER,
|
|
501
|
+
active_agents,
|
|
502
|
+
model_factory_func,
|
|
503
|
+
)
|
|
504
|
+
active_agents.update(orchestrator2_agents)
|
|
505
|
+
|
|
479
506
|
return active_agents
|
|
480
507
|
|
|
481
508
|
|
mcp_agent/core/fastagent.py
CHANGED
|
@@ -31,6 +31,9 @@ from mcp_agent.core.direct_decorators import (
|
|
|
31
31
|
from mcp_agent.core.direct_decorators import (
|
|
32
32
|
evaluator_optimizer as evaluator_optimizer_decorator,
|
|
33
33
|
)
|
|
34
|
+
from mcp_agent.core.direct_decorators import (
|
|
35
|
+
iterative_planner as orchestrator2_decorator,
|
|
36
|
+
)
|
|
34
37
|
from mcp_agent.core.direct_decorators import (
|
|
35
38
|
orchestrator as orchestrator_decorator,
|
|
36
39
|
)
|
|
@@ -249,6 +252,7 @@ class FastAgent:
|
|
|
249
252
|
agent = agent_decorator
|
|
250
253
|
custom = custom_decorator
|
|
251
254
|
orchestrator = orchestrator_decorator
|
|
255
|
+
iterative_planner = orchestrator2_decorator
|
|
252
256
|
router = router_decorator
|
|
253
257
|
chain = chain_decorator
|
|
254
258
|
parallel = parallel_decorator
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""Utilities for detecting and processing Mermaid diagrams in text content."""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import re
|
|
5
|
+
import zlib
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import List, Optional
|
|
8
|
+
|
|
9
|
+
# Mermaid chart viewer URL prefix
|
|
10
|
+
MERMAID_VIEWER_URL = "https://www.mermaidchart.com/play#"
|
|
11
|
+
# mermaid.live#pako= also works but the playground has better ux
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class MermaidDiagram:
|
|
16
|
+
"""Represents a detected Mermaid diagram."""
|
|
17
|
+
|
|
18
|
+
content: str
|
|
19
|
+
title: Optional[str] = None
|
|
20
|
+
start_pos: int = 0
|
|
21
|
+
end_pos: int = 0
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def extract_mermaid_diagrams(text: str) -> List[MermaidDiagram]:
|
|
25
|
+
"""
|
|
26
|
+
Extract all Mermaid diagram blocks from text content.
|
|
27
|
+
|
|
28
|
+
Handles both simple mermaid blocks and blocks with titles:
|
|
29
|
+
- ```mermaid
|
|
30
|
+
- ```mermaid title={Some Title}
|
|
31
|
+
|
|
32
|
+
Also extracts titles from within the diagram content.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
text: The text content to search for Mermaid diagrams
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
List of MermaidDiagram objects found in the text
|
|
39
|
+
"""
|
|
40
|
+
diagrams = []
|
|
41
|
+
|
|
42
|
+
# Pattern to match mermaid code blocks with optional title
|
|
43
|
+
# Matches: ```mermaid or ```mermaid title={...}
|
|
44
|
+
pattern = r"```mermaid(?:\s+title=\{([^}]+)\})?\s*\n(.*?)```"
|
|
45
|
+
|
|
46
|
+
for match in re.finditer(pattern, text, re.DOTALL):
|
|
47
|
+
title = match.group(1) # May be None if no title
|
|
48
|
+
content = match.group(2).strip()
|
|
49
|
+
|
|
50
|
+
if content: # Only add if there's actual diagram content
|
|
51
|
+
# If no title from code fence, look for title in the content
|
|
52
|
+
if not title:
|
|
53
|
+
# Look for various title patterns in mermaid diagrams
|
|
54
|
+
# pie title, graph title, etc.
|
|
55
|
+
title_patterns = [
|
|
56
|
+
r"^\s*title\s+(.+?)(?:\n|$)", # Generic title
|
|
57
|
+
r"^\s*pie\s+title\s+(.+?)(?:\n|$)", # Pie chart title
|
|
58
|
+
r"^\s*gantt\s+title\s+(.+?)(?:\n|$)", # Gantt chart title
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
for title_pattern in title_patterns:
|
|
62
|
+
title_match = re.search(title_pattern, content, re.MULTILINE)
|
|
63
|
+
if title_match:
|
|
64
|
+
title = title_match.group(1).strip()
|
|
65
|
+
break
|
|
66
|
+
|
|
67
|
+
diagrams.append(
|
|
68
|
+
MermaidDiagram(
|
|
69
|
+
content=content, title=title, start_pos=match.start(), end_pos=match.end()
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
return diagrams
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def create_mermaid_live_link(diagram_content: str) -> str:
|
|
77
|
+
"""
|
|
78
|
+
Create a Mermaid Live Editor link from diagram content.
|
|
79
|
+
|
|
80
|
+
The link uses pako compression (zlib) and base64 encoding.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
diagram_content: The Mermaid diagram source code
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Complete URL to Mermaid Live Editor
|
|
87
|
+
"""
|
|
88
|
+
# Create the JSON structure expected by Mermaid Live
|
|
89
|
+
# Escape newlines and quotes in the diagram content
|
|
90
|
+
escaped_content = diagram_content.replace('"', '\\"').replace("\n", "\\n")
|
|
91
|
+
json_str = f'{{"code":"{escaped_content}","mermaid":{{"theme":"default"}},"updateEditor":false,"autoSync":true,"updateDiagram":false}}'
|
|
92
|
+
|
|
93
|
+
# Compress using zlib (pako compatible)
|
|
94
|
+
compressed = zlib.compress(json_str.encode("utf-8"))
|
|
95
|
+
|
|
96
|
+
# Base64 encode
|
|
97
|
+
encoded = base64.urlsafe_b64encode(compressed).decode("utf-8")
|
|
98
|
+
|
|
99
|
+
# Remove padding characters as Mermaid Live doesn't use them
|
|
100
|
+
encoded = encoded.rstrip("=")
|
|
101
|
+
|
|
102
|
+
return f"{MERMAID_VIEWER_URL}pako:{encoded}"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def format_mermaid_links(diagrams: List[MermaidDiagram]) -> List[str]:
|
|
106
|
+
"""
|
|
107
|
+
Format Mermaid diagrams as markdown links.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
diagrams: List of MermaidDiagram objects
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
List of formatted markdown strings
|
|
114
|
+
"""
|
|
115
|
+
links = []
|
|
116
|
+
|
|
117
|
+
for i, diagram in enumerate(diagrams, 1):
|
|
118
|
+
link = create_mermaid_live_link(diagram.content)
|
|
119
|
+
|
|
120
|
+
if diagram.title:
|
|
121
|
+
# Use the title from the diagram with number
|
|
122
|
+
markdown = f"Diagram {i} - {diagram.title}: [Open Diagram]({link})"
|
|
123
|
+
else:
|
|
124
|
+
# Use generic numbering
|
|
125
|
+
markdown = f"Diagram {i}: [Open Diagram]({link})"
|
|
126
|
+
|
|
127
|
+
links.append(markdown)
|
|
128
|
+
|
|
129
|
+
return links
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def detect_diagram_type(content: str) -> str:
|
|
133
|
+
"""
|
|
134
|
+
Detect the type of mermaid diagram from content.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
content: The mermaid diagram source code
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Human-readable diagram type name
|
|
141
|
+
"""
|
|
142
|
+
content_lower = content.strip().lower()
|
|
143
|
+
|
|
144
|
+
# Check for common diagram types
|
|
145
|
+
if content_lower.startswith(("graph ", "flowchart ")):
|
|
146
|
+
return "Flowchart"
|
|
147
|
+
elif content_lower.startswith("sequencediagram"):
|
|
148
|
+
return "Sequence"
|
|
149
|
+
elif content_lower.startswith("pie"):
|
|
150
|
+
return "Pie Chart"
|
|
151
|
+
elif content_lower.startswith("gantt"):
|
|
152
|
+
return "Gantt Chart"
|
|
153
|
+
elif content_lower.startswith("classdiagram"):
|
|
154
|
+
return "Class Diagram"
|
|
155
|
+
elif content_lower.startswith("statediagram"):
|
|
156
|
+
return "State Diagram"
|
|
157
|
+
elif content_lower.startswith("erdiagram"):
|
|
158
|
+
return "ER Diagram"
|
|
159
|
+
elif content_lower.startswith("journey"):
|
|
160
|
+
return "User Journey"
|
|
161
|
+
elif content_lower.startswith("gitgraph"):
|
|
162
|
+
return "Git Graph"
|
|
163
|
+
elif content_lower.startswith("c4context"):
|
|
164
|
+
return "C4 Context"
|
|
165
|
+
elif content_lower.startswith("mindmap"):
|
|
166
|
+
return "Mind Map"
|
|
167
|
+
elif content_lower.startswith("timeline"):
|
|
168
|
+
return "Timeline"
|
|
169
|
+
else:
|
|
170
|
+
return "Diagram"
|
mcp_agent/llm/model_database.py
CHANGED
|
@@ -129,16 +129,16 @@ class ModelDatabase:
|
|
|
129
129
|
context_window=2097152, max_output_tokens=8192, tokenizes=GOOGLE_MULTIMODAL
|
|
130
130
|
)
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
context_window=256000, max_output_tokens=16385, tokenizes=XAI_VISION
|
|
132
|
+
KIMI_MOONSHOT = ModelParameters(
|
|
133
|
+
context_window=131072, max_output_tokens=16384, tokenizes=TEXT_ONLY
|
|
135
134
|
)
|
|
136
135
|
|
|
136
|
+
# FIXME: xAI has not documented the max output tokens for Grok 4. Using Grok 3 as a placeholder. Will need to update when available (if ever)
|
|
137
|
+
GROK_4 = ModelParameters(context_window=256000, max_output_tokens=16385, tokenizes=XAI_VISION)
|
|
138
|
+
|
|
137
139
|
# Source for Grok 3 max output: https://www.reddit.com/r/grok/comments/1j7209p/exploring_grok_3_beta_output_capacity_a_simple/
|
|
138
140
|
# xAI does not document Grok 3 max output tokens, using the above source as a reference.
|
|
139
|
-
GROK_3 = ModelParameters(
|
|
140
|
-
context_window=131072, max_output_tokens=16385, tokenizes=TEXT_ONLY
|
|
141
|
-
)
|
|
141
|
+
GROK_3 = ModelParameters(context_window=131072, max_output_tokens=16385, tokenizes=TEXT_ONLY)
|
|
142
142
|
|
|
143
143
|
# Model configuration database
|
|
144
144
|
MODELS: Dict[str, ModelParameters] = {
|
|
@@ -193,7 +193,6 @@ class ModelDatabase:
|
|
|
193
193
|
"claude-3-7-sonnet": ANTHROPIC_37_SERIES,
|
|
194
194
|
"claude-3-7-sonnet-20250219": ANTHROPIC_37_SERIES,
|
|
195
195
|
"claude-3-7-sonnet-latest": ANTHROPIC_37_SERIES,
|
|
196
|
-
"claude-sonnet-4": ANTHROPIC_SONNET_4_VERSIONED,
|
|
197
196
|
"claude-sonnet-4-0": ANTHROPIC_SONNET_4_VERSIONED,
|
|
198
197
|
"claude-sonnet-4-20250514": ANTHROPIC_SONNET_4_VERSIONED,
|
|
199
198
|
"claude-opus-4": ANTHROPIC_OPUS_4_VERSIONED,
|
|
@@ -214,6 +213,7 @@ class ModelDatabase:
|
|
|
214
213
|
"grok-3-mini": GROK_3,
|
|
215
214
|
"grok-3-fast": GROK_3,
|
|
216
215
|
"grok-3-mini-fast": GROK_3,
|
|
216
|
+
"moonshotai/kimi-k2-instruct": KIMI_MOONSHOT,
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
@classmethod
|
mcp_agent/llm/model_factory.py
CHANGED
|
@@ -19,6 +19,7 @@ from mcp_agent.llm.providers.augmented_llm_deepseek import DeepSeekAugmentedLLM
|
|
|
19
19
|
from mcp_agent.llm.providers.augmented_llm_generic import GenericAugmentedLLM
|
|
20
20
|
from mcp_agent.llm.providers.augmented_llm_google_native import GoogleNativeAugmentedLLM
|
|
21
21
|
from mcp_agent.llm.providers.augmented_llm_google_oai import GoogleOaiAugmentedLLM
|
|
22
|
+
from mcp_agent.llm.providers.augmented_llm_groq import GroqAugmentedLLM
|
|
22
23
|
from mcp_agent.llm.providers.augmented_llm_openai import OpenAIAugmentedLLM
|
|
23
24
|
from mcp_agent.llm.providers.augmented_llm_openrouter import OpenRouterAugmentedLLM
|
|
24
25
|
from mcp_agent.llm.providers.augmented_llm_tensorzero import TensorZeroAugmentedLLM
|
|
@@ -43,6 +44,7 @@ LLMClass = Union[
|
|
|
43
44
|
Type[GenericAugmentedLLM],
|
|
44
45
|
Type[AzureOpenAIAugmentedLLM],
|
|
45
46
|
Type[BedrockAugmentedLLM],
|
|
47
|
+
Type[GroqAugmentedLLM],
|
|
46
48
|
]
|
|
47
49
|
|
|
48
50
|
|
|
@@ -122,7 +124,6 @@ class ModelFactory:
|
|
|
122
124
|
"qwen-plus": Provider.ALIYUN,
|
|
123
125
|
"qwen-max": Provider.ALIYUN,
|
|
124
126
|
"qwen-long": Provider.ALIYUN,
|
|
125
|
-
|
|
126
127
|
}
|
|
127
128
|
|
|
128
129
|
MODEL_ALIASES = {
|
|
@@ -159,6 +160,7 @@ class ModelFactory:
|
|
|
159
160
|
Provider.AZURE: AzureOpenAIAugmentedLLM,
|
|
160
161
|
Provider.ALIYUN: AliyunAugmentedLLM,
|
|
161
162
|
Provider.BEDROCK: BedrockAugmentedLLM,
|
|
163
|
+
Provider.GROQ: GroqAugmentedLLM,
|
|
162
164
|
}
|
|
163
165
|
|
|
164
166
|
# Mapping of special model names to their specific LLM classes
|
|
@@ -213,11 +215,11 @@ class ModelFactory:
|
|
|
213
215
|
# If provider still None, try to get from DEFAULT_PROVIDERS using the model_name_str
|
|
214
216
|
if provider is None:
|
|
215
217
|
provider = cls.DEFAULT_PROVIDERS.get(model_name_str)
|
|
216
|
-
|
|
218
|
+
|
|
217
219
|
# If still None, try pattern matching for Bedrock models
|
|
218
220
|
if provider is None and BedrockAugmentedLLM.matches_model_pattern(model_name_str):
|
|
219
221
|
provider = Provider.BEDROCK
|
|
220
|
-
|
|
222
|
+
|
|
221
223
|
if provider is None:
|
|
222
224
|
raise ModelConfigError(
|
|
223
225
|
f"Unknown model or provider for: {model_string}. Model name parsed as '{model_name_str}'"
|
mcp_agent/llm/provider_types.py
CHANGED
|
@@ -46,7 +46,7 @@ from mcp_agent.llm.augmented_llm import (
|
|
|
46
46
|
)
|
|
47
47
|
from mcp_agent.logging.logger import get_logger
|
|
48
48
|
|
|
49
|
-
DEFAULT_ANTHROPIC_MODEL = "claude-
|
|
49
|
+
DEFAULT_ANTHROPIC_MODEL = "claude-sonnet-4-0"
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
class AnthropicAugmentedLLM(AugmentedLLM[MessageParam, Message]):
|
|
@@ -28,7 +28,7 @@ class DeepSeekAugmentedLLM(OpenAIAugmentedLLM):
|
|
|
28
28
|
model=chosen_model,
|
|
29
29
|
systemPrompt=self.instruction,
|
|
30
30
|
parallel_tool_calls=True,
|
|
31
|
-
max_iterations=
|
|
31
|
+
max_iterations=20,
|
|
32
32
|
use_history=True,
|
|
33
33
|
)
|
|
34
34
|
|
|
@@ -85,7 +85,9 @@ class DeepSeekAugmentedLLM(OpenAIAugmentedLLM):
|
|
|
85
85
|
return self._structured_from_multipart(result, model)
|
|
86
86
|
|
|
87
87
|
@classmethod
|
|
88
|
-
def convert_message_to_message_param(
|
|
88
|
+
def convert_message_to_message_param(
|
|
89
|
+
cls, message: ChatCompletionMessage, **kwargs
|
|
90
|
+
) -> ChatCompletionAssistantMessageParam:
|
|
89
91
|
"""Convert a response object to an input parameter object to allow LLM calls to be chained."""
|
|
90
92
|
if hasattr(message, "reasoning_content"):
|
|
91
93
|
message = copy(message)
|