Compare commits

...

7 Commits

Author SHA1 Message Date
7e1b424adb make contact store version visible 2025-03-10 20:58:24 +01:00
f321cea6eb port to contact4 2025-03-10 16:45:16 +01:00
998ba002e4 update cpp-httplib to v0.19.0 2025-03-10 16:43:23 +01:00
48b4e6ae95 Create LICENSE 2025-02-07 12:32:24 +01:00
31855cd1b1 use sr 2024-10-25 12:54:04 +02:00
301900c507 update to rmmi 2024-10-06 11:46:15 +02:00
55e3a8a6f8 forgotten wip changes 2024-10-06 11:44:19 +02:00
12 changed files with 160 additions and 116 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Erik Scholz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -53,7 +53,7 @@ endif()
if (NOT TARGET httplib::httplib) if (NOT TARGET httplib::httplib)
FetchContent_Declare(httplib FetchContent_Declare(httplib
GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git
GIT_TAG v0.15.3 GIT_TAG v0.19.0
EXCLUDE_FROM_ALL EXCLUDE_FROM_ALL
) )
FetchContent_MakeAvailable(httplib) FetchContent_MakeAvailable(httplib)

View File

@ -1,6 +1,7 @@
#include <solanaceae/plugin/solana_plugin_v1.h> #include <solanaceae/plugin/solana_plugin_v1.h>
#include <solanaceae/util/config_model.hpp> #include <solanaceae/util/config_model.hpp>
#include <solanaceae/contact/contact_store_i.hpp>
#include <solanaceae/llama-cpp-web/text_completion_interface.hpp> #include <solanaceae/llama-cpp-web/text_completion_interface.hpp>
#include <solanaceae/rpbot/rpbot.hpp> #include <solanaceae/rpbot/rpbot.hpp>
#include <solanaceae/message3/message_command_dispatcher.hpp> #include <solanaceae/message3/message_command_dispatcher.hpp>
@ -36,13 +37,13 @@ SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_start(struct SolanaAPI* solana_api)
try { try {
auto* completion = PLUG_RESOLVE_INSTANCE(TextCompletionI); auto* completion = PLUG_RESOLVE_INSTANCE(TextCompletionI);
auto* conf = PLUG_RESOLVE_INSTANCE(ConfigModelI); auto* conf = PLUG_RESOLVE_INSTANCE(ConfigModelI);
auto* cr = PLUG_RESOLVE_INSTANCE_VERSIONED(Contact3Registry, "1"); auto* cs = PLUG_RESOLVE_INSTANCE(ContactStore4I);
auto* rmm = PLUG_RESOLVE_INSTANCE(RegistryMessageModel); auto* rmm = PLUG_RESOLVE_INSTANCE(RegistryMessageModelI);
auto* mcd = PLUG_RESOLVE_INSTANCE(MessageCommandDispatcher); auto* mcd = PLUG_RESOLVE_INSTANCE(MessageCommandDispatcher);
// static store, could be anywhere tho // static store, could be anywhere tho
// construct with fetched dependencies // construct with fetched dependencies
g_rpbot = std::make_unique<RPBot>(*completion, *conf, *cr, *rmm, mcd); g_rpbot = std::make_unique<RPBot>(*completion, *conf, *cs, *rmm, mcd);
// register types // register types
PLUG_PROVIDE_INSTANCE(RPBot, plugin_name, g_rpbot.get()); PLUG_PROVIDE_INSTANCE(RPBot, plugin_name, g_rpbot.get());

View File

@ -77,7 +77,7 @@ int64_t LlamaCppWeb::completeSelect(const std::string_view prompt, const std::ve
} }
//grammar += ")"; //grammar += ")";
//std::cout << "generated grammar:\n" << grammar << "\n"; std::cerr << "generated grammar:\n" << grammar << "\n";
auto ret = complete(nlohmann::json{ auto ret = complete(nlohmann::json{
{"prompt", prompt}, {"prompt", prompt},
@ -89,19 +89,23 @@ int64_t LlamaCppWeb::completeSelect(const std::string_view prompt, const std::ve
{"top_p", 1.0}, // disable {"top_p", 1.0}, // disable
{"n_predict", 256}, // unlikely to ever be so high {"n_predict", 256}, // unlikely to ever be so high
{"seed", _rng()}, {"seed", _rng()},
{"ignore_eos", true},
{"cache_prompt", static_cast<bool>(_use_server_cache)}, {"cache_prompt", static_cast<bool>(_use_server_cache)},
}); });
if (ret.empty()) { if (ret.empty()) {
assert("ret empty" && false);
return -2; return -2;
} }
if (!ret.count("content")) { if (!ret.count("content")) {
assert("no content" && false);
return -3; return -3;
} }
std::string selected = ret.at("content"); std::string selected = ret.at("content");
if (selected.empty()) { if (selected.empty()) {
assert("content empty" && false);
return -4; return -4;
} }
@ -111,6 +115,7 @@ int64_t LlamaCppWeb::completeSelect(const std::string_view prompt, const std::ve
} }
} }
std::cerr << "content does not contain match\n";
std::cerr << "complete failed j:'" << ret.dump() << "'\n"; std::cerr << "complete failed j:'" << ret.dump() << "'\n";
return -5; return -5;
} }
@ -125,7 +130,7 @@ std::string LlamaCppWeb::completeLine(const std::string_view prompt) {
{"top_p", 1.0}, // disable {"top_p", 1.0}, // disable
{"n_predict", 400}, {"n_predict", 400},
{"seed", _rng()}, {"seed", _rng()},
{"stop", {"\n"}}, {"stop", nlohmann::json::array({"\n"})},
{"cache_prompt", static_cast<bool>(_use_server_cache)}, {"cache_prompt", static_cast<bool>(_use_server_cache)},
}); });
@ -147,7 +152,7 @@ nlohmann::json LlamaCppWeb::complete(const nlohmann::json& request_j) {
// steaming instead would be better // steaming instead would be better
_cli.set_read_timeout(std::chrono::minutes(10)); _cli.set_read_timeout(std::chrono::minutes(10));
//std::cout << "j dump: '" << request_j.dump(-1, ' ', true) << "'\n"; std::cerr << "j dump: '" << request_j.dump(-1, ' ', true) << "'\n";
auto res = _cli.Post("/completion", request_j.dump(-1, ' ', true), "application/json"); auto res = _cli.Post("/completion", request_j.dump(-1, ' ', true), "application/json");
@ -159,7 +164,7 @@ nlohmann::json LlamaCppWeb::complete(const nlohmann::json& request_j) {
//res->body.empty() || //res->body.empty() ||
//res->get_header_value("Content-Type") != "application/json" //res->get_header_value("Content-Type") != "application/json"
) { ) {
std::cerr << "error posting\n"; std::cerr << "error posting: '" << res->body << "'\n";
return {}; return {};
} }

View File

@ -19,7 +19,7 @@ struct LlamaCppWeb : public TextCompletionI {
// this is a bad idea // this is a bad idea
static std::minstd_rand thread_local _rng; static std::minstd_rand thread_local _rng;
std::atomic<bool> _use_server_cache {true}; std::atomic<bool> _use_server_cache {false};
LlamaCppWeb( LlamaCppWeb(
ConfigModelI& conf ConfigModelI& conf

View File

@ -2,30 +2,32 @@
#include "./rpbot.hpp" #include "./rpbot.hpp"
#include <solanaceae/contact/contact_store_i.hpp>
#include <solanaceae/contact/components.hpp> #include <solanaceae/contact/components.hpp>
#include <solanaceae/message3/components.hpp> #include <solanaceae/message3/components.hpp>
bool MessagePromptBuilder::buildNameLookup(void) { bool MessagePromptBuilder::buildNameLookup(void) {
if (_cr.all_of<Contact::Components::ParentOf>(_c)) { // group rpbot const auto& cr = _cs.registry();
const auto& subs = _cr.get<Contact::Components::ParentOf>(_c).subs; if (cr.all_of<Contact::Components::ParentOf>(_c)) { // group rpbot
const auto& subs = cr.get<Contact::Components::ParentOf>(_c).subs;
// should include self // should include self
for (const auto sub_c : subs) { for (const auto sub_c : subs) {
if (_cr.all_of<Contact::Components::Name>(sub_c)) { if (cr.all_of<Contact::Components::Name>(sub_c)) {
names[sub_c] = _cr.get<Contact::Components::Name>(sub_c).name; names[sub_c] = cr.get<Contact::Components::Name>(sub_c).name;
} }
} }
} else { // pm rpbot } else { // pm rpbot
if (_cr.all_of<Contact::Components::Name>(_c)) { if (cr.all_of<Contact::Components::Name>(_c)) {
names[_c] = _cr.get<Contact::Components::Name>(_c).name; names[_c] = cr.get<Contact::Components::Name>(_c).name;
} else { } else {
std::cerr << "RPBot error: other missing name\n"; std::cerr << "RPBot error: other missing name\n";
return false; return false;
} }
if (_cr.all_of<Contact::Components::Self>(_c)) { if (cr.all_of<Contact::Components::Self>(_c)) {
const auto self = _cr.get<Contact::Components::Self>(_c).self; const auto self = cr.get<Contact::Components::Self>(_c).self;
if (_cr.all_of<Contact::Components::Name>(self)) { if (cr.all_of<Contact::Components::Name>(self)) {
names[self] = _cr.get<Contact::Components::Name>(self).name; names[self] = cr.get<Contact::Components::Name>(self).name;
} else { } else {
std::cerr << "RPBot error: self missing name\n"; std::cerr << "RPBot error: self missing name\n";
return false; return false;
@ -87,7 +89,7 @@ std::string MessagePromptBuilder::buildPromptMessage(const Message3Handle m) {
} }
std::string MessagePromptBuilder::promptMessagePrefixSimple(const Message3Handle m) { std::string MessagePromptBuilder::promptMessagePrefixSimple(const Message3Handle m) {
const Contact3 from = m.get<Message::Components::ContactFrom>().c; const Contact4 from = m.get<Message::Components::ContactFrom>().c;
if (names.count(from)) { if (names.count(from)) {
return std::string{names[from]}; return std::string{names[from]};
} else { } else {
@ -98,8 +100,8 @@ std::string MessagePromptBuilder::promptMessagePrefixSimple(const Message3Handle
std::string MessagePromptBuilder::promptMessagePrefixDirected(const Message3Handle m) { std::string MessagePromptBuilder::promptMessagePrefixDirected(const Message3Handle m) {
// with both contacts (eg: "Name1 to Name2"; or "Name1 to Everyone" // with both contacts (eg: "Name1 to Name2"; or "Name1 to Everyone"
const Contact3 from = m.get<Message::Components::ContactFrom>().c; const Contact4 from = m.get<Message::Components::ContactFrom>().c;
const Contact3 to = m.get<Message::Components::ContactTo>().c; const Contact4 to = m.get<Message::Components::ContactTo>().c;
std::string res; std::string res;

View File

@ -1,19 +1,19 @@
#pragma once #pragma once
#include <solanaceae/util/config_model.hpp> #include <solanaceae/util/config_model.hpp>
#include <solanaceae/contact/contact_model3.hpp> #include <solanaceae/contact/fwd.hpp>
#include <solanaceae/message3/registry_message_model.hpp> #include <solanaceae/message3/registry_message_model.hpp>
#include <entt/container/dense_map.hpp> #include <entt/container/dense_map.hpp>
// TODO: improve caching // TODO: improve caching
struct MessagePromptBuilder { struct MessagePromptBuilder {
Contact3Registry& _cr; ContactStore4I& _cs;
const Contact3 _c; const Contact4 _c;
RegistryMessageModel& _rmm; RegistryMessageModelI& _rmm;
// lookup table, string_view since no name-components are changed // lookup table, string_view since no name-components are changed
entt::dense_map<Contact3, std::string_view> names; entt::dense_map<Contact4, std::string_view> names;
bool buildNameLookup(void); bool buildNameLookup(void);

View File

@ -18,22 +18,24 @@
#include <cstdint> #include <cstdint>
template<> template<>
void RPBot::stateTransition(const Contact3 c, const StateIdle& from, StateNextActor& to) { void RPBot::stateTransition(const Contact4 c, const StateIdle& from, StateNextActor& to) {
// collect promp // collect promp
MessagePromptBuilder mpb{_cr, c, _rmm, {}}; MessagePromptBuilder mpb{_cs, c, _rmm, {}};
mpb.buildNameLookup(); mpb.buildNameLookup();
const auto& cr = _cs.registry();
int64_t self {-1}; int64_t self {-1};
{ // get set of possible usernames (even if forced, just to make sure) { // get set of possible usernames (even if forced, just to make sure)
// copy mpb.names (contains string views, needs copies) // copy mpb.names (contains string views, needs copies)
for (const auto& [name_c, name] : mpb.names) { for (const auto& [name_c, name] : mpb.names) {
if (_cr.all_of<Contact::Components::TagSelfStrong>(name_c)) { if (cr.all_of<Contact::Components::TagSelfStrong>(name_c)) {
self = to.possible_contacts.size(); self = to.possible_contacts.size();
to.possible_names.push_back(std::string{name}); to.possible_names.push_back(std::string{name});
to.possible_contacts.push_back(name_c); to.possible_contacts.push_back(name_c);
} else if (_cr.all_of<Contact::Components::ConnectionState>(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) { if (cr.get<Contact::Components::ConnectionState>(name_c).state != Contact::Components::ConnectionState::disconnected) {
// online // online
to.possible_names.push_back(std::string{name}); to.possible_names.push_back(std::string{name});
to.possible_contacts.push_back(name_c); to.possible_contacts.push_back(name_c);
@ -55,7 +57,7 @@ void RPBot::stateTransition(const Contact3 c, const StateIdle& from, StateNextAc
} }
{ // - system promp (needs self name etc) { // - system promp (needs self name etc)
if (const auto* id_comp = _cr.try_get<Contact::Components::ID>(c); id_comp != nullptr) { if (const auto* id_comp = cr.try_get<Contact::Components::ID>(c); id_comp != nullptr) {
const auto id_hex = bin2hex(id_comp->data); const auto id_hex = bin2hex(id_comp->data);
to.prompt = _conf.get_string("RPBot", "system_prompt", id_hex).value(); to.prompt = _conf.get_string("RPBot", "system_prompt", id_hex).value();
} else { } else {
@ -109,17 +111,18 @@ void RPBot::stateTransition(const Contact3 c, const StateIdle& from, StateNextAc
} }
template<> template<>
void RPBot::stateTransition(const Contact3, const StateNextActor&, StateIdle& to) { void RPBot::stateTransition(const Contact4, const StateNextActor&, StateIdle& to) {
to.timeout = std::uniform_real_distribution<>{30.f, 5.f*60.f}(_rng); to.timeout = std::uniform_real_distribution<>{30.f, 5.f*60.f}(_rng);
} }
template<> template<>
void RPBot::stateTransition(const Contact3 c, const StateNextActor& from, StateGenerateMsg& to) { void RPBot::stateTransition(const Contact4 c, const StateNextActor& from, StateGenerateMsg& to) {
const auto& cr = _cs.registry();
to.prompt = from.prompt; // TODO: move from? to.prompt = from.prompt; // TODO: move from?
assert(_cr.all_of<Contact::Components::Self>(c)); assert(cr.all_of<Contact::Components::Self>(c));
const Contact3 self = _cr.get<Contact::Components::Self>(c).self; const Contact4 self = cr.get<Contact::Components::Self>(c).self;
to.prompt += _cr.get<Contact::Components::Name>(self).name + ":"; // TODO: remove space to.prompt += cr.get<Contact::Components::Name>(self).name + ":"; // TODO: remove space
{ // launch async { // launch async
to.future = std::async(std::launch::async, [&to, this]() -> std::string { to.future = std::async(std::launch::async, [&to, this]() -> std::string {
@ -129,7 +132,7 @@ void RPBot::stateTransition(const Contact3 c, const StateNextActor& from, StateG
} }
template<> template<>
void RPBot::stateTransition(const Contact3, const StateGenerateMsg&, StateIdle& to) { void RPBot::stateTransition(const Contact4, const StateGenerateMsg&, StateIdle& to) {
// relativly slow delay for multi line messages // relativly slow delay for multi line messages
to.timeout = std::uniform_real_distribution<>{2.f, 15.f}(_rng); to.timeout = std::uniform_real_distribution<>{2.f, 15.f}(_rng);
} }
@ -137,10 +140,10 @@ void RPBot::stateTransition(const Contact3, const StateGenerateMsg&, StateIdle&
RPBot::RPBot( RPBot::RPBot(
TextCompletionI& completion, TextCompletionI& completion,
ConfigModelI& conf, ConfigModelI& conf,
Contact3Registry& cr, ContactStore4I& cs,
RegistryMessageModel& rmm, RegistryMessageModelI& rmm,
MessageCommandDispatcher* mcd MessageCommandDispatcher* mcd
) : _completion(completion), _conf(conf), _cr(cr), _rmm(rmm), _mcd(mcd) { ) : _completion(completion), _conf(conf), _cs(cs), _rmm(rmm), _rmm_sr(_rmm.newSubRef(this)), _mcd(mcd) {
//system_prompt = R"sys(Transcript of a group chat, where Bob talks to online strangers. //system_prompt = R"sys(Transcript of a group chat, where Bob talks to online strangers.
//)sys"; //)sys";
@ -157,7 +160,7 @@ R"sys(Transcript of a group chat, where {self_name} talks to online strangers.
registerCommands(); registerCommands();
_rmm.subscribe(this, RegistryMessageModel_Event::message_construct); _rmm_sr.subscribe(RegistryMessageModel_Event::message_construct);
} }
float RPBot::tick(float time_delta) { float RPBot::tick(float time_delta) {
@ -172,15 +175,16 @@ float RPBot::tick(float time_delta) {
} }
float RPBot::doAllIdle(float time_delta) { float RPBot::doAllIdle(float time_delta) {
auto& cr = _cs.registry();
float min_tick_interval = std::numeric_limits<float>::max(); float min_tick_interval = std::numeric_limits<float>::max();
std::vector<Contact3> to_remove_stateidle; std::vector<Contact4> to_remove_stateidle;
auto view = _cr.view<StateIdle>(); auto view = cr.view<StateIdle>();
view.each([this, time_delta, &to_remove_stateidle, &min_tick_interval](const Contact3 c, StateIdle& state) { view.each([this, &cr, time_delta, &to_remove_stateidle, &min_tick_interval](const Contact4 c, StateIdle& state) {
if (_cr.all_of<TagStopRPBot>(c)) { if (cr.all_of<TagStopRPBot>(c)) {
// marked for deletion, in idle (here) we remove them without adding next state // marked for deletion, in idle (here) we remove them without adding next state
to_remove_stateidle.push_back(c); to_remove_stateidle.push_back(c);
_cr.remove<TagStopRPBot>(c); cr.remove<TagStopRPBot>(c);
return; return;
} }
@ -204,7 +208,7 @@ float RPBot::doAllIdle(float time_delta) {
auto view_it = tmp_view.begin(), view_last = tmp_view.end(); auto view_it = tmp_view.begin(), view_last = tmp_view.end();
for (size_t i = 0; i < max_cont_messages && view_it != view_last; view_it++, i++) { for (size_t i = 0; i < max_cont_messages && view_it != view_last; view_it++, i++) {
// TODO: also test for weak self? // TODO: also test for weak self?
if (!_cr.any_of<Contact::Components::TagSelfStrong>(tmp_view.get<Message::Components::ContactFrom>(*view_it).c)) { if (!cr.any_of<Contact::Components::TagSelfStrong>(tmp_view.get<Message::Components::ContactFrom>(*view_it).c)) {
other_sender = true; other_sender = true;
break; break;
} }
@ -215,7 +219,7 @@ float RPBot::doAllIdle(float time_delta) {
min_tick_interval = 0.1f; min_tick_interval = 0.1f;
// transition to Next // transition to Next
emplaceStateTransition<StateNextActor>(_cr, c, state); emplaceStateTransition<StateNextActor>(_cs, c, state);
return; return;
} }
} }
@ -232,16 +236,17 @@ float RPBot::doAllIdle(float time_delta) {
} }
}); });
_cr.remove<StateIdle>(to_remove_stateidle.cbegin(), to_remove_stateidle.cend()); cr.remove<StateIdle>(to_remove_stateidle.cbegin(), to_remove_stateidle.cend());
return min_tick_interval; return min_tick_interval;
} }
float RPBot::doAllNext(float) { float RPBot::doAllNext(float) {
auto& cr = _cs.registry();
float min_tick_interval = std::numeric_limits<float>::max(); float min_tick_interval = std::numeric_limits<float>::max();
std::vector<Contact3> to_remove; std::vector<Contact4> to_remove;
auto view = _cr.view<StateNextActor>(); auto view = cr.view<StateNextActor>();
view.each([this, &to_remove, &min_tick_interval](const Contact3 c, StateNextActor& state) { view.each([this, &cr, &to_remove, &min_tick_interval](const Contact4 c, StateNextActor& state) {
// TODO: how to timeout? // TODO: how to timeout?
if (state.future.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) { if (state.future.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) {
to_remove.push_back(c); to_remove.push_back(c);
@ -251,9 +256,9 @@ float RPBot::doAllNext(float) {
const auto selected = state.future.get(); const auto selected = state.future.get();
if (selected >= 0 && size_t(selected) < state.possible_names.size()) { if (selected >= 0 && size_t(selected) < state.possible_names.size()) {
std::cout << "next is " << state.possible_names.at(selected) << "(" << selected << ")\n"; std::cout << "next is " << state.possible_names.at(selected) << "(" << selected << ")\n";
if (_cr.all_of<Contact::Components::TagSelfStrong>(state.possible_contacts.at(selected))) { if (cr.all_of<Contact::Components::TagSelfStrong>(state.possible_contacts.at(selected))) {
// we predicted ourselfs // we predicted ourselfs
emplaceStateTransition<StateGenerateMsg>(_cr, c, state); emplaceStateTransition<StateGenerateMsg>(_cs, c, state);
return; return;
} }
} else { } else {
@ -261,21 +266,22 @@ float RPBot::doAllNext(float) {
} }
// transition to Idle // transition to Idle
emplaceStateTransition<StateIdle>(_cr, c, state); emplaceStateTransition<StateIdle>(_cs, c, state);
} }
}); });
_cr.remove<StateNextActor>(to_remove.cbegin(), to_remove.cend()); cr.remove<StateNextActor>(to_remove.cbegin(), to_remove.cend());
return min_tick_interval; return min_tick_interval;
} }
float RPBot::doAllGenerateMsg(float) { float RPBot::doAllGenerateMsg(float) {
auto& cr = _cs.registry();
float min_tick_interval = std::numeric_limits<float>::max(); float min_tick_interval = std::numeric_limits<float>::max();
std::vector<Contact3> to_remove; std::vector<Contact4> to_remove;
auto view = _cr.view<StateGenerateMsg>(); auto view = cr.view<StateGenerateMsg>();
_cr.remove<StateGenerateMsg>(to_remove.cbegin(), to_remove.cend()); cr.remove<StateGenerateMsg>(to_remove.cbegin(), to_remove.cend());
view.each([this, &to_remove, &min_tick_interval](const Contact3 c, StateGenerateMsg& state) { view.each([this, &to_remove, &min_tick_interval](const Contact4 c, StateGenerateMsg& state) {
// TODO: how to timeout? // TODO: how to timeout?
if (state.future.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) { if (state.future.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) {
to_remove.push_back(c); to_remove.push_back(c);
@ -293,11 +299,11 @@ float RPBot::doAllGenerateMsg(float) {
// TODO: timing check? // TODO: timing check?
// transition to Idle // transition to Idle
emplaceStateTransition<StateIdle>(_cr, c, state); emplaceStateTransition<StateIdle>(_cs, c, state);
} }
}); });
_cr.remove<StateGenerateMsg>(to_remove.cbegin(), to_remove.cend()); cr.remove<StateGenerateMsg>(to_remove.cbegin(), to_remove.cend());
return min_tick_interval; return min_tick_interval;
} }
@ -311,39 +317,41 @@ bool RPBot::onEvent(const Message::Events::MessageConstruct& e) {
return false; return false;
} }
auto& cr = _cs.registry();
const auto contact_to = e.e.get<Message::Components::ContactTo>().c; const auto contact_to = e.e.get<Message::Components::ContactTo>().c;
const auto contact_from = e.e.get<Message::Components::ContactFrom>().c; const auto contact_from = e.e.get<Message::Components::ContactFrom>().c;
if (!_cr.valid(contact_to) || !_cr.valid(contact_from)) { if (!cr.valid(contact_to) || !cr.valid(contact_from)) {
std::cerr << "RPBot error: invalid contact in message\n"; std::cerr << "RPBot error: invalid contact in message\n";
return false; return false;
} }
if (_cr.any_of<Contact::Components::TagSelfStrong>(contact_from)) { if (cr.any_of<Contact::Components::TagSelfStrong>(contact_from)) {
return false; // ignore own messages return false; // ignore own messages
} }
Contact3 rpbot_contact = entt::null; Contact4 rpbot_contact = entt::null;
// check ContactTo (public) // check ContactTo (public)
// check ContactTo parent (group private) // check ContactTo parent (group private)
// check ContactFrom (private) // check ContactFrom (private)
if (_cr.any_of<StateIdle, StateNextActor, StateGenerateMsg, StateTimingCheck>(contact_to)) { if (cr.any_of<StateIdle, StateNextActor, StateGenerateMsg, StateTimingCheck>(contact_to)) {
rpbot_contact = contact_to; rpbot_contact = contact_to;
} else if (_cr.all_of<Contact::Components::Parent>(contact_to)) { } else if (cr.all_of<Contact::Components::Parent>(contact_to)) {
rpbot_contact = _cr.get<Contact::Components::Parent>(contact_to).parent; rpbot_contact = cr.get<Contact::Components::Parent>(contact_to).parent;
} else if (_cr.any_of<StateIdle, StateNextActor, StateGenerateMsg, StateTimingCheck>(contact_from)) { } else if (cr.any_of<StateIdle, StateNextActor, StateGenerateMsg, StateTimingCheck>(contact_from)) {
rpbot_contact = contact_from; rpbot_contact = contact_from;
} else { } else {
return false; // not a rpbot related message return false; // not a rpbot related message
} }
if (!_cr.all_of<StateIdle>(rpbot_contact)) { if (!cr.all_of<StateIdle>(rpbot_contact)) {
return false; // not idle return false; // not idle
} }
auto& timeout = _cr.get<StateIdle>(rpbot_contact).timeout; auto& timeout = cr.get<StateIdle>(rpbot_contact).timeout;
// TODO: config with id // TODO: config with id
timeout = std::clamp<float>( timeout = std::clamp<float>(
timeout, timeout,

View File

@ -2,7 +2,7 @@
#include <solanaceae/util/config_model.hpp> #include <solanaceae/util/config_model.hpp>
#include <solanaceae/llama-cpp-web/text_completion_interface.hpp> #include <solanaceae/llama-cpp-web/text_completion_interface.hpp>
#include <solanaceae/contact/contact_model3.hpp> #include <solanaceae/contact/contact_store_i.hpp>
#include <solanaceae/message3/registry_message_model.hpp> #include <solanaceae/message3/registry_message_model.hpp>
#include <solanaceae/message3/message_command_dispatcher.hpp> #include <solanaceae/message3/message_command_dispatcher.hpp>
@ -19,8 +19,9 @@ struct StateTimingCheck;
struct RPBot : public RegistryMessageModelEventI { struct RPBot : public RegistryMessageModelEventI {
TextCompletionI& _completion; TextCompletionI& _completion;
ConfigModelI& _conf; ConfigModelI& _conf;
Contact3Registry& _cr; ContactStore4I& _cs;
RegistryMessageModel& _rmm; RegistryMessageModelI& _rmm;
RegistryMessageModelI::SubscriptionReference _rmm_sr;
MessageCommandDispatcher* _mcd; MessageCommandDispatcher* _mcd;
std::minstd_rand _rng{std::random_device{}()}; std::minstd_rand _rng{std::random_device{}()};
@ -29,8 +30,8 @@ struct RPBot : public RegistryMessageModelEventI {
RPBot( RPBot(
TextCompletionI& completion, TextCompletionI& completion,
ConfigModelI& conf, ConfigModelI& conf,
Contact3Registry& cr, ContactStore4I& cs,
RegistryMessageModel& rmm, RegistryMessageModelI& rmm,
MessageCommandDispatcher* mcd MessageCommandDispatcher* mcd
); );
@ -41,13 +42,13 @@ struct RPBot : public RegistryMessageModelEventI {
protected: // state transitions protected: // state transitions
// all transitions need to be explicitly declared // all transitions need to be explicitly declared
template<typename To, typename From> template<typename To, typename From>
void stateTransition(const Contact3 c, const From& from, To& to) = delete; void stateTransition(const Contact4 c, const From& from, To& to) = delete;
// reg helper // reg helper
template<typename To, typename From> template<typename To, typename From>
To& emplaceStateTransition(Contact3Registry& cr, Contact3 c, const From& state) { To& emplaceStateTransition(ContactStore4I& cs, Contact4 c, const From& state) {
std::cout << "RPBot: transition from " << From::name << " to " << To::name << "\n"; std::cout << "RPBot: transition from " << From::name << " to " << To::name << "\n";
To& to = cr.emplace_or_replace<To>(c); To& to = cs.registry().emplace_or_replace<To>(c);
stateTransition<To>(c, state, to); stateTransition<To>(c, state, to);
return to; return to;
} }

View File

@ -18,16 +18,18 @@ void RPBot::registerCommands(void) {
const auto contact_from = m.get<Message::Components::ContactFrom>().c; const auto contact_from = m.get<Message::Components::ContactFrom>().c;
const auto contact_to = m.get<Message::Components::ContactTo>().c; const auto contact_to = m.get<Message::Components::ContactTo>().c;
auto& cr = _cs.registry();
if (params.empty()) { if (params.empty()) {
// contact_to should be the contact this is for // contact_to should be the contact this is for
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNextActor, StateTimingCheck>(contact_to)) { if (cr.any_of<StateIdle, StateGenerateMsg, StateNextActor, StateTimingCheck>(contact_to)) {
_rmm.sendText( _rmm.sendText(
contact_from, contact_from,
"error: already running" "error: already running"
); );
return true; return true;
} }
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNextActor, StateTimingCheck>(contact_from)) { if (cr.any_of<StateIdle, StateGenerateMsg, StateNextActor, StateTimingCheck>(contact_from)) {
_rmm.sendText( _rmm.sendText(
contact_from, contact_from,
"error: already running" "error: already running"
@ -35,13 +37,13 @@ void RPBot::registerCommands(void) {
return true; return true;
} }
if (_cr.all_of<Contact::Components::ParentOf>(contact_to)) { if (cr.all_of<Contact::Components::ParentOf>(contact_to)) {
// group // group
auto& new_state = _cr.emplace<StateIdle>(contact_to); auto& new_state = cr.emplace<StateIdle>(contact_to);
new_state.timeout = 10.f; new_state.timeout = 10.f;
} else { } else {
// pm // pm
auto& new_state = _cr.emplace<StateIdle>(contact_from); auto& new_state = cr.emplace<StateIdle>(contact_from);
new_state.timeout = 10.f; new_state.timeout = 10.f;
} }
@ -62,10 +64,10 @@ void RPBot::registerCommands(void) {
auto id_bin = hex2bin(params); auto id_bin = hex2bin(params);
auto view = _cr.view<Contact::Components::ID>(); auto view = cr.view<Contact::Components::ID>();
for (auto it = view.begin(), it_end = view.end(); it != it_end; it++) { for (auto it = view.begin(), it_end = view.end(); it != it_end; it++) {
if (view.get<Contact::Components::ID>(*it).data == id_bin) { if (view.get<Contact::Components::ID>(*it).data == id_bin) {
if (_cr.any_of<StateIdle, StateNextActor, StateGenerateMsg, StateTimingCheck>(*it)) { if (cr.any_of<StateIdle, StateNextActor, StateGenerateMsg, StateTimingCheck>(*it)) {
_rmm.sendText( _rmm.sendText(
contact_from, contact_from,
"RPBot already running" "RPBot already running"
@ -73,7 +75,7 @@ void RPBot::registerCommands(void) {
return true; return true;
} }
auto& new_state = _cr.emplace<StateIdle>(*it); auto& new_state = cr.emplace<StateIdle>(*it);
new_state.timeout = 10.f; new_state.timeout = 10.f;
_rmm.sendText( _rmm.sendText(
@ -102,18 +104,20 @@ void RPBot::registerCommands(void) {
const auto contact_from = m.get<Message::Components::ContactFrom>().c; const auto contact_from = m.get<Message::Components::ContactFrom>().c;
const auto contact_to = m.get<Message::Components::ContactTo>().c; const auto contact_to = m.get<Message::Components::ContactTo>().c;
auto& cr = _cs.registry();
if (params.empty()) { if (params.empty()) {
// contact_to should be the contact this is for // contact_to should be the contact this is for
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNextActor, StateTimingCheck>(contact_to)) { if (cr.any_of<StateIdle, StateGenerateMsg, StateNextActor, StateTimingCheck>(contact_to)) {
_cr.emplace_or_replace<TagStopRPBot>(contact_to); cr.emplace_or_replace<TagStopRPBot>(contact_to);
_rmm.sendText( _rmm.sendText(
contact_from, contact_from,
"stopped" "stopped"
); );
return true; return true;
} }
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNextActor, StateTimingCheck>(contact_from)) { if (cr.any_of<StateIdle, StateGenerateMsg, StateNextActor, StateTimingCheck>(contact_from)) {
_cr.emplace_or_replace<TagStopRPBot>(contact_from); cr.emplace_or_replace<TagStopRPBot>(contact_from);
_rmm.sendText( _rmm.sendText(
contact_from, contact_from,
"stopped" "stopped"
@ -138,11 +142,11 @@ void RPBot::registerCommands(void) {
auto id_bin = hex2bin(params); auto id_bin = hex2bin(params);
auto view = _cr.view<Contact::Components::ID>(); auto view = cr.view<Contact::Components::ID>();
for (auto it = view.begin(), it_end = view.end(); it != it_end; it++) { for (auto it = view.begin(), it_end = view.end(); it != it_end; it++) {
if (view.get<Contact::Components::ID>(*it).data == id_bin) { if (view.get<Contact::Components::ID>(*it).data == id_bin) {
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNextActor, StateTimingCheck>(*it)) { if (cr.any_of<StateIdle, StateGenerateMsg, StateNextActor, StateTimingCheck>(*it)) {
_cr.emplace_or_replace<TagStopRPBot>(*it); cr.emplace_or_replace<TagStopRPBot>(*it);
_rmm.sendText( _rmm.sendText(
contact_from, contact_from,
"stopped" "stopped"
@ -176,12 +180,14 @@ void RPBot::registerCommands(void) {
const auto contact_from = m.get<Message::Components::ContactFrom>().c; const auto contact_from = m.get<Message::Components::ContactFrom>().c;
const auto contact_to = m.get<Message::Components::ContactTo>().c; const auto contact_to = m.get<Message::Components::ContactTo>().c;
auto& cr = _cs.registry();
if (params.empty()) { if (params.empty()) {
// contact_to should be the contact this is for // contact_to should be the contact this is for
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNextActor, StateTimingCheck>(contact_to)) { if (cr.any_of<StateIdle, StateGenerateMsg, StateNextActor, StateTimingCheck>(contact_to)) {
if (_cr.all_of<StateIdle>(contact_to)) { if (cr.all_of<StateIdle>(contact_to)) {
_cr.get<StateIdle>(contact_to).force = true; cr.get<StateIdle>(contact_to).force = true;
_cr.get<StateIdle>(contact_to).timeout = 2.f; cr.get<StateIdle>(contact_to).timeout = 2.f;
_rmm.sendText( _rmm.sendText(
contact_from, contact_from,
"forced its hand" "forced its hand"
@ -189,10 +195,10 @@ void RPBot::registerCommands(void) {
} }
return true; return true;
} }
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNextActor, StateTimingCheck>(contact_from)) { if (cr.any_of<StateIdle, StateGenerateMsg, StateNextActor, StateTimingCheck>(contact_from)) {
if (_cr.all_of<StateIdle>(contact_from)) { if (cr.all_of<StateIdle>(contact_from)) {
_cr.get<StateIdle>(contact_from).force = true; cr.get<StateIdle>(contact_from).force = true;
_cr.get<StateIdle>(contact_from).timeout = 2.f; cr.get<StateIdle>(contact_from).timeout = 2.f;
_rmm.sendText( _rmm.sendText(
contact_from, contact_from,
"forced its hand" "forced its hand"
@ -218,13 +224,13 @@ void RPBot::registerCommands(void) {
auto id_bin = hex2bin(params); auto id_bin = hex2bin(params);
auto view = _cr.view<Contact::Components::ID>(); auto view = cr.view<Contact::Components::ID>();
for (auto it = view.begin(), it_end = view.end(); it != it_end; it++) { for (auto it = view.begin(), it_end = view.end(); it != it_end; it++) {
if (view.get<Contact::Components::ID>(*it).data == id_bin) { if (view.get<Contact::Components::ID>(*it).data == id_bin) {
if (_cr.any_of<StateIdle, StateGenerateMsg, StateNextActor, StateTimingCheck>(*it)) { if (cr.any_of<StateIdle, StateGenerateMsg, StateNextActor, StateTimingCheck>(*it)) {
if (_cr.all_of<StateIdle>(*it)) { if (cr.all_of<StateIdle>(*it)) {
_cr.get<StateIdle>(*it).force = true; cr.get<StateIdle>(*it).force = true;
_cr.get<StateIdle>(*it).timeout = 2.f; cr.get<StateIdle>(*it).timeout = 2.f;
_rmm.sendText( _rmm.sendText(
contact_from, contact_from,
"forced its hand" "forced its hand"

View File

@ -22,7 +22,7 @@ struct StateNextActor {
std::string prompt; std::string prompt;
std::vector<std::string> possible_names; std::vector<std::string> possible_names;
std::vector<Contact3> possible_contacts; std::vector<Contact4> possible_contacts;
std::future<int64_t> future; std::future<int64_t> future;
}; };

View File

@ -20,7 +20,7 @@ int main(void) {
} }
std::cerr << lcw._cli.host() << " " << lcw._cli.port() << " endpoint healthy\n"; std::cerr << lcw._cli.host() << " " << lcw._cli.port() << " endpoint healthy\n";
std::cout << "The meaning of life is to" std::cerr << "The meaning of life is to"
<< lcw.complete(nlohmann::json{ << lcw.complete(nlohmann::json{
{"prompt", "The meaning of life is to"}, {"prompt", "The meaning of life is to"},
{"min_p", 0.1}, // model dependent {"min_p", 0.1}, // model dependent
@ -34,9 +34,9 @@ int main(void) {
}) })
<< "\n"; << "\n";
std::cout << "-------------------------\n"; std::cerr << "-------------------------\n";
std::cout << "complete from select:\n"; std::cerr << "complete from select:\n";
std::vector<std::string_view> possible { std::vector<std::string_view> possible {
" die", " die",
" die.", " die.",
@ -46,12 +46,12 @@ int main(void) {
" Hi", " Hi",
}; };
for (size_t i = 0; i < 10; i++) { for (size_t i = 0; i < 10; i++) {
std::cout << "The meaning of life is to"; std::cerr << "The meaning of life is to";
auto res = lcw.completeSelect("The meaning of life is to", possible); auto res = lcw.completeSelect("The meaning of life is to", possible);
if (res < 0) { if (res < 0) {
std::cout << " error--\n"; std::cerr << " error\n";
} else { } else {
std::cout << possible[res] << "\n"; std::cerr << possible[res] << "\n";
} }
} }