Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
KoboldAI
GitHub Repository: KoboldAI/KoboldAI-Client
Path: blob/main/bridge.lua
471 views
1
-- KoboldAI Lua 5.4 Bridge
2
3
4
---@param _python? table<string, any>
5
---@param _bridged? table<string, any>
6
---@return KoboldLib, KoboldCoreLib?
7
return function(_python, _bridged)
8
9
--==========================================================================
10
-- Globally allows using a _kobold_next metamethod for "Kobold" classes only
11
--==========================================================================
12
13
local old_next = next
14
---@generic K, V
15
---@param t table<K, V>
16
---@param k? K
17
---@return K?, V?
18
function next(t, k)
19
local meta = getmetatable(t)
20
return ((meta ~= nil and type(rawget(t, "_name")) == "string" and string.match(rawget(t, "_name"), "^Kobold") and type(meta._kobold_next) == "function") and meta._kobold_next or old_next)(t, k)
21
end
22
23
24
--==========================================================================
25
-- General utility functions
26
--==========================================================================
27
28
---@generic T
29
---@param original T
30
---@return T
31
local function deepcopy(original)
32
if type(original) == "table" then
33
local copy = {}
34
for k, v in old_next, original, nil do
35
copy[k] = deepcopy(v)
36
end
37
setmetatable(copy, deepcopy(getmetatable(original)))
38
return copy
39
end
40
return original
41
end
42
43
---@param paths string|table<integer, string>
44
---@return nil
45
local function set_require_path(paths)
46
if type(paths) == "string" then
47
paths = {paths}
48
end
49
local config = {}
50
local i = 1
51
for substring in string.gmatch(package.config, "[^\n]+") do
52
config[i] = substring
53
i = i + 1
54
end
55
local _paths = {}
56
for i, path in ipairs(paths) do
57
_paths[i] = path .. config[1] .. config[3] .. ".lua" .. config[2] .. path .. config[1] .. config[3] .. config[1] .. "init.lua"
58
end
59
package.path = table.concat(_paths, config[2])
60
package.cpath = ""
61
end
62
63
---@param path string
64
---@param filename string
65
---@return string
66
local function join_folder_and_filename(path, filename)
67
return path .. string.match(package.config, "[^\n]+") .. filename
68
end
69
70
71
--==========================================================================
72
-- _bridged preprocessing
73
--==========================================================================
74
75
local bridged = {}
76
for k in _python.iter(_bridged) do
77
local v = _bridged[k]
78
bridged[k] = type(v) == "userdata" and _python.as_attrgetter(v) or v
79
end
80
set_require_path(bridged.lib_paths)
81
82
83
--==========================================================================
84
-- Wraps most functions in this file so that they restore original
85
-- metatables prior to executing the function body
86
--==========================================================================
87
88
local wrapped = false
89
90
---@class Metatables
91
local metatables = {}
92
local type_map = {
93
_nil = nil,
94
_boolean = false,
95
_number = 0,
96
_string = "",
97
_function = type,
98
_thread = coroutine.create(function() end),
99
}
100
101
function metatables:overwrite()
102
for k, v in pairs(type_map) do
103
self[k] = debug.getmetatable(v)
104
end
105
end
106
107
function metatables:restore()
108
for k, v in pairs(type_map) do
109
debug.setmetatable(v, self[k])
110
end
111
end
112
113
local metatables_original = deepcopy(metatables)
114
metatables_original:overwrite()
115
116
local metawrapper = {}
117
---@generic T : table
118
---@param t T
119
---@return T
120
function metawrapper.__newindex(t, k, wrapped_func)
121
if type(wrapped_func) == "function" then
122
return rawset(t, k, function(...)
123
local _needs_unwrap = false
124
if not wrapped then
125
metatables:overwrite()
126
metatables_original:restore()
127
_needs_unwrap = true
128
wrapped = true
129
end
130
local r = table.pack(wrapped_func(...))
131
if _needs_unwrap then
132
metatables:restore()
133
wrapped = false
134
end
135
return table.unpack(r, 1, r.n)
136
end)
137
else
138
return rawset(t, k, wrapped_func)
139
end
140
end
141
142
143
--==========================================================================
144
-- Modules
145
--==========================================================================
146
147
---@class KoboldLib
148
---@field API_VERSION number
149
---@field authorsnote string
150
---@field authorsnotetemplate string
151
---@field memory string
152
---@field submission string
153
---@field model string
154
---@field modeltype "'readonly'"|"'api'"|"'unknown'"|"'gpt2'"|"'gpt2-medium'"|"'gpt2-large'"|"'gpt2-xl'"|"'gpt-neo-125M'"|"'gpt-neo-1.3B'"|"'gpt-neo-2.7B'"|"'gpt-j-6B'"
155
---@field modelbackend "'readonly'"|"'api'"|"'transformers'"|"'mtj'"
156
---@field is_custommodel boolean
157
---@field custmodpth string
158
---@field logits table<integer, table<integer, number>>
159
---@field logits_rows integer
160
---@field logits_cols integer
161
---@field generated table<integer, table<integer, integer>>
162
---@field generated_rows integer
163
---@field generated_cols integer
164
---@field outputs table<integer, string>
165
---@field num_outputs integer
166
---@field feedback string
167
---@field is_config_file_open boolean
168
local kobold = setmetatable({API_VERSION = 1.2}, metawrapper)
169
local KoboldLib_mt = setmetatable({}, metawrapper)
170
local KoboldLib_getters = setmetatable({}, metawrapper)
171
local KoboldLib_setters = setmetatable({}, metawrapper)
172
173
---@param t KoboldLib
174
function KoboldLib_mt.__index(t, k)
175
local getter = KoboldLib_getters[k]
176
if getter ~= nil then
177
return getter(t)
178
end
179
return rawget(t, k)
180
end
181
182
---@param t KoboldLib
183
function KoboldLib_mt.__newindex(t, k, v)
184
local setter = KoboldLib_setters[k]
185
if setter ~= nil then
186
return setter(t, v)
187
end
188
return rawset(t, k, v)
189
end
190
191
---@class KoboldCoreLib
192
---@field userscripts KoboldUserScriptList
193
local koboldcore = setmetatable({}, metawrapper)
194
local KoboldCoreLib_mt = setmetatable({}, metawrapper)
195
local KoboldCoreLib_getters = setmetatable({}, metawrapper)
196
local KoboldCoreLib_setters = setmetatable({}, metawrapper)
197
198
---@param t KoboldCoreLib
199
function KoboldCoreLib_mt.__index(t, k)
200
local getter = KoboldCoreLib_getters[k]
201
if getter ~= nil then
202
return getter(t, k)
203
end
204
return rawget(t, k)
205
end
206
207
---@param t KoboldCoreLib
208
function KoboldCoreLib_mt.__newindex(t, k, v)
209
local setter = KoboldCoreLib_setters[k]
210
if setter ~= nil then
211
return setter(t, k, v)
212
end
213
return rawset(t, k, v)
214
end
215
216
---@class KoboldBridgeLib
217
local koboldbridge = {}
218
219
koboldbridge.regeneration_required = false
220
koboldbridge.resend_settings_required = false
221
koboldbridge.generating = true
222
koboldbridge.restart_sequence = nil
223
koboldbridge.userstate = nil
224
koboldbridge.logits = {}
225
koboldbridge.vocab_size = 0
226
koboldbridge.generated = {}
227
koboldbridge.generated_cols = 0
228
koboldbridge.outputs = {}
229
koboldbridge.feedback = nil ---@type string?
230
231
function koboldbridge:clear_userscript_metadata()
232
self.logging_name = nil
233
self.filename = nil
234
end
235
236
---@return nil
237
local function maybe_require_regeneration()
238
if koboldbridge.userstate == "genmod" or koboldbridge.userstate == "outmod" then
239
koboldbridge.regeneration_required = true
240
end
241
end
242
243
244
--==========================================================================
245
-- Userscript API: Configuration
246
--==========================================================================
247
248
local config_files = {} ---@type table<string, file*>
249
local config_file_filename_map = {} ---@type table<file*, string>
250
251
---@return file*?
252
local function open_and_handle_errors(...)
253
local file, err_msg = io.open(...)
254
if err_msg ~= nil then
255
koboldbridge.obliterate_multiverse()
256
error(err_msg)
257
return
258
end
259
return file
260
end
261
262
---@param file? file*
263
local function new_close_pre(file)
264
if file == nil then
265
file = io.output()
266
end
267
local filename = config_file_filename_map[file]
268
if filename ~= nil then
269
config_file_filename_map[file] = nil
270
config_files[filename] = nil
271
end
272
end
273
274
---@param f fun(file?: file*)
275
local function _new_close(f)
276
---@param file? file*
277
return function(file)
278
new_close_pre(file)
279
return f(file)
280
end
281
end
282
debug.getmetatable(io.stdout).__index.close = _new_close(io.stdout.close)
283
debug.getmetatable(io.stdout).__close = _new_close(io.stdout.close)
284
285
---@param filename string
286
---@return boolean
287
local function is_config_file_open(filename)
288
return config_files[filename] ~= nil
289
end
290
291
---@param filename string
292
---@param clear? boolean
293
---@return file*
294
local function get_config_file(filename, clear)
295
if not is_config_file_open(filename) then
296
local config_filepath = join_folder_and_filename(bridged.config_path, filename .. ".conf")
297
open_and_handle_errors(config_filepath, "a"):close()
298
config_files[filename] = open_and_handle_errors(config_filepath, clear and "w+b" or "r+b")
299
config_file_filename_map[config_files[filename]] = filename
300
end
301
return config_files[filename]
302
end
303
304
---@param clear? boolean
305
---@return file*
306
function kobold.get_config_file(clear)
307
return get_config_file(koboldbridge.filename, clear)
308
end
309
310
---@param t KoboldLib
311
---@return boolean
312
function KoboldLib_getters.is_config_file_open(t)
313
return is_config_file_open(koboldbridge.filename)
314
end
315
316
---@param t KoboldLib
317
---@param v boolean
318
function KoboldLib_setters.is_config_file_open(t, v)
319
error("`KoboldLib.is_config_file_open` is a read-only attribute")
320
end
321
322
323
--==========================================================================
324
-- Userscript API: World Info
325
--==========================================================================
326
327
local fields = setmetatable({}, metawrapper)
328
329
---@param t KoboldWorldInfoEntry|KoboldWorldInfoFolder|KoboldWorldInfo|KoboldWorldInfoFolderSelector
330
---@return boolean
331
local function check_validity(t)
332
if not t:is_valid() then
333
error("Attempted to use a nonexistent/deleted `"..rawget(t, "_name").."`")
334
return false
335
end
336
return true
337
end
338
339
340
----------------------------------------------------------------------------
341
342
---@class KoboldWorldInfoEntry_base
343
---@type table<integer, nil>
344
local _ = {}
345
346
---@class KoboldWorldInfoEntry : KoboldWorldInfoEntry_base
347
---@field key string
348
---@field keysecondary string
349
---@field content string
350
---@field comment string
351
---@field folder integer
352
---@field num integer
353
---@field selective boolean
354
---@field constant boolean
355
---@field uid integer
356
local KoboldWorldInfoEntry = setmetatable({
357
_name = "KoboldWorldInfoEntry",
358
}, metawrapper)
359
fields.KoboldWorldInfoEntry = {
360
"key",
361
"keysecondary",
362
"content",
363
"comment",
364
"folder",
365
"num",
366
"selective",
367
"constant",
368
"uid",
369
}
370
local KoboldWorldInfoEntry_mt = setmetatable({}, metawrapper)
371
372
local KoboldWorldInfoEntry_fieldtypes = {
373
key = "string",
374
keysecondary = "string",
375
content = "string",
376
comment = "string",
377
selective = "boolean",
378
constant = "boolean",
379
}
380
381
---@return boolean
382
function KoboldWorldInfoEntry:is_valid()
383
return _python.as_attrgetter(bridged.vars.worldinfo_u).get(rawget(self, "_uid")) ~= nil
384
end
385
386
---@param submission? string
387
---@param kwargs? table<string, any>
388
---@return string
389
function KoboldWorldInfoEntry:compute_context(submission, kwargs)
390
if not check_validity(self) then
391
return ""
392
elseif submission == nil then
393
submission = kobold.submission
394
elseif type(submission) ~= "string" then
395
error("`compute_context` takes a string or nil as argument #1, but got a " .. type(submission))
396
return ""
397
end
398
return bridged.compute_context(submission, {self.uid}, nil, kwargs)
399
end
400
401
---@generic K
402
---@param t KoboldWorldInfoEntry|KoboldWorldInfoFolder|KoboldWorldInfo|KoboldWorldInfoFolderSelector
403
---@param k K
404
---@return K, any
405
function KoboldWorldInfoEntry_mt._kobold_next(t, k)
406
local _t = fields[rawget(t, "_name")]
407
if _t == nil then
408
return
409
end
410
return next(_t, k)
411
end
412
413
---@param t KoboldWorldInfoEntry|KoboldWorldInfoFolder|KoboldWorldInfo|KoboldWorldInfoFolderSelector
414
---@return function, KoboldWorldInfoEntry|KoboldWorldInfoFolder|KoboldWorldInfo|KoboldWorldInfoFolderSelector, nil
415
function KoboldWorldInfoEntry_mt.__pairs(t)
416
return next, t, nil
417
end
418
419
---@param t KoboldWorldInfoEntry
420
function KoboldWorldInfoEntry_mt.__index(t, k)
421
if not check_validity(t) then
422
return
423
elseif k == "uid" then
424
return rawget(t, "_uid")
425
elseif type(k) == "string" then
426
return bridged.get_attr(t.uid, k)
427
end
428
end
429
430
---@param t KoboldWorldInfoEntry
431
---@return KoboldWorldInfoEntry
432
function KoboldWorldInfoEntry_mt.__newindex(t, k, v)
433
if not check_validity(t) then
434
return
435
elseif fields[rawget(t, "_name")] then
436
if type(k) == "string" and KoboldWorldInfoEntry_fieldtypes[k] == nil then
437
error("`"..rawget(t, "_name").."."..k.."` is a read-only attribute")
438
return
439
elseif type(k) == "string" and type(v) ~= KoboldWorldInfoEntry_fieldtypes[k] then
440
error("`"..rawget(t, "_name").."."..k.."` must be a "..KoboldWorldInfoEntry_fieldtypes[k].."; you attempted to set it to a "..type(v))
441
return
442
else
443
if k ~= "comment" and not (t.selective and k == "keysecondary") then
444
maybe_require_regeneration()
445
end
446
bridged.set_attr(t.uid, k, v)
447
return t
448
end
449
end
450
return rawset(t, k, v)
451
end
452
453
454
----------------------------------------------------------------------------
455
456
---@class KoboldWorldInfoFolder_base
457
---@type table<integer, KoboldWorldInfoEntry>
458
local _ = {}
459
460
---@class KoboldWorldInfoFolder : KoboldWorldInfoFolder_base
461
---@field uid integer
462
---@field name string
463
local KoboldWorldInfoFolder = setmetatable({
464
_name = "KoboldWorldInfoFolder",
465
}, metawrapper)
466
467
fields.KoboldWorldInfoFolder = {
468
"uid",
469
}
470
local KoboldWorldInfoFolder_mt = setmetatable({}, metawrapper)
471
472
---@param u integer
473
---@return KoboldWorldInfoEntry?
474
function KoboldWorldInfoFolder:finduid(u)
475
if not check_validity(self) or type(u) ~= "number" then
476
return
477
end
478
local query = _python.as_attrgetter(bridged.vars.worldinfo_u).get(u)
479
if query == nil or (rawget(self, "_name") == "KoboldWorldInfoFolder" and self.uid ~= _python.as_attrgetter(query).get("folder")) then
480
return
481
end
482
local entry = deepcopy(KoboldWorldInfoEntry)
483
rawset(entry, "_uid", u)
484
return entry
485
end
486
487
---@param submission? string
488
---@param entries? KoboldWorldInfoEntry|table<any, KoboldWorldInfoEntry>
489
---@param kwargs? table<string, any>
490
---@return string
491
function KoboldWorldInfoFolder:compute_context(submission, entries, kwargs)
492
if not check_validity(self) then
493
return ""
494
elseif submission == nil then
495
submission = kobold.submission
496
elseif type(submission) ~= "string" then
497
error("`compute_context` takes a string or nil as argument #1, but got a " .. type(submission))
498
return ""
499
end
500
local _entries
501
if entries ~= nil then
502
if type(entries) ~= "table" or (entries.name ~= nil and entries.name ~= "KoboldWorldInfoEntry") then
503
error("`compute_context` takes a KoboldWorldInfoEntry, table of KoboldWorldInfoEntries or nil as argument #2, but got a " .. type(entries))
504
return ""
505
elseif entries.name == "KoboldWorldInfoEntry" then
506
_entries = {entries}
507
else
508
_entries = {}
509
for k, v in pairs(entries) do
510
if type(v) == "table" and v.name == "KoboldWorldInfoEntry" and v:is_valid() then
511
_entries[k] = v.uid
512
end
513
end
514
end
515
end
516
local folders
517
if self.name == "KoboldWorldInfoFolder" then
518
folders = {rawget(self, "_uid")}
519
end
520
return bridged.compute_context(submission, _entries, folders, kwargs)
521
end
522
523
---@return boolean
524
function KoboldWorldInfoFolder:is_valid()
525
return _python.as_attrgetter(bridged.vars.wifolders_d).get(rawget(self, "_uid")) ~= nil
526
end
527
528
---@param t KoboldWorldInfoFolder
529
---@return integer
530
function KoboldWorldInfoFolder_mt.__len(t)
531
if not check_validity(t) then
532
return 0
533
end
534
return math.tointeger(_python.builtins.len(_python.as_attrgetter(bridged.vars.wifolders_u).get(t.uid))) - 1
535
end
536
537
KoboldWorldInfoFolder_mt._kobold_next = KoboldWorldInfoEntry_mt._kobold_next
538
539
KoboldWorldInfoFolder_mt.__pairs = KoboldWorldInfoEntry_mt.__pairs
540
541
---@param t KoboldWorldInfoFolder|KoboldWorldInfo
542
---@return KoboldWorldInfoEntry?
543
function KoboldWorldInfoFolder_mt.__index(t, k)
544
if not check_validity(t) then
545
return
546
elseif rawget(t, "_name") == "KoboldWorldInfoFolder" and k == "uid" then
547
return rawget(t, "_uid")
548
elseif rawget(t, "_name") == "KoboldWorldInfoFolder" and k == "name" then
549
return bridged.folder_get_attr(t.uid, k)
550
elseif type(k) == "number" then
551
local query = rawget(t, "_name") == "KoboldWorldInfoFolder" and _python.as_attrgetter(bridged.vars.wifolders_u).get(t.uid) or bridged.vars.worldinfo_i
552
k = math.tointeger(k)
553
if k == nil or k < 1 or k > #t then
554
return
555
end
556
local entry = deepcopy(KoboldWorldInfoEntry)
557
rawset(entry, "_uid", math.tointeger(query[k-1].uid))
558
return entry
559
end
560
end
561
562
---@param t KoboldWorldInfoFolder|KoboldWorldInfo
563
---@return KoboldWorldInfoFolder|KoboldWorldInfo
564
function KoboldWorldInfoFolder_mt.__newindex(t, k, v)
565
if not check_validity(t) then
566
return
567
elseif type(k) == "number" and math.tointeger(k) ~= nil then
568
error("Cannot write to integer indices of `"..rawget(t, "_name").."`")
569
elseif rawget(t, "_name") == "KoboldWorldInfoFolder" and k == "uid" then
570
error("`"..rawget(t, "_name").."."..k.."` is a read-only attribute")
571
elseif t == "name" then
572
if type(v) ~= "string" then
573
error("`"..rawget(t, "_name").."."..k.."` must be a string; you attempted to set it to a "..type(v))
574
return
575
end
576
bridged.folder_set_attr(t.uid, k, v)
577
return t
578
else
579
return rawset(t, k, v)
580
end
581
end
582
583
584
----------------------------------------------------------------------------
585
586
---@class KoboldWorldInfoFolderSelector_base
587
---@type table<integer, KoboldWorldInfoFolder>
588
local _ = {}
589
590
---@class KoboldWorldInfoFolderSelector : KoboldWorldInfoFolderSelector_base
591
local KoboldWorldInfoFolderSelector = setmetatable({
592
_name = "KoboldWorldInfoFolderSelector",
593
}, metawrapper)
594
local KoboldWorldInfoFolderSelector_mt = setmetatable({}, metawrapper)
595
596
---@param u integer
597
---@return KoboldWorldInfoFolder?
598
function KoboldWorldInfoFolderSelector:finduid(u)
599
if not check_validity(self) or type(u) ~= "number" then
600
return
601
end
602
local query = _python.as_attrgetter(bridged.vars.wifolders_d).get(u)
603
if query == nil then
604
return
605
end
606
local folder = deepcopy(KoboldWorldInfoFolder)
607
rawset(folder, "_uid", u)
608
return folder
609
end
610
611
---@return boolean
612
function KoboldWorldInfoFolderSelector:is_valid()
613
return true
614
end
615
616
---@param t KoboldWorldInfoFolderSelector
617
---@return integer
618
function KoboldWorldInfoFolderSelector_mt.__len(t)
619
if not check_validity(t) then
620
return 0
621
end
622
return _python.builtins.len(bridged.vars.wifolders_l)
623
end
624
625
KoboldWorldInfoFolderSelector_mt._kobold_next = KoboldWorldInfoEntry_mt._kobold_next
626
627
KoboldWorldInfoFolderSelector_mt.__pairs = KoboldWorldInfoEntry_mt.__pairs
628
629
---@param t KoboldWorldInfoFolderSelector
630
---@return KoboldWorldInfoFolder?
631
function KoboldWorldInfoFolderSelector_mt.__index(t, k)
632
if not check_validity(t) or type(k) ~= "number" or math.tointeger(k) == nil or k < 1 or k > #t then
633
return
634
end
635
local folder = deepcopy(KoboldWorldInfoFolder)
636
rawset(folder, "_uid", math.tointeger(bridged.vars.wifolders_l[k-1]))
637
return folder
638
end
639
640
---@param t KoboldWorldInfoFolderSelector
641
---@return KoboldWorldInfoFolderSelector
642
function KoboldWorldInfoFolderSelector_mt.__newindex(t, k, v)
643
if check_validity(t) or (type(k) == "number" and math.tointeger(k) ~= nil) then
644
error("Cannot write to integer indices of `"..rawget(t, "_name").."`")
645
end
646
return rawset(t, k, v)
647
end
648
649
650
----------------------------------------------------------------------------
651
652
---@class KoboldWorldInfo : KoboldWorldInfoFolder_base
653
local KoboldWorldInfo = setmetatable({
654
_name = "KoboldWorldInfo",
655
}, metawrapper)
656
local KoboldWorldInfo_mt = setmetatable({}, metawrapper)
657
658
KoboldWorldInfo.folders = KoboldWorldInfoFolderSelector
659
660
KoboldWorldInfo.finduid = KoboldWorldInfoFolder.finduid
661
662
KoboldWorldInfo.compute_context = KoboldWorldInfoFolder.compute_context
663
664
---@return boolean
665
function KoboldWorldInfo:is_valid()
666
return true
667
end
668
669
---@param t KoboldWorldInfo
670
---@return integer
671
function KoboldWorldInfo_mt.__len(t)
672
if not check_validity(t) then
673
return 0
674
end
675
return math.tointeger(_python.builtins.len(bridged.vars.worldinfo)) - math.tointeger(_python.builtins.len(bridged.vars.wifolders_l)) - 1
676
end
677
678
KoboldWorldInfo_mt._kobold_next = KoboldWorldInfoEntry_mt._kobold_next
679
680
KoboldWorldInfo_mt.__pairs = KoboldWorldInfoEntry_mt.__pairs
681
682
KoboldWorldInfo_mt.__index = KoboldWorldInfoFolder_mt.__index
683
684
KoboldWorldInfo_mt.__newindex = KoboldWorldInfoFolder_mt.__newindex
685
686
kobold.worldinfo = KoboldWorldInfo
687
688
689
--==========================================================================
690
-- Userscript API: Story chunks
691
--==========================================================================
692
693
---@class KoboldStoryChunk
694
---@field num integer
695
---@field content string
696
local KoboldStoryChunk = setmetatable({
697
_name = "KoboldStoryChunk",
698
}, metawrapper)
699
local KoboldStoryChunk_mt = setmetatable({}, metawrapper)
700
701
local KoboldStoryChunk_fields = {
702
num = false,
703
content = false,
704
}
705
706
---@generic K
707
---@param t KoboldStoryChunk
708
---@param k K
709
---@return K, any
710
function KoboldStoryChunk_mt._kobold_next(t, k)
711
k = (next(KoboldStoryChunk_fields, k))
712
return k, t[k]
713
end
714
715
---@param t KoboldStoryChunk
716
---@return function, KoboldStoryChunk, nil
717
function KoboldStoryChunk_mt.__pairs(t)
718
return next, t, nil
719
end
720
721
---@param t KoboldStoryChunk
722
function KoboldStoryChunk_mt.__index(t, k)
723
if k == "num" then
724
return rawget(t, "_num")
725
end
726
if k == "content" then
727
if rawget(t, "_num") == 0 then
728
if bridged.vars.gamestarted then
729
local prompt = koboldbridge.userstate == "genmod" and bridged.vars._prompt or bridged.vars.prompt
730
return prompt
731
end
732
end
733
local actions = koboldbridge.userstate == "genmod" and bridged.vars._actions or bridged.vars.actions
734
return _python.as_attrgetter(actions).get(math.tointeger(rawget(t, "_num")) - 1)
735
end
736
end
737
738
---@param t KoboldStoryChunk
739
function KoboldStoryChunk_mt.__newindex(t, k, v)
740
if k == "num" then
741
error("`"..rawget(t, "_name").."."..k.."` is a read-only attribute")
742
return
743
elseif k == "content" then
744
if type(v) ~= "string" then
745
error("`"..rawget(t, "_name").."."..k.."` must be a string; you attempted to set it to a "..type(v))
746
return
747
end
748
local _k = math.tointeger(rawget(t, "_num"))
749
if _k == nil or _k < 0 then
750
return
751
elseif _k == 0 and v == "" then
752
error("Attempted to set the prompt chunk's content to the empty string; this is not allowed")
753
return
754
end
755
local actions = koboldbridge.userstate == "genmod" and bridged.vars._actions or bridged.vars.actions
756
if _k ~= 0 and _python.as_attrgetter(actions).get(_k-1) == nil then
757
return
758
end
759
bridged.set_chunk(_k, v)
760
maybe_require_regeneration()
761
return t
762
end
763
end
764
765
766
----------------------------------------------------------------------------
767
768
---@class KoboldStory_base
769
---@type table<integer, KoboldStoryChunk>
770
local _ = {}
771
772
---@class KoboldStory : KoboldStory_base
773
local KoboldStory = setmetatable({
774
_name = "KoboldStory",
775
}, metawrapper)
776
local KoboldStory_mt = setmetatable({}, metawrapper)
777
778
---@return fun(): KoboldStoryChunk, table, nil
779
function KoboldStory:forward_iter()
780
local actions = koboldbridge.userstate == "genmod" and bridged.vars._actions or bridged.vars.actions
781
local nxt, iterator = _python.iter(actions)
782
local run_once = false
783
local function f()
784
if not bridged.vars.gamestarted then
785
return
786
end
787
local chunk = deepcopy(KoboldStoryChunk)
788
local _k
789
if not run_once then
790
_k = -1
791
run_once = true
792
else
793
_k = nxt(iterator)
794
end
795
if _k == nil then
796
return
797
else
798
_k = math.tointeger(_k) + 1
799
end
800
rawset(chunk, "_num", _k)
801
return chunk
802
end
803
return f, {}, nil
804
end
805
806
---@return fun(): KoboldStoryChunk, table, nil
807
function KoboldStory:reverse_iter()
808
local actions = koboldbridge.userstate == "genmod" and bridged.vars._actions or bridged.vars.actions
809
local nxt, iterator = _python.iter(_python.builtins.reversed(actions))
810
local last_run = false
811
local function f()
812
if not bridged.vars.gamestarted or last_run then
813
return
814
end
815
local chunk = deepcopy(KoboldStoryChunk)
816
local _k = nxt(iterator)
817
if _k == nil then
818
_k = 0
819
last_run = true
820
else
821
_k = math.tointeger(_k) + 1
822
end
823
rawset(chunk, "_num", _k)
824
return chunk
825
end
826
return f, {}, nil
827
end
828
829
---@param t KoboldStory
830
function KoboldStory_mt.__pairs(t)
831
return function() return nil end, t, nil
832
end
833
834
---@param t KoboldStory
835
function KoboldStory_mt.__index(t, k)
836
if type(k) == "number" and math.tointeger(k) ~= nil then
837
local chunk = deepcopy(KoboldStoryChunk)
838
rawset(chunk, "_num", math.tointeger(k))
839
if chunk.content == nil then
840
return nil
841
end
842
return chunk
843
end
844
end
845
846
---@param t KoboldStory
847
function KoboldStory_mt.__newindex(t, k, v)
848
error("`"..rawget(t, "_name").."` is a read-only class")
849
end
850
851
kobold.story = KoboldStory
852
853
854
--==========================================================================
855
-- Userscript API: Settings
856
--==========================================================================
857
858
---@class KoboldSettings_base
859
---@type table<string, any>
860
local _ = {}
861
862
---@class KoboldSettings : KoboldSettings_base
863
---@field numseqs integer
864
---@field genamt integer
865
---@field anotedepth integer
866
---@field settemp number
867
---@field settopp number
868
---@field settopk integer
869
---@field settfs number
870
---@field settypical number
871
---@field settopa number
872
---@field setreppen number
873
---@field setreppenslope number
874
---@field setreppenrange number
875
---@field settknmax integer
876
---@field setwidepth integer
877
---@field setuseprompt boolean
878
---@field setadventure boolean
879
---@field setdynamicscan boolean
880
---@field setnopromptgen boolean
881
---@field setrngpersist boolean
882
---@field temp number
883
---@field topp number
884
---@field topk integer
885
---@field top_p number
886
---@field top_k integer
887
---@field tfs number
888
---@field typical number
889
---@field topa number
890
---@field reppen number
891
---@field reppenslope number
892
---@field reppenrange number
893
---@field tknmax integer
894
---@field widepth integer
895
---@field useprompt boolean
896
---@field adventure boolean
897
---@field dynamicscan boolean
898
---@field nopromptgen boolean
899
---@field rngpersist boolean
900
---@field frmttriminc boolean
901
---@field frmtrmblln boolean
902
---@field frmtrmspch boolean
903
---@field frmtadsnsp boolean
904
---@field frmtsingleline boolean
905
---@field triminc boolean
906
---@field rmblln boolean
907
---@field rmspch boolean
908
---@field adsnsp boolean
909
---@field singleline boolean
910
---@field chatmode boolean
911
---@field chatname string
912
local KoboldSettings = setmetatable({
913
_name = "KoboldSettings",
914
}, metawrapper)
915
local KoboldSettings_mt = setmetatable({}, metawrapper)
916
917
---@generic K
918
---@param t KoboldSettings
919
---@param k K
920
---@return K, any
921
function KoboldSettings_mt._kobold_next(t, k)
922
local v
923
repeat
924
k, v = next()
925
until type(k) ~= "string" or k ~= "_name"
926
return k, v
927
end
928
929
---@param t KoboldSettings
930
---@return function, KoboldSettings, nil
931
function KoboldSettings_mt.__pairs(t)
932
return next, t, nil
933
end
934
935
---@param t KoboldSettings
936
---@return any, boolean
937
function KoboldSettings_mt.__index(t, k)
938
if type(k) ~= "string" then
939
return
940
end
941
if k == "genamt" or k == "output" or k == "setoutput" then
942
return math.tointeger(bridged.get_genamt()), true
943
elseif k == "numseqs" or k == "numseq" or k == "setnumseq" then
944
return math.tointeger(bridged.get_numseqs()), true
945
elseif bridged.has_setting(k) then
946
return bridged.get_setting(k), true
947
else
948
return nil, false
949
end
950
end
951
952
---@param t KoboldSettings_base
953
function KoboldSettings_mt.__newindex(t, k, v)
954
if (k == "genamt" or k == "output" or k == "setoutput") and type(v) == "number" and math.tointeger(v) ~= nil and v >= 0 then
955
bridged.set_genamt(v)
956
koboldbridge.resend_settings_required = true
957
elseif (k == "numseqs" or k == "numseq" or k == "setnumseq") and type(v) == "number" and math.tointeger(v) ~= nil and v >= 1 then
958
if koboldbridge.userstate == "genmod" then
959
error("Cannot set numseqs from a generation modifier")
960
return
961
end
962
bridged.set_numseqs(v)
963
koboldbridge.resend_settings_required = true
964
elseif type(k) == "string" and bridged.has_setting(k) and type(v) == type(bridged.get_setting(k)) then
965
if bridged.set_setting(k, v) == true then
966
maybe_require_regeneration()
967
end
968
koboldbridge.resend_settings_required = true
969
end
970
return t
971
end
972
973
kobold.settings = KoboldSettings
974
975
976
--==========================================================================
977
-- Userscript API: Memory / Author's Note
978
--==========================================================================
979
980
---@param t KoboldLib
981
---@return string
982
function KoboldLib_getters.memory(t)
983
return bridged.get_memory()
984
end
985
986
---@param t KoboldLib
987
---@param v string
988
---@return KoboldLib
989
function KoboldLib_setters.memory(t, v)
990
if type(v) ~= "string" then
991
error("`KoboldLib.memory` must be a string; you attempted to set it to a "..type(v))
992
return
993
end
994
maybe_require_regeneration()
995
bridged.set_memory(v)
996
end
997
998
---@param t KoboldLib
999
---@return string
1000
function KoboldLib_getters.authorsnote(t)
1001
return bridged.get_authorsnote()
1002
end
1003
1004
---@param t KoboldLib
1005
---@param v string
1006
---@return KoboldLib
1007
function KoboldLib_setters.authorsnote(t, v)
1008
if type(v) ~= "string" then
1009
error("`KoboldLib.authorsnote` must be a string; you attempted to set it to a "..type(v))
1010
return
1011
end
1012
maybe_require_regeneration()
1013
bridged.set_authorsnote(v)
1014
end
1015
1016
---@param t KoboldLib
1017
---@return string
1018
function KoboldLib_getters.authorsnotetemplate(t)
1019
return bridged.get_authorsnotetemplate()
1020
end
1021
1022
---@param t KoboldLib
1023
---@param v string
1024
---@return KoboldLib
1025
function KoboldLib_setters.authorsnotetemplate(t, v)
1026
if type(v) ~= "string" then
1027
error("`KoboldLib.authorsnotetemplate` must be a string; you attempted to set it to a "..type(v))
1028
return
1029
end
1030
maybe_require_regeneration()
1031
bridged.set_authorsnotetemplate(v)
1032
end
1033
1034
1035
--==========================================================================
1036
-- Userscript API: User-submitted text (after applying input formatting)
1037
--==========================================================================
1038
1039
---@param t KoboldLib
1040
---@return string
1041
function KoboldLib_getters.submission(t)
1042
return bridged.vars.submission
1043
end
1044
1045
---@param t KoboldLib
1046
---@param v string
1047
function KoboldLib_setters.submission(t, v)
1048
if koboldbridge.userstate ~= "inmod" then
1049
error("Cannot write to `KoboldLib.submission` from outside of an input modifier")
1050
return
1051
elseif type(v) ~= "string" then
1052
error("`KoboldLib.submission` must be a string; you attempted to set it to a " .. type(v))
1053
return
1054
elseif not bridged.vars.gamestarted and v == "" then
1055
error("`KoboldLib.submission` must not be set to the empty string when the story is empty")
1056
return
1057
end
1058
bridged.vars.submission = v
1059
end
1060
1061
1062
--==========================================================================
1063
-- Userscript API: Soft prompt
1064
--==========================================================================
1065
1066
---@param t KoboldLib
1067
---@return string?
1068
function KoboldLib_getters.spfilename(t)
1069
return bridged.get_spfilename()
1070
end
1071
1072
---@param t KoboldLib
1073
---@param v string?
1074
function KoboldLib_setters.spfilename(t, v)
1075
if v:find("/") or v:find("\\") then
1076
error("Cannot set `KoboldLib.spfilename` to a string that contains slashes")
1077
end
1078
if bridged.set_spfilename(v) then
1079
maybe_require_regeneration()
1080
end
1081
end
1082
1083
1084
--==========================================================================
1085
-- Userscript API: Model information
1086
--==========================================================================
1087
1088
---@param t KoboldLib
1089
---@return string
1090
function KoboldLib_getters.modeltype(t)
1091
return bridged.get_modeltype()
1092
end
1093
1094
---@param t KoboldLib
1095
---@param v string
1096
function KoboldLib_setters.modeltype(t, v)
1097
error("`KoboldLib.modeltype` is a read-only attribute")
1098
end
1099
1100
---@param t KoboldLib
1101
---@return string
1102
function KoboldLib_getters.model(t)
1103
return bridged.vars.model
1104
end
1105
1106
---@param t KoboldLib
1107
---@param v string
1108
function KoboldLib_setters.model(t, v)
1109
error("`KoboldLib.model` is a read-only attribute")
1110
end
1111
1112
---@param t KoboldLib
1113
---@return string
1114
function KoboldLib_getters.modelbackend(t)
1115
return bridged.get_modelbackend()
1116
end
1117
1118
---@param t KoboldLib
1119
---@param v string
1120
function KoboldLib_setters.modelbackend(t, v)
1121
error("`KoboldLib.modelbackend` is a read-only attribute")
1122
end
1123
1124
---@param t KoboldLib
1125
---@return boolean
1126
function KoboldLib_getters.is_custommodel(t)
1127
return bridged.is_custommodel()
1128
end
1129
1130
---@param t KoboldLib
1131
---@param v boolean
1132
function KoboldLib_setters.is_custommodel(t, v)
1133
error("`KoboldLib.is_custommodel` is a read-only attribute")
1134
end
1135
1136
---@param t KoboldLib
1137
---@return string
1138
function KoboldLib_getters.custmodpth(t)
1139
return bridged.vars.custmodpth
1140
end
1141
1142
---@param t KoboldLib
1143
---@param v string
1144
function KoboldLib_setters.custmodpth(t, v)
1145
error("`KoboldLib.custmodpth` is a read-only attribute")
1146
end
1147
1148
1149
--==========================================================================
1150
-- Userscript API: Logit Warping
1151
--==========================================================================
1152
1153
---@param t KoboldLib
1154
---@return integer
1155
function KoboldLib_getters.logits_rows(t)
1156
if koboldbridge.userstate ~= "genmod" then
1157
return 0
1158
end
1159
local backend = kobold.modelbackend
1160
if backend == "readonly" or backend == "api" then
1161
return 0
1162
end
1163
return kobold.settings.numseqs
1164
end
1165
1166
---@param t KoboldLib
1167
---@return integer
1168
function KoboldLib_setters.logits_rows(t)
1169
error("`KoboldLib.logits_rows` is a read-only attribute")
1170
end
1171
1172
---@param t KoboldLib
1173
---@return integer
1174
function KoboldLib_getters.logits_cols(t)
1175
if koboldbridge.userstate ~= "genmod" then
1176
return 0
1177
end
1178
local backend = kobold.modelbackend
1179
if backend == "readonly" or backend == "api" then
1180
return 0
1181
end
1182
return math.tointeger(koboldbridge.vocab_size)
1183
end
1184
1185
---@param t KoboldLib
1186
---@return integer
1187
function KoboldLib_setters.logits_cols(t)
1188
error("`KoboldLib.logits_cols` is a read-only attribute")
1189
end
1190
1191
---@param t KoboldLib
1192
---@return table<integer, table<integer, number>>
1193
function KoboldLib_getters.logits(t)
1194
if koboldbridge.userstate ~= "genmod" then
1195
return
1196
end
1197
return koboldbridge.logits
1198
end
1199
1200
---@param t KoboldLib
1201
---@param v table<integer, table<integer, number>>
1202
function KoboldLib_setters.logits(t, v)
1203
if koboldbridge.userstate ~= "genmod" then
1204
error("Cannot write to `KoboldLib.logits` from outside of a generation modifer")
1205
return
1206
elseif type(v) ~= "table" then
1207
error("`KoboldLib.logits` must be a 2D array of numbers; you attempted to set it to a " .. type(v))
1208
return
1209
end
1210
koboldbridge.logits = v
1211
end
1212
1213
1214
--==========================================================================
1215
-- Userscript API: Generated Tokens
1216
--==========================================================================
1217
1218
---@param t KoboldLib
1219
---@return integer
1220
function KoboldLib_getters.generated_rows(t)
1221
local backend = kobold.modelbackend
1222
if backend == "readonly" or backend == "api" then
1223
return 0
1224
elseif koboldbridge.userstate == "outmod" then
1225
return koboldbridge.num_outputs
1226
end
1227
return kobold.settings.numseqs
1228
end
1229
1230
---@param t KoboldLib
1231
---@return integer
1232
function KoboldLib_setters.generated_rows(t)
1233
error("`KoboldLib.generated_rows` is a read-only attribute")
1234
end
1235
1236
---@param t KoboldLib
1237
---@return integer
1238
function KoboldLib_getters.generated_cols(t)
1239
if koboldbridge.userstate ~= "genmod" then
1240
return 0
1241
end
1242
local backend = kobold.modelbackend
1243
if backend == "readonly" or backend == "api" then
1244
return 0
1245
end
1246
return math.tointeger(koboldbridge.generated_cols)
1247
end
1248
1249
---@param t KoboldLib
1250
---@return integer
1251
function KoboldLib_setters.generated_cols(t)
1252
error("`KoboldLib.generated_cols` is a read-only attribute")
1253
end
1254
1255
---@param t KoboldLib
1256
---@return table<integer, table<integer, integer>>
1257
function KoboldLib_getters.generated(t)
1258
if koboldbridge.userstate ~= "genmod" and koboldbridge.userstate ~= "outmod" then
1259
return
1260
end
1261
local backend = kobold.modelbackend
1262
if backend == "readonly" or backend == "api" then
1263
return
1264
end
1265
return koboldbridge.generated
1266
end
1267
1268
---@param t KoboldLib
1269
---@param v table<integer, table<integer, integer>>
1270
function KoboldLib_setters.generated(t, v)
1271
if koboldbridge.userstate ~= "genmod" then
1272
error("Cannot write to `KoboldLib.generated` from outside of a generation modifier")
1273
return
1274
elseif type(v) ~= "table" then
1275
error("`KoboldLib.generated` must be a 2D array of integers; you attempted to set it to a " .. type(v))
1276
return
1277
end
1278
koboldbridge.generated = v
1279
end
1280
1281
1282
--==========================================================================
1283
-- Userscript API: Output
1284
--==========================================================================
1285
1286
---@param t KoboldLib
1287
---@return integer
1288
function KoboldLib_getters.num_outputs(t)
1289
local model = kobold.model
1290
if model == "OAI" or model == "InferKit" then
1291
return 1
1292
end
1293
if koboldbridge.userstate == "outmod" then
1294
return koboldbridge.num_outputs
1295
end
1296
return kobold.settings.numseqs
1297
end
1298
1299
---@param t KoboldLib
1300
---@return integer
1301
function KoboldLib_setters.num_outputs(t)
1302
error("`KoboldLib.num_outputs` is a read-only attribute")
1303
end
1304
1305
---@param t KoboldLib
1306
---@return table<integer, string>
1307
function KoboldLib_getters.outputs(t)
1308
if koboldbridge.userstate ~= "outmod" then
1309
return
1310
end
1311
return koboldbridge.outputs
1312
end
1313
1314
---@param t KoboldLib
1315
---@param v table<integer, string>
1316
function KoboldLib_setters.outputs(t, v)
1317
if koboldbridge.userstate ~= "outmod" then
1318
error("Cannot write to `KoboldLib.outputs` from outside of an output modifier")
1319
return
1320
elseif type(v) ~= "table" then
1321
error("`KoboldLib.outputs` must be a 1D array of strings; you attempted to set it to a " .. type(v))
1322
return
1323
end
1324
koboldbridge.outputs = v
1325
end
1326
1327
1328
--==========================================================================
1329
-- Userscript API: Utilities
1330
--==========================================================================
1331
1332
---@param str string
1333
---@return table<integer, integer>
1334
function kobold.encode(str)
1335
if type(str) ~= "string" then
1336
error("`encode` takes a string as argument, but got a " .. type(str))
1337
return
1338
end
1339
local encoded = {}
1340
for i, token in _python.enumerate(bridged.encode(str)) do
1341
encoded[i+1] = math.tointeger(token)
1342
end
1343
return encoded
1344
end
1345
1346
---@param tok integer|table<integer, integer>
1347
---@return string
1348
function kobold.decode(tok)
1349
if type(tok) ~= "number" and type(tok) ~= "table" then
1350
error("`decode` takes a number or table of numbers as argument, but got a " .. type(tok))
1351
return
1352
end
1353
if type(tok) == "number" then
1354
tok = {tok}
1355
end
1356
local _tok = {}
1357
local _v
1358
for k, v in ipairs(tok) do
1359
_v = math.tointeger(v)
1360
if _v == nil then
1361
error "`decode` got a table with one or more non-integer values"
1362
return
1363
end
1364
_tok[k] = _v
1365
end
1366
return bridged.decode(_tok)
1367
end
1368
1369
---@return nil
1370
function kobold.halt_generation()
1371
koboldbridge.generating = false
1372
end
1373
1374
---@param sequence? integer
1375
---@return nil
1376
function kobold.restart_generation(sequence)
1377
if sequence == nil then
1378
sequence = 0
1379
end
1380
sequence_type = type(sequence)
1381
sequence = math.tointeger(sequence)
1382
if sequence_type ~= "number" then
1383
error("`kobold.restart_generation` takes an integer greater than or equal to 0 or nil as argument, but got a " .. sequence_type)
1384
return
1385
elseif sequence < 0 then
1386
error("`kobold.restart_generation` takes an integer greater than or equal to 0 or nil as argument, but got `" .. sequence .. "`")
1387
return
1388
end
1389
if koboldbridge.userstate ~= "outmod" then
1390
error("Can only call `kobold.restart_generation()` from an output modifier")
1391
return
1392
end
1393
koboldbridge.restart_sequence = sequence
1394
end
1395
1396
---@param t KoboldCoreLib
1397
---@return string
1398
function KoboldLib_getters.feedback(t)
1399
return koboldbridge.feedback
1400
end
1401
1402
---@param t KoboldCoreLib
1403
---@param v string
1404
---@return KoboldCoreLib
1405
function KoboldLib_setters.feedback(t, v)
1406
error("`KoboldLib.feedback` is a read-only attribute")
1407
end
1408
1409
1410
--==========================================================================
1411
-- Core script API
1412
--==========================================================================
1413
1414
koboldbridge.userscripts = {} ---@type table<integer, KoboldUserScriptModule>
1415
koboldbridge.userscriptmodule_filename_map = {} ---@type table<KoboldUserScriptModule, string>
1416
koboldbridge.num_userscripts = 0
1417
koboldbridge.inmod = nil ---@type function?
1418
koboldbridge.genmod = nil ---@type function?
1419
koboldbridge.outmod = nil ---@type function?
1420
1421
---@class KoboldUserScript
1422
---@field inmod? function
1423
---@field genmod? function
1424
---@field outmod? function
1425
1426
---@class KoboldCoreScript
1427
---@field inmod? function
1428
---@field genmod? function
1429
---@field outmod? function
1430
1431
1432
----------------------------------------------------------------------------
1433
1434
---@class KoboldUserScriptModule
1435
---@field filename string
1436
---@field modulename string
1437
---@field description string
1438
---@field is_config_file_open boolean
1439
---@field inmod? function
1440
---@field genmod? function
1441
---@field outmod? function
1442
local KoboldUserScriptModule = setmetatable({
1443
_name = "KoboldUserScriptModule",
1444
}, metawrapper)
1445
local KoboldUserScriptModule_mt = setmetatable({}, metawrapper)
1446
1447
local KoboldUserScriptModule_fields = {
1448
filename = false,
1449
modulename = false,
1450
description = false,
1451
inmod = false,
1452
genmod = false,
1453
outmod = false,
1454
}
1455
1456
---@param clear? boolean
1457
---@return file*
1458
function KoboldUserScriptModule:get_config_file(clear)
1459
return get_config_file(koboldbridge.userscriptmodule_filename_map[self], clear)
1460
end
1461
1462
---@generic K
1463
---@param t KoboldUserScriptModule
1464
---@param k K
1465
---@return K, any
1466
function KoboldUserScriptModule_mt._kobold_next(t, k)
1467
k = (next(KoboldUserScriptModule_fields, k))
1468
return k, t[k]
1469
end
1470
1471
---@param t KoboldUserScriptModule
1472
---@return function, KoboldUserScriptModule, nil
1473
function KoboldUserScriptModule_mt.__pairs(t)
1474
return next, t, nil
1475
end
1476
1477
---@param t KoboldUserScriptModule
1478
function KoboldUserScriptModule_mt.__index(t, k)
1479
if type(k) == "string" and KoboldUserScriptModule_fields[k] ~= nil then
1480
return rawget(t, "_" .. k)
1481
elseif k == "is_config_file_open" then
1482
return is_config_file_open(koboldbridge.userscriptmodule_filename_map[t])
1483
end
1484
return rawget(t, k)
1485
end
1486
1487
---@param t KoboldUserScriptModule
1488
function KoboldUserScriptModule_mt.__newindex(t, k, v)
1489
error("`"..rawget(t, "_name").."` is a read-only class")
1490
end
1491
1492
1493
----------------------------------------------------------------------------
1494
1495
---@class KoboldUserScriptList_base
1496
---@type table<integer, KoboldUserScriptModule>
1497
local _ = {}
1498
1499
---@class KoboldUserScriptList : KoboldUserScriptList_base
1500
local KoboldUserScriptList = setmetatable({
1501
_name = "KoboldUserScriptList",
1502
}, metawrapper)
1503
local KoboldUserScriptList_mt = setmetatable({}, metawrapper)
1504
1505
---@param t KoboldUserScriptList
1506
---@return integer
1507
function KoboldUserScriptList_mt.__len(t)
1508
return koboldbridge.num_userscripts
1509
end
1510
1511
---@param t KoboldUserScriptList
1512
---@param k integer
1513
---@return KoboldUserScriptModule?
1514
function KoboldUserScriptList_mt.__index(t, k)
1515
if type(k) == "number" and math.tointeger(k) ~= nil then
1516
return koboldbridge.userscripts[k]
1517
end
1518
end
1519
1520
---@generic K
1521
---@param t KoboldUserScriptList
1522
---@param k K
1523
---@return K, any
1524
function KoboldUserScriptList_mt._kobold_next(t, k)
1525
if k == nil then
1526
k = 0
1527
elseif type(k) ~= "number" then
1528
return nil
1529
end
1530
k = k + 1
1531
local v = t[k]
1532
if v == nil then
1533
return nil
1534
end
1535
return v.filename, v
1536
end
1537
1538
---@param t KoboldUserScriptList
1539
---@return function, KoboldUserScriptList, nil
1540
function KoboldUserScriptList_mt.__pairs(t)
1541
return next, t, nil
1542
end
1543
1544
---@param t KoboldUserScriptList
1545
function KoboldUserScriptList_mt.__newindex(t, k, v)
1546
error("`"..rawget(t, "_name").."` is a read-only class")
1547
end
1548
1549
1550
----------------------------------------------------------------------------
1551
1552
---@param t KoboldCoreLib
1553
---@return string
1554
function KoboldCoreLib_getters.userscripts(t)
1555
return koboldbridge.userscripts
1556
end
1557
1558
---@param t KoboldCoreLib
1559
---@param v string
1560
---@return KoboldCoreLib
1561
function KoboldCoreLib_setters.userscripts(t, v)
1562
error("`KoboldCoreLib.userscripts` is a read-only attribute")
1563
end
1564
1565
1566
--==========================================================================
1567
-- Sandboxing code
1568
--==========================================================================
1569
1570
local envs = {}
1571
koboldbridge.logging_name = nil
1572
koboldbridge.filename = nil
1573
1574
local sandbox_require_builtins = {
1575
coroutine = true,
1576
package = true,
1577
string = true,
1578
utf8 = true,
1579
table = true,
1580
math = true,
1581
io = true,
1582
os = true,
1583
debug = true,
1584
}
1585
1586
local old_load = load
1587
local function _safe_load(_g)
1588
return function(chunk, chunkname, mode, env)
1589
if mode == nil then
1590
mode = "t"
1591
elseif mode ~= "t" then
1592
error("Calling `load` with a `mode` other than 't' is disabled for security reasons")
1593
return
1594
end
1595
if env == nil then
1596
env = _g
1597
end
1598
return old_load(chunk, chunkname, mode, env)
1599
end
1600
end
1601
1602
local old_loadfile = loadfile
1603
local package_loaded = {} ---@type table<table, table>
1604
local old_package_searchers = package.searchers
1605
---@param modname string
1606
---@param env table<string, any>
1607
---@param search_paths? string|table<integer, string>
1608
---@return any, string?
1609
local function requirex(modname, env, search_paths)
1610
if search_paths == nil then
1611
search_paths = bridged.lib_paths
1612
end
1613
if modname == "bridge" then
1614
return function() return env.kobold, env.koboldcore end
1615
end
1616
if type(modname) == "number" then
1617
modname = tostring(modname)
1618
elseif type(modname) ~= "string" then
1619
error("bad argument #1 to 'require' (string expected, got "..type(modname)..")")
1620
return
1621
end
1622
if sandbox_require_builtins[modname] then
1623
return env[modname]
1624
end
1625
local allowsearch = type(modname) == "string" and string.match(modname, "[^%w._-]") == nil and string.match(modname, "%.%.") == nil
1626
if allowsearch and package_loaded[env] == nil then
1627
package_loaded[env] = {}
1628
elseif allowsearch and package_loaded[env][modname] then
1629
return package_loaded[env][modname]
1630
end
1631
local loader, path
1632
local errors = {}
1633
local n_errors = 0
1634
set_require_path(search_paths)
1635
for k, v in ipairs(old_package_searchers) do
1636
loader, path = v(modname)
1637
if allowsearch and type(loader) == "function" then
1638
break
1639
elseif type(loader) == "string" then
1640
n_errors = n_errors + 1
1641
errors[n_errors] = "\n\t" .. loader
1642
end
1643
end
1644
set_require_path(bridged.lib_paths)
1645
if not allowsearch or type(loader) ~= "function" then
1646
error("module '" .. modname .. "' not found:" .. table.concat(errors))
1647
return
1648
end
1649
local f, err = old_loadfile(path, "t", env)
1650
if err ~= nil then
1651
error(err)
1652
return
1653
end
1654
local retval = (f())
1655
package_loaded[env][modname] = retval == nil or retval
1656
return package_loaded[env][modname], path
1657
end
1658
local function _safe_require(_g)
1659
---@param modname string
1660
---@return any, string?
1661
return function(modname)
1662
return requirex(modname, _g)
1663
end
1664
end
1665
1666
local old_input = io.input
1667
---@param file? string|file*
1668
local function safe_input(file)
1669
if type(file) == "string" then
1670
error("Calling `io.input` with a string as argument is disabled for security reasons")
1671
return
1672
end
1673
return old_input(file)
1674
end
1675
1676
local old_output = io.output
1677
---@param file? string|file*
1678
local function safe_output(file)
1679
if type(file) == "string" then
1680
error("Calling `io.output` with a string as argument is disabled for security reasons")
1681
return
1682
end
1683
return old_output(file)
1684
end
1685
1686
local old_lines = io.lines
1687
---@param filename? string
1688
local function safe_lines(filename, ...)
1689
if type(filename) == "string" then
1690
error("Calling `io.lines` with a string as first argument is disabled for security reasons")
1691
return
1692
end
1693
return old_lines(filename, ...)
1694
end
1695
1696
local function redirected_print(...)
1697
local args = table.pack(...)
1698
for i = 1, args.n do
1699
args[i] = tostring(args[i])
1700
end
1701
bridged.print(table.concat(args, "\t"))
1702
end
1703
1704
local function _redirected_warn()
1705
local do_warning = true
1706
local control_table = {
1707
["@on"] = function()
1708
do_warning = true
1709
end,
1710
["@off"] = function()
1711
do_warning = false
1712
end,
1713
}
1714
return function(...)
1715
local args = table.pack(...)
1716
if args.n == 1 and type(args[1]) == "string" and args[1]:sub(1, 1) == "@" then
1717
local f = control_table[args[1]]
1718
if f ~= nil then
1719
f()
1720
end
1721
return
1722
end
1723
if not do_warning then
1724
return
1725
end
1726
for i = 1, args.n do
1727
args[i] = tostring(args[i])
1728
end
1729
bridged.warn(table.concat(args, "\t"))
1730
end
1731
end
1732
1733
local sandbox_template_env = {
1734
assert = assert,
1735
connectgarbage = collectgarbage,
1736
error = error,
1737
getmetatable = getmetatable,
1738
ipairs = ipairs,
1739
load = nil, ---@type function
1740
next = next,
1741
pairs = pairs,
1742
pcall = pcall,
1743
print = nil, ---@type function
1744
rawequal = rawequal,
1745
rawget = rawget,
1746
rawlen = rawlen,
1747
rawset = rawset,
1748
select = select,
1749
setmetatable = setmetatable,
1750
tonumber = tonumber,
1751
tostring = tostring,
1752
type = type,
1753
_VERSION = _VERSION,
1754
warn = nil, ---@type function
1755
xpcall = xpcall,
1756
coroutine = {
1757
close = coroutine.close,
1758
create = coroutine.create,
1759
isyieldable = coroutine.isyieldable,
1760
resume = coroutine.resume,
1761
running = coroutine.running,
1762
status = coroutine.status,
1763
wrap = coroutine.wrap,
1764
yield = coroutine.yield,
1765
},
1766
require = nil, ---@type function
1767
package = {
1768
config = package.config,
1769
},
1770
string = {
1771
byte = string.byte,
1772
char = string.char,
1773
dump = string.dump,
1774
find = string.find,
1775
format = string.format,
1776
gmatch = string.gmatch,
1777
gsub = string.gsub,
1778
len = string.len,
1779
lower = string.lower,
1780
match = string.match,
1781
pack = string.pack,
1782
packsize = string.packsize,
1783
rep = string.rep,
1784
reverse = string.reverse,
1785
sub = string.sub,
1786
unpack = string.unpack,
1787
upper = string.upper,
1788
},
1789
utf8 = {
1790
char = utf8.char,
1791
charpattern = utf8.charpattern,
1792
codes = utf8.codes,
1793
codepoint = utf8.codepoint,
1794
len = utf8.len,
1795
offset = utf8.offset,
1796
},
1797
table = {
1798
concat = table.concat,
1799
insert = table.insert,
1800
move = table.move,
1801
pack = table.pack,
1802
remove = table.remove,
1803
sort = table.sort,
1804
unpack = table.unpack,
1805
},
1806
math = {
1807
abs = math.abs,
1808
acos = math.acos,
1809
asin = math.asin,
1810
atan = math.atan,
1811
atan2 = math.atan2,
1812
ceil = math.ceil,
1813
cos = math.cos,
1814
cosh = math.cosh,
1815
deg = math.deg,
1816
exp = math.exp,
1817
floor = math.floor,
1818
fmod = math.fmod,
1819
frexp = math.frexp,
1820
huge = math.huge,
1821
ldexp = math.ldexp,
1822
log = math.log,
1823
log10 = math.log10,
1824
max = math.max,
1825
maxinteger = math.maxinteger,
1826
min = math.min,
1827
mininteger = math.mininteger,
1828
modf = math.modf,
1829
pi = math.pi,
1830
pow = math.pow,
1831
rad = math.rad,
1832
random = math.random,
1833
randomseed = function() warn("WARNING: math.randomseed() is not permitted; please use the mt19937ar library instead") end,
1834
sin = math.sin,
1835
sinh = math.sinh,
1836
sqrt = math.sqrt,
1837
tan = math.tan,
1838
tanh = math.tanh,
1839
tointeger = math.tointeger,
1840
type = math.type,
1841
ult = math.ult,
1842
},
1843
io = {
1844
stdin = io.stdin,
1845
stdout = io.stdout,
1846
stderr = io.stderr,
1847
input = safe_input,
1848
output = safe_output,
1849
read = io.read,
1850
write = io.write,
1851
close = _new_close(io.close),
1852
lines = safe_lines,
1853
flush = io.flush,
1854
type = io.type,
1855
},
1856
os = {
1857
clock = os.clock,
1858
date = os.date,
1859
difftime = os.difftime,
1860
exit = function() end,
1861
getenv = os.getenv,
1862
time = os.time,
1863
tmpname = os.tmpname,
1864
},
1865
debug = {
1866
getinfo = debug.getinfo,
1867
gethook = debug.gethook,
1868
getmetatable = debug.getmetatable,
1869
getuservalue = debug.getuservalue,
1870
sethook = debug.sethook,
1871
setmetatable = debug.setmetatable,
1872
setuservalue = debug.setuservalue,
1873
traceback = debug.traceback,
1874
upvalueid = debug.upvalueid,
1875
},
1876
}
1877
1878
function koboldbridge.get_universe(universe)
1879
local env = envs[universe]
1880
if env == nil then
1881
envs[universe] = deepcopy(sandbox_template_env)
1882
env = envs[universe]
1883
envs[universe].kobold = deepcopy(kobold)
1884
if universe == 0 then
1885
envs[universe].koboldcore = deepcopy(koboldcore)
1886
end
1887
envs[universe].load = _safe_load(env)
1888
envs[universe].require = _safe_require(env)
1889
envs[universe].print = redirected_print
1890
envs[universe].warn = _redirected_warn()
1891
env._G = env
1892
end
1893
return env
1894
end
1895
1896
function koboldbridge.obliterate_multiverse()
1897
for k, v in pairs(config_files) do
1898
pcall(v.close, v)
1899
end
1900
envs = {}
1901
koboldbridge.userscripts = {}
1902
koboldbridge.num_userscripts = 0
1903
koboldbridge.inmod = nil
1904
koboldbridge.genmod = nil
1905
koboldbridge.outmod = nil
1906
end
1907
1908
1909
--==========================================================================
1910
-- API for aiserver.py
1911
--==========================================================================
1912
1913
---@return boolean
1914
function koboldbridge.load_userscripts(filenames, modulenames, descriptions)
1915
config_files = {}
1916
config_file_filename_map = {}
1917
koboldbridge.userscripts = {}
1918
koboldbridge.userscriptmodule_filename_map = {}
1919
koboldbridge.num_userscripts = 0
1920
local has_genmod = false
1921
for i, filename in _python.enumerate(filenames) do
1922
bridged.load_callback(filename, modulenames[i])
1923
koboldbridge.logging_name = modulenames[i]
1924
koboldbridge.filename = filename
1925
local f, err = old_loadfile(join_folder_and_filename(bridged.userscript_path, filename), "t", koboldbridge.get_universe(filename))
1926
if err ~= nil then
1927
error(err)
1928
return false
1929
end
1930
---@type KoboldUserScript
1931
local _userscript = f()
1932
koboldbridge.logging_name = nil
1933
koboldbridge.filename = nil
1934
if _userscript.genmod ~= nil then
1935
has_genmod = true
1936
end
1937
local userscript = deepcopy(KoboldUserScriptModule)
1938
rawset(userscript, "_inmod", function()
1939
koboldbridge.logging_name = modulenames[i]
1940
koboldbridge.filename = filename
1941
if _userscript.inmod ~= nil then
1942
_userscript.inmod()
1943
end
1944
koboldbridge:clear_userscript_metadata()
1945
end)
1946
rawset(userscript, "_genmod", function()
1947
koboldbridge.logging_name = modulenames[i]
1948
koboldbridge.filename = filename
1949
if _userscript.genmod ~= nil then
1950
_userscript.genmod()
1951
end
1952
koboldbridge:clear_userscript_metadata()
1953
end)
1954
rawset(userscript, "_outmod", function()
1955
koboldbridge.logging_name = modulenames[i]
1956
koboldbridge.filename = filename
1957
if _userscript.outmod ~= nil then
1958
_userscript.outmod()
1959
end
1960
koboldbridge:clear_userscript_metadata()
1961
end)
1962
rawset(userscript, "_filename", filename)
1963
rawset(userscript, "_modulename", modulenames[i])
1964
rawset(userscript, "_description", descriptions[i])
1965
koboldbridge.userscripts[i+1] = userscript
1966
koboldbridge.userscriptmodule_filename_map[userscript] = filename
1967
koboldbridge.num_userscripts = i + 1
1968
end
1969
return has_genmod
1970
end
1971
1972
---@return nil
1973
function koboldbridge.load_corescript(filename)
1974
local f, err = old_loadfile(join_folder_and_filename(bridged.corescript_path, filename), "t", koboldbridge.get_universe(0))
1975
if err ~= nil then
1976
error(err)
1977
return
1978
end
1979
---@type KoboldCoreScript
1980
local corescript = f()
1981
koboldbridge.inmod = corescript.inmod
1982
koboldbridge.genmod = corescript.genmod
1983
koboldbridge.outmod = corescript.outmod
1984
end
1985
1986
function koboldbridge.execute_inmod()
1987
local r
1988
koboldbridge:clear_userscript_metadata()
1989
koboldbridge.restart_sequence = nil
1990
koboldbridge.userstate = "inmod"
1991
koboldbridge.regeneration_required = false
1992
koboldbridge.generating = true
1993
koboldbridge.generated_cols = 0
1994
koboldbridge.generated = {}
1995
if koboldbridge.inmod ~= nil then
1996
r = koboldbridge.inmod()
1997
end
1998
for i = 1, kobold.settings.numseqs do
1999
koboldbridge.generated[i] = {}
2000
end
2001
koboldbridge.outputs = {}
2002
for i = 1, kobold.num_outputs do
2003
koboldbridge.outputs[i] = {}
2004
end
2005
return r
2006
end
2007
2008
---@return any, boolean
2009
function koboldbridge.execute_genmod()
2010
local r
2011
koboldbridge:clear_userscript_metadata()
2012
koboldbridge.generating = true
2013
koboldbridge.userstate = "genmod"
2014
if koboldbridge.genmod ~= nil then
2015
local _generated = deepcopy(koboldbridge.generated)
2016
if not bridged.vars.nogenmod then
2017
r = koboldbridge.genmod()
2018
end
2019
setmetatable(koboldbridge.logits, nil)
2020
for kr, vr in old_next, koboldbridge.logits, nil do
2021
setmetatable(vr, nil)
2022
for kc, vc in old_next, vr, nil do
2023
if type(vc) ~= "number" then
2024
error("`kobold.logits` must be a 2D table of numbers, but found a non-number element at row " .. kr .. ", column " .. kc)
2025
return r
2026
end
2027
end
2028
end
2029
setmetatable(koboldbridge.generated, nil)
2030
for kr, vr in old_next, koboldbridge.generated, nil do
2031
setmetatable(vr, nil)
2032
for kc, vc in old_next, vr, nil do
2033
if math.tointeger(vc) == nil then
2034
error("`kobold.generated` must be a 2D table of integers, but found a non-integer element at row " .. kr .. ", column " .. kc)
2035
return r
2036
end
2037
vr[kc] = math.tointeger(vc)
2038
if vr[kc] ~= _generated[kr][kc] then
2039
maybe_require_regeneration()
2040
end
2041
end
2042
end
2043
end
2044
koboldbridge.generated_cols = koboldbridge.generated_cols + 1
2045
return r
2046
end
2047
2048
function koboldbridge.execute_outmod()
2049
local r
2050
koboldbridge:clear_userscript_metadata()
2051
koboldbridge.generating = false
2052
koboldbridge.userstate = "outmod"
2053
koboldbridge.num_outputs = kobold.settings.numseqs
2054
if koboldbridge.outmod ~= nil then
2055
local _outputs = deepcopy(koboldbridge.outputs)
2056
r = koboldbridge.outmod()
2057
setmetatable(koboldbridge.outputs, nil)
2058
for k, v in old_next, koboldbridge.outputs, nil do
2059
if type(v) ~= "string" then
2060
error("`kobold.outputs` must be a 1D array of strings, but found a non-string element at index " .. k)
2061
return r
2062
end
2063
if v ~= _outputs[k] then
2064
maybe_require_regeneration()
2065
end
2066
end
2067
end
2068
koboldbridge.userstate = nil
2069
return r
2070
end
2071
2072
2073
--==========================================================================
2074
-- Footer
2075
--==========================================================================
2076
2077
metawrapper.__newindex = nil
2078
setmetatable(KoboldWorldInfoEntry, KoboldWorldInfoEntry_mt)
2079
setmetatable(KoboldWorldInfoFolder, KoboldWorldInfoFolder_mt)
2080
setmetatable(KoboldWorldInfoFolderSelector, KoboldWorldInfoFolderSelector_mt)
2081
setmetatable(KoboldWorldInfo, KoboldWorldInfo_mt)
2082
setmetatable(KoboldStoryChunk, KoboldStoryChunk_mt)
2083
setmetatable(KoboldStory, KoboldStory_mt)
2084
setmetatable(KoboldSettings, KoboldSettings_mt)
2085
setmetatable(KoboldUserScriptModule, KoboldUserScriptModule_mt)
2086
setmetatable(KoboldUserScriptList, KoboldUserScriptList_mt)
2087
setmetatable(kobold, KoboldLib_mt)
2088
setmetatable(koboldcore, KoboldCoreLib_mt)
2089
2090
return kobold, koboldcore, koboldbridge
2091
end
2092
2093