solace-agent-mesh 0.1.2__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.
Potentially problematic release.
This version of solace-agent-mesh might be problematic. Click here for more details.
- solace_agent_mesh/agents/base_agent_component.py +2 -0
- solace_agent_mesh/agents/global/actions/plantuml_diagram.py +14 -2
- solace_agent_mesh/agents/global/actions/plotly_graph.py +49 -40
- solace_agent_mesh/agents/web_request/actions/do_web_request.py +34 -33
- solace_agent_mesh/cli/__init__.py +1 -1
- solace_agent_mesh/cli/commands/add/gateway.py +162 -9
- solace_agent_mesh/cli/commands/build.py +0 -1
- solace_agent_mesh/cli/commands/init/builtin_agent_step.py +1 -6
- solace_agent_mesh/cli/commands/init/create_config_file_step.py +5 -0
- solace_agent_mesh/cli/commands/init/create_other_project_files_step.py +52 -1
- solace_agent_mesh/cli/commands/init/init.py +1 -5
- solace_agent_mesh/cli/commands/init/project_structure_step.py +0 -29
- solace_agent_mesh/cli/commands/plugin/add.py +3 -1
- solace_agent_mesh/cli/commands/plugin/build.py +11 -2
- solace_agent_mesh/cli/commands/plugin/plugin.py +20 -5
- solace_agent_mesh/cli/commands/plugin/remove.py +3 -1
- solace_agent_mesh/cli/config.py +4 -0
- solace_agent_mesh/cli/utils.py +7 -2
- solace_agent_mesh/common/action_response.py +13 -0
- solace_agent_mesh/common/constants.py +12 -0
- solace_agent_mesh/common/postgres_database.py +11 -5
- solace_agent_mesh/common/utils.py +16 -11
- solace_agent_mesh/configs/monitor_stim_and_errors_to_slack.yaml +3 -0
- solace_agent_mesh/configs/service_embedding.yaml +1 -1
- solace_agent_mesh/configs/service_llm.yaml +1 -1
- solace_agent_mesh/gateway/components/gateway_base.py +7 -1
- solace_agent_mesh/gateway/components/gateway_input.py +8 -5
- solace_agent_mesh/gateway/components/gateway_output.py +12 -3
- solace_agent_mesh/orchestrator/action_manager.py +13 -1
- solace_agent_mesh/orchestrator/components/orchestrator_stimulus_processor_component.py +25 -5
- solace_agent_mesh/orchestrator/orchestrator_prompt.py +155 -35
- solace_agent_mesh/services/file_service/file_service.py +5 -0
- solace_agent_mesh/services/file_service/file_service_constants.py +1 -1
- solace_agent_mesh/services/file_service/file_transformations.py +11 -1
- solace_agent_mesh/services/file_service/file_utils.py +2 -0
- solace_agent_mesh/services/history_service/history_providers/base_history_provider.py +21 -45
- solace_agent_mesh/services/history_service/history_providers/file_history_provider.py +74 -0
- solace_agent_mesh/services/history_service/history_providers/index.py +40 -0
- solace_agent_mesh/services/history_service/history_providers/memory_history_provider.py +19 -153
- solace_agent_mesh/services/history_service/history_providers/mongodb_history_provider.py +66 -0
- solace_agent_mesh/services/history_service/history_providers/redis_history_provider.py +40 -137
- solace_agent_mesh/services/history_service/history_providers/sql_history_provider.py +93 -0
- solace_agent_mesh/services/history_service/history_service.py +315 -41
- solace_agent_mesh/services/history_service/long_term_memory/__init__.py +0 -0
- solace_agent_mesh/services/history_service/long_term_memory/long_term_memory.py +399 -0
- solace_agent_mesh/services/llm_service/components/llm_request_component.py +24 -0
- solace_agent_mesh/templates/gateway-config-template.yaml +2 -1
- solace_agent_mesh/templates/gateway-default-config.yaml +3 -3
- solace_agent_mesh/templates/plugin-gateway-default-config.yaml +29 -0
- solace_agent_mesh/templates/rest-api-default-config.yaml +2 -1
- solace_agent_mesh/templates/slack-default-config.yaml +1 -1
- solace_agent_mesh/templates/web-default-config.yaml +2 -1
- {solace_agent_mesh-0.1.2.dist-info → solace_agent_mesh-0.2.0.dist-info}/METADATA +38 -8
- {solace_agent_mesh-0.1.2.dist-info → solace_agent_mesh-0.2.0.dist-info}/RECORD +57 -52
- solace_agent_mesh/cli/commands/init/rest_api_step.py +0 -50
- solace_agent_mesh/cli/commands/init/web_ui_step.py +0 -40
- {solace_agent_mesh-0.1.2.dist-info → solace_agent_mesh-0.2.0.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-0.1.2.dist-info → solace_agent_mesh-0.2.0.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-0.1.2.dist-info → solace_agent_mesh-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -37,7 +37,9 @@ def add_command(name: str, installer: str = None, from_url=None, add_all=False):
|
|
|
37
37
|
f"Module '{name}' not found. Attempting to install '{install_name}' using {installer}..."
|
|
38
38
|
)
|
|
39
39
|
if installer == "pip":
|
|
40
|
-
subprocess.check_call(["
|
|
40
|
+
subprocess.check_call(["pip3", "install", install_name])
|
|
41
|
+
elif installer == "uv":
|
|
42
|
+
subprocess.check_call(["uv", "pip", "install", install_name])
|
|
41
43
|
elif installer == "poetry":
|
|
42
44
|
subprocess.check_call(["poetry", "add", install_name])
|
|
43
45
|
elif installer == "conda":
|
|
@@ -11,7 +11,7 @@ def build_command():
|
|
|
11
11
|
subprocess.check_call(["python", "-m", "build"])
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def get_all_plugin_gateway_interfaces(config, abort):
|
|
14
|
+
def get_all_plugin_gateway_interfaces(config, abort, return_plugin_config=False):
|
|
15
15
|
plugins = config.get("plugins", [])
|
|
16
16
|
gateway_interfaces = {}
|
|
17
17
|
|
|
@@ -27,6 +27,8 @@ def get_all_plugin_gateway_interfaces(config, abort):
|
|
|
27
27
|
if not os.path.exists(interface_path):
|
|
28
28
|
continue
|
|
29
29
|
|
|
30
|
+
interface_gateway_configs = (Config.load_config(os.path.join(plugin_path, Config.user_plugin_config_file)) or {}).get("solace_agent_mesh_plugin", {}).get("interface_gateway_configs", {})
|
|
31
|
+
interface_gateway_config = None
|
|
30
32
|
# Ensuring flow and default pair exist
|
|
31
33
|
interface_pairs = {}
|
|
32
34
|
for file in os.listdir(interface_path):
|
|
@@ -44,7 +46,14 @@ def get_all_plugin_gateway_interfaces(config, abort):
|
|
|
44
46
|
|
|
45
47
|
for name, files in interface_pairs.items():
|
|
46
48
|
if len(files) == 2:
|
|
47
|
-
|
|
49
|
+
if return_plugin_config:
|
|
50
|
+
for interface_name, interface_config in interface_gateway_configs.items():
|
|
51
|
+
if interface_name == name.replace("-", "_"):
|
|
52
|
+
interface_gateway_config = interface_config
|
|
53
|
+
break
|
|
54
|
+
gateway_interfaces[name] = (interface_path, interface_gateway_config)
|
|
55
|
+
else:
|
|
56
|
+
gateway_interfaces[name] = interface_path
|
|
48
57
|
|
|
49
58
|
return gateway_interfaces
|
|
50
59
|
|
|
@@ -44,6 +44,7 @@ def plugin_command(plugin):
|
|
|
44
44
|
@click.argument("name")
|
|
45
45
|
@click.option("--add-all", is_flag=True, help="Added the plugin with default of loading all exported files from the plugin")
|
|
46
46
|
@click.option("--pip", is_flag=True, help="Install with pip.")
|
|
47
|
+
@click.option("--uv", is_flag=True, help="Install with uv pip.")
|
|
47
48
|
@click.option("--poetry", is_flag=True, help="Install with poetry.")
|
|
48
49
|
@click.option("--conda", is_flag=True, help="Install with conda.")
|
|
49
50
|
@click.option(
|
|
@@ -51,7 +52,7 @@ def plugin_command(plugin):
|
|
|
51
52
|
"--from-url",
|
|
52
53
|
help="Install the plugin from a the given URL instead of the given name. (URL can be a file path or a git URL)",
|
|
53
54
|
)
|
|
54
|
-
def add(name, add_all, pip, poetry, conda, from_url):
|
|
55
|
+
def add(name, add_all, uv, pip, poetry, conda, from_url):
|
|
55
56
|
"""
|
|
56
57
|
Add a new plugin to solace-agent-mesh config yaml.
|
|
57
58
|
Optional install the module if not found.
|
|
@@ -59,10 +60,16 @@ def plugin_command(plugin):
|
|
|
59
60
|
Only one installation method can be selected at a time.
|
|
60
61
|
"""
|
|
61
62
|
# Only one option can be true at a time
|
|
62
|
-
if sum([pip, poetry, conda]) > 1:
|
|
63
|
+
if sum([uv, pip, poetry, conda]) > 1:
|
|
63
64
|
log_error("Only one installation method can be selected.")
|
|
64
65
|
return 1
|
|
65
|
-
installer =
|
|
66
|
+
installer = (
|
|
67
|
+
"uv" if uv
|
|
68
|
+
else "pip" if pip
|
|
69
|
+
else "poetry" if poetry
|
|
70
|
+
else "conda" if conda
|
|
71
|
+
else None
|
|
72
|
+
)
|
|
66
73
|
return add_command(name, installer, from_url, add_all)
|
|
67
74
|
|
|
68
75
|
@plugin.command()
|
|
@@ -73,6 +80,12 @@ def plugin_command(plugin):
|
|
|
73
80
|
is_flag=True,
|
|
74
81
|
help="Removes the plugin module using pip",
|
|
75
82
|
)
|
|
83
|
+
@click.option(
|
|
84
|
+
"--uv-uninstall",
|
|
85
|
+
default=False,
|
|
86
|
+
is_flag=True,
|
|
87
|
+
help="Removes the plugin module using uv.",
|
|
88
|
+
)
|
|
76
89
|
@click.option(
|
|
77
90
|
"--poetry-uninstall",
|
|
78
91
|
default=False,
|
|
@@ -85,7 +98,7 @@ def plugin_command(plugin):
|
|
|
85
98
|
is_flag=True,
|
|
86
99
|
help="Removes the plugin module using conda",
|
|
87
100
|
)
|
|
88
|
-
def remove(name, pip_uninstall, poetry_uninstall, conda_uninstall):
|
|
101
|
+
def remove(name, pip_uninstall, uv_uninstall, poetry_uninstall, conda_uninstall):
|
|
89
102
|
"""
|
|
90
103
|
Remove a plugin by removing it from solace-agent-mesh config yaml
|
|
91
104
|
Optionally uninstall the module.
|
|
@@ -93,13 +106,15 @@ def plugin_command(plugin):
|
|
|
93
106
|
Only one uninstallation method can be selected at a time.
|
|
94
107
|
"""
|
|
95
108
|
# Only one option can be true at a time
|
|
96
|
-
if sum([pip_uninstall, poetry_uninstall, conda_uninstall]) > 1:
|
|
109
|
+
if sum([pip_uninstall, uv_uninstall, poetry_uninstall, conda_uninstall]) > 1:
|
|
97
110
|
log_error("Only one uninstallation method can be selected.")
|
|
98
111
|
return 1
|
|
99
112
|
|
|
100
113
|
installer = (
|
|
101
114
|
"pip"
|
|
102
115
|
if pip_uninstall
|
|
116
|
+
else "uv"
|
|
117
|
+
if uv_uninstall
|
|
103
118
|
else "poetry"
|
|
104
119
|
if poetry_uninstall
|
|
105
120
|
else "conda"
|
|
@@ -48,7 +48,9 @@ def remove_command(name: str, installer: str = None):
|
|
|
48
48
|
click.echo(f"Attempting to uninstall module '{name}' using {installer}...")
|
|
49
49
|
try:
|
|
50
50
|
if installer == "pip":
|
|
51
|
-
subprocess.check_call(["
|
|
51
|
+
subprocess.check_call(["pip3", "uninstall", "-y", name])
|
|
52
|
+
elif installer == "uv":
|
|
53
|
+
subprocess.check_call(["uv", "pip", "uninstall", "-y", name])
|
|
52
54
|
elif installer == "poetry":
|
|
53
55
|
subprocess.check_call(["poetry", "remove", name])
|
|
54
56
|
elif installer == "conda":
|
solace_agent_mesh/cli/config.py
CHANGED
solace_agent_mesh/cli/utils.py
CHANGED
|
@@ -42,7 +42,7 @@ def literal_format_template(template, literals):
|
|
|
42
42
|
return template
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
def load_template(name, format_pair={}):
|
|
45
|
+
def load_template(name, format_pair={}, parser=None):
|
|
46
46
|
"""Load a template file and format it with the given key-value pairs."""
|
|
47
47
|
# Construct the path to the template file using a relative path
|
|
48
48
|
template_file = os.path.normpath(
|
|
@@ -53,7 +53,10 @@ def load_template(name, format_pair={}):
|
|
|
53
53
|
return None
|
|
54
54
|
|
|
55
55
|
with open(template_file, "r", encoding="utf-8") as f:
|
|
56
|
-
|
|
56
|
+
if parser:
|
|
57
|
+
file = parser(f)
|
|
58
|
+
else:
|
|
59
|
+
file = f.read()
|
|
57
60
|
|
|
58
61
|
file = literal_format_template(file, format_pair)
|
|
59
62
|
|
|
@@ -75,6 +78,8 @@ def get_display_path(path):
|
|
|
75
78
|
def log_error(message):
|
|
76
79
|
click.echo(click.style(message, fg="red"), err=True)
|
|
77
80
|
|
|
81
|
+
def log_warning(message):
|
|
82
|
+
click.echo(click.style(message, fg="yellow"), err=False)
|
|
78
83
|
|
|
79
84
|
def log_link(message):
|
|
80
85
|
click.echo(click.style(message, fg="blue"), err=False)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""This is the definition of responses for the actions of the system."""
|
|
2
2
|
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
3
5
|
|
|
4
6
|
class RagMatch:
|
|
5
7
|
|
|
@@ -212,6 +214,8 @@ class ActionResponse:
|
|
|
212
214
|
self._is_async: bool = is_async
|
|
213
215
|
# async_response_id - unique identifier for correlating async responses
|
|
214
216
|
self._async_response_id: str = async_response_id
|
|
217
|
+
# originator - the component that originated the action request
|
|
218
|
+
self._originator: Optional[str] = None
|
|
215
219
|
|
|
216
220
|
@property
|
|
217
221
|
def message(self) -> any:
|
|
@@ -269,6 +273,10 @@ class ActionResponse:
|
|
|
269
273
|
def action_params(self) -> dict:
|
|
270
274
|
return self._action_params
|
|
271
275
|
|
|
276
|
+
@property
|
|
277
|
+
def originator(self) -> dict:
|
|
278
|
+
return self._originator
|
|
279
|
+
|
|
272
280
|
@action_list_id.setter
|
|
273
281
|
def action_list_id(self, action_list_id: str):
|
|
274
282
|
self._action_list_id = action_list_id
|
|
@@ -285,6 +293,10 @@ class ActionResponse:
|
|
|
285
293
|
def action_params(self, action_params: dict):
|
|
286
294
|
self._action_params = action_params
|
|
287
295
|
|
|
296
|
+
@originator.setter
|
|
297
|
+
def originator(self, originator: str):
|
|
298
|
+
self._originator = originator
|
|
299
|
+
|
|
288
300
|
@property
|
|
289
301
|
def is_async(self) -> bool:
|
|
290
302
|
return self._is_async
|
|
@@ -324,4 +336,5 @@ class ActionResponse:
|
|
|
324
336
|
response["action_idx"] = self._action_idx
|
|
325
337
|
response["action_name"] = self._action_name
|
|
326
338
|
response["action_params"] = self._action_params
|
|
339
|
+
response["originator"] = self._originator
|
|
327
340
|
return response
|
|
@@ -1,3 +1,15 @@
|
|
|
1
1
|
SOLACE_AGENT_MESH_SYSTEM_SESSION_ID = "solace_agent_mesh_system_session_id"
|
|
2
2
|
|
|
3
3
|
DEFAULT_IDENTITY_KEY_FIELD = "identity"
|
|
4
|
+
|
|
5
|
+
ORCHESTRATOR_COMPONENT_NAME = "orchestrator"
|
|
6
|
+
|
|
7
|
+
HISTORY_MEMORY_ROLE = "history"
|
|
8
|
+
|
|
9
|
+
HISTORY_ACTION_ROLE = "tool_call"
|
|
10
|
+
|
|
11
|
+
HISTORY_USER_ROLE = "user"
|
|
12
|
+
|
|
13
|
+
HISTORY_SYSTEM_ROLE = "system"
|
|
14
|
+
|
|
15
|
+
HISTORY_ASSISTANT_ROLE = "assistant"
|
|
@@ -56,11 +56,17 @@ class PostgreSQLDatabase:
|
|
|
56
56
|
|
|
57
57
|
return cursor
|
|
58
58
|
|
|
59
|
-
def get_db_for_action(action_obj):
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
def get_db_for_action(action_obj, sql_params=None):
|
|
60
|
+
if sql_params:
|
|
61
|
+
sql_host = sql_params.get("sql_host")
|
|
62
|
+
sql_user = sql_params.get("sql_user")
|
|
63
|
+
sql_password = sql_params.get("sql_password")
|
|
64
|
+
sql_database = sql_params.get("sql_database")
|
|
65
|
+
else:
|
|
66
|
+
sql_host = action_obj.get_config("sql_host")
|
|
67
|
+
sql_user = action_obj.get_config("sql_user")
|
|
68
|
+
sql_password = action_obj.get_config("sql_password")
|
|
69
|
+
sql_database = action_obj.get_config("sql_database")
|
|
64
70
|
sql_db = None
|
|
65
71
|
|
|
66
72
|
if sql_host and sql_user and sql_password and sql_database:
|
|
@@ -139,17 +139,22 @@ def parse_file_content(file_xml: str) -> dict:
|
|
|
139
139
|
"""
|
|
140
140
|
Parse the xml tags in the content and return a dictionary of the content.
|
|
141
141
|
"""
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
142
|
+
try:
|
|
143
|
+
ignore_content_tags = ["data"]
|
|
144
|
+
file_dict = xml_to_dict(file_xml, ignore_content_tags)
|
|
145
|
+
dict_keys = list(file_dict.keys())
|
|
146
|
+
top_key = [key for key in dict_keys if key not in ignore_content_tags][0]
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
"data": file_dict.get("data", {}).get("data", ""),
|
|
150
|
+
"url": file_dict.get(top_key, {}).get("url", {}).get("url", ""),
|
|
151
|
+
"mime_type": file_dict.get(top_key, {}).get("mime_type", ""),
|
|
152
|
+
"name": file_dict.get(top_key, {}).get("name", ""),
|
|
153
|
+
}
|
|
154
|
+
except Exception as e:
|
|
155
|
+
result = {"data": "", "url": "", "mime_type": "", "name": ""}
|
|
156
|
+
log.error("Error parsing file content: %s", e)
|
|
157
|
+
return result
|
|
153
158
|
|
|
154
159
|
|
|
155
160
|
def parse_llm_output(llm_output: str) -> dict:
|
|
@@ -99,6 +99,9 @@ flows:
|
|
|
99
99
|
- type: copy
|
|
100
100
|
source_value: '0'
|
|
101
101
|
dest_expression: user_data.output:payload.action_idx
|
|
102
|
+
- type: copy
|
|
103
|
+
source_value: 'stim_and_error_monitor'
|
|
104
|
+
dest_expression: user_data.output:payload.originator
|
|
102
105
|
- type: copy
|
|
103
106
|
source_expression: template:${SOLACE_AGENT_MESH_NAMESPACE}solace-agent-mesh/v1/actionRequest/monitor/x/slack/post_message/{{text://input.payload:correlation_id}}
|
|
104
107
|
dest_expression: user_data.output:topic
|
|
@@ -3,15 +3,17 @@ from solace_ai_connector.common.log import log
|
|
|
3
3
|
from ...services.history_service import HistoryService
|
|
4
4
|
from ..identity.identity_provider import IdentityProvider
|
|
5
5
|
from ...common.constants import DEFAULT_IDENTITY_KEY_FIELD
|
|
6
|
+
from ...orchestrator.orchestrator_prompt import LONG_TERM_MEMORY_PROMPT
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class GatewayBase(ComponentBase):
|
|
9
10
|
def __init__(self, info, **kwargs):
|
|
10
11
|
super().__init__(info, **kwargs)
|
|
11
12
|
self.gateway_id = self.get_config("gateway_id", "default-change-me")
|
|
13
|
+
self.system_purpose_prompt_suffix = ""
|
|
12
14
|
self.history_instance = self._initialize_history()
|
|
13
15
|
|
|
14
|
-
def _initialize_history(self):
|
|
16
|
+
def _initialize_history(self) -> HistoryService:
|
|
15
17
|
self.use_history = self.get_config("retain_history", True)
|
|
16
18
|
|
|
17
19
|
if not self.use_history:
|
|
@@ -19,6 +21,9 @@ class GatewayBase(ComponentBase):
|
|
|
19
21
|
|
|
20
22
|
history_config = self.get_config("history_config", {})
|
|
21
23
|
|
|
24
|
+
if history_config.get("enable_long_term_memory", False):
|
|
25
|
+
self.system_purpose_prompt_suffix = LONG_TERM_MEMORY_PROMPT
|
|
26
|
+
|
|
22
27
|
try:
|
|
23
28
|
return HistoryService(
|
|
24
29
|
history_config, identifier=self.gateway_id + "_history"
|
|
@@ -27,6 +32,7 @@ class GatewayBase(ComponentBase):
|
|
|
27
32
|
log.error("Failed to load history class: %s", e)
|
|
28
33
|
raise
|
|
29
34
|
|
|
35
|
+
|
|
30
36
|
def _initialize_identity_component(self):
|
|
31
37
|
identity_config = self.get_config("identity", {})
|
|
32
38
|
identity_key_field = self.get_config("identity_key_field", DEFAULT_IDENTITY_KEY_FIELD)
|
|
@@ -6,7 +6,7 @@ from uuid import uuid4
|
|
|
6
6
|
from solace_ai_connector.common.message import Message
|
|
7
7
|
from solace_ai_connector.common.log import log
|
|
8
8
|
from ...services.file_service import FileService
|
|
9
|
-
from ...common.constants import DEFAULT_IDENTITY_KEY_FIELD
|
|
9
|
+
from ...common.constants import DEFAULT_IDENTITY_KEY_FIELD, HISTORY_USER_ROLE
|
|
10
10
|
from .gateway_base import GatewayBase
|
|
11
11
|
|
|
12
12
|
info = {
|
|
@@ -130,7 +130,7 @@ class GatewayInput(GatewayBase):
|
|
|
130
130
|
|
|
131
131
|
def __init__(self, **kwargs):
|
|
132
132
|
super().__init__(info, **kwargs)
|
|
133
|
-
self.system_purpose = self.get_config("system_purpose", DEFAULT_SYSTEM_PURPOSE)
|
|
133
|
+
self.system_purpose = self.get_config("system_purpose", DEFAULT_SYSTEM_PURPOSE) + self.system_purpose_prompt_suffix
|
|
134
134
|
self.interaction_type = self.get_config(
|
|
135
135
|
"interaction_type", DEFAULT_INTERACTION_TYPE
|
|
136
136
|
)
|
|
@@ -209,17 +209,20 @@ class GatewayInput(GatewayBase):
|
|
|
209
209
|
|
|
210
210
|
copied_data["history"] = []
|
|
211
211
|
if self.use_history:
|
|
212
|
+
other_history_props = {
|
|
213
|
+
"identity": identity_value,
|
|
214
|
+
}
|
|
212
215
|
prompt = data.get("text", "")
|
|
213
|
-
self.history_instance.store_history(session_id,
|
|
216
|
+
self.history_instance.store_history(session_id, HISTORY_USER_ROLE, prompt, other_history_props)
|
|
214
217
|
|
|
215
218
|
for file in attached_files:
|
|
216
|
-
self.history_instance.store_file(session_id, file)
|
|
219
|
+
self.history_instance.store_file(session_id, file )
|
|
217
220
|
|
|
218
221
|
# retrieve all files for the session
|
|
219
222
|
available_files = self.history_instance.get_files(session_id)
|
|
220
223
|
|
|
221
224
|
# Add history to the data
|
|
222
|
-
copied_data["history"] = self.history_instance.get_history(session_id)
|
|
225
|
+
copied_data["history"] = self.history_instance.get_history(session_id, other_history_props)
|
|
223
226
|
|
|
224
227
|
available_files = json.dumps(available_files)
|
|
225
228
|
except Exception as e:
|
|
@@ -5,7 +5,7 @@ from solace_ai_connector.common.log import log
|
|
|
5
5
|
from .gateway_base import GatewayBase
|
|
6
6
|
from ...services.file_service import FileService
|
|
7
7
|
from ...common.utils import files_to_block_text
|
|
8
|
-
|
|
8
|
+
from ...common.constants import HISTORY_ASSISTANT_ROLE
|
|
9
9
|
|
|
10
10
|
info = {
|
|
11
11
|
"class_name": "GatewayOutput",
|
|
@@ -170,6 +170,7 @@ class GatewayOutput(GatewayBase):
|
|
|
170
170
|
file_service = FileService()
|
|
171
171
|
user_properties = message.get_user_properties()
|
|
172
172
|
session_id = user_properties.get("session_id")
|
|
173
|
+
identity_value = user_properties.get("identity")
|
|
173
174
|
files = data.get("files", [])
|
|
174
175
|
|
|
175
176
|
# Extract the interface queue ID
|
|
@@ -181,6 +182,10 @@ class GatewayOutput(GatewayBase):
|
|
|
181
182
|
)
|
|
182
183
|
|
|
183
184
|
if self.use_history and session_id:
|
|
185
|
+
other_history_props = {
|
|
186
|
+
"identity": identity_value,
|
|
187
|
+
}
|
|
188
|
+
|
|
184
189
|
topic = message.get_topic()
|
|
185
190
|
content = data.get("text") or ""
|
|
186
191
|
|
|
@@ -193,14 +198,18 @@ class GatewayOutput(GatewayBase):
|
|
|
193
198
|
and data.get("last_chunk")
|
|
194
199
|
and "text" in data
|
|
195
200
|
):
|
|
201
|
+
actions_called = user_properties.get("actions_called", [])
|
|
202
|
+
if actions_called:
|
|
203
|
+
self.history_instance.store_actions(session_id, actions_called)
|
|
204
|
+
|
|
196
205
|
if content:
|
|
197
206
|
self.history_instance.store_history(
|
|
198
|
-
session_id,
|
|
207
|
+
session_id, HISTORY_ASSISTANT_ROLE, content, other_history_props
|
|
199
208
|
)
|
|
200
209
|
|
|
201
210
|
for file in files:
|
|
202
211
|
self.history_instance.store_history(
|
|
203
|
-
session_id,
|
|
212
|
+
session_id, HISTORY_ASSISTANT_ROLE, f'\n[Returned file: {{name: {file.get("name")}, url: {file.get("url")}}}]\n', other_history_props
|
|
204
213
|
)
|
|
205
214
|
self.history_instance.store_file(session_id, file)
|
|
206
215
|
|
|
@@ -15,6 +15,7 @@ from datetime import datetime
|
|
|
15
15
|
|
|
16
16
|
from solace_ai_connector.common.log import log
|
|
17
17
|
from ..common.utils import format_agent_response
|
|
18
|
+
from ..common.constants import ORCHESTRATOR_COMPONENT_NAME
|
|
18
19
|
|
|
19
20
|
ACTION_REQUEST_TIMEOUT = 180
|
|
20
21
|
|
|
@@ -78,6 +79,17 @@ class ActionManager:
|
|
|
78
79
|
def add_action_response(self, action_response_obj, response_text_and_files):
|
|
79
80
|
"""Add an action response to the list"""
|
|
80
81
|
action_list_id = action_response_obj.get("action_list_id")
|
|
82
|
+
|
|
83
|
+
originator = action_response_obj.get("originator", "unknown")
|
|
84
|
+
# Ignore responses for actions that are not originated by the orchestrator
|
|
85
|
+
if originator != ORCHESTRATOR_COMPONENT_NAME:
|
|
86
|
+
log.debug(
|
|
87
|
+
"Ignoring response for action not originated by the orchestrator. "
|
|
88
|
+
"originator: %s action_list_id: %s",
|
|
89
|
+
originator, action_list_id
|
|
90
|
+
)
|
|
91
|
+
return None
|
|
92
|
+
|
|
81
93
|
with self.lock:
|
|
82
94
|
action_list = self.action_requests.get(action_list_id)
|
|
83
95
|
if action_list is None:
|
|
@@ -222,4 +234,4 @@ class ActionRequestList:
|
|
|
222
234
|
|
|
223
235
|
def format_ai_response(self):
|
|
224
236
|
"""Format the action response for the AI"""
|
|
225
|
-
return format_agent_response(self.actions)
|
|
237
|
+
return format_agent_response(self.actions)
|
|
@@ -14,6 +14,7 @@ import yaml
|
|
|
14
14
|
from solace_ai_connector.common.log import log
|
|
15
15
|
from solace_ai_connector.common.message import Message
|
|
16
16
|
|
|
17
|
+
from ...common.constants import ORCHESTRATOR_COMPONENT_NAME, HISTORY_MEMORY_ROLE
|
|
17
18
|
from ...services.llm_service.components.llm_request_component import LLMRequestComponent, info as base_info
|
|
18
19
|
from ...services.middleware_service.middleware_service import MiddlewareService
|
|
19
20
|
from ...services.file_service import FileService
|
|
@@ -127,6 +128,18 @@ class OrchestratorStimulusProcessorComponent(LLMRequestComponent):
|
|
|
127
128
|
|
|
128
129
|
user_properties = message.get_user_properties()
|
|
129
130
|
user_properties['timestamp_end'] = time()
|
|
131
|
+
|
|
132
|
+
actions_called = []
|
|
133
|
+
if results:
|
|
134
|
+
for result in results:
|
|
135
|
+
if result.get("payload", {}).get("action_name"):
|
|
136
|
+
actions_called.append({
|
|
137
|
+
"agent_name": result.get("payload", {}).get("agent_name"),
|
|
138
|
+
"action_name": result.get("payload", {}).get("action_name"),
|
|
139
|
+
"action_params": result.get("payload", {}).get("action_params"),
|
|
140
|
+
})
|
|
141
|
+
user_properties['actions_called'] = actions_called
|
|
142
|
+
|
|
130
143
|
message.set_user_properties(user_properties)
|
|
131
144
|
|
|
132
145
|
return results
|
|
@@ -184,7 +197,7 @@ class OrchestratorStimulusProcessorComponent(LLMRequestComponent):
|
|
|
184
197
|
message.set_user_properties(user_properties)
|
|
185
198
|
|
|
186
199
|
input_data = self.get_user_input(chat_text)
|
|
187
|
-
user_info =
|
|
200
|
+
user_info = user_properties.get("user_info", {"email": "unknown"})
|
|
188
201
|
|
|
189
202
|
agent_state_yaml, examples = self.get_agents_yaml(user_properties)
|
|
190
203
|
full_input = {
|
|
@@ -205,7 +218,7 @@ class OrchestratorStimulusProcessorComponent(LLMRequestComponent):
|
|
|
205
218
|
}
|
|
206
219
|
|
|
207
220
|
# Get the prompts
|
|
208
|
-
gateway_history = self.get_gateway_history(data)
|
|
221
|
+
gateway_history, memory_history = self.get_gateway_history(data)
|
|
209
222
|
system_prompt = SystemPrompt(full_input, examples)
|
|
210
223
|
if action_response_reinvoke:
|
|
211
224
|
user_prompt = ActionResponsePrompt(
|
|
@@ -216,6 +229,9 @@ class OrchestratorStimulusProcessorComponent(LLMRequestComponent):
|
|
|
216
229
|
user_prompt = UserStimulusPrompt(
|
|
217
230
|
full_input, gateway_history, errors, has_files
|
|
218
231
|
)
|
|
232
|
+
if memory_history:
|
|
233
|
+
self.history.store_history(stimulus_uuid, "system", memory_history)
|
|
234
|
+
|
|
219
235
|
|
|
220
236
|
# Store the user prompt in the history
|
|
221
237
|
self.history.store_history(stimulus_uuid, "user", user_prompt)
|
|
@@ -374,6 +390,9 @@ class OrchestratorStimulusProcessorComponent(LLMRequestComponent):
|
|
|
374
390
|
|
|
375
391
|
def get_gateway_history(self, data):
|
|
376
392
|
gateway_history = data.get("history", [])
|
|
393
|
+
memory_history = None
|
|
394
|
+
if gateway_history and gateway_history[0].get("role") == HISTORY_MEMORY_ROLE:
|
|
395
|
+
memory_history =gateway_history[0].get("content")
|
|
377
396
|
# Returning the history from the last user message
|
|
378
397
|
first_user_idx = None
|
|
379
398
|
last_user_idx = None
|
|
@@ -384,13 +403,13 @@ class OrchestratorStimulusProcessorComponent(LLMRequestComponent):
|
|
|
384
403
|
last_user_idx = idx
|
|
385
404
|
|
|
386
405
|
if first_user_idx is None:
|
|
387
|
-
return [] # No user query found
|
|
406
|
+
return [], memory_history # No user query found
|
|
388
407
|
|
|
389
408
|
if not last_user_idx > first_user_idx:
|
|
390
409
|
# Latest user message is already handled by orchestator history
|
|
391
|
-
return []
|
|
410
|
+
return [], memory_history
|
|
392
411
|
|
|
393
|
-
return gateway_history[first_user_idx:last_user_idx]
|
|
412
|
+
return gateway_history[first_user_idx:last_user_idx], memory_history
|
|
394
413
|
|
|
395
414
|
def get_user_input(self, chat_text):
|
|
396
415
|
|
|
@@ -458,6 +477,7 @@ class OrchestratorStimulusProcessorComponent(LLMRequestComponent):
|
|
|
458
477
|
"action_name": action_name,
|
|
459
478
|
"action_params": action_params,
|
|
460
479
|
"action_idx": action_idx,
|
|
480
|
+
"originator": ORCHESTRATOR_COMPONENT_NAME,
|
|
461
481
|
},
|
|
462
482
|
"topic": f"{os.getenv('SOLACE_AGENT_MESH_NAMESPACE')}solace-agent-mesh/v1/actionRequest/orchestrator/agent/{agent_name}/{action_name}",
|
|
463
483
|
}
|