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.

Files changed (176) 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/assets/web-visualizer/assets/index-C5awueeJ.js +109 -0
  33. solace_agent_mesh/assets/web-visualizer/assets/index-D0qORgkg.css +1 -0
  34. solace_agent_mesh/assets/web-visualizer/index.html +14 -0
  35. solace_agent_mesh/assets/web-visualizer/vite.svg +1 -0
  36. solace_agent_mesh/cli/__init__.py +1 -0
  37. solace_agent_mesh/cli/commands/__init__.py +0 -0
  38. solace_agent_mesh/cli/commands/add/__init__.py +3 -0
  39. solace_agent_mesh/cli/commands/add/add.py +88 -0
  40. solace_agent_mesh/cli/commands/add/agent.py +110 -0
  41. solace_agent_mesh/cli/commands/add/copy_from_plugin.py +90 -0
  42. solace_agent_mesh/cli/commands/add/gateway.py +221 -0
  43. solace_agent_mesh/cli/commands/build.py +631 -0
  44. solace_agent_mesh/cli/commands/chat/__init__.py +3 -0
  45. solace_agent_mesh/cli/commands/chat/chat.py +361 -0
  46. solace_agent_mesh/cli/commands/config.py +29 -0
  47. solace_agent_mesh/cli/commands/init/__init__.py +3 -0
  48. solace_agent_mesh/cli/commands/init/ai_provider_step.py +76 -0
  49. solace_agent_mesh/cli/commands/init/broker_step.py +102 -0
  50. solace_agent_mesh/cli/commands/init/builtin_agent_step.py +88 -0
  51. solace_agent_mesh/cli/commands/init/check_if_already_done.py +13 -0
  52. solace_agent_mesh/cli/commands/init/create_config_file_step.py +52 -0
  53. solace_agent_mesh/cli/commands/init/create_other_project_files_step.py +96 -0
  54. solace_agent_mesh/cli/commands/init/file_service_step.py +73 -0
  55. solace_agent_mesh/cli/commands/init/init.py +114 -0
  56. solace_agent_mesh/cli/commands/init/project_structure_step.py +45 -0
  57. solace_agent_mesh/cli/commands/init/rest_api_step.py +50 -0
  58. solace_agent_mesh/cli/commands/init/web_ui_step.py +40 -0
  59. solace_agent_mesh/cli/commands/plugin/__init__.py +3 -0
  60. solace_agent_mesh/cli/commands/plugin/add.py +98 -0
  61. solace_agent_mesh/cli/commands/plugin/build.py +217 -0
  62. solace_agent_mesh/cli/commands/plugin/create.py +117 -0
  63. solace_agent_mesh/cli/commands/plugin/plugin.py +109 -0
  64. solace_agent_mesh/cli/commands/plugin/remove.py +71 -0
  65. solace_agent_mesh/cli/commands/run.py +68 -0
  66. solace_agent_mesh/cli/commands/visualizer.py +138 -0
  67. solace_agent_mesh/cli/config.py +81 -0
  68. solace_agent_mesh/cli/main.py +306 -0
  69. solace_agent_mesh/cli/utils.py +246 -0
  70. solace_agent_mesh/common/__init__.py +0 -0
  71. solace_agent_mesh/common/action.py +91 -0
  72. solace_agent_mesh/common/action_list.py +37 -0
  73. solace_agent_mesh/common/action_response.py +327 -0
  74. solace_agent_mesh/common/constants.py +3 -0
  75. solace_agent_mesh/common/mysql_database.py +40 -0
  76. solace_agent_mesh/common/postgres_database.py +79 -0
  77. solace_agent_mesh/common/prompt_templates.py +30 -0
  78. solace_agent_mesh/common/prompt_templates_unused_delete.py +161 -0
  79. solace_agent_mesh/common/stimulus_utils.py +152 -0
  80. solace_agent_mesh/common/time.py +24 -0
  81. solace_agent_mesh/common/utils.py +638 -0
  82. solace_agent_mesh/configs/agent_global.yaml +74 -0
  83. solace_agent_mesh/configs/agent_image_processing.yaml +82 -0
  84. solace_agent_mesh/configs/agent_slack.yaml +64 -0
  85. solace_agent_mesh/configs/agent_web_request.yaml +75 -0
  86. solace_agent_mesh/configs/conversation_to_file.yaml +56 -0
  87. solace_agent_mesh/configs/error_catcher.yaml +56 -0
  88. solace_agent_mesh/configs/monitor.yaml +0 -0
  89. solace_agent_mesh/configs/monitor_stim_and_errors_to_slack.yaml +106 -0
  90. solace_agent_mesh/configs/monitor_user_feedback.yaml +58 -0
  91. solace_agent_mesh/configs/orchestrator.yaml +241 -0
  92. solace_agent_mesh/configs/service_embedding.yaml +81 -0
  93. solace_agent_mesh/configs/service_llm.yaml +265 -0
  94. solace_agent_mesh/configs/visualize_websocket.yaml +55 -0
  95. solace_agent_mesh/gateway/__init__.py +0 -0
  96. solace_agent_mesh/gateway/components/__init__.py +0 -0
  97. solace_agent_mesh/gateway/components/gateway_base.py +41 -0
  98. solace_agent_mesh/gateway/components/gateway_input.py +265 -0
  99. solace_agent_mesh/gateway/components/gateway_output.py +289 -0
  100. solace_agent_mesh/gateway/identity/bamboohr_identity.py +18 -0
  101. solace_agent_mesh/gateway/identity/identity_base.py +10 -0
  102. solace_agent_mesh/gateway/identity/identity_provider.py +60 -0
  103. solace_agent_mesh/gateway/identity/no_identity.py +9 -0
  104. solace_agent_mesh/gateway/identity/passthru_identity.py +9 -0
  105. solace_agent_mesh/monitors/base_monitor_component.py +26 -0
  106. solace_agent_mesh/monitors/feedback/user_feedback_monitor.py +75 -0
  107. solace_agent_mesh/monitors/stim_and_errors/stim_and_error_monitor.py +560 -0
  108. solace_agent_mesh/orchestrator/__init__.py +0 -0
  109. solace_agent_mesh/orchestrator/action_manager.py +225 -0
  110. solace_agent_mesh/orchestrator/components/__init__.py +0 -0
  111. solace_agent_mesh/orchestrator/components/orchestrator_action_manager_timeout_component.py +54 -0
  112. solace_agent_mesh/orchestrator/components/orchestrator_action_response_component.py +179 -0
  113. solace_agent_mesh/orchestrator/components/orchestrator_register_component.py +107 -0
  114. solace_agent_mesh/orchestrator/components/orchestrator_stimulus_processor_component.py +477 -0
  115. solace_agent_mesh/orchestrator/components/orchestrator_streaming_output_component.py +246 -0
  116. solace_agent_mesh/orchestrator/orchestrator_main.py +166 -0
  117. solace_agent_mesh/orchestrator/orchestrator_prompt.py +410 -0
  118. solace_agent_mesh/services/__init__.py +0 -0
  119. solace_agent_mesh/services/authorization/providers/base_authorization_provider.py +56 -0
  120. solace_agent_mesh/services/bamboo_hr_service/__init__.py +3 -0
  121. solace_agent_mesh/services/bamboo_hr_service/bamboo_hr.py +182 -0
  122. solace_agent_mesh/services/common/__init__.py +4 -0
  123. solace_agent_mesh/services/common/auto_expiry.py +45 -0
  124. solace_agent_mesh/services/common/singleton.py +18 -0
  125. solace_agent_mesh/services/file_service/__init__.py +14 -0
  126. solace_agent_mesh/services/file_service/file_manager/__init__.py +0 -0
  127. solace_agent_mesh/services/file_service/file_manager/bucket_file_manager.py +149 -0
  128. solace_agent_mesh/services/file_service/file_manager/file_manager_base.py +162 -0
  129. solace_agent_mesh/services/file_service/file_manager/memory_file_manager.py +64 -0
  130. solace_agent_mesh/services/file_service/file_manager/volume_file_manager.py +106 -0
  131. solace_agent_mesh/services/file_service/file_service.py +432 -0
  132. solace_agent_mesh/services/file_service/file_service_constants.py +54 -0
  133. solace_agent_mesh/services/file_service/file_transformations.py +131 -0
  134. solace_agent_mesh/services/file_service/file_utils.py +322 -0
  135. solace_agent_mesh/services/file_service/transformers/__init__.py +5 -0
  136. solace_agent_mesh/services/history_service/__init__.py +3 -0
  137. solace_agent_mesh/services/history_service/history_providers/__init__.py +0 -0
  138. solace_agent_mesh/services/history_service/history_providers/base_history_provider.py +78 -0
  139. solace_agent_mesh/services/history_service/history_providers/memory_history_provider.py +167 -0
  140. solace_agent_mesh/services/history_service/history_providers/redis_history_provider.py +163 -0
  141. solace_agent_mesh/services/history_service/history_service.py +139 -0
  142. solace_agent_mesh/services/llm_service/components/llm_request_component.py +293 -0
  143. solace_agent_mesh/services/llm_service/components/llm_service_component_base.py +152 -0
  144. solace_agent_mesh/services/middleware_service/__init__.py +0 -0
  145. solace_agent_mesh/services/middleware_service/middleware_service.py +20 -0
  146. solace_agent_mesh/templates/action.py +38 -0
  147. solace_agent_mesh/templates/agent.py +29 -0
  148. solace_agent_mesh/templates/agent.yaml +70 -0
  149. solace_agent_mesh/templates/gateway-config-template.yaml +6 -0
  150. solace_agent_mesh/templates/gateway-default-config.yaml +28 -0
  151. solace_agent_mesh/templates/gateway-flows.yaml +81 -0
  152. solace_agent_mesh/templates/gateway-header.yaml +16 -0
  153. solace_agent_mesh/templates/gateway_base.py +15 -0
  154. solace_agent_mesh/templates/gateway_input.py +98 -0
  155. solace_agent_mesh/templates/gateway_output.py +71 -0
  156. solace_agent_mesh/templates/plugin-pyproject.toml +30 -0
  157. solace_agent_mesh/templates/rest-api-default-config.yaml +24 -0
  158. solace_agent_mesh/templates/rest-api-flows.yaml +80 -0
  159. solace_agent_mesh/templates/slack-default-config.yaml +9 -0
  160. solace_agent_mesh/templates/slack-flows.yaml +90 -0
  161. solace_agent_mesh/templates/solace-agent-mesh-default.yaml +77 -0
  162. solace_agent_mesh/templates/solace-agent-mesh-plugin-default.yaml +8 -0
  163. solace_agent_mesh/templates/web-default-config.yaml +5 -0
  164. solace_agent_mesh/templates/web-flows.yaml +86 -0
  165. solace_agent_mesh/tools/__init__.py +0 -0
  166. solace_agent_mesh/tools/components/__init__.py +0 -0
  167. solace_agent_mesh/tools/components/conversation_formatter.py +111 -0
  168. solace_agent_mesh/tools/components/file_resolver_component.py +58 -0
  169. solace_agent_mesh/tools/config/runtime_config.py +26 -0
  170. solace_agent_mesh-0.1.1.dist-info/METADATA +179 -0
  171. solace_agent_mesh-0.1.1.dist-info/RECORD +174 -0
  172. solace_agent_mesh-0.1.1.dist-info/entry_points.txt +3 -0
  173. solace_agent_mesh-0.0.1.dist-info/licenses/LICENSE.txt → solace_agent_mesh-0.1.1.dist-info/licenses/LICENSE +1 -2
  174. solace_agent_mesh-0.0.1.dist-info/METADATA +0 -51
  175. solace_agent_mesh-0.0.1.dist-info/RECORD +0 -5
  176. {solace_agent_mesh-0.0.1.dist-info → solace_agent_mesh-0.1.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,246 @@
1
+ import os
2
+ import re
3
+ import click
4
+ import importlib
5
+
6
+
7
+ def get_cli_root_dir():
8
+ """Get the path to the CLI root directory."""
9
+ # Get the directory of the current script
10
+ current_dir = os.path.dirname(os.path.abspath(__file__))
11
+ # Construct the root directory
12
+ return os.path.normpath(os.path.join(current_dir, ".."))
13
+
14
+
15
+ def merge_dicts(dict1, dict2):
16
+ """Merge two dictionaries recursively."""
17
+ for key, value in dict2.items():
18
+ if key in dict1 and isinstance(dict1[key], dict) and isinstance(value, dict):
19
+ merge_dicts(dict1[key], value)
20
+ elif key in dict1 and isinstance(dict1[key], list) and isinstance(value, list):
21
+ # set concat arrays
22
+ dict1[key] = list(dict1[key] + value)
23
+ else:
24
+ # dict2 takes precedence
25
+ dict1[key] = value
26
+ return dict1
27
+
28
+
29
+ def remove_duplicate(array, id_fn):
30
+ """Remove duplicates from an array based on a key function. Keeps the last occurrence."""
31
+ seen = set()
32
+ reversed_array = array[::-1]
33
+ deduplicated_array = [
34
+ x for x in reversed_array if not (id_fn(x) in seen or seen.add(id_fn(x)))
35
+ ]
36
+ return deduplicated_array[::-1]
37
+
38
+
39
+ def literal_format_template(template, literals):
40
+ for key, value in literals.items():
41
+ template = template.replace("{{" + key + "}}", value)
42
+ return template
43
+
44
+
45
+ def load_template(name, format_pair={}):
46
+ """Load a template file and format it with the given key-value pairs."""
47
+ # Construct the path to the template file using a relative path
48
+ template_file = os.path.normpath(
49
+ os.path.join(get_cli_root_dir(), "templates", name)
50
+ )
51
+
52
+ if not os.path.exists(template_file):
53
+ return None
54
+
55
+ with open(template_file, "r", encoding="utf-8") as f:
56
+ file = f.read()
57
+
58
+ file = literal_format_template(file, format_pair)
59
+
60
+ return file
61
+
62
+
63
+ def extract_yaml_env_variables(yaml_content):
64
+ """Get environment variables from a yaml file.
65
+ Env variables are annotated with the ${VAR}
66
+ """
67
+ return re.findall(r"\${(\w+)}", yaml_content)
68
+
69
+
70
+ def get_display_path(path):
71
+ """Get the display path of a file path."""
72
+ return path if os.path.isabs(path) else f"./{path}"
73
+
74
+
75
+ def log_error(message):
76
+ click.echo(click.style(message, fg="red"), err=True)
77
+
78
+
79
+ def log_link(message):
80
+ click.echo(click.style(message, fg="blue"), err=False)
81
+
82
+
83
+ def log_success(message):
84
+ click.echo(click.style(message, fg="green"), err=False)
85
+
86
+
87
+ def ask_yes_no_question(question: str, default=False) -> bool:
88
+ """Ask a yes/no question and return the answer."""
89
+ return click.confirm(question, default=default)
90
+
91
+
92
+ def ask_question(question: str, default=None) -> str:
93
+ """Ask a question and return the answer."""
94
+ return click.prompt(question, default=default)
95
+
96
+
97
+ def ask_choice_question(question: str, choices: list, default=None) -> str:
98
+ """Ask a choice question and return the answer."""
99
+ return click.prompt(question, type=click.Choice(choices), default=default)
100
+
101
+
102
+ def ask_password(question: str, default=None) -> str:
103
+ """Ask a password question and return the answer."""
104
+ return click.prompt(question, default=default, hide_input=True)
105
+
106
+
107
+ def ask_if_not_provided(
108
+ options,
109
+ key,
110
+ question,
111
+ default=None,
112
+ none_interactive=False,
113
+ choices=None,
114
+ hide_input=False,
115
+ ):
116
+ """
117
+ Ask a question if the key is not in the options.
118
+ Updates the options dictionary with the answer.
119
+ Returns the answer.
120
+ """
121
+ if key not in options or options[key] is None:
122
+ if none_interactive:
123
+ options[key] = default
124
+ elif choices:
125
+ options[key] = ask_choice_question(question, choices, default=default)
126
+ elif hide_input:
127
+ options[key] = ask_password(question, default=default)
128
+ elif isinstance(default, bool):
129
+ options[key] = ask_yes_no_question(question, default=default)
130
+ else:
131
+ options[key] = ask_question(question, default=default)
132
+ return options[key]
133
+
134
+
135
+ def apply_document_parsers(file_content, parsers, meta={}):
136
+ for parser in parsers:
137
+ file_content = parser(file_content, meta)
138
+ return file_content
139
+
140
+
141
+ def load_plugin(name, return_path_only=False):
142
+ """Load a plugin by name.
143
+
144
+ Args:
145
+ name (str): The name of the plugin to load.
146
+ return_path_only (bool, optional): Whether to return only the path to the module.
147
+
148
+ Returns:
149
+ module: The loaded module.
150
+ module_path: The path to the module. (Or only the path if return_path_only is True)
151
+ """
152
+ # Construct the path to the plugin directory using a relative path
153
+ try:
154
+ # Attempt to import the module
155
+ module = importlib.import_module(name)
156
+ module_path = module.__path__[0]
157
+ except ImportError:
158
+ if return_path_only:
159
+ return None
160
+ return None, None
161
+
162
+ if return_path_only:
163
+ return module_path
164
+
165
+ return module, module_path
166
+
167
+
168
+ def get_formatted_names(name: str):
169
+ name = name.strip()
170
+ parts = re.split(r"[\s_-]", name)
171
+ return {
172
+ "KEBAB_CASE_NAME": "-".join([word.lower() for word in parts]),
173
+ "HYPHENED_NAME": name,
174
+ "CAMEL_CASE_NAME": "".join([word.capitalize() for word in parts]),
175
+ "SNAKE_CASE_NAME": "_".join([word.lower() for word in parts]),
176
+ "SPACED_NAME": " ".join(parts),
177
+ "SNAKE_UPPER_CASE_NAME": "_".join([word.upper() for word in parts]),
178
+ }
179
+
180
+
181
+ def find_last_list_item_indent(yaml_content):
182
+ """
183
+ Finds the indentation of the last list item
184
+ Returns the number of spaces before the dash (-)
185
+ """
186
+ lines = yaml_content.splitlines()
187
+
188
+ # Look for the last line starting with '-' by iterating in reverse
189
+ for line in reversed(lines):
190
+ stripped_line = line.lstrip()
191
+ if stripped_line.startswith('-'):
192
+ return len(line) - len(stripped_line)
193
+
194
+ #default to 0
195
+ return 0
196
+
197
+
198
+ def normalize_and_reindent_yaml(reference_yaml, yaml_content):
199
+ """Given YAML content, adjust the indentation to the given target_dash_indent,
200
+ while preserving the relative indentation of the content.
201
+ """
202
+ target_dash_indent = find_last_list_item_indent(reference_yaml)
203
+
204
+ lines = yaml_content.splitlines()
205
+ if not lines:
206
+ return ""
207
+
208
+ # Find the current dash indentation in the content
209
+ current_dash_indent = None
210
+ for line in lines:
211
+ if line.lstrip().startswith('-'):
212
+ current_dash_indent = len(line) - len(line.lstrip())
213
+ break
214
+
215
+ if current_dash_indent is None:
216
+ current_dash_indent = 0
217
+
218
+ # Calculate the indentation difference
219
+ indent_difference = target_dash_indent - current_dash_indent
220
+
221
+ # Apply the indentation difference to all lines
222
+ reindented_lines = []
223
+ for line in lines:
224
+ if not line.strip():
225
+ # Preserve empty lines
226
+ reindented_lines.append(line)
227
+ else:
228
+ current_indent = len(line) - len(line.lstrip())
229
+ #prevent negative indent
230
+ new_indent = max(0, current_indent + indent_difference)
231
+ reindented_lines.append(' ' * new_indent + line.lstrip())
232
+
233
+ reindented_lines.append('\n')
234
+
235
+ return '\n'.join(reindented_lines)
236
+
237
+ def get_all_cases(name: str):
238
+ name = name.strip()
239
+ parts = re.split(r"[\s_-]", name)
240
+ return {
241
+ "KEBAB_CASE": "-".join([word.lower() for word in parts]),
242
+ "CAMEL_CASE": "".join([word.capitalize() for word in parts]),
243
+ "SNAKE_CASE": "_".join([word.lower() for word in parts]),
244
+ "SPACED": " ".join(parts),
245
+ "SNAKE_UPPER_CASE": "_".join([word.upper() for word in parts]),
246
+ }
File without changes
@@ -0,0 +1,91 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ from .action_response import ActionResponse
4
+
5
+
6
+ class Action(ABC):
7
+
8
+ def __init__(self, attributes, agent=None, config_fn=None, **kwargs):
9
+ self.validate_attributes(attributes)
10
+ self.long_description = attributes.get(
11
+ "long_description", attributes.get("prompt_directive", "<unspecified>")
12
+ )
13
+ self._prompt_directive = attributes.get("prompt_directive")
14
+ self._name = attributes.get("name")
15
+ self._params = attributes.get("params")
16
+ self._disabled = attributes.get("disabled", False)
17
+ self._examples = attributes.get("examples", [])
18
+ self._required_scopes = attributes.get("required_scopes", [])
19
+ self._config_fn = config_fn
20
+ self.agent = agent
21
+ self.kwargs = kwargs
22
+
23
+ @property
24
+ def name(self):
25
+ return self._name
26
+
27
+ @property
28
+ def disabled(self):
29
+ return self._disabled
30
+
31
+ @property
32
+ def examples(self):
33
+ return self._examples
34
+
35
+ @property
36
+ def required_scopes(self):
37
+ return self._required_scopes
38
+
39
+ def set_agent(self, agent):
40
+ self.agent = agent
41
+
42
+ def fix_scopes(self, search_value, replace_value):
43
+ self._required_scopes = [
44
+ scope.replace(search_value, replace_value)
45
+ for scope in self._required_scopes
46
+ ]
47
+
48
+ def get_agent(self):
49
+ return self.agent
50
+
51
+ def set_config_fn(self, config_fn):
52
+ self._config_fn = config_fn
53
+
54
+ def get_config(self, key=None, default=None):
55
+ if self._config_fn:
56
+ return self._config_fn(key, default)
57
+ return default
58
+
59
+ def get_prompt_summary(self, prefix="") -> dict:
60
+ if self.disabled:
61
+ return None
62
+ summary = {}
63
+ action_name = self._name
64
+ summary[action_name] = {
65
+ "desc": self._prompt_directive,
66
+ "params": [f"{param['name']} ({param['desc']})" for param in self._params],
67
+ "examples": self._examples,
68
+ "required_scopes": self._required_scopes,
69
+ }
70
+ return summary
71
+
72
+ def validate_attributes(self, attributes):
73
+ if not attributes.get("name"):
74
+ raise ValueError("Actions must have a name")
75
+ if not attributes.get("prompt_directive"):
76
+ raise ValueError("Actions must have a prompt_directive")
77
+ if attributes.get("params"):
78
+ params = attributes.get("params")
79
+ for param in params:
80
+ if not param.get("name"):
81
+ raise ValueError(
82
+ "Action attributes params must have a descriptive name"
83
+ )
84
+ if not param.get("desc"):
85
+ raise ValueError("Action attributes params must have a description")
86
+ if not param.get("type"):
87
+ raise ValueError("Action attributes params must have a type")
88
+
89
+ @abstractmethod
90
+ def invoke(self, params, meta={}) -> ActionResponse:
91
+ raise NotImplementedError("Invoke method must be implemented in subclass")
@@ -0,0 +1,37 @@
1
+ from .action import Action
2
+
3
+
4
+ class ActionList:
5
+
6
+ def __init__(self, actions_classes, **kwargs):
7
+ self.actions: Action = []
8
+ self.actions_map = {}
9
+ for action_class in actions_classes:
10
+ action = action_class(**kwargs)
11
+ self.actions.append(action)
12
+ self.actions_map[action.name] = action
13
+
14
+ def add_action(self, action):
15
+ self.actions.append(action)
16
+ self.actions_map[action.name] = action
17
+
18
+ def set_agent(self, agent):
19
+ for action in self.actions:
20
+ action.set_agent(agent)
21
+
22
+ def fix_scopes(self, search_value, replace_value):
23
+ for action in self.actions:
24
+ action.fix_scopes(search_value, replace_value)
25
+
26
+ def set_config_fn(self, config_fn):
27
+ for action in self.actions:
28
+ action.set_config_fn(config_fn)
29
+
30
+ def get_prompt_summary(self, prefix="") -> dict:
31
+ summary = []
32
+ for action in self.actions:
33
+ summary.append(action.get_prompt_summary(prefix))
34
+ return summary
35
+
36
+ def get_action(self, name):
37
+ return self.actions_map.get(name)
@@ -0,0 +1,327 @@
1
+ """This is the definition of responses for the actions of the system."""
2
+
3
+
4
+ class RagMatch:
5
+
6
+ def __init__(self, text: str, link: str = None, heading: str = None):
7
+ self._text: str = text
8
+ self._link: str = link
9
+ self._heading: str = heading
10
+
11
+ @property
12
+ def text(self) -> str:
13
+ return self._text
14
+
15
+ @property
16
+ def link(self) -> str:
17
+ return self._link
18
+
19
+ @property
20
+ def heading(self) -> str:
21
+ return self._headings
22
+
23
+ def to_dict(self) -> dict:
24
+ return {
25
+ "text": self._text,
26
+ "link": self._link,
27
+ "heading": self._heading,
28
+ }
29
+
30
+
31
+ class InlineFile:
32
+
33
+ def __init__(self, content: str, name: str, **kwargs):
34
+ if type(content) != str:
35
+ raise ValueError("InlineFile content must be a string")
36
+ self._content: str = content
37
+ self._name: str = name
38
+ self._kwargs: dict = kwargs
39
+
40
+ @property
41
+ def content(self) -> str:
42
+ return self._content
43
+
44
+ @property
45
+ def name(self) -> str:
46
+ return self._name
47
+
48
+ @property
49
+ def kwargs(self) -> dict:
50
+ return self._kwargs
51
+
52
+ def to_dict(self) -> dict:
53
+ return {
54
+ "data": self._content,
55
+ "name": self._name,
56
+ "file_size": len(self._content.encode()),
57
+ "mime_type": "text/plain",
58
+ **self._kwargs,
59
+ }
60
+
61
+
62
+ class WithContextQuery:
63
+
64
+ def __init__(
65
+ self, query: str, context_type: str, context: str = None, since: str = None
66
+ ):
67
+ self._query: str = query
68
+ self._context_type: str = context_type
69
+ self._context: str = context
70
+ self._since: str = since
71
+
72
+ @property
73
+ def query(self) -> str:
74
+ return self._query
75
+
76
+ @property
77
+ def context(self) -> str:
78
+ return self._context
79
+
80
+ @property
81
+ def context_type(self) -> str:
82
+ return self._context_type
83
+
84
+ @property
85
+ def since(self) -> dict:
86
+ return self._since
87
+
88
+ def to_dict(self) -> dict:
89
+ return {
90
+ "query": self._query,
91
+ "context_type": self._context_type,
92
+ "context": self._context,
93
+ "since": self._since,
94
+ }
95
+
96
+
97
+ class RagResponse:
98
+
99
+ def __init__(
100
+ self, data_source: str, matches: list[RagMatch], query: str, prompt: str = None
101
+ ):
102
+ self._data_source: str = data_source
103
+ self._matches: list[RagMatch] = matches
104
+ self._query: str = query
105
+ self._prompt: str = prompt
106
+
107
+ @property
108
+ def data_source(self) -> str:
109
+ return self._data_source
110
+
111
+ @property
112
+ def matches(self) -> list[RagMatch]:
113
+ return self._matches
114
+
115
+ @property
116
+ def query(self) -> str:
117
+ return self._query
118
+
119
+ @property
120
+ def prompt(self) -> str:
121
+ return self._prompt
122
+
123
+ def to_dict(self) -> dict:
124
+ return {
125
+ "data_source": self._data_source,
126
+ "matches": [match.to_dict() for match in self._matches],
127
+ "query": self._query,
128
+ "prompt": self._prompt,
129
+ }
130
+
131
+
132
+ class AgentStateChange:
133
+
134
+ def __init__(self, agent_name: str, new_state: str):
135
+ self._agent_name: str = agent_name
136
+ self._new_state: str = new_state
137
+
138
+ @property
139
+ def agent_name(self) -> str:
140
+ return self._agent_name
141
+
142
+ @property
143
+ def new_state(self) -> str:
144
+ return self._new_state
145
+
146
+ def to_dict(self) -> dict:
147
+ return {"agent_name": self._agent_name, "new_state": self._new_state}
148
+
149
+
150
+ class ErrorInfo:
151
+
152
+ def __init__(self, error_message: str):
153
+ self._error_message: str = error_message
154
+
155
+ @property
156
+ def error_message(self) -> str:
157
+ return self._error_message
158
+
159
+ def __str__(self) -> str:
160
+ return self._error_message
161
+
162
+ def to_dict(self) -> dict:
163
+ return {"error_message": self._error_message}
164
+
165
+
166
+ class ActionResponse:
167
+
168
+ def __init__(
169
+ self,
170
+ message: any = None,
171
+ files: list[str] = None,
172
+ inline_files: list[InlineFile] = None,
173
+ clear_history: bool = False,
174
+ history_depth_to_keep: int = None,
175
+ error_info: ErrorInfo = None,
176
+ agent_state_change: AgentStateChange = None,
177
+ invoke_model_again: bool = False,
178
+ context_query: WithContextQuery = None,
179
+ is_async: bool = False,
180
+ async_response_id: str = None,
181
+ ):
182
+ # Message to return - this could be a string or a slack blocks message
183
+ self._message: str = message
184
+ # Files to return - this is a list of temporary files to return
185
+ self._files: list[str] = files
186
+ # Inline files to return - this is a list of inline files to return
187
+ self._inline_files: list[InlineFile] = inline_files
188
+ # Clear history - this is a flag to clear the chat history
189
+ self._clear_history: bool = clear_history
190
+ # Clear history but keep depth - this is a flag to clear the chat history but keep
191
+ # the last N messages
192
+ self._history_depth_to_keep: int = history_depth_to_keep
193
+ # Error info - this is a dictionary of error information (error_message)
194
+ self._error_info: ErrorInfo = error_info
195
+ # Agent state change - this is a dictionary of agent state change
196
+ # information (agent_name, new_state)
197
+ self._agent_state_change: AgentStateChange = agent_state_change
198
+ # Invoke model again - this is a flag to indicate if the model should be
199
+ # invoked again with the same input
200
+ self._invoke_model_again: bool = invoke_model_again
201
+ # Context query - this contains the query and context type to be used in the next action
202
+ self._context_query: WithContextQuery = context_query
203
+ # action_list_id - identifier of the action_list that this action response is associated with
204
+ self._action_list_id: str = None
205
+ # action_idx - index of the action in the action_list that this action response is associated with
206
+ self._action_idx: int = None
207
+ # action_name - name of the action that this action response is associated with
208
+ self._action_name: str = None
209
+ # action_params - parameters of the action that this action response is associated with
210
+ self._action_params: dict = None
211
+ # is_async - indicates if this is an async response that will be delivered later
212
+ self._is_async: bool = is_async
213
+ # async_response_id - unique identifier for correlating async responses
214
+ self._async_response_id: str = async_response_id
215
+
216
+ @property
217
+ def message(self) -> any:
218
+ return self._message
219
+
220
+ @message.setter
221
+ def message(self, message: any):
222
+ self._message = message
223
+
224
+ @property
225
+ def files(self) -> list[str]:
226
+ return self._files
227
+
228
+ @property
229
+ def inline_files(self) -> list[InlineFile]:
230
+ return self._inline_files
231
+
232
+ @property
233
+ def clear_history(self) -> bool:
234
+ return self._clear_history
235
+
236
+ @property
237
+ def history_depth_to_keep(self) -> int:
238
+ return self._history_depth_to_keep
239
+
240
+ @property
241
+ def error_info(self) -> dict:
242
+ return self._error_info
243
+
244
+ @property
245
+ def agent_state_change(self) -> AgentStateChange:
246
+ return self._agent_state_change
247
+
248
+ @property
249
+ def invoke_model_again(self) -> bool:
250
+ return self._invoke_model_again
251
+
252
+ @property
253
+ def context_query(self) -> WithContextQuery:
254
+ return self._context_query
255
+
256
+ @property
257
+ def action_list_id(self) -> str:
258
+ return self._action_list_id
259
+
260
+ @property
261
+ def action_idx(self) -> int:
262
+ return self._action_idx
263
+
264
+ @property
265
+ def action_name(self) -> str:
266
+ return self._action_name
267
+
268
+ @property
269
+ def action_params(self) -> dict:
270
+ return self._action_params
271
+
272
+ @action_list_id.setter
273
+ def action_list_id(self, action_list_id: str):
274
+ self._action_list_id = action_list_id
275
+
276
+ @action_idx.setter
277
+ def action_idx(self, action_idx: int):
278
+ self._action_idx = action_idx
279
+
280
+ @action_name.setter
281
+ def action_name(self, action_name: str):
282
+ self._action_name = action_name
283
+
284
+ @action_params.setter
285
+ def action_params(self, action_params: dict):
286
+ self._action_params = action_params
287
+
288
+ @property
289
+ def is_async(self) -> bool:
290
+ return self._is_async
291
+
292
+ @property
293
+ def async_response_id(self) -> str:
294
+ return self._async_response_id
295
+
296
+ def to_dict(self) -> dict:
297
+ response = {}
298
+ if self._message:
299
+ response["message"] = self._message
300
+ if self._is_async:
301
+ response["is_async"] = True
302
+ response["async_response_id"] = self._async_response_id
303
+ if self._files:
304
+ # Files are already dictionary
305
+ response["files"] = self._files
306
+ if self._inline_files:
307
+ # Add to the files list
308
+ response["files"] = response.get("files", []) + [
309
+ inline_file.to_dict() for inline_file in self._inline_files
310
+ ]
311
+ if self._clear_history:
312
+ response["clear_history"] = self._clear_history
313
+ if self._history_depth_to_keep:
314
+ response["history_depth_to_keep"] = self._history_depth_to_keep
315
+ if self._error_info:
316
+ response["error_info"] = self._error_info.to_dict()
317
+ if self._agent_state_change:
318
+ response["agent_state_change"] = self._agent_state_change.to_dict()
319
+ if self._invoke_model_again:
320
+ response["invoke_model_again"] = self._invoke_model_again
321
+ if self._context_query:
322
+ response["context_query"] = self._context_query.to_dict()
323
+ response["action_list_id"] = self._action_list_id
324
+ response["action_idx"] = self._action_idx
325
+ response["action_name"] = self._action_name
326
+ response["action_params"] = self._action_params
327
+ return response
@@ -0,0 +1,3 @@
1
+ SOLACE_AGENT_MESH_SYSTEM_SESSION_ID = "solace_agent_mesh_system_session_id"
2
+
3
+ DEFAULT_IDENTITY_KEY_FIELD = "identity"