solace-agent-mesh 0.0.1__py3-none-any.whl → 0.1.1__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/__init__.py +0 -3
- solace_agent_mesh/agents/__init__.py +0 -0
- solace_agent_mesh/agents/base_agent_component.py +224 -0
- solace_agent_mesh/agents/global/__init__.py +0 -0
- solace_agent_mesh/agents/global/actions/__init__.py +0 -0
- solace_agent_mesh/agents/global/actions/agent_state_change.py +54 -0
- solace_agent_mesh/agents/global/actions/clear_history.py +32 -0
- solace_agent_mesh/agents/global/actions/convert_file_to_markdown.py +160 -0
- solace_agent_mesh/agents/global/actions/create_file.py +70 -0
- solace_agent_mesh/agents/global/actions/error_action.py +45 -0
- solace_agent_mesh/agents/global/actions/plantuml_diagram.py +93 -0
- solace_agent_mesh/agents/global/actions/plotly_graph.py +117 -0
- solace_agent_mesh/agents/global/actions/retrieve_file.py +51 -0
- solace_agent_mesh/agents/global/global_agent_component.py +38 -0
- solace_agent_mesh/agents/image_processing/__init__.py +0 -0
- solace_agent_mesh/agents/image_processing/actions/__init__.py +0 -0
- solace_agent_mesh/agents/image_processing/actions/create_image.py +75 -0
- solace_agent_mesh/agents/image_processing/actions/describe_image.py +115 -0
- solace_agent_mesh/agents/image_processing/image_processing_agent_component.py +23 -0
- solace_agent_mesh/agents/slack/__init__.py +1 -0
- solace_agent_mesh/agents/slack/actions/__init__.py +1 -0
- solace_agent_mesh/agents/slack/actions/post_message.py +177 -0
- solace_agent_mesh/agents/slack/slack_agent_component.py +59 -0
- solace_agent_mesh/agents/web_request/__init__.py +0 -0
- solace_agent_mesh/agents/web_request/actions/__init__.py +0 -0
- solace_agent_mesh/agents/web_request/actions/do_image_search.py +84 -0
- solace_agent_mesh/agents/web_request/actions/do_news_search.py +47 -0
- solace_agent_mesh/agents/web_request/actions/do_suggestion_search.py +34 -0
- solace_agent_mesh/agents/web_request/actions/do_web_request.py +134 -0
- solace_agent_mesh/agents/web_request/actions/download_file.py +69 -0
- solace_agent_mesh/agents/web_request/web_request_agent_component.py +33 -0
- solace_agent_mesh/assets/web-visualizer/assets/index-C5awueeJ.js +109 -0
- solace_agent_mesh/assets/web-visualizer/assets/index-D0qORgkg.css +1 -0
- solace_agent_mesh/assets/web-visualizer/index.html +14 -0
- solace_agent_mesh/assets/web-visualizer/vite.svg +1 -0
- solace_agent_mesh/cli/__init__.py +1 -0
- solace_agent_mesh/cli/commands/__init__.py +0 -0
- solace_agent_mesh/cli/commands/add/__init__.py +3 -0
- solace_agent_mesh/cli/commands/add/add.py +88 -0
- solace_agent_mesh/cli/commands/add/agent.py +110 -0
- solace_agent_mesh/cli/commands/add/copy_from_plugin.py +90 -0
- solace_agent_mesh/cli/commands/add/gateway.py +221 -0
- solace_agent_mesh/cli/commands/build.py +631 -0
- solace_agent_mesh/cli/commands/chat/__init__.py +3 -0
- solace_agent_mesh/cli/commands/chat/chat.py +361 -0
- solace_agent_mesh/cli/commands/config.py +29 -0
- solace_agent_mesh/cli/commands/init/__init__.py +3 -0
- solace_agent_mesh/cli/commands/init/ai_provider_step.py +76 -0
- solace_agent_mesh/cli/commands/init/broker_step.py +102 -0
- solace_agent_mesh/cli/commands/init/builtin_agent_step.py +88 -0
- solace_agent_mesh/cli/commands/init/check_if_already_done.py +13 -0
- solace_agent_mesh/cli/commands/init/create_config_file_step.py +52 -0
- solace_agent_mesh/cli/commands/init/create_other_project_files_step.py +96 -0
- solace_agent_mesh/cli/commands/init/file_service_step.py +73 -0
- solace_agent_mesh/cli/commands/init/init.py +114 -0
- solace_agent_mesh/cli/commands/init/project_structure_step.py +45 -0
- solace_agent_mesh/cli/commands/init/rest_api_step.py +50 -0
- solace_agent_mesh/cli/commands/init/web_ui_step.py +40 -0
- solace_agent_mesh/cli/commands/plugin/__init__.py +3 -0
- solace_agent_mesh/cli/commands/plugin/add.py +98 -0
- solace_agent_mesh/cli/commands/plugin/build.py +217 -0
- solace_agent_mesh/cli/commands/plugin/create.py +117 -0
- solace_agent_mesh/cli/commands/plugin/plugin.py +109 -0
- solace_agent_mesh/cli/commands/plugin/remove.py +71 -0
- solace_agent_mesh/cli/commands/run.py +68 -0
- solace_agent_mesh/cli/commands/visualizer.py +138 -0
- solace_agent_mesh/cli/config.py +81 -0
- solace_agent_mesh/cli/main.py +306 -0
- solace_agent_mesh/cli/utils.py +246 -0
- solace_agent_mesh/common/__init__.py +0 -0
- solace_agent_mesh/common/action.py +91 -0
- solace_agent_mesh/common/action_list.py +37 -0
- solace_agent_mesh/common/action_response.py +327 -0
- solace_agent_mesh/common/constants.py +3 -0
- solace_agent_mesh/common/mysql_database.py +40 -0
- solace_agent_mesh/common/postgres_database.py +79 -0
- solace_agent_mesh/common/prompt_templates.py +30 -0
- solace_agent_mesh/common/prompt_templates_unused_delete.py +161 -0
- solace_agent_mesh/common/stimulus_utils.py +152 -0
- solace_agent_mesh/common/time.py +24 -0
- solace_agent_mesh/common/utils.py +638 -0
- solace_agent_mesh/configs/agent_global.yaml +74 -0
- solace_agent_mesh/configs/agent_image_processing.yaml +82 -0
- solace_agent_mesh/configs/agent_slack.yaml +64 -0
- solace_agent_mesh/configs/agent_web_request.yaml +75 -0
- solace_agent_mesh/configs/conversation_to_file.yaml +56 -0
- solace_agent_mesh/configs/error_catcher.yaml +56 -0
- solace_agent_mesh/configs/monitor.yaml +0 -0
- solace_agent_mesh/configs/monitor_stim_and_errors_to_slack.yaml +106 -0
- solace_agent_mesh/configs/monitor_user_feedback.yaml +58 -0
- solace_agent_mesh/configs/orchestrator.yaml +241 -0
- solace_agent_mesh/configs/service_embedding.yaml +81 -0
- solace_agent_mesh/configs/service_llm.yaml +265 -0
- solace_agent_mesh/configs/visualize_websocket.yaml +55 -0
- solace_agent_mesh/gateway/__init__.py +0 -0
- solace_agent_mesh/gateway/components/__init__.py +0 -0
- solace_agent_mesh/gateway/components/gateway_base.py +41 -0
- solace_agent_mesh/gateway/components/gateway_input.py +265 -0
- solace_agent_mesh/gateway/components/gateway_output.py +289 -0
- solace_agent_mesh/gateway/identity/bamboohr_identity.py +18 -0
- solace_agent_mesh/gateway/identity/identity_base.py +10 -0
- solace_agent_mesh/gateway/identity/identity_provider.py +60 -0
- solace_agent_mesh/gateway/identity/no_identity.py +9 -0
- solace_agent_mesh/gateway/identity/passthru_identity.py +9 -0
- solace_agent_mesh/monitors/base_monitor_component.py +26 -0
- solace_agent_mesh/monitors/feedback/user_feedback_monitor.py +75 -0
- solace_agent_mesh/monitors/stim_and_errors/stim_and_error_monitor.py +560 -0
- solace_agent_mesh/orchestrator/__init__.py +0 -0
- solace_agent_mesh/orchestrator/action_manager.py +225 -0
- solace_agent_mesh/orchestrator/components/__init__.py +0 -0
- solace_agent_mesh/orchestrator/components/orchestrator_action_manager_timeout_component.py +54 -0
- solace_agent_mesh/orchestrator/components/orchestrator_action_response_component.py +179 -0
- solace_agent_mesh/orchestrator/components/orchestrator_register_component.py +107 -0
- solace_agent_mesh/orchestrator/components/orchestrator_stimulus_processor_component.py +477 -0
- solace_agent_mesh/orchestrator/components/orchestrator_streaming_output_component.py +246 -0
- solace_agent_mesh/orchestrator/orchestrator_main.py +166 -0
- solace_agent_mesh/orchestrator/orchestrator_prompt.py +410 -0
- solace_agent_mesh/services/__init__.py +0 -0
- solace_agent_mesh/services/authorization/providers/base_authorization_provider.py +56 -0
- solace_agent_mesh/services/bamboo_hr_service/__init__.py +3 -0
- solace_agent_mesh/services/bamboo_hr_service/bamboo_hr.py +182 -0
- solace_agent_mesh/services/common/__init__.py +4 -0
- solace_agent_mesh/services/common/auto_expiry.py +45 -0
- solace_agent_mesh/services/common/singleton.py +18 -0
- solace_agent_mesh/services/file_service/__init__.py +14 -0
- solace_agent_mesh/services/file_service/file_manager/__init__.py +0 -0
- solace_agent_mesh/services/file_service/file_manager/bucket_file_manager.py +149 -0
- solace_agent_mesh/services/file_service/file_manager/file_manager_base.py +162 -0
- solace_agent_mesh/services/file_service/file_manager/memory_file_manager.py +64 -0
- solace_agent_mesh/services/file_service/file_manager/volume_file_manager.py +106 -0
- solace_agent_mesh/services/file_service/file_service.py +432 -0
- solace_agent_mesh/services/file_service/file_service_constants.py +54 -0
- solace_agent_mesh/services/file_service/file_transformations.py +131 -0
- solace_agent_mesh/services/file_service/file_utils.py +322 -0
- solace_agent_mesh/services/file_service/transformers/__init__.py +5 -0
- solace_agent_mesh/services/history_service/__init__.py +3 -0
- solace_agent_mesh/services/history_service/history_providers/__init__.py +0 -0
- solace_agent_mesh/services/history_service/history_providers/base_history_provider.py +78 -0
- solace_agent_mesh/services/history_service/history_providers/memory_history_provider.py +167 -0
- solace_agent_mesh/services/history_service/history_providers/redis_history_provider.py +163 -0
- solace_agent_mesh/services/history_service/history_service.py +139 -0
- solace_agent_mesh/services/llm_service/components/llm_request_component.py +293 -0
- solace_agent_mesh/services/llm_service/components/llm_service_component_base.py +152 -0
- solace_agent_mesh/services/middleware_service/__init__.py +0 -0
- solace_agent_mesh/services/middleware_service/middleware_service.py +20 -0
- solace_agent_mesh/templates/action.py +38 -0
- solace_agent_mesh/templates/agent.py +29 -0
- solace_agent_mesh/templates/agent.yaml +70 -0
- solace_agent_mesh/templates/gateway-config-template.yaml +6 -0
- solace_agent_mesh/templates/gateway-default-config.yaml +28 -0
- solace_agent_mesh/templates/gateway-flows.yaml +81 -0
- solace_agent_mesh/templates/gateway-header.yaml +16 -0
- solace_agent_mesh/templates/gateway_base.py +15 -0
- solace_agent_mesh/templates/gateway_input.py +98 -0
- solace_agent_mesh/templates/gateway_output.py +71 -0
- solace_agent_mesh/templates/plugin-pyproject.toml +30 -0
- solace_agent_mesh/templates/rest-api-default-config.yaml +24 -0
- solace_agent_mesh/templates/rest-api-flows.yaml +80 -0
- solace_agent_mesh/templates/slack-default-config.yaml +9 -0
- solace_agent_mesh/templates/slack-flows.yaml +90 -0
- solace_agent_mesh/templates/solace-agent-mesh-default.yaml +77 -0
- solace_agent_mesh/templates/solace-agent-mesh-plugin-default.yaml +8 -0
- solace_agent_mesh/templates/web-default-config.yaml +5 -0
- solace_agent_mesh/templates/web-flows.yaml +86 -0
- solace_agent_mesh/tools/__init__.py +0 -0
- solace_agent_mesh/tools/components/__init__.py +0 -0
- solace_agent_mesh/tools/components/conversation_formatter.py +111 -0
- solace_agent_mesh/tools/components/file_resolver_component.py +58 -0
- solace_agent_mesh/tools/config/runtime_config.py +26 -0
- solace_agent_mesh-0.1.1.dist-info/METADATA +179 -0
- solace_agent_mesh-0.1.1.dist-info/RECORD +174 -0
- solace_agent_mesh-0.1.1.dist-info/entry_points.txt +3 -0
- solace_agent_mesh-0.0.1.dist-info/licenses/LICENSE.txt → solace_agent_mesh-0.1.1.dist-info/licenses/LICENSE +1 -2
- solace_agent_mesh-0.0.1.dist-info/METADATA +0 -51
- solace_agent_mesh-0.0.1.dist-info/RECORD +0 -5
- {solace_agent_mesh-0.0.1.dist-info → solace_agent_mesh-0.1.1.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Manage a MySQL database connection."""
|
|
2
|
+
|
|
3
|
+
import mysql.connector
|
|
4
|
+
|
|
5
|
+
class MySQLDatabase:
|
|
6
|
+
def __init__(self, host: str, user: str, password: str, database: str):
|
|
7
|
+
self.host = host
|
|
8
|
+
self.user = user
|
|
9
|
+
self.password = password
|
|
10
|
+
self.database = database
|
|
11
|
+
self.connection = None
|
|
12
|
+
|
|
13
|
+
if ":" in self.host:
|
|
14
|
+
self.host, self.port = self.host.split(":")
|
|
15
|
+
else:
|
|
16
|
+
self.port = 3306
|
|
17
|
+
|
|
18
|
+
def cursor(self, **kwargs):
|
|
19
|
+
if self.connection is None:
|
|
20
|
+
self.connect()
|
|
21
|
+
try:
|
|
22
|
+
return self.connection.cursor(**kwargs)
|
|
23
|
+
except mysql.connector.errors.OperationalError:
|
|
24
|
+
self.connect()
|
|
25
|
+
return self.connection.cursor(**kwargs)
|
|
26
|
+
|
|
27
|
+
def connect(self):
|
|
28
|
+
return mysql.connector.connect(
|
|
29
|
+
host=self.host,
|
|
30
|
+
port=self.port,
|
|
31
|
+
user=self.user,
|
|
32
|
+
password=self.password,
|
|
33
|
+
database=self.database,
|
|
34
|
+
raise_on_warnings=False,
|
|
35
|
+
connection_timeout=60,
|
|
36
|
+
autocommit=True,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def close(self):
|
|
40
|
+
self.connection.close()
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Manage a PostgreSQL database connection."""
|
|
2
|
+
|
|
3
|
+
import psycopg2
|
|
4
|
+
import psycopg2.extras
|
|
5
|
+
from solace_ai_connector.common.log import log
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PostgreSQLDatabase:
|
|
9
|
+
def __init__(self, host: str, user: str, password: str, database: str):
|
|
10
|
+
self.host = host
|
|
11
|
+
self.user = user
|
|
12
|
+
self.password = password
|
|
13
|
+
self.database = database
|
|
14
|
+
self.connection = None
|
|
15
|
+
|
|
16
|
+
if ":" in self.host:
|
|
17
|
+
self.host, self.port = self.host.split(":")
|
|
18
|
+
else:
|
|
19
|
+
self.port = 5432
|
|
20
|
+
|
|
21
|
+
def cursor(self, **kwargs):
|
|
22
|
+
if self.connection is None or self.connection.closed:
|
|
23
|
+
self.connect()
|
|
24
|
+
try:
|
|
25
|
+
return self.connection.cursor(**kwargs)
|
|
26
|
+
except Exception: # pylint: disable=broad-except
|
|
27
|
+
self.connect()
|
|
28
|
+
return self.connection.cursor(**kwargs)
|
|
29
|
+
|
|
30
|
+
def connect(self, auto_commit=True):
|
|
31
|
+
self.connection = psycopg2.connect(
|
|
32
|
+
host=self.host,
|
|
33
|
+
port=self.port,
|
|
34
|
+
user=self.user,
|
|
35
|
+
password=self.password,
|
|
36
|
+
database=self.database,
|
|
37
|
+
connect_timeout=10,
|
|
38
|
+
)
|
|
39
|
+
self.connection.autocommit = auto_commit
|
|
40
|
+
|
|
41
|
+
def close(self):
|
|
42
|
+
self.connection.close()
|
|
43
|
+
|
|
44
|
+
def execute(self, query, params=None):
|
|
45
|
+
sanity = 3
|
|
46
|
+
while True:
|
|
47
|
+
try:
|
|
48
|
+
cursor = self.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
|
|
49
|
+
cursor.execute(query, params)
|
|
50
|
+
break
|
|
51
|
+
except Exception as e:
|
|
52
|
+
log.error("Database error: %s", e)
|
|
53
|
+
sanity -= 1
|
|
54
|
+
if sanity == 0:
|
|
55
|
+
raise e
|
|
56
|
+
|
|
57
|
+
return cursor
|
|
58
|
+
|
|
59
|
+
def get_db_for_action(action_obj):
|
|
60
|
+
sql_host = action_obj.get_config("sql_host")
|
|
61
|
+
sql_user = action_obj.get_config("sql_user")
|
|
62
|
+
sql_password = action_obj.get_config("sql_password")
|
|
63
|
+
sql_database = action_obj.get_config("sql_database")
|
|
64
|
+
sql_db = None
|
|
65
|
+
|
|
66
|
+
if sql_host and sql_user and sql_password and sql_database:
|
|
67
|
+
sql_db = PostgreSQLDatabase(
|
|
68
|
+
host=sql_host,
|
|
69
|
+
user=sql_user,
|
|
70
|
+
password=sql_password,
|
|
71
|
+
database=sql_database,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if sql_db is None:
|
|
75
|
+
raise ValueError(
|
|
76
|
+
f"SQL database expected but not configured on {action_obj.__class__.__name__}"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
return sql_db
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
from langchain_core.messages import HumanMessage
|
|
3
|
+
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def FilterLocationsSystemPrompt(locations: List[str]) -> str:
|
|
8
|
+
return f"""
|
|
9
|
+
You will use your geographical knowledge to filter the following list of locations
|
|
10
|
+
into a list of locations that fall within the user's request. Those locations
|
|
11
|
+
must be returned in a yaml array of the following schema:
|
|
12
|
+
|
|
13
|
+
<response-schema-yaml>
|
|
14
|
+
matching_locations:
|
|
15
|
+
- <location1>
|
|
16
|
+
- <location2>
|
|
17
|
+
...
|
|
18
|
+
</response-schema-yaml>
|
|
19
|
+
|
|
20
|
+
<locations>
|
|
21
|
+
{yaml.dump(locations)}
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def FilterLocationsUserPrompt(location_filter: str) -> HumanMessage:
|
|
26
|
+
return f"""
|
|
27
|
+
<user-location-filter>
|
|
28
|
+
{location_filter}
|
|
29
|
+
</user-location-filter>
|
|
30
|
+
"""
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from langchain_core.messages import HumanMessage
|
|
2
|
+
import yaml
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def RagWithBundleSummarySystemPrompt() -> str:
|
|
6
|
+
return """
|
|
7
|
+
You are the first stage in providing a great answer to a user's question
|
|
8
|
+
about Solace products. To get a great answer, you must work with the
|
|
9
|
+
user to ensure that their question is sufficiently unambiguous and
|
|
10
|
+
detailed. Specifically, with Solace products it is important to ensure that
|
|
11
|
+
the correct operational context is provided when necessary, since Solace brokers
|
|
12
|
+
can be appliances, self-managed software instances (VMs, containers, ...) or they
|
|
13
|
+
could be using Solace Cloud. Each of these operational contexts can have different
|
|
14
|
+
configurations and requirements. However, if the question is about a feature that
|
|
15
|
+
is common to all Solace products, then that context is not necessary. Make sure that
|
|
16
|
+
the question is clear and detailed enough to provide a good answer.
|
|
17
|
+
|
|
18
|
+
Along with their query, you are provided with summaries of all the bundles of documentation
|
|
19
|
+
available to retrieve answers to the user's question. If the question is sufficiently
|
|
20
|
+
detailed, then for each bundle summary, you must give it a relevance score from 0 to 5, where 5 is
|
|
21
|
+
indicates that the bundle will very likely answer the question, and 0 indicates that
|
|
22
|
+
the bundle is not relevant to the question. If the question is not detailed enough,
|
|
23
|
+
then you must ask the user for more information to clarify the question. For questions
|
|
24
|
+
relating to the Software broker, make user you include the Software Broker bundle. Similarly,
|
|
25
|
+
for Appliances, include the Appliance bundle and for Solace Cloud, include the Solace Cloud bundle.
|
|
26
|
+
|
|
27
|
+
When selecting bundles, you must return an object with the bundle indexes as keys and
|
|
28
|
+
the relevance scores as values. List the bundles in the order that they are provided to
|
|
29
|
+
you and give each one a score.
|
|
30
|
+
|
|
31
|
+
Some things that you should consider when scoring the bundles are:
|
|
32
|
+
- The term 'services' refers to all the protocols and input/outputs of the brokers. All information
|
|
33
|
+
about ports, protocols, and services should be in the 'services' bundle.
|
|
34
|
+
- When dealing specifically with default port values, it is necessary to go to the platform-specific
|
|
35
|
+
bundle. For example, the default port for the Software broker is different from the default port for
|
|
36
|
+
the appliance or Solace Cloud instance. The general services page has default port info but it might be wrong.
|
|
37
|
+
- 'gather-diagnostics' is a highly used term relating to debugging and troubleshooting of the brokers. Getting these
|
|
38
|
+
diagnostics varies greatly between the different types of brokers.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
The answer must follow this YAML format:
|
|
42
|
+
|
|
43
|
+
Either: (for when the question does not need further clarification)
|
|
44
|
+
<response>
|
|
45
|
+
bundles:
|
|
46
|
+
0: <score0>
|
|
47
|
+
1: <score1>
|
|
48
|
+
...
|
|
49
|
+
query: <clarified-question>
|
|
50
|
+
</response>
|
|
51
|
+
|
|
52
|
+
or: (for when the question needs further clarification)
|
|
53
|
+
<response>
|
|
54
|
+
question: <clarification-question>
|
|
55
|
+
</response>
|
|
56
|
+
|
|
57
|
+
<example-request-1>
|
|
58
|
+
query: How do I get 'gather diagnostics' on a Solace broker?
|
|
59
|
+
</example-request-1>
|
|
60
|
+
<example-response-1>
|
|
61
|
+
<response>
|
|
62
|
+
question: Are you asking about a Solace appliance, a software broker or a Solace Cloud instance?
|
|
63
|
+
</response>
|
|
64
|
+
</example-response-1>
|
|
65
|
+
<example-response-2>
|
|
66
|
+
<response>
|
|
67
|
+
bundles:
|
|
68
|
+
0: 5
|
|
69
|
+
1: 2
|
|
70
|
+
2: 0
|
|
71
|
+
3: 3
|
|
72
|
+
query: How do I configure a Solace broker to use TLS?
|
|
73
|
+
</response>
|
|
74
|
+
</example-response-2>
|
|
75
|
+
<example-response-3>
|
|
76
|
+
<response>
|
|
77
|
+
question: Are you running the software broker yourself or through Solace Cloud?
|
|
78
|
+
</response>
|
|
79
|
+
</example-response-3>
|
|
80
|
+
<example-response-4>
|
|
81
|
+
<response>
|
|
82
|
+
bundles:
|
|
83
|
+
0: 0
|
|
84
|
+
1: 5
|
|
85
|
+
2: 3
|
|
86
|
+
query: How do I get 'gather diagnostics' on a Solace broker?
|
|
87
|
+
</response>
|
|
88
|
+
</example-response-4>
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def RagWithBundleSummaryUserPrompt(query: str, bundle_content: str):
|
|
93
|
+
return f"""
|
|
94
|
+
<user-query>\n{query}</user-query>\n\n<bundles>\n{bundle_content}\n</bundles>\n
|
|
95
|
+
|
|
96
|
+
Reminder, use the context above to answer this query:
|
|
97
|
+
<user-query-reminder>\n{query}</user-query-reminder>\n
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def RagWithBundleSummarySystemPromptPart2() -> str:
|
|
102
|
+
return """
|
|
103
|
+
You are the second stage in providing a great answer to a user's question
|
|
104
|
+
about Solace products. Your task is to take the user's question and the
|
|
105
|
+
context provided below and use that context to provide a well-thoughtout and
|
|
106
|
+
professional answer in markdown. The answer should be detailed enough to
|
|
107
|
+
provide the user with the information they need, but it should not be overly
|
|
108
|
+
verbose. The answer should be written in a professional tone and should be
|
|
109
|
+
free of spelling and grammatical errors. Always provide links to the source
|
|
110
|
+
of the information, which is given above the context starting with 'Page: ...'.
|
|
111
|
+
|
|
112
|
+
If you can't find the answer in the provided context, you must state that the
|
|
113
|
+
information that you have is insufficient to answer the question.
|
|
114
|
+
|
|
115
|
+
No not add any information that is not in the context provided. Don't trust the
|
|
116
|
+
general services page (Managing-Services) for default port information. The default
|
|
117
|
+
port for the Software broker is different from the default port for the appliance or
|
|
118
|
+
Solace Cloud instance. Look deeper for that information.
|
|
119
|
+
|
|
120
|
+
Solace CLI commands are immediately persisted. It is not necessary to 'write memory' or
|
|
121
|
+
'commit' the configuration. The user knows this, so there is no need to
|
|
122
|
+
mention it in the answer.
|
|
123
|
+
|
|
124
|
+
When providing an answer it is essential to provide links to the source of the
|
|
125
|
+
data as shown in the context below (Page: ...)
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def RagWithBundleSummaryUserPromptPart2(query: str, context: str):
|
|
130
|
+
return f"""
|
|
131
|
+
<user-query>\n{query}</user-query>\n\n<answer-context>\n{context}\n</answer-context>\n
|
|
132
|
+
|
|
133
|
+
Reminder, use the context above to answer this query: (and keep answers to only the context provided)
|
|
134
|
+
<user-query-reminder>\n{query}</user-query-reminder>
|
|
135
|
+
<rule>Add links to the source of the information in the context - important</rule>
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def RagWithBundleSummaryUserPromptClarificationQuestion(question: str):
|
|
140
|
+
return f"To clarify the customer documentation query, you must ask the user the following question: {question} and then perform the customer documentation query again."
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def ChannelHistoryQueryPrompt(query: str, history_messages: list) -> HumanMessage:
|
|
144
|
+
return HumanMessage(
|
|
145
|
+
content=f"""
|
|
146
|
+
You (orchestrator) are being asked to query the channel history with the following query:
|
|
147
|
+
<channel_history_query>
|
|
148
|
+
{query}
|
|
149
|
+
</channel_history_query>
|
|
150
|
+
|
|
151
|
+
The messages in the channel history are shown below with the most recent first. You should use this information to answer the query.
|
|
152
|
+
You should return a send_message action with the answer to the query.
|
|
153
|
+
<channel_history_messages>
|
|
154
|
+
{yaml.dump(history_messages)}
|
|
155
|
+
</channel_history_messages>
|
|
156
|
+
"""
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def BasicStatementPrompt(statement: str) -> HumanMessage:
|
|
161
|
+
return HumanMessage(content=statement)
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Utilities for describing stimulus state and events.
|
|
2
|
+
|
|
3
|
+
This module provides functions for generating human-readable descriptions
|
|
4
|
+
of stimulus state, including events, errors and completion status.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, List
|
|
8
|
+
import time
|
|
9
|
+
import json
|
|
10
|
+
|
|
11
|
+
from solace_ai_connector.common.log import log
|
|
12
|
+
from ..services.file_service import FileService
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _format_timestamp(timestamp: float) -> str:
|
|
16
|
+
"""Formats a Unix timestamp into a human readable string.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
timestamp: Unix timestamp to format
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Formatted timestamp string in YYYY-MM-DD HH:MM:SS format
|
|
23
|
+
"""
|
|
24
|
+
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _format_event(event: Dict) -> str:
|
|
28
|
+
"""Formats a single stimulus event into markdown.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
event: Dictionary containing event details including topic, payload,
|
|
32
|
+
user properties and timestamp
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Markdown formatted string describing the event
|
|
36
|
+
"""
|
|
37
|
+
timestamp = _format_timestamp(event.get("timestamp", 0))
|
|
38
|
+
topic = event.get("topic", "")
|
|
39
|
+
payload = event.get("payload", {})
|
|
40
|
+
user_props = event.get("user_properties", {})
|
|
41
|
+
|
|
42
|
+
# Format based on topic pattern, similar to ConversationFormatter
|
|
43
|
+
if "/stimulus/" in topic:
|
|
44
|
+
stimulus_text = payload.get("text", "")
|
|
45
|
+
identity = user_props.get("identity", "unknown")
|
|
46
|
+
if "/reinvoke" in topic:
|
|
47
|
+
return f"### {timestamp} - Reinvoke Request\nFrom: {identity}\n```\n{stimulus_text}\n```\n"
|
|
48
|
+
return f"### {timestamp} - Initial Request\nFrom: {identity}\n```\n{stimulus_text}\n```\n"
|
|
49
|
+
|
|
50
|
+
if "/response/" in topic or "/streamingResponse/" in topic:
|
|
51
|
+
if isinstance(payload, dict) and payload.get("last_chunk") is True:
|
|
52
|
+
response_text = payload.get("text", "")
|
|
53
|
+
return f"### {timestamp} - Response:\n{response_text}\n\n"
|
|
54
|
+
return ""
|
|
55
|
+
|
|
56
|
+
if "/responseComplete/" in topic:
|
|
57
|
+
return f"### {timestamp} - Response Complete\n"
|
|
58
|
+
|
|
59
|
+
if "/actionRequest/" in topic:
|
|
60
|
+
action_name = payload.get("action_name", "")
|
|
61
|
+
action_params = payload.get("action_params", {})
|
|
62
|
+
params_text = "\n".join(f"- {k}: {v}" for k, v in action_params.items())
|
|
63
|
+
return f"### {timestamp} - Action Request: {action_name}\n{params_text}\n"
|
|
64
|
+
|
|
65
|
+
if "/actionResponse/" in topic:
|
|
66
|
+
topic_parts = topic.split("/agent/")[1].split("/")
|
|
67
|
+
agent_name = topic_parts[0]
|
|
68
|
+
action_name = topic_parts[1]
|
|
69
|
+
response_text = "\n".join(f"- {k}: {v}" for k, v in payload.items())
|
|
70
|
+
return f"### {timestamp} - Action Response: {agent_name}.{action_name}\n{response_text}\n"
|
|
71
|
+
|
|
72
|
+
return f"### {timestamp} - Other Event\nTopic: {topic}\n"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _format_error(error: Dict) -> str:
|
|
76
|
+
"""Formats a single stimulus error into markdown.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
error: Dictionary containing error details including message,
|
|
80
|
+
timestamp and user properties
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Markdown formatted string describing the error
|
|
84
|
+
"""
|
|
85
|
+
timestamp = _format_timestamp(error.get("timestamp", 0))
|
|
86
|
+
message = error.get("message", "Unknown error")
|
|
87
|
+
source = error.get("source", "Unknown source")
|
|
88
|
+
return f"### {timestamp} - Error\nSource: {source}\nDetails: {message}\n"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def describe_stimulus(stimulus_uuid: str, state: Dict, is_timeout: bool = False) -> str:
|
|
92
|
+
"""Generates a markdown description of a stimulus's complete state.
|
|
93
|
+
|
|
94
|
+
Takes a stimulus state dictionary and generates a human-readable markdown
|
|
95
|
+
description including all events, errors and completion status.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
stimulus_uuid: UUID of the stimulus
|
|
99
|
+
state: Dictionary containing complete stimulus state including events,
|
|
100
|
+
errors and metadata
|
|
101
|
+
is_timeout: Whether this stimulus timed out
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Markdown formatted string describing the complete stimulus journey
|
|
105
|
+
"""
|
|
106
|
+
events: List[Dict] = state.get("events", [])
|
|
107
|
+
errors: List[Dict] = state.get("errors", [])
|
|
108
|
+
completion_status = state.get("completion_status", "unknown")
|
|
109
|
+
|
|
110
|
+
# Start with header
|
|
111
|
+
description = f"# Stimulus {stimulus_uuid}\n\n"
|
|
112
|
+
|
|
113
|
+
# Add completion status with timeout indicator if applicable
|
|
114
|
+
status_str = completion_status.upper()
|
|
115
|
+
if is_timeout:
|
|
116
|
+
status_str += " (TIMED OUT)"
|
|
117
|
+
description += f"**Status:** {status_str}\n\n"
|
|
118
|
+
|
|
119
|
+
# Add events section
|
|
120
|
+
if events:
|
|
121
|
+
description += "## Events\n\n"
|
|
122
|
+
for event in events:
|
|
123
|
+
description += _format_event(event)
|
|
124
|
+
|
|
125
|
+
# Add errors section
|
|
126
|
+
if errors:
|
|
127
|
+
description += "\n## Errors\n\n"
|
|
128
|
+
for error in errors:
|
|
129
|
+
description += _format_error(error)
|
|
130
|
+
|
|
131
|
+
# Add any available files
|
|
132
|
+
available_files_json = (
|
|
133
|
+
events[-1].get("user_properties", {}).get("available_files", None)
|
|
134
|
+
)
|
|
135
|
+
available_files = []
|
|
136
|
+
if available_files_json:
|
|
137
|
+
try:
|
|
138
|
+
available_files_user_prop = json.loads(available_files_json)
|
|
139
|
+
if available_files_user_prop:
|
|
140
|
+
file_service = FileService()
|
|
141
|
+
available_files = [
|
|
142
|
+
file_service.get_file_block_by_metadata(file)
|
|
143
|
+
for file in available_files_user_prop
|
|
144
|
+
]
|
|
145
|
+
except Exception as e:
|
|
146
|
+
log.error("Failed to get available files: %s", str(e))
|
|
147
|
+
|
|
148
|
+
if available_files:
|
|
149
|
+
description += "\n## Available Files\n\n"
|
|
150
|
+
description += "\n\n".join(available_files)
|
|
151
|
+
|
|
152
|
+
return description
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
ONE_DAY = 86400
|
|
2
|
+
"""
|
|
3
|
+
Number of seconds in a day.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
ONE_HOUR = 3600
|
|
7
|
+
"""
|
|
8
|
+
Number of seconds in an hour.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
THIRTY_MINUTES = 1800
|
|
12
|
+
"""
|
|
13
|
+
Number of seconds in thirty minutes.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
TEN_MINUTES = 600
|
|
17
|
+
"""
|
|
18
|
+
Number of seconds in ten minutes.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
FIVE_MINUTES = 300
|
|
22
|
+
"""
|
|
23
|
+
Number of seconds in five minutes.
|
|
24
|
+
"""
|