fast-agent-mcp 0.3.12__py3-none-any.whl → 0.3.14__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/agents/llm_agent.py +15 -34
- fast_agent/agents/llm_decorator.py +13 -2
- fast_agent/agents/mcp_agent.py +18 -2
- fast_agent/agents/tool_agent.py +8 -10
- fast_agent/cli/commands/check_config.py +45 -1
- fast_agent/config.py +63 -0
- fast_agent/constants.py +3 -0
- fast_agent/context.py +42 -9
- fast_agent/core/logging/listeners.py +1 -1
- fast_agent/event_progress.py +2 -3
- fast_agent/interfaces.py +9 -2
- fast_agent/llm/model_factory.py +4 -0
- fast_agent/llm/provider/google/google_converter.py +10 -3
- fast_agent/llm/provider_key_manager.py +1 -0
- fast_agent/llm/provider_types.py +1 -0
- fast_agent/llm/request_params.py +3 -1
- fast_agent/mcp/mcp_agent_client_session.py +13 -0
- fast_agent/mcp/mcp_aggregator.py +313 -40
- fast_agent/mcp/mcp_connection_manager.py +95 -22
- fast_agent/mcp/skybridge.py +45 -0
- fast_agent/mcp/sse_tracking.py +287 -0
- fast_agent/mcp/transport_tracking.py +37 -3
- fast_agent/mcp/types.py +24 -0
- fast_agent/resources/examples/workflows/router.py +1 -0
- fast_agent/resources/setup/fastagent.config.yaml +5 -0
- fast_agent/ui/console_display.py +347 -20
- fast_agent/ui/enhanced_prompt.py +107 -58
- fast_agent/ui/interactive_prompt.py +57 -34
- fast_agent/ui/mcp_display.py +159 -41
- fast_agent/ui/rich_progress.py +4 -1
- {fast_agent_mcp-0.3.12.dist-info → fast_agent_mcp-0.3.14.dist-info}/METADATA +16 -7
- {fast_agent_mcp-0.3.12.dist-info → fast_agent_mcp-0.3.14.dist-info}/RECORD +35 -32
- {fast_agent_mcp-0.3.12.dist-info → fast_agent_mcp-0.3.14.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.3.12.dist-info → fast_agent_mcp-0.3.14.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.3.12.dist-info → fast_agent_mcp-0.3.14.dist-info}/licenses/LICENSE +0 -0
fast_agent/ui/console_display.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
from json import JSONDecodeError
|
|
3
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Set, Tuple, Union
|
|
4
4
|
|
|
5
5
|
from mcp.types import CallToolResult
|
|
6
6
|
from rich.panel import Panel
|
|
7
7
|
from rich.text import Text
|
|
8
8
|
|
|
9
|
+
from fast_agent.constants import REASONING
|
|
9
10
|
from fast_agent.ui import console
|
|
10
11
|
from fast_agent.ui.mcp_ui_utils import UILink
|
|
11
12
|
from fast_agent.ui.mermaid_utils import (
|
|
@@ -17,6 +18,7 @@ from fast_agent.ui.mermaid_utils import (
|
|
|
17
18
|
|
|
18
19
|
if TYPE_CHECKING:
|
|
19
20
|
from fast_agent.mcp.prompt_message_extended import PromptMessageExtended
|
|
21
|
+
from fast_agent.mcp.skybridge import SkybridgeServerConfig
|
|
20
22
|
|
|
21
23
|
CODE_STYLE = "native"
|
|
22
24
|
|
|
@@ -144,6 +146,25 @@ class ConsoleDisplay:
|
|
|
144
146
|
self._markup = config.logger.enable_markup if config else True
|
|
145
147
|
self._escape_xml = True
|
|
146
148
|
|
|
149
|
+
@staticmethod
|
|
150
|
+
def _format_elapsed(elapsed: float) -> str:
|
|
151
|
+
"""Format elapsed seconds for display."""
|
|
152
|
+
if elapsed < 0:
|
|
153
|
+
elapsed = 0.0
|
|
154
|
+
if elapsed < 0.001:
|
|
155
|
+
return "<1ms"
|
|
156
|
+
if elapsed < 1:
|
|
157
|
+
return f"{elapsed * 1000:.0f}ms"
|
|
158
|
+
if elapsed < 10:
|
|
159
|
+
return f"{elapsed:.2f}s"
|
|
160
|
+
if elapsed < 60:
|
|
161
|
+
return f"{elapsed:.1f}s"
|
|
162
|
+
minutes, seconds = divmod(elapsed, 60)
|
|
163
|
+
if minutes < 60:
|
|
164
|
+
return f"{int(minutes)}m {seconds:02.0f}s"
|
|
165
|
+
hours, minutes = divmod(int(minutes), 60)
|
|
166
|
+
return f"{hours}h {minutes:02d}m"
|
|
167
|
+
|
|
147
168
|
def display_message(
|
|
148
169
|
self,
|
|
149
170
|
content: Any,
|
|
@@ -156,6 +177,7 @@ class ConsoleDisplay:
|
|
|
156
177
|
is_error: bool = False,
|
|
157
178
|
truncate_content: bool = True,
|
|
158
179
|
additional_message: Text | None = None,
|
|
180
|
+
pre_content: Text | None = None,
|
|
159
181
|
) -> None:
|
|
160
182
|
"""
|
|
161
183
|
Unified method to display formatted messages to the console.
|
|
@@ -170,6 +192,8 @@ class ConsoleDisplay:
|
|
|
170
192
|
max_item_length: Optional max length for bottom metadata items (with ellipsis)
|
|
171
193
|
is_error: For tool results, whether this is an error (uses red color)
|
|
172
194
|
truncate_content: Whether to truncate long content
|
|
195
|
+
additional_message: Optional Rich Text appended after the main content
|
|
196
|
+
pre_content: Optional Rich Text shown before the main content
|
|
173
197
|
"""
|
|
174
198
|
# Get configuration for this message type
|
|
175
199
|
config = MESSAGE_CONFIGS[message_type]
|
|
@@ -191,6 +215,8 @@ class ConsoleDisplay:
|
|
|
191
215
|
self._create_combined_separator_status(left, right_info)
|
|
192
216
|
|
|
193
217
|
# Display the content
|
|
218
|
+
if pre_content and pre_content.plain:
|
|
219
|
+
console.console.print(pre_content, markup=self._markup)
|
|
194
220
|
self._display_content(
|
|
195
221
|
content, truncate_content, is_error, message_type, check_markdown_markers=False
|
|
196
222
|
)
|
|
@@ -513,8 +539,21 @@ class ConsoleDisplay:
|
|
|
513
539
|
|
|
514
540
|
return formatted
|
|
515
541
|
|
|
516
|
-
def show_tool_result(
|
|
517
|
-
|
|
542
|
+
def show_tool_result(
|
|
543
|
+
self,
|
|
544
|
+
result: CallToolResult,
|
|
545
|
+
name: str | None = None,
|
|
546
|
+
tool_name: str | None = None,
|
|
547
|
+
skybridge_config: "SkybridgeServerConfig | None" = None,
|
|
548
|
+
) -> None:
|
|
549
|
+
"""Display a tool result in the new visual style.
|
|
550
|
+
|
|
551
|
+
Args:
|
|
552
|
+
result: The tool result to display
|
|
553
|
+
name: Optional agent name
|
|
554
|
+
tool_name: Optional tool name for skybridge detection
|
|
555
|
+
skybridge_config: Optional skybridge configuration for the server
|
|
556
|
+
"""
|
|
518
557
|
if not self.config or not self.config.logger.show_tools:
|
|
519
558
|
return
|
|
520
559
|
|
|
@@ -523,6 +562,20 @@ class ConsoleDisplay:
|
|
|
523
562
|
|
|
524
563
|
# Analyze content to determine display format and status
|
|
525
564
|
content = result.content
|
|
565
|
+
structured_content = getattr(result, "structuredContent", None)
|
|
566
|
+
has_structured = structured_content is not None
|
|
567
|
+
|
|
568
|
+
# Determine if this is a skybridge tool
|
|
569
|
+
is_skybridge_tool = False
|
|
570
|
+
skybridge_resource_uri = None
|
|
571
|
+
if has_structured and tool_name and skybridge_config:
|
|
572
|
+
# Check if this tool is a valid skybridge tool
|
|
573
|
+
for tool_cfg in skybridge_config.tools:
|
|
574
|
+
if tool_cfg.tool_name == tool_name and tool_cfg.is_valid:
|
|
575
|
+
is_skybridge_tool = True
|
|
576
|
+
skybridge_resource_uri = tool_cfg.resource_uri
|
|
577
|
+
break
|
|
578
|
+
|
|
526
579
|
if result.isError:
|
|
527
580
|
status = "ERROR"
|
|
528
581
|
else:
|
|
@@ -544,15 +597,15 @@ class ConsoleDisplay:
|
|
|
544
597
|
|
|
545
598
|
# Build transport channel info for bottom bar
|
|
546
599
|
channel = getattr(result, "transport_channel", None)
|
|
547
|
-
|
|
600
|
+
bottom_metadata_items: List[str] = []
|
|
548
601
|
if channel:
|
|
549
602
|
# Format channel info for bottom bar
|
|
550
603
|
if channel == "post-json":
|
|
551
604
|
transport_info = "HTTP (JSON-RPC)"
|
|
552
605
|
elif channel == "post-sse":
|
|
553
|
-
transport_info = "
|
|
606
|
+
transport_info = "Legacy SSE"
|
|
554
607
|
elif channel == "get":
|
|
555
|
-
transport_info = "
|
|
608
|
+
transport_info = "Legacy SSE"
|
|
556
609
|
elif channel == "resumption":
|
|
557
610
|
transport_info = "Resumption"
|
|
558
611
|
elif channel == "stdio":
|
|
@@ -560,21 +613,144 @@ class ConsoleDisplay:
|
|
|
560
613
|
else:
|
|
561
614
|
transport_info = channel.upper()
|
|
562
615
|
|
|
563
|
-
|
|
616
|
+
bottom_metadata_items.append(transport_info)
|
|
617
|
+
|
|
618
|
+
elapsed = getattr(result, "transport_elapsed", None)
|
|
619
|
+
if isinstance(elapsed, (int, float)):
|
|
620
|
+
bottom_metadata_items.append(self._format_elapsed(float(elapsed)))
|
|
621
|
+
|
|
622
|
+
# Add structured content indicator if present
|
|
623
|
+
if has_structured:
|
|
624
|
+
bottom_metadata_items.append("Structured ■")
|
|
625
|
+
|
|
626
|
+
bottom_metadata = bottom_metadata_items or None
|
|
564
627
|
|
|
565
628
|
# Build right info (without channel info)
|
|
566
629
|
right_info = f"[dim]tool result - {status}[/dim]"
|
|
567
630
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
content
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
631
|
+
if has_structured:
|
|
632
|
+
# Handle structured content display manually to insert it before bottom separator
|
|
633
|
+
# Display main content without bottom separator
|
|
634
|
+
config = MESSAGE_CONFIGS[MessageType.TOOL_RESULT]
|
|
635
|
+
block_color = "red" if result.isError else config["block_color"]
|
|
636
|
+
arrow = config["arrow"]
|
|
637
|
+
arrow_style = config["arrow_style"]
|
|
638
|
+
left = f"[{block_color}]▎[/{block_color}][{arrow_style}]{arrow}[/{arrow_style}]"
|
|
639
|
+
if name:
|
|
640
|
+
left += f" [{block_color if not result.isError else 'red'}]{name}[/{block_color if not result.isError else 'red'}]"
|
|
641
|
+
|
|
642
|
+
# Top separator
|
|
643
|
+
self._create_combined_separator_status(left, right_info)
|
|
644
|
+
|
|
645
|
+
# Main content
|
|
646
|
+
self._display_content(
|
|
647
|
+
content, True, result.isError, MessageType.TOOL_RESULT, check_markdown_markers=False
|
|
648
|
+
)
|
|
649
|
+
|
|
650
|
+
# Structured content separator and display
|
|
651
|
+
console.console.print()
|
|
652
|
+
total_width = console.console.size.width
|
|
653
|
+
|
|
654
|
+
if is_skybridge_tool:
|
|
655
|
+
# Skybridge: magenta separator with resource URI
|
|
656
|
+
resource_label = (
|
|
657
|
+
f"skybridge resource: {skybridge_resource_uri}"
|
|
658
|
+
if skybridge_resource_uri
|
|
659
|
+
else "skybridge resource"
|
|
660
|
+
)
|
|
661
|
+
prefix = Text("─| ")
|
|
662
|
+
prefix.stylize("dim")
|
|
663
|
+
resource_text = Text(resource_label, style="magenta")
|
|
664
|
+
suffix = Text(" |")
|
|
665
|
+
suffix.stylize("dim")
|
|
666
|
+
|
|
667
|
+
separator_line = Text()
|
|
668
|
+
separator_line.append_text(prefix)
|
|
669
|
+
separator_line.append_text(resource_text)
|
|
670
|
+
separator_line.append_text(suffix)
|
|
671
|
+
remaining = total_width - separator_line.cell_len
|
|
672
|
+
if remaining > 0:
|
|
673
|
+
separator_line.append("─" * remaining, style="dim")
|
|
674
|
+
console.console.print(separator_line, markup=self._markup)
|
|
675
|
+
console.console.print()
|
|
676
|
+
|
|
677
|
+
# Display with bright syntax highlighting
|
|
678
|
+
import json
|
|
679
|
+
|
|
680
|
+
from rich.syntax import Syntax
|
|
681
|
+
|
|
682
|
+
json_str = json.dumps(structured_content, indent=2)
|
|
683
|
+
syntax_obj = Syntax(json_str, "json", theme=CODE_STYLE, background_color="default")
|
|
684
|
+
console.console.print(syntax_obj, markup=self._markup)
|
|
685
|
+
else:
|
|
686
|
+
# Regular tool: dim separator
|
|
687
|
+
prefix = Text("─| ")
|
|
688
|
+
prefix.stylize("dim")
|
|
689
|
+
label_text = Text("Structured Content", style="dim")
|
|
690
|
+
suffix = Text(" |")
|
|
691
|
+
suffix.stylize("dim")
|
|
692
|
+
|
|
693
|
+
separator_line = Text()
|
|
694
|
+
separator_line.append_text(prefix)
|
|
695
|
+
separator_line.append_text(label_text)
|
|
696
|
+
separator_line.append_text(suffix)
|
|
697
|
+
remaining = total_width - separator_line.cell_len
|
|
698
|
+
if remaining > 0:
|
|
699
|
+
separator_line.append("─" * remaining, style="dim")
|
|
700
|
+
console.console.print(separator_line, markup=self._markup)
|
|
701
|
+
console.console.print()
|
|
702
|
+
|
|
703
|
+
# Display truncated content in dim
|
|
704
|
+
from rich.pretty import Pretty
|
|
705
|
+
|
|
706
|
+
if self.config and self.config.logger.truncate_tools:
|
|
707
|
+
pretty_obj = Pretty(structured_content, max_length=10, max_string=50)
|
|
708
|
+
else:
|
|
709
|
+
pretty_obj = Pretty(structured_content)
|
|
710
|
+
console.console.print(pretty_obj, style="dim", markup=self._markup)
|
|
711
|
+
|
|
712
|
+
# Bottom separator with metadata
|
|
713
|
+
console.console.print()
|
|
714
|
+
if bottom_metadata:
|
|
715
|
+
display_items = (
|
|
716
|
+
self._shorten_items(bottom_metadata, 12) if True else bottom_metadata
|
|
717
|
+
)
|
|
718
|
+
prefix = Text("─| ")
|
|
719
|
+
prefix.stylize("dim")
|
|
720
|
+
suffix = Text(" |")
|
|
721
|
+
suffix.stylize("dim")
|
|
722
|
+
available = max(0, total_width - prefix.cell_len - suffix.cell_len)
|
|
723
|
+
|
|
724
|
+
metadata_text = self._format_bottom_metadata(
|
|
725
|
+
display_items,
|
|
726
|
+
None,
|
|
727
|
+
config["highlight_color"],
|
|
728
|
+
max_width=available,
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
line = Text()
|
|
732
|
+
line.append_text(prefix)
|
|
733
|
+
line.append_text(metadata_text)
|
|
734
|
+
line.append_text(suffix)
|
|
735
|
+
remaining = total_width - line.cell_len
|
|
736
|
+
if remaining > 0:
|
|
737
|
+
line.append("─" * remaining, style="dim")
|
|
738
|
+
console.console.print(line, markup=self._markup)
|
|
739
|
+
else:
|
|
740
|
+
console.console.print("─" * total_width, style="dim")
|
|
741
|
+
console.console.print()
|
|
742
|
+
|
|
743
|
+
else:
|
|
744
|
+
# No structured content - use standard display
|
|
745
|
+
self.display_message(
|
|
746
|
+
content=content,
|
|
747
|
+
message_type=MessageType.TOOL_RESULT,
|
|
748
|
+
name=name,
|
|
749
|
+
right_info=right_info,
|
|
750
|
+
bottom_metadata=bottom_metadata,
|
|
751
|
+
is_error=result.isError,
|
|
752
|
+
truncate_content=True,
|
|
753
|
+
)
|
|
578
754
|
|
|
579
755
|
def show_tool_call(
|
|
580
756
|
self,
|
|
@@ -638,9 +814,7 @@ class ConsoleDisplay:
|
|
|
638
814
|
# No active prompt_toolkit session - display with rich as before
|
|
639
815
|
# Combined separator and status line
|
|
640
816
|
if agent_name:
|
|
641
|
-
left =
|
|
642
|
-
f"[magenta]▎[/magenta][dim magenta]▶[/dim magenta] [magenta]{agent_name}[/magenta]"
|
|
643
|
-
)
|
|
817
|
+
left = f"[magenta]▎[/magenta][dim magenta]▶[/dim magenta] [magenta]{agent_name}[/magenta]"
|
|
644
818
|
else:
|
|
645
819
|
left = "[magenta]▎[/magenta][dim magenta]▶[/dim magenta]"
|
|
646
820
|
|
|
@@ -696,6 +870,140 @@ class ConsoleDisplay:
|
|
|
696
870
|
console.console.print(combined, markup=self._markup)
|
|
697
871
|
console.console.print()
|
|
698
872
|
|
|
873
|
+
@staticmethod
|
|
874
|
+
def summarize_skybridge_configs(
|
|
875
|
+
configs: Mapping[str, "SkybridgeServerConfig"] | None,
|
|
876
|
+
) -> Tuple[List[Dict[str, Any]], List[str]]:
|
|
877
|
+
"""Convert raw Skybridge configs into display-friendly summary data."""
|
|
878
|
+
server_rows: List[Dict[str, Any]] = []
|
|
879
|
+
warnings: List[str] = []
|
|
880
|
+
warning_seen: Set[str] = set()
|
|
881
|
+
|
|
882
|
+
if not configs:
|
|
883
|
+
return server_rows, warnings
|
|
884
|
+
|
|
885
|
+
def add_warning(message: str) -> None:
|
|
886
|
+
formatted = message.strip()
|
|
887
|
+
if not formatted:
|
|
888
|
+
return
|
|
889
|
+
if formatted not in warning_seen:
|
|
890
|
+
warnings.append(formatted)
|
|
891
|
+
warning_seen.add(formatted)
|
|
892
|
+
|
|
893
|
+
for server_name in sorted(configs.keys()):
|
|
894
|
+
config = configs.get(server_name)
|
|
895
|
+
if not config:
|
|
896
|
+
continue
|
|
897
|
+
resources = list(config.ui_resources or [])
|
|
898
|
+
has_skybridge_signal = bool(
|
|
899
|
+
config.enabled or resources or config.tools or config.warnings
|
|
900
|
+
)
|
|
901
|
+
if not has_skybridge_signal:
|
|
902
|
+
continue
|
|
903
|
+
|
|
904
|
+
valid_resource_count = sum(1 for resource in resources if resource.is_skybridge)
|
|
905
|
+
|
|
906
|
+
server_rows.append(
|
|
907
|
+
{
|
|
908
|
+
"server_name": server_name,
|
|
909
|
+
"config": config,
|
|
910
|
+
"resources": resources,
|
|
911
|
+
"valid_resource_count": valid_resource_count,
|
|
912
|
+
"total_resource_count": len(resources),
|
|
913
|
+
"active_tools": [
|
|
914
|
+
{
|
|
915
|
+
"name": tool.display_name,
|
|
916
|
+
"template": str(tool.template_uri) if tool.template_uri else None,
|
|
917
|
+
}
|
|
918
|
+
for tool in config.tools
|
|
919
|
+
if tool.is_valid
|
|
920
|
+
],
|
|
921
|
+
"enabled": config.enabled,
|
|
922
|
+
}
|
|
923
|
+
)
|
|
924
|
+
|
|
925
|
+
for warning in config.warnings:
|
|
926
|
+
message = warning.strip()
|
|
927
|
+
if not message:
|
|
928
|
+
continue
|
|
929
|
+
if not message.startswith(server_name):
|
|
930
|
+
message = f"{server_name} {message}"
|
|
931
|
+
add_warning(message)
|
|
932
|
+
|
|
933
|
+
return server_rows, warnings
|
|
934
|
+
|
|
935
|
+
def show_skybridge_summary(
|
|
936
|
+
self,
|
|
937
|
+
agent_name: str,
|
|
938
|
+
configs: Mapping[str, "SkybridgeServerConfig"] | None,
|
|
939
|
+
) -> None:
|
|
940
|
+
"""Display Skybridge availability and warnings."""
|
|
941
|
+
server_rows, warnings = self.summarize_skybridge_configs(configs)
|
|
942
|
+
|
|
943
|
+
if not server_rows and not warnings:
|
|
944
|
+
return
|
|
945
|
+
|
|
946
|
+
heading = "[dim]OpenAI Apps SDK ([/dim][cyan]skybridge[/cyan][dim]) detected:[/dim]"
|
|
947
|
+
console.console.print()
|
|
948
|
+
console.console.print(heading, markup=self._markup)
|
|
949
|
+
|
|
950
|
+
if not server_rows:
|
|
951
|
+
console.console.print("[dim] ● none detected[/dim]", markup=self._markup)
|
|
952
|
+
else:
|
|
953
|
+
for row in server_rows:
|
|
954
|
+
server_name = row["server_name"]
|
|
955
|
+
resource_count = row["valid_resource_count"]
|
|
956
|
+
total_resource_count = row["total_resource_count"]
|
|
957
|
+
tool_infos = row["active_tools"]
|
|
958
|
+
enabled = row["enabled"]
|
|
959
|
+
|
|
960
|
+
tool_count = len(tool_infos)
|
|
961
|
+
tool_word = "tool" if tool_count == 1 else "tools"
|
|
962
|
+
resource_word = (
|
|
963
|
+
"skybridge resource" if resource_count == 1 else "skybridge resources"
|
|
964
|
+
)
|
|
965
|
+
tool_segment = f"[cyan]{tool_count}[/cyan][dim] {tool_word}[/dim]"
|
|
966
|
+
resource_segment = f"[cyan]{resource_count}[/cyan][dim] {resource_word}[/dim]"
|
|
967
|
+
name_style = "cyan" if enabled else "yellow"
|
|
968
|
+
status_suffix = "" if enabled else "[dim] (issues detected)[/dim]"
|
|
969
|
+
|
|
970
|
+
console.console.print(
|
|
971
|
+
f"[dim] ● [/dim][{name_style}]{server_name}[/{name_style}]{status_suffix}"
|
|
972
|
+
f"[dim] — [/dim]{tool_segment}[dim], [/dim]{resource_segment}",
|
|
973
|
+
markup=self._markup,
|
|
974
|
+
)
|
|
975
|
+
|
|
976
|
+
for tool_info in tool_infos:
|
|
977
|
+
template_text = (
|
|
978
|
+
f"[dim] ({tool_info['template']})[/dim]" if tool_info["template"] else ""
|
|
979
|
+
)
|
|
980
|
+
console.console.print(
|
|
981
|
+
f"[dim] ▶ [/dim][white]{tool_info['name']}[/white]{template_text}",
|
|
982
|
+
markup=self._markup,
|
|
983
|
+
)
|
|
984
|
+
|
|
985
|
+
if tool_count == 0 and resource_count > 0:
|
|
986
|
+
console.console.print(
|
|
987
|
+
"[dim] ▶ tools not linked[/dim]",
|
|
988
|
+
markup=self._markup,
|
|
989
|
+
)
|
|
990
|
+
if not enabled and total_resource_count > resource_count:
|
|
991
|
+
invalid_count = total_resource_count - resource_count
|
|
992
|
+
invalid_word = "resource" if invalid_count == 1 else "resources"
|
|
993
|
+
console.console.print(
|
|
994
|
+
(
|
|
995
|
+
"[dim] ▶ "
|
|
996
|
+
f"[/dim][cyan]{invalid_count}[/cyan][dim] {invalid_word} detected with non-skybridge MIME type[/dim]"
|
|
997
|
+
),
|
|
998
|
+
markup=self._markup,
|
|
999
|
+
)
|
|
1000
|
+
|
|
1001
|
+
for warning_entry in warnings:
|
|
1002
|
+
console.console.print(
|
|
1003
|
+
f"[dim red] ▶ [/dim red][red]warning[/red] [dim]{warning_entry}[/dim]",
|
|
1004
|
+
markup=self._markup,
|
|
1005
|
+
)
|
|
1006
|
+
|
|
699
1007
|
async def show_assistant_message(
|
|
700
1008
|
self,
|
|
701
1009
|
message_text: Union[str, Text, "PromptMessageExtended"],
|
|
@@ -724,8 +1032,26 @@ class ConsoleDisplay:
|
|
|
724
1032
|
# Extract text from PromptMessageExtended if needed
|
|
725
1033
|
from fast_agent.types import PromptMessageExtended
|
|
726
1034
|
|
|
1035
|
+
pre_content: Text | None = None
|
|
1036
|
+
|
|
727
1037
|
if isinstance(message_text, PromptMessageExtended):
|
|
728
1038
|
display_text = message_text.last_text() or ""
|
|
1039
|
+
|
|
1040
|
+
channels = message_text.channels or {}
|
|
1041
|
+
reasoning_blocks = channels.get(REASONING) or []
|
|
1042
|
+
if reasoning_blocks:
|
|
1043
|
+
from fast_agent.mcp.helpers.content_helpers import get_text
|
|
1044
|
+
|
|
1045
|
+
reasoning_segments = []
|
|
1046
|
+
for block in reasoning_blocks:
|
|
1047
|
+
text = get_text(block)
|
|
1048
|
+
if text:
|
|
1049
|
+
reasoning_segments.append(text)
|
|
1050
|
+
|
|
1051
|
+
if reasoning_segments:
|
|
1052
|
+
joined = "\n".join(reasoning_segments)
|
|
1053
|
+
if joined.strip():
|
|
1054
|
+
pre_content = Text(joined, style="dim default")
|
|
729
1055
|
else:
|
|
730
1056
|
display_text = message_text
|
|
731
1057
|
|
|
@@ -743,6 +1069,7 @@ class ConsoleDisplay:
|
|
|
743
1069
|
max_item_length=max_item_length,
|
|
744
1070
|
truncate_content=False, # Assistant messages shouldn't be truncated
|
|
745
1071
|
additional_message=additional_message,
|
|
1072
|
+
pre_content=pre_content,
|
|
746
1073
|
)
|
|
747
1074
|
|
|
748
1075
|
# Handle mermaid diagrams separately (after the main message)
|