cc-libs/apis/libtrapgpt.lua

179 lines
4.9 KiB
Lua

local DEFAULT_THROTTLE_SECONDS = 5;
local DEFAULT_MAX_REPLY_CHARS = 160;
local DEFAULT_PREFIX = 'TrapGPT';
local DEFAULT_SESSION_SETTING_KEY = 'trapgpt.opencc.session_id';
local SILENCE = 'SILENCE';
local function nowSeconds()
if os.epoch then
return os.epoch('utc') / 1000;
end
return os.clock();
end
local function resolveNumber(value, defaultValue)
local n = tonumber(value);
if not n or n < 0 then return defaultValue; end
return n;
end
local function trim(s)
return tostring(s or ''):gsub('^%s+', ''):gsub('%s+$', '');
end
local function truncate(s, maxChars)
s = trim(s);
if #s <= maxChars then return s; end
if maxChars <= 3 then return string.sub(s, 1, maxChars); end
return string.sub(s, 1, maxChars - 3) .. '...';
end
local function formatChatLine(message)
local at = message.at and ('@' .. tostring(math.floor(message.at)) .. ' ') or '';
return at .. tostring(message.username or '?') .. ': ' .. tostring(message.text or '');
end
local function buildPrompt(messages, firstBatch, maxReplyChars)
local lines = {
'Tu es TrapGPT dans le chat Minecraft.',
'Reponds seulement si utile.',
'Reponse tres concise: une phrase courte, maximum ' .. tostring(maxReplyChars) .. ' caracteres.',
'Pas de markdown. Ne repete pas l historique.',
'Si aucune reponse utile, reponds exactement: ' .. SILENCE,
};
if firstBatch then
lines[#lines + 1] = 'Contexte initial: voici les premiers messages recus.';
end
lines[#lines + 1] = '';
lines[#lines + 1] = 'Nouveaux messages chat depuis le dernier envoi:';
for _, message in ipairs(messages) do
lines[#lines + 1] = formatChatLine(message);
end
return table.concat(lines, '\n');
end
local function createTrapGpt(opts)
opts = opts or {};
local settingsLib = opts.settings or settings;
local nowFunc = opts.now or nowSeconds;
local sleepFunc = opts.sleep or sleep;
local ai = opts.ai or require('/apis/libai')();
local chatBox = opts.chatBox;
local log = opts.log or print;
local api = {};
local history = {};
local sentIndex = 0;
local firstBatch = true;
local lastSendAt = 0;
local active = false;
local stopped = false;
local function throttleSeconds()
return resolveNumber(settingsLib.get('trapgpt.throttle_seconds'), DEFAULT_THROTTLE_SECONDS);
end
local function maxReplyChars()
return math.max(1, resolveNumber(settingsLib.get('trapgpt.max_reply_chars'), DEFAULT_MAX_REPLY_CHARS));
end
local function prefix()
local value = settingsLib.get('trapgpt.prefix');
if type(value) ~= 'string' or value == '' then return DEFAULT_PREFIX; end
return value;
end
local function queuedMessages()
local messages = {};
for i = sentIndex + 1, #history do
messages[#messages + 1] = history[i];
end
return messages;
end
local function shouldIgnore(username, text, isHidden)
if isHidden then return true; end
if type(text) ~= 'string' or trim(text) == '' then return true; end
if type(username) == 'string' and username == prefix() then return true; end
return false;
end
function api.onChat(username, message, uuid, isHidden, messageUtf8)
local text = messageUtf8 or message;
if shouldIgnore(username, text, isHidden) then return false; end
history[#history + 1] = {
username = username,
text = text,
uuid = uuid,
at = nowFunc(),
};
return true;
end
function api.pendingCount()
return #history - sentIndex;
end
function api.history()
return history;
end
function api.buildPrompt(messages)
return buildPrompt(messages, firstBatch, maxReplyChars());
end
function api.processOnce()
if active or api.pendingCount() <= 0 then return false; end
local waitSeconds = throttleSeconds() - (nowFunc() - lastSendAt);
if waitSeconds > 0 then sleepFunc(waitSeconds); end
local startIndex = sentIndex + 1;
local messages = queuedMessages();
if #messages == 0 then return false; end
active = true;
local ok, result = ai.ask(buildPrompt(messages, firstBatch, maxReplyChars()), {
sessionSettingKey = DEFAULT_SESSION_SETTING_KEY,
sessionTitle = 'trapgpt',
});
active = false;
lastSendAt = nowFunc();
if not ok then
log('trapgpt ai error: ' .. tostring(result));
return false;
end
sentIndex = startIndex + #messages - 1;
firstBatch = false;
local reply = truncate(result.reply, maxReplyChars());
if reply == '' or reply == SILENCE then return true; end
if chatBox then
local sent, err = chatBox.sendMessage(reply, prefix());
if not sent then log('trapgpt chat error: ' .. tostring(err)); end
end
return true, reply;
end
function api.stop()
stopped = true;
end
function api.run()
while not stopped do
if api.pendingCount() > 0 then
api.processOnce();
else
sleepFunc(0.25);
end
end
end
return api;
end
return createTrapGpt;