From 982ac508dd1555dadb6bfc6560ba5e9215f4dcbb Mon Sep 17 00:00:00 2001 From: fbrand-new Date: Fri, 21 Jun 2024 14:57:44 +0200 Subject: [PATCH 1/3] [yarpDeviceLLM] Added refresh conversation rpc - Implementation of refreshConversation is deferred to individual devices - Fixed conversation stream for LLM_nws - Added refresh conversation to test. Updated documentation --- doc/release/master.md | 7 ++++++- .../fake/fakeLLMDevice/FakeLLMDevice.cpp | 21 +++++++++++++++++++ .../fake/fakeLLMDevice/FakeLLMDevice.h | 1 + src/devices/messages/ILLMMsgs/ILLMMsgs.thrift | 1 + .../LLM_nwc_yarp/LLM_nwc_yarp.cpp | 5 +++++ .../LLM_nwc_yarp/LLM_nwc_yarp.h | 1 + .../LLM_nws_yarp/ILLMServerImpl.cpp | 18 ++++++++++++++++ .../LLM_nws_yarp/ILLMServerImpl.h | 1 + src/libYARP_dev/src/yarp/dev/ILLM.h | 6 ++++++ src/libYARP_dev/src/yarp/dev/tests/ILLMTest.h | 9 ++++++++ 10 files changed, 69 insertions(+), 1 deletion(-) diff --git a/doc/release/master.md b/doc/release/master.md index a2a4e6d3661..4c2ff99bbed 100644 --- a/doc/release/master.md +++ b/doc/release/master.md @@ -38,7 +38,6 @@ New Features * Added new command line tool `yarpDeviceParamParserGenerator`. See official yarp documentation (cmd_yarpDeviceParamParserGenerator.dox) -* Added LLM_Message data type to propagate LLM answers #### Docker * Added two parameters to yarp `Dockerfile`: @@ -55,3 +54,9 @@ New Features * Added new device `deviceBundler` which can be useful to open two devices and attach them while using a single yarpdev command line. See https://github.com/robotology/yarp/discussions/3078 + +#### llmDevice + +* Added LLM_Message data type to propagate LLM answers + +* Added refreshConversation feature in the interface to allow users to restart the conversation mantaining the same prompt. diff --git a/src/devices/fake/fakeLLMDevice/FakeLLMDevice.cpp b/src/devices/fake/fakeLLMDevice/FakeLLMDevice.cpp index f956306a9cf..c87dfb8f96d 100644 --- a/src/devices/fake/fakeLLMDevice/FakeLLMDevice.cpp +++ b/src/devices/fake/fakeLLMDevice/FakeLLMDevice.cpp @@ -67,6 +67,27 @@ bool FakeLLMDevice::deleteConversation() noexcept return true; } +bool FakeLLMDevice::refreshConversation() noexcept +{ + std::string current_prompt; + + if(!this->readPrompt(current_prompt)) + { + yError() << "No prompt found in the conversation. Cannot refresh."; + return false; + } + + this->deleteConversation(); + + if(!this->setPrompt(current_prompt)) + { + yError() << "Failed to refresh the conversation."; + return false; + } + + return true; +} + bool FakeLLMDevice::open(yarp::os::Searchable& config) { if (!this->parseParams(config)) {return false;} diff --git a/src/devices/fake/fakeLLMDevice/FakeLLMDevice.h b/src/devices/fake/fakeLLMDevice/FakeLLMDevice.h index d093b80803d..ee7967b94e3 100644 --- a/src/devices/fake/fakeLLMDevice/FakeLLMDevice.h +++ b/src/devices/fake/fakeLLMDevice/FakeLLMDevice.h @@ -31,6 +31,7 @@ class FakeLLMDevice : public yarp::dev::ILLM, bool ask(const std::string &question, yarp::dev::LLM_Message &oAnswer) override; bool getConversation(std::vector &oConversation) override; bool deleteConversation() noexcept override; + bool refreshConversation() noexcept override; bool open(yarp::os::Searchable& config) override; bool close() override; diff --git a/src/devices/messages/ILLMMsgs/ILLMMsgs.thrift b/src/devices/messages/ILLMMsgs/ILLMMsgs.thrift index 43bdfd70fa2..d1bad0672ab 100644 --- a/src/devices/messages/ILLMMsgs/ILLMMsgs.thrift +++ b/src/devices/messages/ILLMMsgs/ILLMMsgs.thrift @@ -32,4 +32,5 @@ service ILLMMsgs { return_ask ask(1: string question); return_getConversation getConversation(); bool deleteConversation(); + bool refreshConversation(); } diff --git a/src/devices/networkWrappers/LLM_nwc_yarp/LLM_nwc_yarp.cpp b/src/devices/networkWrappers/LLM_nwc_yarp/LLM_nwc_yarp.cpp index 2971637ecec..7725552c2e2 100644 --- a/src/devices/networkWrappers/LLM_nwc_yarp/LLM_nwc_yarp.cpp +++ b/src/devices/networkWrappers/LLM_nwc_yarp/LLM_nwc_yarp.cpp @@ -83,3 +83,8 @@ bool LLM_nwc_yarp::deleteConversation() { return m_LLM_RPC.deleteConversation(); } + +bool LLM_nwc_yarp::refreshConversation() +{ + return m_LLM_RPC.refreshConversation(); +} \ No newline at end of file diff --git a/src/devices/networkWrappers/LLM_nwc_yarp/LLM_nwc_yarp.h b/src/devices/networkWrappers/LLM_nwc_yarp/LLM_nwc_yarp.h index 35398cbdc87..0bbdc14e2d3 100644 --- a/src/devices/networkWrappers/LLM_nwc_yarp/LLM_nwc_yarp.h +++ b/src/devices/networkWrappers/LLM_nwc_yarp/LLM_nwc_yarp.h @@ -40,4 +40,5 @@ class LLM_nwc_yarp : public yarp::dev::DeviceDriver, bool ask(const std::string& question, yarp::dev::LLM_Message& oAnswer) override; bool getConversation(std::vector& oConversation) override; bool deleteConversation() override; + bool refreshConversation() override; }; diff --git a/src/devices/networkWrappers/LLM_nws_yarp/ILLMServerImpl.cpp b/src/devices/networkWrappers/LLM_nws_yarp/ILLMServerImpl.cpp index ffafa552b44..a29e89de00b 100644 --- a/src/devices/networkWrappers/LLM_nws_yarp/ILLMServerImpl.cpp +++ b/src/devices/networkWrappers/LLM_nws_yarp/ILLMServerImpl.cpp @@ -73,6 +73,7 @@ void ILLMRPCd::m_stream_conversation() } auto& bot = m_streaming_port.prepare(); + bot.clear(); auto& list = bot.addList(); for (const auto& message : conversation) { auto& message_bot = list.addList(); @@ -128,3 +129,20 @@ bool ILLMRPCd::deleteConversation() return ret; } + +bool ILLMRPCd::refreshConversation() +{ + bool ret = false; + if (m_iLlm == nullptr) { + yCError(LLMSERVER, "Invalid interface"); + return false; + } + + ret = m_iLlm->refreshConversation(); + + if (ret) { + m_stream_conversation(); + } + + return ret; +} diff --git a/src/devices/networkWrappers/LLM_nws_yarp/ILLMServerImpl.h b/src/devices/networkWrappers/LLM_nws_yarp/ILLMServerImpl.h index 17aaeb756e7..774fc15cf4c 100644 --- a/src/devices/networkWrappers/LLM_nws_yarp/ILLMServerImpl.h +++ b/src/devices/networkWrappers/LLM_nws_yarp/ILLMServerImpl.h @@ -35,4 +35,5 @@ class ILLMRPCd : public yarp::dev::llm::ILLMMsgs yarp::dev::llm::return_ask ask(const std::string& question) override; yarp::dev::llm::return_getConversation getConversation() override; bool deleteConversation() override; + bool refreshConversation() override; }; diff --git a/src/libYARP_dev/src/yarp/dev/ILLM.h b/src/libYARP_dev/src/yarp/dev/ILLM.h index 7cebbea6e37..11b0b9b969d 100644 --- a/src/libYARP_dev/src/yarp/dev/ILLM.h +++ b/src/libYARP_dev/src/yarp/dev/ILLM.h @@ -63,6 +63,12 @@ class YARP_dev_API yarp::dev::ILLM * @return true/false */ virtual bool deleteConversation() = 0; + + /** + * Refresh the conversation + * @return true/false + */ + virtual bool refreshConversation() = 0; }; #endif diff --git a/src/libYARP_dev/src/yarp/dev/tests/ILLMTest.h b/src/libYARP_dev/src/yarp/dev/tests/ILLMTest.h index 07518a46177..a200bd471f9 100644 --- a/src/libYARP_dev/src/yarp/dev/tests/ILLMTest.h +++ b/src/libYARP_dev/src/yarp/dev/tests/ILLMTest.h @@ -47,6 +47,15 @@ inline void exec_iLLM_test_1(yarp::dev::ILLM* illm) std::vector conversation; b = illm->getConversation(conversation); CHECK(b); + + b = illm->refreshConversation(); + CHECK(b); + + b = illm->readPrompt(prompt); + CHECK(b); + + b = illm->deleteConversation(); + CHECK(b); } } From bb8d2ee7e632ad15e3a32307887145b6c5c93a69 Mon Sep 17 00:00:00 2001 From: fbrand-new Date: Tue, 23 Jul 2024 12:33:19 +0200 Subject: [PATCH 2/3] Fixed trailing spaces and EoL --- src/devices/fake/fakeLLMDevice/FakeLLMDevice.cpp | 2 +- src/devices/networkWrappers/LLM_nwc_yarp/LLM_nwc_yarp.cpp | 2 +- src/libYARP_dev/src/yarp/dev/ILLM.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/devices/fake/fakeLLMDevice/FakeLLMDevice.cpp b/src/devices/fake/fakeLLMDevice/FakeLLMDevice.cpp index c87dfb8f96d..9b3effe04e8 100644 --- a/src/devices/fake/fakeLLMDevice/FakeLLMDevice.cpp +++ b/src/devices/fake/fakeLLMDevice/FakeLLMDevice.cpp @@ -84,7 +84,7 @@ bool FakeLLMDevice::refreshConversation() noexcept yError() << "Failed to refresh the conversation."; return false; } - + return true; } diff --git a/src/devices/networkWrappers/LLM_nwc_yarp/LLM_nwc_yarp.cpp b/src/devices/networkWrappers/LLM_nwc_yarp/LLM_nwc_yarp.cpp index 7725552c2e2..c1069331b72 100644 --- a/src/devices/networkWrappers/LLM_nwc_yarp/LLM_nwc_yarp.cpp +++ b/src/devices/networkWrappers/LLM_nwc_yarp/LLM_nwc_yarp.cpp @@ -87,4 +87,4 @@ bool LLM_nwc_yarp::deleteConversation() bool LLM_nwc_yarp::refreshConversation() { return m_LLM_RPC.refreshConversation(); -} \ No newline at end of file +} diff --git a/src/libYARP_dev/src/yarp/dev/ILLM.h b/src/libYARP_dev/src/yarp/dev/ILLM.h index 11b0b9b969d..4dab489fe7b 100644 --- a/src/libYARP_dev/src/yarp/dev/ILLM.h +++ b/src/libYARP_dev/src/yarp/dev/ILLM.h @@ -64,7 +64,7 @@ class YARP_dev_API yarp::dev::ILLM */ virtual bool deleteConversation() = 0; - /** + /** * Refresh the conversation * @return true/false */ From a5b5d360b712edb37bb2e5008bb4cbe172917fe4 Mon Sep 17 00:00:00 2001 From: fbrand-new Date: Tue, 23 Jul 2024 14:11:03 +0200 Subject: [PATCH 3/3] Added generated code from thrift to src to compile on CI --- .../yarp/dev/llm/ILLMMsgs.cpp | 220 ++++++++++++++++++ .../yarp/dev/llm/ILLMMsgs.h | 2 + 2 files changed, 222 insertions(+) diff --git a/src/devices/messages/ILLMMsgs/idl_generated_code/yarp/dev/llm/ILLMMsgs.cpp b/src/devices/messages/ILLMMsgs/idl_generated_code/yarp/dev/llm/ILLMMsgs.cpp index 37e4f1bc6c5..2573a358d1f 100644 --- a/src/devices/messages/ILLMMsgs/idl_generated_code/yarp/dev/llm/ILLMMsgs.cpp +++ b/src/devices/messages/ILLMMsgs/idl_generated_code/yarp/dev/llm/ILLMMsgs.cpp @@ -316,6 +316,64 @@ class ILLMMsgs_deleteConversation_helper : static constexpr const char* s_help{""}; }; +// refreshConversation helper class declaration +class ILLMMsgs_refreshConversation_helper : + public yarp::os::Portable +{ +public: + ILLMMsgs_refreshConversation_helper() = default; + bool write(yarp::os::ConnectionWriter& connection) const override; + bool read(yarp::os::ConnectionReader& connection) override; + + class Command : + public yarp::os::idl::WirePortable + { + public: + Command() = default; + ~Command() override = default; + + bool write(yarp::os::ConnectionWriter& connection) const override; + bool read(yarp::os::ConnectionReader& connection) override; + + bool write(const yarp::os::idl::WireWriter& writer) const override; + bool writeTag(const yarp::os::idl::WireWriter& writer) const; + bool writeArgs(const yarp::os::idl::WireWriter& writer) const; + + bool read(yarp::os::idl::WireReader& reader) override; + bool readTag(yarp::os::idl::WireReader& reader); + bool readArgs(yarp::os::idl::WireReader& reader); + }; + + class Reply : + public yarp::os::idl::WirePortable + { + public: + Reply() = default; + ~Reply() override = default; + + bool write(yarp::os::ConnectionWriter& connection) const override; + bool read(yarp::os::ConnectionReader& connection) override; + + bool write(const yarp::os::idl::WireWriter& writer) const override; + bool read(yarp::os::idl::WireReader& reader) override; + + bool return_helper{false}; + }; + + using funcptr_t = bool (*)(); + void call(ILLMMsgs* ptr); + + Command cmd; + Reply reply; + + static constexpr const char* s_tag{"refreshConversation"}; + static constexpr size_t s_tag_len{1}; + static constexpr size_t s_cmd_len{1}; + static constexpr size_t s_reply_len{1}; + static constexpr const char* s_prototype{"bool ILLMMsgs::refreshConversation()"}; + static constexpr const char* s_help{""}; +}; + // setPrompt helper class implementation ILLMMsgs_setPrompt_helper::ILLMMsgs_setPrompt_helper(const std::string& prompt) : cmd{prompt} @@ -1023,6 +1081,139 @@ void ILLMMsgs_deleteConversation_helper::call(ILLMMsgs* ptr) reply.return_helper = ptr->deleteConversation(); } +// refreshConversation helper class implementation +bool ILLMMsgs_refreshConversation_helper::write(yarp::os::ConnectionWriter& connection) const +{ + return cmd.write(connection); +} + +bool ILLMMsgs_refreshConversation_helper::read(yarp::os::ConnectionReader& connection) +{ + return reply.read(connection); +} + +bool ILLMMsgs_refreshConversation_helper::Command::write(yarp::os::ConnectionWriter& connection) const +{ + yarp::os::idl::WireWriter writer(connection); + if (!writer.writeListHeader(s_cmd_len)) { + return false; + } + return write(writer); +} + +bool ILLMMsgs_refreshConversation_helper::Command::read(yarp::os::ConnectionReader& connection) +{ + yarp::os::idl::WireReader reader(connection); + if (!reader.readListHeader()) { + reader.fail(); + return false; + } + return read(reader); +} + +bool ILLMMsgs_refreshConversation_helper::Command::write(const yarp::os::idl::WireWriter& writer) const +{ + if (!writeTag(writer)) { + return false; + } + if (!writeArgs(writer)) { + return false; + } + return true; +} + +bool ILLMMsgs_refreshConversation_helper::Command::writeTag(const yarp::os::idl::WireWriter& writer) const +{ + if (!writer.writeTag(s_tag, 1, s_tag_len)) { + return false; + } + return true; +} + +bool ILLMMsgs_refreshConversation_helper::Command::writeArgs(const yarp::os::idl::WireWriter& writer [[maybe_unused]]) const +{ + return true; +} + +bool ILLMMsgs_refreshConversation_helper::Command::read(yarp::os::idl::WireReader& reader) +{ + if (!readTag(reader)) { + return false; + } + if (!readArgs(reader)) { + return false; + } + return true; +} + +bool ILLMMsgs_refreshConversation_helper::Command::readTag(yarp::os::idl::WireReader& reader) +{ + std::string tag = reader.readTag(s_tag_len); + if (reader.isError()) { + return false; + } + if (tag != s_tag) { + reader.fail(); + return false; + } + return true; +} + +bool ILLMMsgs_refreshConversation_helper::Command::readArgs(yarp::os::idl::WireReader& reader) +{ + if (!reader.noMore()) { + reader.fail(); + return false; + } + return true; +} + +bool ILLMMsgs_refreshConversation_helper::Reply::write(yarp::os::ConnectionWriter& connection) const +{ + yarp::os::idl::WireWriter writer(connection); + return write(writer); +} + +bool ILLMMsgs_refreshConversation_helper::Reply::read(yarp::os::ConnectionReader& connection) +{ + yarp::os::idl::WireReader reader(connection); + return read(reader); +} + +bool ILLMMsgs_refreshConversation_helper::Reply::write(const yarp::os::idl::WireWriter& writer) const +{ + if (!writer.isNull()) { + if (!writer.writeListHeader(s_reply_len)) { + return false; + } + if (!writer.writeBool(return_helper)) { + return false; + } + } + return true; +} + +bool ILLMMsgs_refreshConversation_helper::Reply::read(yarp::os::idl::WireReader& reader) +{ + if (!reader.readListReturn()) { + return false; + } + if (reader.noMore()) { + reader.fail(); + return false; + } + if (!reader.readBool(return_helper)) { + reader.fail(); + return false; + } + return true; +} + +void ILLMMsgs_refreshConversation_helper::call(ILLMMsgs* ptr) +{ + reply.return_helper = ptr->refreshConversation(); +} + // Constructor ILLMMsgs::ILLMMsgs() { @@ -1079,6 +1270,16 @@ bool ILLMMsgs::deleteConversation() return ok ? helper.reply.return_helper : bool{}; } +bool ILLMMsgs::refreshConversation() +{ + if (!yarp().canWrite()) { + yError("Missing server method '%s'?", ILLMMsgs_refreshConversation_helper::s_prototype); + } + ILLMMsgs_refreshConversation_helper helper{}; + bool ok = yarp().write(helper, helper); + return ok ? helper.reply.return_helper : bool{}; +} + // help method std::vector ILLMMsgs::help(const std::string& functionName) { @@ -1091,6 +1292,7 @@ std::vector ILLMMsgs::help(const std::string& functionName) helpString.emplace_back(ILLMMsgs_ask_helper::s_tag); helpString.emplace_back(ILLMMsgs_getConversation_helper::s_tag); helpString.emplace_back(ILLMMsgs_deleteConversation_helper::s_tag); + helpString.emplace_back(ILLMMsgs_refreshConversation_helper::s_tag); helpString.emplace_back("help"); } else { if (functionName == ILLMMsgs_setPrompt_helper::s_tag) { @@ -1108,6 +1310,9 @@ std::vector ILLMMsgs::help(const std::string& functionName) if (functionName == ILLMMsgs_deleteConversation_helper::s_tag) { helpString.emplace_back(ILLMMsgs_deleteConversation_helper::s_prototype); } + if (functionName == ILLMMsgs_refreshConversation_helper::s_tag) { + helpString.emplace_back(ILLMMsgs_refreshConversation_helper::s_prototype); + } if (functionName == "help") { helpString.emplace_back("std::vector help(const std::string& functionName = \"--all\")"); helpString.emplace_back("Return list of available commands, or help message for a specific function"); @@ -1215,6 +1420,21 @@ bool ILLMMsgs::read(yarp::os::ConnectionReader& connection) reader.accept(); return true; } + if (tag == ILLMMsgs_refreshConversation_helper::s_tag) { + ILLMMsgs_refreshConversation_helper helper; + if (!helper.cmd.readArgs(reader)) { + return false; + } + + helper.call(this); + + yarp::os::idl::WireWriter writer(reader); + if (!helper.reply.write(writer)) { + return false; + } + reader.accept(); + return true; + } if (tag == "help") { std::string functionName; if (!reader.readString(functionName)) { diff --git a/src/devices/messages/ILLMMsgs/idl_generated_code/yarp/dev/llm/ILLMMsgs.h b/src/devices/messages/ILLMMsgs/idl_generated_code/yarp/dev/llm/ILLMMsgs.h index 5a2cbfefa39..7b7e3f749b8 100644 --- a/src/devices/messages/ILLMMsgs/idl_generated_code/yarp/dev/llm/ILLMMsgs.h +++ b/src/devices/messages/ILLMMsgs/idl_generated_code/yarp/dev/llm/ILLMMsgs.h @@ -36,6 +36,8 @@ class ILLMMsgs : virtual bool deleteConversation(); + virtual bool refreshConversation(); + // help method virtual std::vector help(const std::string& functionName = "--all");