solace-agent-mesh 0.0.1__py3-none-any.whl → 0.1.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.

Files changed (172) hide show
  1. solace_agent_mesh/__init__.py +0 -3
  2. solace_agent_mesh/agents/__init__.py +0 -0
  3. solace_agent_mesh/agents/base_agent_component.py +224 -0
  4. solace_agent_mesh/agents/global/__init__.py +0 -0
  5. solace_agent_mesh/agents/global/actions/__init__.py +0 -0
  6. solace_agent_mesh/agents/global/actions/agent_state_change.py +54 -0
  7. solace_agent_mesh/agents/global/actions/clear_history.py +32 -0
  8. solace_agent_mesh/agents/global/actions/convert_file_to_markdown.py +160 -0
  9. solace_agent_mesh/agents/global/actions/create_file.py +70 -0
  10. solace_agent_mesh/agents/global/actions/error_action.py +45 -0
  11. solace_agent_mesh/agents/global/actions/plantuml_diagram.py +93 -0
  12. solace_agent_mesh/agents/global/actions/plotly_graph.py +117 -0
  13. solace_agent_mesh/agents/global/actions/retrieve_file.py +51 -0
  14. solace_agent_mesh/agents/global/global_agent_component.py +38 -0
  15. solace_agent_mesh/agents/image_processing/__init__.py +0 -0
  16. solace_agent_mesh/agents/image_processing/actions/__init__.py +0 -0
  17. solace_agent_mesh/agents/image_processing/actions/create_image.py +75 -0
  18. solace_agent_mesh/agents/image_processing/actions/describe_image.py +115 -0
  19. solace_agent_mesh/agents/image_processing/image_processing_agent_component.py +23 -0
  20. solace_agent_mesh/agents/slack/__init__.py +1 -0
  21. solace_agent_mesh/agents/slack/actions/__init__.py +1 -0
  22. solace_agent_mesh/agents/slack/actions/post_message.py +177 -0
  23. solace_agent_mesh/agents/slack/slack_agent_component.py +59 -0
  24. solace_agent_mesh/agents/web_request/__init__.py +0 -0
  25. solace_agent_mesh/agents/web_request/actions/__init__.py +0 -0
  26. solace_agent_mesh/agents/web_request/actions/do_image_search.py +84 -0
  27. solace_agent_mesh/agents/web_request/actions/do_news_search.py +47 -0
  28. solace_agent_mesh/agents/web_request/actions/do_suggestion_search.py +34 -0
  29. solace_agent_mesh/agents/web_request/actions/do_web_request.py +134 -0
  30. solace_agent_mesh/agents/web_request/actions/download_file.py +69 -0
  31. solace_agent_mesh/agents/web_request/web_request_agent_component.py +33 -0
  32. solace_agent_mesh/cli/__init__.py +1 -0
  33. solace_agent_mesh/cli/commands/__init__.py +0 -0
  34. solace_agent_mesh/cli/commands/add/__init__.py +3 -0
  35. solace_agent_mesh/cli/commands/add/add.py +88 -0
  36. solace_agent_mesh/cli/commands/add/agent.py +110 -0
  37. solace_agent_mesh/cli/commands/add/copy_from_plugin.py +90 -0
  38. solace_agent_mesh/cli/commands/add/gateway.py +221 -0
  39. solace_agent_mesh/cli/commands/build.py +631 -0
  40. solace_agent_mesh/cli/commands/chat/__init__.py +3 -0
  41. solace_agent_mesh/cli/commands/chat/chat.py +361 -0
  42. solace_agent_mesh/cli/commands/config.py +29 -0
  43. solace_agent_mesh/cli/commands/init/__init__.py +3 -0
  44. solace_agent_mesh/cli/commands/init/ai_provider_step.py +76 -0
  45. solace_agent_mesh/cli/commands/init/broker_step.py +102 -0
  46. solace_agent_mesh/cli/commands/init/builtin_agent_step.py +88 -0
  47. solace_agent_mesh/cli/commands/init/check_if_already_done.py +13 -0
  48. solace_agent_mesh/cli/commands/init/create_config_file_step.py +52 -0
  49. solace_agent_mesh/cli/commands/init/create_other_project_files_step.py +96 -0
  50. solace_agent_mesh/cli/commands/init/file_service_step.py +73 -0
  51. solace_agent_mesh/cli/commands/init/init.py +114 -0
  52. solace_agent_mesh/cli/commands/init/project_structure_step.py +45 -0
  53. solace_agent_mesh/cli/commands/init/rest_api_step.py +50 -0
  54. solace_agent_mesh/cli/commands/init/web_ui_step.py +40 -0
  55. solace_agent_mesh/cli/commands/plugin/__init__.py +3 -0
  56. solace_agent_mesh/cli/commands/plugin/add.py +98 -0
  57. solace_agent_mesh/cli/commands/plugin/build.py +217 -0
  58. solace_agent_mesh/cli/commands/plugin/create.py +117 -0
  59. solace_agent_mesh/cli/commands/plugin/plugin.py +109 -0
  60. solace_agent_mesh/cli/commands/plugin/remove.py +71 -0
  61. solace_agent_mesh/cli/commands/run.py +68 -0
  62. solace_agent_mesh/cli/commands/visualizer.py +138 -0
  63. solace_agent_mesh/cli/config.py +81 -0
  64. solace_agent_mesh/cli/main.py +306 -0
  65. solace_agent_mesh/cli/utils.py +246 -0
  66. solace_agent_mesh/common/__init__.py +0 -0
  67. solace_agent_mesh/common/action.py +91 -0
  68. solace_agent_mesh/common/action_list.py +37 -0
  69. solace_agent_mesh/common/action_response.py +327 -0
  70. solace_agent_mesh/common/constants.py +3 -0
  71. solace_agent_mesh/common/mysql_database.py +40 -0
  72. solace_agent_mesh/common/postgres_database.py +79 -0
  73. solace_agent_mesh/common/prompt_templates.py +30 -0
  74. solace_agent_mesh/common/prompt_templates_unused_delete.py +161 -0
  75. solace_agent_mesh/common/stimulus_utils.py +152 -0
  76. solace_agent_mesh/common/time.py +24 -0
  77. solace_agent_mesh/common/utils.py +638 -0
  78. solace_agent_mesh/configs/agent_global.yaml +74 -0
  79. solace_agent_mesh/configs/agent_image_processing.yaml +82 -0
  80. solace_agent_mesh/configs/agent_slack.yaml +64 -0
  81. solace_agent_mesh/configs/agent_web_request.yaml +75 -0
  82. solace_agent_mesh/configs/conversation_to_file.yaml +56 -0
  83. solace_agent_mesh/configs/error_catcher.yaml +56 -0
  84. solace_agent_mesh/configs/monitor.yaml +0 -0
  85. solace_agent_mesh/configs/monitor_stim_and_errors_to_slack.yaml +106 -0
  86. solace_agent_mesh/configs/monitor_user_feedback.yaml +58 -0
  87. solace_agent_mesh/configs/orchestrator.yaml +241 -0
  88. solace_agent_mesh/configs/service_embedding.yaml +81 -0
  89. solace_agent_mesh/configs/service_llm.yaml +265 -0
  90. solace_agent_mesh/configs/visualize_websocket.yaml +55 -0
  91. solace_agent_mesh/gateway/__init__.py +0 -0
  92. solace_agent_mesh/gateway/components/__init__.py +0 -0
  93. solace_agent_mesh/gateway/components/gateway_base.py +41 -0
  94. solace_agent_mesh/gateway/components/gateway_input.py +265 -0
  95. solace_agent_mesh/gateway/components/gateway_output.py +289 -0
  96. solace_agent_mesh/gateway/identity/bamboohr_identity.py +18 -0
  97. solace_agent_mesh/gateway/identity/identity_base.py +10 -0
  98. solace_agent_mesh/gateway/identity/identity_provider.py +60 -0
  99. solace_agent_mesh/gateway/identity/no_identity.py +9 -0
  100. solace_agent_mesh/gateway/identity/passthru_identity.py +9 -0
  101. solace_agent_mesh/monitors/base_monitor_component.py +26 -0
  102. solace_agent_mesh/monitors/feedback/user_feedback_monitor.py +75 -0
  103. solace_agent_mesh/monitors/stim_and_errors/stim_and_error_monitor.py +560 -0
  104. solace_agent_mesh/orchestrator/__init__.py +0 -0
  105. solace_agent_mesh/orchestrator/action_manager.py +225 -0
  106. solace_agent_mesh/orchestrator/components/__init__.py +0 -0
  107. solace_agent_mesh/orchestrator/components/orchestrator_action_manager_timeout_component.py +54 -0
  108. solace_agent_mesh/orchestrator/components/orchestrator_action_response_component.py +179 -0
  109. solace_agent_mesh/orchestrator/components/orchestrator_register_component.py +107 -0
  110. solace_agent_mesh/orchestrator/components/orchestrator_stimulus_processor_component.py +477 -0
  111. solace_agent_mesh/orchestrator/components/orchestrator_streaming_output_component.py +246 -0
  112. solace_agent_mesh/orchestrator/orchestrator_main.py +166 -0
  113. solace_agent_mesh/orchestrator/orchestrator_prompt.py +410 -0
  114. solace_agent_mesh/services/__init__.py +0 -0
  115. solace_agent_mesh/services/authorization/providers/base_authorization_provider.py +56 -0
  116. solace_agent_mesh/services/bamboo_hr_service/__init__.py +3 -0
  117. solace_agent_mesh/services/bamboo_hr_service/bamboo_hr.py +182 -0
  118. solace_agent_mesh/services/common/__init__.py +4 -0
  119. solace_agent_mesh/services/common/auto_expiry.py +45 -0
  120. solace_agent_mesh/services/common/singleton.py +18 -0
  121. solace_agent_mesh/services/file_service/__init__.py +14 -0
  122. solace_agent_mesh/services/file_service/file_manager/__init__.py +0 -0
  123. solace_agent_mesh/services/file_service/file_manager/bucket_file_manager.py +149 -0
  124. solace_agent_mesh/services/file_service/file_manager/file_manager_base.py +162 -0
  125. solace_agent_mesh/services/file_service/file_manager/memory_file_manager.py +64 -0
  126. solace_agent_mesh/services/file_service/file_manager/volume_file_manager.py +106 -0
  127. solace_agent_mesh/services/file_service/file_service.py +432 -0
  128. solace_agent_mesh/services/file_service/file_service_constants.py +54 -0
  129. solace_agent_mesh/services/file_service/file_transformations.py +131 -0
  130. solace_agent_mesh/services/file_service/file_utils.py +322 -0
  131. solace_agent_mesh/services/file_service/transformers/__init__.py +5 -0
  132. solace_agent_mesh/services/history_service/__init__.py +3 -0
  133. solace_agent_mesh/services/history_service/history_providers/__init__.py +0 -0
  134. solace_agent_mesh/services/history_service/history_providers/base_history_provider.py +78 -0
  135. solace_agent_mesh/services/history_service/history_providers/memory_history_provider.py +167 -0
  136. solace_agent_mesh/services/history_service/history_providers/redis_history_provider.py +163 -0
  137. solace_agent_mesh/services/history_service/history_service.py +139 -0
  138. solace_agent_mesh/services/llm_service/components/llm_request_component.py +293 -0
  139. solace_agent_mesh/services/llm_service/components/llm_service_component_base.py +152 -0
  140. solace_agent_mesh/services/middleware_service/__init__.py +0 -0
  141. solace_agent_mesh/services/middleware_service/middleware_service.py +20 -0
  142. solace_agent_mesh/templates/action.py +38 -0
  143. solace_agent_mesh/templates/agent.py +29 -0
  144. solace_agent_mesh/templates/agent.yaml +70 -0
  145. solace_agent_mesh/templates/gateway-config-template.yaml +6 -0
  146. solace_agent_mesh/templates/gateway-default-config.yaml +28 -0
  147. solace_agent_mesh/templates/gateway-flows.yaml +81 -0
  148. solace_agent_mesh/templates/gateway-header.yaml +16 -0
  149. solace_agent_mesh/templates/gateway_base.py +15 -0
  150. solace_agent_mesh/templates/gateway_input.py +98 -0
  151. solace_agent_mesh/templates/gateway_output.py +71 -0
  152. solace_agent_mesh/templates/plugin-pyproject.toml +30 -0
  153. solace_agent_mesh/templates/rest-api-default-config.yaml +23 -0
  154. solace_agent_mesh/templates/rest-api-flows.yaml +80 -0
  155. solace_agent_mesh/templates/slack-default-config.yaml +9 -0
  156. solace_agent_mesh/templates/slack-flows.yaml +90 -0
  157. solace_agent_mesh/templates/solace-agent-mesh-default.yaml +77 -0
  158. solace_agent_mesh/templates/solace-agent-mesh-plugin-default.yaml +8 -0
  159. solace_agent_mesh/templates/web-default-config.yaml +5 -0
  160. solace_agent_mesh/templates/web-flows.yaml +86 -0
  161. solace_agent_mesh/tools/__init__.py +0 -0
  162. solace_agent_mesh/tools/components/__init__.py +0 -0
  163. solace_agent_mesh/tools/components/conversation_formatter.py +111 -0
  164. solace_agent_mesh/tools/components/file_resolver_component.py +58 -0
  165. solace_agent_mesh/tools/config/runtime_config.py +26 -0
  166. solace_agent_mesh-0.1.0.dist-info/METADATA +179 -0
  167. solace_agent_mesh-0.1.0.dist-info/RECORD +170 -0
  168. solace_agent_mesh-0.1.0.dist-info/entry_points.txt +3 -0
  169. solace_agent_mesh-0.0.1.dist-info/licenses/LICENSE.txt → solace_agent_mesh-0.1.0.dist-info/licenses/LICENSE +1 -2
  170. solace_agent_mesh-0.0.1.dist-info/METADATA +0 -51
  171. solace_agent_mesh-0.0.1.dist-info/RECORD +0 -5
  172. {solace_agent_mesh-0.0.1.dist-info → solace_agent_mesh-0.1.0.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
+ """