fast-agent-mcp 0.4.7__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.
Files changed (261) hide show
  1. fast_agent/__init__.py +183 -0
  2. fast_agent/acp/__init__.py +19 -0
  3. fast_agent/acp/acp_aware_mixin.py +304 -0
  4. fast_agent/acp/acp_context.py +437 -0
  5. fast_agent/acp/content_conversion.py +136 -0
  6. fast_agent/acp/filesystem_runtime.py +427 -0
  7. fast_agent/acp/permission_store.py +269 -0
  8. fast_agent/acp/server/__init__.py +5 -0
  9. fast_agent/acp/server/agent_acp_server.py +1472 -0
  10. fast_agent/acp/slash_commands.py +1050 -0
  11. fast_agent/acp/terminal_runtime.py +408 -0
  12. fast_agent/acp/tool_permission_adapter.py +125 -0
  13. fast_agent/acp/tool_permissions.py +474 -0
  14. fast_agent/acp/tool_progress.py +814 -0
  15. fast_agent/agents/__init__.py +85 -0
  16. fast_agent/agents/agent_types.py +64 -0
  17. fast_agent/agents/llm_agent.py +350 -0
  18. fast_agent/agents/llm_decorator.py +1139 -0
  19. fast_agent/agents/mcp_agent.py +1337 -0
  20. fast_agent/agents/tool_agent.py +271 -0
  21. fast_agent/agents/workflow/agents_as_tools_agent.py +849 -0
  22. fast_agent/agents/workflow/chain_agent.py +212 -0
  23. fast_agent/agents/workflow/evaluator_optimizer.py +380 -0
  24. fast_agent/agents/workflow/iterative_planner.py +652 -0
  25. fast_agent/agents/workflow/maker_agent.py +379 -0
  26. fast_agent/agents/workflow/orchestrator_models.py +218 -0
  27. fast_agent/agents/workflow/orchestrator_prompts.py +248 -0
  28. fast_agent/agents/workflow/parallel_agent.py +250 -0
  29. fast_agent/agents/workflow/router_agent.py +353 -0
  30. fast_agent/cli/__init__.py +0 -0
  31. fast_agent/cli/__main__.py +73 -0
  32. fast_agent/cli/commands/acp.py +159 -0
  33. fast_agent/cli/commands/auth.py +404 -0
  34. fast_agent/cli/commands/check_config.py +783 -0
  35. fast_agent/cli/commands/go.py +514 -0
  36. fast_agent/cli/commands/quickstart.py +557 -0
  37. fast_agent/cli/commands/serve.py +143 -0
  38. fast_agent/cli/commands/server_helpers.py +114 -0
  39. fast_agent/cli/commands/setup.py +174 -0
  40. fast_agent/cli/commands/url_parser.py +190 -0
  41. fast_agent/cli/constants.py +40 -0
  42. fast_agent/cli/main.py +115 -0
  43. fast_agent/cli/terminal.py +24 -0
  44. fast_agent/config.py +798 -0
  45. fast_agent/constants.py +41 -0
  46. fast_agent/context.py +279 -0
  47. fast_agent/context_dependent.py +50 -0
  48. fast_agent/core/__init__.py +92 -0
  49. fast_agent/core/agent_app.py +448 -0
  50. fast_agent/core/core_app.py +137 -0
  51. fast_agent/core/direct_decorators.py +784 -0
  52. fast_agent/core/direct_factory.py +620 -0
  53. fast_agent/core/error_handling.py +27 -0
  54. fast_agent/core/exceptions.py +90 -0
  55. fast_agent/core/executor/__init__.py +0 -0
  56. fast_agent/core/executor/executor.py +280 -0
  57. fast_agent/core/executor/task_registry.py +32 -0
  58. fast_agent/core/executor/workflow_signal.py +324 -0
  59. fast_agent/core/fastagent.py +1186 -0
  60. fast_agent/core/logging/__init__.py +5 -0
  61. fast_agent/core/logging/events.py +138 -0
  62. fast_agent/core/logging/json_serializer.py +164 -0
  63. fast_agent/core/logging/listeners.py +309 -0
  64. fast_agent/core/logging/logger.py +278 -0
  65. fast_agent/core/logging/transport.py +481 -0
  66. fast_agent/core/prompt.py +9 -0
  67. fast_agent/core/prompt_templates.py +183 -0
  68. fast_agent/core/validation.py +326 -0
  69. fast_agent/event_progress.py +62 -0
  70. fast_agent/history/history_exporter.py +49 -0
  71. fast_agent/human_input/__init__.py +47 -0
  72. fast_agent/human_input/elicitation_handler.py +123 -0
  73. fast_agent/human_input/elicitation_state.py +33 -0
  74. fast_agent/human_input/form_elements.py +59 -0
  75. fast_agent/human_input/form_fields.py +256 -0
  76. fast_agent/human_input/simple_form.py +113 -0
  77. fast_agent/human_input/types.py +40 -0
  78. fast_agent/interfaces.py +310 -0
  79. fast_agent/llm/__init__.py +9 -0
  80. fast_agent/llm/cancellation.py +22 -0
  81. fast_agent/llm/fastagent_llm.py +931 -0
  82. fast_agent/llm/internal/passthrough.py +161 -0
  83. fast_agent/llm/internal/playback.py +129 -0
  84. fast_agent/llm/internal/silent.py +41 -0
  85. fast_agent/llm/internal/slow.py +38 -0
  86. fast_agent/llm/memory.py +275 -0
  87. fast_agent/llm/model_database.py +490 -0
  88. fast_agent/llm/model_factory.py +388 -0
  89. fast_agent/llm/model_info.py +102 -0
  90. fast_agent/llm/prompt_utils.py +155 -0
  91. fast_agent/llm/provider/anthropic/anthropic_utils.py +84 -0
  92. fast_agent/llm/provider/anthropic/cache_planner.py +56 -0
  93. fast_agent/llm/provider/anthropic/llm_anthropic.py +796 -0
  94. fast_agent/llm/provider/anthropic/multipart_converter_anthropic.py +462 -0
  95. fast_agent/llm/provider/bedrock/bedrock_utils.py +218 -0
  96. fast_agent/llm/provider/bedrock/llm_bedrock.py +2207 -0
  97. fast_agent/llm/provider/bedrock/multipart_converter_bedrock.py +84 -0
  98. fast_agent/llm/provider/google/google_converter.py +466 -0
  99. fast_agent/llm/provider/google/llm_google_native.py +681 -0
  100. fast_agent/llm/provider/openai/llm_aliyun.py +31 -0
  101. fast_agent/llm/provider/openai/llm_azure.py +143 -0
  102. fast_agent/llm/provider/openai/llm_deepseek.py +76 -0
  103. fast_agent/llm/provider/openai/llm_generic.py +35 -0
  104. fast_agent/llm/provider/openai/llm_google_oai.py +32 -0
  105. fast_agent/llm/provider/openai/llm_groq.py +42 -0
  106. fast_agent/llm/provider/openai/llm_huggingface.py +85 -0
  107. fast_agent/llm/provider/openai/llm_openai.py +1195 -0
  108. fast_agent/llm/provider/openai/llm_openai_compatible.py +138 -0
  109. fast_agent/llm/provider/openai/llm_openrouter.py +45 -0
  110. fast_agent/llm/provider/openai/llm_tensorzero_openai.py +128 -0
  111. fast_agent/llm/provider/openai/llm_xai.py +38 -0
  112. fast_agent/llm/provider/openai/multipart_converter_openai.py +561 -0
  113. fast_agent/llm/provider/openai/openai_multipart.py +169 -0
  114. fast_agent/llm/provider/openai/openai_utils.py +67 -0
  115. fast_agent/llm/provider/openai/responses.py +133 -0
  116. fast_agent/llm/provider_key_manager.py +139 -0
  117. fast_agent/llm/provider_types.py +34 -0
  118. fast_agent/llm/request_params.py +61 -0
  119. fast_agent/llm/sampling_converter.py +98 -0
  120. fast_agent/llm/stream_types.py +9 -0
  121. fast_agent/llm/usage_tracking.py +445 -0
  122. fast_agent/mcp/__init__.py +56 -0
  123. fast_agent/mcp/common.py +26 -0
  124. fast_agent/mcp/elicitation_factory.py +84 -0
  125. fast_agent/mcp/elicitation_handlers.py +164 -0
  126. fast_agent/mcp/gen_client.py +83 -0
  127. fast_agent/mcp/helpers/__init__.py +36 -0
  128. fast_agent/mcp/helpers/content_helpers.py +352 -0
  129. fast_agent/mcp/helpers/server_config_helpers.py +25 -0
  130. fast_agent/mcp/hf_auth.py +147 -0
  131. fast_agent/mcp/interfaces.py +92 -0
  132. fast_agent/mcp/logger_textio.py +108 -0
  133. fast_agent/mcp/mcp_agent_client_session.py +411 -0
  134. fast_agent/mcp/mcp_aggregator.py +2175 -0
  135. fast_agent/mcp/mcp_connection_manager.py +723 -0
  136. fast_agent/mcp/mcp_content.py +262 -0
  137. fast_agent/mcp/mime_utils.py +108 -0
  138. fast_agent/mcp/oauth_client.py +509 -0
  139. fast_agent/mcp/prompt.py +159 -0
  140. fast_agent/mcp/prompt_message_extended.py +155 -0
  141. fast_agent/mcp/prompt_render.py +84 -0
  142. fast_agent/mcp/prompt_serialization.py +580 -0
  143. fast_agent/mcp/prompts/__init__.py +0 -0
  144. fast_agent/mcp/prompts/__main__.py +7 -0
  145. fast_agent/mcp/prompts/prompt_constants.py +18 -0
  146. fast_agent/mcp/prompts/prompt_helpers.py +238 -0
  147. fast_agent/mcp/prompts/prompt_load.py +186 -0
  148. fast_agent/mcp/prompts/prompt_server.py +552 -0
  149. fast_agent/mcp/prompts/prompt_template.py +438 -0
  150. fast_agent/mcp/resource_utils.py +215 -0
  151. fast_agent/mcp/sampling.py +200 -0
  152. fast_agent/mcp/server/__init__.py +4 -0
  153. fast_agent/mcp/server/agent_server.py +613 -0
  154. fast_agent/mcp/skybridge.py +44 -0
  155. fast_agent/mcp/sse_tracking.py +287 -0
  156. fast_agent/mcp/stdio_tracking_simple.py +59 -0
  157. fast_agent/mcp/streamable_http_tracking.py +309 -0
  158. fast_agent/mcp/tool_execution_handler.py +137 -0
  159. fast_agent/mcp/tool_permission_handler.py +88 -0
  160. fast_agent/mcp/transport_tracking.py +634 -0
  161. fast_agent/mcp/types.py +24 -0
  162. fast_agent/mcp/ui_agent.py +48 -0
  163. fast_agent/mcp/ui_mixin.py +209 -0
  164. fast_agent/mcp_server_registry.py +89 -0
  165. fast_agent/py.typed +0 -0
  166. fast_agent/resources/examples/data-analysis/analysis-campaign.py +189 -0
  167. fast_agent/resources/examples/data-analysis/analysis.py +68 -0
  168. fast_agent/resources/examples/data-analysis/fastagent.config.yaml +41 -0
  169. fast_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +1471 -0
  170. fast_agent/resources/examples/mcp/elicitations/elicitation_account_server.py +88 -0
  171. fast_agent/resources/examples/mcp/elicitations/elicitation_forms_server.py +297 -0
  172. fast_agent/resources/examples/mcp/elicitations/elicitation_game_server.py +164 -0
  173. fast_agent/resources/examples/mcp/elicitations/fastagent.config.yaml +35 -0
  174. fast_agent/resources/examples/mcp/elicitations/fastagent.secrets.yaml.example +17 -0
  175. fast_agent/resources/examples/mcp/elicitations/forms_demo.py +107 -0
  176. fast_agent/resources/examples/mcp/elicitations/game_character.py +65 -0
  177. fast_agent/resources/examples/mcp/elicitations/game_character_handler.py +256 -0
  178. fast_agent/resources/examples/mcp/elicitations/tool_call.py +21 -0
  179. fast_agent/resources/examples/mcp/state-transfer/agent_one.py +18 -0
  180. fast_agent/resources/examples/mcp/state-transfer/agent_two.py +18 -0
  181. fast_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml +27 -0
  182. fast_agent/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example +15 -0
  183. fast_agent/resources/examples/researcher/fastagent.config.yaml +61 -0
  184. fast_agent/resources/examples/researcher/researcher-eval.py +53 -0
  185. fast_agent/resources/examples/researcher/researcher-imp.py +189 -0
  186. fast_agent/resources/examples/researcher/researcher.py +36 -0
  187. fast_agent/resources/examples/tensorzero/.env.sample +2 -0
  188. fast_agent/resources/examples/tensorzero/Makefile +31 -0
  189. fast_agent/resources/examples/tensorzero/README.md +56 -0
  190. fast_agent/resources/examples/tensorzero/agent.py +35 -0
  191. fast_agent/resources/examples/tensorzero/demo_images/clam.jpg +0 -0
  192. fast_agent/resources/examples/tensorzero/demo_images/crab.png +0 -0
  193. fast_agent/resources/examples/tensorzero/demo_images/shrimp.png +0 -0
  194. fast_agent/resources/examples/tensorzero/docker-compose.yml +105 -0
  195. fast_agent/resources/examples/tensorzero/fastagent.config.yaml +19 -0
  196. fast_agent/resources/examples/tensorzero/image_demo.py +67 -0
  197. fast_agent/resources/examples/tensorzero/mcp_server/Dockerfile +25 -0
  198. fast_agent/resources/examples/tensorzero/mcp_server/entrypoint.sh +35 -0
  199. fast_agent/resources/examples/tensorzero/mcp_server/mcp_server.py +31 -0
  200. fast_agent/resources/examples/tensorzero/mcp_server/pyproject.toml +11 -0
  201. fast_agent/resources/examples/tensorzero/simple_agent.py +25 -0
  202. fast_agent/resources/examples/tensorzero/tensorzero_config/system_schema.json +29 -0
  203. fast_agent/resources/examples/tensorzero/tensorzero_config/system_template.minijinja +11 -0
  204. fast_agent/resources/examples/tensorzero/tensorzero_config/tensorzero.toml +35 -0
  205. fast_agent/resources/examples/workflows/agents_as_tools_extended.py +73 -0
  206. fast_agent/resources/examples/workflows/agents_as_tools_simple.py +50 -0
  207. fast_agent/resources/examples/workflows/chaining.py +37 -0
  208. fast_agent/resources/examples/workflows/evaluator.py +77 -0
  209. fast_agent/resources/examples/workflows/fastagent.config.yaml +26 -0
  210. fast_agent/resources/examples/workflows/graded_report.md +89 -0
  211. fast_agent/resources/examples/workflows/human_input.py +28 -0
  212. fast_agent/resources/examples/workflows/maker.py +156 -0
  213. fast_agent/resources/examples/workflows/orchestrator.py +70 -0
  214. fast_agent/resources/examples/workflows/parallel.py +56 -0
  215. fast_agent/resources/examples/workflows/router.py +69 -0
  216. fast_agent/resources/examples/workflows/short_story.md +13 -0
  217. fast_agent/resources/examples/workflows/short_story.txt +19 -0
  218. fast_agent/resources/setup/.gitignore +30 -0
  219. fast_agent/resources/setup/agent.py +28 -0
  220. fast_agent/resources/setup/fastagent.config.yaml +65 -0
  221. fast_agent/resources/setup/fastagent.secrets.yaml.example +38 -0
  222. fast_agent/resources/setup/pyproject.toml.tmpl +23 -0
  223. fast_agent/skills/__init__.py +9 -0
  224. fast_agent/skills/registry.py +235 -0
  225. fast_agent/tools/elicitation.py +369 -0
  226. fast_agent/tools/shell_runtime.py +402 -0
  227. fast_agent/types/__init__.py +59 -0
  228. fast_agent/types/conversation_summary.py +294 -0
  229. fast_agent/types/llm_stop_reason.py +78 -0
  230. fast_agent/types/message_search.py +249 -0
  231. fast_agent/ui/__init__.py +38 -0
  232. fast_agent/ui/console.py +59 -0
  233. fast_agent/ui/console_display.py +1080 -0
  234. fast_agent/ui/elicitation_form.py +946 -0
  235. fast_agent/ui/elicitation_style.py +59 -0
  236. fast_agent/ui/enhanced_prompt.py +1400 -0
  237. fast_agent/ui/history_display.py +734 -0
  238. fast_agent/ui/interactive_prompt.py +1199 -0
  239. fast_agent/ui/markdown_helpers.py +104 -0
  240. fast_agent/ui/markdown_truncator.py +1004 -0
  241. fast_agent/ui/mcp_display.py +857 -0
  242. fast_agent/ui/mcp_ui_utils.py +235 -0
  243. fast_agent/ui/mermaid_utils.py +169 -0
  244. fast_agent/ui/message_primitives.py +50 -0
  245. fast_agent/ui/notification_tracker.py +205 -0
  246. fast_agent/ui/plain_text_truncator.py +68 -0
  247. fast_agent/ui/progress_display.py +10 -0
  248. fast_agent/ui/rich_progress.py +195 -0
  249. fast_agent/ui/streaming.py +774 -0
  250. fast_agent/ui/streaming_buffer.py +449 -0
  251. fast_agent/ui/tool_display.py +422 -0
  252. fast_agent/ui/usage_display.py +204 -0
  253. fast_agent/utils/__init__.py +5 -0
  254. fast_agent/utils/reasoning_stream_parser.py +77 -0
  255. fast_agent/utils/time.py +22 -0
  256. fast_agent/workflow_telemetry.py +261 -0
  257. fast_agent_mcp-0.4.7.dist-info/METADATA +788 -0
  258. fast_agent_mcp-0.4.7.dist-info/RECORD +261 -0
  259. fast_agent_mcp-0.4.7.dist-info/WHEEL +4 -0
  260. fast_agent_mcp-0.4.7.dist-info/entry_points.txt +7 -0
  261. fast_agent_mcp-0.4.7.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,59 @@
1
+ """Custom form elements for elicitation forms."""
2
+
3
+ from typing import Optional, Sequence, TypeVar
4
+
5
+ from prompt_toolkit.formatted_text import AnyFormattedText
6
+ from prompt_toolkit.validation import ValidationError
7
+ from prompt_toolkit.widgets import CheckboxList
8
+
9
+ _T = TypeVar("_T")
10
+
11
+
12
+ class ValidatedCheckboxList(CheckboxList[_T]):
13
+ """CheckboxList with min/max items validation."""
14
+
15
+ def __init__(
16
+ self,
17
+ values: Sequence[tuple[_T, AnyFormattedText]],
18
+ default_values: Optional[Sequence[_T]] = None,
19
+ min_items: Optional[int] = None,
20
+ max_items: Optional[int] = None,
21
+ ):
22
+ """
23
+ Initialize checkbox list with validation.
24
+
25
+ Args:
26
+ values: List of (value, label) tuples
27
+ default_values: Initially selected values
28
+ min_items: Minimum number of items that must be selected
29
+ max_items: Maximum number of items that can be selected
30
+ """
31
+ super().__init__(values, default_values=default_values)
32
+ self.min_items = min_items
33
+ self.max_items = max_items
34
+
35
+ @property
36
+ def validation_error(self) -> Optional[ValidationError]:
37
+ """
38
+ Check if current selection is valid.
39
+
40
+ Returns:
41
+ ValidationError if invalid, None if valid
42
+ """
43
+ selected_count = len(self.current_values)
44
+
45
+ if self.min_items is not None and selected_count < self.min_items:
46
+ if self.min_items == 1:
47
+ message = "At least 1 selection required"
48
+ else:
49
+ message = f"At least {self.min_items} selections required"
50
+ return ValidationError(message=message)
51
+
52
+ if self.max_items is not None and selected_count > self.max_items:
53
+ if self.max_items == 1:
54
+ message = "Only 1 selection allowed"
55
+ else:
56
+ message = f"Maximum {self.max_items} selections allowed"
57
+ return ValidationError(message=message)
58
+
59
+ return None
@@ -0,0 +1,256 @@
1
+ """High-level field types for elicitation forms with default support."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Union
5
+
6
+
7
+ @dataclass
8
+ class StringField:
9
+ """String field with validation and default support."""
10
+
11
+ title: str | None = None
12
+ description: str | None = None
13
+ default: str | None = None
14
+ min_length: int | None = None
15
+ max_length: int | None = None
16
+ pattern: str | None = None
17
+ format: str | None = None # email, uri, date, date-time
18
+
19
+ def to_schema(self) -> dict[str, Any]:
20
+ """Convert to MCP elicitation schema format."""
21
+ schema: dict[str, Any] = {"type": "string"}
22
+
23
+ if self.title:
24
+ schema["title"] = self.title
25
+ if self.description:
26
+ schema["description"] = self.description
27
+ if self.default is not None:
28
+ schema["default"] = self.default
29
+ if self.min_length is not None:
30
+ schema["minLength"] = self.min_length
31
+ if self.max_length is not None:
32
+ schema["maxLength"] = self.max_length
33
+ if self.pattern is not None:
34
+ schema["pattern"] = self.pattern
35
+ if self.format:
36
+ schema["format"] = self.format
37
+
38
+ return schema
39
+
40
+
41
+ @dataclass
42
+ class IntegerField:
43
+ """Integer field with validation and default support."""
44
+
45
+ title: str | None = None
46
+ description: str | None = None
47
+ default: int | None = None
48
+ minimum: int | None = None
49
+ maximum: int | None = None
50
+
51
+ def to_schema(self) -> dict[str, Any]:
52
+ """Convert to MCP elicitation schema format."""
53
+ schema: dict[str, Any] = {"type": "integer"}
54
+
55
+ if self.title:
56
+ schema["title"] = self.title
57
+ if self.description:
58
+ schema["description"] = self.description
59
+ if self.default is not None:
60
+ schema["default"] = self.default
61
+ if self.minimum is not None:
62
+ schema["minimum"] = self.minimum
63
+ if self.maximum is not None:
64
+ schema["maximum"] = self.maximum
65
+
66
+ return schema
67
+
68
+
69
+ @dataclass
70
+ class NumberField:
71
+ """Number (float) field with validation and default support."""
72
+
73
+ title: str | None = None
74
+ description: str | None = None
75
+ default: float | None = None
76
+ minimum: float | None = None
77
+ maximum: float | None = None
78
+
79
+ def to_schema(self) -> dict[str, Any]:
80
+ """Convert to MCP elicitation schema format."""
81
+ schema: dict[str, Any] = {"type": "number"}
82
+
83
+ if self.title:
84
+ schema["title"] = self.title
85
+ if self.description:
86
+ schema["description"] = self.description
87
+ if self.default is not None:
88
+ schema["default"] = self.default
89
+ if self.minimum is not None:
90
+ schema["minimum"] = self.minimum
91
+ if self.maximum is not None:
92
+ schema["maximum"] = self.maximum
93
+
94
+ return schema
95
+
96
+
97
+ @dataclass
98
+ class BooleanField:
99
+ """Boolean field with default support."""
100
+
101
+ title: str | None = None
102
+ description: str | None = None
103
+ default: bool | None = None
104
+
105
+ def to_schema(self) -> dict[str, Any]:
106
+ """Convert to MCP elicitation schema format."""
107
+ schema: dict[str, Any] = {"type": "boolean"}
108
+
109
+ if self.title:
110
+ schema["title"] = self.title
111
+ if self.description:
112
+ schema["description"] = self.description
113
+ if self.default is not None:
114
+ schema["default"] = self.default
115
+
116
+ return schema
117
+
118
+
119
+ @dataclass
120
+ class EnumField:
121
+ """Enum/choice field with default support."""
122
+
123
+ choices: list[str]
124
+ choice_names: list[str] | None = None # Human-readable names
125
+ title: str | None = None
126
+ description: str | None = None
127
+ default: str | None = None
128
+
129
+ def to_schema(self) -> dict[str, Any]:
130
+ """Convert to MCP elicitation schema format."""
131
+ schema: dict[str, Any] = {"type": "string", "enum": self.choices}
132
+
133
+ if self.title:
134
+ schema["title"] = self.title
135
+ if self.description:
136
+ schema["description"] = self.description
137
+ if self.default is not None:
138
+ schema["default"] = self.default
139
+ if self.choice_names:
140
+ schema["enumNames"] = self.choice_names
141
+
142
+ return schema
143
+
144
+
145
+ # Field type union
146
+ FieldType = Union[StringField, IntegerField, NumberField, BooleanField, EnumField]
147
+
148
+
149
+ class FormSchema:
150
+ """High-level form schema builder."""
151
+
152
+ def __init__(self, **fields: FieldType):
153
+ """Create a form schema with named fields."""
154
+ self.fields = fields
155
+ self._required_fields: list[str] = []
156
+
157
+ def required(self, *field_names: str) -> "FormSchema":
158
+ """Mark fields as required."""
159
+ self._required_fields.extend(field_names)
160
+ return self
161
+
162
+ def to_schema(self) -> dict[str, Any]:
163
+ """Convert to MCP ElicitRequestedSchema format."""
164
+ properties = {}
165
+
166
+ for field_name, field in self.fields.items():
167
+ properties[field_name] = field.to_schema()
168
+
169
+ schema: dict[str, Any] = {"type": "object", "properties": properties}
170
+
171
+ if self._required_fields:
172
+ schema["required"] = self._required_fields
173
+
174
+ return schema
175
+
176
+
177
+ # Convenience functions for creating fields
178
+ def string(
179
+ title: str | None = None,
180
+ description: str | None = None,
181
+ default: str | None = None,
182
+ min_length: int | None = None,
183
+ max_length: int | None = None,
184
+ pattern: str | None = None,
185
+ format: str | None = None,
186
+ ) -> StringField:
187
+ """Create a string field."""
188
+ return StringField(title, description, default, min_length, max_length, pattern, format)
189
+
190
+
191
+ def email(
192
+ title: str | None = None, description: str | None = None, default: str | None = None
193
+ ) -> StringField:
194
+ """Create an email field."""
195
+ return StringField(title, description, default, format="email")
196
+
197
+
198
+ def url(
199
+ title: str | None = None, description: str | None = None, default: str | None = None
200
+ ) -> StringField:
201
+ """Create a URL field."""
202
+ return StringField(title, description, default, format="uri")
203
+
204
+
205
+ def date(
206
+ title: str | None = None, description: str | None = None, default: str | None = None
207
+ ) -> StringField:
208
+ """Create a date field."""
209
+ return StringField(title, description, default, format="date")
210
+
211
+
212
+ def datetime(
213
+ title: str | None = None, description: str | None = None, default: str | None = None
214
+ ) -> StringField:
215
+ """Create a datetime field."""
216
+ return StringField(title, description, default, format="date-time")
217
+
218
+
219
+ def integer(
220
+ title: str | None = None,
221
+ description: str | None = None,
222
+ default: int | None = None,
223
+ minimum: int | None = None,
224
+ maximum: int | None = None,
225
+ ) -> IntegerField:
226
+ """Create an integer field."""
227
+ return IntegerField(title, description, default, minimum, maximum)
228
+
229
+
230
+ def number(
231
+ title: str | None = None,
232
+ description: str | None = None,
233
+ default: float | None = None,
234
+ minimum: float | None = None,
235
+ maximum: float | None = None,
236
+ ) -> NumberField:
237
+ """Create a number field."""
238
+ return NumberField(title, description, default, minimum, maximum)
239
+
240
+
241
+ def boolean(
242
+ title: str | None = None, description: str | None = None, default: bool | None = None
243
+ ) -> BooleanField:
244
+ """Create a boolean field."""
245
+ return BooleanField(title, description, default)
246
+
247
+
248
+ def choice(
249
+ choices: list[str],
250
+ choice_names: list[str] | None = None,
251
+ title: str | None = None,
252
+ description: str | None = None,
253
+ default: str | None = None,
254
+ ) -> EnumField:
255
+ """Create a choice/enum field."""
256
+ return EnumField(choices, choice_names, title, description, default)
@@ -0,0 +1,113 @@
1
+ """Simple form API for elicitation schemas without MCP wrappers."""
2
+
3
+ import asyncio
4
+ from typing import Any, Union
5
+
6
+ from mcp.types import ElicitRequestedSchema
7
+
8
+ from fast_agent.human_input.form_fields import FormSchema
9
+
10
+
11
+ async def form(
12
+ schema: Union[FormSchema, ElicitRequestedSchema, dict[str, Any]],
13
+ message: str = "Please fill out the form",
14
+ title: str = "Form Input",
15
+ ) -> dict[str, Any] | None:
16
+ """
17
+ Simple form API that presents an elicitation form and returns results.
18
+
19
+ Args:
20
+ schema: FormSchema, ElicitRequestedSchema, or dict schema
21
+ message: Message to display to the user
22
+ title: Title for the form (used as agent_name)
23
+
24
+ Returns:
25
+ Dict with form data if accepted, None if cancelled/declined
26
+
27
+ Example:
28
+ from fast_agent.human_input.form_fields import FormSchema, string, email, integer
29
+
30
+ schema = FormSchema(
31
+ name=string("Name", "Your full name", min_length=2),
32
+ email=email("Email", "Your email address"),
33
+ age=integer("Age", "Your age", minimum=0, maximum=120)
34
+ ).required("name", "email")
35
+
36
+ result = await form(schema, "Please enter your information")
37
+ if result:
38
+ print(f"Name: {result['name']}, Email: {result['email']}")
39
+ """
40
+ # Convert schema to ElicitRequestedSchema format
41
+ if isinstance(schema, FormSchema):
42
+ elicit_schema = schema.to_schema()
43
+ elif isinstance(schema, dict):
44
+ elicit_schema = schema
45
+ else:
46
+ elicit_schema = schema
47
+
48
+ # Import here to avoid import-time cycles via package __init__
49
+ from fast_agent.ui.elicitation_form import show_simple_elicitation_form
50
+
51
+ # Show the form
52
+ action, result = await show_simple_elicitation_form(
53
+ schema=elicit_schema, message=message, agent_name=title, server_name="SimpleForm"
54
+ )
55
+
56
+ # Return results based on action
57
+ if action == "accept":
58
+ return result
59
+ else:
60
+ return None
61
+
62
+
63
+ def form_sync(
64
+ schema: Union[FormSchema, ElicitRequestedSchema, dict[str, Any]],
65
+ message: str = "Please fill out the form",
66
+ title: str = "Form Input",
67
+ ) -> dict[str, Any] | None:
68
+ """
69
+ Synchronous wrapper for the form function.
70
+
71
+ Args:
72
+ schema: FormSchema, ElicitRequestedSchema, or dict schema
73
+ message: Message to display to the user
74
+ title: Title for the form (used as agent_name)
75
+
76
+ Returns:
77
+ Dict with form data if accepted, None if cancelled/declined
78
+ """
79
+ return asyncio.run(form(schema, message, title))
80
+
81
+
82
+ # Convenience function with a shorter name
83
+ async def ask(
84
+ schema: Union[FormSchema, ElicitRequestedSchema, dict[str, Any]],
85
+ message: str = "Please provide the requested information",
86
+ ) -> dict[str, Any] | None:
87
+ """
88
+ Short alias for form() function.
89
+
90
+ Example:
91
+ from fast_agent.human_input.form_fields import FormSchema, string, email
92
+
93
+ schema = FormSchema(
94
+ name=string("Name", "Your name"),
95
+ email=email("Email", "Your email")
96
+ ).required("name")
97
+
98
+ result = await ask(schema, "What's your info?")
99
+ """
100
+ return await form(schema, message)
101
+
102
+
103
+ def ask_sync(
104
+ schema: Union[FormSchema, ElicitRequestedSchema, dict[str, Any]],
105
+ message: str = "Please provide the requested information",
106
+ ) -> dict[str, Any] | None:
107
+ """
108
+ Synchronous version of ask().
109
+
110
+ Example:
111
+ result = ask_sync(schema, "What's your info?")
112
+ """
113
+ return form_sync(schema, message)
@@ -0,0 +1,40 @@
1
+ from typing import Any
2
+
3
+ from pydantic import BaseModel
4
+
5
+ HUMAN_INPUT_SIGNAL_NAME = "__human_input__"
6
+
7
+
8
+ class HumanInputRequest(BaseModel):
9
+ """Represents a request for human input."""
10
+
11
+ prompt: str
12
+ """The prompt to show to the user"""
13
+
14
+ description: str | None = None
15
+ """Optional description of what the input is for"""
16
+
17
+ request_id: str | None = None
18
+ """Unique identifier for this request"""
19
+
20
+ workflow_id: str | None = None
21
+ """Optional workflow ID if using workflow engine"""
22
+
23
+ timeout_seconds: int | None = None
24
+ """Optional timeout in seconds"""
25
+
26
+ metadata: dict | None = None
27
+ """Additional request payload"""
28
+
29
+
30
+ class HumanInputResponse(BaseModel):
31
+ """Represents a response to a human input request"""
32
+
33
+ request_id: str
34
+ """ID of the original request"""
35
+
36
+ response: str
37
+ """The input provided by the human"""
38
+
39
+ metadata: dict[str, Any] | None = None
40
+ """Additional response payload"""