fast-agent-mcp 0.2.57__py3-none-any.whl → 0.3.0__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/__init__.py +127 -0
- fast_agent/agents/__init__.py +36 -0
- {mcp_agent/core → fast_agent/agents}/agent_types.py +2 -1
- fast_agent/agents/llm_agent.py +217 -0
- fast_agent/agents/llm_decorator.py +486 -0
- mcp_agent/agents/base_agent.py → fast_agent/agents/mcp_agent.py +377 -385
- fast_agent/agents/tool_agent.py +168 -0
- {mcp_agent → fast_agent}/agents/workflow/chain_agent.py +43 -33
- {mcp_agent → fast_agent}/agents/workflow/evaluator_optimizer.py +31 -35
- {mcp_agent → fast_agent}/agents/workflow/iterative_planner.py +56 -47
- {mcp_agent → fast_agent}/agents/workflow/orchestrator_models.py +4 -4
- {mcp_agent → fast_agent}/agents/workflow/parallel_agent.py +34 -41
- {mcp_agent → fast_agent}/agents/workflow/router_agent.py +54 -39
- {mcp_agent → fast_agent}/cli/__main__.py +5 -3
- {mcp_agent → fast_agent}/cli/commands/check_config.py +95 -66
- {mcp_agent → fast_agent}/cli/commands/go.py +20 -11
- {mcp_agent → fast_agent}/cli/commands/quickstart.py +4 -4
- {mcp_agent → fast_agent}/cli/commands/server_helpers.py +1 -1
- {mcp_agent → fast_agent}/cli/commands/setup.py +64 -134
- {mcp_agent → fast_agent}/cli/commands/url_parser.py +9 -8
- {mcp_agent → fast_agent}/cli/main.py +36 -16
- {mcp_agent → fast_agent}/cli/terminal.py +2 -2
- {mcp_agent → fast_agent}/config.py +13 -2
- fast_agent/constants.py +8 -0
- {mcp_agent → fast_agent}/context.py +24 -19
- {mcp_agent → fast_agent}/context_dependent.py +9 -5
- fast_agent/core/__init__.py +17 -0
- {mcp_agent → fast_agent}/core/agent_app.py +39 -36
- fast_agent/core/core_app.py +135 -0
- {mcp_agent → fast_agent}/core/direct_decorators.py +12 -26
- {mcp_agent → fast_agent}/core/direct_factory.py +95 -73
- {mcp_agent → fast_agent/core}/executor/executor.py +4 -5
- {mcp_agent → fast_agent}/core/fastagent.py +32 -32
- fast_agent/core/logging/__init__.py +5 -0
- {mcp_agent → fast_agent/core}/logging/events.py +3 -3
- {mcp_agent → fast_agent/core}/logging/json_serializer.py +1 -1
- {mcp_agent → fast_agent/core}/logging/listeners.py +85 -7
- {mcp_agent → fast_agent/core}/logging/logger.py +7 -7
- {mcp_agent → fast_agent/core}/logging/transport.py +10 -11
- fast_agent/core/prompt.py +9 -0
- {mcp_agent → fast_agent}/core/validation.py +4 -4
- fast_agent/event_progress.py +61 -0
- fast_agent/history/history_exporter.py +44 -0
- {mcp_agent → fast_agent}/human_input/__init__.py +9 -12
- {mcp_agent → fast_agent}/human_input/elicitation_handler.py +26 -8
- {mcp_agent → fast_agent}/human_input/elicitation_state.py +7 -7
- {mcp_agent → fast_agent}/human_input/simple_form.py +6 -4
- {mcp_agent → fast_agent}/human_input/types.py +1 -18
- fast_agent/interfaces.py +228 -0
- fast_agent/llm/__init__.py +9 -0
- mcp_agent/llm/augmented_llm.py → fast_agent/llm/fastagent_llm.py +128 -218
- fast_agent/llm/internal/passthrough.py +137 -0
- mcp_agent/llm/augmented_llm_playback.py → fast_agent/llm/internal/playback.py +29 -25
- mcp_agent/llm/augmented_llm_silent.py → fast_agent/llm/internal/silent.py +10 -17
- fast_agent/llm/internal/slow.py +38 -0
- {mcp_agent → fast_agent}/llm/memory.py +40 -30
- {mcp_agent → fast_agent}/llm/model_database.py +35 -2
- {mcp_agent → fast_agent}/llm/model_factory.py +103 -77
- fast_agent/llm/model_info.py +126 -0
- {mcp_agent/llm/providers → fast_agent/llm/provider/anthropic}/anthropic_utils.py +7 -7
- fast_agent/llm/provider/anthropic/llm_anthropic.py +603 -0
- {mcp_agent/llm/providers → fast_agent/llm/provider/anthropic}/multipart_converter_anthropic.py +79 -86
- fast_agent/llm/provider/bedrock/bedrock_utils.py +218 -0
- fast_agent/llm/provider/bedrock/llm_bedrock.py +2192 -0
- {mcp_agent/llm/providers → fast_agent/llm/provider/google}/google_converter.py +66 -14
- fast_agent/llm/provider/google/llm_google_native.py +431 -0
- mcp_agent/llm/providers/augmented_llm_aliyun.py → fast_agent/llm/provider/openai/llm_aliyun.py +6 -7
- mcp_agent/llm/providers/augmented_llm_azure.py → fast_agent/llm/provider/openai/llm_azure.py +4 -4
- mcp_agent/llm/providers/augmented_llm_deepseek.py → fast_agent/llm/provider/openai/llm_deepseek.py +10 -11
- mcp_agent/llm/providers/augmented_llm_generic.py → fast_agent/llm/provider/openai/llm_generic.py +4 -4
- mcp_agent/llm/providers/augmented_llm_google_oai.py → fast_agent/llm/provider/openai/llm_google_oai.py +4 -4
- mcp_agent/llm/providers/augmented_llm_groq.py → fast_agent/llm/provider/openai/llm_groq.py +14 -16
- mcp_agent/llm/providers/augmented_llm_openai.py → fast_agent/llm/provider/openai/llm_openai.py +133 -206
- mcp_agent/llm/providers/augmented_llm_openrouter.py → fast_agent/llm/provider/openai/llm_openrouter.py +6 -6
- mcp_agent/llm/providers/augmented_llm_tensorzero_openai.py → fast_agent/llm/provider/openai/llm_tensorzero_openai.py +17 -16
- mcp_agent/llm/providers/augmented_llm_xai.py → fast_agent/llm/provider/openai/llm_xai.py +6 -6
- {mcp_agent/llm/providers → fast_agent/llm/provider/openai}/multipart_converter_openai.py +125 -63
- {mcp_agent/llm/providers → fast_agent/llm/provider/openai}/openai_multipart.py +12 -12
- {mcp_agent/llm/providers → fast_agent/llm/provider/openai}/openai_utils.py +18 -16
- {mcp_agent → fast_agent}/llm/provider_key_manager.py +2 -2
- {mcp_agent → fast_agent}/llm/provider_types.py +2 -0
- {mcp_agent → fast_agent}/llm/sampling_converter.py +15 -12
- {mcp_agent → fast_agent}/llm/usage_tracking.py +23 -5
- fast_agent/mcp/__init__.py +43 -0
- {mcp_agent → fast_agent}/mcp/elicitation_factory.py +3 -3
- {mcp_agent → fast_agent}/mcp/elicitation_handlers.py +19 -10
- {mcp_agent → fast_agent}/mcp/gen_client.py +3 -3
- fast_agent/mcp/helpers/__init__.py +36 -0
- fast_agent/mcp/helpers/content_helpers.py +183 -0
- {mcp_agent → fast_agent}/mcp/helpers/server_config_helpers.py +8 -8
- {mcp_agent → fast_agent}/mcp/hf_auth.py +25 -23
- fast_agent/mcp/interfaces.py +93 -0
- {mcp_agent → fast_agent}/mcp/logger_textio.py +4 -4
- {mcp_agent → fast_agent}/mcp/mcp_agent_client_session.py +49 -44
- {mcp_agent → fast_agent}/mcp/mcp_aggregator.py +66 -115
- {mcp_agent → fast_agent}/mcp/mcp_connection_manager.py +16 -23
- {mcp_agent/core → fast_agent/mcp}/mcp_content.py +23 -15
- {mcp_agent → fast_agent}/mcp/mime_utils.py +39 -0
- fast_agent/mcp/prompt.py +159 -0
- mcp_agent/mcp/prompt_message_multipart.py → fast_agent/mcp/prompt_message_extended.py +27 -20
- {mcp_agent → fast_agent}/mcp/prompt_render.py +21 -19
- {mcp_agent → fast_agent}/mcp/prompt_serialization.py +46 -46
- fast_agent/mcp/prompts/__main__.py +7 -0
- {mcp_agent → fast_agent}/mcp/prompts/prompt_helpers.py +31 -30
- {mcp_agent → fast_agent}/mcp/prompts/prompt_load.py +8 -8
- {mcp_agent → fast_agent}/mcp/prompts/prompt_server.py +11 -19
- {mcp_agent → fast_agent}/mcp/prompts/prompt_template.py +18 -18
- {mcp_agent → fast_agent}/mcp/resource_utils.py +1 -1
- {mcp_agent → fast_agent}/mcp/sampling.py +31 -26
- {mcp_agent/mcp_server → fast_agent/mcp/server}/__init__.py +1 -1
- {mcp_agent/mcp_server → fast_agent/mcp/server}/agent_server.py +5 -6
- fast_agent/mcp/ui_agent.py +48 -0
- fast_agent/mcp/ui_mixin.py +209 -0
- fast_agent/mcp_server_registry.py +90 -0
- {mcp_agent → fast_agent}/resources/examples/data-analysis/analysis-campaign.py +5 -4
- {mcp_agent → fast_agent}/resources/examples/data-analysis/analysis.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/elicitation_forms_server.py +25 -3
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/forms_demo.py +3 -3
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/game_character.py +2 -2
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/game_character_handler.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/tool_call.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/agent_one.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/agent_two.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/researcher/researcher-eval.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/researcher/researcher-imp.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/researcher/researcher.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/tensorzero/agent.py +2 -2
- {mcp_agent → fast_agent}/resources/examples/tensorzero/image_demo.py +3 -3
- {mcp_agent → fast_agent}/resources/examples/tensorzero/simple_agent.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/workflows/chaining.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/workflows/evaluator.py +3 -3
- {mcp_agent → fast_agent}/resources/examples/workflows/human_input.py +5 -3
- {mcp_agent → fast_agent}/resources/examples/workflows/orchestrator.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/workflows/parallel.py +2 -2
- {mcp_agent → fast_agent}/resources/examples/workflows/router.py +5 -2
- fast_agent/resources/setup/.gitignore +24 -0
- fast_agent/resources/setup/agent.py +18 -0
- fast_agent/resources/setup/fastagent.config.yaml +44 -0
- fast_agent/resources/setup/fastagent.secrets.yaml.example +38 -0
- fast_agent/tools/elicitation.py +369 -0
- fast_agent/types/__init__.py +32 -0
- fast_agent/types/llm_stop_reason.py +77 -0
- fast_agent/ui/__init__.py +38 -0
- fast_agent/ui/console_display.py +1005 -0
- {mcp_agent/human_input → fast_agent/ui}/elicitation_form.py +56 -39
- mcp_agent/human_input/elicitation_forms.py → fast_agent/ui/elicitation_style.py +1 -1
- {mcp_agent/core → fast_agent/ui}/enhanced_prompt.py +96 -25
- {mcp_agent/core → fast_agent/ui}/interactive_prompt.py +330 -125
- fast_agent/ui/mcp_ui_utils.py +224 -0
- {mcp_agent → fast_agent/ui}/progress_display.py +2 -2
- {mcp_agent/logging → fast_agent/ui}/rich_progress.py +4 -4
- {mcp_agent/core → fast_agent/ui}/usage_display.py +3 -8
- {fast_agent_mcp-0.2.57.dist-info → fast_agent_mcp-0.3.0.dist-info}/METADATA +7 -7
- fast_agent_mcp-0.3.0.dist-info/RECORD +202 -0
- fast_agent_mcp-0.3.0.dist-info/entry_points.txt +5 -0
- fast_agent_mcp-0.2.57.dist-info/RECORD +0 -192
- fast_agent_mcp-0.2.57.dist-info/entry_points.txt +0 -6
- mcp_agent/__init__.py +0 -114
- mcp_agent/agents/agent.py +0 -92
- mcp_agent/agents/workflow/__init__.py +0 -1
- mcp_agent/agents/workflow/orchestrator_agent.py +0 -597
- mcp_agent/app.py +0 -175
- mcp_agent/core/__init__.py +0 -26
- mcp_agent/core/prompt.py +0 -191
- mcp_agent/event_progress.py +0 -134
- mcp_agent/human_input/handler.py +0 -81
- mcp_agent/llm/__init__.py +0 -2
- mcp_agent/llm/augmented_llm_passthrough.py +0 -232
- mcp_agent/llm/augmented_llm_slow.py +0 -53
- mcp_agent/llm/providers/__init__.py +0 -8
- mcp_agent/llm/providers/augmented_llm_anthropic.py +0 -717
- mcp_agent/llm/providers/augmented_llm_bedrock.py +0 -1788
- mcp_agent/llm/providers/augmented_llm_google_native.py +0 -495
- mcp_agent/llm/providers/sampling_converter_anthropic.py +0 -57
- mcp_agent/llm/providers/sampling_converter_openai.py +0 -26
- mcp_agent/llm/sampling_format_converter.py +0 -37
- mcp_agent/logging/__init__.py +0 -0
- mcp_agent/mcp/__init__.py +0 -50
- mcp_agent/mcp/helpers/__init__.py +0 -25
- mcp_agent/mcp/helpers/content_helpers.py +0 -187
- mcp_agent/mcp/interfaces.py +0 -266
- mcp_agent/mcp/prompts/__init__.py +0 -0
- mcp_agent/mcp/prompts/__main__.py +0 -10
- mcp_agent/mcp_server_registry.py +0 -343
- mcp_agent/tools/tool_definition.py +0 -14
- mcp_agent/ui/console_display.py +0 -790
- mcp_agent/ui/console_display_legacy.py +0 -401
- {mcp_agent → fast_agent}/agents/workflow/orchestrator_prompts.py +0 -0
- {mcp_agent/agents → fast_agent/cli}/__init__.py +0 -0
- {mcp_agent → fast_agent}/cli/constants.py +0 -0
- {mcp_agent → fast_agent}/core/error_handling.py +0 -0
- {mcp_agent → fast_agent}/core/exceptions.py +0 -0
- {mcp_agent/cli → fast_agent/core/executor}/__init__.py +0 -0
- {mcp_agent → fast_agent/core}/executor/task_registry.py +0 -0
- {mcp_agent → fast_agent/core}/executor/workflow_signal.py +0 -0
- {mcp_agent → fast_agent}/human_input/form_fields.py +0 -0
- {mcp_agent → fast_agent}/llm/prompt_utils.py +0 -0
- {mcp_agent/core → fast_agent/llm}/request_params.py +0 -0
- {mcp_agent → fast_agent}/mcp/common.py +0 -0
- {mcp_agent/executor → fast_agent/mcp/prompts}/__init__.py +0 -0
- {mcp_agent → fast_agent}/mcp/prompts/prompt_constants.py +0 -0
- {mcp_agent → fast_agent}/py.typed +0 -0
- {mcp_agent → fast_agent}/resources/examples/data-analysis/fastagent.config.yaml +0 -0
- {mcp_agent → fast_agent}/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -0
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/elicitation_account_server.py +0 -0
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/elicitation_game_server.py +0 -0
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/fastagent.config.yaml +0 -0
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/fastagent.secrets.yaml.example +0 -0
- {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/fastagent.config.yaml +0 -0
- {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example +0 -0
- {mcp_agent → fast_agent}/resources/examples/researcher/fastagent.config.yaml +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/.env.sample +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/Makefile +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/README.md +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/demo_images/clam.jpg +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/demo_images/crab.png +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/demo_images/shrimp.png +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/docker-compose.yml +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/fastagent.config.yaml +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/mcp_server/Dockerfile +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/mcp_server/entrypoint.sh +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/mcp_server/mcp_server.py +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/mcp_server/pyproject.toml +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/tensorzero_config/system_schema.json +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/tensorzero_config/system_template.minijinja +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/tensorzero_config/tensorzero.toml +0 -0
- {mcp_agent → fast_agent}/resources/examples/workflows/fastagent.config.yaml +0 -0
- {mcp_agent → fast_agent}/resources/examples/workflows/graded_report.md +0 -0
- {mcp_agent → fast_agent}/resources/examples/workflows/short_story.md +0 -0
- {mcp_agent → fast_agent}/resources/examples/workflows/short_story.txt +0 -0
- {mcp_agent → fast_agent/ui}/console.py +0 -0
- {mcp_agent/core → fast_agent/ui}/mermaid_utils.py +0 -0
- {fast_agent_mcp-0.2.57.dist-info → fast_agent_mcp-0.3.0.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.2.57.dist-info → fast_agent_mcp-0.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,9 +5,7 @@ from typing import Any, Dict, Optional
|
|
|
5
5
|
|
|
6
6
|
from mcp.types import ElicitRequestedSchema
|
|
7
7
|
from prompt_toolkit import Application
|
|
8
|
-
from prompt_toolkit.application.current import get_app
|
|
9
8
|
from prompt_toolkit.buffer import Buffer
|
|
10
|
-
from prompt_toolkit.filters import Condition
|
|
11
9
|
from prompt_toolkit.formatted_text import FormattedText
|
|
12
10
|
from prompt_toolkit.key_binding import KeyBindings
|
|
13
11
|
from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
|
|
@@ -24,8 +22,7 @@ from prompt_toolkit.widgets import (
|
|
|
24
22
|
from pydantic import AnyUrl, EmailStr
|
|
25
23
|
from pydantic import ValidationError as PydanticValidationError
|
|
26
24
|
|
|
27
|
-
from
|
|
28
|
-
from mcp_agent.human_input.elicitation_state import elicitation_state
|
|
25
|
+
from fast_agent.ui.elicitation_style import ELICITATION_STYLE
|
|
29
26
|
|
|
30
27
|
|
|
31
28
|
class SimpleNumberValidator(Validator):
|
|
@@ -282,10 +279,15 @@ class ElicitationForm:
|
|
|
282
279
|
]
|
|
283
280
|
)
|
|
284
281
|
|
|
285
|
-
#
|
|
282
|
+
# Choose dialog title: prefer schema.title if provided
|
|
283
|
+
dialog_title = self.schema.get("title") if isinstance(self.schema, dict) else None
|
|
284
|
+
if not dialog_title or not isinstance(dialog_title, str):
|
|
285
|
+
dialog_title = "Elicitation Request"
|
|
286
|
+
|
|
287
|
+
# Create dialog frame with dynamic title
|
|
286
288
|
dialog = Frame(
|
|
287
289
|
body=full_content,
|
|
288
|
-
title=
|
|
290
|
+
title=dialog_title,
|
|
289
291
|
style="class:dialog",
|
|
290
292
|
)
|
|
291
293
|
|
|
@@ -327,16 +329,21 @@ class ElicitationForm:
|
|
|
327
329
|
def focus_previous_left(event):
|
|
328
330
|
focus_previous(event)
|
|
329
331
|
|
|
330
|
-
#
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
@kb.add("c-m", filter=not_in_multiline) # Enter to submit only when not in multiline
|
|
332
|
+
# Enter always submits
|
|
333
|
+
@kb.add("c-m")
|
|
334
334
|
def submit(event):
|
|
335
335
|
self._accept()
|
|
336
336
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
337
|
+
# Ctrl+J inserts newlines
|
|
338
|
+
@kb.add("c-j")
|
|
339
|
+
def insert_newline(event):
|
|
340
|
+
# Insert a newline at the cursor position
|
|
341
|
+
event.current_buffer.insert_text("\n")
|
|
342
|
+
# Mark this field as multiline when user adds a newline
|
|
343
|
+
for field_name, widget in self.field_widgets.items():
|
|
344
|
+
if isinstance(widget, Buffer) and widget == event.current_buffer:
|
|
345
|
+
self.multiline_fields.add(field_name)
|
|
346
|
+
break
|
|
340
347
|
|
|
341
348
|
# ESC should ALWAYS cancel immediately, no matter what
|
|
342
349
|
@kb.add("escape", eager=True, is_global=True)
|
|
@@ -353,7 +360,7 @@ class ElicitationForm:
|
|
|
353
360
|
[
|
|
354
361
|
(
|
|
355
362
|
"class:bottom-toolbar.text",
|
|
356
|
-
" <TAB
|
|
363
|
+
" <TAB>/↑↓→← navigate. <ENTER> submit. <Ctrl+J> insert new line. <ESC> cancel. ",
|
|
357
364
|
),
|
|
358
365
|
(
|
|
359
366
|
"class:bottom-toolbar.text",
|
|
@@ -510,7 +517,8 @@ class ElicitationForm:
|
|
|
510
517
|
enum_names = field_def.get("enumNames", enum_values)
|
|
511
518
|
values = [(val, name) for val, name in zip(enum_values, enum_names)]
|
|
512
519
|
|
|
513
|
-
|
|
520
|
+
default_value = field_def.get("default")
|
|
521
|
+
radio_list = RadioList(values=values, default=default_value)
|
|
514
522
|
self.field_widgets[field_name] = radio_list
|
|
515
523
|
|
|
516
524
|
return HSplit([label, Frame(radio_list, height=min(len(values) + 2, 6))])
|
|
@@ -541,23 +549,35 @@ class ElicitationForm:
|
|
|
541
549
|
else:
|
|
542
550
|
constraints = {}
|
|
543
551
|
|
|
544
|
-
|
|
552
|
+
default_value = field_def.get("default")
|
|
553
|
+
|
|
554
|
+
# Determine if field should be multiline based on max_length or default value length
|
|
545
555
|
if field_type == "string":
|
|
546
556
|
max_length = constraints.get("maxLength")
|
|
557
|
+
# Check default value length if maxLength not specified
|
|
558
|
+
if not max_length and default_value is not None:
|
|
559
|
+
max_length = len(str(default_value))
|
|
547
560
|
else:
|
|
548
561
|
max_length = None
|
|
549
|
-
|
|
562
|
+
|
|
563
|
+
# Check if default value contains newlines
|
|
564
|
+
if field_type == "string" and default_value is not None and "\n" in str(default_value):
|
|
565
|
+
multiline = True
|
|
566
|
+
self.multiline_fields.add(field_name) # Track multiline fields
|
|
567
|
+
# Set height to actual line count for fields with newlines in default
|
|
568
|
+
initial_height = str(default_value).count("\n") + 1
|
|
569
|
+
elif max_length and max_length > 100:
|
|
550
570
|
# Use multiline for longer fields
|
|
551
571
|
multiline = True
|
|
552
572
|
self.multiline_fields.add(field_name) # Track multiline fields
|
|
553
573
|
if max_length <= 300:
|
|
554
|
-
|
|
574
|
+
initial_height = 3
|
|
555
575
|
else:
|
|
556
|
-
|
|
576
|
+
initial_height = 5
|
|
557
577
|
else:
|
|
558
578
|
# Single line for shorter fields
|
|
559
579
|
multiline = False
|
|
560
|
-
|
|
580
|
+
initial_height = 1
|
|
561
581
|
|
|
562
582
|
buffer = Buffer(
|
|
563
583
|
validator=validator,
|
|
@@ -566,6 +586,8 @@ class ElicitationForm:
|
|
|
566
586
|
complete_while_typing=False, # Disable completion for cleaner experience
|
|
567
587
|
enable_history_search=False, # Disable history for cleaner experience
|
|
568
588
|
)
|
|
589
|
+
if default_value is not None:
|
|
590
|
+
buffer.text = str(default_value)
|
|
569
591
|
self.field_widgets[field_name] = buffer
|
|
570
592
|
|
|
571
593
|
# Create dynamic style function for focus highlighting and validation errors
|
|
@@ -581,31 +603,24 @@ class ElicitationForm:
|
|
|
581
603
|
else:
|
|
582
604
|
return "class:input-field"
|
|
583
605
|
|
|
606
|
+
# Create a dynamic height function based on content
|
|
607
|
+
def get_dynamic_height():
|
|
608
|
+
if not buffer.text:
|
|
609
|
+
return initial_height
|
|
610
|
+
# Calculate height based on number of newlines in buffer
|
|
611
|
+
line_count = buffer.text.count("\n") + 1
|
|
612
|
+
# Use initial height as minimum, grow up to 20 lines
|
|
613
|
+
return min(max(line_count, initial_height), 20)
|
|
614
|
+
|
|
584
615
|
text_input = Window(
|
|
585
616
|
BufferControl(buffer=buffer),
|
|
586
|
-
height=height
|
|
617
|
+
height=get_dynamic_height, # Use dynamic height function
|
|
587
618
|
style=get_field_style, # Use dynamic style function
|
|
588
619
|
wrap_lines=True if multiline else False, # Enable word wrap for multiline
|
|
589
620
|
)
|
|
590
621
|
|
|
591
622
|
return HSplit([label, Frame(text_input)])
|
|
592
623
|
|
|
593
|
-
def _is_in_multiline_field(self) -> bool:
|
|
594
|
-
"""Check if currently focused field is a multiline field."""
|
|
595
|
-
|
|
596
|
-
focused = get_app().layout.current_control
|
|
597
|
-
|
|
598
|
-
# Find which field this control belongs to
|
|
599
|
-
# Only Buffer widgets can be multiline, so only check those
|
|
600
|
-
for field_name, widget in self.field_widgets.items():
|
|
601
|
-
if (
|
|
602
|
-
isinstance(widget, Buffer)
|
|
603
|
-
and hasattr(focused, "buffer")
|
|
604
|
-
and widget == focused.buffer
|
|
605
|
-
):
|
|
606
|
-
return field_name in self.multiline_fields
|
|
607
|
-
return False
|
|
608
|
-
|
|
609
624
|
def _validate_form(self) -> tuple[bool, Optional[str]]:
|
|
610
625
|
"""Validate the entire form."""
|
|
611
626
|
|
|
@@ -715,8 +730,10 @@ class ElicitationForm:
|
|
|
715
730
|
self.app.exit()
|
|
716
731
|
|
|
717
732
|
def _cancel_all(self):
|
|
718
|
-
"""Handle cancel all
|
|
719
|
-
|
|
733
|
+
"""Handle cancel all: signal disable; no side effects here.
|
|
734
|
+
|
|
735
|
+
UI emits an action; handler/orchestration is responsible for updating state.
|
|
736
|
+
"""
|
|
720
737
|
self.action = "disable"
|
|
721
738
|
self._clear_status_bar()
|
|
722
739
|
self.app.exit()
|
|
@@ -19,8 +19,9 @@ from prompt_toolkit.key_binding import KeyBindings
|
|
|
19
19
|
from prompt_toolkit.styles import Style
|
|
20
20
|
from rich import print as rich_print
|
|
21
21
|
|
|
22
|
-
from
|
|
23
|
-
from
|
|
22
|
+
from fast_agent.agents.agent_types import AgentType
|
|
23
|
+
from fast_agent.core.exceptions import PromptExitError
|
|
24
|
+
from fast_agent.llm.model_info import get_model_info
|
|
24
25
|
|
|
25
26
|
# Get the application version
|
|
26
27
|
try:
|
|
@@ -59,9 +60,12 @@ async def _display_agent_info_helper(agent_name: str, agent_provider: object) ->
|
|
|
59
60
|
# This is a single agent
|
|
60
61
|
agent = agent_provider
|
|
61
62
|
|
|
62
|
-
# Get counts
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
# Get counts TODO -- add this to the type library or adjust the way aggregator/reporting works
|
|
64
|
+
server_count = 0
|
|
65
|
+
if hasattr(agent, "_aggregator") and hasattr(agent._aggregator, "server_names"):
|
|
66
|
+
server_count = (
|
|
67
|
+
len(agent._aggregator.server_names) if agent._aggregator.server_names else 0
|
|
68
|
+
)
|
|
65
69
|
|
|
66
70
|
tools_result = await agent.list_tools()
|
|
67
71
|
tool_count = (
|
|
@@ -290,12 +294,13 @@ class AgentCompleter(Completer):
|
|
|
290
294
|
self.agents = agents
|
|
291
295
|
# Map commands to their descriptions for better completion hints
|
|
292
296
|
self.commands = {
|
|
293
|
-
"tools": "List
|
|
294
|
-
"prompt": "List and
|
|
297
|
+
"tools": "List available MCP tools",
|
|
298
|
+
"prompt": "List and choose MCP prompts, or apply specific prompt (/prompt <name>)",
|
|
295
299
|
"agents": "List available agents",
|
|
296
300
|
"usage": "Show current usage statistics",
|
|
297
301
|
"markdown": "Show last assistant message without markdown formatting",
|
|
298
|
-
"
|
|
302
|
+
"save_history": "Save history; .json = MCP JSON, others = Markdown",
|
|
303
|
+
"help": "Show commands and shortcuts",
|
|
299
304
|
"clear": "Clear the screen",
|
|
300
305
|
"STOP": "Stop this prompting session and move to next workflow step",
|
|
301
306
|
"EXIT": "Exit fast-agent, terminating any running workflows",
|
|
@@ -578,32 +583,91 @@ async def get_enhanced_input(
|
|
|
578
583
|
if in_multiline_mode:
|
|
579
584
|
mode_style = "ansired" # More noticeable for multiline mode
|
|
580
585
|
mode_text = "MULTILINE"
|
|
581
|
-
|
|
586
|
+
# toggle_text = "Normal"
|
|
582
587
|
else:
|
|
583
588
|
mode_style = "ansigreen"
|
|
584
589
|
mode_text = "NORMAL"
|
|
585
|
-
toggle_text = "Multiline"
|
|
590
|
+
# toggle_text = "Multiline"
|
|
586
591
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
("Ctrl+J", "Newline" if not in_multiline_mode else None),
|
|
590
|
-
("Ctrl+E", "External"),
|
|
591
|
-
("Ctrl+Y", "Copy"),
|
|
592
|
-
("Ctrl+L", "Clear"),
|
|
593
|
-
("↑/↓", "History"),
|
|
594
|
-
("EXIT", "Exit")
|
|
595
|
-
]
|
|
596
|
-
|
|
597
|
-
newline = "Ctrl+J:Submit" if in_multiline_mode else "<Enter>:Submit"
|
|
592
|
+
# No shortcut hints in the toolbar for now
|
|
593
|
+
shortcuts = []
|
|
598
594
|
|
|
599
595
|
# Only show relevant shortcuts based on mode
|
|
600
596
|
shortcuts = [(k, v) for k, v in shortcuts if v]
|
|
601
597
|
|
|
602
598
|
shortcut_text = " | ".join(f"{key}:{action}" for key, action in shortcuts)
|
|
603
599
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
600
|
+
# Resolve model name and TDV from the current agent if available
|
|
601
|
+
model_display = None
|
|
602
|
+
tdv_segment = None
|
|
603
|
+
try:
|
|
604
|
+
agent_obj = (
|
|
605
|
+
agent_provider._agent(agent_name)
|
|
606
|
+
if agent_provider and hasattr(agent_provider, "_agent")
|
|
607
|
+
else agent_provider
|
|
608
|
+
)
|
|
609
|
+
if agent_obj and hasattr(agent_obj, "llm") and agent_obj.llm:
|
|
610
|
+
model_name = getattr(agent_obj.llm, "model_name", None)
|
|
611
|
+
if model_name:
|
|
612
|
+
# Truncate model name to max 25 characters with ellipsis
|
|
613
|
+
max_len = 25
|
|
614
|
+
if len(model_name) > max_len:
|
|
615
|
+
# Keep total length at max_len including ellipsis
|
|
616
|
+
model_display = model_name[: max_len - 1] + "…"
|
|
617
|
+
else:
|
|
618
|
+
model_display = model_name
|
|
619
|
+
|
|
620
|
+
# Build TDV capability segment based on model database
|
|
621
|
+
info = get_model_info(agent_obj)
|
|
622
|
+
# Default to text-only if info resolution fails for any reason
|
|
623
|
+
t, d, v = (True, False, False)
|
|
624
|
+
if info:
|
|
625
|
+
t, d, v = info.tdv_flags
|
|
626
|
+
|
|
627
|
+
def _style_flag(letter: str, supported: bool) -> str:
|
|
628
|
+
# Enabled uses the same color as NORMAL mode (ansigreen), disabled is dim
|
|
629
|
+
enabled_color = "ansigreen"
|
|
630
|
+
return (
|
|
631
|
+
f"<style fg='{enabled_color}' bg='ansiblack'>{letter}</style>"
|
|
632
|
+
if supported
|
|
633
|
+
else f"<style fg='ansiblack' bg='ansiwhite'>{letter}</style>"
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
tdv_segment = f"{_style_flag('T', t)}{_style_flag('D', d)}{_style_flag('V', v)}"
|
|
637
|
+
except Exception:
|
|
638
|
+
# If anything goes wrong determining the model, omit it gracefully
|
|
639
|
+
model_display = None
|
|
640
|
+
tdv_segment = None
|
|
641
|
+
|
|
642
|
+
# Build dynamic middle segments: model (in green) and optional shortcuts
|
|
643
|
+
middle_segments = []
|
|
644
|
+
if model_display:
|
|
645
|
+
# Model chip + inline TDV flags
|
|
646
|
+
if tdv_segment:
|
|
647
|
+
middle_segments.append(
|
|
648
|
+
f"{tdv_segment} <style bg='ansigreen'>{model_display}</style>"
|
|
649
|
+
)
|
|
650
|
+
else:
|
|
651
|
+
middle_segments.append(f"<style bg='ansigreen'>{model_display}</style>")
|
|
652
|
+
if shortcut_text:
|
|
653
|
+
middle_segments.append(shortcut_text)
|
|
654
|
+
middle = " | ".join(middle_segments)
|
|
655
|
+
|
|
656
|
+
# Version/app label in green (dynamic version)
|
|
657
|
+
version_segment = f"fast-agent {app_version}"
|
|
658
|
+
|
|
659
|
+
if middle:
|
|
660
|
+
return HTML(
|
|
661
|
+
f" <style fg='{toolbar_color}' bg='ansiblack'> {agent_name} </style> "
|
|
662
|
+
f" {middle} | <style fg='{mode_style}' bg='ansiblack'> {mode_text} </style> | "
|
|
663
|
+
f"{version_segment}"
|
|
664
|
+
)
|
|
665
|
+
else:
|
|
666
|
+
return HTML(
|
|
667
|
+
f" <style fg='{toolbar_color}' bg='ansiblack'> {agent_name} </style> "
|
|
668
|
+
f"Mode: <style fg='{mode_style}' bg='ansiblack'> {mode_text} </style> | "
|
|
669
|
+
f"{version_segment}"
|
|
670
|
+
)
|
|
607
671
|
|
|
608
672
|
# A more terminal-agnostic style that should work across themes
|
|
609
673
|
custom_style = Style.from_dict(
|
|
@@ -676,7 +740,7 @@ async def get_enhanced_input(
|
|
|
676
740
|
def pre_process_input(text):
|
|
677
741
|
# Command processing
|
|
678
742
|
if text and text.startswith("/"):
|
|
679
|
-
if text == "/":
|
|
743
|
+
if text == "/":
|
|
680
744
|
return ""
|
|
681
745
|
cmd_parts = text[1:].strip().split(maxsplit=1)
|
|
682
746
|
cmd = cmd_parts[0].lower()
|
|
@@ -691,6 +755,11 @@ async def get_enhanced_input(
|
|
|
691
755
|
return "SHOW_USAGE"
|
|
692
756
|
elif cmd == "markdown":
|
|
693
757
|
return "MARKDOWN"
|
|
758
|
+
elif cmd in ("save_history", "save"):
|
|
759
|
+
# Return a structured action for the interactive loop to handle
|
|
760
|
+
# Prefer programmatic saving via HistoryExporter; fall back to magic-string there if needed
|
|
761
|
+
filename = cmd_parts[1].strip() if len(cmd_parts) > 1 and cmd_parts[1].strip() else None
|
|
762
|
+
return {"save_history": True, "filename": filename}
|
|
694
763
|
elif cmd == "prompt":
|
|
695
764
|
# Handle /prompt with no arguments as interactive mode
|
|
696
765
|
if len(cmd_parts) > 1:
|
|
@@ -867,6 +936,8 @@ async def handle_special_commands(command, agent_app=None):
|
|
|
867
936
|
rich_print(" /prompt <name> - Apply a specific prompt by name")
|
|
868
937
|
rich_print(" /usage - Show current usage statistics")
|
|
869
938
|
rich_print(" /markdown - Show last assistant message without markdown formatting")
|
|
939
|
+
rich_print(" /save_history <filename> - Save current chat history to a file")
|
|
940
|
+
rich_print(" [dim]Tip: Use a .json extension for MCP-compatible JSON; any other extension saves Markdown.[/dim]")
|
|
870
941
|
rich_print(" @agent_name - Switch to agent")
|
|
871
942
|
rich_print(" STOP - Return control back to the workflow")
|
|
872
943
|
rich_print(" EXIT - Exit fast-agent, terminating any running workflows")
|