cc-libs/apis/libaihelloworld.lua

191 lines
5.6 KiB
Lua

local _VERSION = '0.2.1';
local DEFAULT_PROMPT = 'reply with exactly: pong';
local B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
local function base64encode(s)
local pad = (3 - #s % 3) % 3;
s = s .. string.rep('\0', pad);
local r = {};
for i = 1, #s, 3 do
local a, b, c = s:byte(i), s:byte(i + 1), s:byte(i + 2);
local n = a * 65536 + b * 256 + c;
r[#r + 1] = B64:sub(math.floor(n / 262144) % 64 + 1, math.floor(n / 262144) % 64 + 1)
.. B64:sub(math.floor(n / 4096) % 64 + 1, math.floor(n / 4096) % 64 + 1)
.. B64:sub(math.floor(n / 64) % 64 + 1, math.floor(n / 64) % 64 + 1)
.. B64:sub(n % 64 + 1, n % 64 + 1);
end
local result = table.concat(r);
if pad > 0 then
result = result:sub(1, #result - pad) .. string.rep('=', pad);
end
return result;
end
local function trimTrailingSlash(s)
return (s:gsub('/+$', ''));
end
local function readAllAndClose(response)
local body = response.readAll();
response.close();
return body;
end
local function statusCode(response)
if response.getResponseCode then
return response.getResponseCode();
end
return nil;
end
local function extractTextParts(parts)
local texts = {};
for _, part in ipairs(parts) do
if part.type == 'text' and type(part.text) == 'string' then
texts[#texts + 1] = part.text;
end
end
return table.concat(texts, '');
end
local function createAiHelloWorld(opts)
opts = opts or {};
local httpLib = opts.http or http;
local settingsLib = opts.settings or settings;
local prompt = opts.prompt or DEFAULT_PROMPT;
local api = {};
function api.version()
return _VERSION;
end
local function resolveConfig(options)
local url = options.serverUrl or settingsLib.get('opencc.server_url');
if not url or url == '' then
return nil, 'missing opencc.server_url; run: settings set opencc.server_url <url>';
end
local username = options.username or settingsLib.get('opencc.username') or 'opencode';
local password = options.password or settingsLib.get('opencc.password') or '';
return { url = trimTrailingSlash(url), username = username, password = password };
end
local function buildHeaders(cfg)
local headers = {
['Content-Type'] = 'application/json',
['Accept'] = 'application/json',
};
if cfg.password and cfg.password ~= '' then
headers['Authorization'] = 'Basic ' .. base64encode(cfg.username .. ':' .. cfg.password);
end
return headers;
end
local function doGet(cfg, path)
local response, _, errorResponse = httpLib.get(cfg.url .. path, buildHeaders(cfg));
response = response or errorResponse;
if not response then
return nil, 'serveur injoignable';
end
local code = statusCode(response);
local body = readAllAndClose(response);
return body, code;
end
local function doPost(cfg, path, payload)
local response, _, errorResponse = httpLib.post(
cfg.url .. path,
textutils.serializeJSON(payload),
buildHeaders(cfg)
);
response = response or errorResponse;
if not response then
return nil, 'serveur injoignable';
end
local code = statusCode(response);
local body = readAllAndClose(response);
return body, code;
end
function api.clearSession()
settingsLib.unset('opencc.session_id');
if settingsLib.save then settingsLib.save(); end
end
function api.listSessions(options)
options = options or {};
local cfg, err = resolveConfig(options);
if not cfg then return false, err; end
local body, code = doGet(cfg, '/session');
if not body then return false, code; end
if code and code ~= 200 then
return false, 'erreur serveur: HTTP ' .. tostring(code);
end
local decoded = textutils.unserializeJSON(body);
if type(decoded) ~= 'table' then
return false, 'reponse invalide';
end
return true, decoded;
end
function api.askHello(options)
options = options or {};
local cfg, err = resolveConfig(options);
if not cfg then return false, err; end
local sessionId = options.sessionId;
if sessionId == nil then
sessionId = settingsLib.get('opencc.session_id');
end
if not sessionId or sessionId == '' then
local body, code = doPost(cfg, '/session', { title = 'cc-helloworld' });
if not body then return false, code; end
if code and code ~= 200 then
return false, 'impossible de creer une session: HTTP ' .. tostring(code);
end
local decoded = textutils.unserializeJSON(body);
if type(decoded) ~= 'table' or type(decoded.id) ~= 'string' then
return false, 'reponse session invalide';
end
sessionId = decoded.id;
settingsLib.set('opencc.session_id', sessionId);
if settingsLib.save then settingsLib.save(); end
end
local body, code = doPost(cfg, '/session/' .. sessionId .. '/message', {
parts = { { type = 'text', text = prompt } },
});
if not body then return false, code; end
if code == 404 then
settingsLib.unset('opencc.session_id');
if settingsLib.save then settingsLib.save(); end
return false, 'session introuvable; lance: ai-helloworld --new';
end
if code and code ~= 200 then
return false, 'erreur message: HTTP ' .. tostring(code);
end
local decoded = textutils.unserializeJSON(body);
if type(decoded) ~= 'table' or type(decoded.parts) ~= 'table' then
return false, 'reponse message invalide';
end
local reply = extractTextParts(decoded.parts);
if reply == '' then
return false, 'reponse vide';
end
return true, { reply = reply, sessionId = sessionId };
end
return api;
end
return createAiHelloWorld;