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,631 @@
1
+ import os
2
+ import sys
3
+ import click
4
+ import shutil
5
+ import re
6
+ from functools import partial
7
+
8
+ from cli.commands.init import init_command
9
+ from cli.commands.plugin.build import get_all_plugin_gateway_interfaces, build_plugins
10
+ from cli.config import Config
11
+ from cli.utils import (
12
+ literal_format_template,
13
+ load_template,
14
+ get_cli_root_dir,
15
+ extract_yaml_env_variables,
16
+ get_display_path,
17
+ log_error,
18
+ apply_document_parsers,
19
+ find_last_list_item_indent,
20
+ normalize_and_reindent_yaml
21
+ )
22
+
23
+
24
+ def abort_cleanup(build_dir):
25
+ """Abort the build and cleanup the build directory."""
26
+ log_error("Build aborted.")
27
+ click.echo("Cleaning up build directory.")
28
+ os.system(f"rm -rf {build_dir}")
29
+ sys.exit(1)
30
+
31
+
32
+ def write_env_variables(env_variables, output_file, default_env_vars):
33
+ """Extract environment variables to a file."""
34
+
35
+ try:
36
+ env_variables = list(sorted(env_variables))
37
+ # Check if directory exists
38
+ if not os.path.exists(output_file):
39
+ dir_name = os.path.dirname(output_file)
40
+ if dir_name:
41
+ os.makedirs(dir_name, exist_ok=True)
42
+
43
+ existing_env_variables = []
44
+ # Check if the file exists
45
+ if os.path.exists(output_file):
46
+ # Read existing env variables
47
+ with open(output_file, "r", encoding="utf-8") as f:
48
+ lines = f.read().splitlines()
49
+ existing_env_variables = [
50
+ line.split("=")[0].strip() for line in lines if "=" in line
51
+ ]
52
+
53
+ # Remove existing env variables
54
+ env_variables = [
55
+ env_var
56
+ for env_var in env_variables
57
+ if env_var not in existing_env_variables
58
+ ]
59
+
60
+ # Create env file content
61
+ env_file_content = ""
62
+ for env_var in env_variables:
63
+ env_file_content += f"{env_var}=\n"
64
+
65
+ # Add default env variables
66
+ for key, value in default_env_vars.items():
67
+ if key not in existing_env_variables:
68
+ env_file_content += f"{key}={value}\n"
69
+
70
+ # Append to the file
71
+ with open(output_file, "a", encoding="utf-8") as f:
72
+ f.write(env_file_content)
73
+ click.echo(
74
+ f"Environment variables extracted to {get_display_path(output_file)}"
75
+ )
76
+ except Exception as e:
77
+ click.echo(f"Something went wrong in extract environment variables to a file")
78
+
79
+
80
+ def resolve_relative_import_path_hof(module_name):
81
+ """Replaces all src.path imports with the the provided module name
82
+ Only paths that are in the packages artifact are replaced
83
+ """
84
+ root_dir = get_cli_root_dir()
85
+ # All directories in the root directory
86
+ directories = [
87
+ d for d in os.listdir(root_dir) if os.path.isdir(os.path.join(root_dir, d))
88
+ ]
89
+ # Acceptable relative directories
90
+ relative_directories_pair = [
91
+ (f" src.{dir}", f" {module_name}.{dir}") for dir in directories
92
+ ]
93
+
94
+ def resolve_relative_import_path(file_content, meta):
95
+ if "skip_relative_import_path" in meta and meta["skip_relative_import_path"]:
96
+ return file_content
97
+ for relative_dir, module_dir in relative_directories_pair:
98
+ file_content = file_content.replace(relative_dir, module_dir)
99
+ return file_content
100
+
101
+ return resolve_relative_import_path
102
+
103
+
104
+ def build_agents(config, build_config_dir, abort, parsers):
105
+ configs_path = config["config_directory"]
106
+ agent_config_path = os.path.join(configs_path, "agents")
107
+ # Check if the agents directory exists
108
+ if not os.path.exists(agent_config_path):
109
+ click.echo(
110
+ f"No user defined agents were found at '{get_display_path(agent_config_path)}'."
111
+ )
112
+ else:
113
+ click.echo("Building agents.")
114
+ # Get all agent configuration files
115
+ agent_configs = [
116
+ f for f in os.listdir(agent_config_path) if f.endswith(".yaml")
117
+ ]
118
+ for agent_config in agent_configs:
119
+ try:
120
+ # Read agent configuration file
121
+ agent_config_file = os.path.join(agent_config_path, agent_config)
122
+ with open(agent_config_file, "r", encoding="utf-8") as f:
123
+ agent_config_content = f.read()
124
+ # Apply document parsers
125
+ meta = {"skip_relative_import_path": True}
126
+ agent_config_content = apply_document_parsers(
127
+ agent_config_content, parsers, meta
128
+ )
129
+ # Write agent configuration to build directory
130
+ agent_build_path = os.path.join(
131
+ build_config_dir, "agent_" + agent_config
132
+ )
133
+ with open(agent_build_path, "w", encoding="utf-8") as f:
134
+ f.write(agent_config_content)
135
+ except IOError as e:
136
+ log_error(
137
+ f'Error reading agent configuration file for "{agent_config}": {e}'
138
+ )
139
+ abort()
140
+ except Exception as e:
141
+ log_error(
142
+ f'Error building agent configuration for "{agent_config}": {e}'
143
+ )
144
+ abort()
145
+
146
+
147
+ def build_gateways(config, build_config_dir, abort, parsers, plugin_gateway_interfaces):
148
+ configs_path = config["config_directory"]
149
+ gateways_config_path = os.path.join(configs_path, "gateways")
150
+
151
+ # Check if the gateways directory exists
152
+ if not os.path.exists(gateways_config_path):
153
+ click.echo(
154
+ f"No user defined gateways were found at '{get_display_path(gateways_config_path)}'."
155
+ )
156
+ else:
157
+ click.echo("Building gateways.")
158
+
159
+ # Loop over the subdirectories of gateways_config_path
160
+ for subdir in os.listdir(gateways_config_path):
161
+ build_specific_gateway(
162
+ build_config_dir,
163
+ abort,
164
+ parsers,
165
+ gateways_config_path,
166
+ subdir,
167
+ plugin_gateway_interfaces,
168
+ )
169
+
170
+
171
+ def build_specific_gateway(
172
+ build_config_dir,
173
+ abort,
174
+ parsers,
175
+ gateways_config_path,
176
+ gateway_name,
177
+ known_plugin_interfaces=[],
178
+ ):
179
+ subdir_path = os.path.join(gateways_config_path, gateway_name)
180
+ if os.path.isdir(subdir_path):
181
+ click.echo(f"Building gateway in subdirectory: {gateway_name}")
182
+
183
+ try:
184
+ click.echo("Building gateway template.")
185
+ # Load common header and gateway config
186
+ gateway_header_template = load_template("gateway-header.yaml")
187
+ gateway_config_file = os.path.join(subdir_path, "gateway.yaml")
188
+ with open(gateway_config_file, "r", encoding="utf-8") as g:
189
+ gateway_config_content = g.read()
190
+
191
+ click.echo("Getting interface types.")
192
+ known_interfaces = ["slack", "web", "rest-api"]
193
+ interface_files = []
194
+
195
+ # Find the interface types by looking at the files in the subdirectory
196
+ for file in os.listdir(subdir_path):
197
+ if (
198
+ file.endswith(".yaml")
199
+ and file != "gateway.yaml"
200
+ and not file.endswith("-flows.yaml")
201
+ ):
202
+ interface_files.append(file)
203
+
204
+ # Process interfaces
205
+ for interface_file in interface_files:
206
+ interface_name = interface_file.split(".yaml")[0]
207
+ if gateway_name == interface_name:
208
+ interface_config = (
209
+ f"gateway_{gateway_name}.yaml"
210
+ )
211
+ gateway_id = gateway_name
212
+ else:
213
+ interface_config = (
214
+ f"gateway_{gateway_name}_{interface_name.replace('-', '_')}.yaml"
215
+ )
216
+ gateway_id = f"{gateway_name}_{interface_name}"
217
+ interface_build_path = os.path.join(build_config_dir, interface_config)
218
+
219
+ complete_interface_gateway = gateway_header_template
220
+
221
+ reindented_gateway_config_content = normalize_and_reindent_yaml(complete_interface_gateway, gateway_config_content)
222
+ complete_interface_gateway += reindented_gateway_config_content
223
+
224
+ # interface specific config
225
+ interface_config_file = os.path.join(subdir_path, interface_file)
226
+ with open(interface_config_file, "r", encoding="utf-8") as g:
227
+ file_content = g.read()
228
+ reindented_file_content = normalize_and_reindent_yaml(complete_interface_gateway, file_content)
229
+ complete_interface_gateway += reindented_file_content
230
+
231
+ # Write interface specific flows
232
+ complete_interface_gateway += "\nflows:\n"
233
+
234
+ if interface_name in known_interfaces:
235
+ custom_interface_flows_path = os.path.join(
236
+ subdir_path, f"{interface_name}-flows.yaml"
237
+ )
238
+ # First check for custom flow file, fall back to template
239
+ if os.path.exists(custom_interface_flows_path):
240
+ with open(
241
+ custom_interface_flows_path, "r", encoding="utf-8"
242
+ ) as g:
243
+ flow_content = g.read()
244
+ else:
245
+ flow_content = load_template(f"{interface_name}-flows.yaml")
246
+ complete_interface_gateway += flow_content
247
+ elif interface_name in known_plugin_interfaces:
248
+ # Write interface specific flows
249
+ interface_flows_path = os.path.join(
250
+ known_plugin_interfaces[interface_name],
251
+ f"{interface_name}-flows.yaml",
252
+ )
253
+ try:
254
+ with open(interface_flows_path, "r", encoding="utf-8") as g:
255
+ interface_flow_template_file = g.read()
256
+ except IOError as e:
257
+ log_error(
258
+ f'Expected plugin flow file "{interface_name}-flows.yaml" is not present.'
259
+ )
260
+ abort()
261
+ complete_interface_gateway += interface_flow_template_file
262
+ else:
263
+ # This is a custom interface. The related flows can be in
264
+ # the same directory as the interface config and end with -flows.yaml
265
+ # or it will use the default flows template
266
+ custom_interface_flows_path = os.path.join(
267
+ subdir_path, f"{interface_name}-flows.yaml"
268
+ )
269
+
270
+ if os.path.exists(custom_interface_flows_path):
271
+ with open(
272
+ custom_interface_flows_path, "r", encoding="utf-8"
273
+ ) as g:
274
+ interface_flow_template_file = g.read()
275
+ else:
276
+ interface_flow_template_file = load_template("gateway-flows.yaml")
277
+
278
+ complete_interface_gateway += interface_flow_template_file
279
+
280
+ # Apply parsers to gateway config for this interface
281
+ meta = {
282
+ "extra_literals": {
283
+ "GATEWAY_ID": gateway_id,
284
+ "SNAKE_CASE_NAME": gateway_id.replace("-", "_"),
285
+ }
286
+ }
287
+
288
+ complete_interface_gateway = apply_document_parsers(
289
+ complete_interface_gateway, parsers, meta
290
+ )
291
+ with open(interface_build_path, "w", encoding="utf-8") as f:
292
+ f.write(complete_interface_gateway)
293
+
294
+ except IOError as e:
295
+ log_error(
296
+ f'Error reading gateway configuration file for "{gateway_name}": {e}'
297
+ )
298
+ abort()
299
+ except Exception as e:
300
+ log_error(f'Error building gateway configuration for "{gateway_name}": {e}')
301
+ abort()
302
+
303
+
304
+ def build_built_in_agents(config, build_config_dir, abort, parsers):
305
+ ### Built-in agents
306
+ click.echo("Building built-in agents.")
307
+ built_in_agents = config["built_in"]["agents"]
308
+ filtered_agent_names = [
309
+ f"agent_{agent.get('name')}.yaml"
310
+ for agent in built_in_agents
311
+ if agent.get("name") and agent.get("enabled")
312
+ ]
313
+ agents_config_source_path = os.path.join(get_cli_root_dir(), "configs")
314
+
315
+ for agent in filtered_agent_names:
316
+ # Check if agent exists
317
+ agent_config_file = os.path.join(agents_config_source_path, agent)
318
+ if not os.path.exists(agent_config_file):
319
+ log_error(
320
+ f"Error: Built-in agent configuration file for {agent} not found.",
321
+ )
322
+ abort()
323
+ try:
324
+ # Read agent configuration file
325
+ with open(agent_config_file, "r", encoding="utf-8") as f:
326
+ agent_config_content = f.read()
327
+ # Apply document parsers
328
+ agent_config_content = apply_document_parsers(agent_config_content, parsers)
329
+ # Write agent configuration to build directory
330
+ agent_build_path = os.path.join(build_config_dir, agent)
331
+ with open(agent_build_path, "w", encoding="utf-8") as f:
332
+ f.write(agent_config_content)
333
+ except IOError as e:
334
+ log_error(
335
+ f"Error reading built-in agent configuration file for {agent}: {e}",
336
+ )
337
+ abort()
338
+ except Exception as e:
339
+ log_error(
340
+ f"Error building built-in agent configuration for {agent}: {e}",
341
+ )
342
+ abort()
343
+
344
+
345
+ def build_solace_agent_mesh(config, build_config_dir, abort, parsers):
346
+ click.echo("Building configs required for Solace Agent Mesh.")
347
+ configs_source_path = os.path.join(get_cli_root_dir(), "configs")
348
+ # Prefixes to skip
349
+ skip_prefixes = ["agent", "gateway", "_"]
350
+ # Get all config file names
351
+ config_files = [
352
+ f
353
+ for f in os.listdir(configs_source_path)
354
+ if f.endswith(".yaml")
355
+ and not any(f.startswith(prefix) for prefix in skip_prefixes)
356
+ ]
357
+ for config_file in config_files:
358
+ try:
359
+ # Read config file
360
+ config_file_path = os.path.join(configs_source_path, config_file)
361
+ with open(config_file_path, "r", encoding="utf-8") as f:
362
+ config_content = f.read()
363
+ if not config_content.strip():
364
+ continue
365
+ # Apply document parsers
366
+ config_content = apply_document_parsers(config_content, parsers)
367
+ # Write config to build directory
368
+ config_build_path = os.path.join(build_config_dir, config_file)
369
+ with open(config_build_path, "w", encoding="utf-8") as f:
370
+ f.write(config_content)
371
+ except IOError as e:
372
+ log_error(
373
+ f'Error reading config file for "{config_file}": {e}',
374
+ )
375
+ abort()
376
+ except Exception as e:
377
+ log_error(
378
+ f'Error building config file for "{config_file}": {e}',
379
+ )
380
+ abort()
381
+
382
+
383
+ def build_runtime_config(config, build_config_dir, abort):
384
+ runtime_config_path = os.path.join(build_config_dir, "config.yaml")
385
+ runtime_config = config.get("runtime", {})
386
+ try:
387
+ Config.write_config(runtime_config, runtime_config_path)
388
+ click.echo(f"Created runtime config at {get_display_path(runtime_config_path)}")
389
+ except IOError as e:
390
+ log_error(f"Error writing runtime config file: {e}")
391
+ abort()
392
+
393
+
394
+ def build_force_overwrite(config, build_config_dir, abort, parsers, overwrite_src_path):
395
+ """
396
+ Overwriting default system config files with user provide overwrite files
397
+ """
398
+ # Check if the overwrite directory exists
399
+ if not os.path.exists(overwrite_src_path):
400
+ return
401
+ click.echo(
402
+ f"Force overwriting default config files with provided files from '{get_display_path(overwrite_src_path)}'."
403
+ )
404
+ overwrite_files = [f for f in os.listdir(overwrite_src_path) if f.endswith(".yaml")]
405
+ for overwrite_file in overwrite_files:
406
+ try:
407
+ # Read overwrite file
408
+ overwrite_file_path = os.path.join(overwrite_src_path, overwrite_file)
409
+ with open(overwrite_file_path, "r", encoding="utf-8") as f:
410
+ overwrite_content = f.read()
411
+ if not overwrite_content.strip():
412
+ continue
413
+ # Apply document parsers
414
+ overwrite_content = apply_document_parsers(overwrite_content, parsers)
415
+ # Write overwrite to build directory
416
+ overwrite_build_path = os.path.join(build_config_dir, overwrite_file)
417
+ with open(overwrite_build_path, "w", encoding="utf-8") as f:
418
+ f.write(overwrite_content)
419
+ except IOError as e:
420
+ log_error(
421
+ f'Error reading overwrite file for "{overwrite_file}": {e}',
422
+ )
423
+ abort()
424
+ except Exception as e:
425
+ log_error(
426
+ f'Error building overwrite file for "{overwrite_file}": {e}',
427
+ )
428
+ abort()
429
+
430
+
431
+ def build_command(skip_without_asking=False, no_init=False):
432
+ """Build the Solace Agent Mesh application."""
433
+ config_path = Config.user_config_file
434
+ config = click.get_current_context().obj["solace_agent_mesh"]
435
+
436
+ if not os.path.exists(config_path) and not no_init:
437
+ init_command({"skip": skip_without_asking})
438
+ config = Config.get_config(config_path).get("solace_agent_mesh")
439
+
440
+ click.echo("Building Solace Agent Mesh application")
441
+ configs_path = config["config_directory"]
442
+ modules_path = config["modules_directory"]
443
+ build_dir = config["build"]["build_directory"]
444
+
445
+ build_config_dir = os.path.join(build_dir, "configs")
446
+ abort = partial(abort_cleanup, build_dir)
447
+
448
+ skip_user_configs = False
449
+
450
+ # List of all required env variables
451
+ env_variables = set()
452
+
453
+ orchestrator_instance_count = str(config["build"].get("orchestrator_instance_count"))
454
+ # check if it's a number
455
+ if not orchestrator_instance_count.isdigit():
456
+ log_error("Orchestrator instance count must be a number.")
457
+ abort()
458
+
459
+ # Format literals
460
+ format_literals = {
461
+ "MODULE_DIRECTORY": modules_path.replace("\\", ".").replace("/", "."),
462
+ "ORCHESTRATOR_INSTANCE_COUNT": orchestrator_instance_count,
463
+ }
464
+
465
+ ####################
466
+ # Document Parsers #
467
+ ####################
468
+ parsers = []
469
+
470
+ ### ENV Parser
471
+ def extract_env_var(content, _):
472
+ # Extract environment variables from the config
473
+ variables = extract_yaml_env_variables(content)
474
+ for var in variables:
475
+ env_variables.add(var)
476
+ return content
477
+
478
+ parsers.append(extract_env_var)
479
+
480
+ ### Log level override
481
+ log_level = config["build"].get("log_level_override")
482
+
483
+ def log_level_override(content, _):
484
+ if log_level:
485
+ content = re.sub(
486
+ r"log_file_level: \w+", f"log_file_level: {log_level}", content
487
+ )
488
+ content = re.sub(
489
+ r"stdout_log_level: \w+", f"stdout_log_level: {log_level}", content
490
+ )
491
+ return content
492
+
493
+ parsers.append(log_level_override)
494
+ ### Relative import path resolver Parser
495
+ resolve_relative_import_path = resolve_relative_import_path_hof("solace_agent_mesh")
496
+ parsers.append(resolve_relative_import_path)
497
+
498
+ ### Literal formatter Parser
499
+ def format_literals_parser(content, meta):
500
+ if "extra_literals" in meta and meta["extra_literals"]:
501
+ return literal_format_template(
502
+ content,
503
+ {
504
+ **format_literals,
505
+ **meta["extra_literals"],
506
+ },
507
+ )
508
+ return literal_format_template(content, format_literals)
509
+
510
+ parsers.append(format_literals_parser)
511
+
512
+ ########################
513
+ # Handling Directories #
514
+ ########################
515
+
516
+ # Check if the config directory exists
517
+ if not os.path.exists(configs_path):
518
+ if skip_without_asking:
519
+ click.echo(
520
+ f"No user defined components were found at '{get_display_path(configs_path)}'."
521
+ )
522
+ skip_user_configs = True
523
+ else:
524
+ skip_user_configs = click.confirm(
525
+ f"No user defined components were found at '{get_display_path(configs_path)}'. Do you want to continue the build?",
526
+ default=False,
527
+ )
528
+ if not skip_user_configs:
529
+ log_error("Build aborted.")
530
+ return 1
531
+
532
+ try:
533
+ # Delete build directory if it exists
534
+ if os.path.exists(build_dir):
535
+ os.system(f"rm -rf {build_dir}")
536
+ # Create recursive directories if they don't exist
537
+ os.makedirs(build_config_dir, exist_ok=True)
538
+ except IOError as e:
539
+ log_error(f"Error creating build directory: {e}")
540
+ abort()
541
+
542
+ plugin_gateway_interfaces = get_all_plugin_gateway_interfaces(config, abort)
543
+ ####################
544
+ # Building Plugins #
545
+ ####################
546
+ if "plugins" in config and isinstance(config["plugins"], list):
547
+ build_plugins(
548
+ config,
549
+ build_config_dir,
550
+ abort,
551
+ parsers,
552
+ plugin_gateway_interfaces,
553
+ build_specific_gateway,
554
+ )
555
+
556
+ ########################
557
+ # User Defined Configs #
558
+ ########################
559
+ if skip_user_configs:
560
+ click.echo("Skipping user defined components.")
561
+ else:
562
+ ####################
563
+ # Building Modules #
564
+ ####################
565
+ try:
566
+ if not os.path.exists(modules_path):
567
+ click.echo(
568
+ f"No user defined modules were found at '{get_display_path(modules_path)}'."
569
+ )
570
+ else:
571
+ click.echo("Building modules.")
572
+ os.system(f"cp -r {modules_path} {build_dir}")
573
+ except IOError as e:
574
+ log_error(f"Error copying modules directory: {e}")
575
+ abort()
576
+
577
+ ###################
578
+ # Building Agents #
579
+ ###################
580
+ build_agents(config, build_config_dir, abort, parsers)
581
+
582
+ #####################
583
+ # Building Gateways #
584
+ #####################
585
+ build_gateways(
586
+ config, build_config_dir, abort, parsers, plugin_gateway_interfaces
587
+ )
588
+
589
+ ############################
590
+ # Building Built-in Agents #
591
+ ############################
592
+ build_built_in_agents(config, build_config_dir, abort, parsers)
593
+
594
+ ###########################
595
+ # Building Solace Agent Mesh #
596
+ ###########################
597
+ build_solace_agent_mesh(config, build_config_dir, abort, parsers)
598
+
599
+ ###########################
600
+ # Building Runtime Config #
601
+ ###########################
602
+ build_runtime_config(config, build_dir, abort)
603
+
604
+ ############################
605
+ # Building Force Overwrite #
606
+ ############################
607
+ overwrite_src_path = config.get("overwrite_directory")
608
+ overwrite_plugins_path = os.path.join(build_dir, "overwrites")
609
+ if os.path.exists(overwrite_plugins_path):
610
+ build_force_overwrite(
611
+ config, build_config_dir, abort, parsers, overwrite_plugins_path
612
+ )
613
+ # remove the overwrite directory
614
+ shutil.rmtree(overwrite_plugins_path)
615
+
616
+ build_force_overwrite(config, build_config_dir, abort, parsers, overwrite_src_path)
617
+
618
+ ####################
619
+ # Default ENV Vars #
620
+ ####################
621
+ default_env_vars = {
622
+ "RUNTIME_CONFIG_PATH": os.path.join(build_dir, "config.yaml"),
623
+ }
624
+ # Handle environment variables
625
+ if config["build"]["extract_env_vars"]:
626
+ write_env_variables(env_variables, config["env_file"], default_env_vars)
627
+
628
+ # Build completed
629
+ click.echo(click.style("Build completed.", bold=True, fg="green"))
630
+ click.echo(f"\tBuild directory: {get_display_path(build_dir)}")
631
+ return 0
@@ -0,0 +1,3 @@
1
+ from .chat import chat_command
2
+
3
+ __all__ = ["chat_command"]