solace-agent-mesh 0.1.3__py3-none-any.whl → 0.2.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 (73) hide show
  1. solace_agent_mesh/agents/global/actions/plantuml_diagram.py +9 -2
  2. solace_agent_mesh/agents/global/actions/plotly_graph.py +70 -46
  3. solace_agent_mesh/agents/web_request/actions/do_web_request.py +34 -33
  4. solace_agent_mesh/cli/__init__.py +1 -1
  5. solace_agent_mesh/cli/commands/add/copy_from_plugin.py +8 -6
  6. solace_agent_mesh/cli/commands/add/gateway.py +162 -9
  7. solace_agent_mesh/cli/commands/build.py +15 -1
  8. solace_agent_mesh/cli/commands/init/ai_provider_step.py +45 -28
  9. solace_agent_mesh/cli/commands/init/broker_step.py +1 -4
  10. solace_agent_mesh/cli/commands/init/create_config_file_step.py +8 -0
  11. solace_agent_mesh/cli/commands/init/create_other_project_files_step.py +52 -1
  12. solace_agent_mesh/cli/commands/init/init.py +50 -37
  13. solace_agent_mesh/cli/commands/plugin/build.py +60 -9
  14. solace_agent_mesh/cli/commands/run.py +2 -2
  15. solace_agent_mesh/cli/config.py +4 -0
  16. solace_agent_mesh/cli/main.py +14 -8
  17. solace_agent_mesh/cli/utils.py +7 -2
  18. solace_agent_mesh/common/constants.py +10 -0
  19. solace_agent_mesh/common/prompt_templates.py +1 -3
  20. solace_agent_mesh/common/utils.py +104 -30
  21. solace_agent_mesh/config_portal/__init__.py +0 -0
  22. solace_agent_mesh/config_portal/backend/__init__.py +0 -0
  23. solace_agent_mesh/config_portal/backend/common.py +35 -0
  24. solace_agent_mesh/config_portal/backend/server.py +233 -0
  25. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-DRPGOzHj.js +42 -0
  26. solace_agent_mesh/config_portal/frontend/static/client/assets/components-ZIfdTbrV.js +191 -0
  27. solace_agent_mesh/config_portal/frontend/static/client/assets/entry.client-DX1misIU.js +19 -0
  28. solace_agent_mesh/config_portal/frontend/static/client/assets/index-BJHAE5s4.js +17 -0
  29. solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-8147e469.js +1 -0
  30. solace_agent_mesh/config_portal/frontend/static/client/assets/root-DgMDqKDc.js +10 -0
  31. solace_agent_mesh/config_portal/frontend/static/client/assets/root-hhS5izs8.css +1 -0
  32. solace_agent_mesh/config_portal/frontend/static/client/favicon.ico +0 -0
  33. solace_agent_mesh/config_portal/frontend/static/client/index.html +7 -0
  34. solace_agent_mesh/configs/orchestrator.yaml +1 -1
  35. solace_agent_mesh/configs/service_embedding.yaml +1 -1
  36. solace_agent_mesh/configs/service_llm.yaml +1 -1
  37. solace_agent_mesh/gateway/components/gateway_base.py +7 -1
  38. solace_agent_mesh/gateway/components/gateway_input.py +8 -5
  39. solace_agent_mesh/gateway/components/gateway_output.py +12 -3
  40. solace_agent_mesh/orchestrator/components/orchestrator_action_manager_timeout_component.py +4 -0
  41. solace_agent_mesh/orchestrator/components/orchestrator_stimulus_processor_component.py +43 -12
  42. solace_agent_mesh/orchestrator/components/orchestrator_streaming_output_component.py +19 -5
  43. solace_agent_mesh/orchestrator/orchestrator_main.py +11 -5
  44. solace_agent_mesh/orchestrator/orchestrator_prompt.py +184 -60
  45. solace_agent_mesh/services/file_service/file_service.py +5 -0
  46. solace_agent_mesh/services/file_service/file_service_constants.py +1 -1
  47. solace_agent_mesh/services/file_service/file_transformations.py +11 -1
  48. solace_agent_mesh/services/file_service/file_utils.py +2 -0
  49. solace_agent_mesh/services/history_service/history_providers/base_history_provider.py +21 -46
  50. solace_agent_mesh/services/history_service/history_providers/file_history_provider.py +74 -0
  51. solace_agent_mesh/services/history_service/history_providers/index.py +40 -0
  52. solace_agent_mesh/services/history_service/history_providers/memory_history_provider.py +19 -156
  53. solace_agent_mesh/services/history_service/history_providers/mongodb_history_provider.py +66 -0
  54. solace_agent_mesh/services/history_service/history_providers/redis_history_provider.py +40 -140
  55. solace_agent_mesh/services/history_service/history_providers/sql_history_provider.py +93 -0
  56. solace_agent_mesh/services/history_service/history_service.py +315 -41
  57. solace_agent_mesh/services/history_service/long_term_memory/__init__.py +0 -0
  58. solace_agent_mesh/services/history_service/long_term_memory/long_term_memory.py +399 -0
  59. solace_agent_mesh/services/llm_service/components/llm_request_component.py +19 -0
  60. solace_agent_mesh/templates/gateway-config-template.yaml +2 -1
  61. solace_agent_mesh/templates/gateway-default-config.yaml +3 -3
  62. solace_agent_mesh/templates/plugin-gateway-default-config.yaml +29 -0
  63. solace_agent_mesh/templates/rest-api-default-config.yaml +2 -1
  64. solace_agent_mesh/templates/slack-default-config.yaml +1 -1
  65. solace_agent_mesh/templates/solace-agent-mesh-default.yaml +9 -0
  66. solace_agent_mesh/templates/web-default-config.yaml +2 -1
  67. solace_agent_mesh-0.2.1.dist-info/METADATA +172 -0
  68. {solace_agent_mesh-0.1.3.dist-info → solace_agent_mesh-0.2.1.dist-info}/RECORD +71 -52
  69. solace_agent_mesh/common/prompt_templates_unused_delete.py +0 -161
  70. solace_agent_mesh-0.1.3.dist-info/METADATA +0 -208
  71. {solace_agent_mesh-0.1.3.dist-info → solace_agent_mesh-0.2.1.dist-info}/WHEEL +0 -0
  72. {solace_agent_mesh-0.1.3.dist-info → solace_agent_mesh-0.2.1.dist-info}/entry_points.txt +0 -0
  73. {solace_agent_mesh-0.1.3.dist-info → solace_agent_mesh-0.2.1.dist-info}/licenses/LICENSE +0 -0
@@ -54,8 +54,15 @@ class PlantUmlDiagram(Action):
54
54
  {"role": "user", "content": description},
55
55
  ]
56
56
  agent = self.get_agent()
57
- response = agent.do_llm_service_request(messages=messages)
58
- expression = response.get("content")
57
+ try:
58
+ response = agent.do_llm_service_request(messages=messages)
59
+ expression = response.get("content")
60
+ except TimeoutError as e:
61
+ log.error("LLM request timed out: %s", str(e))
62
+ return ActionResponse(message="LLM request timed out")
63
+ except Exception as e:
64
+ log.error("Failed to process content with LLM: %s", str(e))
65
+ return ActionResponse(message="Failed to process content with LLM")
59
66
 
60
67
  # Surround expression with @startuml and @enduml if missing
61
68
  if not expression.startswith("@startuml"):
@@ -37,46 +37,44 @@ class PlotlyGraph(Action):
37
37
  ],
38
38
  "required_scopes": ["global:plotly:read"],
39
39
  "examples": [
40
- """ <example>
41
- <example_docstring>
42
- This is an example of a user asking for a bar graph. The plotly action from the global agent is invoked to generate the graph.
43
- </example_docstring>
44
- <example_stimulus>
45
- <{tp}stimulus starting_id="12">
46
- Please generate a random bar graph for me
47
- </{tp}stimulus>
48
- <{tp}stimulus_metadata>
49
- local_time: 2024-09-04 15:59:02 EDT-0400 (Wednesday)
50
- </{tp}stimulus_metadata>
51
- </example_stimulus>
52
- <example_response>
53
- <{tp}reasoning>
54
- - user has requested a random bar graph
55
- - invoke the plotly action from the global agent to generate a bar graph with random data
56
- </{tp}reasoning>
57
- Certainly! I'd be happy to generate a random bar graph for you.
58
- <{tp}status_update>I'll use our plotting tool to create this for you right away.</{tp}status_update>
59
- <{tp}invoke_action agent="global" action="plotly">
60
- <{tp}parameter name="plotly_figure_config">
61
- {{
62
- "data": [
63
- {{
64
- "x": ["A", "B", "C", "D", "E"],
65
- "y": [23, 45, 56, 78, 90],
66
- "type": "bar"
67
- }}
68
- ],
69
- "layout": {{
70
- "title": "Random Bar Graph",
71
- "xaxis": {{"title": "Categories"}},
72
- "yaxis": {{"title": "Values"}}
73
- }}
74
- }}
75
- </{tp}parameter>
76
- </{tp}invoke_action>
77
- </example_response>
78
- </example>
79
- """
40
+ {
41
+ "docstring": "This is an example of a user asking for a bar graph. The plotly action from the global agent is invoked to generate the graph.",
42
+ "tag_prefix_placeholder": "{tp}",
43
+ "starting_id": "12",
44
+ "user_input": "Please generate a random bar graph for me",
45
+ "metadata": [
46
+ "local_time: 2024-09-04 15:59:02 EDT-0400 (Wednesday)"
47
+ ],
48
+ "reasoning": [
49
+ "- user has requested a random bar graph",
50
+ "- invoke the plotly action from the global agent to generate a bar graph with random data",
51
+ ],
52
+ "response_text": "Certainly! I'd be happy to generate a random bar graph for you.",
53
+ "status_update": "I'll use our plotting tool to create this for you right away.",
54
+ "action": {
55
+ "agent": "global",
56
+ "name": "plotly",
57
+ "parameter_name": "plotly_figure_config",
58
+ "parameters": {
59
+ "plotly_figure_config": [
60
+ "{",
61
+ ' "data": [',
62
+ " {",
63
+ ' "x": ["A", "B", "C", "D", "E"],',
64
+ ' "y": [23, 45, 56, 78, 90],',
65
+ ' "type": "bar"',
66
+ " }",
67
+ " ],",
68
+ ' "layout": {',
69
+ ' "title": "Random Bar Graph",',
70
+ ' "xaxis": {"title": "Categories"},',
71
+ ' "yaxis": {"title": "Values"}',
72
+ " }",
73
+ "}",
74
+ ]
75
+ },
76
+ },
77
+ }
80
78
  ],
81
79
  },
82
80
  **kwargs,
@@ -84,15 +82,38 @@ class PlotlyGraph(Action):
84
82
 
85
83
  def invoke(self, params, meta={}) -> ActionResponse:
86
84
  if platform.system() == "Windows":
87
- kaleido_version = version('kaleido')
88
- min_version = parse('0.1.0.post1')
89
- max_version = parse('0.2.0')
90
- if parse(kaleido_version) < min_version or parse(kaleido_version) >= max_version:
85
+ kaleido_version = version("kaleido")
86
+ min_version = parse("0.1.0.post1")
87
+ max_version = parse("0.2.0")
88
+ if (
89
+ parse(kaleido_version) < min_version
90
+ or parse(kaleido_version) >= max_version
91
+ ):
91
92
  return ActionResponse(
92
93
  message="For Windows users, the plotting functionality requires a specific version of Kaleido. Please refer to the documentation."
93
94
  )
94
95
  obj = params["plotly_figure_config"]
95
96
  if isinstance(obj, str):
97
+ # First remove any leading/trailing whitespace
98
+ obj = obj.strip()
99
+
100
+ # Next remove ```yaml or ```json
101
+ if obj.startswith("```yaml") or obj.startswith("```json"):
102
+ obj = obj[7:]
103
+ elif obj.startswith("```"):
104
+ obj = obj[3:]
105
+ # Remove any trailing ``` characters
106
+ if obj.endswith("```"):
107
+ obj = obj[:-3]
108
+ # Remove any leading/trailing whitespace again
109
+ obj = obj.strip()
110
+
111
+ # If the string starts with a quote and ends with that same quote, remove them
112
+ if obj.startswith("'") and obj.endswith("'"):
113
+ obj = obj[1:-1]
114
+ elif obj.startswith('"') and obj.endswith('"'):
115
+ obj = obj[1:-1]
116
+
96
117
  # Remove any leading/trailing quote characters
97
118
  obj = obj.strip("'\" ")
98
119
  try:
@@ -112,7 +133,9 @@ class PlotlyGraph(Action):
112
133
  pio.write_image(fig, byte_io)
113
134
  byte_io.seek(0)
114
135
  file_service = FileService()
115
- image_name = "generated_graph_" + str(random.randint(100000, 999999)) + ".png"
136
+ image_name = (
137
+ "generated_graph_" + str(random.randint(100000, 999999)) + ".png"
138
+ )
116
139
  image_meta = file_service.upload_from_buffer(
117
140
  byte_io.read(),
118
141
  image_name,
@@ -122,7 +145,8 @@ class PlotlyGraph(Action):
122
145
  files.append(image_meta)
123
146
  except Exception as e:
124
147
  return ActionResponse(
125
- message="Could not create plotly graph. Please check the plotly figure configuration. plotly error: " + str(e),
148
+ message="Could not create plotly graph. Please check the plotly figure configuration. plotly error: "
149
+ + str(e),
126
150
  )
127
151
 
128
152
  return ActionResponse(files=files)
@@ -31,37 +31,31 @@ class DoWebRequest(Action):
31
31
  ],
32
32
  "required_scopes": ["web_request:do_web_request:read"],
33
33
  "examples": [
34
- """ <example>
35
- <example_docstring>
36
- This is an example of a user requesting to fetch information from the web. The web_request agent is open so invoke the do_web_request action to fetch the content from the url and process the information according to the llm_prompt.
37
- </example_docstring>
38
- <example_stimulus>
39
- <{tp}stimulus starting_id="10"/>
40
- What is the weather in Ottawa?
41
- </{tp}stimulus>
42
- <{tp}stimulus_metadata>
43
- local_time: 2024-11-06 12:33:12 EST-0500 (Wednesday)
44
- </{tp}stimulus_metadata>
45
- </example_stimulus>
46
- <example_response>
47
- <{tp}reasoning>
48
- - User is asking for current weather information in Ottawa
49
- - We need to fetch up-to-date weather data
50
- - Use the web_request agent to get the latest weather information
51
- - Plan to use the Environment Canada website for accurate local weather data
52
- </{tp}reasoning>
53
-
54
- Certainly! I\'ll fetch the current weather information for Ottawa for you right away.
55
-
56
- <{tp}invoke_action agent="web_request" action="do_web_request">
57
- <{tp}parameter name="url">https://weather.gc.ca/city/pages/on-118_metric_e.html</{tp}parameter>
58
- <{tp}parameter name="llm_prompt">Extract the current temperature, weather conditions, and any important weather alerts or warnings for Ottawa from the webpage. Format the response as a bulleted list with emoji where appropriate.</{tp}parameter>
59
- </{tp}invoke_action>
60
-
61
- <{tp}status_update>Retrieving the latest weather data for Ottawa...</{tp}status_update>'
62
- </example_response>
63
- </example>
64
- """
34
+ {
35
+ "docstring": "This is an example of a user requesting to fetch information from the web. The web_request agent is open so invoke the do_web_request action to fetch the content from the url and process the information according to the llm_prompt.",
36
+ "tag_prefix_placeholder": "{tp}",
37
+ "starting_id": "10",
38
+ "user_input": "What is the weather in Ottawa?",
39
+ "metadata": [
40
+ "local_time: 2024-11-06 12:33:12 EST-0500 (Wednesday)"
41
+ ],
42
+ "reasoning": [
43
+ "- User is asking for current weather information in Ottawa",
44
+ "- We need to fetch up-to-date weather data",
45
+ "- Use the web_request agent to get the latest weather information",
46
+ "- Plan to use the Environment Canada website for accurate local weather data"
47
+ ],
48
+ "response_text": "Certainly! I'll fetch the current weather information for Ottawa for you right away.",
49
+ "status_update": "Retrieving the latest weather data for Ottawa...",
50
+ "action": {
51
+ "agent": "web_request",
52
+ "name": "do_web_request",
53
+ "parameters": {
54
+ "url": "https://weather.gc.ca/city/pages/on-118_metric_e.html",
55
+ "llm_prompt": "Extract the current temperature, weather conditions, and any important weather alerts or warnings for Ottawa from the webpage. Format the response as a bulleted list with emoji where appropriate."
56
+ }
57
+ }
58
+ }
65
59
  ],
66
60
  },
67
61
  **kwargs,
@@ -127,8 +121,15 @@ class DoWebRequest(Action):
127
121
  ]
128
122
 
129
123
  agent = self.get_agent()
130
- response = agent.do_llm_service_request(messages=messages)
131
- content = response.get("content")
124
+ try:
125
+ response = agent.do_llm_service_request(messages=messages)
126
+ content = response.get("content")
127
+ except TimeoutError as e:
128
+ log.error("LLM request timed out: %s", str(e))
129
+ return ActionResponse(message="LLM request timed out")
130
+ except Exception as e:
131
+ log.error("Failed to process content with LLM: %s", str(e))
132
+ return ActionResponse(message="Failed to process content with LLM")
132
133
 
133
134
  # Code to create the image using the provided content
134
135
  return ActionResponse(message=content)
@@ -1 +1 @@
1
- __version__ = "0.1.3"
1
+ __version__ = "0.2.1"
@@ -16,7 +16,7 @@ def copy_from_plugin(name, plugin_name, entity_type):
16
16
  if entity_type not in ["agents", "gateways"]:
17
17
  log_error("Invalid entity type.")
18
18
  return 1
19
-
19
+
20
20
  src_entity_name = name
21
21
  if ":" in plugin_name:
22
22
  plugin_name, src_entity_name = plugin_name.split(":")
@@ -33,11 +33,13 @@ def copy_from_plugin(name, plugin_name, entity_type):
33
33
  item_name = f"{item_name}.yaml" if entity_type == "agents" else item_name
34
34
 
35
35
  src_entity_name = src_entity_name.replace("-", "_")
36
- src_entity_name = f"{src_entity_name}.yaml" if entity_type == "agents" else src_entity_name
36
+ src_entity_name = (
37
+ f"{src_entity_name}.yaml" if entity_type == "agents" else src_entity_name
38
+ )
37
39
  template_path = os.path.join(plugin_path, "configs", entity_type, src_entity_name)
38
40
 
39
41
  if not os.path.exists(template_path):
40
- log_error(f"Could not find '{item_name}' in '{plugin_name}' plugin.")
42
+ log_error(f"Could not find '{src_entity_name}' in '{plugin_name}' plugin.")
41
43
  return 1
42
44
 
43
45
  config = click.get_current_context().obj
@@ -80,11 +82,11 @@ def copy_from_plugin(name, plugin_name, entity_type):
80
82
  else:
81
83
  log_error("Invalid entity type.")
82
84
  return 1
83
-
85
+
84
86
  temp_file = os.path.join(config_directory, "__TEMPLATES_WILL_BE_HERE__")
85
87
  if os.path.exists(temp_file):
86
88
  os.remove(temp_file)
87
-
89
+
88
90
  click.echo(
89
91
  f"Copied {entity_type[:-1]} '{name}' from plugin '{plugin_name}' at: {get_display_path(target_directory)}"
90
- )
92
+ )
@@ -1,8 +1,9 @@
1
1
  import os
2
2
  import sys
3
3
  import click
4
+ import re
4
5
 
5
- from cli.utils import get_display_path, load_template, log_error, get_formatted_names
6
+ from cli.utils import get_display_path, load_template, log_error, get_formatted_names, log_warning
6
7
  from cli.commands.plugin.build import get_all_plugin_gateway_interfaces
7
8
  from cli.config import Config
8
9
 
@@ -34,6 +35,126 @@ def _add_python_files(modules_directory, template_args, created_file_names):
34
35
  f.write(gateway_template)
35
36
  created_file_names.append(gateway_path)
36
37
 
38
+ def _update_gateway_yaml(yaml_string, interface_gateway_config):
39
+ # Update system_purpose
40
+ if "system_purpose" in interface_gateway_config:
41
+ yaml_string = re.sub(
42
+ r'(system_purpose: >\n\s+)(.*?)(?=\n\s{4}\S)',
43
+ lambda m: f'{m.group(1)}{interface_gateway_config["system_purpose"]}',
44
+ yaml_string,
45
+ flags=re.DOTALL
46
+ )
47
+
48
+ # Update interaction_type
49
+ if "interaction_type" in interface_gateway_config:
50
+ yaml_string = re.sub(
51
+ r'(interaction_type: )".*?"',
52
+ lambda m: f'{m.group(1)}"{interface_gateway_config["interaction_type"]}"',
53
+ yaml_string
54
+ )
55
+
56
+ # Update history settings
57
+ history_config = interface_gateway_config.get("history", {})
58
+ if not history_config.get("enabled", False):
59
+ yaml_string = re.sub(r'\n\s+history_config: \n\s+<<: \*default_history_policy', '', yaml_string)
60
+ yaml_string = re.sub(r'(retain_history: )true', r'\1false', yaml_string)
61
+ yaml_string = re.sub(
62
+ r'\n- history_policy:.*?(?=\n\n)',
63
+ '',
64
+ yaml_string,
65
+ flags=re.DOTALL
66
+ )
67
+
68
+ else:
69
+ if history_config.get("type") is not None:
70
+ yaml_string = re.sub(
71
+ r'(type: )".*?"',
72
+ lambda m: f'{m.group(1)}"{history_config.get("type")}"',
73
+ yaml_string,
74
+ count=1 # Update only the first occurrence
75
+ )
76
+ if history_config.get("time_to_live") is not None:
77
+ yaml_string = re.sub(
78
+ r'(time_to_live: )\d+',
79
+ lambda m: f'{m.group(1)}{history_config.get("time_to_live")}',
80
+ yaml_string
81
+ )
82
+ if history_config.get("expiration_check_interval") is not None:
83
+ yaml_string = re.sub(
84
+ r'(expiration_check_interval: )\d+',
85
+ lambda m: f'{m.group(1)}{history_config.get("expiration_check_interval")}',
86
+ yaml_string
87
+ )
88
+
89
+ if history_config.get("max_turns") is not None:
90
+ yaml_string = re.sub(
91
+ r'(max_turns: )\d+',
92
+ lambda m: f'{m.group(1)}{history_config.get("max_turns")}',
93
+ yaml_string
94
+ )
95
+ if history_config.get("max_characters") is not None:
96
+ yaml_string = re.sub(
97
+ r'(max_characters: )\d+',
98
+ lambda m: f'{m.group(1)}{history_config.get("max_characters")}',
99
+ yaml_string
100
+ )
101
+ if history_config.get("enforce_alternate_message_roles") is not None:
102
+ yaml_string = re.sub(
103
+ r'(enforce_alternate_message_roles: )\w+',
104
+ lambda m: f'{m.group(1)}{str(history_config.get("enforce_alternate_message_roles", True)).lower()}',
105
+ yaml_string
106
+ )
107
+
108
+ # Inject long-term memory before history_policy
109
+ if history_config.get("long_term_memory", {}).get("enabled", False):
110
+ # Build llm_config string
111
+ llm_config_items = history_config['long_term_memory'].get('llm_config', {})
112
+ if llm_config_items:
113
+ llm_config_parts = []
114
+ for key, value in llm_config_items.items():
115
+ llm_config_parts.append(f"{key}: {value}")
116
+ llm_config = "\n " + "\n ".join(llm_config_parts) + "\n"
117
+ else:
118
+ llm_config = " {} \n"
119
+
120
+ # Build store_config string
121
+ store_config_items = history_config['long_term_memory'].get('store_config', {})
122
+ if store_config_items:
123
+ store_config_parts = []
124
+ for key, value in store_config_items.items():
125
+ store_config_parts.append(f"{key}: {value}")
126
+ store_config = "\n " + "\n ".join(store_config_parts) + "\n"
127
+ else:
128
+ store_config = " {} \n"
129
+ long_term_yaml = (
130
+ 'long_term_memory: true\n'
131
+ ' long_term_memory_config:\n'
132
+ f' llm_config:{llm_config}'
133
+ f' store_config:{store_config}'
134
+ ' '
135
+ )
136
+
137
+ if "long_term_memory:" not in yaml_string:
138
+ yaml_string = re.sub(
139
+ r'(history_policy: # History provider configs.*?\n(\s+max_turns: \d+))',
140
+ lambda m: f'{long_term_yaml}{m.group(1)}',
141
+ yaml_string,
142
+ flags=re.DOTALL
143
+ )
144
+
145
+ # Inject type_config under history_policy
146
+ type_config = history_config.get("type_config", {})
147
+ if type_config:
148
+ type_config_yaml = '\n'.join([f' {key}: {value}' for key, value in type_config.items()])
149
+ yaml_string = re.sub(
150
+ r'(history_policy: # History provider configs.*?)\n',
151
+ lambda m: f'{m.group(1)}\n{type_config_yaml}\n',
152
+ yaml_string,
153
+ flags=re.DOTALL
154
+ )
155
+
156
+ return yaml_string
157
+
37
158
  def add_gateway_command(name, interfaces):
38
159
  """
39
160
  Creates a gateway configuration directory and files based on provided templates.
@@ -76,7 +197,7 @@ def add_gateway_command(name, interfaces):
76
197
  def abort():
77
198
  sys.exit(1)
78
199
 
79
- plugin_gateway_interfaces = get_all_plugin_gateway_interfaces(config, abort)
200
+ plugin_gateway_interfaces = get_all_plugin_gateway_interfaces(config, abort, return_plugin_config=True)
80
201
 
81
202
  # Check if the gateway directory already exists
82
203
  if os.path.exists(gateway_directory):
@@ -91,12 +212,11 @@ def add_gateway_command(name, interfaces):
91
212
 
92
213
  # Create the gateway config file from the template directory
93
214
  # The file name is gateway-default-config.yaml but write it as gateway.yaml in the gateway_directory
94
- gateway_config_template = load_template("gateway-default-config.yaml")
95
215
  gateway_config_path = os.path.join(gateway_directory, "gateway.yaml")
96
- with open(gateway_config_path, "w", encoding="utf-8") as f:
97
- f.write(gateway_config_template)
98
-
99
- created_file_names.append(gateway_config_path)
216
+ gateway_config_default = load_template("gateway-default-config.yaml")
217
+ gateway_config = gateway_config_default
218
+ gateway_config_interface_default_name = None # the interface name that is used for the default gateway config
219
+ gateway_config_overwritten = False
100
220
 
101
221
  # If the interface list is not empty, create the interface config files
102
222
  if interfaces:
@@ -105,8 +225,17 @@ def add_gateway_command(name, interfaces):
105
225
  # The name will be {interface}-default-config.yaml
106
226
  # Write the file as {interface}.yaml in the gateway_directory
107
227
  if interface in plugin_gateway_interfaces:
228
+ interface_gateway_config = plugin_gateway_interfaces[interface][1]
229
+ # Checking if the interface has default values for the gateway.yaml
230
+ if interface_gateway_config:
231
+ # Checking if another interface has already been used to create the gateway.yaml
232
+ if gateway_config_interface_default_name:
233
+ # Overwrite the default gateway config with the current interface config
234
+ gateway_config_overwritten = True
235
+ gateway_config_interface_default_name = interface
236
+ gateway_config = _update_gateway_yaml(gateway_config_default, interface_gateway_config)
108
237
  interface_default_path = os.path.join(
109
- plugin_gateway_interfaces[interface],
238
+ plugin_gateway_interfaces[interface][0],
110
239
  f"{interface}-default-config.yaml",
111
240
  )
112
241
  if not os.path.exists(interface_default_path):
@@ -149,6 +278,17 @@ def add_gateway_command(name, interfaces):
149
278
  log_error(f"Error creating gateway, gateway config was created: {e}")
150
279
  return 1
151
280
 
281
+ with open(gateway_config_path, "w", encoding="utf-8") as f:
282
+ f.write(gateway_config)
283
+
284
+ if gateway_config_overwritten:
285
+ log_warning(("Multiple interface configurations found. "
286
+ "Overwriting default gateway configuration with "
287
+ f"{gateway_config_interface_default_name} interface configuration."
288
+ ))
289
+
290
+ created_file_names.append(gateway_config_path)
291
+
152
292
  temp_file = os.path.join(config_directory, "__TEMPLATES_WILL_BE_HERE__")
153
293
  if os.path.exists(temp_file):
154
294
  os.remove(temp_file)
@@ -169,7 +309,8 @@ def add_interface_command(name):
169
309
  Creates a new gateway interface with the provided name.
170
310
  """
171
311
  config = Config.get_plugin_config()
172
- plugin_name = config.get("solace_agent_mesh_plugin", {}).get("name")
312
+ plugin_config = config.get("solace_agent_mesh_plugin", {})
313
+ plugin_name = plugin_config.get("name")
173
314
  if not plugin_name or plugin_name == "solace-agent-mesh-plugin":
174
315
  log_error("Could not find a valid plugin project")
175
316
  return 1
@@ -210,6 +351,18 @@ def add_interface_command(name):
210
351
 
211
352
  _add_python_files(modules_directory, template_args, created_file_names)
212
353
 
354
+ # Update the 'solace-agent-mesh-plugin' configuration file
355
+ plugin_config["includes_gateway_interface"] = True
356
+ plugin_gateway_config_template = load_template("plugin-gateway-default-config.yaml", parser=Config.get_yaml_parser())
357
+ if not plugin_gateway_config_template:
358
+ log_error("Error: Plugin gateway config template not found.")
359
+ else:
360
+ if "interface_gateway_configs" not in plugin_config:
361
+ plugin_config["interface_gateway_configs"] = {}
362
+ plugin_config["interface_gateway_configs"][template_args["HYPHENED_NAME"]] = plugin_gateway_config_template.get("plugin_gateway_default_config", {})
363
+ Config.write_config(config, Config.user_plugin_config_file)
364
+ click.echo(f"Updated {Config.user_plugin_config_file} with default gateway interface configuration for {template_args['HYPHENED_NAME']}")
365
+
213
366
  except IOError as e:
214
367
  log_error(f"Error creating gateway interface, {e}")
215
368
  return 1
@@ -16,7 +16,6 @@ from cli.utils import (
16
16
  get_display_path,
17
17
  log_error,
18
18
  apply_document_parsers,
19
- find_last_list_item_indent,
20
19
  normalize_and_reindent_yaml
21
20
  )
22
21
 
@@ -379,7 +378,22 @@ def build_solace_agent_mesh(config, build_config_dir, abort, parsers):
379
378
  if f.endswith(".yaml")
380
379
  and not any(f.startswith(prefix) for prefix in skip_prefixes)
381
380
  ]
381
+
382
+ # Check if embedding service is enabled
383
+ embedding_enabled = True # Default to True for backward compatibility
384
+ built_in_services = config.get("built_in", {}).get("services", [])
385
+ if built_in_services: # Only check if services section exists
386
+ embedding_enabled = False
387
+ for service in built_in_services:
388
+ if service.get("name") == "embedding" and service.get("enabled"):
389
+ embedding_enabled = True
390
+ break
391
+
382
392
  for config_file in config_files:
393
+ # Skip embedding service if not enabled
394
+ if config_file == "service_embedding.yaml" and not embedding_enabled:
395
+ click.echo(f"Skipping embedding service as it is disabled.")
396
+ continue
383
397
  try:
384
398
  # Read config file
385
399
  config_file_path = os.path.join(configs_source_path, config_file)
@@ -39,38 +39,55 @@ def ai_provider_step(options, default_options, none_interactive, abort):
39
39
  default_options["llm_model_name"],
40
40
  none_interactive,
41
41
  )
42
-
43
- ask_if_not_provided(
42
+ # Ask if the user wants to enable the embedding service
43
+ embedding_enabled = ask_if_not_provided(
44
44
  options,
45
- "embedding_endpoint_url",
46
- "Provide Embedding endpoint URL",
47
- default_options["embedding_endpoint_url"],
45
+ "embedding_service_enabled",
46
+ "Enable embedding service for vector embeddings?",
47
+ default_options["embedding_service_enabled"],
48
48
  none_interactive,
49
49
  )
50
50
 
51
- ask_if_not_provided(
52
- options,
53
- "embedding_api_key",
54
- "Provide Embedding API Key",
55
- default_options["embedding_api_key"],
56
- none_interactive,
57
- hide_input=True,
58
- )
51
+ options["embedding_service_enabled"] = embedding_enabled
59
52
 
60
- click.echo(
61
- click.style(
62
- (
63
- "The model name should follow the format `provider/model-name`."
64
- "\n\t For example: openai/text-embedding-ada-002 or openai/my-model-that-follows-openai-api"
65
- ),
66
- fg="yellow",
53
+ # Only ask for embedding configuration if the service is enabled
54
+ if embedding_enabled:
55
+ ask_if_not_provided(
56
+ options,
57
+ "embedding_endpoint_url",
58
+ "Provide Embedding endpoint URL",
59
+ default_options["embedding_endpoint_url"],
60
+ none_interactive,
67
61
  )
68
- )
69
62
 
70
- ask_if_not_provided(
71
- options,
72
- "embedding_model_name",
73
- "Provide Embedding model name to use",
74
- default_options["embedding_model_name"],
75
- none_interactive,
76
- )
63
+ ask_if_not_provided(
64
+ options,
65
+ "embedding_api_key",
66
+ "Provide Embedding API Key",
67
+ default_options["embedding_api_key"],
68
+ none_interactive,
69
+ hide_input=True,
70
+ )
71
+
72
+ click.echo(
73
+ click.style(
74
+ (
75
+ "The model name should follow the format `provider/model-name`."
76
+ "\n\t For example: openai/text-embedding-ada-002 or openai/my-model-that-follows-openai-api"
77
+ ),
78
+ fg="yellow",
79
+ )
80
+ )
81
+
82
+ ask_if_not_provided(
83
+ options,
84
+ "embedding_model_name",
85
+ "Provide Embedding model name to use",
86
+ default_options["embedding_model_name"],
87
+ none_interactive,
88
+ )
89
+ else:
90
+ # Set empty values for embedding configuration if the service is disabled
91
+ options["embedding_endpoint_url"] = ""
92
+ options["embedding_api_key"] = ""
93
+ options["embedding_model_name"] = ""
@@ -3,10 +3,7 @@ import click
3
3
  import os
4
4
 
5
5
  from cli.utils import ask_if_not_provided
6
-
7
-
8
- CONTAINER_RUN_COMMAND = " run -d -p 8080:8080 -p 55554:55555 -p 8008:8008 -p 1883:1883 -p 8000:8000 -p 5672:5672 -p 9000:9000 -p 2222:2222 --shm-size=2g --env username_admin_globalaccesslevel=admin --env username_admin_password=admin --name=solace solace/solace-pubsub-standard"
9
-
6
+ from solace_agent_mesh.config_portal.backend.common import CONTAINER_RUN_COMMAND
10
7
 
11
8
  def broker_step(options, default_options, none_interactive, abort):
12
9
  """