/ cli / commands / init_cmd / __init__.py
__init__.py
  1  from pathlib import Path
  2  
  3  import click
  4  
  5  from ...utils import ask_yes_no_question
  6  from .broker_step import broker_setup_step
  7  from .directory_step import create_project_directories
  8  from .env_step import ENV_DEFAULTS, create_env_file
  9  from .orchestrator_step import ORCHESTRATOR_DEFAULTS as O_DEFAULTS
 10  from .orchestrator_step import create_orchestrator_config
 11  from .platform_service_step import PLATFORM_SERVICE_DEFAULTS, create_platform_service_config
 12  from .project_files_step import create_project_files
 13  from .web_init_step import perform_web_init
 14  from .webui_gateway_step import WEBUI_GATEWAY_DEFAULTS, create_webui_gateway_config
 15  
 16  
 17  def _get_flat_orchestrator_defaults():
 18      """Flattens and maps ORCHESTRATOR_DEFAULTS for DEFAULT_INIT_VALUES."""
 19      flat_defaults = {}
 20      flat_defaults["agent_name"] = O_DEFAULTS["agent_name"]
 21      flat_defaults["supports_streaming"] = O_DEFAULTS["supports_streaming"]
 22      flat_defaults["session_service_type"] = "memory"
 23      flat_defaults["session_service_behavior"] = "PERSISTENT"
 24      flat_defaults["artifact_service_type"] = O_DEFAULTS["artifact_service"]["type"]
 25      flat_defaults["artifact_service_base_path"] = O_DEFAULTS["artifact_service"][
 26          "base_path"
 27      ]
 28      flat_defaults["artifact_service_scope"] = O_DEFAULTS["artifact_service"][
 29          "artifact_scope"
 30      ]
 31      flat_defaults["artifact_service_bucket_name"] = O_DEFAULTS["artifact_service"].get(
 32          "bucket_name", ""
 33      )
 34      flat_defaults["artifact_service_endpoint_url"] = O_DEFAULTS["artifact_service"].get(
 35          "endpoint_url", ""
 36      )
 37      flat_defaults["artifact_service_region"] = O_DEFAULTS["artifact_service"].get(
 38          "region", "us-east-1"
 39      )
 40      flat_defaults["artifact_handling_mode"] = O_DEFAULTS["artifact_handling_mode"]
 41      flat_defaults["enable_embed_resolution"] = O_DEFAULTS["enable_embed_resolution"]
 42      flat_defaults["enable_artifact_content_instruction"] = O_DEFAULTS[
 43          "enable_artifact_content_instruction"
 44      ]
 45      flat_defaults["agent_card_description"] = O_DEFAULTS["agent_card"]["description"]
 46      flat_defaults["agent_card_default_input_modes"] = ",".join(
 47          O_DEFAULTS["agent_card"]["defaultInputModes"]
 48      )
 49      flat_defaults["agent_card_default_output_modes"] = ",".join(
 50          O_DEFAULTS["agent_card"]["defaultOutputModes"]
 51      )
 52      flat_defaults["agent_discovery_enabled"] = O_DEFAULTS["agent_discovery"]["enabled"]
 53      flat_defaults["agent_card_publishing_interval"] = O_DEFAULTS[
 54          "agent_card_publishing"
 55      ]["interval_seconds"]
 56      flat_defaults["inter_agent_communication_allow_list"] = ",".join(
 57          O_DEFAULTS["inter_agent_communication"]["allow_list"]
 58      )
 59      flat_defaults["inter_agent_communication_deny_list"] = ",".join(
 60          O_DEFAULTS["inter_agent_communication"].get("deny_list", [])
 61      )
 62      flat_defaults["inter_agent_communication_timeout"] = O_DEFAULTS[
 63          "inter_agent_communication"
 64      ]["request_timeout_seconds"]
 65      return flat_defaults
 66  
 67  
 68  DEFAULT_INIT_VALUES = {
 69      "broker_type": "1",
 70      "broker_url": "ws://localhost:8008",
 71      "broker_vpn": "default",
 72      "broker_username": "default",
 73      "broker_password": "default",
 74      "container_engine": "docker",
 75      "SOLACE_LOCAL_BROKER_URL": "ws://localhost:8008",
 76      "SOLACE_LOCAL_BROKER_VPN": "default",
 77      "SOLACE_LOCAL_BROKER_USERNAME": "default",
 78      "SOLACE_LOCAL_BROKER_PASSWORD": "default",
 79      "DEV_BROKER_URL": "ws://localhost:8008",
 80      "DEV_BROKER_VPN": "default",
 81      "DEV_BROKER_USERNAME": "default",
 82      "DEV_BROKER_PASSWORD": "default",
 83      "llm_endpoint_url": "YOUR_LLM_ENDPOINT_URL_HERE",
 84      "llm_api_key": "YOUR_LLM_API_KEY_HERE",
 85      "llm_planning_model_name": "YOUR_LLM_PLANNING_MODEL_NAME_HERE",
 86      "llm_general_model_name": "YOUR_LLM_GENERAL_MODEL_NAME_HERE",
 87      "namespace": "solace_app/",
 88      "dev_mode": "false",
 89      **_get_flat_orchestrator_defaults(),
 90      "add_webui_gateway": True,
 91      "webui_session_secret_key": ENV_DEFAULTS.get("SESSION_SECRET_KEY"),
 92      "webui_fastapi_host": ENV_DEFAULTS.get("FASTAPI_HOST"),
 93      "webui_fastapi_port": int(ENV_DEFAULTS.get("FASTAPI_PORT", "8000")),
 94      "webui_fastapi_https_port": int(ENV_DEFAULTS.get("FASTAPI_HTTPS_PORT", "8443")),
 95      "webui_ssl_keyfile": ENV_DEFAULTS.get("SSL_KEYFILE", ""),
 96      "webui_ssl_certfile": ENV_DEFAULTS.get("SSL_CERTFILE", ""),
 97      "webui_ssl_keyfile_password": ENV_DEFAULTS.get("SSL_KEYFILE_PASSWORD", ""),
 98      "webui_enable_embed_resolution": ENV_DEFAULTS.get(
 99          "ENABLE_EMBED_RESOLUTION", "true"
100      ).lower()
101      == "true",
102      "webui_frontend_welcome_message": WEBUI_GATEWAY_DEFAULTS.get(
103          "frontend_welcome_message", "How can I assist you today?"
104      ),
105      "webui_frontend_bot_name": WEBUI_GATEWAY_DEFAULTS.get(
106          "frontend_bot_name", "Solace Agent Mesh"
107      ),
108      "webui_frontend_collect_feedback": WEBUI_GATEWAY_DEFAULTS.get(
109          "frontend_collect_feedback", False
110      ),
111      "platform_api_host": PLATFORM_SERVICE_DEFAULTS.get("platform_api_host"),
112      "platform_api_port": PLATFORM_SERVICE_DEFAULTS.get("platform_api_port"),
113  }
114  
115  
116  def run_init_flow(skip_interactive: bool, use_web_based_init_flag: bool, **cli_options):
117      """
118      Orchestrates the initialization of a new Solace application project
119      by running a sequence of steps.
120      """
121      click.echo(
122          click.style("Initializing Solace Application Project...", bold=True, fg="blue")
123      )
124      options = {k: v for k, v in cli_options.items() if v is not None}
125  
126      actual_use_web_init = use_web_based_init_flag
127      if not skip_interactive and not use_web_based_init_flag:
128          actual_use_web_init = ask_yes_no_question(
129              "Would you like to configure your project through a web interface in your browser?",
130              default=True,
131          )
132  
133      project_root = Path.cwd()
134      click.echo(f"Project will be initialized in: {project_root}")
135  
136      if actual_use_web_init:
137          if skip_interactive:
138              click.echo(
139                  click.style(
140                      "Web-based init (--gui) is not compatible with --skip. Proceeding with CLI-based init using provided options or defaults.",
141                      fg="yellow",
142                  )
143              )
144          else:
145              options = perform_web_init(options)
146              skip_interactive = True
147  
148      steps = [
149          ("Broker Setup", broker_setup_step),
150          (
151              "Project Directory Setup",
152              lambda opts, defs, skip: create_project_directories(project_root),
153          ),
154          (
155              "Project Files Creation",
156              lambda opts, defs, skip: create_project_files(project_root),
157          ),
158          (
159              "Main Orchestrator Configuration",
160              lambda opts, defs, skip: create_orchestrator_config(
161                  project_root, opts, skip
162              ),
163          ),
164          (
165              "Web UI Gateway Configuration",
166              lambda opts, defs, skip: create_webui_gateway_config(
167                  project_root, opts, skip, defs
168              ),
169          ),
170          (
171              "Platform Service Configuration",
172              lambda opts, defs, skip: create_platform_service_config(
173                  project_root, opts, skip, defs
174              ),
175          ),
176          (
177              ".env File Creation",
178              lambda opts, defs, skip: create_env_file(project_root, opts, skip),
179          ),
180      ]
181  
182      step_count = 0
183      total_display_steps = len([s_name for s_name, _ in steps if s_name])
184  
185      for step_name, step_function in steps:
186          if step_name:
187              step_count += 1
188              click.echo(
189                  click.style(
190                      f"\n--- Step {step_count} of {total_display_steps}: {step_name} ---",
191                      bold=True,
192                      fg="blue",
193                  )
194              )
195  
196          step_function(options, DEFAULT_INIT_VALUES, skip_interactive)
197  
198      click.echo(click.style("\nProject initialization complete!", fg="green", bold=True))
199      click.echo(
200          click.style(
201              "Review the generated files, especially .env and configuration YAMLs.",
202              fg="yellow",
203          )
204      )
205      click.echo(
206          click.style(
207              "Next steps: Consider running 'solace-agent-mesh run' or 'sam run'.",
208              fg="blue",
209          )
210      )
211  
212  
213  @click.command(name="init")
214  @click.option(
215      "--skip",
216      is_flag=True,
217      default=False,
218      help="Non-interactive mode. Skip all prompts and use default values where applicable.",
219  )
220  @click.option(
221      "--gui",
222      is_flag=True,
223      default=False,
224      help="Launch the browser-based initialization interface.",
225  )
226  @click.option("--llm-service-endpoint", type=str, help="LLM Service Endpoint URL.")
227  @click.option("--llm-service-api-key", type=str, help="LLM Service API Key.")
228  @click.option(
229      "--llm-service-planning-model-name", type=str, help="LLM Planning Model Name."
230  )
231  @click.option(
232      "--llm-service-general-model-name", type=str, help="LLM General Model Name."
233  )
234  @click.option("--namespace", type=str, help="Namespace for the project.")
235  @click.option(
236      "--broker-type",
237      type=click.Choice(
238          ["1", "2", "3", "solace", "container", "dev_mode", "dev_broker", "dev"],
239          case_sensitive=False,
240      ),
241      help="Broker type: 1/solace (existing), 2/container (new local), 3/dev (dev mode).",
242  )
243  @click.option("--broker-url", type=str, help="Solace broker URL endpoint.")
244  @click.option("--broker-vpn", type=str, help="Solace broker VPN name.")
245  @click.option("--broker-username", type=str, help="Solace broker username.")
246  @click.option("--broker-password", type=str, help="Solace broker password.")
247  @click.option(
248      "--container-engine",
249      type=click.Choice(["podman", "docker"], case_sensitive=False),
250      help="Container engine for local broker.",
251  )
252  @click.option(
253      "--dev-mode",
254      "dev_mode_flag",
255      is_flag=True,
256      help="Shortcut to select dev mode for broker (equivalent to --broker-type 3/dev).",
257  )
258  @click.option("--agent-name", type=str, help="Agent name for the main orchestrator.")
259  @click.option(
260      "--supports-streaming",
261      is_flag=True,
262      help="Enable streaming support for the agent.",
263      default=None,
264  )
265  @click.option(
266      "--session-service-type",
267      type=click.Choice(["memory", "vertex_rag", "sql"]),
268      help="Session service type.",
269  )
270  @click.option(
271      "--session-service-behavior",
272      type=click.Choice(["PERSISTENT", "RUN_BASED"]),
273      help="Session service behavior.",
274  )
275  @click.option(
276      "--artifact-service-type",
277      type=click.Choice(["memory", "filesystem", "gcs", "s3"]),
278      help="Artifact service type.",
279  )
280  @click.option(
281      "--artifact-service-base-path",
282      type=str,
283      help="Artifact service base path (for filesystem type).",
284  )
285  @click.option(
286      "--artifact-service-bucket-name",
287      type=str,
288      help="S3 bucket name (for s3 artifact service type).",
289  )
290  @click.option(
291      "--artifact-service-endpoint-url",
292      type=str,
293      help="S3 endpoint URL (for s3 artifact service type, optional for AWS S3).",
294  )
295  @click.option(
296      "--artifact-service-region",
297      type=str,
298      help="S3 region (for s3 artifact service type).",
299  )
300  @click.option(
301      "--artifact-service-scope",
302      type=click.Choice(["namespace", "app", "custom"]),
303      help="Artifact service scope.",
304  )
305  @click.option(
306      "--artifact-handling-mode",
307      type=click.Choice(["ignore", "embed", "reference"]),
308      help="Artifact handling mode.",
309  )
310  @click.option(
311      "--enable-embed-resolution",
312      is_flag=True,
313      help="Enable embed resolution.",
314      default=None,
315  )
316  @click.option(
317      "--enable-artifact-content-instruction",
318      is_flag=True,
319      help="Enable artifact content instruction.",
320      default=None,
321  )
322  @click.option(
323      "--enable-builtin-artifact-tools",
324      is_flag=True,
325      help="Enable built-in artifact tools.",
326      default=None,
327  )
328  @click.option(
329      "--enable-builtin-data-tools",
330      is_flag=True,
331      help="Enable built-in data tools.",
332      default=None,
333  )
334  @click.option("--agent-card-description", type=str, help="Agent card description.")
335  @click.option(
336      "--agent-card-default-input-modes",
337      type=str,
338      help="Agent card default input modes (comma-separated).",
339  )
340  @click.option(
341      "--agent-card-default-output-modes",
342      type=str,
343      help="Agent card default output modes (comma-separated).",
344  )
345  @click.option(
346      "--agent-discovery-enabled",
347      is_flag=True,
348      help="Enable agent discovery.",
349      default=None,
350  )
351  @click.option(
352      "--agent-card-publishing-interval",
353      type=int,
354      help="Agent card publishing interval (seconds).",
355  )
356  @click.option(
357      "--inter-agent-communication-allow-list",
358      type=str,
359      help="Inter-agent communication allow list (comma-separated, use * for all).",
360  )
361  @click.option(
362      "--inter-agent-communication-deny-list",
363      type=str,
364      help="Inter-agent communication deny list (comma-separated).",
365  )
366  @click.option(
367      "--inter-agent-communication-timeout",
368      type=int,
369      help="Inter-agent communication timeout (seconds).",
370  )
371  @click.option(
372      "--add-webui-gateway",
373      is_flag=True,
374      default=None,
375      help="Add a default Web UI gateway configuration.",
376  )
377  @click.option(
378      "--webui-session-secret-key", type=str, help="Session secret key for Web UI."
379  )
380  @click.option("--webui-fastapi-host", type=str, help="Host for Web UI FastAPI server.")
381  @click.option("--webui-fastapi-port", type=int, help="Port for Web UI FastAPI server.")
382  @click.option(
383      "--webui-fastapi-https-port", type=int, help="HTTPS port for Web UI FastAPI server."
384  )
385  @click.option("--webui-ssl-keyfile", type=str, help="SSL key file path for Web UI.")
386  @click.option(
387      "--webui-ssl-certfile", type=str, help="SSL certificate file path for Web UI."
388  )
389  @click.option(
390      "--webui-ssl-keyfile-password", type=str, help="SSL key file passphrase for Web UI."
391  )
392  @click.option(
393      "--webui-enable-embed-resolution",
394      is_flag=True,
395      default=None,
396      help="Enable embed resolution for Web UI.",
397  )
398  @click.option(
399      "--webui-frontend-welcome-message",
400      type=str,
401      help="Frontend welcome message for Web UI.",
402  )
403  @click.option(
404      "--webui-frontend-bot-name", type=str, help="Frontend bot name for Web UI."
405  )
406  @click.option(
407      "--webui-frontend-collect-feedback",
408      is_flag=True,
409      default=None,
410      help="Enable feedback collection in Web UI.",
411  )
412  @click.option(
413      "--web-ui-gateway-database-url",
414      type=str,
415      help="Database URL for the WebUI Gateway.",
416  )
417  @click.option(
418      "--orchestrator-database-url",
419      type=str,
420      help="Database URL for the Orchestrator.",
421  )
422  @click.option(
423      "--platform-api-host", type=str, help="Host for Platform API server."
424  )
425  @click.option(
426      "--platform-api-port", type=int, help="Port for Platform API server."
427  )
428  def init(**kwargs):
429      """
430      Initialize a new Solace application project.
431      Creates a directory structure, default configuration files, and a .env file.
432      """
433      use_web_based_init_val = kwargs.get("gui", False)
434  
435      if kwargs.get("dev_mode_flag"):
436          if kwargs.get("broker_type") is None:
437              kwargs["broker_type"] = "dev"
438          elif kwargs.get("broker_type") not in ["3", "dev", "dev_mode", "dev_broker"]:
439              click.echo(
440                  click.style(
441                      f"Warning: --dev-mode flag is set, but --broker-type is also set to '{kwargs.get('broker_type')}'. Dev mode will be used for broker configuration.",
442                      fg="yellow",
443                  )
444              )
445              kwargs["broker_type"] = "dev"
446  
447      skip_interactive_val = kwargs.pop("skip", False)
448      use_web_based_init_val = kwargs.pop("gui", False)
449  
450      run_init_flow(
451          skip_interactive=skip_interactive_val,
452          use_web_based_init_flag=use_web_based_init_val,
453          **kwargs,
454      )