__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 )