diff --git a/documentation/docs/api-reference/cli.md b/documentation/docs/api-reference/cli.md index d8983090..9427e391 100644 --- a/documentation/docs/api-reference/cli.md +++ b/documentation/docs/api-reference/cli.md @@ -99,6 +99,8 @@ Options: - `--user-id, -u TEXT`: User ID for authorization flows +- `--response-only, -ro`: Return only the JSON payload response without metadata + **Example Output:** - Session and Request IDs displayed in panel header @@ -229,6 +231,9 @@ agentcore invoke '{"prompt": "Secure request"}' --bearer-token eyJhbGciOiJIUzI1N # Invoke local agent agentcore invoke '{"prompt": "Test locally"}' --local + +# Invoke with response-only flag (returns only the JSON payload) +agentcore invoke '{"prompt": "Get raw response"}' --response-only ``` ### Check Status diff --git a/src/bedrock_agentcore_starter_toolkit/cli/runtime/commands.py b/src/bedrock_agentcore_starter_toolkit/cli/runtime/commands.py index 2d752da4..ab4fd4df 100644 --- a/src/bedrock_agentcore_starter_toolkit/cli/runtime/commands.py +++ b/src/bedrock_agentcore_starter_toolkit/cli/runtime/commands.py @@ -602,10 +602,11 @@ def _show_invoke_info_panel(agent_name: str, invoke_result=None, config=None): ) -def _show_success_response(content): +def _show_success_response(content, response_only=False): """Show success response content below panel.""" if content: - console.print("\n[bold]Response:[/bold]") + if not response_only: + console.print("\n[bold]Response:[/bold]") console.print(content) @@ -670,6 +671,9 @@ def invoke( help="Custom headers (format: 'Header1:value,Header2:value2'). " "Headers will be auto-prefixed with 'X-Amzn-Bedrock-AgentCore-Runtime-Custom-' if not already present.", ), + response_only: Optional[bool] = typer.Option( + False, "--response-only", "-ro", help="Return only the JSON payload response without metadata" + ), ): """Invoke Bedrock AgentCore endpoint.""" config_path = Path.cwd() / ".bedrock_agentcore.yaml" @@ -725,7 +729,12 @@ def invoke( custom_headers=custom_headers, ) agent_display = config.name if config else (agent or "unknown") - _show_invoke_info_panel(agent_display, result, config) + + # Show info panel only if response_only is False + if not response_only: + _show_invoke_info_panel(agent_display, result, config) + + # Process response content if result.response != {}: content = result.response if isinstance(content, dict) and "response" in content: @@ -752,7 +761,9 @@ def invoke( content = parsed except (json.JSONDecodeError, TypeError): pass - _show_success_response(content) + + # Display the content with or without formatting based on response_only flag + _show_success_response(content, response_only) except FileNotFoundError: _show_configuration_not_found_panel() diff --git a/tests/cli/runtime/test_commands.py b/tests/cli/runtime/test_commands.py index 4407570a..114b7cf1 100644 --- a/tests/cli/runtime/test_commands.py +++ b/tests/cli/runtime/test_commands.py @@ -3087,3 +3087,74 @@ def test_invoke_with_headers_local_mode(self, tmp_path): assert call_args.kwargs["custom_headers"] == expected_headers finally: os.chdir(original_cwd) + + def test_invoke_with_response_only_flag(self, tmp_path): + """Test invoke command with response_only flag.""" + config_file = tmp_path / ".bedrock_agentcore.yaml" + config_content = """ +default_agent: test-agent +agents: + test-agent: + name: test-agent + entrypoint: test.py +""" + config_file.write_text(config_content.strip()) + + with ( + patch("bedrock_agentcore_starter_toolkit.cli.runtime.commands.load_config") as mock_load_config, + patch("bedrock_agentcore_starter_toolkit.cli.runtime.commands.invoke_bedrock_agentcore") as mock_invoke, + patch("bedrock_agentcore_starter_toolkit.cli.runtime.commands.console.print") as mock_print, + patch("bedrock_agentcore_starter_toolkit.cli.runtime.commands._show_invoke_info_panel") as mock_show_panel, + patch("bedrock_agentcore_starter_toolkit.cli.runtime.commands._show_success_response") as mock_show_response, + ): + # Mock project config and agent config + mock_project_config = Mock() + mock_agent_config = Mock() + mock_agent_config.authorizer_configuration = None + mock_project_config.get_agent_config.return_value = mock_agent_config + mock_load_config.return_value = mock_project_config + + # Create a response with nested structure + mock_result = Mock() + mock_result.response = {"response": "This is the raw response"} + mock_result.session_id = "test-session" + mock_invoke.return_value = mock_result + + original_cwd = Path.cwd() + os.chdir(tmp_path) + + try: + # Test with response_only flag + result = self.runner.invoke( + app, ["invoke", '{"message": "hello"}', "--response-only"] + ) + + assert result.exit_code == 0 + + # Verify response_only was passed to invoke_bedrock_agentcore + call_args = mock_invoke.call_args + + # Verify the info panel was not shown when response_only is True + mock_show_panel.assert_not_called() + mock_show_response.assert_not_called() + + # Verify console.print was called directly with the content + mock_print.assert_any_call("This is the raw response") + + # Test without response_only flag + mock_show_panel.reset_mock() + mock_show_response.reset_mock() + mock_print.reset_mock() + + result = self.runner.invoke( + app, ["invoke", '{"message": "hello"}'] + ) + + assert result.exit_code == 0 + + # Verify the info panel was shown when response_only is False + mock_show_panel.assert_called_once() + mock_show_response.assert_called_once() + + finally: + os.chdir(original_cwd)