fast-agent-mcp 0.1.12__py3-none-any.whl → 0.2.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.
Files changed (169) hide show
  1. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/METADATA +3 -4
  2. fast_agent_mcp-0.2.0.dist-info/RECORD +123 -0
  3. mcp_agent/__init__.py +75 -0
  4. mcp_agent/agents/agent.py +61 -415
  5. mcp_agent/agents/base_agent.py +522 -0
  6. mcp_agent/agents/workflow/__init__.py +1 -0
  7. mcp_agent/agents/workflow/chain_agent.py +173 -0
  8. mcp_agent/agents/workflow/evaluator_optimizer.py +362 -0
  9. mcp_agent/agents/workflow/orchestrator_agent.py +591 -0
  10. mcp_agent/{workflows/orchestrator → agents/workflow}/orchestrator_models.py +11 -21
  11. mcp_agent/agents/workflow/parallel_agent.py +182 -0
  12. mcp_agent/agents/workflow/router_agent.py +307 -0
  13. mcp_agent/app.py +15 -19
  14. mcp_agent/cli/commands/bootstrap.py +19 -38
  15. mcp_agent/cli/commands/config.py +4 -4
  16. mcp_agent/cli/commands/setup.py +7 -14
  17. mcp_agent/cli/main.py +7 -10
  18. mcp_agent/cli/terminal.py +3 -3
  19. mcp_agent/config.py +25 -40
  20. mcp_agent/context.py +12 -21
  21. mcp_agent/context_dependent.py +3 -5
  22. mcp_agent/core/agent_types.py +10 -7
  23. mcp_agent/core/direct_agent_app.py +179 -0
  24. mcp_agent/core/direct_decorators.py +443 -0
  25. mcp_agent/core/direct_factory.py +476 -0
  26. mcp_agent/core/enhanced_prompt.py +23 -55
  27. mcp_agent/core/exceptions.py +8 -8
  28. mcp_agent/core/fastagent.py +145 -371
  29. mcp_agent/core/interactive_prompt.py +424 -0
  30. mcp_agent/core/mcp_content.py +17 -17
  31. mcp_agent/core/prompt.py +6 -9
  32. mcp_agent/core/request_params.py +6 -3
  33. mcp_agent/core/validation.py +92 -18
  34. mcp_agent/executor/decorator_registry.py +9 -17
  35. mcp_agent/executor/executor.py +8 -17
  36. mcp_agent/executor/task_registry.py +2 -4
  37. mcp_agent/executor/temporal.py +19 -41
  38. mcp_agent/executor/workflow.py +3 -5
  39. mcp_agent/executor/workflow_signal.py +15 -21
  40. mcp_agent/human_input/handler.py +4 -7
  41. mcp_agent/human_input/types.py +2 -3
  42. mcp_agent/llm/__init__.py +2 -0
  43. mcp_agent/llm/augmented_llm.py +450 -0
  44. mcp_agent/llm/augmented_llm_passthrough.py +162 -0
  45. mcp_agent/llm/augmented_llm_playback.py +83 -0
  46. mcp_agent/llm/memory.py +103 -0
  47. mcp_agent/{workflows/llm → llm}/model_factory.py +22 -16
  48. mcp_agent/{workflows/llm → llm}/prompt_utils.py +1 -3
  49. mcp_agent/llm/providers/__init__.py +8 -0
  50. mcp_agent/{workflows/llm → llm/providers}/anthropic_utils.py +8 -25
  51. mcp_agent/{workflows/llm → llm/providers}/augmented_llm_anthropic.py +56 -194
  52. mcp_agent/llm/providers/augmented_llm_deepseek.py +53 -0
  53. mcp_agent/{workflows/llm → llm/providers}/augmented_llm_openai.py +99 -190
  54. mcp_agent/{workflows/llm → llm}/providers/multipart_converter_anthropic.py +72 -71
  55. mcp_agent/{workflows/llm → llm}/providers/multipart_converter_openai.py +65 -71
  56. mcp_agent/{workflows/llm → llm}/providers/openai_multipart.py +16 -44
  57. mcp_agent/{workflows/llm → llm/providers}/openai_utils.py +4 -4
  58. mcp_agent/{workflows/llm → llm}/providers/sampling_converter_anthropic.py +9 -11
  59. mcp_agent/{workflows/llm → llm}/providers/sampling_converter_openai.py +8 -12
  60. mcp_agent/{workflows/llm → llm}/sampling_converter.py +3 -31
  61. mcp_agent/llm/sampling_format_converter.py +37 -0
  62. mcp_agent/logging/events.py +1 -5
  63. mcp_agent/logging/json_serializer.py +7 -6
  64. mcp_agent/logging/listeners.py +20 -23
  65. mcp_agent/logging/logger.py +17 -19
  66. mcp_agent/logging/rich_progress.py +10 -8
  67. mcp_agent/logging/tracing.py +4 -6
  68. mcp_agent/logging/transport.py +22 -22
  69. mcp_agent/mcp/gen_client.py +1 -3
  70. mcp_agent/mcp/interfaces.py +117 -110
  71. mcp_agent/mcp/logger_textio.py +97 -0
  72. mcp_agent/mcp/mcp_agent_client_session.py +7 -7
  73. mcp_agent/mcp/mcp_agent_server.py +8 -8
  74. mcp_agent/mcp/mcp_aggregator.py +102 -143
  75. mcp_agent/mcp/mcp_connection_manager.py +20 -27
  76. mcp_agent/mcp/prompt_message_multipart.py +68 -16
  77. mcp_agent/mcp/prompt_render.py +77 -0
  78. mcp_agent/mcp/prompt_serialization.py +30 -48
  79. mcp_agent/mcp/prompts/prompt_constants.py +18 -0
  80. mcp_agent/mcp/prompts/prompt_helpers.py +327 -0
  81. mcp_agent/mcp/prompts/prompt_load.py +109 -0
  82. mcp_agent/mcp/prompts/prompt_server.py +155 -195
  83. mcp_agent/mcp/prompts/prompt_template.py +35 -66
  84. mcp_agent/mcp/resource_utils.py +7 -14
  85. mcp_agent/mcp/sampling.py +17 -17
  86. mcp_agent/mcp_server/agent_server.py +13 -17
  87. mcp_agent/mcp_server_registry.py +13 -22
  88. mcp_agent/resources/examples/{workflows → in_dev}/agent_build.py +3 -2
  89. mcp_agent/resources/examples/in_dev/slides.py +110 -0
  90. mcp_agent/resources/examples/internal/agent.py +6 -3
  91. mcp_agent/resources/examples/internal/fastagent.config.yaml +8 -2
  92. mcp_agent/resources/examples/internal/job.py +2 -1
  93. mcp_agent/resources/examples/internal/prompt_category.py +1 -1
  94. mcp_agent/resources/examples/internal/prompt_sizing.py +3 -5
  95. mcp_agent/resources/examples/internal/sizer.py +2 -1
  96. mcp_agent/resources/examples/internal/social.py +2 -1
  97. mcp_agent/resources/examples/prompting/agent.py +2 -1
  98. mcp_agent/resources/examples/prompting/image_server.py +4 -8
  99. mcp_agent/resources/examples/prompting/work_with_image.py +19 -0
  100. mcp_agent/ui/console_display.py +16 -20
  101. fast_agent_mcp-0.1.12.dist-info/RECORD +0 -161
  102. mcp_agent/core/agent_app.py +0 -646
  103. mcp_agent/core/agent_utils.py +0 -71
  104. mcp_agent/core/decorators.py +0 -455
  105. mcp_agent/core/factory.py +0 -463
  106. mcp_agent/core/proxies.py +0 -269
  107. mcp_agent/core/types.py +0 -24
  108. mcp_agent/eval/__init__.py +0 -0
  109. mcp_agent/mcp/stdio.py +0 -111
  110. mcp_agent/resources/examples/data-analysis/analysis-campaign.py +0 -188
  111. mcp_agent/resources/examples/data-analysis/analysis.py +0 -65
  112. mcp_agent/resources/examples/data-analysis/fastagent.config.yaml +0 -41
  113. mcp_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -1471
  114. mcp_agent/resources/examples/mcp_researcher/researcher-eval.py +0 -53
  115. mcp_agent/resources/examples/researcher/fastagent.config.yaml +0 -66
  116. mcp_agent/resources/examples/researcher/researcher-eval.py +0 -53
  117. mcp_agent/resources/examples/researcher/researcher-imp.py +0 -190
  118. mcp_agent/resources/examples/researcher/researcher.py +0 -38
  119. mcp_agent/resources/examples/workflows/chaining.py +0 -44
  120. mcp_agent/resources/examples/workflows/evaluator.py +0 -78
  121. mcp_agent/resources/examples/workflows/fastagent.config.yaml +0 -24
  122. mcp_agent/resources/examples/workflows/human_input.py +0 -25
  123. mcp_agent/resources/examples/workflows/orchestrator.py +0 -73
  124. mcp_agent/resources/examples/workflows/parallel.py +0 -78
  125. mcp_agent/resources/examples/workflows/router.py +0 -53
  126. mcp_agent/resources/examples/workflows/sse.py +0 -23
  127. mcp_agent/telemetry/__init__.py +0 -0
  128. mcp_agent/telemetry/usage_tracking.py +0 -18
  129. mcp_agent/workflows/__init__.py +0 -0
  130. mcp_agent/workflows/embedding/__init__.py +0 -0
  131. mcp_agent/workflows/embedding/embedding_base.py +0 -61
  132. mcp_agent/workflows/embedding/embedding_cohere.py +0 -49
  133. mcp_agent/workflows/embedding/embedding_openai.py +0 -46
  134. mcp_agent/workflows/evaluator_optimizer/__init__.py +0 -0
  135. mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +0 -481
  136. mcp_agent/workflows/intent_classifier/__init__.py +0 -0
  137. mcp_agent/workflows/intent_classifier/intent_classifier_base.py +0 -120
  138. mcp_agent/workflows/intent_classifier/intent_classifier_embedding.py +0 -134
  139. mcp_agent/workflows/intent_classifier/intent_classifier_embedding_cohere.py +0 -45
  140. mcp_agent/workflows/intent_classifier/intent_classifier_embedding_openai.py +0 -45
  141. mcp_agent/workflows/intent_classifier/intent_classifier_llm.py +0 -161
  142. mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py +0 -60
  143. mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py +0 -60
  144. mcp_agent/workflows/llm/__init__.py +0 -0
  145. mcp_agent/workflows/llm/augmented_llm.py +0 -753
  146. mcp_agent/workflows/llm/augmented_llm_passthrough.py +0 -241
  147. mcp_agent/workflows/llm/augmented_llm_playback.py +0 -109
  148. mcp_agent/workflows/llm/providers/__init__.py +0 -8
  149. mcp_agent/workflows/llm/sampling_format_converter.py +0 -22
  150. mcp_agent/workflows/orchestrator/__init__.py +0 -0
  151. mcp_agent/workflows/orchestrator/orchestrator.py +0 -578
  152. mcp_agent/workflows/parallel/__init__.py +0 -0
  153. mcp_agent/workflows/parallel/fan_in.py +0 -350
  154. mcp_agent/workflows/parallel/fan_out.py +0 -187
  155. mcp_agent/workflows/parallel/parallel_llm.py +0 -166
  156. mcp_agent/workflows/router/__init__.py +0 -0
  157. mcp_agent/workflows/router/router_base.py +0 -368
  158. mcp_agent/workflows/router/router_embedding.py +0 -240
  159. mcp_agent/workflows/router/router_embedding_cohere.py +0 -59
  160. mcp_agent/workflows/router/router_embedding_openai.py +0 -59
  161. mcp_agent/workflows/router/router_llm.py +0 -320
  162. mcp_agent/workflows/swarm/__init__.py +0 -0
  163. mcp_agent/workflows/swarm/swarm.py +0 -320
  164. mcp_agent/workflows/swarm/swarm_anthropic.py +0 -42
  165. mcp_agent/workflows/swarm/swarm_openai.py +0 -41
  166. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/WHEEL +0 -0
  167. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/entry_points.txt +0 -0
  168. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/licenses/LICENSE +0 -0
  169. /mcp_agent/{workflows/orchestrator → agents/workflow}/orchestrator_prompts.py +0 -0
@@ -1,481 +0,0 @@
1
- import contextlib
2
- from enum import Enum
3
- from typing import Callable, List, Optional, Type, TYPE_CHECKING
4
- from pydantic import BaseModel, Field
5
-
6
- from mcp_agent.workflows.llm.augmented_llm import (
7
- AugmentedLLM,
8
- MessageParamT,
9
- MessageT,
10
- ModelT,
11
- RequestParams,
12
- )
13
- from mcp_agent.agents.agent import Agent
14
- from mcp_agent.core.agent_types import AgentConfig
15
- from mcp_agent.logging.logger import get_logger
16
- from mcp_agent.workflows.llm.augmented_llm_passthrough import PassthroughLLM
17
-
18
- if TYPE_CHECKING:
19
- from mcp_agent.context import Context
20
-
21
- logger = get_logger(__name__)
22
-
23
-
24
- class QualityRating(str, Enum):
25
- """Enum for evaluation quality ratings"""
26
-
27
- POOR = 0 # Major improvements needed
28
- FAIR = 1 # Several improvements needed
29
- GOOD = 2 # Minor improvements possible
30
- EXCELLENT = 3 # No improvements needed
31
-
32
-
33
- class EvaluationResult(BaseModel):
34
- """Model representing the evaluation result from the evaluator LLM"""
35
-
36
- rating: QualityRating = Field(description="Quality rating of the response")
37
- feedback: str = Field(
38
- description="Specific feedback and suggestions for improvement"
39
- )
40
- needs_improvement: bool = Field(
41
- description="Whether the output needs further improvement"
42
- )
43
- focus_areas: List[str] = Field(
44
- default_factory=list, description="Specific areas to focus on in next iteration"
45
- )
46
-
47
-
48
- class EvaluatorOptimizerLLM(AugmentedLLM[MessageParamT, MessageT]):
49
- """
50
- Implementation of the evaluator-optimizer workflow where one LLM generates responses
51
- while another provides evaluation and feedback in a refinement loop.
52
-
53
- This can be used either:
54
- 1. As a standalone workflow with its own optimizer agent
55
- 2. As a wrapper around another workflow (Orchestrator, Router, ParallelLLM) to add
56
- evaluation and refinement capabilities
57
-
58
- When to use this workflow:
59
- - When you have clear evaluation criteria and iterative refinement provides value
60
- - When LLM responses improve with articulated feedback
61
- - When the task benefits from focused iteration on specific aspects
62
-
63
- Examples:
64
- - Literary translation with "expert" refinement
65
- - Complex search tasks needing multiple rounds
66
- - Document writing requiring multiple revisions
67
- """
68
-
69
- def _initialize_default_params(self, kwargs: dict) -> RequestParams:
70
- """Initialize default parameters using the workflow's settings."""
71
- return RequestParams(
72
- systemPrompt=self.instruction,
73
- parallel_tool_calls=True,
74
- max_iterations=10,
75
- use_history=self.generator_use_history, # Use generator's history setting
76
- )
77
-
78
- def _init_request_params(self):
79
- """Initialize request parameters for both generator and evaluator components."""
80
- # Set up workflow's default params based on generator's history setting
81
- self.default_request_params = self._initialize_default_params({})
82
-
83
- # Ensure evaluator's request params have history disabled
84
- if hasattr(self.evaluator_llm, "default_request_params"):
85
- self.evaluator_llm.default_request_params.use_history = False
86
-
87
- def __init__(
88
- self,
89
- generator: Agent | AugmentedLLM,
90
- evaluator: str | Agent | AugmentedLLM,
91
- min_rating: QualityRating = QualityRating.GOOD,
92
- max_refinements: int = 3,
93
- llm_factory: Callable[[Agent], AugmentedLLM] | None = None,
94
- context: Optional["Context"] = None,
95
- name: Optional[str] = None,
96
- instruction: Optional[str] = None,
97
- ):
98
- """
99
- Initialize the evaluator-optimizer workflow.
100
-
101
- Args:
102
- generator: The agent/LLM/workflow that generates responses
103
- evaluator: The evaluator (string instruction, Agent or AugmentedLLM)
104
- min_rating: Minimum acceptable quality rating
105
- max_refinements: Maximum refinement iterations
106
- llm_factory: Factory to create LLMs from agents when needed
107
- name: Optional name for the workflow (defaults to generator's name)
108
- instruction: Optional instruction (defaults to generator's instruction)
109
- """
110
- # Set initial attributes
111
- self.name = name or getattr(generator, "name", "EvaluatorOptimizer")
112
- self.llm_factory = llm_factory
113
- self.generator = generator
114
- self.evaluator = evaluator
115
- self.min_rating = min_rating
116
- self.max_refinements = max_refinements
117
-
118
- # Determine generator's history setting directly based on type
119
- self.generator_use_history = False
120
- if isinstance(generator, Agent):
121
- self.generator_use_history = generator.config.use_history
122
- elif isinstance(generator, AugmentedLLM):
123
- if hasattr(generator, "aggregator") and isinstance(
124
- generator.aggregator, Agent
125
- ):
126
- self.generator_use_history = generator.aggregator.config.use_history
127
- elif hasattr(generator, "default_request_params"):
128
- self.generator_use_history = getattr(
129
- generator.default_request_params, "use_history", False
130
- )
131
- # All other types default to False
132
-
133
- # Initialize parent class
134
- super().__init__(context=context, name=name or getattr(generator, "name", None))
135
-
136
- # Create a PassthroughLLM as _llm property
137
- # TODO -- remove this when we fix/remove the inheritance hierarchy
138
- self._llm = PassthroughLLM(name=f"{self.name}_passthrough", context=context)
139
-
140
- # Set up the generator based on type
141
- if isinstance(generator, Agent):
142
- if not llm_factory:
143
- raise ValueError(
144
- "llm_factory is required when using an Agent generator"
145
- )
146
-
147
- # Use existing LLM if available, otherwise create new one
148
- self.generator_llm = getattr(generator, "_llm", None) or llm_factory(
149
- agent=generator
150
- )
151
- self.aggregator = generator
152
- self.instruction = instruction or (
153
- generator.instruction
154
- if isinstance(generator.instruction, str)
155
- else None
156
- )
157
- elif isinstance(generator, AugmentedLLM):
158
- self.generator_llm = generator
159
- self.aggregator = getattr(generator, "aggregator", None)
160
- self.instruction = instruction or generator.instruction
161
- else:
162
- # ChainProxy-like object
163
- self.generator_llm = generator
164
- self.aggregator = None
165
- self.instruction = (
166
- instruction or f"Chain of agents: {', '.join(generator._sequence)}"
167
- )
168
-
169
- # Set up the evaluator - always disable history
170
- if isinstance(evaluator, str):
171
- if not llm_factory:
172
- raise ValueError(
173
- "llm_factory is required when using a string evaluator"
174
- )
175
-
176
- evaluator_agent = Agent(
177
- name="Evaluator",
178
- instruction=evaluator,
179
- config=AgentConfig(
180
- name="Evaluator",
181
- instruction=evaluator,
182
- servers=[],
183
- use_history=False,
184
- ),
185
- )
186
- self.evaluator_llm = llm_factory(agent=evaluator_agent)
187
- elif isinstance(evaluator, Agent):
188
- if not llm_factory:
189
- raise ValueError(
190
- "llm_factory is required when using an Agent evaluator"
191
- )
192
-
193
- # Disable history and use/create LLM
194
- evaluator.config.use_history = False
195
- self.evaluator_llm = getattr(evaluator, "_llm", None) or llm_factory(
196
- agent=evaluator
197
- )
198
- elif isinstance(evaluator, AugmentedLLM):
199
- self.evaluator_llm = evaluator
200
- # Ensure history is disabled
201
- if hasattr(self.evaluator_llm, "default_request_params"):
202
- self.evaluator_llm.default_request_params.use_history = False
203
- else:
204
- raise ValueError(f"Unsupported evaluator type: {type(evaluator)}")
205
-
206
- # Track iteration history
207
- self.refinement_history = []
208
-
209
- # Set up workflow's default params
210
- self.default_request_params = self._initialize_default_params({})
211
-
212
- # Ensure evaluator's request params have history disabled
213
- if hasattr(self.evaluator_llm, "default_request_params"):
214
- self.evaluator_llm.default_request_params.use_history = False
215
-
216
- async def generate(
217
- self,
218
- message: str | MessageParamT | List[MessageParamT],
219
- request_params: RequestParams | None = None,
220
- ) -> List[MessageT]:
221
- """Generate an optimized response through evaluation-guided refinement"""
222
- refinement_count = 0
223
- response = None
224
- best_response = None
225
- best_rating = QualityRating.POOR
226
- self.refinement_history = []
227
-
228
- # Get request params with proper use_history setting
229
- params = self.get_request_params(request_params)
230
-
231
- # Use a single AsyncExitStack for the entire method to maintain connections
232
- async with contextlib.AsyncExitStack() as stack:
233
- # Enter all agent contexts once at the beginning
234
- if isinstance(self.generator, Agent):
235
- await stack.enter_async_context(self.generator)
236
- if isinstance(self.evaluator, Agent):
237
- await stack.enter_async_context(self.evaluator)
238
-
239
- # Initial generation - pass parameters to any type of generator
240
- response = await self.generator_llm.generate_str(
241
- message=message,
242
- request_params=params, # Pass params which may override use_history
243
- )
244
-
245
- best_response = response
246
-
247
- while refinement_count < self.max_refinements:
248
- logger.debug("Generator result:", data=response)
249
-
250
- # Evaluate current response
251
- eval_prompt = self._build_eval_prompt(
252
- original_request=str(message),
253
- current_response=response, # response is already a string
254
- iteration=refinement_count,
255
- )
256
-
257
- # No need for nested AsyncExitStack here - using the outer one
258
- evaluation_result = await self.evaluator_llm.generate_structured(
259
- message=eval_prompt,
260
- response_model=EvaluationResult,
261
- request_params=request_params,
262
- )
263
-
264
- # Track iteration
265
- self.refinement_history.append(
266
- {
267
- "attempt": refinement_count + 1,
268
- "response": response,
269
- "evaluation_result": evaluation_result,
270
- }
271
- )
272
-
273
- logger.debug("Evaluator result:", data=evaluation_result)
274
-
275
- # Track best response (using enum ordering)
276
- if evaluation_result.rating.value > best_rating.value:
277
- best_rating = evaluation_result.rating
278
- best_response = response
279
- logger.debug(
280
- "New best response:",
281
- data={"rating": best_rating, "response": best_response},
282
- )
283
-
284
- # Check if we've reached acceptable quality
285
- if (
286
- evaluation_result.rating.value >= self.min_rating.value
287
- or not evaluation_result.needs_improvement
288
- ):
289
- logger.debug(
290
- f"Acceptable quality {evaluation_result.rating.value} reached",
291
- data={
292
- "rating": evaluation_result.rating.value,
293
- "needs_improvement": evaluation_result.needs_improvement,
294
- "min_rating": self.min_rating.value,
295
- },
296
- )
297
- break
298
-
299
- # Generate refined response
300
- refinement_prompt = self._build_refinement_prompt(
301
- original_request=str(message),
302
- current_response=response,
303
- feedback=evaluation_result,
304
- iteration=refinement_count,
305
- use_history=self.generator_use_history, # Use the generator's history setting
306
- )
307
-
308
- # Pass parameters to any type of generator
309
- response = await self.generator_llm.generate_str(
310
- message=refinement_prompt,
311
- request_params=params, # Pass params which may override use_history
312
- )
313
-
314
- refinement_count += 1
315
-
316
- # Return the best response as a list with a single string element
317
- # This makes it consistent with other AugmentedLLM implementations
318
- # that return List[MessageT]
319
- return [best_response]
320
-
321
- async def generate_str(
322
- self,
323
- message: str | MessageParamT | List[MessageParamT],
324
- request_params: RequestParams | None = None,
325
- ) -> str:
326
- """Generate an optimized response and return it as a string"""
327
- response = await self.generate(
328
- message=message,
329
- request_params=request_params,
330
- )
331
- # Since generate now returns [best_response], just return the first element
332
- return str(response[0])
333
-
334
- async def generate_structured(
335
- self,
336
- message: str | MessageParamT | List[MessageParamT],
337
- response_model: Type[ModelT],
338
- request_params: RequestParams | None = None,
339
- ) -> ModelT:
340
- """Generate an optimized structured response"""
341
- response_str = await self.generate_str(
342
- message=message, request_params=request_params
343
- )
344
-
345
- return await self.generator.generate_structured(
346
- message=response_str,
347
- response_model=response_model,
348
- request_params=request_params,
349
- )
350
-
351
- def _build_eval_prompt(
352
- self, original_request: str, current_response: str, iteration: int
353
- ) -> str:
354
- """Build the evaluation prompt for the evaluator"""
355
- return f"""
356
- You are an expert evaluator for content quality. Your task is to evaluate a response against the user's original request.
357
-
358
- Evaluate the response for iteration {iteration + 1} and provide structured feedback on its quality and areas for improvement.
359
-
360
- <fastagent:data>
361
- <fastagent:request>
362
- {original_request}
363
- </fastagent:request>
364
-
365
- <fastagent:response>
366
- {current_response}
367
- </fastagent:response>
368
-
369
- <fastagent:evaluation-criteria>
370
- {self.evaluator.instruction}
371
- </fastagent:evaluation-criteria>
372
- </fastagent:data>
373
-
374
- <fastagent:instruction>
375
- Provide a structured evaluation with the following components:
376
-
377
- <rating>
378
- Choose one: EXCELLENT, GOOD, FAIR, or POOR
379
- - EXCELLENT: No improvements needed
380
- - GOOD: Only minor improvements possible
381
- - FAIR: Several improvements needed
382
- - POOR: Major improvements needed
383
- </rating>
384
-
385
- <details>
386
- Provide specific, actionable feedback and suggestions for improvement.
387
- Be precise about what works well and what could be improved.
388
- </details>
389
-
390
- <needs_improvement>
391
- Indicate true/false whether further improvement is needed.
392
- </needs_improvement>
393
-
394
- <focus-areas>
395
- List 1-3 specific areas to focus on in the next iteration.
396
- Be concrete and actionable in your recommendations.
397
- </focus-areas>
398
- </fastagent:instruction>
399
- """
400
-
401
- def _build_refinement_prompt(
402
- self,
403
- original_request: str,
404
- current_response: str,
405
- feedback: EvaluationResult,
406
- iteration: int,
407
- use_history: bool = None,
408
- ) -> str:
409
- """Build the refinement prompt for the optimizer"""
410
- # Get the correct history setting - use param if provided, otherwise class default
411
- if use_history is None:
412
- use_history = (
413
- self.generator_use_history
414
- ) # Use generator's setting as default
415
-
416
- # Start with clear non-delimited instructions
417
- prompt = f"""
418
- You are tasked with improving a response based on expert feedback. This is iteration {iteration + 1} of the refinement process.
419
-
420
- Your goal is to address all feedback points while maintaining accuracy and relevance to the original request.
421
- """
422
-
423
- # Add data section with all relevant information
424
- prompt += """
425
- <fastagent:data>
426
- """
427
-
428
- # Add request
429
- prompt += f"""
430
- <fastagent:request>
431
- {original_request}
432
- </fastagent:request>
433
- """
434
-
435
- # Only include previous response if history is not enabled
436
- if not use_history:
437
- prompt += f"""
438
- <fastagent:previous-response>
439
- {current_response}
440
- </fastagent:previous-response>
441
- """
442
-
443
- # Always include the feedback
444
- prompt += f"""
445
- <fastagent:feedback>
446
- <rating>{feedback.rating}</rating>
447
- <details>{feedback.feedback}</details>
448
- <focus-areas>{", ".join(feedback.focus_areas) if feedback.focus_areas else "None specified"}</focus-areas>
449
- </fastagent:feedback>
450
- </fastagent:data>
451
- """
452
-
453
- # Customize instruction based on history availability
454
- if not use_history:
455
- prompt += """
456
- <fastagent:instruction>
457
- Create an improved version of the response that:
458
- 1. Directly addresses each point in the feedback
459
- 2. Focuses on the specific areas mentioned for improvement
460
- 3. Maintains all the strengths of the original response
461
- 4. Remains accurate and relevant to the original request
462
-
463
- Provide your complete improved response without explanations or commentary.
464
- </fastagent:instruction>
465
- """
466
- else:
467
- prompt += """
468
- <fastagent:instruction>
469
- Your previous response is available in your conversation history.
470
-
471
- Create an improved version that:
472
- 1. Directly addresses each point in the feedback
473
- 2. Focuses on the specific areas mentioned for improvement
474
- 3. Maintains all the strengths of your original response
475
- 4. Remains accurate and relevant to the original request
476
-
477
- Provide your complete improved response without explanations or commentary.
478
- </fastagent:instruction>
479
- """
480
-
481
- return prompt
File without changes
@@ -1,120 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from typing import Dict, List, Optional, TYPE_CHECKING
3
- from pydantic import BaseModel, Field
4
-
5
- if TYPE_CHECKING:
6
- from mcp_agent.context import Context
7
-
8
-
9
- class Intent(BaseModel):
10
- """A class that represents a single intent category"""
11
-
12
- name: str
13
- """The name of the intent"""
14
-
15
- description: str | None = None
16
- """A description of what this intent represents"""
17
-
18
- examples: List[str] = Field(default_factory=list)
19
- """Example phrases or requests that match this intent"""
20
-
21
- metadata: Dict[str, str] = Field(default_factory=dict)
22
- """Additional metadata about the intent that might be useful for classification"""
23
-
24
-
25
- class IntentClassificationResult(BaseModel):
26
- """A class that represents the result of intent classification"""
27
-
28
- intent: str
29
- """The classified intent name"""
30
-
31
- p_score: float | None = None
32
- """
33
- The probability score (i.e. 0->1) of the classification.
34
- This is optional and may only be provided if the classifier is probabilistic (e.g. a probabilistic binary classifier).
35
- """
36
-
37
- extracted_entities: Dict[str, str] = Field(default_factory=dict)
38
- """Any entities or parameters extracted from the input request that are relevant to the intent"""
39
-
40
-
41
- class IntentClassifier(ABC):
42
- """
43
- Base class for intent classification. This can be implemented using different approaches
44
- like LLMs, embedding models, traditional ML classification models, or rule-based systems.
45
-
46
- When to use this:
47
- - When you need to understand the user's intention before routing or processing
48
- - When you want to extract structured information from natural language inputs
49
- - When you need to handle multiple related but distinct types of requests
50
-
51
- Examples:
52
- - Classifying customer service requests (complaint, question, feedback)
53
- - Understanding user commands in a chat interface
54
- - Determining the type of analysis requested for a dataset
55
- """
56
-
57
- def __init__(
58
- self, intents: List[Intent], context: Optional["Context"] = None, **kwargs
59
- ):
60
- super().__init__(context=context, **kwargs)
61
- self.intents = {intent.name: intent for intent in intents}
62
- self.initialized: bool = False
63
-
64
- if not self.intents:
65
- raise ValueError("At least one intent must be provided")
66
-
67
- @abstractmethod
68
- async def classify(
69
- self, request: str, top_k: int = 1
70
- ) -> List[IntentClassificationResult]:
71
- """
72
- Classify the input request into one or more intents.
73
-
74
- Args:
75
- request: The input text to classify
76
- top_k: Maximum number of top intent matches to return. May return fewer.
77
-
78
- Returns:
79
- List of classification results, ordered by confidence
80
- """
81
-
82
- async def initialize(self):
83
- """Initialize the classifier. Override this method if needed."""
84
- self.initialized = True
85
-
86
-
87
- # Example
88
- # Define some intents
89
- # intents = [
90
- # Intent(
91
- # name="schedule_meeting",
92
- # description="Schedule or set up a meeting or appointment",
93
- # examples=[
94
- # "Can you schedule a meeting with John?",
95
- # "Set up a call for next week",
96
- # "I need to arrange a meeting"
97
- # ]
98
- # ),
99
- # Intent(
100
- # name="check_calendar",
101
- # description="Check calendar availability or existing appointments",
102
- # examples=[
103
- # "What meetings do I have today?",
104
- # "Show me my calendar",
105
- # "Am I free tomorrow afternoon?"
106
- # ]
107
- # )
108
- # ]
109
-
110
- # # Initialize with OpenAI embeddings
111
- # classifier = OpenAIEmbeddingIntentClassifier(intents=intents, model="text-embedding-3-small")
112
-
113
- # # Or use Cohere embeddings
114
- # classifier = OpenAIEmbeddingIntentClassifier(intents=intents, model="embed-multilingual-v3.0")
115
-
116
- # # Classify some text
117
- # results = await classifier.classify(
118
- # request="Can you set up a meeting with Sarah for tomorrow?"
119
- # top_k=3
120
- # )