/ cli / commands / init_cmd / webui_gateway_step.py
webui_gateway_step.py
  1  import re
  2  
  3  import click
  4  from pathlib import Path
  5  
  6  from ...utils import ask_if_not_provided, ask_yes_no_question, load_template
  7  
  8  
  9  WEBUI_GATEWAY_DEFAULTS = {
 10      "webui_frontend_welcome_message": "",
 11      "webui_frontend_bot_name": "Solace Agent Mesh",
 12      "webui_frontend_logo_url": "",
 13      "webui_frontend_collect_feedback": False,
 14      "webui_session_secret_key": "please_change_me_in",
 15      "webui_fastapi_host": "127.0.0.1",
 16      "webui_fastapi_port": 8000,
 17      "webui_fastapi_https_port": 8443,
 18      "webui_ssl_keyfile": "",
 19      "webui_ssl_certfile": "",
 20      "webui_ssl_keyfile_password": "",
 21      "webui_enable_embed_resolution": True,
 22  }
 23  
 24  
 25  def create_webui_gateway_config(
 26      project_root: Path, options: dict, skip_interactive: bool, default_values: dict
 27  ) -> bool:
 28      """
 29      Gathers WebUI Gateway options and creates the configuration file (configs/gateways/webui.yaml)
 30      if the user opts in. It customizes the template based on user input or defaults.
 31      Returns True on success or if skipped, False on failure.
 32      """
 33      click.echo("Configuring Web UI Gateway options...")
 34  
 35      add_gateway = options.get("add_webui_gateway")
 36      if not skip_interactive and add_gateway is None:
 37          add_gateway = default_values.get("add_webui_gateway", True)
 38      elif add_gateway is None:
 39          add_gateway = default_values.get("add_webui_gateway", True)
 40  
 41      options["add_webui_gateway"] = add_gateway
 42  
 43      if not add_gateway:
 44          click.echo(click.style("  Skipping Web UI Gateway file creation.", fg="yellow"))
 45          return True
 46  
 47      options["webui_session_secret_key"] = ask_if_not_provided(
 48          options,
 49          "webui_session_secret_key",
 50          "Enter Web UI Session Secret Key",
 51          default=default_values.get(
 52              "webui_session_secret_key",
 53              WEBUI_GATEWAY_DEFAULTS["webui_session_secret_key"],
 54          ),
 55          none_interactive=skip_interactive,
 56          hide_input=True,
 57      )
 58      options["webui_fastapi_host"] = ask_if_not_provided(
 59          options,
 60          "webui_fastapi_host",
 61          "Enter Web UI FastAPI Host",
 62          default=default_values.get(
 63              "webui_fastapi_host", WEBUI_GATEWAY_DEFAULTS["webui_fastapi_host"]
 64          ),
 65          none_interactive=skip_interactive,
 66      )
 67      options["webui_fastapi_port"] = ask_if_not_provided(
 68          options,
 69          "webui_fastapi_port",
 70          "Enter Web UI FastAPI Port",
 71          default=default_values.get(
 72              "webui_fastapi_port", WEBUI_GATEWAY_DEFAULTS["webui_fastapi_port"]
 73          ),
 74          none_interactive=skip_interactive,
 75      )
 76      options["webui_fastapi_https_port"] = ask_if_not_provided(
 77          options,
 78          "webui_fastapi_https_port",
 79          "Enter Web UI FastAPI HTTPS Port",
 80          default=default_values.get("webui_fastapi_https_port", 8443),
 81          none_interactive=skip_interactive,
 82      )
 83      options["webui_enable_embed_resolution"] = ask_if_not_provided(
 84          options,
 85          "webui_enable_embed_resolution",
 86          "Enable Embed Resolution for Web UI? (true/false)",
 87          default=default_values.get(
 88              "webui_enable_embed_resolution",
 89              WEBUI_GATEWAY_DEFAULTS["webui_enable_embed_resolution"],
 90          ),
 91          none_interactive=skip_interactive,
 92          is_bool=True,
 93      )
 94      options["webui_ssl_keyfile"] = ask_if_not_provided(
 95          options,
 96          "webui_ssl_keyfile",
 97          "Enter SSL Key File Path",
 98          default=default_values.get("webui_ssl_keyfile", ""),
 99          none_interactive=skip_interactive,
100      )
101      options["webui_ssl_certfile"] = ask_if_not_provided(
102          options,
103          "webui_ssl_certfile",
104          "Enter SSL Certificate File Path",
105          default=default_values.get("webui_ssl_certfile", ""),
106          none_interactive=skip_interactive,
107      )
108      options["webui_ssl_keyfile_password"] = ask_if_not_provided(
109          options,
110          "webui_ssl_keyfile_password",
111          "Enter SSL Key File Passphrase",
112          default=default_values.get("webui_ssl_keyfile_password", ""),
113          none_interactive=skip_interactive,
114          hide_input=True,
115      )
116  
117      options["webui_frontend_welcome_message"] = ask_if_not_provided(
118          options,
119          "webui_frontend_welcome_message",
120          "Enter Frontend Welcome Message for Web UI",
121          default=default_values.get(
122              "webui_frontend_welcome_message",
123              WEBUI_GATEWAY_DEFAULTS["webui_frontend_welcome_message"],
124          ),
125          none_interactive=skip_interactive,
126      )
127      options["webui_frontend_bot_name"] = ask_if_not_provided(
128          options,
129          "webui_frontend_bot_name",
130          "Enter Frontend Bot Name for Web UI",
131          default=default_values.get(
132              "webui_frontend_bot_name", WEBUI_GATEWAY_DEFAULTS["webui_frontend_bot_name"]
133          ),
134          none_interactive=skip_interactive,
135      )
136      options["webui_frontend_logo_url"] = ask_if_not_provided(
137          options,
138          "webui_frontend_logo_url",
139          "Enter Frontend Logo URL (PNG, SVG, JPG or data URI)",
140          default=default_values.get(
141              "webui_frontend_logo_url", WEBUI_GATEWAY_DEFAULTS["webui_frontend_logo_url"]
142          ),
143          none_interactive=skip_interactive,
144      )
145      options["webui_frontend_collect_feedback"] = ask_if_not_provided(
146          options,
147          "webui_frontend_collect_feedback",
148          "Enable Frontend Feedback Collection for Web UI? (true/false)",
149          default=default_values.get(
150              "webui_frontend_collect_feedback",
151              WEBUI_GATEWAY_DEFAULTS["webui_frontend_collect_feedback"],
152          ),
153          none_interactive=skip_interactive,
154          is_bool=True,
155      )
156  
157      click.echo("Creating Web UI Gateway configuration file...")
158      destination_path = project_root / "configs" / "gateways" / "webui.yaml"
159  
160      try:
161          template_content = load_template("webui.yaml")
162          
163          session_service_lines = [
164              f'type: "sql"',
165              'database_url: "${WEB_UI_GATEWAY_DATABASE_URL, sqlite:///webui_gateway.db}"',
166              f'default_behavior: "PERSISTENT"',
167          ]
168          session_service_block = "\n" + "\n".join(
169              [f"        {line}" for line in session_service_lines]
170          )
171          
172          replacements = {
173              "__FRONTEND_WELCOME_MESSAGE__": str(
174                  options.get("webui_frontend_welcome_message", '${FRONTEND_WELCOME_MESSAGE, "Hello, how can I assist you?"}')
175              ),
176              "__FRONTEND_BOT_NAME__": str(
177                  options.get("webui_frontend_bot_name", "${FRONTEND_BOT_NAME, Solace Agent Mesh}")
178              ),
179              "__FRONTEND_LOGO_URL__": str(
180                  options.get("webui_frontend_logo_url", "${WEBUI_FRONTEND_LOGO_URL}")
181              ),
182              "__FRONTEND_COLLECT_FEEDBACK__": str(
183                  options.get("webui_frontend_collect_feedback", "${FRONTEND_COLLECT_FEEDBACK, false}")
184              ).lower(),
185              "__SESSION_SERVICE__": session_service_block,
186          }
187  
188          modified_content = template_content
189          for placeholder, value in replacements.items():
190              if value is not None:
191                  modified_content = modified_content.replace(placeholder, str(value))
192  
193          # If no LLM provider, strip the model anchor line (keep model_provider)
194          llm_provider = options.get("llm_provider", "")
195          if not llm_provider:
196              modified_content = re.sub(
197                  r"^\s*model: \*general_model\n",
198                  "",
199                  modified_content,
200                  flags=re.MULTILINE,
201              )
202  
203          destination_path.parent.mkdir(parents=True, exist_ok=True)
204          with open(destination_path, "w", encoding="utf-8") as f:
205              f.write(modified_content)
206  
207          click.echo(f"  Created: {destination_path.relative_to(project_root)}")
208          return True
209  
210      except FileNotFoundError:
211          click.echo(click.style("Error: Template file not found.", fg="red"), err=True)
212          return False
213      except IOError as e:
214          click.echo(
215              click.style(f"Error writing file {destination_path}: {e}", fg="red"),
216              err=True,
217          )
218          return False
219      except Exception as e:
220          click.echo(
221              click.style(
222                  f"An unexpected error occurred during Web UI Gateway configuration: {e}",
223                  fg="red",
224              ),
225              err=True,
226          )
227          return False