solace-agent-mesh 0.2.0__py3-none-any.whl → 0.2.2__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/agents/global/actions/plantuml_diagram.py +94 -36
- solace_agent_mesh/agents/global/actions/plotly_graph.py +48 -22
- solace_agent_mesh/cli/__init__.py +1 -1
- solace_agent_mesh/cli/commands/add/agent.py +1 -1
- solace_agent_mesh/cli/commands/add/copy_from_plugin.py +9 -7
- solace_agent_mesh/cli/commands/add/gateway.py +2 -2
- solace_agent_mesh/cli/commands/build.py +15 -0
- solace_agent_mesh/cli/commands/init/ai_provider_step.py +45 -28
- solace_agent_mesh/cli/commands/init/broker_step.py +1 -4
- solace_agent_mesh/cli/commands/init/check_if_already_done.py +1 -1
- solace_agent_mesh/cli/commands/init/create_config_file_step.py +8 -0
- solace_agent_mesh/cli/commands/init/init.py +20 -38
- solace_agent_mesh/cli/commands/init/web_init_step.py +32 -0
- solace_agent_mesh/cli/commands/plugin/build.py +52 -10
- solace_agent_mesh/cli/commands/plugin/create.py +3 -3
- solace_agent_mesh/cli/commands/run.py +2 -2
- solace_agent_mesh/cli/main.py +20 -8
- solace_agent_mesh/common/prompt_templates.py +1 -3
- solace_agent_mesh/common/utils.py +88 -19
- solace_agent_mesh/config_portal/__init__.py +0 -0
- solace_agent_mesh/config_portal/backend/__init__.py +0 -0
- solace_agent_mesh/config_portal/backend/common.py +35 -0
- solace_agent_mesh/config_portal/backend/server.py +233 -0
- solace_agent_mesh/config_portal/frontend/static/client/Solace_community_logo.png +0 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-b13CSm84.js +42 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/components-ZIfdTbrV.js +191 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/entry.client-DX1misIU.js +19 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/index-BJHAE5s4.js +17 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-c92a7808.js +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-BApq5dPK.js +10 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-DX4gQ516.css +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/favicon.ico +0 -0
- solace_agent_mesh/config_portal/frontend/static/client/index.html +7 -0
- solace_agent_mesh/configs/orchestrator.yaml +1 -1
- solace_agent_mesh/orchestrator/components/orchestrator_action_manager_timeout_component.py +4 -0
- solace_agent_mesh/orchestrator/components/orchestrator_stimulus_processor_component.py +46 -16
- solace_agent_mesh/orchestrator/components/orchestrator_streaming_output_component.py +19 -5
- solace_agent_mesh/orchestrator/orchestrator_main.py +11 -5
- solace_agent_mesh/orchestrator/orchestrator_prompt.py +78 -74
- solace_agent_mesh/services/history_service/history_providers/sql_history_provider.py +1 -1
- solace_agent_mesh/services/llm_service/components/llm_request_component.py +54 -31
- solace_agent_mesh/templates/rest-api-default-config.yaml +4 -2
- solace_agent_mesh/templates/solace-agent-mesh-default.yaml +9 -0
- solace_agent_mesh/templates/web-default-config.yaml +4 -2
- solace_agent_mesh-0.2.2.dist-info/METADATA +172 -0
- {solace_agent_mesh-0.2.0.dist-info → solace_agent_mesh-0.2.2.dist-info}/RECORD +49 -35
- solace_agent_mesh/common/prompt_templates_unused_delete.py +0 -161
- solace_agent_mesh-0.2.0.dist-info/METADATA +0 -209
- {solace_agent_mesh-0.2.0.dist-info → solace_agent_mesh-0.2.2.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-0.2.0.dist-info → solace_agent_mesh-0.2.2.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-0.2.0.dist-info → solace_agent_mesh-0.2.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import platform
|
|
4
4
|
import os
|
|
5
5
|
import tempfile
|
|
6
|
+
import uuid
|
|
7
|
+
import shutil
|
|
6
8
|
import subprocess
|
|
7
9
|
|
|
8
10
|
from solace_ai_connector.common.log import log
|
|
@@ -11,11 +13,51 @@ from ....common.action_response import ActionResponse
|
|
|
11
13
|
from ....services.file_service import FileService
|
|
12
14
|
|
|
13
15
|
PLANTUML_PROMPT = """Generate a PlantUML markup language for the given description by the user. Supports sequence diagrams, class diagrams, use case diagrams, activity diagrams, component diagrams, state diagrams, and timing diagrams. Newlines must be escaped with a backslash and put quotes around participants names. Respond only with PlantUML markup, do not include any other text, symbols or quotes.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
|
|
17
|
+
Rules to follow for all diagrams:
|
|
18
|
+
- Always include @startuml at the beginning and @enduml at the end
|
|
19
|
+
- Use simple quotes for names, not double quotes
|
|
20
|
+
- Avoid using <<stereotype>> syntax in state diagrams unless absolutely necessary
|
|
21
|
+
- For state diagrams, use simple state names without spaces (like LoginScreen instead of "Login Screen")
|
|
22
|
+
- If you need descriptive names, use the 'as' keyword (e.g., [*] --> LoginScreen as "Login Screen")
|
|
23
|
+
- Don't use variables if they are not declared
|
|
24
|
+
- Never skip lines
|
|
25
|
+
- For notes in all diagrams:
|
|
26
|
+
- Use the simple syntax: `note over Participant: Text` without any positioning numbers
|
|
27
|
+
- Do not use positioning numbers (like `at 25`) in note commands
|
|
28
|
+
- Never combine multiple participants in a single note
|
|
29
|
+
|
|
30
|
+
Rules for sequence diagrams:
|
|
31
|
+
- Only use notes to cover one or two participants per line
|
|
32
|
+
- Do not use the `activate` or `deactivate` commands directly after a delay notation (`...`), whether directly or with a note in between
|
|
33
|
+
- Do not use the `activate` or `deactivate` commands twice in a row, whether directly or with a note in between
|
|
34
|
+
|
|
35
|
+
Rules for activity diagrams:
|
|
36
|
+
- Do not use the `backward:` keyword for loops or returning to previous steps
|
|
37
|
+
- Instead, use the label and goto pattern:
|
|
38
|
+
1. Add a label to the target activity: `label label_name`
|
|
39
|
+
2. Use goto to reference that label: `goto label_name`
|
|
40
|
+
- Always place labels immediately after the activity they refer to
|
|
41
|
+
- Labels should use lowercase with underscores for readability (e.g., `label browse_products`)
|
|
42
|
+
|
|
43
|
+
Rules for state diagrams:
|
|
44
|
+
- Use simple state names without spaces and without quotes
|
|
45
|
+
- When stereotypes are absolutely necessary, use the correct syntax:
|
|
46
|
+
- For existing states: `state StateName <<stereotype>>`
|
|
47
|
+
- For new states with descriptions: `state "Description" as StateName <<stereotype>>`
|
|
48
|
+
- Always place stereotypes after the state name, not before
|
|
49
|
+
- If using skinparam for stereotype styling, ensure it's properly defined: `skinparam state \{ BackgroundColor<<stereotype>> Color\}
|
|
50
|
+
- For notes in state diagrams:
|
|
51
|
+
- Do NOT use `note over StateName` syntax (this is for sequence diagrams)
|
|
52
|
+
- Instead use directional positioning: `note left of StateName`, `note right of StateName`, `note top of StateName`, or `note bottom of StateName`
|
|
53
|
+
- Example: `note left of OrderCreated : Initial customer order`
|
|
54
|
+
|
|
55
|
+
Rules for timing diagrams:
|
|
56
|
+
- Never include notes
|
|
57
|
+
- For 'robust' participants, use named states instead of symbolic states
|
|
58
|
+
- CORRECT: `PS is "ON"` or `PS is ON`
|
|
59
|
+
- INCORRECT: `PS is {+}` or `PS is {-}`
|
|
60
|
+
- For 'concise' participants, use numeric values (e.g., `BP is 10`)
|
|
19
61
|
"""
|
|
20
62
|
|
|
21
63
|
|
|
@@ -42,10 +84,6 @@ class PlantUmlDiagram(Action):
|
|
|
42
84
|
)
|
|
43
85
|
|
|
44
86
|
def invoke(self, params, meta={}) -> ActionResponse:
|
|
45
|
-
if platform.system() == "Windows":
|
|
46
|
-
return ActionResponse(
|
|
47
|
-
message=f"Unfortunately, the PlantUML is not available on {platform.system()}"
|
|
48
|
-
)
|
|
49
87
|
# Do a local command to run plantuml -tpng
|
|
50
88
|
description = params.get("diagram_description")
|
|
51
89
|
agent = self.get_agent()
|
|
@@ -70,34 +108,54 @@ class PlantUmlDiagram(Action):
|
|
|
70
108
|
if not expression.endswith("@enduml"):
|
|
71
109
|
expression = f"{expression}\n@enduml"
|
|
72
110
|
log.debug("PlantUML expression: %s", expression)
|
|
73
|
-
img_path = None
|
|
74
111
|
files = []
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
temp_dir = tempfile.mkdtemp()
|
|
115
|
+
# Create a unique filename with .puml extension
|
|
116
|
+
diagram_id = str(uuid.uuid4())
|
|
117
|
+
puml_filename = f"diagram_{diagram_id}.puml"
|
|
118
|
+
puml_path = os.path.join(temp_dir, puml_filename)
|
|
119
|
+
png_path = os.path.join(temp_dir, f"diagram_{diagram_id}.png")
|
|
120
|
+
|
|
121
|
+
# Write PlantUML content to input file
|
|
122
|
+
with open(puml_path, 'w', encoding='utf-8') as f:
|
|
123
|
+
f.write(expression)
|
|
124
|
+
|
|
125
|
+
# Run PlantUML
|
|
126
|
+
subprocess.run(
|
|
127
|
+
["plantuml", "-tpng", puml_path, "-o", temp_dir],
|
|
128
|
+
shell=platform.system() == "Windows",
|
|
129
|
+
check=True,
|
|
130
|
+
capture_output=True,
|
|
131
|
+
text=True,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Upload the generated image
|
|
135
|
+
file_service = FileService()
|
|
136
|
+
file_meta = file_service.upload_from_file(
|
|
137
|
+
png_path,
|
|
138
|
+
meta.get("session_id"),
|
|
139
|
+
data_source="Global Agent - PlantUML Action",
|
|
140
|
+
)
|
|
141
|
+
files.append(file_meta)
|
|
142
|
+
|
|
143
|
+
except FileNotFoundError as e:
|
|
144
|
+
log.error("PlantUML executable not found: %s", str(e))
|
|
145
|
+
return ActionResponse(
|
|
146
|
+
message="PlantUML executable not found. Please ensure PlantUML is installed and available in PATH.",
|
|
147
|
+
)
|
|
148
|
+
except subprocess.CalledProcessError as e:
|
|
149
|
+
log.error("Subprocess failed with stderr: %s", str(e))
|
|
150
|
+
return ActionResponse(
|
|
151
|
+
message=f"Failed to create diagram: {str(e)}",
|
|
152
|
+
)
|
|
153
|
+
finally:
|
|
154
|
+
if 'temp_dir' in locals() and os.path.exists(temp_dir):
|
|
155
|
+
try:
|
|
156
|
+
shutil.rmtree(temp_dir) # Remove directory and all contents
|
|
157
|
+
except:
|
|
158
|
+
pass
|
|
101
159
|
|
|
102
160
|
return ActionResponse(
|
|
103
161
|
message=f"diagram created with the plantUML: \n```\n{expression}\n```",
|
|
@@ -47,7 +47,7 @@ class PlotlyGraph(Action):
|
|
|
47
47
|
],
|
|
48
48
|
"reasoning": [
|
|
49
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"
|
|
50
|
+
"- invoke the plotly action from the global agent to generate a bar graph with random data",
|
|
51
51
|
],
|
|
52
52
|
"response_text": "Certainly! I'd be happy to generate a random bar graph for you.",
|
|
53
53
|
"status_update": "I'll use our plotting tool to create this for you right away.",
|
|
@@ -57,23 +57,23 @@ class PlotlyGraph(Action):
|
|
|
57
57
|
"parameter_name": "plotly_figure_config",
|
|
58
58
|
"parameters": {
|
|
59
59
|
"plotly_figure_config": [
|
|
60
|
-
"{
|
|
61
|
-
|
|
62
|
-
" {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
" }
|
|
60
|
+
"{",
|
|
61
|
+
' "data": [',
|
|
62
|
+
" {",
|
|
63
|
+
' "x": ["A", "B", "C", "D", "E"],',
|
|
64
|
+
' "y": [23, 45, 56, 78, 90],',
|
|
65
|
+
' "type": "bar"',
|
|
66
|
+
" }",
|
|
67
67
|
" ],",
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
" }
|
|
73
|
-
"}
|
|
68
|
+
' "layout": {',
|
|
69
|
+
' "title": "Random Bar Graph",',
|
|
70
|
+
' "xaxis": {"title": "Categories"},',
|
|
71
|
+
' "yaxis": {"title": "Values"}',
|
|
72
|
+
" }",
|
|
73
|
+
"}",
|
|
74
74
|
]
|
|
75
|
-
}
|
|
76
|
-
}
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
77
|
}
|
|
78
78
|
],
|
|
79
79
|
},
|
|
@@ -82,15 +82,38 @@ class PlotlyGraph(Action):
|
|
|
82
82
|
|
|
83
83
|
def invoke(self, params, meta={}) -> ActionResponse:
|
|
84
84
|
if platform.system() == "Windows":
|
|
85
|
-
kaleido_version = version(
|
|
86
|
-
min_version = parse(
|
|
87
|
-
max_version = parse(
|
|
88
|
-
if
|
|
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
|
+
):
|
|
89
92
|
return ActionResponse(
|
|
90
93
|
message="For Windows users, the plotting functionality requires a specific version of Kaleido. Please refer to the documentation."
|
|
91
94
|
)
|
|
92
95
|
obj = params["plotly_figure_config"]
|
|
93
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
|
+
|
|
94
117
|
# Remove any leading/trailing quote characters
|
|
95
118
|
obj = obj.strip("'\" ")
|
|
96
119
|
try:
|
|
@@ -110,7 +133,9 @@ class PlotlyGraph(Action):
|
|
|
110
133
|
pio.write_image(fig, byte_io)
|
|
111
134
|
byte_io.seek(0)
|
|
112
135
|
file_service = FileService()
|
|
113
|
-
image_name =
|
|
136
|
+
image_name = (
|
|
137
|
+
"generated_graph_" + str(random.randint(100000, 999999)) + ".png"
|
|
138
|
+
)
|
|
114
139
|
image_meta = file_service.upload_from_buffer(
|
|
115
140
|
byte_io.read(),
|
|
116
141
|
image_name,
|
|
@@ -120,7 +145,8 @@ class PlotlyGraph(Action):
|
|
|
120
145
|
files.append(image_meta)
|
|
121
146
|
except Exception as e:
|
|
122
147
|
return ActionResponse(
|
|
123
|
-
message="Could not create plotly graph. Please check the plotly figure configuration. plotly error: "
|
|
148
|
+
message="Could not create plotly graph. Please check the plotly figure configuration. plotly error: "
|
|
149
|
+
+ str(e),
|
|
124
150
|
)
|
|
125
151
|
|
|
126
152
|
return ActionResponse(files=files)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.2.
|
|
1
|
+
__version__ = "0.2.2"
|
|
@@ -101,7 +101,7 @@ def add_agent_command(name):
|
|
|
101
101
|
|
|
102
102
|
click.echo(f"Created sample action at: {get_display_path(action_path)}")
|
|
103
103
|
|
|
104
|
-
temp_file = os.path.join(config_directory, "
|
|
104
|
+
temp_file = os.path.join(config_directory, ".gitkeep")
|
|
105
105
|
if os.path.exists(temp_file):
|
|
106
106
|
os.remove(temp_file)
|
|
107
107
|
except IOError as e:
|
|
@@ -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 =
|
|
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 '{
|
|
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
|
-
|
|
84
|
-
temp_file = os.path.join(config_directory, "
|
|
85
|
+
|
|
86
|
+
temp_file = os.path.join(config_directory, ".gitkeep")
|
|
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
|
+
)
|
|
@@ -289,7 +289,7 @@ def add_gateway_command(name, interfaces):
|
|
|
289
289
|
|
|
290
290
|
created_file_names.append(gateway_config_path)
|
|
291
291
|
|
|
292
|
-
temp_file = os.path.join(config_directory, "
|
|
292
|
+
temp_file = os.path.join(config_directory, ".gitkeep")
|
|
293
293
|
if os.path.exists(temp_file):
|
|
294
294
|
os.remove(temp_file)
|
|
295
295
|
|
|
@@ -328,7 +328,7 @@ def add_interface_command(name):
|
|
|
328
328
|
try:
|
|
329
329
|
os.makedirs(interfaces_directory, exist_ok=True)
|
|
330
330
|
|
|
331
|
-
temp_file = os.path.join(interfaces_directory, "
|
|
331
|
+
temp_file = os.path.join(interfaces_directory, ".gitkeep")
|
|
332
332
|
if os.path.exists(temp_file):
|
|
333
333
|
os.remove(temp_file)
|
|
334
334
|
|
|
@@ -378,7 +378,22 @@ def build_solace_agent_mesh(config, build_config_dir, abort, parsers):
|
|
|
378
378
|
if f.endswith(".yaml")
|
|
379
379
|
and not any(f.startswith(prefix) for prefix in skip_prefixes)
|
|
380
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
|
+
|
|
381
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
|
|
382
397
|
try:
|
|
383
398
|
# Read config file
|
|
384
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
|
-
"
|
|
46
|
-
"
|
|
47
|
-
default_options["
|
|
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
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
"""
|
|
@@ -9,5 +9,5 @@ def check_if_already_done(options, default_options, none_interactive, abort):
|
|
|
9
9
|
config_path = Config.user_config_file
|
|
10
10
|
if os.path.exists(config_path):
|
|
11
11
|
abort(
|
|
12
|
-
"The project has already been initialized. If you want to reinitialize the project, please delete the solace-agent-mesh.yaml file
|
|
12
|
+
"The project has already been initialized. If you want to reinitialize the project, please delete the solace-agent-mesh.yaml file"
|
|
13
13
|
)
|
|
@@ -21,6 +21,14 @@ def create_config_file_step(options, default_options, none_interactive, abort):
|
|
|
21
21
|
builtin_agents = options.get("built_in_agent", [])
|
|
22
22
|
for agent in builtin_agents_ref:
|
|
23
23
|
agent["enabled"] = agent["name"] in builtin_agents or agent["name"] == "global"
|
|
24
|
+
|
|
25
|
+
# Set up the built-in services
|
|
26
|
+
builtin_services_ref = (
|
|
27
|
+
sam_config.get("solace_agent_mesh", {}).get("built_in", {}).get("services", [])
|
|
28
|
+
)
|
|
29
|
+
for service in builtin_services_ref:
|
|
30
|
+
if service["name"] == "embedding":
|
|
31
|
+
service["enabled"] = options.get("embedding_service_enabled", False)
|
|
24
32
|
|
|
25
33
|
# Set up the project structure
|
|
26
34
|
sam_config.get("solace_agent_mesh", {})["config_directory"] = options.get("config_dir")
|
|
@@ -9,45 +9,13 @@ from .create_config_file_step import create_config_file_step
|
|
|
9
9
|
from .file_service_step import file_service_step
|
|
10
10
|
from .project_structure_step import project_structure_step
|
|
11
11
|
from .create_other_project_files_step import create_other_project_files_step
|
|
12
|
+
from .web_init_step import web_init_step
|
|
12
13
|
|
|
13
14
|
from cli.utils import (
|
|
14
15
|
log_error,
|
|
16
|
+
ask_yes_no_question,
|
|
15
17
|
)
|
|
16
|
-
|
|
17
|
-
default_options = {
|
|
18
|
-
"namespace": "",
|
|
19
|
-
"config_dir": "configs",
|
|
20
|
-
"module_dir": "modules",
|
|
21
|
-
"env_file": ".env",
|
|
22
|
-
"build_dir": "build",
|
|
23
|
-
"broker_type": "container",
|
|
24
|
-
"broker_url": "ws://localhost:8008",
|
|
25
|
-
"broker_vpn": "default",
|
|
26
|
-
"broker_username": "default",
|
|
27
|
-
"broker_password": "default",
|
|
28
|
-
"container_engine": "docker",
|
|
29
|
-
"llm_model_name": "openai/gpt-4o",
|
|
30
|
-
"llm_endpoint_url": "https://api.openai.com/v1",
|
|
31
|
-
"llm_api_key": "",
|
|
32
|
-
"embedding_model_name": "openai/text-embedding-ada-002",
|
|
33
|
-
"embedding_endpoint_url": "https://api.openai.com/v1",
|
|
34
|
-
"embedding_api_key": "",
|
|
35
|
-
"built_in_agent": [],
|
|
36
|
-
"file_service_provider": "volume",
|
|
37
|
-
"file_service_config": ["volume=/tmp/solace-agent-mesh"],
|
|
38
|
-
"env_var": [],
|
|
39
|
-
"rest_api_enabled": True,
|
|
40
|
-
"rest_api_server_input_port": "5050",
|
|
41
|
-
"rest_api_server_host": "127.0.0.1",
|
|
42
|
-
"rest_api_server_input_endpoint": "/api/v1/request",
|
|
43
|
-
"rest_api_gateway_name": "rest-api",
|
|
44
|
-
"webui_enabled": True,
|
|
45
|
-
"webui_listen_port": "5001",
|
|
46
|
-
"webui_host": "127.0.0.1"
|
|
47
|
-
}
|
|
48
|
-
"""
|
|
49
|
-
Default options for the init command.
|
|
50
|
-
"""
|
|
18
|
+
from solace_agent_mesh.config_portal.backend.common import default_options
|
|
51
19
|
|
|
52
20
|
|
|
53
21
|
def abort(message: str):
|
|
@@ -61,14 +29,19 @@ def init_command(options={}):
|
|
|
61
29
|
"""
|
|
62
30
|
Initialize the Solace Agent Mesh application.
|
|
63
31
|
"""
|
|
64
|
-
click.echo(click.style("Initializing Solace Agent Mesh", bold=True, fg="blue"))
|
|
65
32
|
skip = False
|
|
66
33
|
if "skip" in options and options["skip"]:
|
|
67
34
|
skip = True
|
|
68
35
|
|
|
36
|
+
click.echo(click.style("Initializing Solace Agent Mesh", bold=True, fg="blue"))
|
|
37
|
+
check_if_already_done(options, default_options, skip, abort)
|
|
38
|
+
|
|
39
|
+
use_web_based_init = options.get("use_web_based_init", False)
|
|
40
|
+
if not use_web_based_init and not skip:
|
|
41
|
+
use_web_based_init = ask_yes_no_question("Would you like to configure your project through a web interface in your browser?", True)
|
|
42
|
+
|
|
69
43
|
# no description for hidden steps
|
|
70
|
-
|
|
71
|
-
("", check_if_already_done),
|
|
44
|
+
cli_steps = [
|
|
72
45
|
("Project structure setup", project_structure_step),
|
|
73
46
|
("Broker setup", broker_step),
|
|
74
47
|
("AI provider setup", ai_provider_step),
|
|
@@ -77,6 +50,15 @@ def init_command(options={}):
|
|
|
77
50
|
("", create_config_file_step),
|
|
78
51
|
("Setting up project", create_other_project_files_step),
|
|
79
52
|
]
|
|
53
|
+
|
|
54
|
+
web_steps = [
|
|
55
|
+
("Initilize in web", web_init_step),
|
|
56
|
+
("", create_config_file_step),
|
|
57
|
+
("", create_other_project_files_step),
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
steps = web_steps if use_web_based_init else cli_steps
|
|
61
|
+
|
|
80
62
|
non_hidden_steps_count = len([step for step in steps if step[0]])
|
|
81
63
|
|
|
82
64
|
step = 0
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import multiprocessing
|
|
3
|
+
from solace_agent_mesh.config_portal.backend.server import run_flask
|
|
4
|
+
|
|
5
|
+
def web_init_step(options, default_options, none_interactive, abort):
|
|
6
|
+
if not none_interactive:
|
|
7
|
+
with multiprocessing.Manager() as manager:
|
|
8
|
+
# Create a shared configuration dictionary
|
|
9
|
+
shared_config = manager.dict()
|
|
10
|
+
|
|
11
|
+
# Start the Flask server with the shared config
|
|
12
|
+
init_gui_process = multiprocessing.Process(
|
|
13
|
+
target=run_flask,
|
|
14
|
+
args=("127.0.0.1", 5002, shared_config)
|
|
15
|
+
)
|
|
16
|
+
init_gui_process.start()
|
|
17
|
+
|
|
18
|
+
click.echo(click.style("Web configuration portal is running at http://127.0.0.1:5002", fg="green"))
|
|
19
|
+
click.echo("Complete the configuration in your browser to continue...")
|
|
20
|
+
|
|
21
|
+
# Wait for the Flask server to finish
|
|
22
|
+
init_gui_process.join()
|
|
23
|
+
|
|
24
|
+
# Get the configuration from the shared dictionary
|
|
25
|
+
if shared_config:
|
|
26
|
+
# Convert from manager.dict to regular dict
|
|
27
|
+
config_from_portal = dict(shared_config)
|
|
28
|
+
options.update(config_from_portal)
|
|
29
|
+
click.echo(click.style("Configuration received from portal", fg="green"))
|
|
30
|
+
|
|
31
|
+
else:
|
|
32
|
+
abort("Web configuration failed, please try again.")
|