Skip to content

Commit f4f0e78

Browse files
authored
hotfix/cli with server support python protocol (#172)
* Fix python protocol bug in cli, Add --with-server to cli * bump version * Revert rogue dir
1 parent 3944358 commit f4f0e78

3 files changed

Lines changed: 72 additions & 4 deletions

File tree

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.6.3
1+
0.6.4

rogue/__main__.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import sys
44
import time
55
from argparse import ArgumentParser, Namespace
6+
from ipaddress import ip_address
67
from pathlib import Path
78

89
import platformdirs
@@ -100,6 +101,13 @@ def parse_args() -> Namespace:
100101
help="Run in non-interactive CLI mode",
101102
parents=[common_parser()],
102103
)
104+
cli_parser.add_argument(
105+
"--with-server",
106+
action="store_true",
107+
default=False,
108+
help="Start the rogue server alongside the CLI",
109+
)
110+
set_server_args(cli_parser)
103111
set_cli_args(cli_parser)
104112

105113
# TUI mode
@@ -119,6 +127,20 @@ def parse_args() -> Namespace:
119127
return parser.parse_args()
120128

121129

130+
def build_local_server_url(host: str, port: int) -> str:
131+
"""Build an HTTP URL for a server bound to host:port, normalizing wildcards."""
132+
http_host = host
133+
if http_host in ("0.0.0.0", "::"): # nosec B104
134+
http_host = "127.0.0.1"
135+
else:
136+
try:
137+
if ip_address(http_host).version == 6:
138+
http_host = f"[{http_host}]"
139+
except ValueError:
140+
pass # hostname, leave as-is
141+
return f"http://{http_host}:{port}"
142+
143+
122144
def start_example_agent(
123145
example_name: str,
124146
host: str,
@@ -279,7 +301,32 @@ def main() -> None:
279301
if args.mode == "server":
280302
run_server(args, background=False)
281303
elif args.mode == "cli":
282-
exit_code = asyncio.run(run_cli(args))
304+
server_process = None
305+
if args.with_server:
306+
server_process = run_server(
307+
args,
308+
background=True,
309+
log_file=log_file_path,
310+
)
311+
if not server_process:
312+
logger.error("Failed to start rogue server. Exiting.")
313+
sys.exit(1)
314+
# Point the CLI at the embedded server.
315+
args.rogue_server_url = build_local_server_url(
316+
args.host,
317+
args.port,
318+
)
319+
logger.info(
320+
"Rogue server started for CLI",
321+
extra={"rogue_server_url": args.rogue_server_url},
322+
)
323+
324+
try:
325+
exit_code = asyncio.run(run_cli(args))
326+
finally:
327+
if server_process:
328+
server_process.terminate()
329+
server_process.join()
283330
sys.exit(exit_code)
284331
elif args.mode == "tui":
285332
if not RogueTuiInstaller().install_rogue_tui():

sdks/python/rogue_sdk/types.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ class RedTeamConfig(BaseModel):
178178
frameworks: List[str] = Field(
179179
default_factory=list,
180180
description=(
181-
"Framework IDs for report mapping " "(e.g., 'owasp-llm', 'mitre-atlas')"
181+
"Framework IDs for report mapping (e.g., 'owasp-llm', 'mitre-atlas')"
182182
),
183183
)
184184
random_seed: Optional[int] = Field(
@@ -293,7 +293,7 @@ class AgentConfig(BaseModel):
293293
"""Configuration for the agent being evaluated."""
294294

295295
protocol: Protocol = Protocol.A2A
296-
transport: Transport = None # type: ignore # fixed in model_post_init
296+
transport: Transport | None = None # type: ignore # fixed in model_post_init
297297
evaluated_agent_url: Optional[HttpUrl] = None # Optional for PYTHON protocol
298298
evaluated_agent_auth_type: AuthType = Field(
299299
default=AuthType.NO_AUTH,
@@ -337,6 +337,27 @@ def check_auth_credentials(self) -> "AgentConfig":
337337
)
338338
return self
339339

340+
@model_validator(mode="after")
341+
def validate_transport(self) -> "AgentConfig":
342+
if self.protocol == Protocol.PYTHON:
343+
if self.transport is not None:
344+
raise ValueError(
345+
"Transport cannot be provided when protocol is PYTHON",
346+
)
347+
else:
348+
return self
349+
350+
if self.transport is None:
351+
raise ValueError(
352+
"Transport is required when protocol is not PYTHON",
353+
)
354+
355+
if not self.transport.is_valid_for_protocol(self.protocol): # type: ignore
356+
raise ValueError(
357+
"Transport is not valid for the selected protocol",
358+
)
359+
return self
360+
340361
@model_validator(mode="after")
341362
def check_protocol_requirements(self) -> "AgentConfig":
342363
"""Validate protocol-specific requirements."""

0 commit comments

Comments
 (0)