rasa-pro 3.13.7__py3-none-any.whl → 3.14.0.dev2__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 rasa-pro might be problematic. Click here for more details.
- rasa/agents/__init__.py +0 -0
- rasa/agents/agent_factory.py +122 -0
- rasa/agents/agent_manager.py +162 -0
- rasa/agents/constants.py +31 -0
- rasa/agents/core/__init__.py +0 -0
- rasa/agents/core/agent_protocol.py +108 -0
- rasa/agents/core/types.py +70 -0
- rasa/agents/exceptions.py +8 -0
- rasa/agents/protocol/__init__.py +5 -0
- rasa/agents/protocol/a2a/__init__.py +0 -0
- rasa/agents/protocol/a2a/a2a_agent.py +51 -0
- rasa/agents/protocol/mcp/__init__.py +0 -0
- rasa/agents/protocol/mcp/mcp_base_agent.py +697 -0
- rasa/agents/protocol/mcp/mcp_open_agent.py +275 -0
- rasa/agents/protocol/mcp/mcp_task_agent.py +447 -0
- rasa/agents/schemas/__init__.py +6 -0
- rasa/agents/schemas/agent_input.py +24 -0
- rasa/agents/schemas/agent_output.py +26 -0
- rasa/agents/schemas/agent_tool_result.py +51 -0
- rasa/agents/schemas/agent_tool_schema.py +112 -0
- rasa/agents/templates/__init__.py +0 -0
- rasa/agents/templates/mcp_open_agent_prompt_template.jinja2 +15 -0
- rasa/agents/templates/mcp_task_agent_prompt_template.jinja2 +13 -0
- rasa/agents/utils.py +72 -0
- rasa/api.py +5 -0
- rasa/cli/arguments/default_arguments.py +12 -0
- rasa/cli/arguments/run.py +2 -0
- rasa/cli/dialogue_understanding_test.py +4 -0
- rasa/cli/e2e_test.py +4 -0
- rasa/cli/inspect.py +3 -0
- rasa/cli/llm_fine_tuning.py +5 -0
- rasa/cli/run.py +4 -0
- rasa/cli/shell.py +3 -0
- rasa/cli/train.py +2 -2
- rasa/constants.py +6 -0
- rasa/core/actions/action.py +69 -39
- rasa/core/actions/action_run_slot_rejections.py +1 -1
- rasa/core/agent.py +16 -0
- rasa/core/available_agents.py +196 -0
- rasa/core/available_endpoints.py +30 -0
- rasa/core/channels/development_inspector.py +47 -14
- rasa/core/channels/inspector/dist/assets/{arc-0b11fe30.js → arc-2e78c586.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-9eef30a7.js → blockDiagram-38ab4fdb-806b712e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-03e94f28.js → c4Diagram-3d4e48cf-0745efa9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/channel-c436ca7c.js +1 -0
- rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-95c09eba.js → classDiagram-70f12bd4-7bd1082b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-38e8446c.js → classDiagram-v2-f2320105-d937ba49.js} +1 -1
- rasa/core/channels/inspector/dist/assets/clone-50dd656b.js +1 -0
- rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-57dc3038.js → createText-2e5e7dd3-a2a564ca.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-4bac0545.js → edges-e0da2a9e-b5256940.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-81795c90.js → erDiagram-9861fffd-e6883ad2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-89489ae6.js → flowDb-956e92f1-e576fc02.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-cd152627.js → flowDiagram-66a62f08-2e298d01.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-2b2aeaf8.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-3da369bc.js → flowchart-elk-definition-4a651766-dd7b150a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-85ec16f8.js → ganttDiagram-c361ad54-5b79575c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-495bc140.js → gitGraphDiagram-72cf32ee-3016f40a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{graph-1ec4d266.js → graph-3e19170f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/index-1bd9135e.js +1353 -0
- rasa/core/channels/inspector/dist/assets/{index-3862675e-0a0e97c9.js → index-3862675e-eb9c86de.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-4d54bcde.js → infoDiagram-f8f76790-b4280e4d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-dc097114.js → journeyDiagram-49397b02-556091f8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-1a08981e.js → layout-08436411.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-95f7f1d3.js → line-683c4f3b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-97e69543.js → linear-cee6d791.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-8c71ff03.js → mindmap-definition-fc14e90a-a0bf0b1a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-f14c71c7.js → pieDiagram-8a3498a8-3730d5c4.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-f1d3c9ff.js → quadrantDiagram-120e2f19-12a20fed.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-bfa2412f.js → requirementDiagram-deff3bca-b9732102.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-53f2c97b.js → sankeyDiagram-04a897e0-a2e72776.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-319d7c0e.js → sequenceDiagram-704730f1-8b7a76bb.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-76a09418.js → stateDiagram-587899a1-e65853ac.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-a67f15d4.js → stateDiagram-v2-d93cdb3a-6f58a44b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-0654e7c3.js → styles-6aaf32cf-df25b934.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9a916d00-1394bb9d.js → styles-9a916d00-88357141.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-c10674c1-e4c5bdae.js → styles-c10674c1-d600174d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-50957104.js → svgDrawCommon-08f97a94-4adc3e0b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-b0885a6a.js → timeline-definition-85554ec2-42816fa1.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-79e6541a.js → xychartDiagram-e933f94c-621eb66a.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +2 -2
- rasa/core/channels/inspector/index.html +1 -1
- rasa/core/channels/inspector/src/App.tsx +53 -7
- rasa/core/channels/inspector/src/components/Chat.tsx +3 -2
- rasa/core/channels/inspector/src/components/DiagramFlow.tsx +1 -1
- rasa/core/channels/inspector/src/components/DialogueStack.tsx +7 -5
- rasa/core/channels/inspector/src/components/LatencyDisplay.tsx +268 -0
- rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +6 -2
- rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +8 -3
- rasa/core/channels/inspector/src/helpers/formatters.ts +24 -3
- rasa/core/channels/inspector/src/theme/base/styles.ts +19 -1
- rasa/core/channels/inspector/src/types.ts +12 -0
- rasa/core/channels/studio_chat.py +125 -34
- rasa/core/channels/voice_ready/twilio_voice.py +1 -1
- rasa/core/channels/voice_stream/audiocodes.py +9 -6
- rasa/core/channels/voice_stream/browser_audio.py +39 -4
- rasa/core/channels/voice_stream/call_state.py +13 -2
- rasa/core/channels/voice_stream/genesys.py +16 -13
- rasa/core/channels/voice_stream/jambonz.py +13 -11
- rasa/core/channels/voice_stream/twilio_media_streams.py +14 -13
- rasa/core/channels/voice_stream/util.py +11 -1
- rasa/core/channels/voice_stream/voice_channel.py +101 -29
- rasa/core/constants.py +4 -0
- rasa/core/nlg/contextual_response_rephraser.py +11 -7
- rasa/core/nlg/generator.py +21 -5
- rasa/core/nlg/response.py +43 -6
- rasa/core/nlg/translate.py +8 -0
- rasa/core/policies/enterprise_search_policy.py +4 -2
- rasa/core/policies/flow_policy.py +2 -2
- rasa/core/policies/flows/flow_executor.py +374 -35
- rasa/core/policies/flows/mcp_tool_executor.py +240 -0
- rasa/core/processor.py +6 -1
- rasa/core/run.py +8 -1
- rasa/core/utils.py +21 -1
- rasa/dialogue_understanding/commands/__init__.py +8 -0
- rasa/dialogue_understanding/commands/cancel_flow_command.py +97 -4
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +11 -0
- rasa/dialogue_understanding/commands/clarify_command.py +10 -0
- rasa/dialogue_understanding/commands/continue_agent_command.py +91 -0
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +11 -0
- rasa/dialogue_understanding/commands/restart_agent_command.py +162 -0
- rasa/dialogue_understanding/commands/start_flow_command.py +129 -8
- rasa/dialogue_understanding/commands/utils.py +6 -2
- rasa/dialogue_understanding/generator/command_parser.py +4 -0
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +50 -12
- rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +61 -0
- rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +61 -0
- rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +81 -0
- rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +81 -0
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +7 -6
- rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +7 -6
- rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +41 -2
- rasa/dialogue_understanding/patterns/continue_interrupted.py +163 -1
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +51 -7
- rasa/dialogue_understanding/stack/dialogue_stack.py +123 -2
- rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +57 -0
- rasa/dialogue_understanding/stack/utils.py +3 -2
- rasa/dialogue_understanding_test/du_test_runner.py +7 -2
- rasa/dialogue_understanding_test/du_test_schema.yml +3 -3
- rasa/e2e_test/e2e_test_runner.py +5 -0
- rasa/e2e_test/e2e_test_schema.yml +3 -3
- rasa/model_manager/model_api.py +1 -1
- rasa/model_manager/socket_bridge.py +8 -2
- rasa/server.py +10 -0
- rasa/shared/agents/__init__.py +0 -0
- rasa/shared/agents/utils.py +35 -0
- rasa/shared/constants.py +5 -0
- rasa/shared/core/constants.py +12 -1
- rasa/shared/core/domain.py +5 -5
- rasa/shared/core/events.py +319 -0
- rasa/shared/core/flows/flows_list.py +2 -2
- rasa/shared/core/flows/flows_yaml_schema.json +101 -186
- rasa/shared/core/flows/steps/call.py +51 -5
- rasa/shared/core/flows/validation.py +45 -7
- rasa/shared/core/flows/yaml_flows_io.py +3 -3
- rasa/shared/providers/llm/_base_litellm_client.py +39 -7
- rasa/shared/providers/llm/litellm_router_llm_client.py +8 -4
- rasa/shared/providers/llm/llm_client.py +7 -3
- rasa/shared/providers/llm/llm_response.py +49 -0
- rasa/shared/providers/llm/self_hosted_llm_client.py +8 -4
- rasa/shared/utils/common.py +2 -1
- rasa/shared/utils/llm.py +28 -5
- rasa/shared/utils/mcp/__init__.py +0 -0
- rasa/shared/utils/mcp/server_connection.py +157 -0
- rasa/shared/utils/schemas/events.py +42 -0
- rasa/studio/upload.py +4 -7
- rasa/tracing/instrumentation/instrumentation.py +4 -2
- rasa/utils/common.py +53 -0
- rasa/utils/licensing.py +21 -10
- rasa/utils/plotting.py +1 -1
- rasa/version.py +1 -1
- {rasa_pro-3.13.7.dist-info → rasa_pro-3.14.0.dev2.dist-info}/METADATA +16 -15
- {rasa_pro-3.13.7.dist-info → rasa_pro-3.14.0.dev2.dist-info}/RECORD +175 -138
- rasa/core/channels/inspector/dist/assets/channel-51d02e9e.js +0 -1
- rasa/core/channels/inspector/dist/assets/clone-cc738fa6.js +0 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-0c716443.js +0 -1
- rasa/core/channels/inspector/dist/assets/index-c804b295.js +0 -1335
- {rasa_pro-3.13.7.dist-info → rasa_pro-3.14.0.dev2.dist-info}/NOTICE +0 -0
- {rasa_pro-3.13.7.dist-info → rasa_pro-3.14.0.dev2.dist-info}/WHEEL +0 -0
- {rasa_pro-3.13.7.dist-info → rasa_pro-3.14.0.dev2.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import json
|
|
3
|
+
import re
|
|
4
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
5
|
+
|
|
6
|
+
import structlog
|
|
7
|
+
from jinja2 import Template
|
|
8
|
+
from pypred import Predicate
|
|
9
|
+
|
|
10
|
+
from rasa.agents.constants import (
|
|
11
|
+
KEY_CONTENT,
|
|
12
|
+
KEY_ROLE,
|
|
13
|
+
KEY_TOOL_CALL_ID,
|
|
14
|
+
)
|
|
15
|
+
from rasa.agents.core.types import AgentStatus, ProtocolType
|
|
16
|
+
from rasa.agents.protocol.mcp.mcp_base_agent import MCPBaseAgent
|
|
17
|
+
from rasa.agents.schemas import (
|
|
18
|
+
AgentInput,
|
|
19
|
+
AgentOutput,
|
|
20
|
+
AgentToolResult,
|
|
21
|
+
)
|
|
22
|
+
from rasa.core.available_agents import AgentMCPServerConfig, ProtocolConfig
|
|
23
|
+
from rasa.shared.agents.utils import make_agent_identifier
|
|
24
|
+
from rasa.shared.constants import (
|
|
25
|
+
ROLE_TOOL,
|
|
26
|
+
)
|
|
27
|
+
from rasa.shared.core.events import SlotSet
|
|
28
|
+
from rasa.shared.providers.llm.llm_response import LLMResponse
|
|
29
|
+
|
|
30
|
+
DEFAULT_TASK_AGENT_PROMPT_TEMPLATE = importlib.resources.read_text(
|
|
31
|
+
"rasa.agents.templates", "mcp_task_agent_prompt_template.jinja2"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
structlogger = structlog.get_logger()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class MCPTaskAgent(MCPBaseAgent):
|
|
38
|
+
"""MCPTaskAgent client implementation"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
name: str,
|
|
43
|
+
description: str,
|
|
44
|
+
protocol_type: ProtocolConfig,
|
|
45
|
+
server_configs: List[AgentMCPServerConfig],
|
|
46
|
+
llm_config: Optional[Dict[str, Any]] = None,
|
|
47
|
+
prompt_template: Optional[str] = None,
|
|
48
|
+
timeout: Optional[int] = None,
|
|
49
|
+
max_retries: Optional[int] = None,
|
|
50
|
+
):
|
|
51
|
+
super().__init__(
|
|
52
|
+
name,
|
|
53
|
+
description,
|
|
54
|
+
protocol_type,
|
|
55
|
+
server_configs,
|
|
56
|
+
llm_config,
|
|
57
|
+
prompt_template,
|
|
58
|
+
timeout,
|
|
59
|
+
max_retries,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def protocol_type(self) -> ProtocolType:
|
|
64
|
+
return ProtocolType.MCP_TASK
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def get_default_prompt_template() -> str:
|
|
68
|
+
return DEFAULT_TASK_AGENT_PROMPT_TEMPLATE
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def get_agent_specific_built_in_tools(
|
|
72
|
+
cls, agent_input: AgentInput
|
|
73
|
+
) -> List[Dict[str, Any]]:
|
|
74
|
+
"""Get agentic specific built-in tools."""
|
|
75
|
+
return [
|
|
76
|
+
cls.get_slot_specific_set_slot_tool(slot_name)
|
|
77
|
+
for slot_name in cls._get_slot_names_from_exit_conditions(agent_input)
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def _get_slot_names_from_exit_conditions(cls, agent_input: AgentInput) -> List[str]:
|
|
82
|
+
"""Extract valid slot names from exit conditions."""
|
|
83
|
+
exit_conditions = agent_input.metadata.get("exit_if", [])
|
|
84
|
+
|
|
85
|
+
# Find all unique names matching "slots.<name>"
|
|
86
|
+
extracted_slot_names = {
|
|
87
|
+
name
|
|
88
|
+
for condition in exit_conditions
|
|
89
|
+
for name in re.findall(r"\bslots\.(\w+)", condition)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Keep only slots that actually exist in agent_input.slots
|
|
93
|
+
valid_slot_names = [
|
|
94
|
+
slot_name
|
|
95
|
+
for slot_name in extracted_slot_names
|
|
96
|
+
if slot_name in agent_input.slots
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
structlogger.debug(
|
|
100
|
+
"mcp_task_agent.get_agent_specific_built_in_tools.slot_names",
|
|
101
|
+
exit_conditions=exit_conditions,
|
|
102
|
+
slot_names=valid_slot_names,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
return valid_slot_names
|
|
106
|
+
|
|
107
|
+
@classmethod
|
|
108
|
+
def get_slot_specific_set_slot_tool(cls, slot_name: str) -> Dict[str, Any]:
|
|
109
|
+
"""Get the set slot tool."""
|
|
110
|
+
return {
|
|
111
|
+
"type": "function",
|
|
112
|
+
"function": {
|
|
113
|
+
"name": f"set_slot_{slot_name}",
|
|
114
|
+
# We need to add the description, slot type, and allowed values
|
|
115
|
+
# (if present) to the description as well.
|
|
116
|
+
"description": f"Set the slot {slot_name} to a specific value.",
|
|
117
|
+
"parameters": {
|
|
118
|
+
"type": "object",
|
|
119
|
+
"properties": {
|
|
120
|
+
"slot_value": {
|
|
121
|
+
"type": "string",
|
|
122
|
+
"description": "The value to assign to the slot.",
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
"required": ["slot_value"],
|
|
126
|
+
"additionalProperties": False,
|
|
127
|
+
},
|
|
128
|
+
"strict": True,
|
|
129
|
+
},
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@classmethod
|
|
133
|
+
def _is_exit_conditions_met(
|
|
134
|
+
cls, agent_input: AgentInput, slots: Dict[str, Any]
|
|
135
|
+
) -> Tuple[bool, Optional[str]]:
|
|
136
|
+
"""Check if the exit conditions are met.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
agent_input: The agent input.
|
|
140
|
+
slots: The slots to check the exit conditions against.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
A tuple containing a boolean indicating if the exit conditions are met
|
|
144
|
+
and a string indicating if an internal error occurred.
|
|
145
|
+
"""
|
|
146
|
+
if not slots:
|
|
147
|
+
return False, None
|
|
148
|
+
|
|
149
|
+
exit_conditions = agent_input.metadata.get("exit_if", [])
|
|
150
|
+
current_context = {"slots": slots}
|
|
151
|
+
|
|
152
|
+
internal_error = None
|
|
153
|
+
all_conditions_met = True
|
|
154
|
+
|
|
155
|
+
for condition in exit_conditions:
|
|
156
|
+
try:
|
|
157
|
+
rendered_template = Template(condition).render(current_context)
|
|
158
|
+
predicate = Predicate(rendered_template)
|
|
159
|
+
condition_result = predicate.evaluate(current_context)
|
|
160
|
+
structlogger.debug(
|
|
161
|
+
"mcp_task_agent.is_exit_conditions_met.predicate.result",
|
|
162
|
+
predicate=predicate.description(),
|
|
163
|
+
condition_result=condition_result,
|
|
164
|
+
slots=slots,
|
|
165
|
+
rendered_template=rendered_template,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# All conditions must be met (AND logic)
|
|
169
|
+
if not condition_result:
|
|
170
|
+
all_conditions_met = False
|
|
171
|
+
break
|
|
172
|
+
|
|
173
|
+
except (TypeError, Exception) as e:
|
|
174
|
+
structlogger.error(
|
|
175
|
+
"mcp_task_agent.is_exit_conditions_met.predicate.error",
|
|
176
|
+
predicate=condition,
|
|
177
|
+
error=str(e),
|
|
178
|
+
)
|
|
179
|
+
all_conditions_met = False
|
|
180
|
+
internal_error = str(e)
|
|
181
|
+
break
|
|
182
|
+
|
|
183
|
+
structlogger.debug(
|
|
184
|
+
"mcp_task_agent.is_exit_conditions_met.result",
|
|
185
|
+
all_conditions_met=all_conditions_met,
|
|
186
|
+
internal_error=internal_error,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
return all_conditions_met, internal_error
|
|
190
|
+
|
|
191
|
+
def _get_slot_name_from_tool_name(self, tool_name: str) -> Optional[str]:
|
|
192
|
+
"""Get the slot name from the tool name."""
|
|
193
|
+
match = re.match(r"^set_slot_(\w+)$", tool_name)
|
|
194
|
+
if match:
|
|
195
|
+
return match.group(1)
|
|
196
|
+
return None
|
|
197
|
+
|
|
198
|
+
def _run_set_slot_tool(
|
|
199
|
+
self, slot_name: str, arguments: Dict[str, Any]
|
|
200
|
+
) -> Dict[str, Any]:
|
|
201
|
+
"""Run the set slot tool."""
|
|
202
|
+
slot_value = arguments.get("slot_value")
|
|
203
|
+
|
|
204
|
+
# Handle type conversion for common cases
|
|
205
|
+
if isinstance(slot_value, str):
|
|
206
|
+
# Convert common boolean strings to actual booleans
|
|
207
|
+
if slot_value.lower() == "true":
|
|
208
|
+
slot_value = True
|
|
209
|
+
elif slot_value.lower() == "false":
|
|
210
|
+
slot_value = False
|
|
211
|
+
|
|
212
|
+
return {slot_name: slot_value}
|
|
213
|
+
|
|
214
|
+
def _generate_agent_task_completed_output(
|
|
215
|
+
self,
|
|
216
|
+
agent_input: AgentInput,
|
|
217
|
+
slots: Dict[str, Any],
|
|
218
|
+
tool_results: Dict[str, AgentToolResult],
|
|
219
|
+
) -> AgentOutput:
|
|
220
|
+
"""Generate an agent task completed output."""
|
|
221
|
+
_slot_names_to_be_filled = self._get_slot_names_from_exit_conditions(
|
|
222
|
+
agent_input
|
|
223
|
+
)
|
|
224
|
+
return AgentOutput(
|
|
225
|
+
id=agent_input.id,
|
|
226
|
+
status=AgentStatus.COMPLETED,
|
|
227
|
+
events=[
|
|
228
|
+
SlotSet(slot_name, slot_value)
|
|
229
|
+
for slot_name, slot_value in slots.items()
|
|
230
|
+
if slot_name in _slot_names_to_be_filled
|
|
231
|
+
],
|
|
232
|
+
tool_results=self._get_tool_results_for_agent_output(
|
|
233
|
+
agent_input, tool_results
|
|
234
|
+
),
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
async def send_message(self, agent_input: AgentInput) -> AgentOutput:
|
|
238
|
+
"""Send a message to the LLM and return the response."""
|
|
239
|
+
messages = self.build_messages_for_llm_request(agent_input)
|
|
240
|
+
tool_results: Dict[str, AgentToolResult] = {}
|
|
241
|
+
|
|
242
|
+
_slots = agent_input.slots.copy()
|
|
243
|
+
_available_tools = self.get_available_tools(agent_input)
|
|
244
|
+
_available_tools_names = [tool.name for tool in _available_tools]
|
|
245
|
+
|
|
246
|
+
# Convert available tools to OpenAI JSON format
|
|
247
|
+
tools_in_openai_format = [
|
|
248
|
+
tool.to_openai_json_format() for tool in _available_tools
|
|
249
|
+
]
|
|
250
|
+
|
|
251
|
+
for iteration in range(self.MAX_ITERATIONS):
|
|
252
|
+
try:
|
|
253
|
+
# Make the LLM call using the llm_client
|
|
254
|
+
structlogger.info(
|
|
255
|
+
"mcp_task_agent.send_message.sending_message_to_llm",
|
|
256
|
+
messages=messages,
|
|
257
|
+
event_info=f"Sending message to LLM (iteration {iteration + 1})",
|
|
258
|
+
agent_name=self._name,
|
|
259
|
+
agent_id=str(make_agent_identifier(self._name, self.protocol_type)),
|
|
260
|
+
)
|
|
261
|
+
llm_response = LLMResponse.ensure_llm_response(
|
|
262
|
+
await self.llm_client.acompletion(
|
|
263
|
+
messages, tools=tools_in_openai_format
|
|
264
|
+
)
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
# If no response from LLM, return an error output.
|
|
268
|
+
if llm_response is None or not (
|
|
269
|
+
llm_response.choices or llm_response.tool_calls
|
|
270
|
+
):
|
|
271
|
+
event_info = "No response from LLM."
|
|
272
|
+
structlogger.warning(
|
|
273
|
+
"mcp_task_agent.send_message.no_llm_response",
|
|
274
|
+
event_info=event_info,
|
|
275
|
+
agent_name=self._name,
|
|
276
|
+
agent_id=str(
|
|
277
|
+
make_agent_identifier(self._name, self.protocol_type)
|
|
278
|
+
),
|
|
279
|
+
)
|
|
280
|
+
return AgentOutput(
|
|
281
|
+
id=agent_input.id,
|
|
282
|
+
status=AgentStatus.RECOVERABLE_ERROR,
|
|
283
|
+
error_message=event_info,
|
|
284
|
+
tool_results=self._get_tool_results_for_agent_output(
|
|
285
|
+
agent_input, tool_results
|
|
286
|
+
),
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
# If no tool calls, return the response directly with input required.
|
|
290
|
+
if not llm_response.tool_calls and len(llm_response.choices) == 1:
|
|
291
|
+
return AgentOutput(
|
|
292
|
+
id=agent_input.id,
|
|
293
|
+
status=AgentStatus.INPUT_REQUIRED,
|
|
294
|
+
response_message=llm_response.choices[0],
|
|
295
|
+
tool_results=self._get_tool_results_for_agent_output(
|
|
296
|
+
agent_input, tool_results
|
|
297
|
+
),
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
# If there are tool calls, process them.
|
|
301
|
+
if llm_response.tool_calls:
|
|
302
|
+
# Add the assistant message with tool calls to the messages.
|
|
303
|
+
messages.append(
|
|
304
|
+
self._get_assistant_message_with_tool_calls(llm_response)
|
|
305
|
+
)
|
|
306
|
+
for tool_call in llm_response.tool_calls:
|
|
307
|
+
structlogger.info(
|
|
308
|
+
"mcp_task_agent.send_message.tool_call",
|
|
309
|
+
event_info=(
|
|
310
|
+
f"Processing tool call `{tool_call.tool_name}` with "
|
|
311
|
+
f"args: {tool_call.tool_args}"
|
|
312
|
+
),
|
|
313
|
+
tool_name=tool_call.tool_name,
|
|
314
|
+
tool_args=json.dumps(tool_call.tool_args),
|
|
315
|
+
agent_name=self._name,
|
|
316
|
+
agent_id=str(
|
|
317
|
+
make_agent_identifier(self._name, self.protocol_type)
|
|
318
|
+
),
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
# If the tool is not available, return a fatal error output.
|
|
322
|
+
if tool_call.tool_name not in _available_tools_names:
|
|
323
|
+
event_info = f"Tool {tool_call.tool_name} is not available."
|
|
324
|
+
structlogger.error(
|
|
325
|
+
"mcp_task_agent.send_message.tool_not_available",
|
|
326
|
+
tool_name=tool_call.tool_name,
|
|
327
|
+
event_info=event_info,
|
|
328
|
+
user_message=agent_input.user_message,
|
|
329
|
+
)
|
|
330
|
+
return AgentOutput(
|
|
331
|
+
id=agent_input.id,
|
|
332
|
+
status=AgentStatus.FATAL_ERROR,
|
|
333
|
+
error_message=event_info,
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
if slot_name := self._get_slot_name_from_tool_name(
|
|
337
|
+
tool_call.tool_name
|
|
338
|
+
):
|
|
339
|
+
if (
|
|
340
|
+
slot_name in agent_input.slots
|
|
341
|
+
and "slot_value" in tool_call.tool_args
|
|
342
|
+
):
|
|
343
|
+
_slots.update(
|
|
344
|
+
self._run_set_slot_tool(
|
|
345
|
+
slot_name, tool_call.tool_args
|
|
346
|
+
)
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
# Add the tool call message to the messages for
|
|
350
|
+
# slot-setting tools
|
|
351
|
+
messages.append(
|
|
352
|
+
{
|
|
353
|
+
KEY_ROLE: ROLE_TOOL,
|
|
354
|
+
KEY_TOOL_CALL_ID: tool_call.id,
|
|
355
|
+
KEY_CONTENT: f"Slot {slot_name} set to "
|
|
356
|
+
f"{tool_call.tool_args.get('slot_value')}",
|
|
357
|
+
}
|
|
358
|
+
)
|
|
359
|
+
else:
|
|
360
|
+
return AgentOutput(
|
|
361
|
+
id=agent_input.id,
|
|
362
|
+
status=AgentStatus.FATAL_ERROR,
|
|
363
|
+
error_message=(
|
|
364
|
+
f"The slot `{slot_name}` that the tool "
|
|
365
|
+
f"`{tool_call.tool_name}` is trying to set "
|
|
366
|
+
f"is not found in agent input."
|
|
367
|
+
),
|
|
368
|
+
)
|
|
369
|
+
else:
|
|
370
|
+
# Execute the tool call.
|
|
371
|
+
tool_output = await self._execute_tool_call(
|
|
372
|
+
tool_call.tool_name,
|
|
373
|
+
tool_call.tool_args,
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
# If the tool call failed, generate an agent error output.
|
|
377
|
+
if tool_output.is_error or tool_output.result is None:
|
|
378
|
+
return self._generate_agent_error_output(
|
|
379
|
+
tool_output, agent_input, tool_call
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
# Store the tool output in the tool_results.
|
|
383
|
+
tool_results[tool_call.id] = tool_output
|
|
384
|
+
|
|
385
|
+
# Add the tool call message to the messages.
|
|
386
|
+
messages.append(
|
|
387
|
+
{
|
|
388
|
+
KEY_ROLE: ROLE_TOOL,
|
|
389
|
+
KEY_TOOL_CALL_ID: tool_call.id,
|
|
390
|
+
KEY_CONTENT: tool_output.result,
|
|
391
|
+
}
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
exit_met, internal_error = self._is_exit_conditions_met(
|
|
395
|
+
agent_input, _slots
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
# Agent signals task completion if exit conditions are met.
|
|
399
|
+
if exit_met:
|
|
400
|
+
return self._generate_agent_task_completed_output(
|
|
401
|
+
agent_input, _slots, tool_results
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
# If an internal error occurred while checking the exit
|
|
405
|
+
# conditions, return a fatal error output.
|
|
406
|
+
if internal_error:
|
|
407
|
+
return AgentOutput(
|
|
408
|
+
id=agent_input.id,
|
|
409
|
+
status=AgentStatus.FATAL_ERROR,
|
|
410
|
+
response_message=(
|
|
411
|
+
"An internal error occurred while checking the "
|
|
412
|
+
"exit conditions."
|
|
413
|
+
),
|
|
414
|
+
tool_results=self._get_tool_results_for_agent_output(
|
|
415
|
+
agent_input, tool_results
|
|
416
|
+
),
|
|
417
|
+
error_message=internal_error,
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
except Exception as e:
|
|
421
|
+
structlogger.error(
|
|
422
|
+
"mcp_task_agent.send_message.error_in_agent_loop",
|
|
423
|
+
event_info=f"Failed to send message: {e}",
|
|
424
|
+
user_message=agent_input.user_message,
|
|
425
|
+
agent_name=self._name,
|
|
426
|
+
agent_id=str(make_agent_identifier(self._name, self.protocol_type)),
|
|
427
|
+
)
|
|
428
|
+
return AgentOutput(
|
|
429
|
+
id=agent_input.id,
|
|
430
|
+
status=AgentStatus.FATAL_ERROR,
|
|
431
|
+
response_message=f"I encountered an error: {e!s}",
|
|
432
|
+
tool_results=self._get_tool_results_for_agent_output(
|
|
433
|
+
agent_input, tool_results
|
|
434
|
+
),
|
|
435
|
+
error_message=str(e),
|
|
436
|
+
)
|
|
437
|
+
return AgentOutput(
|
|
438
|
+
id=agent_input.id,
|
|
439
|
+
status=AgentStatus.COMPLETED,
|
|
440
|
+
response_message=(
|
|
441
|
+
"I've completed my research but couldn't provide a final answer within"
|
|
442
|
+
"the allowed steps."
|
|
443
|
+
),
|
|
444
|
+
tool_results=self._get_tool_results_for_agent_output(
|
|
445
|
+
agent_input, tool_results
|
|
446
|
+
),
|
|
447
|
+
)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
from rasa.agents.schemas.agent_input import AgentInput
|
|
2
|
+
from rasa.agents.schemas.agent_output import AgentOutput
|
|
3
|
+
from rasa.agents.schemas.agent_tool_result import AgentToolResult
|
|
4
|
+
from rasa.agents.schemas.agent_tool_schema import AgentToolSchema
|
|
5
|
+
|
|
6
|
+
__all__ = ["AgentInput", "AgentOutput", "AgentToolSchema", "AgentToolResult"]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from rasa.shared.core.events import Event
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AgentInput(BaseModel):
|
|
9
|
+
"""A class that represents the schema of the input to the agent."""
|
|
10
|
+
|
|
11
|
+
id: str
|
|
12
|
+
user_message: str
|
|
13
|
+
slots: Dict[str, Any]
|
|
14
|
+
conversation_history: str
|
|
15
|
+
events: List[Event]
|
|
16
|
+
metadata: Dict[str, Any]
|
|
17
|
+
timestamp: Optional[str] = None
|
|
18
|
+
|
|
19
|
+
class Config:
|
|
20
|
+
"""Skip validation for Event class as pydantic does not know how to
|
|
21
|
+
serialize or handle instances of the class.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
arbitrary_types_allowed = True
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from rasa.agents.core.types import AgentStatus
|
|
6
|
+
from rasa.shared.core.events import SlotSet
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AgentOutput(BaseModel):
|
|
10
|
+
"""A class that represents the schema of the output from the agent."""
|
|
11
|
+
|
|
12
|
+
id: str
|
|
13
|
+
status: AgentStatus
|
|
14
|
+
response_message: Optional[str] = None
|
|
15
|
+
events: Optional[List[SlotSet]] = None
|
|
16
|
+
tool_results: Optional[List[List[Dict[str, Any]]]] = None
|
|
17
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
18
|
+
timestamp: Optional[str] = None
|
|
19
|
+
error_message: Optional[str] = None
|
|
20
|
+
|
|
21
|
+
class Config:
|
|
22
|
+
"""Skip validation for SlotSet class as pydantic does not know how to
|
|
23
|
+
serialize or handle instances of the class.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
arbitrary_types_allowed = True
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from mcp.types import CallToolResult
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AgentToolResult(BaseModel):
|
|
9
|
+
tool_name: str
|
|
10
|
+
result: Optional[str] = None
|
|
11
|
+
is_error: bool = False
|
|
12
|
+
error_message: Optional[str] = None
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def from_mcp_tool_result(
|
|
16
|
+
cls, tool_name: str, tool_result: CallToolResult
|
|
17
|
+
) -> "AgentToolResult":
|
|
18
|
+
if tool_result.isError:
|
|
19
|
+
return cls(
|
|
20
|
+
tool_name=tool_name,
|
|
21
|
+
result=None,
|
|
22
|
+
is_error=tool_result.isError,
|
|
23
|
+
error_message=str(tool_result.content),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# try to use structured content if available
|
|
27
|
+
if tool_result.structuredContent:
|
|
28
|
+
return cls(
|
|
29
|
+
tool_name=tool_name,
|
|
30
|
+
result=json.dumps(tool_result.structuredContent),
|
|
31
|
+
is_error=tool_result.isError,
|
|
32
|
+
)
|
|
33
|
+
# fallback to content if structured content is not available
|
|
34
|
+
elif tool_result.content:
|
|
35
|
+
# TODO try to parse the content using the output schema defined
|
|
36
|
+
# in the tool schema (if present)
|
|
37
|
+
return cls(
|
|
38
|
+
tool_name=tool_name,
|
|
39
|
+
result=json.dumps(
|
|
40
|
+
[content.model_dump() for content in tool_result.content]
|
|
41
|
+
),
|
|
42
|
+
is_error=tool_result.isError,
|
|
43
|
+
)
|
|
44
|
+
# if no content is available, return None
|
|
45
|
+
else:
|
|
46
|
+
return cls(
|
|
47
|
+
tool_name=tool_name,
|
|
48
|
+
result=None,
|
|
49
|
+
is_error=tool_result.isError,
|
|
50
|
+
error_message="No content returned from tool `{tool_name}`",
|
|
51
|
+
)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
from typing import Any, Dict, Optional
|
|
2
|
+
|
|
3
|
+
from mcp import Tool
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
from rasa.agents.constants import (
|
|
7
|
+
TOOL_ADDITIONAL_PROPERTIES_KEY,
|
|
8
|
+
TOOL_DESCRIPTION_KEY,
|
|
9
|
+
TOOL_NAME_KEY,
|
|
10
|
+
TOOL_PARAMETERS_KEY,
|
|
11
|
+
TOOL_PROPERTIES_KEY,
|
|
12
|
+
TOOL_PROPERTY_TYPE_KEY,
|
|
13
|
+
TOOL_REQUIRED_KEY,
|
|
14
|
+
TOOL_STRICT_KEY,
|
|
15
|
+
TOOL_TYPE_FUNCTION_KEY,
|
|
16
|
+
TOOL_TYPE_KEY,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AgentToolSchema(BaseModel):
|
|
21
|
+
name: str
|
|
22
|
+
parameters: dict[str, Any]
|
|
23
|
+
strict: bool
|
|
24
|
+
description: Optional[str] = None
|
|
25
|
+
type: str = "function"
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def from_mcp_tool(cls, tool: Tool) -> "AgentToolSchema":
|
|
29
|
+
"""Convert MCP Tool to AgentToolSchema."""
|
|
30
|
+
parameters = tool.inputSchema.copy() if tool.inputSchema else {}
|
|
31
|
+
|
|
32
|
+
if parameters:
|
|
33
|
+
cls._validate_and_fix_parameters(parameters)
|
|
34
|
+
|
|
35
|
+
return cls(
|
|
36
|
+
name=tool.name,
|
|
37
|
+
description=tool.description,
|
|
38
|
+
parameters=parameters,
|
|
39
|
+
strict=False,
|
|
40
|
+
type=TOOL_TYPE_FUNCTION_KEY,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def from_openai_json_format(cls, tool: Dict[str, Any]) -> "AgentToolSchema":
|
|
45
|
+
"""Convert OpenAI dict format to AgentToolSchema."""
|
|
46
|
+
function_data = tool[TOOL_TYPE_FUNCTION_KEY]
|
|
47
|
+
parameters = function_data.get(TOOL_PARAMETERS_KEY, {})
|
|
48
|
+
|
|
49
|
+
if parameters:
|
|
50
|
+
cls._validate_and_fix_parameters(parameters)
|
|
51
|
+
|
|
52
|
+
return cls(
|
|
53
|
+
name=function_data[TOOL_NAME_KEY],
|
|
54
|
+
description=function_data[TOOL_DESCRIPTION_KEY],
|
|
55
|
+
parameters=parameters,
|
|
56
|
+
strict=function_data.get(TOOL_STRICT_KEY, False),
|
|
57
|
+
type=tool[TOOL_TYPE_KEY],
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def _validate_and_fix_parameters(parameters: Dict[str, Any]) -> None:
|
|
62
|
+
"""Validate and fix parameters to ensure they meet OpenAI function calling
|
|
63
|
+
requirements."""
|
|
64
|
+
if not parameters:
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
# Ensure additionalProperties is set at the top level
|
|
68
|
+
if TOOL_ADDITIONAL_PROPERTIES_KEY not in parameters:
|
|
69
|
+
parameters[TOOL_ADDITIONAL_PROPERTIES_KEY] = False
|
|
70
|
+
|
|
71
|
+
# Ensure required is set at the top level
|
|
72
|
+
if TOOL_REQUIRED_KEY not in parameters:
|
|
73
|
+
parameters[TOOL_REQUIRED_KEY] = []
|
|
74
|
+
|
|
75
|
+
# Ensure all properties have types, required field and additionalProperties
|
|
76
|
+
if TOOL_PROPERTIES_KEY in parameters:
|
|
77
|
+
AgentToolSchema._ensure_property_types(parameters)
|
|
78
|
+
|
|
79
|
+
@staticmethod
|
|
80
|
+
def _ensure_property_types(parameters: Dict[str, Any]) -> None:
|
|
81
|
+
"""Ensure all properties in parameters have a type defined and
|
|
82
|
+
additionalProperties is set."""
|
|
83
|
+
properties = parameters[TOOL_PROPERTIES_KEY]
|
|
84
|
+
|
|
85
|
+
if not properties:
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
for _, prop_schema in properties.items():
|
|
89
|
+
if not isinstance(prop_schema, dict):
|
|
90
|
+
continue
|
|
91
|
+
|
|
92
|
+
# Ensure the property has a type
|
|
93
|
+
if TOOL_PROPERTY_TYPE_KEY not in prop_schema:
|
|
94
|
+
prop_schema[TOOL_PROPERTY_TYPE_KEY] = "string"
|
|
95
|
+
|
|
96
|
+
# If it's an object type, ensure additionalProperties and required
|
|
97
|
+
# fields are set
|
|
98
|
+
if prop_schema[TOOL_PROPERTY_TYPE_KEY] == "object":
|
|
99
|
+
if TOOL_ADDITIONAL_PROPERTIES_KEY not in prop_schema:
|
|
100
|
+
prop_schema[TOOL_ADDITIONAL_PROPERTIES_KEY] = False
|
|
101
|
+
|
|
102
|
+
# Ensure required key exists for object properties
|
|
103
|
+
if TOOL_REQUIRED_KEY not in prop_schema:
|
|
104
|
+
prop_schema[TOOL_REQUIRED_KEY] = []
|
|
105
|
+
|
|
106
|
+
def to_openai_json_format(self) -> Dict[str, Any]:
|
|
107
|
+
"""Convert AgentToolSchema to OpenAI format."""
|
|
108
|
+
# Ensure the schema is valid before conversion
|
|
109
|
+
return {
|
|
110
|
+
TOOL_TYPE_KEY: TOOL_TYPE_FUNCTION_KEY,
|
|
111
|
+
TOOL_TYPE_FUNCTION_KEY: self.model_dump(exclude={TOOL_TYPE_KEY}),
|
|
112
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
You are a helpful assistant that should assist the user in the best possible way.
|
|
2
|
+
|
|
3
|
+
### Primary Task
|
|
4
|
+
{{ description }}
|
|
5
|
+
|
|
6
|
+
### Instructions
|
|
7
|
+
* Always make sure to output responses to the user in a clear, helpful format.
|
|
8
|
+
* Always avoid asking multiple questions at once. Ask questions sequentially one at a time and wait for the user's response before proceeding to next question.
|
|
9
|
+
* Always avoid making assumptions about what values to pass into tools. Ask for clarification if a user's request is ambiguous.
|
|
10
|
+
* Once your primary task, described above, is completed, you must call the `task_completed` tool to signal that you have finished assisting the user. Do not end the conversation or indicate completion without the `task_completed` tool call.
|
|
11
|
+
* Do NOT call the `task_completed` tool unless you're absolutely certain that your primary task (NOT the slot corrections, updates, etc), described above, is fully completed. Focus on the task given in the task description above.
|
|
12
|
+
* Strictly avoid making up information or ability to take some action which is not available in `tool` provided.
|
|
13
|
+
|
|
14
|
+
### Conversation history
|
|
15
|
+
{{ conversation_history }}
|