solace-agent-mesh 0.0.1__py3-none-any.whl → 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of solace-agent-mesh might be problematic. Click here for more details.
- solace_agent_mesh/__init__.py +0 -3
- solace_agent_mesh/agents/__init__.py +0 -0
- solace_agent_mesh/agents/base_agent_component.py +224 -0
- solace_agent_mesh/agents/global/__init__.py +0 -0
- solace_agent_mesh/agents/global/actions/__init__.py +0 -0
- solace_agent_mesh/agents/global/actions/agent_state_change.py +54 -0
- solace_agent_mesh/agents/global/actions/clear_history.py +32 -0
- solace_agent_mesh/agents/global/actions/convert_file_to_markdown.py +160 -0
- solace_agent_mesh/agents/global/actions/create_file.py +70 -0
- solace_agent_mesh/agents/global/actions/error_action.py +45 -0
- solace_agent_mesh/agents/global/actions/plantuml_diagram.py +93 -0
- solace_agent_mesh/agents/global/actions/plotly_graph.py +117 -0
- solace_agent_mesh/agents/global/actions/retrieve_file.py +51 -0
- solace_agent_mesh/agents/global/global_agent_component.py +38 -0
- solace_agent_mesh/agents/image_processing/__init__.py +0 -0
- solace_agent_mesh/agents/image_processing/actions/__init__.py +0 -0
- solace_agent_mesh/agents/image_processing/actions/create_image.py +75 -0
- solace_agent_mesh/agents/image_processing/actions/describe_image.py +115 -0
- solace_agent_mesh/agents/image_processing/image_processing_agent_component.py +23 -0
- solace_agent_mesh/agents/slack/__init__.py +1 -0
- solace_agent_mesh/agents/slack/actions/__init__.py +1 -0
- solace_agent_mesh/agents/slack/actions/post_message.py +177 -0
- solace_agent_mesh/agents/slack/slack_agent_component.py +59 -0
- solace_agent_mesh/agents/web_request/__init__.py +0 -0
- solace_agent_mesh/agents/web_request/actions/__init__.py +0 -0
- solace_agent_mesh/agents/web_request/actions/do_image_search.py +84 -0
- solace_agent_mesh/agents/web_request/actions/do_news_search.py +47 -0
- solace_agent_mesh/agents/web_request/actions/do_suggestion_search.py +34 -0
- solace_agent_mesh/agents/web_request/actions/do_web_request.py +134 -0
- solace_agent_mesh/agents/web_request/actions/download_file.py +69 -0
- solace_agent_mesh/agents/web_request/web_request_agent_component.py +33 -0
- solace_agent_mesh/assets/web-visualizer/assets/index-C5awueeJ.js +109 -0
- solace_agent_mesh/assets/web-visualizer/assets/index-D0qORgkg.css +1 -0
- solace_agent_mesh/assets/web-visualizer/index.html +14 -0
- solace_agent_mesh/assets/web-visualizer/vite.svg +1 -0
- solace_agent_mesh/cli/__init__.py +1 -0
- solace_agent_mesh/cli/commands/__init__.py +0 -0
- solace_agent_mesh/cli/commands/add/__init__.py +3 -0
- solace_agent_mesh/cli/commands/add/add.py +88 -0
- solace_agent_mesh/cli/commands/add/agent.py +110 -0
- solace_agent_mesh/cli/commands/add/copy_from_plugin.py +90 -0
- solace_agent_mesh/cli/commands/add/gateway.py +221 -0
- solace_agent_mesh/cli/commands/build.py +631 -0
- solace_agent_mesh/cli/commands/chat/__init__.py +3 -0
- solace_agent_mesh/cli/commands/chat/chat.py +361 -0
- solace_agent_mesh/cli/commands/config.py +29 -0
- solace_agent_mesh/cli/commands/init/__init__.py +3 -0
- solace_agent_mesh/cli/commands/init/ai_provider_step.py +76 -0
- solace_agent_mesh/cli/commands/init/broker_step.py +102 -0
- solace_agent_mesh/cli/commands/init/builtin_agent_step.py +88 -0
- solace_agent_mesh/cli/commands/init/check_if_already_done.py +13 -0
- solace_agent_mesh/cli/commands/init/create_config_file_step.py +52 -0
- solace_agent_mesh/cli/commands/init/create_other_project_files_step.py +96 -0
- solace_agent_mesh/cli/commands/init/file_service_step.py +73 -0
- solace_agent_mesh/cli/commands/init/init.py +114 -0
- solace_agent_mesh/cli/commands/init/project_structure_step.py +45 -0
- solace_agent_mesh/cli/commands/init/rest_api_step.py +50 -0
- solace_agent_mesh/cli/commands/init/web_ui_step.py +40 -0
- solace_agent_mesh/cli/commands/plugin/__init__.py +3 -0
- solace_agent_mesh/cli/commands/plugin/add.py +98 -0
- solace_agent_mesh/cli/commands/plugin/build.py +217 -0
- solace_agent_mesh/cli/commands/plugin/create.py +117 -0
- solace_agent_mesh/cli/commands/plugin/plugin.py +109 -0
- solace_agent_mesh/cli/commands/plugin/remove.py +71 -0
- solace_agent_mesh/cli/commands/run.py +68 -0
- solace_agent_mesh/cli/commands/visualizer.py +138 -0
- solace_agent_mesh/cli/config.py +81 -0
- solace_agent_mesh/cli/main.py +306 -0
- solace_agent_mesh/cli/utils.py +246 -0
- solace_agent_mesh/common/__init__.py +0 -0
- solace_agent_mesh/common/action.py +91 -0
- solace_agent_mesh/common/action_list.py +37 -0
- solace_agent_mesh/common/action_response.py +327 -0
- solace_agent_mesh/common/constants.py +3 -0
- solace_agent_mesh/common/mysql_database.py +40 -0
- solace_agent_mesh/common/postgres_database.py +79 -0
- solace_agent_mesh/common/prompt_templates.py +30 -0
- solace_agent_mesh/common/prompt_templates_unused_delete.py +161 -0
- solace_agent_mesh/common/stimulus_utils.py +152 -0
- solace_agent_mesh/common/time.py +24 -0
- solace_agent_mesh/common/utils.py +638 -0
- solace_agent_mesh/configs/agent_global.yaml +74 -0
- solace_agent_mesh/configs/agent_image_processing.yaml +82 -0
- solace_agent_mesh/configs/agent_slack.yaml +64 -0
- solace_agent_mesh/configs/agent_web_request.yaml +75 -0
- solace_agent_mesh/configs/conversation_to_file.yaml +56 -0
- solace_agent_mesh/configs/error_catcher.yaml +56 -0
- solace_agent_mesh/configs/monitor.yaml +0 -0
- solace_agent_mesh/configs/monitor_stim_and_errors_to_slack.yaml +106 -0
- solace_agent_mesh/configs/monitor_user_feedback.yaml +58 -0
- solace_agent_mesh/configs/orchestrator.yaml +241 -0
- solace_agent_mesh/configs/service_embedding.yaml +81 -0
- solace_agent_mesh/configs/service_llm.yaml +265 -0
- solace_agent_mesh/configs/visualize_websocket.yaml +55 -0
- solace_agent_mesh/gateway/__init__.py +0 -0
- solace_agent_mesh/gateway/components/__init__.py +0 -0
- solace_agent_mesh/gateway/components/gateway_base.py +41 -0
- solace_agent_mesh/gateway/components/gateway_input.py +265 -0
- solace_agent_mesh/gateway/components/gateway_output.py +289 -0
- solace_agent_mesh/gateway/identity/bamboohr_identity.py +18 -0
- solace_agent_mesh/gateway/identity/identity_base.py +10 -0
- solace_agent_mesh/gateway/identity/identity_provider.py +60 -0
- solace_agent_mesh/gateway/identity/no_identity.py +9 -0
- solace_agent_mesh/gateway/identity/passthru_identity.py +9 -0
- solace_agent_mesh/monitors/base_monitor_component.py +26 -0
- solace_agent_mesh/monitors/feedback/user_feedback_monitor.py +75 -0
- solace_agent_mesh/monitors/stim_and_errors/stim_and_error_monitor.py +560 -0
- solace_agent_mesh/orchestrator/__init__.py +0 -0
- solace_agent_mesh/orchestrator/action_manager.py +225 -0
- solace_agent_mesh/orchestrator/components/__init__.py +0 -0
- solace_agent_mesh/orchestrator/components/orchestrator_action_manager_timeout_component.py +54 -0
- solace_agent_mesh/orchestrator/components/orchestrator_action_response_component.py +179 -0
- solace_agent_mesh/orchestrator/components/orchestrator_register_component.py +107 -0
- solace_agent_mesh/orchestrator/components/orchestrator_stimulus_processor_component.py +477 -0
- solace_agent_mesh/orchestrator/components/orchestrator_streaming_output_component.py +246 -0
- solace_agent_mesh/orchestrator/orchestrator_main.py +166 -0
- solace_agent_mesh/orchestrator/orchestrator_prompt.py +410 -0
- solace_agent_mesh/services/__init__.py +0 -0
- solace_agent_mesh/services/authorization/providers/base_authorization_provider.py +56 -0
- solace_agent_mesh/services/bamboo_hr_service/__init__.py +3 -0
- solace_agent_mesh/services/bamboo_hr_service/bamboo_hr.py +182 -0
- solace_agent_mesh/services/common/__init__.py +4 -0
- solace_agent_mesh/services/common/auto_expiry.py +45 -0
- solace_agent_mesh/services/common/singleton.py +18 -0
- solace_agent_mesh/services/file_service/__init__.py +14 -0
- solace_agent_mesh/services/file_service/file_manager/__init__.py +0 -0
- solace_agent_mesh/services/file_service/file_manager/bucket_file_manager.py +149 -0
- solace_agent_mesh/services/file_service/file_manager/file_manager_base.py +162 -0
- solace_agent_mesh/services/file_service/file_manager/memory_file_manager.py +64 -0
- solace_agent_mesh/services/file_service/file_manager/volume_file_manager.py +106 -0
- solace_agent_mesh/services/file_service/file_service.py +432 -0
- solace_agent_mesh/services/file_service/file_service_constants.py +54 -0
- solace_agent_mesh/services/file_service/file_transformations.py +131 -0
- solace_agent_mesh/services/file_service/file_utils.py +322 -0
- solace_agent_mesh/services/file_service/transformers/__init__.py +5 -0
- solace_agent_mesh/services/history_service/__init__.py +3 -0
- solace_agent_mesh/services/history_service/history_providers/__init__.py +0 -0
- solace_agent_mesh/services/history_service/history_providers/base_history_provider.py +78 -0
- solace_agent_mesh/services/history_service/history_providers/memory_history_provider.py +167 -0
- solace_agent_mesh/services/history_service/history_providers/redis_history_provider.py +163 -0
- solace_agent_mesh/services/history_service/history_service.py +139 -0
- solace_agent_mesh/services/llm_service/components/llm_request_component.py +293 -0
- solace_agent_mesh/services/llm_service/components/llm_service_component_base.py +152 -0
- solace_agent_mesh/services/middleware_service/__init__.py +0 -0
- solace_agent_mesh/services/middleware_service/middleware_service.py +20 -0
- solace_agent_mesh/templates/action.py +38 -0
- solace_agent_mesh/templates/agent.py +29 -0
- solace_agent_mesh/templates/agent.yaml +70 -0
- solace_agent_mesh/templates/gateway-config-template.yaml +6 -0
- solace_agent_mesh/templates/gateway-default-config.yaml +28 -0
- solace_agent_mesh/templates/gateway-flows.yaml +81 -0
- solace_agent_mesh/templates/gateway-header.yaml +16 -0
- solace_agent_mesh/templates/gateway_base.py +15 -0
- solace_agent_mesh/templates/gateway_input.py +98 -0
- solace_agent_mesh/templates/gateway_output.py +71 -0
- solace_agent_mesh/templates/plugin-pyproject.toml +30 -0
- solace_agent_mesh/templates/rest-api-default-config.yaml +24 -0
- solace_agent_mesh/templates/rest-api-flows.yaml +80 -0
- solace_agent_mesh/templates/slack-default-config.yaml +9 -0
- solace_agent_mesh/templates/slack-flows.yaml +90 -0
- solace_agent_mesh/templates/solace-agent-mesh-default.yaml +77 -0
- solace_agent_mesh/templates/solace-agent-mesh-plugin-default.yaml +8 -0
- solace_agent_mesh/templates/web-default-config.yaml +5 -0
- solace_agent_mesh/templates/web-flows.yaml +86 -0
- solace_agent_mesh/tools/__init__.py +0 -0
- solace_agent_mesh/tools/components/__init__.py +0 -0
- solace_agent_mesh/tools/components/conversation_formatter.py +111 -0
- solace_agent_mesh/tools/components/file_resolver_component.py +58 -0
- solace_agent_mesh/tools/config/runtime_config.py +26 -0
- solace_agent_mesh-0.1.1.dist-info/METADATA +179 -0
- solace_agent_mesh-0.1.1.dist-info/RECORD +174 -0
- solace_agent_mesh-0.1.1.dist-info/entry_points.txt +3 -0
- solace_agent_mesh-0.0.1.dist-info/licenses/LICENSE.txt → solace_agent_mesh-0.1.1.dist-info/licenses/LICENSE +1 -2
- solace_agent_mesh-0.0.1.dist-info/METADATA +0 -51
- solace_agent_mesh-0.0.1.dist-info/RECORD +0 -5
- {solace_agent_mesh-0.0.1.dist-info → solace_agent_mesh-0.1.1.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,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
|