refactor, add stop and force commands
refine hardcoded system prompt a little
This commit is contained in:
parent
784fea96d6
commit
c99115f0d4
@ -35,7 +35,9 @@ add_library(solanaceae_rpbot
|
||||
./solanaceae/rpbot/message_prompt_builder.cpp
|
||||
|
||||
./solanaceae/rpbot/rpbot.hpp
|
||||
./solanaceae/rpbot/rpbot_states.hpp
|
||||
./solanaceae/rpbot/rpbot.cpp
|
||||
./solanaceae/rpbot/rpbot_commands.cpp
|
||||
)
|
||||
|
||||
target_include_directories(solanaceae_rpbot PUBLIC .)
|
||||
|
@ -1,7 +1,8 @@
|
||||
#include "./rpbot.hpp"
|
||||
|
||||
#include "./message_prompt_builder.hpp"
|
||||
#include "solanaceae/contact/contact_model3.hpp"
|
||||
|
||||
#include "./rpbot_states.hpp"
|
||||
|
||||
#include <solanaceae/contact/components.hpp>
|
||||
#include <solanaceae/message3/components.hpp>
|
||||
@ -10,46 +11,10 @@
|
||||
#include <limits>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
// sleeps until onMsg or onTimer
|
||||
struct StateIdle {
|
||||
static constexpr const char* name {"StateIdle"};
|
||||
float timeout {0.f};
|
||||
};
|
||||
|
||||
// determines if self should generate a message
|
||||
struct StateNext {
|
||||
static constexpr const char* name {"StateNext"};
|
||||
|
||||
std::string prompt;
|
||||
std::vector<std::string> possible_names;
|
||||
std::vector<Contact3> possible_contacts;
|
||||
|
||||
std::future<int64_t> future;
|
||||
};
|
||||
|
||||
// generate message
|
||||
struct StateGenerateMsg {
|
||||
static constexpr const char* name {"StateGenerateMsg"};
|
||||
|
||||
std::string prompt;
|
||||
|
||||
// returns new line (single message)
|
||||
std::future<std::string> future;
|
||||
};
|
||||
|
||||
// look if it took too long/too many new messages came in
|
||||
// while also optionally sleeping to make message appear not too fast
|
||||
// HACK: skip, just send for now
|
||||
struct StateTimingCheck {
|
||||
static constexpr const char* name {"StateTimingCheck"};
|
||||
int tmp;
|
||||
};
|
||||
|
||||
template<>
|
||||
void RPBot::stateTransition(const Contact3 c, const StateIdle& from, StateNext& to) {
|
||||
// collect promp
|
||||
@ -71,23 +36,25 @@ void RPBot::stateTransition(const Contact3 c, const StateIdle& from, StateNext&
|
||||
|
||||
std::cout << "prompt for next: '" << to.prompt << "'\n";
|
||||
|
||||
{ // get set of possible usernames
|
||||
int64_t self {-1};
|
||||
{ // get set of possible usernames (even if forced, just to make sure)
|
||||
// copy mpb.names (contains string views, needs copies)
|
||||
for (const auto& [name_c, name] : mpb.names) {
|
||||
if (_cr.all_of<Contact::Components::ConnectionState>(name_c)) {
|
||||
if (_cr.all_of<Contact::Components::TagSelfStrong>(name_c)) {
|
||||
self = to.possible_contacts.size();
|
||||
to.possible_names.push_back(std::string{name});
|
||||
to.possible_contacts.push_back(name_c);
|
||||
} else if (_cr.all_of<Contact::Components::ConnectionState>(name_c)) {
|
||||
if (_cr.get<Contact::Components::ConnectionState>(name_c).state != Contact::Components::ConnectionState::disconnected) {
|
||||
// online
|
||||
to.possible_names.push_back(std::string{name});
|
||||
to.possible_contacts.push_back(name_c);
|
||||
}
|
||||
} else if (_cr.all_of<Contact::Components::TagSelfStrong>(name_c)) {
|
||||
to.possible_names.push_back(std::string{name});
|
||||
to.possible_contacts.push_back(name_c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ // launch async
|
||||
if (!from.force) { // launch async
|
||||
// copy names for string view param (lol)
|
||||
std::vector<std::string_view> pnames;
|
||||
for (const auto& n : to.possible_names) {
|
||||
@ -97,6 +64,11 @@ void RPBot::stateTransition(const Contact3 c, const StateIdle& from, StateNext&
|
||||
to.future = std::async(std::launch::async, [pnames, &to, this]() -> int64_t {
|
||||
return _completion.completeSelect(to.prompt, pnames);
|
||||
});
|
||||
} else {
|
||||
std::cout << "next forced with self " << self << "\n";
|
||||
// forced, we predict ourselfs
|
||||
// TODO: set without future?
|
||||
to.future = std::async(std::launch::async, [self]() -> int64_t { return self; });
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,13 +107,25 @@ RPBot::RPBot(
|
||||
) : _completion(completion), _conf(conf), _cr(cr), _rmm(rmm), _mcd(mcd) {
|
||||
//system_prompt = R"sys(Transcript of a group chat, where Bob talks to online strangers.
|
||||
//)sys";
|
||||
system_prompt = "Transcript of a group chat, where ";
|
||||
// TODO: name is chat specific, leave system prompt a template
|
||||
std::string self_name {"Bob"};
|
||||
if (_conf.has_string("tox", "name")) {
|
||||
system_prompt += _conf.get_string("tox", "name").value();
|
||||
} else {
|
||||
system_prompt += std::string{"Bob"};
|
||||
self_name = _conf.get_string("tox", "name").value();
|
||||
}
|
||||
system_prompt += std::string{" talks to online strangers.\n"};
|
||||
|
||||
#if 1
|
||||
system_prompt = "Transcript of a group chat, where ";
|
||||
system_prompt += self_name;
|
||||
system_prompt += " talks to online strangers. ";
|
||||
system_prompt += self_name;
|
||||
system_prompt += " is creative and curious. ";
|
||||
system_prompt += self_name;
|
||||
system_prompt += " is precise in its writing, but with occasional typos.";
|
||||
#else
|
||||
#include "./test_system_prompt1.inl"
|
||||
#endif
|
||||
|
||||
system_prompt += "\n"; // last entry
|
||||
|
||||
registerCommands();
|
||||
}
|
||||
@ -157,96 +141,19 @@ float RPBot::tick(float time_delta) {
|
||||
return min_tick_interval;
|
||||
}
|
||||
|
||||
void RPBot::registerCommands(void) {
|
||||
if (_mcd == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
_mcd->registerCommand(
|
||||
"RPBot", "rpbot",
|
||||
"start",
|
||||
[this](std::string_view params, Message3Handle m) -> bool {
|
||||
const auto contact_from = m.get<Message::Components::ContactFrom>().c;
|
||||
const auto contact_to = m.get<Message::Components::ContactTo>().c;
|
||||
|
||||
if (params.empty()) {
|
||||
// contact_to should be the contact this is for
|
||||
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNext, StateTimingCheck>(contact_to)) {
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"error: already running"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNext, StateTimingCheck>(contact_from)) {
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"error: already running"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_cr.all_of<Contact::Components::ParentOf>(contact_to)) {
|
||||
// group
|
||||
auto& new_state = _cr.emplace<StateIdle>(contact_to);
|
||||
new_state.timeout = 10.f;
|
||||
} else {
|
||||
// pm
|
||||
auto& new_state = _cr.emplace<StateIdle>(contact_from);
|
||||
new_state.timeout = 10.f;
|
||||
}
|
||||
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"RPBot started"
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
// id in params
|
||||
if (params.size() % 2 != 0) {
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"malformed hex id"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto id_bin = hex2bin(params);
|
||||
|
||||
auto view = _cr.view<Contact::Components::ID>();
|
||||
for (auto it = view.begin(), it_end = view.end(); it != it_end; it++) {
|
||||
if (view.get<Contact::Components::ID>(*it).data == id_bin) {
|
||||
auto& new_state = _cr.emplace<StateIdle>(*it);
|
||||
new_state.timeout = 10.f;
|
||||
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"RPBot started"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"no contact found for id"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
"Start RPBot in current contact.",
|
||||
MessageCommandDispatcher::Perms::ADMIN // TODO: should proably be MODERATOR
|
||||
);
|
||||
|
||||
std::cout << "RPBot: registered commands\n";
|
||||
}
|
||||
|
||||
float RPBot::doAllIdle(float time_delta) {
|
||||
float min_tick_interval = std::numeric_limits<float>::max();
|
||||
std::vector<Contact3> to_remove_stateidle;
|
||||
auto view = _cr.view<StateIdle>();
|
||||
|
||||
view.each([this, time_delta, &to_remove_stateidle, &min_tick_interval](const Contact3 c, StateIdle& state) {
|
||||
if (_cr.all_of<TagStopRPBot>(c)) {
|
||||
// marked for deletion, in idle (here) we remove them without adding next state
|
||||
to_remove_stateidle.push_back(c);
|
||||
_cr.remove<TagStopRPBot>(c);
|
||||
return;
|
||||
}
|
||||
|
||||
state.timeout -= time_delta;
|
||||
if (state.timeout <= 0.f) {
|
||||
std::cout << "RPBot: idle timed out\n";
|
||||
|
249
src/solanaceae/rpbot/rpbot_commands.cpp
Normal file
249
src/solanaceae/rpbot/rpbot_commands.cpp
Normal file
@ -0,0 +1,249 @@
|
||||
#include "./rpbot.hpp"
|
||||
|
||||
#include "./rpbot_states.hpp"
|
||||
|
||||
#include <solanaceae/contact/components.hpp>
|
||||
#include <solanaceae/message3/components.hpp>
|
||||
#include <solanaceae/util/utils.hpp>
|
||||
|
||||
void RPBot::registerCommands(void) {
|
||||
if (_mcd == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
_mcd->registerCommand(
|
||||
"RPBot", "rpbot",
|
||||
"start",
|
||||
[this](std::string_view params, Message3Handle m) -> bool {
|
||||
const auto contact_from = m.get<Message::Components::ContactFrom>().c;
|
||||
const auto contact_to = m.get<Message::Components::ContactTo>().c;
|
||||
|
||||
if (params.empty()) {
|
||||
// contact_to should be the contact this is for
|
||||
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNext, StateTimingCheck>(contact_to)) {
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"error: already running"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNext, StateTimingCheck>(contact_from)) {
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"error: already running"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_cr.all_of<Contact::Components::ParentOf>(contact_to)) {
|
||||
// group
|
||||
auto& new_state = _cr.emplace<StateIdle>(contact_to);
|
||||
new_state.timeout = 10.f;
|
||||
} else {
|
||||
// pm
|
||||
auto& new_state = _cr.emplace<StateIdle>(contact_from);
|
||||
new_state.timeout = 10.f;
|
||||
}
|
||||
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"RPBot started"
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
// id in params
|
||||
if (params.size() % 2 != 0) {
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"malformed hex id"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto id_bin = hex2bin(params);
|
||||
|
||||
auto view = _cr.view<Contact::Components::ID>();
|
||||
for (auto it = view.begin(), it_end = view.end(); it != it_end; it++) {
|
||||
if (view.get<Contact::Components::ID>(*it).data == id_bin) {
|
||||
auto& new_state = _cr.emplace<StateIdle>(*it);
|
||||
new_state.timeout = 10.f;
|
||||
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"RPBot started"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"no contact found for id"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
"Start RPBot in current contact.",
|
||||
MessageCommandDispatcher::Perms::ADMIN // TODO: should proably be MODERATOR
|
||||
);
|
||||
|
||||
_mcd->registerCommand(
|
||||
"RPBot", "rpbot",
|
||||
"stop",
|
||||
[this](std::string_view params, Message3Handle m) -> bool {
|
||||
const auto contact_from = m.get<Message::Components::ContactFrom>().c;
|
||||
const auto contact_to = m.get<Message::Components::ContactTo>().c;
|
||||
|
||||
if (params.empty()) {
|
||||
// contact_to should be the contact this is for
|
||||
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNext, StateTimingCheck>(contact_to)) {
|
||||
_cr.emplace_or_replace<TagStopRPBot>(contact_to);
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"stopped"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNext, StateTimingCheck>(contact_from)) {
|
||||
_cr.emplace_or_replace<TagStopRPBot>(contact_from);
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"stopped"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"error: not running"
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
// id in params
|
||||
if (params.size() % 2 != 0) {
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"malformed hex id"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto id_bin = hex2bin(params);
|
||||
|
||||
auto view = _cr.view<Contact::Components::ID>();
|
||||
for (auto it = view.begin(), it_end = view.end(); it != it_end; it++) {
|
||||
if (view.get<Contact::Components::ID>(*it).data == id_bin) {
|
||||
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNext, StateTimingCheck>(*it)) {
|
||||
_cr.emplace_or_replace<TagStopRPBot>(*it);
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"stopped"
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"error: not running"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"no contact found for id"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
"Stop RPBot in current or id contact.",
|
||||
MessageCommandDispatcher::Perms::ADMIN // TODO: should proably be MODERATOR
|
||||
);
|
||||
|
||||
_mcd->registerCommand(
|
||||
"RPBot", "rpbot",
|
||||
"force",
|
||||
[this](std::string_view params, Message3Handle m) -> bool {
|
||||
const auto contact_from = m.get<Message::Components::ContactFrom>().c;
|
||||
const auto contact_to = m.get<Message::Components::ContactTo>().c;
|
||||
|
||||
if (params.empty()) {
|
||||
// contact_to should be the contact this is for
|
||||
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNext, StateTimingCheck>(contact_to)) {
|
||||
if (_cr.all_of<StateIdle>(contact_to)) {
|
||||
_cr.get<StateIdle>(contact_to).force = true;
|
||||
_cr.get<StateIdle>(contact_to).timeout = 2.f;
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"forced its hand"
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNext, StateTimingCheck>(contact_from)) {
|
||||
if (_cr.all_of<StateIdle>(contact_from)) {
|
||||
_cr.get<StateIdle>(contact_from).force = true;
|
||||
_cr.get<StateIdle>(contact_from).timeout = 2.f;
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"forced its hand"
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"error: not running"
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
// id in params
|
||||
if (params.size() % 2 != 0) {
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"malformed hex id"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto id_bin = hex2bin(params);
|
||||
|
||||
auto view = _cr.view<Contact::Components::ID>();
|
||||
for (auto it = view.begin(), it_end = view.end(); it != it_end; it++) {
|
||||
if (view.get<Contact::Components::ID>(*it).data == id_bin) {
|
||||
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNext, StateTimingCheck>(*it)) {
|
||||
if (_cr.all_of<StateIdle>(*it)) {
|
||||
_cr.get<StateIdle>(*it).force = true;
|
||||
_cr.get<StateIdle>(*it).timeout = 2.f;
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"forced its hand"
|
||||
);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"error: not running"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_rmm.sendText(
|
||||
contact_from,
|
||||
"no contact found for id"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
"force it to generate a message",
|
||||
MessageCommandDispatcher::Perms::ADMIN // TODO: should proably be MODERATOR
|
||||
);
|
||||
|
||||
std::cout << "RPBot: registered commands\n";
|
||||
}
|
||||
|
47
src/solanaceae/rpbot/rpbot_states.hpp
Normal file
47
src/solanaceae/rpbot/rpbot_states.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "./rpbot.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <future>
|
||||
#include <cstdint>
|
||||
|
||||
struct TagStopRPBot {};
|
||||
|
||||
// sleeps until onMsg or onTimer
|
||||
struct StateIdle {
|
||||
static constexpr const char* name {"StateIdle"};
|
||||
float timeout {0.f};
|
||||
bool force {false};
|
||||
};
|
||||
|
||||
// determines if self should generate a message
|
||||
struct StateNext {
|
||||
static constexpr const char* name {"StateNext"};
|
||||
|
||||
std::string prompt;
|
||||
std::vector<std::string> possible_names;
|
||||
std::vector<Contact3> possible_contacts;
|
||||
|
||||
std::future<int64_t> future;
|
||||
};
|
||||
|
||||
// generate message
|
||||
struct StateGenerateMsg {
|
||||
static constexpr const char* name {"StateGenerateMsg"};
|
||||
|
||||
std::string prompt;
|
||||
|
||||
// returns new line (single message)
|
||||
std::future<std::string> future;
|
||||
};
|
||||
|
||||
// look if it took too long/too many new messages came in
|
||||
// while also optionally sleeping to make message appear not too fast
|
||||
// HACK: skip, just send for now
|
||||
struct StateTimingCheck {
|
||||
static constexpr const char* name {"StateTimingCheck"};
|
||||
int tmp;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user