|
|
|
|
@ -94,7 +94,7 @@
|
|
|
|
|
*
|
|
|
|
|
* The fields that make up a given chat-handshake-template-standard include
|
|
|
|
|
*
|
|
|
|
|
* * global-> begin & end
|
|
|
|
|
* * global -> begin & end
|
|
|
|
|
*
|
|
|
|
|
* * system -> begin, prefix, suffix & end
|
|
|
|
|
*
|
|
|
|
|
@ -281,19 +281,21 @@ public:
|
|
|
|
|
return typeid(*this).name();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void dump() {
|
|
|
|
|
std::string dump(const std::string &msgTag) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
std::string me = name() + ":" + __func__;
|
|
|
|
|
LOGXLN("INFO:%s:NumTypes:%zu", me.c_str(), types.length());
|
|
|
|
|
LOGXLN("INFO:%s:NumParts:%zu", me.c_str(), parts.size());
|
|
|
|
|
LOGXLN("INFO:%s:StrLength:%zu", me.c_str(), str().length());
|
|
|
|
|
ss << msgTag << ":NumTypes:" << types.length() << std::endl;
|
|
|
|
|
ss << msgTag << ":NumParts:" << parts.size() << std::endl;
|
|
|
|
|
ss << msgTag << ":StrLength:" << str().length() << std::endl;
|
|
|
|
|
if (parts.size() != types.length()) {
|
|
|
|
|
LOG_TEELN("DBUG:%s:Mismatch between parts and types", me.c_str());
|
|
|
|
|
LOG_TEELN("DBUG:%s:Mismatch between parts[%zu] and types[%zu]", me.c_str(), parts.size(), types.length());
|
|
|
|
|
}
|
|
|
|
|
int i = 0;
|
|
|
|
|
for(auto part: parts) {
|
|
|
|
|
LOGXLN("INFO:%s:%c:%s", me.c_str(), types[i], part.c_str());
|
|
|
|
|
ss << msgTag << ":Part:" << i << ":" << types[i] << ":" << part << std::endl;
|
|
|
|
|
i += 1;
|
|
|
|
|
}
|
|
|
|
|
return ss.str();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
@ -305,6 +307,213 @@ public:
|
|
|
|
|
|
|
|
|
|
ChatTemplates(GroupKVMapMapVariant defaultMap) : GroupKV(defaultMap) {}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if the specified chat-template exists or not.
|
|
|
|
|
* NOTE: This doesnt cross check, if the template inturn contains all the required fields or not.
|
|
|
|
|
*/
|
|
|
|
|
bool tmpl_exists(const std::string &tmpl) {
|
|
|
|
|
if (!group_exists(tmpl)) {
|
|
|
|
|
LOG_TEELN("WARN:CT:%s: tmpl[%s] not found...", __func__, tmpl.c_str());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if all expected keys/fields are present wrt the specified chat-template.
|
|
|
|
|
* If any key/field is missing, expect a exception.
|
|
|
|
|
*
|
|
|
|
|
* Additionally also return a string containing info about all the fields.
|
|
|
|
|
*/
|
|
|
|
|
bool tmpl_basiccheck(const std::string &tmpl, std::stringstream &ss, const std::string &msgTag) {
|
|
|
|
|
|
|
|
|
|
if (!tmpl_exists(tmpl)) {
|
|
|
|
|
LOGXLN("ERRR:CT:%s:Specified template-id [%s] not found", msgTag.c_str(), tmpl.c_str());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string globalBegin = get_value<std::string>(tmpl, { K_GLOBAL, K_BEGIN });
|
|
|
|
|
std::string globalEnd = get_value<std::string>(tmpl, { K_GLOBAL, K_END });
|
|
|
|
|
|
|
|
|
|
std::string systemBegin = get_value<std::string>(tmpl, { K_SYSTEM, K_BEGIN });
|
|
|
|
|
std::string systemPrefix = get_value<std::string>(tmpl, { K_SYSTEM, K_PREFIX });
|
|
|
|
|
std::string systemSuffix = get_value<std::string>(tmpl, { K_SYSTEM, K_SUFFIX });
|
|
|
|
|
std::string systemEnd = get_value<std::string>(tmpl, { K_SYSTEM, K_END });
|
|
|
|
|
|
|
|
|
|
std::string userBegin = get_value<std::string>(tmpl, { K_USER, K_BEGIN });
|
|
|
|
|
std::string userPrefix = get_value<std::string>(tmpl, { K_USER, K_PREFIX });
|
|
|
|
|
std::string userSuffix = get_value<std::string>(tmpl, { K_USER, K_SUFFIX });
|
|
|
|
|
std::string userEnd = get_value<std::string>(tmpl, { K_USER, K_END });
|
|
|
|
|
|
|
|
|
|
std::string assistantBegin = get_value<std::string>(tmpl, { K_ASSISTANT, K_BEGIN });
|
|
|
|
|
std::string assistantPrefix = get_value<std::string>(tmpl, { K_ASSISTANT, K_PREFIX });
|
|
|
|
|
std::string assistantSuffix = get_value<std::string>(tmpl, { K_ASSISTANT, K_SUFFIX });
|
|
|
|
|
std::string assistantEnd = get_value<std::string>(tmpl, { K_ASSISTANT, K_END });
|
|
|
|
|
|
|
|
|
|
std::string reversePrompt = get_value<std::string>(tmpl, { K_REVERSE_PROMPT });
|
|
|
|
|
|
|
|
|
|
bool systemHasSuffix = get_value<bool>(tmpl, { K_SYSTEMUSER_SYSTEM_HAS_SUFFIX });
|
|
|
|
|
bool systemHasEnd = get_value<bool>(tmpl, { K_SYSTEMUSER_SYSTEM_HAS_END });
|
|
|
|
|
bool userHasBegin = get_value<bool>(tmpl, { K_SYSTEMUSER_1ST_USER_HAS_BEGIN });
|
|
|
|
|
bool userHasPrefix = get_value<bool>(tmpl, { K_SYSTEMUSER_1ST_USER_HAS_PREFIX });
|
|
|
|
|
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << "global-begin" << ":" << globalBegin << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << "global-end" << ":" << globalEnd << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << "system-begin" << ":" << systemBegin << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << "system-prefix" << ":" << systemPrefix << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << "system-suffix" << ":" << systemSuffix << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << "system-end" << ":" << systemEnd << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << "user-begin" << ":" << userBegin << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << "user-prefix" << ":" << userPrefix << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << "user-suffix" << ":" << userSuffix << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << "user-end" << ":" << userEnd << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << "assistant-begin" << ":" << assistantBegin << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << "assistant-prefix" << ":" << assistantPrefix << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << "assistant-suffix" << ":" << assistantSuffix << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << "assistant-end" << ":" << assistantEnd << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << K_REVERSE_PROMPT << ":" << reversePrompt << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << K_SYSTEMUSER_SYSTEM_HAS_SUFFIX << ":" << systemHasSuffix << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << K_SYSTEMUSER_SYSTEM_HAS_END << ":" << systemHasEnd << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << K_SYSTEMUSER_1ST_USER_HAS_BEGIN << ":" << userHasBegin << std::endl;
|
|
|
|
|
ss << msgTag << ":" + tmpl + ":" << K_SYSTEMUSER_1ST_USER_HAS_PREFIX << ":" << userHasPrefix << std::endl;
|
|
|
|
|
|
|
|
|
|
if (!userEnd.empty()) {
|
|
|
|
|
LOG_TEELN("WARN:CT:%s:User-End seems to be set to [%s], do cross check if this is proper and needed", msgTag.c_str(), userEnd.c_str());
|
|
|
|
|
}
|
|
|
|
|
if (!assistantBegin.empty()) {
|
|
|
|
|
LOG_TEELN("WARN:CT:%s:Assistant-Begin seems to be set to [%s], do cross check if this is proper and needed", msgTag.c_str(), assistantBegin.c_str());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* For the specified chat-template, get the value associated with the specified key/field.
|
|
|
|
|
*/
|
|
|
|
|
template <typename SupportedDataType>
|
|
|
|
|
SupportedDataType tmpl_getkey(const std::string &tmpl, const std::string &key, const SupportedDataType &defaultValue) {
|
|
|
|
|
return get_value(tmpl, {key}, defaultValue, "CTTmplGetKey");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* For the specified chat-template and the role within, cumulate the values of the specified keys/fields
|
|
|
|
|
* and return the same.
|
|
|
|
|
*/
|
|
|
|
|
std::string tmpl_role_getkeys(const std::string &tmpl, const std::string &role, const std::vector<std::string> &keys) {
|
|
|
|
|
std::string got = "";
|
|
|
|
|
std::string sKeys = "";
|
|
|
|
|
for(auto key: keys) {
|
|
|
|
|
got += get_value<std::string>(tmpl, {role, key}, "", "CTTmplRoleGetKeys");
|
|
|
|
|
sKeys += "+";
|
|
|
|
|
sKeys += key;
|
|
|
|
|
}
|
|
|
|
|
LDBUG_LN("DBUG:CT:%s:%s:%s:%s:%s", __func__, tmpl.c_str(), role.c_str(), sKeys.c_str(), got.c_str());
|
|
|
|
|
return got;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Given the template standard and a bunch of messages including their roles, this returns
|
|
|
|
|
* tagged messages, subPartsTypes string and subPartsLens vector. The returned subParts
|
|
|
|
|
* types string and lens vector help identify the parts of the tagged msgs string,
|
|
|
|
|
* which relate to passed msgs and added tags.
|
|
|
|
|
*
|
|
|
|
|
* * a string containing the tagged messages
|
|
|
|
|
* * global-begin + 1 or more [[role-begin] + [role-prefix] + msg + [role-suffix] +[role-end]] + global-end
|
|
|
|
|
* * a string where the chars contain info about
|
|
|
|
|
* type of sub-strings/parts that make up the tagged messages string.
|
|
|
|
|
* * a vector of ints, which give the length of each part in the tagged messages string.
|
|
|
|
|
*
|
|
|
|
|
* If a combination of system-user messages is passed, then tags between the 1st system and
|
|
|
|
|
* the 1st user message, is based on the flags set wrt the corresponding template standard.
|
|
|
|
|
* If you dont want this behaviour, pass non 0 values wrt the optional cntSystemMsgCnt and
|
|
|
|
|
* cntUserMsgCnt arguments.
|
|
|
|
|
*/
|
|
|
|
|
bool chaton_tmpl_apply_ex(
|
|
|
|
|
const std::string &tmpl,
|
|
|
|
|
const std::vector<const llama_chat_message *> &msgs,
|
|
|
|
|
bool alertAssistantAtEnd,
|
|
|
|
|
bool applyGlobalIfAny,
|
|
|
|
|
std::string &tagged,
|
|
|
|
|
std::string &types,
|
|
|
|
|
std::vector<int32_t> &lens,
|
|
|
|
|
int curSystemMsgCnt = 0,
|
|
|
|
|
int curUserMsgCnt = 0
|
|
|
|
|
) {
|
|
|
|
|
if (!tmpl_exists(tmpl)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
ChatParts cp = {};
|
|
|
|
|
if (applyGlobalIfAny) {
|
|
|
|
|
std::string globalBegin = tmpl_role_getkeys(tmpl, K_GLOBAL, {K_BEGIN});
|
|
|
|
|
cp.add_part(ChatParts::S, globalBegin);
|
|
|
|
|
}
|
|
|
|
|
int cntSystem = curSystemMsgCnt;
|
|
|
|
|
int cntUser = curUserMsgCnt;
|
|
|
|
|
int cntOthers = 0;
|
|
|
|
|
for(const auto msg: msgs) {
|
|
|
|
|
auto role = msg->role;
|
|
|
|
|
auto content = msg->content;
|
|
|
|
|
std::string begin = tmpl_role_getkeys(tmpl, role, {K_BEGIN});
|
|
|
|
|
auto prefix = tmpl_role_getkeys(tmpl, role, {K_PREFIX});
|
|
|
|
|
auto suffix = tmpl_role_getkeys(tmpl, role, {K_SUFFIX});
|
|
|
|
|
auto end = tmpl_role_getkeys(tmpl, role, {K_END});
|
|
|
|
|
if (role == K_SYSTEM) {
|
|
|
|
|
cntSystem += 1;
|
|
|
|
|
cp.add_part(ChatParts::S, begin);
|
|
|
|
|
cp.add_part(ChatParts::S, prefix);
|
|
|
|
|
} else if (role == K_USER) {
|
|
|
|
|
cntUser += 1;
|
|
|
|
|
if ((cntSystem == 1) && (cntUser == 1)) {
|
|
|
|
|
if (tmpl_getkey(tmpl, K_SYSTEMUSER_1ST_USER_HAS_BEGIN, true)) {
|
|
|
|
|
cp.add_part(ChatParts::S, begin);
|
|
|
|
|
}
|
|
|
|
|
if (tmpl_getkey(tmpl, K_SYSTEMUSER_1ST_USER_HAS_PREFIX, true)) {
|
|
|
|
|
cp.add_part(ChatParts::S, prefix);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cp.add_part(ChatParts::S, begin);
|
|
|
|
|
cp.add_part(ChatParts::S, prefix);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cntOthers += 1;
|
|
|
|
|
cp.add_part(ChatParts::S, begin);
|
|
|
|
|
cp.add_part(ChatParts::S, prefix);
|
|
|
|
|
}
|
|
|
|
|
cp.add_part(ChatParts::N, content);
|
|
|
|
|
if (role == K_SYSTEM) {
|
|
|
|
|
if (cntSystem == 1) {
|
|
|
|
|
if (tmpl_getkey(tmpl, K_SYSTEMUSER_SYSTEM_HAS_SUFFIX, true)) {
|
|
|
|
|
cp.add_part(ChatParts::S, suffix);
|
|
|
|
|
}
|
|
|
|
|
if (tmpl_getkey(tmpl, K_SYSTEMUSER_SYSTEM_HAS_END, true)) {
|
|
|
|
|
cp.add_part(ChatParts::S, end);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cp.add_part(ChatParts::S, suffix);
|
|
|
|
|
cp.add_part(ChatParts::S, end);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cp.add_part(ChatParts::S, suffix);
|
|
|
|
|
cp.add_part(ChatParts::S, end);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (alertAssistantAtEnd) {
|
|
|
|
|
auto assistantBeginPrefix = tmpl_role_getkeys(tmpl, K_ASSISTANT, {K_BEGIN, K_PREFIX});
|
|
|
|
|
cp.add_part(ChatParts::S, assistantBeginPrefix);
|
|
|
|
|
}
|
|
|
|
|
if (applyGlobalIfAny) {
|
|
|
|
|
auto globalEnd = tmpl_role_getkeys(tmpl, K_GLOBAL, {K_END});
|
|
|
|
|
cp.add_part(ChatParts::S, globalEnd);
|
|
|
|
|
}
|
|
|
|
|
LDBUG_LN("DBUG:CT:%s", cp.dump("INFO:ChatOnTmplApplyEx").c_str());
|
|
|
|
|
tagged = cp.str();
|
|
|
|
|
LOGLN("DBUG:CT:%s:%s:%s", __func__, tmpl.c_str(), tagged.c_str());
|
|
|
|
|
LOGLN("DBUG:CT:%s:%s:CntSys[%d]:CntUsr[%d]:CntOthers[%d]", __func__, tmpl.c_str(), cntSystem, cntUser, cntOthers);
|
|
|
|
|
types = cp.get_partstypes();
|
|
|
|
|
lens = cp.get_partslens();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#include "chaton_meta.hpp"
|
|
|
|
|
@ -313,7 +522,7 @@ public:
|
|
|
|
|
#ifdef CHATON_JSON
|
|
|
|
|
|
|
|
|
|
template <typename SupportedType>
|
|
|
|
|
inline SupportedType json_get(json &j, std::vector<std::string> keys, const std::string &msgTag) {
|
|
|
|
|
inline SupportedType json_get(json &j, const std::vector<std::string_view> &keys, const std::string &msgTag) {
|
|
|
|
|
json curJ = j;
|
|
|
|
|
std::stringstream skey;
|
|
|
|
|
int i = 0;
|
|
|
|
|
@ -325,7 +534,7 @@ inline SupportedType json_get(json &j, std::vector<std::string> keys, const std:
|
|
|
|
|
curJ = curJ[key];
|
|
|
|
|
} else {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "ERRR:ChatON:" << __func__ << ":" << msgTag << ":Key [" << skey.str() << "] is missing";
|
|
|
|
|
ss << "ERRR:ChatON:" << __func__ << ":" << msgTag << ":KeyChain [" << skey.str() << "] is missing";
|
|
|
|
|
throw std::runtime_error(ss.str());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -394,203 +603,39 @@ inline bool chaton_meta_load(const std::string &fname) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline bool chaton_tmpl_exists(const std::string &tmpl) {
|
|
|
|
|
if (!gCT.group_exists(tmpl)) {
|
|
|
|
|
LOG_TEELN("WARN:%s: tmpl[%s] not found...", __func__, tmpl.c_str());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
return gCT.tmpl_exists(tmpl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline std::string chaton_tmpl_role_kv(const std::string &tmpl, const std::string &role, const std::vector<std::string> &keys) {
|
|
|
|
|
std::string got = "";
|
|
|
|
|
std::string sKeys = "";
|
|
|
|
|
for(auto key: keys) {
|
|
|
|
|
got += gCT.get_value<std::string>(tmpl, {role, key}, "");
|
|
|
|
|
sKeys += "+";
|
|
|
|
|
sKeys += key;
|
|
|
|
|
}
|
|
|
|
|
LOGLN("DBUG:%s:%s:%s:%s:%s", __func__, tmpl.c_str(), role.c_str(), sKeys.c_str(), got.c_str());
|
|
|
|
|
return got;
|
|
|
|
|
inline std::string chaton_tmpl_role_getkeys(const std::string &tmpl, const std::string &role, const std::vector<std::string> &keys) {
|
|
|
|
|
return gCT.tmpl_role_getkeys(tmpl, role, keys);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline std::string chaton_tmpl_kv(const std::string &tmpl, const std::string &key) {
|
|
|
|
|
std::string got = gCT.get_value<std::string>(tmpl, {key}, "");
|
|
|
|
|
LOGLN("DBUG:%s:%s:%s:%s", __func__, tmpl.c_str(), key.c_str(), got.c_str());
|
|
|
|
|
return got;
|
|
|
|
|
inline std::string chaton_tmpl_getkey_str(const std::string &tmpl, const std::string &key) {
|
|
|
|
|
return gCT.tmpl_getkey<std::string>(tmpl, {key}, "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool chaton_tmpl_kv_bool(const std::string &tmpl, const std::string &key) {
|
|
|
|
|
bool got = gCT.get_value(tmpl, {key}, false);
|
|
|
|
|
LOGLN("DBUG:%s:%s:%s:%d", __func__, tmpl.c_str(), key.c_str(), got);
|
|
|
|
|
return got;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Given the template standard, role and a message, this returns
|
|
|
|
|
// a tagged message, types string and lens vector wrt the parts that make up the returned string
|
|
|
|
|
//
|
|
|
|
|
// * a string containing the tagged message
|
|
|
|
|
// * role-(begin+prefix) + msg + role-(suffix+end)
|
|
|
|
|
// * a string where the chars contain info about
|
|
|
|
|
// type of sub-strings/parts that make up the tagged message.
|
|
|
|
|
// * a vector of ints, which give the length of each part in the tagged message.
|
|
|
|
|
inline bool chaton_tmpl_apply_single_ex(
|
|
|
|
|
const std::string &tmpl,
|
|
|
|
|
const std::string &role,
|
|
|
|
|
const std::string &content,
|
|
|
|
|
std::string &tagged,
|
|
|
|
|
std::string &types,
|
|
|
|
|
std::vector<int32_t> &lens
|
|
|
|
|
) {
|
|
|
|
|
if (!chaton_tmpl_exists(tmpl)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
ChatParts cp = {};
|
|
|
|
|
std::string beginPrefix = chaton_tmpl_role_kv(tmpl, role, {K_BEGIN, K_PREFIX});
|
|
|
|
|
std::string suffixEnd = chaton_tmpl_role_kv(tmpl, role, {K_SUFFIX, K_END});
|
|
|
|
|
cp.add_part(ChatParts::S, beginPrefix);
|
|
|
|
|
cp.add_part(ChatParts::N, content);
|
|
|
|
|
cp.add_part(ChatParts::S, suffixEnd);
|
|
|
|
|
cp.dump();
|
|
|
|
|
tagged = cp.str();
|
|
|
|
|
LOGLN("DBUG:%s:%s:%s:%s", __func__, tmpl.c_str(), role.c_str(), tagged.c_str());
|
|
|
|
|
types = cp.get_partstypes();
|
|
|
|
|
lens = cp.get_partslens();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Given the template standard, role and a message, this returns the tagged message.
|
|
|
|
|
//
|
|
|
|
|
// * a string containing the tagged message
|
|
|
|
|
// * role-(begin+prefix) + msg + role-(suffix+end)
|
|
|
|
|
inline size_t chaton_tmpl_apply_single(
|
|
|
|
|
const std::string &tmpl,
|
|
|
|
|
const std::string &role,
|
|
|
|
|
const std::string &content,
|
|
|
|
|
std::string &tagged
|
|
|
|
|
) {
|
|
|
|
|
std::string types;
|
|
|
|
|
std::vector<int32_t> lens;
|
|
|
|
|
if (!chaton_tmpl_apply_single_ex(tmpl, role, content, tagged, types, lens)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return tagged.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Apply chat-handshake-template for the specified template standard and role.
|
|
|
|
|
* If the passed char array is smaller than that required for the tagged message,
|
|
|
|
|
* * part of the tagged message which fits within dest buffer is copied
|
|
|
|
|
* * the returned value, indicates the size of the actual tagged message
|
|
|
|
|
* NOTE:
|
|
|
|
|
* * ideally the passed char array should be able to fit the tagged message+0|null char.
|
|
|
|
|
* * if the return value from this function is larger than or equal to destLength,
|
|
|
|
|
* then you will have to increase the size of the dest buffer, and call this
|
|
|
|
|
* function a second time, to ensure that one gets the full tagged message.
|
|
|
|
|
*/
|
|
|
|
|
inline size_t chat_tmpl_apply_single_capi(
|
|
|
|
|
const char *tmpl,
|
|
|
|
|
const char *role,
|
|
|
|
|
const char *content,
|
|
|
|
|
char *dest,
|
|
|
|
|
const size_t destLength
|
|
|
|
|
) {
|
|
|
|
|
std::string tagged;
|
|
|
|
|
auto taggedLength = chaton_tmpl_apply_single(tmpl, role, content, tagged);
|
|
|
|
|
if (taggedLength <= 0) {
|
|
|
|
|
return taggedLength;
|
|
|
|
|
}
|
|
|
|
|
if (dest && (destLength > 0)) {
|
|
|
|
|
strlcpy(dest, tagged.c_str(), destLength);
|
|
|
|
|
}
|
|
|
|
|
return taggedLength;
|
|
|
|
|
inline bool chaton_tmpl_getkey_bool(const std::string &tmpl, const std::string &key) {
|
|
|
|
|
return gCT.tmpl_getkey<bool>(tmpl, {key}, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Given the template standard and a bunch of messages including their roles, this returns
|
|
|
|
|
// tagged messages, types string and lens vector. Returned types string and lens vector help
|
|
|
|
|
// identify the parts of the tagged msgs string, which relate to passed msgs and added tags.
|
|
|
|
|
// the tagged messages as a string.
|
|
|
|
|
// global-begin + 1 or more [[role-begin] + [role-prefix] + msg + [role-suffix] +[role-end]] + global-end
|
|
|
|
|
//
|
|
|
|
|
// * a string containing the tagged messages
|
|
|
|
|
// * global-begin + 1 or more [[role-begin] + [role-prefix] + msg + [role-suffix] +[role-end]] + global-end
|
|
|
|
|
// * a string where the chars contain info about
|
|
|
|
|
// type of sub-strings/parts that make up the tagged messages string.
|
|
|
|
|
// * a vector of ints, which give the length of each part in the tagged messages string.
|
|
|
|
|
//
|
|
|
|
|
// if a combination of system-user messages is passed, then tags between the system
|
|
|
|
|
// and the 1st user message, is based on the flags set wrt the corresponding template standard.
|
|
|
|
|
// Additionally also return info about the parts that make up the tagged message.
|
|
|
|
|
inline bool chaton_tmpl_apply_ex(
|
|
|
|
|
const std::string &tmpl,
|
|
|
|
|
const std::vector<const llama_chat_message *> &msgs,
|
|
|
|
|
bool alertAssistantAtEnd,
|
|
|
|
|
bool applyGlobalIfAny,
|
|
|
|
|
std::string &tagged,
|
|
|
|
|
std::string &types,
|
|
|
|
|
std::vector<int32_t> &lens
|
|
|
|
|
std::vector<int32_t> &lens,
|
|
|
|
|
int curSystemMsgCnt = 0,
|
|
|
|
|
int curUserMsgCnt = 0
|
|
|
|
|
) {
|
|
|
|
|
if (!chaton_tmpl_exists(tmpl)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
ChatParts cp = {};
|
|
|
|
|
std::string globalBegin = chaton_tmpl_role_kv(tmpl, K_GLOBAL, {K_BEGIN});
|
|
|
|
|
cp.add_part(ChatParts::S, globalBegin);
|
|
|
|
|
int cntSystem = 0;
|
|
|
|
|
int cntUser = 0;
|
|
|
|
|
int cntOthers = 0;
|
|
|
|
|
for(const auto msg: msgs) {
|
|
|
|
|
auto role = msg->role;
|
|
|
|
|
auto content = msg->content;
|
|
|
|
|
std::string begin = chaton_tmpl_role_kv(tmpl, role, {K_BEGIN});
|
|
|
|
|
auto prefix = chaton_tmpl_role_kv(tmpl, role, {K_PREFIX});
|
|
|
|
|
auto suffix = chaton_tmpl_role_kv(tmpl, role, {K_SUFFIX});
|
|
|
|
|
auto end = chaton_tmpl_role_kv(tmpl, role, {K_END});
|
|
|
|
|
if (role == K_SYSTEM) {
|
|
|
|
|
cntSystem += 1;
|
|
|
|
|
cp.add_part(ChatParts::S, begin);
|
|
|
|
|
cp.add_part(ChatParts::S, prefix);
|
|
|
|
|
} else if (role == K_USER) {
|
|
|
|
|
cntUser += 1;
|
|
|
|
|
if ((cntSystem == 1) && (cntUser == 1)) {
|
|
|
|
|
if (chaton_tmpl_kv_bool(tmpl, K_SYSTEMUSER_1ST_USER_HAS_BEGIN)) {
|
|
|
|
|
cp.add_part(ChatParts::S, begin);
|
|
|
|
|
}
|
|
|
|
|
if (chaton_tmpl_kv_bool(tmpl, K_SYSTEMUSER_1ST_USER_HAS_PREFIX)) {
|
|
|
|
|
cp.add_part(ChatParts::S, prefix);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cp.add_part(ChatParts::S, begin);
|
|
|
|
|
cp.add_part(ChatParts::S, prefix);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cntOthers += 1;
|
|
|
|
|
cp.add_part(ChatParts::S, begin);
|
|
|
|
|
cp.add_part(ChatParts::S, prefix);
|
|
|
|
|
}
|
|
|
|
|
cp.add_part(ChatParts::N, content);
|
|
|
|
|
if (role == K_SYSTEM) {
|
|
|
|
|
if (chaton_tmpl_kv_bool(tmpl, K_SYSTEMUSER_SYSTEM_HAS_SUFFIX)) {
|
|
|
|
|
cp.add_part(ChatParts::S, suffix);
|
|
|
|
|
}
|
|
|
|
|
if (chaton_tmpl_kv_bool(tmpl, K_SYSTEMUSER_SYSTEM_HAS_END)) {
|
|
|
|
|
cp.add_part(ChatParts::S, end);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cp.add_part(ChatParts::S, suffix);
|
|
|
|
|
cp.add_part(ChatParts::S, end);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (alertAssistantAtEnd) {
|
|
|
|
|
auto assistantBeginPrefix = chaton_tmpl_role_kv(tmpl, K_ASSISTANT, {K_BEGIN, K_PREFIX});
|
|
|
|
|
cp.add_part(ChatParts::S, assistantBeginPrefix);
|
|
|
|
|
}
|
|
|
|
|
auto globalEnd = chaton_tmpl_role_kv(tmpl, K_GLOBAL, {K_END});
|
|
|
|
|
cp.add_part(ChatParts::S, globalEnd);
|
|
|
|
|
cp.dump();
|
|
|
|
|
tagged = cp.str();
|
|
|
|
|
LOGLN("DBUG:%s:%s:%s", __func__, tmpl.c_str(), tagged.c_str());
|
|
|
|
|
LOGLN("DBUG:%s:%s:CntSys[%d]:CntUsr[%d]:CntOthers[%d]", __func__, tmpl.c_str(), cntSystem, cntUser, cntOthers);
|
|
|
|
|
types = cp.get_partstypes();
|
|
|
|
|
lens = cp.get_partslens();
|
|
|
|
|
return true;
|
|
|
|
|
return gCT.chaton_tmpl_apply_ex(tmpl, msgs, alertAssistantAtEnd, applyGlobalIfAny, tagged, types, lens, curSystemMsgCnt, curUserMsgCnt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Given the template standard and a bunch of messages including their roles, this returns
|
|
|
|
|
@ -600,11 +645,36 @@ inline int32_t chaton_tmpl_apply(
|
|
|
|
|
const std::string &tmpl,
|
|
|
|
|
const std::vector<const llama_chat_message *> &msgs,
|
|
|
|
|
bool alertAssistantAtEnd,
|
|
|
|
|
bool applyGlobalIfAny,
|
|
|
|
|
std::string &tagged
|
|
|
|
|
) {
|
|
|
|
|
std::string types;
|
|
|
|
|
std::vector<int32_t> lens;
|
|
|
|
|
if (!chaton_tmpl_apply_ex(tmpl, msgs, alertAssistantAtEnd, tagged, types, lens)) {
|
|
|
|
|
if (!chaton_tmpl_apply_ex(tmpl, msgs, alertAssistantAtEnd, applyGlobalIfAny, tagged, types, lens)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return tagged.size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int BYPASS_MSGCNT = 101;
|
|
|
|
|
//
|
|
|
|
|
// Given the template standard, role and a message, this creates the tagged message.
|
|
|
|
|
//
|
|
|
|
|
// string containing the tagged message
|
|
|
|
|
// * role-(begin+prefix) + msg + role-(suffix+end)
|
|
|
|
|
//
|
|
|
|
|
inline size_t chaton_tmpl_apply_single(
|
|
|
|
|
const std::string &tmpl,
|
|
|
|
|
const std::string &role,
|
|
|
|
|
const std::string &content,
|
|
|
|
|
bool alertAssistantAtEnd,
|
|
|
|
|
bool applyGlobalIfAny,
|
|
|
|
|
std::string &tagged
|
|
|
|
|
) {
|
|
|
|
|
std::string types;
|
|
|
|
|
std::vector<int32_t> lens;
|
|
|
|
|
llama_chat_message cm {role.c_str(), content.c_str()};
|
|
|
|
|
if (!chaton_tmpl_apply_ex(tmpl, {&cm}, alertAssistantAtEnd, applyGlobalIfAny, tagged, types, lens, BYPASS_MSGCNT, BYPASS_MSGCNT)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return tagged.size();
|
|
|
|
|
@ -639,7 +709,7 @@ inline int32_t chaton_tmpl_apply_capi(
|
|
|
|
|
vMsgs.push_back(&msgs[i]);
|
|
|
|
|
}
|
|
|
|
|
std::string taggedMsgs;
|
|
|
|
|
int32_t taggedLength = chaton_tmpl_apply(tmpl, vMsgs, alertAssistantAtEnd, taggedMsgs);
|
|
|
|
|
int32_t taggedLength = chaton_tmpl_apply(tmpl, vMsgs, alertAssistantAtEnd, true, taggedMsgs);
|
|
|
|
|
if (taggedLength < 0) {
|
|
|
|
|
return taggedLength;
|
|
|
|
|
}
|
|
|
|
|
@ -684,7 +754,7 @@ inline int32_t chaton_tmpl_apply_ex_capi(
|
|
|
|
|
std::string taggedMsgs;
|
|
|
|
|
std::string types;
|
|
|
|
|
std::vector<int32_t> lens;
|
|
|
|
|
if (!chaton_tmpl_apply_ex(tmpl, vMsgs, alertAssistantAtEnd, taggedMsgs, types, lens)) {
|
|
|
|
|
if (!chaton_tmpl_apply_ex(tmpl, vMsgs, alertAssistantAtEnd, true, taggedMsgs, types, lens)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
int32_t taggedLength = taggedMsgs.size();
|
|
|
|
|
@ -706,7 +776,7 @@ inline int32_t chaton_tmpl_apply_ex_capi(
|
|
|
|
|
return taggedLength;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copied from common.cpp
|
|
|
|
|
// Copied from common.cpp, updated wrt model and logging flow.
|
|
|
|
|
inline std::vector<llama_token> chaton_llama_tokenize(
|
|
|
|
|
const struct llama_model * model,
|
|
|
|
|
const std::string & text,
|
|
|
|
|
@ -761,74 +831,29 @@ inline std::vector<llama_token> chaton_llama_tokenize_ex(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* if tmpl is
|
|
|
|
|
* * empty string, then dump the full loaded chaton-meta
|
|
|
|
|
* * chaton-template-id, then dump contents related to that specific chat-handshake-template-standard
|
|
|
|
|
* NOTE: It uses the exception raising get_value to check if the tags related keys are present
|
|
|
|
|
* wrt the specified template-standard/model-id or not.
|
|
|
|
|
* Dump the full loaded chaton templates data
|
|
|
|
|
* Additionally if a chaton-template-id is specified
|
|
|
|
|
* dump contents related to that specific chat-handshake-template-standard
|
|
|
|
|
* NOTE: It uses the exception raising get_value to check if all the required
|
|
|
|
|
* keys/fields are present wrt the specified template-standard/model-id or not.
|
|
|
|
|
*/
|
|
|
|
|
inline bool _chaton_meta_dump(std::string &tmpl) {
|
|
|
|
|
if (!tmpl.empty()) {
|
|
|
|
|
if (!gCT.group_exists(tmpl)) {
|
|
|
|
|
LOGXLN("ERRR:%s:Specified template-id [%s] not found", __func__, tmpl.c_str());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
inline bool _chaton_meta_validate_dump(std::string &tmpl) {
|
|
|
|
|
LOGXLN("\n\nINFO:%s:%s:\n%s", __func__, tmpl.c_str(), gCT.dump(tmpl, "INFO:ChatOnMetaValidateDump:").c_str());
|
|
|
|
|
if (tmpl.empty()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
LOGXLN("\n\nINFO:%s:%s:\n%s", __func__, tmpl.c_str(), gCT.dump(tmpl, "INFO:ChatOnMetaDump:").c_str());
|
|
|
|
|
if (!tmpl.empty()) {
|
|
|
|
|
std::string globalBegin = gCT.get_value<std::string>(tmpl, { K_GLOBAL, K_BEGIN });
|
|
|
|
|
std::string globalEnd = gCT.get_value<std::string>(tmpl, { K_GLOBAL, K_END });
|
|
|
|
|
std::string systemBegin = gCT.get_value<std::string>(tmpl, { K_SYSTEM, K_BEGIN });
|
|
|
|
|
std::string systemPrefix = gCT.get_value<std::string>(tmpl, { K_SYSTEM, K_PREFIX });
|
|
|
|
|
std::string systemSuffix = gCT.get_value<std::string>(tmpl, { K_SYSTEM, K_SUFFIX });
|
|
|
|
|
std::string systemEnd = gCT.get_value<std::string>(tmpl, { K_SYSTEM, K_END });
|
|
|
|
|
std::string userBegin = gCT.get_value<std::string>(tmpl, { K_USER, K_BEGIN });
|
|
|
|
|
std::string userPrefix = gCT.get_value<std::string>(tmpl, { K_USER, K_PREFIX });
|
|
|
|
|
std::string userSuffix = gCT.get_value<std::string>(tmpl, { K_USER, K_SUFFIX });
|
|
|
|
|
std::string userEnd = gCT.get_value<std::string>(tmpl, { K_USER, K_END });
|
|
|
|
|
std::string assistantBegin = gCT.get_value<std::string>(tmpl, { K_ASSISTANT, K_BEGIN });
|
|
|
|
|
std::string assistantPrefix = gCT.get_value<std::string>(tmpl, { K_ASSISTANT, K_PREFIX });
|
|
|
|
|
std::string assistantSuffix = gCT.get_value<std::string>(tmpl, { K_ASSISTANT, K_SUFFIX });
|
|
|
|
|
std::string assistantEnd = gCT.get_value<std::string>(tmpl, { K_ASSISTANT, K_END });
|
|
|
|
|
std::string reversePrompt = gCT.get_value<std::string>(tmpl, { K_REVERSE_PROMPT });
|
|
|
|
|
bool systemHasSuffix = gCT.get_value<bool>(tmpl, { K_SYSTEMUSER_SYSTEM_HAS_SUFFIX });
|
|
|
|
|
bool systemHasEnd = gCT.get_value<bool>(tmpl, { K_SYSTEMUSER_SYSTEM_HAS_END });
|
|
|
|
|
bool userHasBegin = gCT.get_value<bool>(tmpl, { K_SYSTEMUSER_1ST_USER_HAS_BEGIN });
|
|
|
|
|
bool userHasPrefix = gCT.get_value<bool>(tmpl, { K_SYSTEMUSER_1ST_USER_HAS_PREFIX });
|
|
|
|
|
|
|
|
|
|
LOGXLN("INFO:%s:%s:%s", __func__, "global->begin", globalBegin.c_str());
|
|
|
|
|
LOGXLN("INFO:%s:%s:%s", __func__, "global->end", globalEnd.c_str());
|
|
|
|
|
LOGXLN("INFO:%s:%s:%s", __func__, "system->begin", systemBegin.c_str());
|
|
|
|
|
LOGXLN("INFO:%s:%s:%s", __func__, "system->prefix", systemPrefix.c_str());
|
|
|
|
|
LOGXLN("INFO:%s:%s:%s", __func__, "system->suffix", systemSuffix.c_str());
|
|
|
|
|
LOGXLN("INFO:%s:%s:%s", __func__, "system->end", systemEnd.c_str());
|
|
|
|
|
LOGXLN("INFO:%s:%s:%s", __func__, "user->begin", userBegin.c_str());
|
|
|
|
|
LOGXLN("INFO:%s:%s:%s", __func__, "user->prefix", userPrefix.c_str());
|
|
|
|
|
LOGXLN("INFO:%s:%s:%s", __func__, "user->suffix", userSuffix.c_str());
|
|
|
|
|
LOGXLN("INFO:%s:%s:%s", __func__, "user->end", userEnd.c_str());
|
|
|
|
|
LOGXLN("INFO:%s:%s:%s", __func__, "assistant->begin", assistantBegin.c_str());
|
|
|
|
|
LOGXLN("INFO:%s:%s:%s", __func__, "assistant->prefix", assistantPrefix.c_str());
|
|
|
|
|
LOGXLN("INFO:%s:%s:%s", __func__, "assistant->suffix", assistantSuffix.c_str());
|
|
|
|
|
LOGXLN("INFO:%s:%s:%s", __func__, "assistant->end", assistantEnd.c_str());
|
|
|
|
|
LOGXLN("INFO:%s:%s:%s", __func__, K_REVERSE_PROMPT, reversePrompt.c_str());
|
|
|
|
|
LOGXLN("INFO:%s:%s:%d", __func__, K_SYSTEMUSER_SYSTEM_HAS_SUFFIX, systemHasSuffix);
|
|
|
|
|
LOGXLN("INFO:%s:%s:%d", __func__, K_SYSTEMUSER_SYSTEM_HAS_END, systemHasEnd);
|
|
|
|
|
LOGXLN("INFO:%s:%s:%d", __func__, K_SYSTEMUSER_1ST_USER_HAS_BEGIN, userHasBegin);
|
|
|
|
|
LOGXLN("INFO:%s:%s:%d", __func__, K_SYSTEMUSER_1ST_USER_HAS_PREFIX, userHasPrefix);
|
|
|
|
|
|
|
|
|
|
if (!userEnd.empty()) {
|
|
|
|
|
LOG_TEELN("WARN:%s:User->End seems to be set to [%s], do cross check if this is proper and needed", __func__, userEnd.c_str());
|
|
|
|
|
}
|
|
|
|
|
if (!assistantBegin.empty()) {
|
|
|
|
|
LOG_TEELN("WARN:%s:Assistant->Begin seems to be set to [%s], do cross check if this is proper and needed", __func__, assistantBegin.c_str());
|
|
|
|
|
}
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
if (gCT.tmpl_basiccheck(tmpl, ss, "INFO:ChatOnMetaValidateDump")) {
|
|
|
|
|
LOGXLN("%s", ss.str().c_str());
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Verify that specified chaton-template-id contains required fields using meta-dump
|
|
|
|
|
* Verify that specified chaton-template-id contains required fields using meta-validate-dump
|
|
|
|
|
*/
|
|
|
|
|
inline bool chaton_meta_ok(std::string &tmpl) {
|
|
|
|
|
return _chaton_meta_dump(tmpl);
|
|
|
|
|
return _chaton_meta_validate_dump(tmpl);
|
|
|
|
|
}
|
|
|
|
|
|