solace-agent-mesh 0.0.1__py3-none-any.whl → 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of solace-agent-mesh might be problematic. Click here for more details.
- 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/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 +23 -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.0.dist-info/METADATA +179 -0
- solace_agent_mesh-0.1.0.dist-info/RECORD +170 -0
- solace_agent_mesh-0.1.0.dist-info/entry_points.txt +3 -0
- solace_agent_mesh-0.0.1.dist-info/licenses/LICENSE.txt → solace_agent_mesh-0.1.0.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.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import requests
|
|
3
|
+
import os
|
|
4
|
+
import json
|
|
5
|
+
import pyperclip
|
|
6
|
+
import uuid
|
|
7
|
+
import base64
|
|
8
|
+
import stat
|
|
9
|
+
|
|
10
|
+
from ...utils import log_error
|
|
11
|
+
from ...utils import log_link
|
|
12
|
+
from ...utils import log_success
|
|
13
|
+
|
|
14
|
+
XDG_CONFIG_HOME = os.getenv(
|
|
15
|
+
"XDG_CONFIG_HOME", os.path.expanduser("~/.config/solace_agent_mesh")
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
XDG_DOWNLOAD_DIR = os.getenv(
|
|
19
|
+
"XDG_DOWNLOAD_DIR", os.path.expanduser("~/Downloads/solace_agent_mesh")
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
TOKEN_FILE = os.path.join(XDG_CONFIG_HOME, "token.json")
|
|
23
|
+
CONFIG_FILE = os.path.join(XDG_CONFIG_HOME, "setting.conf")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def validate_token(server, access_token):
|
|
27
|
+
"""
|
|
28
|
+
Validate the access token with the server.
|
|
29
|
+
"""
|
|
30
|
+
response = requests.post(
|
|
31
|
+
f"{server}/is_token_valid",
|
|
32
|
+
headers={"Authorization": f"Bearer {access_token}"},
|
|
33
|
+
)
|
|
34
|
+
return response.status_code
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def config_directory():
|
|
38
|
+
"""
|
|
39
|
+
Get the configuration directory path.
|
|
40
|
+
"""
|
|
41
|
+
return XDG_CONFIG_HOME
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def file_directory():
|
|
45
|
+
"""
|
|
46
|
+
Get the file directory path.
|
|
47
|
+
"""
|
|
48
|
+
return XDG_DOWNLOAD_DIR
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def set_permission(file_path):
|
|
52
|
+
"""
|
|
53
|
+
Set file permissions to be readable and writable only by the owner.
|
|
54
|
+
"""
|
|
55
|
+
os.chmod(file_path, stat.S_IRUSR | stat.S_IWUSR)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def save_configuration(conf: dict):
|
|
59
|
+
"""
|
|
60
|
+
Save the server address to the configuration file.
|
|
61
|
+
"""
|
|
62
|
+
home_dir = config_directory()
|
|
63
|
+
|
|
64
|
+
# Create the home directory if it does not exist
|
|
65
|
+
os.makedirs(home_dir, exist_ok=True)
|
|
66
|
+
|
|
67
|
+
# Load existing configuration if it exists
|
|
68
|
+
if os.path.exists(CONFIG_FILE):
|
|
69
|
+
with open(CONFIG_FILE, "r") as f:
|
|
70
|
+
existing_conf = json.load(f)
|
|
71
|
+
else:
|
|
72
|
+
existing_conf = {}
|
|
73
|
+
|
|
74
|
+
# Update the existing configuration with new values
|
|
75
|
+
existing_conf.update(conf)
|
|
76
|
+
|
|
77
|
+
# Save the updated configuration
|
|
78
|
+
with open(CONFIG_FILE, "w") as f:
|
|
79
|
+
json.dump(existing_conf, f)
|
|
80
|
+
set_permission(CONFIG_FILE)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def save_files(files):
|
|
84
|
+
"""
|
|
85
|
+
Save files to the file directory.
|
|
86
|
+
"""
|
|
87
|
+
if files:
|
|
88
|
+
file_directory_path = file_directory()
|
|
89
|
+
os.makedirs(file_directory_path, exist_ok=True)
|
|
90
|
+
for file in files:
|
|
91
|
+
file_name = file["name"]
|
|
92
|
+
file_content = file["content"]
|
|
93
|
+
file_type = file["mime_type"]
|
|
94
|
+
if file_type.startswith("image/"):
|
|
95
|
+
# Decode Base64
|
|
96
|
+
file_content = base64.b64decode(file_content)
|
|
97
|
+
else:
|
|
98
|
+
file_content = file_content.encode("utf-8")
|
|
99
|
+
|
|
100
|
+
file_path = os.path.join(file_directory_path, file_name)
|
|
101
|
+
with open(file_path, "wb") as f:
|
|
102
|
+
f.write(file_content)
|
|
103
|
+
click.echo(f"File saved in {file_path}")
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def chat_command(chat):
|
|
107
|
+
"""
|
|
108
|
+
Login and start a chat conversation.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
@chat.command()
|
|
112
|
+
@click.argument("server")
|
|
113
|
+
def login(server):
|
|
114
|
+
"""Log in to the solace-agent-mesh application."""
|
|
115
|
+
if not server:
|
|
116
|
+
log_error("Server address is required.")
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
# Save the server address
|
|
120
|
+
save_configuration({"auth_server": server})
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
url = f"{server}/login"
|
|
124
|
+
click.secho(
|
|
125
|
+
"\U0001F447 Please authenticate by visiting the following URL:",
|
|
126
|
+
fg="green",
|
|
127
|
+
)
|
|
128
|
+
log_link(url)
|
|
129
|
+
|
|
130
|
+
click.prompt(
|
|
131
|
+
click.style(
|
|
132
|
+
"\U0001F4C4 Copy the entire json object and press Enter (Ctrl+D to exit)",
|
|
133
|
+
fg="green",
|
|
134
|
+
),
|
|
135
|
+
default="",
|
|
136
|
+
show_default=False,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
token = pyperclip.paste()
|
|
140
|
+
|
|
141
|
+
# Save the response as JSON
|
|
142
|
+
with open(TOKEN_FILE, "w") as f:
|
|
143
|
+
json.dump(json.loads(token), f)
|
|
144
|
+
set_permission(TOKEN_FILE)
|
|
145
|
+
|
|
146
|
+
token_data = json.loads(token)
|
|
147
|
+
|
|
148
|
+
# Verify the token
|
|
149
|
+
try:
|
|
150
|
+
status_code = validate_token(server, token_data.get("access_token"))
|
|
151
|
+
if status_code == 200:
|
|
152
|
+
log_success("Login successful.")
|
|
153
|
+
else:
|
|
154
|
+
log_error("Token is invalid or expired. Please log in again.")
|
|
155
|
+
return
|
|
156
|
+
except requests.exceptions.RequestException as e:
|
|
157
|
+
log_error(f"Token validation failed: {e}")
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
except requests.exceptions.RequestException as e:
|
|
161
|
+
log_error(f"Login failed: {e}")
|
|
162
|
+
|
|
163
|
+
@chat.command()
|
|
164
|
+
def logout():
|
|
165
|
+
"""Log out from the solace-agent-mesh application."""
|
|
166
|
+
if os.path.exists(TOKEN_FILE):
|
|
167
|
+
os.remove(TOKEN_FILE)
|
|
168
|
+
log_success("Logout successful.")
|
|
169
|
+
else:
|
|
170
|
+
log_error("No active session found.")
|
|
171
|
+
|
|
172
|
+
@chat.command()
|
|
173
|
+
@click.option(
|
|
174
|
+
"-s",
|
|
175
|
+
"--stream",
|
|
176
|
+
is_flag=True,
|
|
177
|
+
help="Stream mode.",
|
|
178
|
+
default=False,
|
|
179
|
+
)
|
|
180
|
+
@click.option(
|
|
181
|
+
"-a",
|
|
182
|
+
"--auth",
|
|
183
|
+
is_flag=True,
|
|
184
|
+
help="Enable authentication.",
|
|
185
|
+
default=False,
|
|
186
|
+
)
|
|
187
|
+
@click.option(
|
|
188
|
+
"-u",
|
|
189
|
+
"--url",
|
|
190
|
+
help="Chat endpoint url (e.g., https://<server>/api/v1/request).",
|
|
191
|
+
)
|
|
192
|
+
@click.option(
|
|
193
|
+
"-f",
|
|
194
|
+
"--file",
|
|
195
|
+
type=click.Path(exists=True, dir_okay=False, resolve_path=True),
|
|
196
|
+
multiple=True,
|
|
197
|
+
help="Attach files to the chat. Use -f or --file for each file.",
|
|
198
|
+
)
|
|
199
|
+
def start(stream, auth, url, file):
|
|
200
|
+
"""Establish a chat with solace-agent-mesh plugin application."""
|
|
201
|
+
|
|
202
|
+
# Load the rest server address
|
|
203
|
+
rest_server = None
|
|
204
|
+
if url is not None:
|
|
205
|
+
# save the server address
|
|
206
|
+
rest_server = url
|
|
207
|
+
save_configuration({"rest_api_server": rest_server})
|
|
208
|
+
elif os.path.exists(CONFIG_FILE):
|
|
209
|
+
# load the server address from the configuration file
|
|
210
|
+
with open(CONFIG_FILE, "r") as f:
|
|
211
|
+
setting_data = json.load(f)
|
|
212
|
+
rest_server = setting_data.get("rest_api_server", None)
|
|
213
|
+
if rest_server is None:
|
|
214
|
+
rest_server = click.prompt(
|
|
215
|
+
click.style(
|
|
216
|
+
"💬 Enter the server address (e.g., https://<server>/api/v1/request): ",
|
|
217
|
+
fg="green",
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
# Save the server address
|
|
221
|
+
save_configuration({"rest_api_server": rest_server})
|
|
222
|
+
|
|
223
|
+
session_id = str(uuid.uuid4())
|
|
224
|
+
rest_endpoint_headers = {}
|
|
225
|
+
|
|
226
|
+
# Authenticate if it is enabled
|
|
227
|
+
if auth:
|
|
228
|
+
# Load authentication server
|
|
229
|
+
with open(CONFIG_FILE, "r") as f:
|
|
230
|
+
setting_data = json.load(f)
|
|
231
|
+
auth_server = setting_data.get("auth_server", "")
|
|
232
|
+
if not auth_server:
|
|
233
|
+
log_error(
|
|
234
|
+
"Authentication server not found. Please log in using 'sam login <server name>'."
|
|
235
|
+
)
|
|
236
|
+
return
|
|
237
|
+
|
|
238
|
+
# Load the token
|
|
239
|
+
if not os.path.exists(TOKEN_FILE):
|
|
240
|
+
log_error(
|
|
241
|
+
"Token not found. Please log in using 'sam login <server name>'."
|
|
242
|
+
)
|
|
243
|
+
return
|
|
244
|
+
|
|
245
|
+
try:
|
|
246
|
+
with open(TOKEN_FILE, "r") as f:
|
|
247
|
+
token_data = json.load(f)
|
|
248
|
+
access_token = token_data.get("access_token")
|
|
249
|
+
refresh_token = token_data.get("refresh_token")
|
|
250
|
+
|
|
251
|
+
if access_token and refresh_token:
|
|
252
|
+
# Validate token
|
|
253
|
+
try:
|
|
254
|
+
status_code = validate_token(auth_server, access_token)
|
|
255
|
+
if status_code != 200:
|
|
256
|
+
# Refresh token
|
|
257
|
+
response = requests.post(
|
|
258
|
+
f"{auth_server}/refresh_token",
|
|
259
|
+
headers={
|
|
260
|
+
"Content-Type": "application/json",
|
|
261
|
+
"Authorization": f"Bearer {access_token}",
|
|
262
|
+
},
|
|
263
|
+
json={"refresh_token": refresh_token},
|
|
264
|
+
)
|
|
265
|
+
if response.status_code == 200:
|
|
266
|
+
access_token = response.json()
|
|
267
|
+
else:
|
|
268
|
+
log_error(
|
|
269
|
+
"Failed to refresh token. Please log in again."
|
|
270
|
+
)
|
|
271
|
+
return
|
|
272
|
+
rest_endpoint_headers = {
|
|
273
|
+
"Authorization": f"Bearer {access_token}"
|
|
274
|
+
}
|
|
275
|
+
except requests.exceptions.RequestException as e:
|
|
276
|
+
log_error(f"Token validation failed: {e}")
|
|
277
|
+
return
|
|
278
|
+
else:
|
|
279
|
+
log_error(
|
|
280
|
+
"Invalid token data. Please log in again using 'sam login <server name>'."
|
|
281
|
+
)
|
|
282
|
+
return
|
|
283
|
+
except Exception as e:
|
|
284
|
+
log_error(
|
|
285
|
+
f"Token data not found: {e}. Please log in again using 'sam login <server name>'."
|
|
286
|
+
)
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
try:
|
|
290
|
+
files_to_send = []
|
|
291
|
+
if file:
|
|
292
|
+
for file_path in file:
|
|
293
|
+
file_name = os.path.basename(file_path)
|
|
294
|
+
files_to_send.append(("files", (file_name, open(file_path, "rb"))))
|
|
295
|
+
|
|
296
|
+
while True:
|
|
297
|
+
user_prompt = click.prompt(
|
|
298
|
+
click.style("💬 How can I help you? (Ctrl+D to exit)", fg="green")
|
|
299
|
+
)
|
|
300
|
+
# Exit the chat
|
|
301
|
+
if user_prompt.lower() == "exit":
|
|
302
|
+
break
|
|
303
|
+
# Skip empty prompts
|
|
304
|
+
if not user_prompt.strip():
|
|
305
|
+
continue
|
|
306
|
+
|
|
307
|
+
try:
|
|
308
|
+
response = requests.post(
|
|
309
|
+
f"{rest_server}",
|
|
310
|
+
headers=rest_endpoint_headers,
|
|
311
|
+
data={
|
|
312
|
+
"prompt": user_prompt,
|
|
313
|
+
"stream": stream,
|
|
314
|
+
"session_id": session_id,
|
|
315
|
+
},
|
|
316
|
+
files=files_to_send,
|
|
317
|
+
)
|
|
318
|
+
response.raise_for_status()
|
|
319
|
+
|
|
320
|
+
if stream:
|
|
321
|
+
buffer = ""
|
|
322
|
+
for chunk in response.iter_content(chunk_size=1):
|
|
323
|
+
utf_chunk = chunk.decode("utf-8")
|
|
324
|
+
buffer += utf_chunk
|
|
325
|
+
|
|
326
|
+
while "\n\n" in buffer:
|
|
327
|
+
split_chunk = buffer.split("\n\n", 1)
|
|
328
|
+
data_chunk = split_chunk[0]
|
|
329
|
+
buffer = split_chunk[1]
|
|
330
|
+
|
|
331
|
+
if data_chunk == "data: [DONE]":
|
|
332
|
+
break
|
|
333
|
+
data = json.loads(data_chunk[6:])
|
|
334
|
+
content = data.get("content", "")
|
|
335
|
+
if content:
|
|
336
|
+
click.echo(content, nl=False)
|
|
337
|
+
status_message = data.get("status_message", "")
|
|
338
|
+
if status_message:
|
|
339
|
+
click.secho(status_message, nl=True, fg="yellow")
|
|
340
|
+
# Save files
|
|
341
|
+
if "files" in data:
|
|
342
|
+
files = data["files"]
|
|
343
|
+
save_files(files)
|
|
344
|
+
else:
|
|
345
|
+
data = response.json()
|
|
346
|
+
click.echo(f"{data['response']['content']}")
|
|
347
|
+
# Save files
|
|
348
|
+
if "files" in data["response"]:
|
|
349
|
+
files = data["response"]["files"]
|
|
350
|
+
save_files(files)
|
|
351
|
+
click.echo("\n")
|
|
352
|
+
|
|
353
|
+
except requests.exceptions.RequestException as e:
|
|
354
|
+
log_error(f"Request failed: {e}")
|
|
355
|
+
return
|
|
356
|
+
|
|
357
|
+
except json.JSONDecodeError:
|
|
358
|
+
log_error(
|
|
359
|
+
"Sorry! I could not response to your request. Please try again later."
|
|
360
|
+
)
|
|
361
|
+
return
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import click
|
|
3
|
+
|
|
4
|
+
from cli.config import Config
|
|
5
|
+
from cli.utils import load_template, get_display_path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def config_command():
|
|
9
|
+
"""Update the config file with new default settings."""
|
|
10
|
+
config_path = Config.user_config_file
|
|
11
|
+
|
|
12
|
+
if not os.path.exists(config_path):
|
|
13
|
+
click.echo(
|
|
14
|
+
click.style(
|
|
15
|
+
(
|
|
16
|
+
"No config file found. "
|
|
17
|
+
"\n\tRun `solace-agent-mesh init` to generate a new config file."
|
|
18
|
+
),
|
|
19
|
+
fg="yellow",
|
|
20
|
+
)
|
|
21
|
+
)
|
|
22
|
+
return 1
|
|
23
|
+
|
|
24
|
+
config = Config.get_config(config_path)
|
|
25
|
+
Config.write_config(config, config_path)
|
|
26
|
+
|
|
27
|
+
click.echo(
|
|
28
|
+
f"Config file updated with latest default configurations at {get_display_path(config_path)}"
|
|
29
|
+
)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from cli.utils import ask_if_not_provided
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def ai_provider_step(options, default_options, none_interactive, abort):
|
|
7
|
+
"""
|
|
8
|
+
Initialize the AI provider.
|
|
9
|
+
"""
|
|
10
|
+
ask_if_not_provided(
|
|
11
|
+
options,
|
|
12
|
+
"llm_endpoint_url",
|
|
13
|
+
"Provide LLM endpoint URL",
|
|
14
|
+
default_options["llm_endpoint_url"],
|
|
15
|
+
none_interactive,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
ask_if_not_provided(
|
|
19
|
+
options,
|
|
20
|
+
"llm_api_key",
|
|
21
|
+
"Provide LLM API Key",
|
|
22
|
+
default_options["llm_api_key"],
|
|
23
|
+
none_interactive,
|
|
24
|
+
hide_input=True,
|
|
25
|
+
)
|
|
26
|
+
click.echo(
|
|
27
|
+
click.style(
|
|
28
|
+
(
|
|
29
|
+
"The model name should follow the format `provider/model-name`."
|
|
30
|
+
"\n\t For example: openai/gpt-4o or openai/my-model-that-follows-openai-api"
|
|
31
|
+
),
|
|
32
|
+
fg="yellow",
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
ask_if_not_provided(
|
|
36
|
+
options,
|
|
37
|
+
"llm_model_name",
|
|
38
|
+
"Provide LLM model name to use",
|
|
39
|
+
default_options["llm_model_name"],
|
|
40
|
+
none_interactive,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
ask_if_not_provided(
|
|
44
|
+
options,
|
|
45
|
+
"embedding_endpoint_url",
|
|
46
|
+
"Provide Embedding endpoint URL",
|
|
47
|
+
default_options["embedding_endpoint_url"],
|
|
48
|
+
none_interactive,
|
|
49
|
+
)
|
|
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
|
+
)
|
|
59
|
+
|
|
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",
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
|
|
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
|
+
)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import click
|
|
3
|
+
import os
|
|
4
|
+
|
|
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
|
+
|
|
10
|
+
|
|
11
|
+
def broker_step(options, default_options, none_interactive, abort):
|
|
12
|
+
"""
|
|
13
|
+
Initialize the broker.
|
|
14
|
+
"""
|
|
15
|
+
broker_type = ask_if_not_provided(
|
|
16
|
+
options,
|
|
17
|
+
"broker_type",
|
|
18
|
+
(
|
|
19
|
+
"Which broker type do you want to use?\n"
|
|
20
|
+
"\t1) Existing Solace Pub/Sub+ broker\n"
|
|
21
|
+
"\t2) New local Solace PubSub+ broker container (podman/docker)\n"
|
|
22
|
+
"\t3) Run in 'dev mode' - all in one process (not recommended for production)\n"
|
|
23
|
+
"Enter the number of your choice"
|
|
24
|
+
),
|
|
25
|
+
"1",
|
|
26
|
+
none_interactive,
|
|
27
|
+
["1", "2", "3"],
|
|
28
|
+
)
|
|
29
|
+
if broker_type == "2" or broker_type == "container":
|
|
30
|
+
options["dev_mode"] = "false"
|
|
31
|
+
# Check if the user have podman or docker installed
|
|
32
|
+
has_podman = shutil.which("podman")
|
|
33
|
+
has_docker = shutil.which("docker")
|
|
34
|
+
if not has_podman and not has_docker:
|
|
35
|
+
abort(
|
|
36
|
+
"You need to have either podman or docker installed to use the container broker."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
container_engine = "podman" if has_podman else "docker"
|
|
40
|
+
if has_podman and has_docker:
|
|
41
|
+
container_engine = ask_if_not_provided(
|
|
42
|
+
options,
|
|
43
|
+
"container_engine",
|
|
44
|
+
"Which container engine do you want to use?",
|
|
45
|
+
"podman",
|
|
46
|
+
none_interactive,
|
|
47
|
+
["podman", "docker"],
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Run command for the container start
|
|
51
|
+
command = container_engine + CONTAINER_RUN_COMMAND
|
|
52
|
+
click.echo(
|
|
53
|
+
f"Running the Solace PubSub+ broker container using {container_engine}"
|
|
54
|
+
)
|
|
55
|
+
response_status = os.system(command)
|
|
56
|
+
if response_status != 0:
|
|
57
|
+
abort("Failed to start the Solace PubSub+ broker container.")
|
|
58
|
+
|
|
59
|
+
options["broker_url"] = default_options["broker_url"]
|
|
60
|
+
options["broker_vpn"] = default_options["broker_vpn"]
|
|
61
|
+
options["broker_username"] = default_options["broker_username"]
|
|
62
|
+
options["broker_password"] = default_options["broker_password"]
|
|
63
|
+
|
|
64
|
+
elif broker_type == "1" or broker_type == "solace":
|
|
65
|
+
options["dev_mode"] = "false"
|
|
66
|
+
ask_if_not_provided(
|
|
67
|
+
options,
|
|
68
|
+
"broker_url",
|
|
69
|
+
"Enter the Solace broker url endpoint",
|
|
70
|
+
default_options["broker_url"],
|
|
71
|
+
none_interactive,
|
|
72
|
+
)
|
|
73
|
+
ask_if_not_provided(
|
|
74
|
+
options,
|
|
75
|
+
"broker_vpn",
|
|
76
|
+
"Enter the Solace broker vpn name",
|
|
77
|
+
default_options["broker_vpn"],
|
|
78
|
+
none_interactive,
|
|
79
|
+
)
|
|
80
|
+
ask_if_not_provided(
|
|
81
|
+
options,
|
|
82
|
+
"broker_username",
|
|
83
|
+
"Enter the Solace broker username",
|
|
84
|
+
default_options["broker_username"],
|
|
85
|
+
none_interactive,
|
|
86
|
+
)
|
|
87
|
+
ask_if_not_provided(
|
|
88
|
+
options,
|
|
89
|
+
"broker_password",
|
|
90
|
+
"Enter the Solace broker password",
|
|
91
|
+
default_options["broker_password"],
|
|
92
|
+
none_interactive,
|
|
93
|
+
hide_input=True,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
elif (
|
|
97
|
+
broker_type == "3" or
|
|
98
|
+
broker_type == "dev_broker" or
|
|
99
|
+
broker_type == "dev_mode" or
|
|
100
|
+
broker_type == "dev"
|
|
101
|
+
):
|
|
102
|
+
options["dev_mode"] = "true"
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from cli.utils import ask_if_not_provided
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def builtin_agent_step(options, default_options, none_interactive, abort):
|
|
5
|
+
"""
|
|
6
|
+
Initialize the built-in agent.
|
|
7
|
+
"""
|
|
8
|
+
agents = {
|
|
9
|
+
"web_request": (True, "can make queries to web to get real-time data", []),
|
|
10
|
+
"image_processing": (
|
|
11
|
+
True,
|
|
12
|
+
"generate images from text or convert images to text (The model name should be formatted like provider/model-name)",
|
|
13
|
+
["IMAGE_GEN_MODEL=", "IMAGE_GEN_ENDPOINT=", "IMAGE_GEN_API_KEY="],
|
|
14
|
+
),
|
|
15
|
+
"slack": (
|
|
16
|
+
False,
|
|
17
|
+
"Slack agent, send messages to Slack channels",
|
|
18
|
+
["MONITOR_SLACK_BOT_TOKEN="],
|
|
19
|
+
),
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
env_vars = {}
|
|
23
|
+
if "env_var" in options and options["env_var"]:
|
|
24
|
+
env_var_list = list(options["env_var"])
|
|
25
|
+
for env_var in env_var_list:
|
|
26
|
+
if "=" in env_var:
|
|
27
|
+
key, value = env_var.split("=")
|
|
28
|
+
env_vars[key] = value
|
|
29
|
+
|
|
30
|
+
if (
|
|
31
|
+
"built_in_agent" in options
|
|
32
|
+
and (
|
|
33
|
+
isinstance(options["built_in_agent"], tuple)
|
|
34
|
+
or isinstance(options["built_in_agent"], list)
|
|
35
|
+
)
|
|
36
|
+
and len(options["built_in_agent"]) > 0
|
|
37
|
+
):
|
|
38
|
+
options["built_in_agent"] = list(options["built_in_agent"])
|
|
39
|
+
# Update env vars with default values
|
|
40
|
+
for name, agent in agents.items():
|
|
41
|
+
if name in options["built_in_agent"]:
|
|
42
|
+
for pair in agent[2]:
|
|
43
|
+
key, value = pair.split("=")
|
|
44
|
+
if key not in env_vars:
|
|
45
|
+
env_vars[key] = value
|
|
46
|
+
options["env_var"] = [f"{key}={value}" for key, value in env_vars.items()]
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
if none_interactive:
|
|
50
|
+
selected_agents_list = [
|
|
51
|
+
agent for agent, (enabled, _, _), in agents.items() if enabled
|
|
52
|
+
]
|
|
53
|
+
options["built_in_agent"] = selected_agents_list
|
|
54
|
+
# Update env vars with default values
|
|
55
|
+
for name, agent in agents.items():
|
|
56
|
+
if name in options["built_in_agent"]:
|
|
57
|
+
for pair in agent[2]:
|
|
58
|
+
key, value = pair.split("=")
|
|
59
|
+
if key not in env_vars:
|
|
60
|
+
env_vars[key] = value
|
|
61
|
+
options["env_var"] = [f"{key}={value}" for key, value in env_vars.items()]
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
selected_agents = {}
|
|
65
|
+
for agent, (default, description, pairs) in agents.items():
|
|
66
|
+
enabled = ask_if_not_provided(
|
|
67
|
+
selected_agents,
|
|
68
|
+
agent,
|
|
69
|
+
f"Enable {agent} agent: {description}",
|
|
70
|
+
default,
|
|
71
|
+
none_interactive,
|
|
72
|
+
)
|
|
73
|
+
if enabled:
|
|
74
|
+
for pair in pairs:
|
|
75
|
+
key, value = pair.split("=")
|
|
76
|
+
ask_if_not_provided(
|
|
77
|
+
env_vars,
|
|
78
|
+
key,
|
|
79
|
+
f'\tAgent "{agent}" requires env value "{key}":',
|
|
80
|
+
value,
|
|
81
|
+
none_interactive,
|
|
82
|
+
hide_input="KEY" in key.upper(),
|
|
83
|
+
)
|
|
84
|
+
selected_agents_list = [
|
|
85
|
+
agent for agent, enabled in selected_agents.items() if enabled
|
|
86
|
+
]
|
|
87
|
+
options["built_in_agent"] = selected_agents_list
|
|
88
|
+
options["env_var"] = [f"{key}={value}" for key, value in env_vars.items()]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from cli.config import Config
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def check_if_already_done(options, default_options, none_interactive, abort):
|
|
7
|
+
"""
|
|
8
|
+
Checks if the init command has already been done"""
|
|
9
|
+
config_path = Config.user_config_file
|
|
10
|
+
if os.path.exists(config_path):
|
|
11
|
+
abort(
|
|
12
|
+
"The project has already been initialized. If you want to reinitialize the project, please delete the solace-agent-mesh.yaml file."
|
|
13
|
+
)
|