fast-agent-mcp 0.3.2__py3-none-any.whl → 0.3.4__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 +6 -3
- fast_agent/agents/__init__.py +63 -14
- fast_agent/agents/llm_decorator.py +2 -1
- fast_agent/agents/tool_agent.py +4 -1
- fast_agent/agents/workflow/orchestrator_models.py +1 -1
- fast_agent/cli/commands/go.py +1 -1
- fast_agent/cli/commands/url_parser.py +3 -3
- fast_agent/cli/main.py +7 -1
- fast_agent/config.py +6 -0
- fast_agent/core/__init__.py +65 -25
- fast_agent/core/direct_decorators.py +85 -103
- fast_agent/core/direct_factory.py +1 -1
- fast_agent/core/error_handling.py +1 -1
- fast_agent/core/fastagent.py +153 -34
- fast_agent/core/logging/events.py +4 -9
- fast_agent/llm/prompt_utils.py +10 -4
- fast_agent/llm/provider/anthropic/llm_anthropic.py +16 -5
- fast_agent/llm/provider/bedrock/llm_bedrock.py +13 -5
- fast_agent/llm/provider/google/llm_google_native.py +13 -2
- fast_agent/llm/provider/openai/llm_openai.py +22 -13
- fast_agent/mcp/ui_agent.py +1 -1
- fast_agent/resources/examples/data-analysis/analysis-campaign.py +1 -1
- fast_agent/resources/examples/data-analysis/analysis.py +1 -1
- fast_agent/resources/examples/mcp/elicitations/forms_demo.py +1 -1
- fast_agent/resources/examples/mcp/elicitations/game_character.py +1 -1
- fast_agent/resources/examples/mcp/elicitations/tool_call.py +1 -1
- fast_agent/resources/examples/mcp/state-transfer/agent_one.py +1 -1
- fast_agent/resources/examples/mcp/state-transfer/agent_two.py +1 -1
- fast_agent/resources/examples/researcher/researcher-eval.py +1 -1
- fast_agent/resources/examples/researcher/researcher-imp.py +1 -1
- fast_agent/resources/examples/researcher/researcher.py +1 -1
- fast_agent/resources/examples/tensorzero/agent.py +1 -1
- fast_agent/resources/examples/tensorzero/image_demo.py +1 -1
- fast_agent/resources/examples/tensorzero/simple_agent.py +1 -1
- fast_agent/resources/examples/workflows/chaining.py +1 -1
- fast_agent/resources/examples/workflows/evaluator.py +1 -1
- fast_agent/resources/examples/workflows/human_input.py +1 -1
- fast_agent/resources/examples/workflows/orchestrator.py +1 -1
- fast_agent/resources/examples/workflows/parallel.py +1 -1
- fast_agent/resources/examples/workflows/router.py +1 -1
- fast_agent/resources/setup/agent.py +1 -1
- fast_agent/resources/setup/fastagent.config.yaml +1 -0
- fast_agent/ui/mcp_ui_utils.py +12 -1
- fast_agent/ui/rich_progress.py +8 -6
- {fast_agent_mcp-0.3.2.dist-info → fast_agent_mcp-0.3.4.dist-info}/METADATA +2 -2
- {fast_agent_mcp-0.3.2.dist-info → fast_agent_mcp-0.3.4.dist-info}/RECORD +49 -49
- {fast_agent_mcp-0.3.2.dist-info → fast_agent_mcp-0.3.4.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.3.2.dist-info → fast_agent_mcp-0.3.4.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.3.2.dist-info → fast_agent_mcp-0.3.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,7 +5,7 @@ Error handling utilities for agent operations.
|
|
|
5
5
|
from rich import print
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def handle_error(e: Exception, error_type: str, suggestion: str = None) -> None:
|
|
8
|
+
def handle_error(e: Exception, error_type: str, suggestion: str | None = None) -> None:
|
|
9
9
|
"""
|
|
10
10
|
Handle errors with consistent formatting and messaging.
|
|
11
11
|
|
fast_agent/core/fastagent.py
CHANGED
|
@@ -10,7 +10,19 @@ import sys
|
|
|
10
10
|
from contextlib import asynccontextmanager
|
|
11
11
|
from importlib.metadata import version as get_version
|
|
12
12
|
from pathlib import Path
|
|
13
|
-
from typing import
|
|
13
|
+
from typing import (
|
|
14
|
+
TYPE_CHECKING,
|
|
15
|
+
Any,
|
|
16
|
+
AsyncIterator,
|
|
17
|
+
Awaitable,
|
|
18
|
+
Callable,
|
|
19
|
+
Dict,
|
|
20
|
+
List,
|
|
21
|
+
Literal,
|
|
22
|
+
Optional,
|
|
23
|
+
ParamSpec,
|
|
24
|
+
TypeVar,
|
|
25
|
+
)
|
|
14
26
|
|
|
15
27
|
import yaml
|
|
16
28
|
from opentelemetry import trace
|
|
@@ -67,6 +79,9 @@ from fast_agent.mcp.prompts.prompt_load import load_prompt_multipart
|
|
|
67
79
|
from fast_agent.ui.usage_display import display_usage_report
|
|
68
80
|
|
|
69
81
|
if TYPE_CHECKING:
|
|
82
|
+
from mcp.client.session import ElicitationFnT
|
|
83
|
+
from pydantic import AnyUrl
|
|
84
|
+
|
|
70
85
|
from fast_agent.interfaces import AgentProtocol
|
|
71
86
|
from fast_agent.types import PromptMessageExtended
|
|
72
87
|
|
|
@@ -250,32 +265,137 @@ class FastAgent:
|
|
|
250
265
|
"""Access the application context"""
|
|
251
266
|
return self.app.context
|
|
252
267
|
|
|
253
|
-
# Decorator methods with
|
|
268
|
+
# Decorator methods with precise signatures for IDE completion
|
|
254
269
|
# Provide annotations so IDEs can discover these attributes on instances
|
|
255
270
|
if TYPE_CHECKING: # pragma: no cover - typing aid only
|
|
256
|
-
from
|
|
257
|
-
|
|
258
|
-
from fast_agent.
|
|
259
|
-
DecoratedAgentProtocol,
|
|
260
|
-
DecoratedChainProtocol,
|
|
261
|
-
DecoratedEvaluatorOptimizerProtocol,
|
|
262
|
-
DecoratedOrchestratorProtocol,
|
|
263
|
-
DecoratedParallelProtocol,
|
|
264
|
-
DecoratedRouterProtocol,
|
|
265
|
-
)
|
|
271
|
+
from pathlib import Path
|
|
272
|
+
|
|
273
|
+
from fast_agent.types import RequestParams
|
|
266
274
|
|
|
267
275
|
P = ParamSpec("P")
|
|
268
276
|
R = TypeVar("R")
|
|
269
277
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
278
|
+
def agent(
|
|
279
|
+
self,
|
|
280
|
+
name: str = "default",
|
|
281
|
+
instruction_or_kwarg: Optional[str | Path | AnyUrl] = None,
|
|
282
|
+
*,
|
|
283
|
+
instruction: str | Path | AnyUrl = "You are a helpful agent.",
|
|
284
|
+
servers: List[str] = [],
|
|
285
|
+
tools: Optional[Dict[str, List[str]]] = None,
|
|
286
|
+
resources: Optional[Dict[str, List[str]]] = None,
|
|
287
|
+
prompts: Optional[Dict[str, List[str]]] = None,
|
|
288
|
+
model: Optional[str] = None,
|
|
289
|
+
use_history: bool = True,
|
|
290
|
+
request_params: RequestParams | None = None,
|
|
291
|
+
human_input: bool = False,
|
|
292
|
+
default: bool = False,
|
|
293
|
+
elicitation_handler: Optional[ElicitationFnT] = None,
|
|
294
|
+
api_key: str | None = None,
|
|
295
|
+
) -> Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[R]]]: ...
|
|
296
|
+
|
|
297
|
+
def custom(
|
|
298
|
+
self,
|
|
299
|
+
cls,
|
|
300
|
+
name: str = "default",
|
|
301
|
+
instruction_or_kwarg: Optional[str | Path | AnyUrl] = None,
|
|
302
|
+
*,
|
|
303
|
+
instruction: str | Path | AnyUrl = "You are a helpful agent.",
|
|
304
|
+
servers: List[str] = [],
|
|
305
|
+
tools: Optional[Dict[str, List[str]]] = None,
|
|
306
|
+
resources: Optional[Dict[str, List[str]]] = None,
|
|
307
|
+
prompts: Optional[Dict[str, List[str]]] = None,
|
|
308
|
+
model: Optional[str] = None,
|
|
309
|
+
use_history: bool = True,
|
|
310
|
+
request_params: RequestParams | None = None,
|
|
311
|
+
human_input: bool = False,
|
|
312
|
+
default: bool = False,
|
|
313
|
+
elicitation_handler: Optional[ElicitationFnT] = None,
|
|
314
|
+
api_key: str | None = None,
|
|
315
|
+
) -> Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[R]]]: ...
|
|
316
|
+
|
|
317
|
+
def orchestrator(
|
|
318
|
+
self,
|
|
319
|
+
name: str,
|
|
320
|
+
*,
|
|
321
|
+
agents: List[str],
|
|
322
|
+
instruction: str
|
|
323
|
+
| Path
|
|
324
|
+
| AnyUrl = "You are an expert planner. Given an objective task and a list of Agents\n(which are collections of capabilities), your job is to break down the objective\ninto a series of steps, which can be performed by these agents.\n",
|
|
325
|
+
model: Optional[str] = None,
|
|
326
|
+
request_params: RequestParams | None = None,
|
|
327
|
+
use_history: bool = False,
|
|
328
|
+
human_input: bool = False,
|
|
329
|
+
plan_type: Literal["full", "iterative"] = "full",
|
|
330
|
+
plan_iterations: int = 5,
|
|
331
|
+
default: bool = False,
|
|
332
|
+
api_key: str | None = None,
|
|
333
|
+
) -> Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[R]]]: ...
|
|
334
|
+
|
|
335
|
+
def iterative_planner(
|
|
336
|
+
self,
|
|
337
|
+
name: str,
|
|
338
|
+
*,
|
|
339
|
+
agents: List[str],
|
|
340
|
+
instruction: str | Path | AnyUrl = "You are an expert planner. Plan iteratively.",
|
|
341
|
+
model: Optional[str] = None,
|
|
342
|
+
request_params: RequestParams | None = None,
|
|
343
|
+
plan_iterations: int = -1,
|
|
344
|
+
default: bool = False,
|
|
345
|
+
api_key: str | None = None,
|
|
346
|
+
) -> Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[R]]]: ...
|
|
347
|
+
|
|
348
|
+
def router(
|
|
349
|
+
self,
|
|
350
|
+
name: str,
|
|
351
|
+
*,
|
|
352
|
+
agents: List[str],
|
|
353
|
+
instruction: Optional[str | Path | AnyUrl] = None,
|
|
354
|
+
servers: List[str] = [],
|
|
355
|
+
tools: Optional[Dict[str, List[str]]] = None,
|
|
356
|
+
resources: Optional[Dict[str, List[str]]] = None,
|
|
357
|
+
prompts: Optional[Dict[str, List[str]]] = None,
|
|
358
|
+
model: Optional[str] = None,
|
|
359
|
+
use_history: bool = False,
|
|
360
|
+
request_params: RequestParams | None = None,
|
|
361
|
+
human_input: bool = False,
|
|
362
|
+
default: bool = False,
|
|
363
|
+
elicitation_handler: Optional[ElicitationFnT] = None,
|
|
364
|
+
api_key: str | None = None,
|
|
365
|
+
) -> Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[R]]]: ...
|
|
366
|
+
|
|
367
|
+
def chain(
|
|
368
|
+
self,
|
|
369
|
+
name: str,
|
|
370
|
+
*,
|
|
371
|
+
sequence: List[str],
|
|
372
|
+
instruction: Optional[str | Path | AnyUrl] = None,
|
|
373
|
+
cumulative: bool = False,
|
|
374
|
+
default: bool = False,
|
|
375
|
+
) -> Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[R]]]: ...
|
|
376
|
+
|
|
377
|
+
def parallel(
|
|
378
|
+
self,
|
|
379
|
+
name: str,
|
|
380
|
+
*,
|
|
381
|
+
fan_out: List[str],
|
|
382
|
+
fan_in: str | None = None,
|
|
383
|
+
instruction: Optional[str | Path | AnyUrl] = None,
|
|
384
|
+
include_request: bool = True,
|
|
385
|
+
default: bool = False,
|
|
386
|
+
) -> Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[R]]]: ...
|
|
387
|
+
|
|
388
|
+
def evaluator_optimizer(
|
|
389
|
+
self,
|
|
390
|
+
name: str,
|
|
391
|
+
*,
|
|
392
|
+
generator: str,
|
|
393
|
+
evaluator: str,
|
|
394
|
+
instruction: Optional[str | Path | AnyUrl] = None,
|
|
395
|
+
min_rating: str = "GOOD",
|
|
396
|
+
max_refinements: int = 3,
|
|
397
|
+
default: bool = False,
|
|
398
|
+
) -> Callable[[Callable[P, Awaitable[R]]], Callable[P, Awaitable[R]]]: ...
|
|
279
399
|
|
|
280
400
|
# Runtime bindings (actual implementations)
|
|
281
401
|
agent = agent_decorator
|
|
@@ -288,7 +408,7 @@ class FastAgent:
|
|
|
288
408
|
evaluator_optimizer = evaluator_optimizer_decorator
|
|
289
409
|
|
|
290
410
|
@asynccontextmanager
|
|
291
|
-
async def run(self):
|
|
411
|
+
async def run(self) -> AsyncIterator["AgentApp"]:
|
|
292
412
|
"""
|
|
293
413
|
Context manager for running the application.
|
|
294
414
|
Initializes all registered agents.
|
|
@@ -308,15 +428,14 @@ class FastAgent:
|
|
|
308
428
|
try:
|
|
309
429
|
async with self.app.run():
|
|
310
430
|
# Apply quiet mode if requested
|
|
311
|
-
if
|
|
312
|
-
|
|
313
|
-
and
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
self.app.context.config.logger.show_tools = False
|
|
431
|
+
if quiet_mode:
|
|
432
|
+
cfg = self.app.context.config
|
|
433
|
+
if cfg is not None and cfg.logger is not None:
|
|
434
|
+
# Update our app's config directly
|
|
435
|
+
cfg_logger = cfg.logger
|
|
436
|
+
cfg_logger.progress_display = False
|
|
437
|
+
cfg_logger.show_chat = False
|
|
438
|
+
cfg_logger.show_tools = False
|
|
320
439
|
|
|
321
440
|
# Directly disable the progress display singleton
|
|
322
441
|
from fast_agent.ui.progress_display import progress_display
|
|
@@ -437,12 +556,12 @@ class FastAgent:
|
|
|
437
556
|
try:
|
|
438
557
|
# Get response from the agent
|
|
439
558
|
agent = active_agents[agent_name]
|
|
440
|
-
|
|
559
|
+
prompt_result = await agent.generate(prompt)
|
|
441
560
|
|
|
442
561
|
# In quiet mode, just print the raw response
|
|
443
562
|
# The chat display should already be turned off by the configuration
|
|
444
563
|
if self.args.quiet:
|
|
445
|
-
print(f"{
|
|
564
|
+
print(f"{prompt_result.last_text()}")
|
|
446
565
|
|
|
447
566
|
raise SystemExit(0)
|
|
448
567
|
except Exception as e:
|
|
@@ -590,7 +709,7 @@ class FastAgent:
|
|
|
590
709
|
original_args.quiet if original_args and hasattr(original_args, "quiet") else False
|
|
591
710
|
)
|
|
592
711
|
self.args.model = None
|
|
593
|
-
if hasattr(original_args, "model"):
|
|
712
|
+
if original_args is not None and hasattr(original_args, "model"):
|
|
594
713
|
self.args.model = original_args.model
|
|
595
714
|
|
|
596
715
|
# Run the application, which will detect the server flag and start server mode
|
|
@@ -5,12 +5,7 @@ Events and event filters for the logger module for the MCP Agent
|
|
|
5
5
|
import logging
|
|
6
6
|
import random
|
|
7
7
|
from datetime import datetime
|
|
8
|
-
from typing import
|
|
9
|
-
Any,
|
|
10
|
-
Dict,
|
|
11
|
-
Literal,
|
|
12
|
-
Set,
|
|
13
|
-
)
|
|
8
|
+
from typing import Any, Dict, Literal
|
|
14
9
|
|
|
15
10
|
from pydantic import BaseModel, ConfigDict, Field
|
|
16
11
|
|
|
@@ -64,9 +59,9 @@ class EventFilter(BaseModel):
|
|
|
64
59
|
- a minimum severity level (DEBUG < INFO < WARNING < ERROR)
|
|
65
60
|
"""
|
|
66
61
|
|
|
67
|
-
types:
|
|
68
|
-
names:
|
|
69
|
-
namespaces:
|
|
62
|
+
types: set[EventType] = Field(default_factory=set)
|
|
63
|
+
names: set[str] = Field(default_factory=set)
|
|
64
|
+
namespaces: set[str] = Field(default_factory=set)
|
|
70
65
|
min_level: EventType | None = "debug"
|
|
71
66
|
|
|
72
67
|
def matches(self, event: Event) -> bool:
|
fast_agent/llm/prompt_utils.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
XML formatting utilities for consistent prompt engineering across components.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from typing import Dict, List, Optional,
|
|
5
|
+
from typing import Dict, List, Optional, TypedDict
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def format_xml_tag(
|
|
@@ -100,10 +100,16 @@ def format_server_info(
|
|
|
100
100
|
return format_fastagent_tag("server", f"\n{server_content}\n", {"name": server_name})
|
|
101
101
|
|
|
102
102
|
|
|
103
|
+
class ServerInfo(TypedDict, total=False):
|
|
104
|
+
name: str
|
|
105
|
+
description: str
|
|
106
|
+
tools: List[Dict[str, str]]
|
|
107
|
+
|
|
108
|
+
|
|
103
109
|
def format_agent_info(
|
|
104
110
|
agent_name: str,
|
|
105
111
|
description: Optional[str] = None,
|
|
106
|
-
servers: Optional[List[
|
|
112
|
+
servers: Optional[List[ServerInfo]] = None,
|
|
107
113
|
) -> str:
|
|
108
114
|
"""
|
|
109
115
|
Format agent information consistently across router and orchestrator modules.
|
|
@@ -133,8 +139,8 @@ def format_agent_info(
|
|
|
133
139
|
server_tags = []
|
|
134
140
|
for server in servers:
|
|
135
141
|
server_name = server.get("name", "")
|
|
136
|
-
server_desc = server.get("description"
|
|
137
|
-
server_tools = server.get("tools"
|
|
142
|
+
server_desc = server.get("description")
|
|
143
|
+
server_tools = server.get("tools")
|
|
138
144
|
server_tag = format_server_info(server_name, server_desc, server_tools)
|
|
139
145
|
server_tags.append(server_tag)
|
|
140
146
|
|
|
@@ -291,6 +291,7 @@ class AnthropicLLM(FastAgentLLM[MessageParam, Message]):
|
|
|
291
291
|
request_params: RequestParams | None = None,
|
|
292
292
|
structured_model: Type[ModelT] | None = None,
|
|
293
293
|
tools: List[Tool] | None = None,
|
|
294
|
+
pre_messages: List[MessageParam] | None = None,
|
|
294
295
|
) -> PromptMessageExtended:
|
|
295
296
|
"""
|
|
296
297
|
Process a query using an LLM and available tools.
|
|
@@ -304,7 +305,7 @@ class AnthropicLLM(FastAgentLLM[MessageParam, Message]):
|
|
|
304
305
|
|
|
305
306
|
try:
|
|
306
307
|
anthropic = AsyncAnthropic(api_key=api_key, base_url=base_url)
|
|
307
|
-
messages: List[MessageParam] = []
|
|
308
|
+
messages: List[MessageParam] = list(pre_messages) if pre_messages else []
|
|
308
309
|
params = self.get_request_params(request_params)
|
|
309
310
|
except AuthenticationError as e:
|
|
310
311
|
raise ProviderKeyError(
|
|
@@ -312,7 +313,7 @@ class AnthropicLLM(FastAgentLLM[MessageParam, Message]):
|
|
|
312
313
|
"The configured Anthropic API key was rejected.\nPlease check that your API key is valid and not expired.",
|
|
313
314
|
) from e
|
|
314
315
|
|
|
315
|
-
# Always include prompt messages, but only include conversation history
|
|
316
|
+
# Always include prompt messages, but only include conversation history if enabled
|
|
316
317
|
messages.extend(self.history.get(include_completion_history=params.use_history))
|
|
317
318
|
messages.append(message_param) # message_param is the current user turn
|
|
318
319
|
|
|
@@ -470,6 +471,9 @@ class AnthropicLLM(FastAgentLLM[MessageParam, Message]):
|
|
|
470
471
|
tools: List[Tool] | None = None,
|
|
471
472
|
is_template: bool = False,
|
|
472
473
|
) -> PromptMessageExtended:
|
|
474
|
+
# Effective params for this turn
|
|
475
|
+
params = self.get_request_params(request_params)
|
|
476
|
+
|
|
473
477
|
# Check the last message role
|
|
474
478
|
last_message = multipart_messages[-1]
|
|
475
479
|
|
|
@@ -477,7 +481,7 @@ class AnthropicLLM(FastAgentLLM[MessageParam, Message]):
|
|
|
477
481
|
messages_to_add = (
|
|
478
482
|
multipart_messages[:-1] if last_message.role == "user" else multipart_messages
|
|
479
483
|
)
|
|
480
|
-
converted = []
|
|
484
|
+
converted: List[MessageParam] = []
|
|
481
485
|
|
|
482
486
|
# Get cache mode configuration
|
|
483
487
|
cache_mode = self._get_cache_mode()
|
|
@@ -499,12 +503,19 @@ class AnthropicLLM(FastAgentLLM[MessageParam, Message]):
|
|
|
499
503
|
|
|
500
504
|
converted.append(anthropic_msg)
|
|
501
505
|
|
|
502
|
-
|
|
506
|
+
# Persist prior only when history is enabled; otherwise inline for this call
|
|
507
|
+
pre_messages: List[MessageParam] | None = None
|
|
508
|
+
if params.use_history:
|
|
509
|
+
self.history.extend(converted, is_prompt=is_template)
|
|
510
|
+
else:
|
|
511
|
+
pre_messages = converted
|
|
503
512
|
|
|
504
513
|
if last_message.role == "user":
|
|
505
514
|
logger.debug("Last message in prompt is from user, generating assistant response")
|
|
506
515
|
message_param = AnthropicConverter.convert_to_anthropic(last_message)
|
|
507
|
-
return await self._anthropic_completion(
|
|
516
|
+
return await self._anthropic_completion(
|
|
517
|
+
message_param, request_params, tools=tools, pre_messages=pre_messages
|
|
518
|
+
)
|
|
508
519
|
else:
|
|
509
520
|
# For assistant messages: Return the last message content as text
|
|
510
521
|
logger.debug("Last message in prompt is from assistant, returning it directly")
|
|
@@ -1192,6 +1192,7 @@ class BedrockLLM(FastAgentLLM[BedrockMessageParam, BedrockMessage]):
|
|
|
1192
1192
|
message_param: BedrockMessageParam,
|
|
1193
1193
|
request_params: RequestParams | None = None,
|
|
1194
1194
|
tools: List[Tool] | None = None,
|
|
1195
|
+
pre_messages: List[BedrockMessageParam] | None = None,
|
|
1195
1196
|
) -> PromptMessageExtended:
|
|
1196
1197
|
"""
|
|
1197
1198
|
Process a query using Bedrock and available tools.
|
|
@@ -1200,7 +1201,7 @@ class BedrockLLM(FastAgentLLM[BedrockMessageParam, BedrockMessage]):
|
|
|
1200
1201
|
client = self._get_bedrock_runtime_client()
|
|
1201
1202
|
|
|
1202
1203
|
try:
|
|
1203
|
-
messages: List[BedrockMessageParam] = []
|
|
1204
|
+
messages: List[BedrockMessageParam] = list(pre_messages) if pre_messages else []
|
|
1204
1205
|
params = self.get_request_params(request_params)
|
|
1205
1206
|
except (ClientError, BotoCoreError) as e:
|
|
1206
1207
|
error_msg = str(e)
|
|
@@ -1869,8 +1870,13 @@ class BedrockLLM(FastAgentLLM[BedrockMessageParam, BedrockMessage]):
|
|
|
1869
1870
|
bedrock_msg = self._convert_multipart_to_bedrock_message(msg)
|
|
1870
1871
|
converted.append(bedrock_msg)
|
|
1871
1872
|
|
|
1872
|
-
#
|
|
1873
|
-
self.
|
|
1873
|
+
# Only persist prior messages when history is enabled; otherwise inline for this call
|
|
1874
|
+
params = self.get_request_params(request_params)
|
|
1875
|
+
pre_messages: List[BedrockMessageParam] | None = None
|
|
1876
|
+
if params.use_history:
|
|
1877
|
+
self.history.extend(converted, is_prompt=is_template)
|
|
1878
|
+
else:
|
|
1879
|
+
pre_messages = converted
|
|
1874
1880
|
|
|
1875
1881
|
if last_message.role == "assistant":
|
|
1876
1882
|
# For assistant messages: Return the last message (no completion needed)
|
|
@@ -1884,8 +1890,10 @@ class BedrockLLM(FastAgentLLM[BedrockMessageParam, BedrockMessage]):
|
|
|
1884
1890
|
# Convert the last user message to Bedrock message parameter format
|
|
1885
1891
|
message_param = self._convert_multipart_to_bedrock_message(last_message)
|
|
1886
1892
|
|
|
1887
|
-
# Call the
|
|
1888
|
-
return await self._bedrock_completion(
|
|
1893
|
+
# Call the completion method with optional pre_messages for no-history mode
|
|
1894
|
+
return await self._bedrock_completion(
|
|
1895
|
+
message_param, request_params, tools, pre_messages=pre_messages
|
|
1896
|
+
)
|
|
1889
1897
|
|
|
1890
1898
|
def _generate_simplified_schema(self, model: Type[ModelT]) -> str:
|
|
1891
1899
|
"""Generates a simplified, human-readable schema with inline enum constraints."""
|
|
@@ -280,9 +280,15 @@ class GoogleNativeLLM(FastAgentLLM[types.Content, types.Content]):
|
|
|
280
280
|
)
|
|
281
281
|
|
|
282
282
|
if messages_to_add:
|
|
283
|
-
# Convert prior messages to google.genai Content
|
|
283
|
+
# Convert prior messages to google.genai Content
|
|
284
284
|
converted_prior = self._converter.convert_to_google_content(messages_to_add)
|
|
285
|
-
|
|
285
|
+
# Only persist prior context when history is enabled; otherwise inline later
|
|
286
|
+
if request_params.use_history:
|
|
287
|
+
self.history.extend(converted_prior, is_prompt=is_template)
|
|
288
|
+
else:
|
|
289
|
+
# Prepend prior context directly to the turn message list
|
|
290
|
+
# This keeps the single-turn chain intact without relying on provider memory
|
|
291
|
+
pass
|
|
286
292
|
|
|
287
293
|
if last_message.role == "assistant":
|
|
288
294
|
# No generation required; the provided assistant message is the output
|
|
@@ -322,6 +328,11 @@ class GoogleNativeLLM(FastAgentLLM[types.Content, types.Content]):
|
|
|
322
328
|
# convert_to_google_content returns a list; preserve order after tool responses
|
|
323
329
|
turn_messages.extend(user_contents)
|
|
324
330
|
|
|
331
|
+
# If not using provider history, include prior messages inline for this turn
|
|
332
|
+
if messages_to_add and not request_params.use_history:
|
|
333
|
+
prior_contents = self._converter.convert_to_google_content(messages_to_add)
|
|
334
|
+
turn_messages = prior_contents + turn_messages
|
|
335
|
+
|
|
325
336
|
# If we somehow have no provider-native parts, ensure we send an empty user content
|
|
326
337
|
if not turn_messages:
|
|
327
338
|
turn_messages.append(types.Content(role="user", parts=[types.Part.from_text("")]))
|
|
@@ -448,32 +448,41 @@ class OpenAILLM(FastAgentLLM[ChatCompletionMessageParam, ChatCompletionMessage])
|
|
|
448
448
|
tools: List[Tool] | None = None,
|
|
449
449
|
is_template: bool = False,
|
|
450
450
|
) -> PromptMessageExtended:
|
|
451
|
-
#
|
|
451
|
+
# Determine effective params to respect use_history for this turn
|
|
452
|
+
req_params = self.get_request_params(request_params)
|
|
452
453
|
|
|
453
454
|
last_message = multipart_messages[-1]
|
|
454
455
|
|
|
455
|
-
#
|
|
456
|
-
# if the last message is a "user" inference is required
|
|
456
|
+
# Prepare prior messages (everything before the last user message), or all if last is assistant
|
|
457
457
|
messages_to_add = (
|
|
458
458
|
multipart_messages[:-1] if last_message.role == "user" else multipart_messages
|
|
459
459
|
)
|
|
460
|
-
|
|
460
|
+
|
|
461
|
+
converted_prior: List[ChatCompletionMessageParam] = []
|
|
461
462
|
for msg in messages_to_add:
|
|
462
463
|
# convert_to_openai now returns a list of messages
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
self.history.extend(converted, is_prompt=is_template)
|
|
464
|
+
converted_prior.extend(OpenAIConverter.convert_to_openai(msg))
|
|
466
465
|
|
|
467
|
-
|
|
466
|
+
# If the last message is from the assistant, no inference required
|
|
467
|
+
if last_message.role == "assistant":
|
|
468
468
|
return last_message
|
|
469
469
|
|
|
470
|
-
|
|
471
|
-
|
|
470
|
+
# Convert the last user message
|
|
471
|
+
converted_last = OpenAIConverter.convert_to_openai(last_message)
|
|
472
|
+
if not converted_last:
|
|
472
473
|
# Fallback for empty conversion
|
|
473
|
-
|
|
474
|
+
converted_last = [{"role": "user", "content": ""}]
|
|
475
|
+
|
|
476
|
+
# History-aware vs stateless turn construction
|
|
477
|
+
if req_params.use_history:
|
|
478
|
+
# Persist prior context to provider memory; send only the last message for this turn
|
|
479
|
+
self.history.extend(converted_prior, is_prompt=is_template)
|
|
480
|
+
turn_messages = converted_last
|
|
481
|
+
else:
|
|
482
|
+
# Do NOT persist; inline the full turn context to the provider call
|
|
483
|
+
turn_messages = converted_prior + converted_last
|
|
474
484
|
|
|
475
|
-
|
|
476
|
-
return await self._openai_completion(converted_messages, request_params, tools)
|
|
485
|
+
return await self._openai_completion(turn_messages, req_params, tools)
|
|
477
486
|
|
|
478
487
|
def _prepare_api_request(
|
|
479
488
|
self, messages, tools: List[ChatCompletionToolParam] | None, request_params: RequestParams
|
fast_agent/mcp/ui_agent.py
CHANGED
|
@@ -13,7 +13,7 @@ import asyncio
|
|
|
13
13
|
from rich.console import Console
|
|
14
14
|
from rich.panel import Panel
|
|
15
15
|
|
|
16
|
-
from fast_agent
|
|
16
|
+
from fast_agent import FastAgent
|
|
17
17
|
from fast_agent.mcp.helpers.content_helpers import get_resource_text
|
|
18
18
|
|
|
19
19
|
fast = FastAgent("Elicitation Forms Demo", quiet=True)
|
|
@@ -14,7 +14,7 @@ from game_character_handler import game_character_elicitation_handler
|
|
|
14
14
|
from rich.console import Console
|
|
15
15
|
from rich.panel import Panel
|
|
16
16
|
|
|
17
|
-
from fast_agent
|
|
17
|
+
from fast_agent import FastAgent
|
|
18
18
|
from fast_agent.mcp.helpers.content_helpers import get_resource_text
|
|
19
19
|
|
|
20
20
|
fast = FastAgent("Game Character Creator", quiet=True)
|
|
@@ -6,7 +6,7 @@ from typing import List, Union
|
|
|
6
6
|
|
|
7
7
|
from mcp.types import ImageContent, TextContent
|
|
8
8
|
|
|
9
|
-
from fast_agent
|
|
9
|
+
from fast_agent import FastAgent
|
|
10
10
|
from fast_agent.core.prompt import Prompt
|
|
11
11
|
from fast_agent.llm.request_params import RequestParams
|
|
12
12
|
|