Skip to content

Commit

Permalink
OpenAI example client ported to SMACC (#557)
Browse files Browse the repository at this point in the history
* initial commit for openAI example

* Replaced get with post, added key fetch logic

* Added ability to modify the body of an http request for the http client

* Added functionality to encode and send image data to ChatGPT

* Added APIs to add headers to an outgoing HTTP request

* HTTP requests can now deal with CDNs

* Expanded example openAI node to submit a more sophisticated request with
an image

* Using env variable for test iamge location

* Fixed commit errors

* Renamed instantiation of HTTP request CB to something more specific to OpenAI
  • Loading branch information
yassiezar authored Aug 30, 2024
1 parent 5fd2fa2 commit 8c0de46
Show file tree
Hide file tree
Showing 24 changed files with 2,006 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ class ClHttp : public smacc2::ISmaccClient

void onInitialize() override;

void makeRequest(const kHttpRequestMethod http_method, const std::string & path = "/");
void makeRequest(
const kHttpRequestMethod http_method, const std::string & path = "/",
const std::string & body = "",
const std::unordered_map<std::string, std::string> & headers = {});

private:
const int HTTP_VERSION = 11;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,15 @@ class http_session : public std::enable_shared_from_this<http_session>, public h
boost::asio::ip::tcp::resolver::results_type::endpoint_type) override;
void on_write(boost::beast::error_code ec, std::size_t bytes_transferred) override;
void on_read(boost::beast::error_code ec, std::size_t bytes_transferred) override;
void setBody(const std::string & body) override;
void setHeaders(const std::unordered_map<std::string, std::string> & headers) override {}

std::function<void(const TResponse &)> onResponse;

boost::asio::ip::tcp::resolver resolver_;
boost::beast::tcp_stream stream_;
boost::beast::flat_buffer buffer_; // (Must persist between reads)
boost::beast::http::request<boost::beast::http::empty_body> req_;
boost::beast::http::response<boost::beast::http::string_body> res_;
boost::beast::http::request<boost::beast::http::string_body> req_;
TResponse res_;
};
} // namespace cl_http
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class http_session_base
const boost::beast::http::verb http_method, const int & version) = 0;

virtual std::string getPort() = 0;
virtual void setBody(const std::string & body) = 0;
virtual void setHeaders(const std::unordered_map<std::string, std::string> & headers) = 0;

protected:
virtual void on_resolve(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Author: Jacobus Lock
#include <http_client/http_session_base.hpp>
#include <iostream>
#include <string>
#include <unordered_map>

namespace cl_http
{
Expand Down Expand Up @@ -65,14 +66,17 @@ class ssl_http_session : public std::enable_shared_from_this<ssl_http_session>,
void on_write(boost::beast::error_code ec, std::size_t bytes_transferred) override;
void on_read(boost::beast::error_code ec, std::size_t bytes_transferred) override;
void on_shutdown(boost::beast::error_code ec) override;
void setBody(const std::string & body) override;
void setHeaders(const std::unordered_map<std::string, std::string> & headers) override;
void appendToHeader(const std::string & key, const std::string & val);

std::function<void(const TResponse &)> onResponse;

boost::asio::ip::tcp::resolver resolver_;
// boost::beast::tcp_stream stream_;
boost::beast::ssl_stream<boost::beast::tcp_stream> stream_;
boost::beast::flat_buffer buffer_; // (Must persist between reads)
boost::beast::http::request<boost::beast::http::empty_body> req_;
boost::beast::http::response<boost::beast::http::string_body> res_;
boost::beast::http::request<boost::beast::http::string_body> req_;
TResponse res_;
};
} // namespace cl_http
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ ClHttp::ClHttp(const std::string & server_name, const int & timeout)
timeout_{timeout},
server_{server_name},
worker_guard_{boost::asio::make_work_guard(io_context_.get_executor())},
ssl_context_{boost::asio::ssl::context::tlsv12_client}
ssl_context_{boost::asio::ssl::context::tlsv13_client}
{
ssl_context_.set_default_verify_paths();
ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer);
Expand All @@ -48,7 +48,9 @@ void ClHttp::onInitialize()
}
}

void ClHttp::makeRequest(const kHttpRequestMethod http_method, const std::string & path)
void ClHttp::makeRequest(
const kHttpRequestMethod http_method, const std::string & path, const std::string & body,
const std::unordered_map<std::string, std::string> & headers)
{
auto path_used = path;
if (path[0] != '/')
Expand All @@ -63,20 +65,23 @@ void ClHttp::makeRequest(const kHttpRequestMethod http_method, const std::string
RCLCPP_INFO(this->getLogger(), "Path %s", path_used.c_str());
RCLCPP_INFO(this->getLogger(), "Port %s", server_.getPort().c_str());

std::shared_ptr<http_session_base> http_session_ptr;

if (server_.isSSL())
{
std::make_shared<ssl_http_session>(
boost::asio::make_strand(io_context_), ssl_context_, callbackHandler)
->run(
server_.getServerName(), path_used, static_cast<boost::beast::http::verb>(http_method),
HTTP_VERSION);
http_session_ptr = std::make_shared<ssl_http_session>(
boost::asio::make_strand(io_context_), ssl_context_, callbackHandler);
}
else
{
std::make_shared<http_session>(boost::asio::make_strand(io_context_), callbackHandler)
->run(
server_.getServerName(), path_used, static_cast<boost::beast::http::verb>(http_method),
HTTP_VERSION);
http_session_ptr =
std::make_shared<http_session>(boost::asio::make_strand(io_context_), callbackHandler);
}

http_session_ptr->setBody(body);
http_session_ptr->setHeaders(headers);
http_session_ptr->run(
server_.getServerName(), path_used, static_cast<boost::beast::http::verb>(http_method),
HTTP_VERSION);
}
} // namespace cl_http
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ http_session::http_session(
{
}

void http_session::setBody(const std::string & body) { req_.body() = body; }

void http_session::run(
const std::string & host, const std::string & target, const boost::beast::http::verb http_method,
const int & version)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@ ssl_http_session::ssl_http_session(
{
}

void ssl_http_session::setBody(const std::string & body) { req_.body() = body; }

void ssl_http_session::setHeaders(const std::unordered_map<std::string, std::string> & headers)
{
for (const auto & [key, val] : headers)
{
this->appendToHeader(key, val);
}
}

void ssl_http_session::appendToHeader(const std::string & key, const std::string & val)
{
req_.set(key, val);
}

void ssl_http_session::run(
const std::string & host, const std::string & target, const boost::beast::http::verb http_method,
const int & version)
Expand All @@ -42,6 +57,14 @@ void ssl_http_session::run(
req_.set(boost::beast::http::field::host, host);
req_.set(boost::beast::http::field::user_agent, BOOST_BEAST_VERSION_STRING);

// Set an SNI to deal with CDNs
if (!SSL_set_tlsext_host_name(stream_.native_handle(), host.c_str()))
{
boost::system::error_code ec{
static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category()};
std::cerr << ec.message() << "\n";
return;
}
// Look up the domain name
resolver_.async_resolve(
host.c_str(), kPort.c_str(),
Expand Down
64 changes: 64 additions & 0 deletions smacc2_sm_reference_library/sm_atomic_openai/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
cmake_minimum_required(VERSION 3.5)
project(sm_atomic_openai)

set(CMAKE_BUILD_TYPE "Debug")

# Default to C++17
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(smacc2 REQUIRED)
find_package(ros_timer_client REQUIRED)
find_package(http_client REQUIRED)
find_package(Boost COMPONENTS thread REQUIRED)
find_package(OpenSSL REQUIRED)
find_package(OpenCV REQUIRED)

include_directories(include
${smacc2_INCLUDE_DIRS}
${ros_timer_client_INCLUDE_DIRS}
${http_client_INCLUDE_DIRS}
)

add_executable(${PROJECT_NAME}_node
src/sm_atomic_openai/sm_atomic_openai_node.cpp)

target_link_libraries(${PROJECT_NAME}_node
${smacc2_LIBRARIES}
${ros_timer_client_LIBRARIES}
${http_client_LIBRARIES}
${Boost_LIBRARIES}
${OpenCV_LIBRARIES}
OpenSSL::SSL
OpenSSL::Crypto
)

ament_target_dependencies(${PROJECT_NAME}_node smacc2)

install(
DIRECTORY include/
DESTINATION include
)

install(DIRECTORY
launch
DESTINATION share/${PROJECT_NAME}
)

install(DIRECTORY
config
DESTINATION share/${PROJECT_NAME}
)

install(TARGETS
${PROJECT_NAME}_node
DESTINATION lib/${PROJECT_NAME})

ament_package()
48 changes: 48 additions & 0 deletions smacc2_sm_reference_library/sm_atomic_openai/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<h2>State Machine Diagram</h2>

![sm_atomic_http](docs/SmAtomic_2021-10-18_93942.svg)

<h2>Description</h2> A completely minimal state machine example.<br></br>

<h2>Build Instructions</h2>

First, source your chosen ros2 distro.
```
source /opt/ros/rolling/setup.bash
```
```
source /opt/ros/humble/setup.bash
```

Before you build, make sure you've installed all the dependencies...

```
rosdep install --ignore-src --from-paths src -y -r
```

Then build with colcon build...

```
colcon build
```
<h2>Operating Instructions</h2>
After you build, remember to source the proper install folder...

```
source ~/workspace/humble_ws/install/setup.bash
```

And then run the launch file...

```
ros2 launch sm_atomic_http sm_atomic_http.launch
```

<h2>Viewer Instructions</h2>
If you have the SMACC2 Runtime Analyzer installed then type...

```
ros2 run smacc2_rta smacc2_rta
```

If you don't have the SMACC2 Runtime Analyzer click <a href="https://robosoft.ai/product-category/smacc2-runtime-analyzer/">here</a>.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
global_parameter_server:
ros__parameters:
signal_detector_loop_freq: 200
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
state_machine_rosparam_ws: /SmAtomicOpenAI
success_switch:
- type: state_reached
state_name: "sm_atomic_openai::State2"
failure_switch:
- type: timeout
duration: 10.0 # sec
Loading

0 comments on commit 8c0de46

Please sign in to comment.