solace-agent-mesh 1.1.0__py3-none-any.whl → 1.3.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.

Files changed (139) hide show
  1. solace_agent_mesh/agent/adk/runner.py +18 -12
  2. solace_agent_mesh/agent/adk/services.py +3 -3
  3. solace_agent_mesh/agent/protocol/event_handlers.py +27 -21
  4. solace_agent_mesh/agent/sac/app.py +1 -1
  5. solace_agent_mesh/agent/sac/component.py +0 -1
  6. solace_agent_mesh/assets/docs/404.html +2 -2
  7. solace_agent_mesh/assets/docs/assets/js/{main.a75ecc0d.js → main.08d30374.js} +2 -2
  8. solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +2 -2
  9. solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +2 -2
  10. solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +2 -2
  11. solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +2 -2
  12. solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +2 -2
  13. solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +2 -2
  14. solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +2 -2
  15. solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +2 -2
  16. solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +2 -2
  17. solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +2 -2
  18. solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +2 -2
  19. solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +2 -2
  20. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +2 -2
  21. solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +2 -2
  22. solace_agent_mesh/assets/docs/docs/documentation/migration-guides/a2a-upgrade-to-0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html +2 -2
  23. solace_agent_mesh/assets/docs/docs/documentation/migration-guides/a2a-upgrade-to-0.3.0/a2a-technical-migration-map/index.html +2 -2
  24. solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +2 -2
  25. solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +2 -2
  26. solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +2 -2
  27. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +2 -2
  28. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +2 -2
  29. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +2 -2
  30. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +2 -2
  31. solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +2 -2
  32. solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +2 -2
  33. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +2 -2
  34. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +2 -2
  35. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +2 -2
  36. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +2 -2
  37. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +2 -2
  38. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +2 -2
  39. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +2 -2
  40. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +2 -2
  41. solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +2 -2
  42. solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +2 -2
  43. solace_agent_mesh/assets/docs/lunr-index-1757433031159.json +1 -0
  44. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  45. solace_agent_mesh/assets/docs/search-doc-1757433031159.json +1 -0
  46. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  47. solace_agent_mesh/cli/__init__.py +1 -1
  48. solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +125 -48
  49. solace_agent_mesh/cli/commands/eval_cmd.py +14 -0
  50. solace_agent_mesh/cli/commands/init_cmd/__init__.py +53 -31
  51. solace_agent_mesh/cli/commands/init_cmd/database_step.py +91 -0
  52. solace_agent_mesh/cli/commands/init_cmd/env_step.py +19 -8
  53. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +80 -25
  54. solace_agent_mesh/cli/commands/init_cmd/web_init_step.py +32 -10
  55. solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +74 -15
  56. solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +0 -2
  57. solace_agent_mesh/cli/commands/run_cmd.py +5 -3
  58. solace_agent_mesh/cli/utils.py +68 -12
  59. solace_agent_mesh/client/webui/frontend/static/assets/authCallback-vY5eu2lI.js +1 -0
  60. solace_agent_mesh/client/webui/frontend/static/assets/client-BeBkzgWW.js +25 -0
  61. solace_agent_mesh/client/webui/frontend/static/assets/main-Bjys1KQs.js +339 -0
  62. solace_agent_mesh/client/webui/frontend/static/assets/main-C03yrETa.css +1 -0
  63. solace_agent_mesh/client/webui/frontend/static/assets/vendor-CE0AeXyK.js +395 -0
  64. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -2
  65. solace_agent_mesh/client/webui/frontend/static/index.html +4 -3
  66. solace_agent_mesh/common/utils/embeds/resolver.py +1 -0
  67. solace_agent_mesh/config_portal/backend/common.py +2 -2
  68. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-bFMKlzKf.js +98 -0
  69. solace_agent_mesh/config_portal/frontend/static/client/assets/{manifest-d845808d.js → manifest-89db7c30.js} +1 -1
  70. solace_agent_mesh/config_portal/frontend/static/client/index.html +1 -1
  71. solace_agent_mesh/evaluation/message_organizer.py +35 -56
  72. solace_agent_mesh/evaluation/run.py +26 -5
  73. solace_agent_mesh/evaluation/subscriber.py +35 -10
  74. solace_agent_mesh/evaluation/summary_builder.py +27 -34
  75. solace_agent_mesh/gateway/http_sse/ARCHITECTURE_GUIDE.md +676 -0
  76. solace_agent_mesh/gateway/http_sse/alembic/env.py +85 -0
  77. solace_agent_mesh/gateway/http_sse/alembic/script.py.mako +28 -0
  78. solace_agent_mesh/gateway/http_sse/alembic/versions/b1c2d3e4f5g6_add_database_indexes.py +83 -0
  79. solace_agent_mesh/gateway/http_sse/alembic/versions/d5b3f8f2e9a0_create_initial_database.py +58 -0
  80. solace_agent_mesh/gateway/http_sse/alembic.ini +147 -0
  81. solace_agent_mesh/gateway/http_sse/api/__init__.py +11 -0
  82. solace_agent_mesh/gateway/http_sse/api/controllers/__init__.py +9 -0
  83. solace_agent_mesh/gateway/http_sse/api/controllers/session_controller.py +355 -0
  84. solace_agent_mesh/gateway/http_sse/api/controllers/task_controller.py +279 -0
  85. solace_agent_mesh/gateway/http_sse/api/controllers/user_controller.py +35 -0
  86. solace_agent_mesh/gateway/http_sse/api/dto/__init__.py +10 -0
  87. solace_agent_mesh/gateway/http_sse/api/dto/requests/__init__.py +37 -0
  88. solace_agent_mesh/gateway/http_sse/api/dto/requests/session_requests.py +49 -0
  89. solace_agent_mesh/gateway/http_sse/api/dto/requests/task_requests.py +66 -0
  90. solace_agent_mesh/gateway/http_sse/api/dto/responses/__init__.py +43 -0
  91. solace_agent_mesh/gateway/http_sse/api/dto/responses/session_responses.py +68 -0
  92. solace_agent_mesh/gateway/http_sse/api/dto/responses/task_responses.py +74 -0
  93. solace_agent_mesh/gateway/http_sse/app.py +31 -1
  94. solace_agent_mesh/gateway/http_sse/application/__init__.py +3 -0
  95. solace_agent_mesh/gateway/http_sse/application/services/__init__.py +3 -0
  96. solace_agent_mesh/gateway/http_sse/application/services/session_service.py +135 -0
  97. solace_agent_mesh/gateway/http_sse/component.py +224 -62
  98. solace_agent_mesh/gateway/http_sse/dependencies.py +142 -39
  99. solace_agent_mesh/gateway/http_sse/domain/entities/__init__.py +3 -0
  100. solace_agent_mesh/gateway/http_sse/domain/entities/session.py +90 -0
  101. solace_agent_mesh/gateway/http_sse/domain/repositories/__init__.py +3 -0
  102. solace_agent_mesh/gateway/http_sse/domain/repositories/session_repository.py +54 -0
  103. solace_agent_mesh/gateway/http_sse/infrastructure/__init__.py +4 -0
  104. solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/__init__.py +3 -0
  105. solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/container.py +123 -0
  106. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/__init__.py +4 -0
  107. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_persistence_service.py +16 -0
  108. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_service.py +119 -0
  109. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/models.py +31 -0
  110. solace_agent_mesh/gateway/http_sse/infrastructure/persistence_service.py +12 -0
  111. solace_agent_mesh/gateway/http_sse/infrastructure/repositories/__init__.py +3 -0
  112. solace_agent_mesh/gateway/http_sse/infrastructure/repositories/session_repository.py +174 -0
  113. solace_agent_mesh/gateway/http_sse/main.py +289 -85
  114. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +121 -54
  115. solace_agent_mesh/gateway/http_sse/routers/config.py +3 -1
  116. solace_agent_mesh/gateway/http_sse/routers/tasks.py +83 -2
  117. solace_agent_mesh/gateway/http_sse/routers/visualization.py +7 -7
  118. solace_agent_mesh/gateway/http_sse/session_manager.py +64 -30
  119. solace_agent_mesh/gateway/http_sse/shared/__init__.py +9 -0
  120. solace_agent_mesh/gateway/http_sse/shared/auth_utils.py +29 -0
  121. solace_agent_mesh/gateway/http_sse/shared/enums.py +45 -0
  122. solace_agent_mesh/gateway/http_sse/shared/types.py +45 -0
  123. solace_agent_mesh/templates/shared_config.yaml +4 -5
  124. solace_agent_mesh/templates/webui.yaml +8 -10
  125. {solace_agent_mesh-1.1.0.dist-info → solace_agent_mesh-1.3.0.dist-info}/METADATA +5 -3
  126. {solace_agent_mesh-1.1.0.dist-info → solace_agent_mesh-1.3.0.dist-info}/RECORD +130 -91
  127. solace_agent_mesh/assets/docs/lunr-index-1756992446316.json +0 -1
  128. solace_agent_mesh/assets/docs/search-doc-1756992446316.json +0 -1
  129. solace_agent_mesh/client/webui/frontend/static/assets/authCallback-BmF2l6vg.js +0 -1
  130. solace_agent_mesh/client/webui/frontend/static/assets/client-D881Dttc.js +0 -49
  131. solace_agent_mesh/client/webui/frontend/static/assets/main-C0jZjYa8.js +0 -699
  132. solace_agent_mesh/client/webui/frontend/static/assets/main-CCeG324-.css +0 -1
  133. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-Bym6YkMd.js +0 -98
  134. solace_agent_mesh/gateway/http_sse/routers/sessions.py +0 -85
  135. solace_agent_mesh/gateway/http_sse/routers/users.py +0 -59
  136. /solace_agent_mesh/assets/docs/assets/js/{main.a75ecc0d.js.LICENSE.txt → main.08d30374.js.LICENSE.txt} +0 -0
  137. {solace_agent_mesh-1.1.0.dist-info → solace_agent_mesh-1.3.0.dist-info}/WHEEL +0 -0
  138. {solace_agent_mesh-1.1.0.dist-info → solace_agent_mesh-1.3.0.dist-info}/entry_points.txt +0 -0
  139. {solace_agent_mesh-1.1.0.dist-info → solace_agent_mesh-1.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -1 +1 @@
1
- __version__ = "1.1.0"
1
+ __version__ = "1.3.0"
@@ -1,23 +1,37 @@
1
1
  import json
2
+ import sys
2
3
  from pathlib import Path
3
4
 
4
5
  import click
5
6
  import yaml
6
7
 
7
- from config_portal.backend.common import (
8
- AGENT_DEFAULTS,
9
- USE_DEFAULT_SHARED_ARTIFACT,
10
- USE_DEFAULT_SHARED_SESSION,
11
- )
8
+ from config_portal.backend.common import AGENT_DEFAULTS, USE_DEFAULT_SHARED_ARTIFACT
12
9
 
13
10
  from ...utils import (
14
11
  ask_if_not_provided,
12
+ ask_yes_no_question,
13
+ create_and_validate_database,
15
14
  get_formatted_names,
16
15
  indent_multiline_string,
17
16
  load_template,
18
17
  )
19
18
  from .web_add_agent_step import launch_add_agent_web_portal
20
19
 
20
+ DATABASE_URL_KEY = "database_url"
21
+
22
+
23
+ def _append_to_env_file(project_root: Path, key: str, value: str):
24
+ env_path = project_root / ".env"
25
+ try:
26
+ with open(env_path, "a", encoding="utf-8") as f:
27
+ f.write(f'\n{key}="{value}"\n')
28
+ return True
29
+ except OSError as e:
30
+ click.echo(
31
+ click.style(f"Error appending to .env file: {e}", fg="red"), err=True
32
+ )
33
+ return False
34
+
21
35
 
22
36
  def _write_agent_yaml_from_data(
23
37
  agent_name_input: str, config_options: dict, project_root: Path
@@ -39,17 +53,27 @@ def _write_agent_yaml_from_data(
39
53
  try:
40
54
  modified_content = load_template("agent_template.yaml")
41
55
  session_service_type_opt = config_options.get("session_service_type")
42
- if (
43
- session_service_type_opt
44
- and session_service_type_opt != USE_DEFAULT_SHARED_SESSION
45
- ):
56
+ if session_service_type_opt and session_service_type_opt != "memory":
46
57
  type_val = session_service_type_opt
47
58
  behavior_val = config_options.get(
48
59
  "session_service_behavior", AGENT_DEFAULTS["session_service_behavior"]
49
60
  )
50
- session_service_block = (
51
- f'\n type: "{type_val}"\n'
52
- f' default_behavior: "{behavior_val}"'
61
+
62
+ session_service_lines = [
63
+ f'type: "{type_val}"',
64
+ f'default_behavior: "{behavior_val}"',
65
+ ]
66
+
67
+ if type_val == "sql":
68
+ database_url_placeholder = (
69
+ f"${{{formatted_names['SNAKE_UPPER_CASE_NAME']}_DATABASE_URL}}"
70
+ )
71
+ session_service_lines.append(
72
+ f'database_url: "{database_url_placeholder}"'
73
+ )
74
+
75
+ session_service_block = "\n" + "\n".join(
76
+ [f" {line}" for line in session_service_lines]
53
77
  )
54
78
  else:
55
79
  session_service_block = "*default_session_service"
@@ -70,25 +94,9 @@ def _write_agent_yaml_from_data(
70
94
  )
71
95
  custom_artifact_lines.append(f'base_path: "{base_path_val}"')
72
96
  elif type_val == "s3":
73
- bucket_name_val = config_options.get(
74
- "artifact_service_bucket_name",
75
- AGENT_DEFAULTS.get("artifact_service_bucket_name", ""),
76
- )
77
- custom_artifact_lines.append(f'bucket_name: "{bucket_name_val}"')
78
-
79
- endpoint_url_val = config_options.get(
80
- "artifact_service_endpoint_url",
81
- AGENT_DEFAULTS.get("artifact_service_endpoint_url", ""),
82
- )
83
- if endpoint_url_val:
84
- custom_artifact_lines.append(f'endpoint_url: "{endpoint_url_val}"')
85
-
86
- region_val = config_options.get(
87
- "artifact_service_region",
88
- AGENT_DEFAULTS.get("artifact_service_region", "us-east-1"),
89
- )
90
- if region_val:
91
- custom_artifact_lines.append(f'region: "{region_val}"')
97
+ custom_artifact_lines.append("bucket_name: ${S3_BUCKET_NAME}")
98
+ custom_artifact_lines.append("endpoint_url: ${S3_ENDPOINT_URL:-}")
99
+ custom_artifact_lines.append("region: ${S3_REGION}")
92
100
  custom_artifact_lines.append(f"artifact_scope: {scope_val}")
93
101
  artifact_service_block = "\n" + "\n".join(
94
102
  [f" {line}" for line in custom_artifact_lines]
@@ -187,7 +195,9 @@ def _write_agent_yaml_from_data(
187
195
 
188
196
  replacements = {
189
197
  "__AGENT_NAME__": agent_name_camel,
190
- "__AGENT_SPACED_NAME__": get_formatted_names(agent_name_camel).get("SPACED_CAPITALIZED_NAME"),
198
+ "__AGENT_SPACED_NAME__": get_formatted_names(agent_name_camel).get(
199
+ "SPACED_CAPITALIZED_NAME"
200
+ ),
191
201
  "__NAMESPACE__": config_options.get(
192
202
  "namespace", AGENT_DEFAULTS["namespace"]
193
203
  ),
@@ -283,6 +293,12 @@ def _write_agent_yaml_from_data(
283
293
 
284
294
  for placeholder, value in replacements.items():
285
295
  modified_content = modified_content.replace(placeholder, str(value))
296
+ if config_options.get(DATABASE_URL_KEY):
297
+ env_key = f"{formatted_names['SNAKE_UPPER_CASE_NAME']}_DATABASE_URL"
298
+ if not _append_to_env_file(
299
+ project_root, env_key, config_options[DATABASE_URL_KEY]
300
+ ):
301
+ return False, "Failed to write to .env file.", ""
286
302
 
287
303
  with open(agent_config_file_path, "w", encoding="utf-8") as f:
288
304
  f.write(modified_content)
@@ -370,17 +386,68 @@ def create_agent_config(
370
386
  "Session service type",
371
387
  AGENT_DEFAULTS["session_service_type"],
372
388
  skip_interactive,
373
- choices=[USE_DEFAULT_SHARED_SESSION, "memory", "vertex_rag"],
389
+ choices=["sql", "memory", "vertex_rag"],
390
+ )
391
+
392
+ if collected_options.get("session_service_type") == "sql":
393
+ if DATABASE_URL_KEY not in collected_options:
394
+ use_own_db = False
395
+ if not skip_interactive:
396
+ use_own_db = ask_yes_no_question(
397
+ f"Do you want to use your own database for the '{agent_name_camel_case}' agent?\n"
398
+ f" (If no, SQLite embedded database will be used)",
399
+ default=False,
400
+ )
401
+
402
+ if use_own_db:
403
+ database_url = ask_if_not_provided(
404
+ collected_options,
405
+ DATABASE_URL_KEY,
406
+ f"Enter the full database URL for the {agent_name_camel_case} agent (e.g., postgresql://user:pass@host/db)",
407
+ none_interactive=skip_interactive,
408
+ )
409
+ collected_options[DATABASE_URL_KEY] = database_url
410
+ else:
411
+ data_dir = project_root / "data"
412
+ data_dir.mkdir(exist_ok=True)
413
+ db_file = data_dir / f"{formatted_names['SNAKE_CASE_NAME']}.db"
414
+ database_url = f"sqlite:///{db_file.resolve()}"
415
+ click.echo(
416
+ f" Using default SQLite database for {agent_name_camel_case} agent: {db_file}"
417
+ )
418
+ collected_options[DATABASE_URL_KEY] = database_url
419
+
420
+ try:
421
+ db_url = collected_options.get(DATABASE_URL_KEY)
422
+ if not db_url:
423
+ error_msg = "Database URL was not provided or determined despite SQL session service being selected."
424
+ click.echo(
425
+ click.style(f"Internal Error: {error_msg}", fg="red"), err=True
426
+ )
427
+ raise ValueError(error_msg)
428
+
429
+ click.echo(f" Validating database: {db_url}")
430
+ create_and_validate_database(
431
+ db_url, f"{agent_name_camel_case} agent database"
432
+ )
433
+ except Exception as e:
434
+ click.echo(
435
+ click.style(
436
+ f"Error validating database URL '{collected_options.get(DATABASE_URL_KEY)}': {e}",
437
+ fg="red",
438
+ ),
439
+ err=True,
440
+ )
441
+ return False
442
+
443
+ collected_options["session_service_behavior"] = ask_if_not_provided(
444
+ collected_options,
445
+ "session_service_behavior",
446
+ "Session service behavior",
447
+ AGENT_DEFAULTS["session_service_behavior"],
448
+ skip_interactive,
449
+ choices=["PERSISTENT", "RUN_BASED"],
374
450
  )
375
- if collected_options["session_service_type"] != USE_DEFAULT_SHARED_SESSION:
376
- collected_options["session_service_behavior"] = ask_if_not_provided(
377
- collected_options,
378
- "session_service_behavior",
379
- "Session service behavior",
380
- AGENT_DEFAULTS["session_service_behavior"],
381
- skip_interactive,
382
- choices=["PERSISTENT", "RUN_BASED"],
383
- )
384
451
 
385
452
  collected_options["artifact_service_type"] = ask_if_not_provided(
386
453
  collected_options,
@@ -469,7 +536,9 @@ def create_agent_config(
469
536
  skip_interactive,
470
537
  )
471
538
  collected_options["agent_card_default_input_modes"] = [
472
- mode.strip() for mode in default_input_modes_str.split(",") if mode.strip()
539
+ mode.strip()
540
+ for mode in (default_input_modes_str or "").split(",")
541
+ if mode.strip()
473
542
  ]
474
543
 
475
544
  default_output_modes_str = ask_if_not_provided(
@@ -480,7 +549,9 @@ def create_agent_config(
480
549
  skip_interactive,
481
550
  )
482
551
  collected_options["agent_card_default_output_modes"] = [
483
- mode.strip() for mode in default_output_modes_str.split(",") if mode.strip()
552
+ mode.strip()
553
+ for mode in (default_output_modes_str or "").split(",")
554
+ if mode.strip()
484
555
  ]
485
556
 
486
557
  collected_options["agent_card_skills_str"] = ask_if_not_provided(
@@ -514,8 +585,9 @@ def create_agent_config(
514
585
  ",".join(AGENT_DEFAULTS["inter_agent_communication_allow_list"]),
515
586
  skip_interactive,
516
587
  )
588
+ allow_list_items = (allow_list_str or "").split(",")
517
589
  collected_options["inter_agent_communication_allow_list"] = [
518
- item.strip() for item in allow_list_str.split(",") if item.strip()
590
+ item.strip() for item in allow_list_items if item.strip()
519
591
  ]
520
592
 
521
593
  deny_list_str = ask_if_not_provided(
@@ -525,8 +597,9 @@ def create_agent_config(
525
597
  ",".join(AGENT_DEFAULTS["inter_agent_communication_deny_list"]),
526
598
  skip_interactive,
527
599
  )
600
+ deny_list_items = (deny_list_str or "").split(",")
528
601
  collected_options["inter_agent_communication_deny_list"] = [
529
- item.strip() for item in deny_list_str.split(",") if item.strip()
602
+ item.strip() for item in deny_list_items if item.strip()
530
603
  ]
531
604
 
532
605
  collected_options["inter_agent_communication_timeout"] = ask_if_not_provided(
@@ -546,7 +619,7 @@ def create_agent_config(
546
619
  skip_interactive,
547
620
  )
548
621
  try:
549
- tools_list = json.loads(tools_json_str)
622
+ tools_list = json.loads(tools_json_str or "[]")
550
623
  if not isinstance(tools_list, list):
551
624
  tools_list = []
552
625
  if not skip_interactive:
@@ -595,7 +668,7 @@ def create_agent_config(
595
668
  @click.option("--instruction", help="Custom instruction for the agent.")
596
669
  @click.option(
597
670
  "--session-service-type",
598
- type=click.Choice(["memory", "vertex_rag"]),
671
+ type=click.Choice(["sql", "memory", "vertex_rag"]),
599
672
  help="Session service type.",
600
673
  )
601
674
  @click.option(
@@ -603,6 +676,9 @@ def create_agent_config(
603
676
  type=click.Choice(["PERSISTENT", "RUN_BASED"]),
604
677
  help="Session service behavior.",
605
678
  )
679
+ @click.option(
680
+ "--database-url", help="Database URL for session service (if type is 'sql')."
681
+ )
606
682
  @click.option(
607
683
  "--artifact-service-type",
608
684
  type=click.Choice(["memory", "filesystem", "gcs", "s3"]),
@@ -714,3 +790,4 @@ def add_agent(name: str, gui: bool = False, **kwargs):
714
790
  ),
715
791
  err=True,
716
792
  )
793
+ sys.exit(1)
@@ -1,10 +1,23 @@
1
1
  import click
2
+ import pkg_resources
2
3
  from pathlib import Path
3
4
 
4
5
  from evaluation.run import main as run_evaluation_main
5
6
  from cli.utils import error_exit, load_template
6
7
 
7
8
 
9
+ def _ensure_sam_rest_gateway_installed():
10
+ """Checks if the sam-rest-gateway package is installed."""
11
+ try:
12
+ pkg_resources.get_distribution("sam-rest-gateway")
13
+ except pkg_resources.DistributionNotFound:
14
+ error_exit(
15
+ "Error: 'sam-rest-gateway' is not installed. "
16
+ "Please install it using: "
17
+ 'pip install "sam-rest-gateway @ git+https://github.com/SolaceLabs/solace-agent-mesh-core-plugins#subdirectory=sam-rest-gateway"'
18
+ )
19
+
20
+
8
21
  def _ensure_eval_backend_config_exists():
9
22
  """Checks for eval_backend.yaml and creates it from a template if missing."""
10
23
  project_root = Path.cwd()
@@ -62,6 +75,7 @@ def eval_cmd(test_suite_config_path, verbose):
62
75
  fg="blue",
63
76
  )
64
77
  )
78
+ _ensure_sam_rest_gateway_installed()
65
79
  _ensure_eval_backend_config_exists()
66
80
  try:
67
81
  run_evaluation_main(test_suite_config_path, verbose=verbose)
@@ -1,17 +1,17 @@
1
- import click
2
1
  from pathlib import Path
3
2
 
3
+ import click
4
+
4
5
  from ...utils import ask_yes_no_question
5
- from .web_init_step import perform_web_init
6
+ from .broker_step import broker_setup_step
7
+ from .database_step import database_setup_step
6
8
  from .directory_step import create_project_directories
9
+ from .env_step import ENV_DEFAULTS, create_env_file
10
+ from .orchestrator_step import ORCHESTRATOR_DEFAULTS as O_DEFAULTS
11
+ from .orchestrator_step import create_orchestrator_config
7
12
  from .project_files_step import create_project_files
8
- from .env_step import create_env_file, ENV_DEFAULTS
9
- from .broker_step import broker_setup_step
10
- from .orchestrator_step import (
11
- create_orchestrator_config,
12
- ORCHESTRATOR_DEFAULTS as O_DEFAULTS,
13
- )
14
- from .webui_gateway_step import create_webui_gateway_config, WEBUI_GATEWAY_DEFAULTS
13
+ from .web_init_step import perform_web_init
14
+ from .webui_gateway_step import WEBUI_GATEWAY_DEFAULTS, create_webui_gateway_config
15
15
 
16
16
 
17
17
  def _get_flat_orchestrator_defaults():
@@ -19,10 +19,8 @@ def _get_flat_orchestrator_defaults():
19
19
  flat_defaults = {}
20
20
  flat_defaults["agent_name"] = O_DEFAULTS["agent_name"]
21
21
  flat_defaults["supports_streaming"] = O_DEFAULTS["supports_streaming"]
22
- flat_defaults["session_service_type"] = O_DEFAULTS["session_service"]["type"]
23
- flat_defaults["session_service_behavior"] = O_DEFAULTS["session_service"][
24
- "default_behavior"
25
- ]
22
+ flat_defaults["session_service_type"] = "memory"
23
+ flat_defaults["session_service_behavior"] = "PERSISTENT"
26
24
  flat_defaults["artifact_service_type"] = O_DEFAULTS["artifact_service"]["type"]
27
25
  flat_defaults["artifact_service_base_path"] = O_DEFAULTS["artifact_service"][
28
26
  "base_path"
@@ -30,9 +28,15 @@ def _get_flat_orchestrator_defaults():
30
28
  flat_defaults["artifact_service_scope"] = O_DEFAULTS["artifact_service"][
31
29
  "artifact_scope"
32
30
  ]
33
- flat_defaults["artifact_service_bucket_name"] = O_DEFAULTS["artifact_service"].get("bucket_name", "")
34
- flat_defaults["artifact_service_endpoint_url"] = O_DEFAULTS["artifact_service"].get("endpoint_url", "")
35
- flat_defaults["artifact_service_region"] = O_DEFAULTS["artifact_service"].get("region", "us-east-1")
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
+ )
36
40
  flat_defaults["artifact_handling_mode"] = O_DEFAULTS["artifact_handling_mode"]
37
41
  flat_defaults["enable_embed_resolution"] = O_DEFAULTS["enable_embed_resolution"]
38
42
  flat_defaults["enable_artifact_content_instruction"] = O_DEFAULTS[
@@ -124,6 +128,9 @@ def run_init_flow(skip_interactive: bool, use_web_based_init_flag: bool, **cli_o
124
128
  default=True,
125
129
  )
126
130
 
131
+ project_root = Path.cwd()
132
+ click.echo(f"Project will be initialized in: {project_root}")
133
+
127
134
  if actual_use_web_init:
128
135
  if skip_interactive:
129
136
  click.echo(
@@ -136,10 +143,7 @@ def run_init_flow(skip_interactive: bool, use_web_based_init_flag: bool, **cli_o
136
143
  options = perform_web_init(options)
137
144
  skip_interactive = True
138
145
 
139
- project_root = Path.cwd()
140
- click.echo(f"Project will be initialized in: {project_root}")
141
-
142
- shared_post_data_gathering_steps = [
146
+ steps = [
143
147
  ("Broker Setup", broker_setup_step),
144
148
  (
145
149
  "Project Directory Setup",
@@ -161,20 +165,20 @@ def run_init_flow(skip_interactive: bool, use_web_based_init_flag: bool, **cli_o
161
165
  project_root, opts, skip, defs
162
166
  ),
163
167
  ),
168
+ (
169
+ "Database Setup",
170
+ lambda opts, defs, skip: database_setup_step(project_root, opts, skip),
171
+ ),
164
172
  (
165
173
  ".env File Creation",
166
174
  lambda opts, defs, skip: create_env_file(project_root, opts, skip),
167
175
  ),
168
176
  ]
169
177
 
170
- current_steps_sequence = shared_post_data_gathering_steps
171
-
172
178
  step_count = 0
173
- total_display_steps = len(
174
- [s_name for s_name, _ in current_steps_sequence if s_name]
175
- )
179
+ total_display_steps = len([s_name for s_name, _ in steps if s_name])
176
180
 
177
- for step_name, step_function in current_steps_sequence:
181
+ for step_name, step_function in steps:
178
182
  if step_name:
179
183
  step_count += 1
180
184
  click.echo(
@@ -256,7 +260,7 @@ def run_init_flow(skip_interactive: bool, use_web_based_init_flag: bool, **cli_o
256
260
  )
257
261
  @click.option(
258
262
  "--session-service-type",
259
- type=click.Choice(["memory", "vertex_rag"]),
263
+ type=click.Choice(["memory", "vertex_rag", "sql"]),
260
264
  help="Session service type.",
261
265
  )
262
266
  @click.option(
@@ -371,10 +375,16 @@ def run_init_flow(skip_interactive: bool, use_web_based_init_flag: bool, **cli_o
371
375
  )
372
376
  @click.option("--webui-fastapi-host", type=str, help="Host for Web UI FastAPI server.")
373
377
  @click.option("--webui-fastapi-port", type=int, help="Port for Web UI FastAPI server.")
374
- @click.option("--webui-fastapi-https-port", type=int, help="HTTPS port for Web UI FastAPI server.")
378
+ @click.option(
379
+ "--webui-fastapi-https-port", type=int, help="HTTPS port for Web UI FastAPI server."
380
+ )
375
381
  @click.option("--webui-ssl-keyfile", type=str, help="SSL key file path for Web UI.")
376
- @click.option("--webui-ssl-certfile", type=str, help="SSL certificate file path for Web UI.")
377
- @click.option("--webui-ssl-keyfile-password", type=str, help="SSL key file passphrase for Web UI.")
382
+ @click.option(
383
+ "--webui-ssl-certfile", type=str, help="SSL certificate file path for Web UI."
384
+ )
385
+ @click.option(
386
+ "--webui-ssl-keyfile-password", type=str, help="SSL key file passphrase for Web UI."
387
+ )
378
388
  @click.option(
379
389
  "--webui-enable-embed-resolution",
380
390
  is_flag=True,
@@ -395,11 +405,23 @@ def run_init_flow(skip_interactive: bool, use_web_based_init_flag: bool, **cli_o
395
405
  default=None,
396
406
  help="Enable feedback collection in Web UI.",
397
407
  )
408
+ @click.option(
409
+ "--web-ui-gateway-database-url",
410
+ type=str,
411
+ help="Database URL for the WebUI Gateway.",
412
+ )
413
+ @click.option(
414
+ "--orchestrator-database-url",
415
+ type=str,
416
+ help="Database URL for the Orchestrator.",
417
+ )
398
418
  def init(**kwargs):
399
419
  """
400
420
  Initialize a new Solace application project.
401
421
  Creates a directory structure, default configuration files, and a .env file.
402
422
  """
423
+ use_web_based_init_val = kwargs.get("gui", False)
424
+
403
425
  if kwargs.get("dev_mode_flag"):
404
426
  if kwargs.get("broker_type") is None:
405
427
  kwargs["broker_type"] = "dev"
@@ -419,4 +441,4 @@ def init(**kwargs):
419
441
  skip_interactive=skip_interactive_val,
420
442
  use_web_based_init_flag=use_web_based_init_val,
421
443
  **kwargs,
422
- )
444
+ )
@@ -0,0 +1,91 @@
1
+ from pathlib import Path
2
+
3
+ import click
4
+
5
+ from ...utils import (
6
+ ask_if_not_provided,
7
+ ask_yes_no_question,
8
+ create_and_validate_database,
9
+ )
10
+
11
+
12
+ def prompt_for_db_credentials(
13
+ options: dict, db_type: str, skip_interactive: bool
14
+ ) -> str:
15
+ if skip_interactive:
16
+ db_url = options.get(f"{db_type}_database_url")
17
+ if not db_url:
18
+ raise ValueError(
19
+ f"Database URL for {db_type} is required in non-interactive mode"
20
+ )
21
+ return db_url
22
+
23
+ # Ask which database type to use
24
+ db_backend = ask_if_not_provided(
25
+ {},
26
+ "db_backend",
27
+ f"Which database would you like to use for {db_type}?",
28
+ choices=["sqlite", "postgresql"],
29
+ default="sqlite",
30
+ )
31
+
32
+ if db_backend == "postgresql":
33
+ db_url = ask_if_not_provided(
34
+ options,
35
+ f"{db_type}_database_url",
36
+ f"Enter the PostgreSQL URL for {db_type} (e.g., postgresql://user:pass@host:5432/dbname)",
37
+ none_interactive=skip_interactive,
38
+ )
39
+ else:
40
+ # Use SQLite as fallback/default
41
+ return None # Will be handled by the SQLite setup logic
42
+
43
+ return db_url
44
+
45
+
46
+ def database_setup_step(
47
+ project_root: Path, options: dict, skip_interactive: bool
48
+ ) -> bool:
49
+ click.echo("Setting up database(s)...")
50
+
51
+ db_configs = []
52
+ if options.get("add_webui_gateway"):
53
+ db_configs.append(
54
+ ("gateway", "web_ui_gateway_database_url", "webui_gateway.db")
55
+ )
56
+ if options.get("use_orchestrator_db"):
57
+ kebab_name = options.get("agent_name", "orchestrator").lower().replace("_", "-")
58
+ db_configs.append(
59
+ ("orchestrator", "orchestrator_database_url", f"{kebab_name}.db")
60
+ )
61
+
62
+ for db_type, url_key, default_filename in db_configs:
63
+ if options.get(url_key):
64
+ database_url = options[url_key]
65
+ click.echo(f" Using provided database URL for {db_type}.")
66
+ else:
67
+ use_own_db = False
68
+ if not skip_interactive:
69
+ use_own_db = ask_yes_no_question(
70
+ f"Do you want to use your own database for the {db_type}?",
71
+ default=False,
72
+ )
73
+
74
+ if use_own_db:
75
+ database_url = prompt_for_db_credentials(
76
+ options, db_type, skip_interactive
77
+ )
78
+ else:
79
+ data_dir = project_root / "data"
80
+ data_dir.mkdir(exist_ok=True)
81
+ db_file = data_dir / default_filename
82
+ database_url = f"sqlite:///{db_file.resolve()}"
83
+ click.echo(f" Using default SQLite database for {db_type}: {db_file}")
84
+
85
+ options[url_key] = database_url
86
+
87
+ # Create and validate database connection
88
+ create_and_validate_database(database_url, f"{url_key} database")
89
+
90
+ click.echo(" Database setup complete.")
91
+ return True
@@ -18,6 +18,8 @@ ENV_DEFAULTS = {
18
18
  "FASTAPI_PORT": "8000",
19
19
  "FASTAPI_HTTPS_PORT": "8443",
20
20
  "ENABLE_EMBED_RESOLUTION": "true",
21
+ "WEB_UI_GATEWAY_DATABASE_URL": "sqlite:///data/webui_gateway.db",
22
+ "ORCHESTRATOR_DATABASE_URL": "sqlite:///data/orchestrator.db",
21
23
  "SSL_KEYFILE": "",
22
24
  "SSL_CERTFILE": "",
23
25
  "SSL_KEYFILE_PASSWORD": "",
@@ -39,28 +41,28 @@ def create_env_file(project_root: Path, options: dict, skip_interactive: bool) -
39
41
 
40
42
  env_params_config = [
41
43
  (
42
- "llm_endpoint_url",
44
+ "llm_service_endpoint",
43
45
  "LLM_SERVICE_ENDPOINT",
44
46
  "Enter LLM Service Endpoint URL",
45
47
  False,
46
48
  "LLM_SERVICE_ENDPOINT",
47
49
  ),
48
50
  (
49
- "llm_api_key",
51
+ "llm_service_api_key",
50
52
  "LLM_SERVICE_API_KEY",
51
53
  "Enter LLM Service API Key",
52
54
  True,
53
55
  "LLM_SERVICE_API_KEY",
54
56
  ),
55
57
  (
56
- "llm_planning_model_name",
58
+ "llm_service_planning_model_name",
57
59
  "LLM_SERVICE_PLANNING_MODEL_NAME",
58
60
  "Enter LLM Planning Model Name (e.g., openai/gpt-4o)",
59
61
  False,
60
62
  "LLM_SERVICE_PLANNING_MODEL_NAME",
61
63
  ),
62
64
  (
63
- "llm_general_model_name",
65
+ "llm_service_general_model_name",
64
66
  "LLM_SERVICE_GENERAL_MODEL_NAME",
65
67
  "Enter LLM General Model Name (e.g., openai/gpt-3.5-turbo)",
66
68
  False,
@@ -134,28 +136,28 @@ def create_env_file(project_root: Path, options: dict, skip_interactive: bool) -
134
136
  "FASTAPI_HTTPS_PORT",
135
137
  "Enter Web UI FastAPI HTTPS Port",
136
138
  False,
137
- "FASTAPI_HTTPS_PORT"
139
+ "FASTAPI_HTTPS_PORT",
138
140
  ),
139
141
  (
140
142
  "webui_ssl_keyfile",
141
143
  "SSL_KEYFILE",
142
144
  "Enter SSL Key File Path",
143
145
  False,
144
- "SSL_KEYFILE"
146
+ "SSL_KEYFILE",
145
147
  ),
146
148
  (
147
149
  "webui_ssl_certfile",
148
150
  "SSL_CERTFILE",
149
151
  "Enter SSL Certificate File Path",
150
152
  False,
151
- "SSL_CERTFILE"
153
+ "SSL_CERTFILE",
152
154
  ),
153
155
  (
154
156
  "webui_ssl_keyfile_password",
155
157
  "SSL_KEYFILE_PASSWORD",
156
158
  "Enter SSL Key File Passphrase",
157
159
  True,
158
- "SSL_KEYFILE_PASSWORD"
160
+ "SSL_KEYFILE_PASSWORD",
159
161
  ),
160
162
  (
161
163
  "webui_enable_embed_resolution",
@@ -207,6 +209,15 @@ def create_env_file(project_root: Path, options: dict, skip_interactive: bool) -
207
209
  )
208
210
  env_vars_to_write[env_name] = options.get(opt_key)
209
211
 
212
+ if options.get("web_ui_gateway_database_url"):
213
+ env_vars_to_write["WEB_UI_GATEWAY_DATABASE_URL"] = options[
214
+ "web_ui_gateway_database_url"
215
+ ]
216
+ if options.get("orchestrator_database_url"):
217
+ env_vars_to_write["ORCHESTRATOR_DATABASE_URL"] = options[
218
+ "orchestrator_database_url"
219
+ ]
220
+
210
221
  if (
211
222
  env_vars_to_write.get("NAMESPACE")
212
223
  and env_vars_to_write["NAMESPACE"] != ENV_DEFAULTS.get("NAMESPACE")