Path: blob/main/userscripts/kaipreset_location_scanner.lua
473 views
-- Location scanner1-- Activates world info entries based on what the AI thinks the current location2-- is.34-- This file is part of KoboldAI.5--6-- KoboldAI is free software: you can redistribute it and/or modify7-- it under the terms of the GNU Affero General Public License as published by8-- the Free Software Foundation, either version 3 of the License, or9-- (at your option) any later version.10--11-- This program is distributed in the hope that it will be useful,12-- but WITHOUT ANY WARRANTY; without even the implied warranty of13-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14-- GNU Affero General Public License for more details.15--16-- You should have received a copy of the GNU Affero General Public License17-- along with this program. If not, see <https://www.gnu.org/licenses/>.1819kobold = require("bridge")() -- This line is optional and is only for EmmyLua type annotations20local userscript = {} ---@class KoboldUserScript212223local example_config = [[;-- Location scanner24;--25;-- Usage instructions:26;--27;-- 1. Create a world info folder with name containing the string28;-- "<||ls||>" (without the double quotes). The name can be anything as29;-- long as it contains that inside it somewhere -- for example, you could30;-- set the name to "Locations <||ls||>".31;--32;-- 2. Create a non-selective, constant world info key _in that folder_ with key33;-- "<||lslocation||>" (without the double quotes). Every once in a while,34;-- this script will generate 20 tokens using "The current location"35;-- as the submission and save the output into the <||lslocation||> entry.36;--37;-- 3. Put some other world info entries into the world info folder. These38;-- entries will _only_ be triggered by the contents of the <||lslocation||>39;-- entry and not by your story itself, or if it has constant key turned on.40;--41;-- You can edit some of the configuration values below to modify some of this42;-- behaviour:43;--44return {45location_folder = "<||ls||>",46location_key = "<||lslocation||>",47submission = "\n\nThe current location:",48n_wait = 12, -- The script will run its extra generation every time your story grows by this many chunks.49n_tokens = 20, -- Number of tokens to generate in extra generation50singleline = true, -- true or false; true will result in the extra generation's output being cut off after the first line.51trim = true, -- true or false; true will result in the extra generation's output being cut off after the end of its last sentence.52include = false, -- true or false; true will result in the <||lslocation||> entry's content being included in the story.53template = "<|>", -- Allows you to format the extra generation's output; for example, to surround the output in square brackets, set this to "[<|>]"54}55]]5657local cfg ---@type table<string, any>58do59-- If config file is empty, write example config60local f <close> = kobold.get_config_file()61f:seek("set")62if f:read(1) == nil then63f:write(example_config)64end65f:seek("set")66example_config = nil6768-- Read config69local err70cfg, err = load(f:read("a"))71if err ~= nil then72error(err)73end74cfg = cfg()75end7677if cfg.include == nil then78cfg.include = false79elseif cfg.template == nil then80cfg.template = "<|>"81end828384local folder ---@type KoboldWorldInfoFolder|nil85local entry ---@type KoboldWorldInfoEntry|nil86local location = ""87local orig_entry_map = {} ---@type table<integer, KoboldWorldInfoEntry>88local repeated = false89local last_quotient = math.huge9091local genamt = 09293function userscript.inmod()94if repeated then95kobold.submission = cfg.submission96genamt = kobold.settings.genamt97kobold.settings.genamt = cfg.n_tokens98end99100if entry == nil or folder == nil or not entry:is_valid() or not folder:is_valid() then101folder = nil102entry = nil103for i, f in ipairs(kobold.worldinfo.folders) do104if f.name:find(cfg.location_folder, 1, true) ~= nil then105folder = f106break107end108end109if folder ~= nil then110for i, e in ipairs(folder) do111if e.key:find(cfg.location_key, 1, true) ~= nil then112entry = e113break114end115end116end117end118119orig_entry_map = {}120121if entry ~= nil then122location = entry.content123entry.constant = not not cfg.include124end125126if folder ~= nil then127for i, e in ipairs(folder) do128if entry == nil or e.uid ~= entry.uid then129orig_entry_map[e.uid] = {130constant = e.constant,131key = e.key,132keysecondary = e.keysecondary,133}134e.constant = e.constant or (not repeated and e:compute_context("", {scan_story=false}) ~= e:compute_context(location, {scan_story=false}))135e.key = ""136e.keysecondary = ""137end138end139end140end141142function userscript.outmod()143if entry ~= nil and entry:is_valid() then144entry.constant = true145end146147if repeated then148local output = kobold.outputs[1]149kobold.outputs[1] = ""150151for chunk in kobold.story:reverse_iter() do152if chunk.content ~= "" then153chunk.content = ""154break155end156end157158kobold.settings.genamt = genamt159160output = output:match("^%s*(.*)%s*$")161162print("Extra generation result (prior to formatting): " .. output)163164if cfg.singleline then165output = output:match("^[^\n]*")166end167168if cfg.trim then169local i = 0170while true do171local j = output:find("[.?!)]", i + 1)172if j == nil then173break174end175i = j176end177if i > 0 then178if output:sub(i+1, i+1) == '"' then179i = i + 1180end181output = output:sub(1, i)182end183end184185location = cfg.template:gsub("<|>", output)186187print("Extra generation result (after formatting): " .. location)188189if entry ~= nil and entry:is_valid() then190entry.content = location191end192end193194local size = 0195for _ in kobold.story:forward_iter() do196size = size + 1197end198199for uid, orig in pairs(orig_entry_map) do200for k, v in pairs(orig) do201kobold.worldinfo:finduid(uid)[k] = v202end203end204205local quotient = math.floor(size / cfg.n_wait)206if repeated then207repeated = false208elseif quotient > last_quotient then209print("Running extra generation")210kobold.restart_generation()211repeated = true212end213last_quotient = quotient214end215216return userscript217218219