Skip to content

Commit 89ae893

Browse files
authored
feat(DATAGO-121014): Add cli command for adding proxy (#746)
* feat(DATAGO-121014): Add cli command for adding proxy * minor changes
1 parent a76b849 commit 89ae893

File tree

6 files changed

+412
-3
lines changed

6 files changed

+412
-3
lines changed

cli/commands/add_cmd/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import click
22
from .agent_cmd import add_agent
33
from .gateway_cmd import add_gateway
4+
from .proxy_cmd import add_proxy
45

56

67
@click.group(name="add")
78
def add():
89
"""
9-
Creates templates for agents or gateways.
10+
Creates templates for agents, gateways, or proxies.
1011
"""
1112
pass
1213

1314

1415
add.add_command(add_agent, name="agent")
1516
add.add_command(add_gateway, name="gateway")
17+
add.add_command(add_proxy, name="proxy")

cli/commands/add_cmd/proxy_cmd.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import sys
2+
from pathlib import Path
3+
4+
import click
5+
6+
from ...utils import (
7+
get_formatted_names,
8+
load_template,
9+
)
10+
11+
12+
def _write_proxy_yaml(proxy_name_input: str, project_root: Path) -> tuple[bool, str, str]:
13+
"""
14+
Writes the proxy YAML file based on proxy_template.yaml.
15+
16+
Args:
17+
proxy_name_input: Name provided by user
18+
project_root: Project root directory
19+
20+
Returns:
21+
Tuple of (success, message, relative_file_path)
22+
"""
23+
agents_config_dir = project_root / "configs" / "agents"
24+
agents_config_dir.mkdir(parents=True, exist_ok=True)
25+
26+
formatted_names = get_formatted_names(proxy_name_input)
27+
proxy_name_pascal = formatted_names["PASCAL_CASE_NAME"]
28+
file_name_snake = formatted_names["SNAKE_CASE_NAME"]
29+
30+
proxy_config_file_path = agents_config_dir / f"{file_name_snake}_proxy.yaml"
31+
32+
try:
33+
# Load template
34+
template_content = load_template("proxy_template.yaml")
35+
36+
# Replace placeholder
37+
modified_content = template_content.replace("__PROXY_NAME__", proxy_name_pascal)
38+
39+
# Write file
40+
with open(proxy_config_file_path, "w", encoding="utf-8") as f:
41+
f.write(modified_content)
42+
43+
relative_file_path = str(proxy_config_file_path.relative_to(project_root))
44+
return (
45+
True,
46+
f"Proxy configuration created: {relative_file_path}",
47+
relative_file_path,
48+
)
49+
except FileNotFoundError as e:
50+
return (
51+
False,
52+
f"Error: Template file 'proxy_template.yaml' not found: {e}",
53+
"",
54+
)
55+
except Exception as e:
56+
import traceback
57+
click.echo(
58+
f"DEBUG: Error in _write_proxy_yaml: {e}\n{traceback.format_exc()}",
59+
err=True,
60+
)
61+
return (
62+
False,
63+
f"Error creating proxy configuration file {proxy_config_file_path}: {e}",
64+
"",
65+
)
66+
67+
68+
@click.command(name="proxy")
69+
@click.argument("name", required=False)
70+
@click.option(
71+
"--skip",
72+
is_flag=True,
73+
help="Skip interactive prompts (creates proxy with default template).",
74+
)
75+
def add_proxy(name: str, skip: bool = False):
76+
"""
77+
Creates a new A2A proxy configuration.
78+
79+
NAME: Name of the proxy component to create (e.g., my-proxy).
80+
"""
81+
if not name:
82+
click.echo(
83+
click.style(
84+
"Error: You must provide a proxy name.",
85+
fg="red",
86+
),
87+
err=True,
88+
)
89+
return
90+
91+
click.echo(f"Creating proxy configuration for '{name}'...")
92+
93+
project_root = Path.cwd()
94+
success, message, _ = _write_proxy_yaml(name, project_root)
95+
96+
if success:
97+
click.echo(click.style(message, fg="green"))
98+
else:
99+
click.echo(click.style(message, fg="red"), err=True)
100+
sys.exit(1)

config_portal/backend/common.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,16 @@
7373
),
7474
}
7575

76+
PROXY_DEFAULTS = {
77+
"namespace": "${NAMESPACE}",
78+
"artifact_service_type": "filesystem",
79+
"artifact_service_base_path": "/tmp/samv2",
80+
"artifact_service_scope": "namespace",
81+
"artifact_handling_mode": "reference",
82+
"discovery_interval_seconds": 5,
83+
"proxied_agents": [],
84+
}
85+
7686

7787
port_55555 = "-p 55554:55555" if sys.platform == "darwin" else "-p 55555:55555"
7888

docs/docs/documentation/components/cli.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,10 @@ Use the `--skip` option and provide the necessary options to run the command in
8989

9090
### `add` - Create a New Component
9191

92-
To add a new component, such as an agent or gateway, use the `add` command with the appropriate options.
92+
To add a new component, such as an agent, gateway, or proxy, use the `add` command with the appropriate options.
9393

9494
```sh
95-
sam add [agent|gateway] [OPTIONS] NAME
95+
sam add [agent|gateway|proxy] [OPTIONS] NAME
9696
```
9797

9898
#### Add `agent`
@@ -156,6 +156,29 @@ sam add gateway [OPTIONS] [NAME]
156156

157157
For more information, see [Gateways](gateways.md).
158158

159+
#### Add `proxy`
160+
161+
Use `proxy` to add an A2A proxy component that bridges external HTTP-based agents to the Solace Agent Mesh.
162+
163+
```sh
164+
sam add proxy [OPTIONS] [NAME]
165+
```
166+
167+
The proxy command creates a configuration file in `configs/agents/` that you can customize to connect external agents to the mesh.
168+
169+
##### Options:
170+
171+
- `--skip` – Skip interactive prompts and create the proxy with default template.
172+
- `-h, --help` – Displays the help message and exits.
173+
174+
##### Example:
175+
176+
```sh
177+
sam add proxy myProxy --skip
178+
```
179+
180+
This creates `configs/agents/my_proxy_proxy.yaml` with the default proxy configuration template.
181+
159182

160183

161184
### `run` - Run the Agent Mesh Application

templates/proxy_template.yaml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# A2A Proxy Configuration File
2+
3+
log:
4+
stdout_log_level: INFO
5+
log_file_level: DEBUG
6+
log_file: a2a_proxy.log
7+
8+
# Shared SAM config (includes broker connection details)
9+
!include shared_config.yaml
10+
11+
apps:
12+
- name: "__PROXY_NAME____app"
13+
app_base_path: .
14+
app_module: src.solace_agent_mesh.agent.proxies.a2a.app
15+
broker:
16+
<<: *broker_connection
17+
18+
# --- App Level Config for the Proxy ---
19+
app_config:
20+
namespace: ${NAMESPACE}
21+
22+
# Configuration for the artifact service used by the proxy itself.
23+
# When the proxy receives an artifact from a downstream agent, it will
24+
# store it here before forwarding it on the Solace mesh.
25+
artifact_service:
26+
type: "filesystem"
27+
base_path: "/tmp/samv2"
28+
artifact_scope: namespace # Default scope, shares artifacts within the NAMESPACE
29+
30+
# How the proxy should handle artifacts it sends over Solace.
31+
# 'reference': Sends an artifact:// URI.
32+
# 'embed': Sends the full content base64 encoded.
33+
artifact_handling_mode: "reference"
34+
35+
# Interval in seconds for the proxy to re-fetch agent cards from
36+
# downstream agents. Set to 0 to disable periodic checks.
37+
discovery_interval_seconds: 5
38+
tools:
39+
- group_name: artifact_management
40+
tool_type: builtin-group
41+
42+
# --- List of Downstream Agents to Proxy ---
43+
# A single proxy can manage multiple external agents.
44+
# Each agent can have its own URL, authentication, and timeout settings.
45+
proxied_agents:
46+
# Example 1: A simple agent without authentication
47+
- name: "HelloWorld" # The name this agent will have on the Solace mesh
48+
url: "http://localhost:9999" # The real HTTP endpoint of the agent
49+
50+
# Example 2: Another agent without authentication
51+
- name: "AnalysisAgent"
52+
url: "http://127.0.0.1:10001"
53+
54+
# You can add more agents here with different configurations:
55+
# - name: "SecureAgent"
56+
# url: "https://api.example.com/agent"
57+
# authentication:
58+
# type: "static_bearer"
59+
# token: "${SECURE_AGENT_TOKEN}"
60+
# request_timeout_seconds: 180
61+
# # use_agent_card_url: false # If true (default), uses URL from agent card for tasks.
62+
# # If false, uses configured URL directly for all calls.

0 commit comments

Comments
 (0)