Skip to content

Instantly share code, notes, and snippets.

@sunho
Created July 15, 2024 07:21
Show Gist options
  • Save sunho/5c2ee82710c59b05282978fa0b3a7227 to your computer and use it in GitHub Desktop.
Save sunho/5c2ee82710c59b05282978fa0b3a7227 to your computer and use it in GitHub Desktop.
local pattern = "[Current date and time:*|*Location:(*)|*Character:*|*Emotion:(*)|*outfit:*|*Situation:*|*Inner*thoughts:*]"
local assets = {"Adventurers_Guild_Afternoon", "Adventurers_Guild_Morning", "Adventurers_Guild_Night", "Bedroom_Afternoon", "Bedroom_Morning", "Bedroom_Night", "Bookstore _Afternoon", "Bookstore_Morning", "Bookstore_Night", "Clothing_shop_Afternoon", "Clothing_shop_Morning", "Clothing_shop_Night", "Dungeon_Afternoon", "Dungeon_Morning", "Dungeon_Night", "Living_room_Afternoon", "Living_room_Morning", "Living_room_Night", "Magic_Classroom_Afternoon", "Magic_Classroom_Morning", "Magic_Classroom_Night", "Market_place_Afternoon", "Market_place_Morning", "Market_place_Night", "Mountain_Afternoon", "Mountain_Morning", "Mountain_Night", "Ocean_Afternoon", "Ocean_Morning", "Ocean_Night", "Park_Afternoon", "Park_Morning", "Park_Night", "Potion_Shop_Afternoon", "Potion_Shop_Morning", "Potion_Shop_Night", "Restaurant_Afternoon", "Restaurant_Morning", "Restaurant_Night", "Restroom_Afternoon", "Restroom_Morning", "Restroom_Night", "Street_Afternoon", "Street_Morning", "Street_Night", "Weapons_Shop_Afternoon", "Weapons_Shop_Morning", "Weapons_Shop_Night", "Yard_Afternoon", "Yard_Morning", "Yard_Night"}
local assets2 = {"After_sex", "angry", "annoyed", "aroused", "blushing_shyly", "bored", "childlike_whining", "comforted", "confused", "contemptuous", "coughing", "crying_with_eyes_closed", "crying_with_eyes open", "curious", "Dating_with_Adair", "dazed", "depressed", "disappointed", "disgusted", "embarrassed", "evil_smile", "fidgeting_shyly", "flustered", "forced_smile", "full-face_blush", "giggling", "guilty", "happy_smile", "indifferent", "Injured", "joyful", "laughing", "looking_away_shyly", "lovestuck", "nervous_pout", "nervous_smile", "nervous", "pout", "proud", "sad", "scared", "seductive_smile", "serious", "shocked", "Sleep", "sleepy", "smile", "smirk", "smug", "surprised", "thinking", "Use_magic_in_fight", "worried", "Magic class_"}
local asset_list = {
[1]=assets,
[2]=assets2
}
local correction_prompt = [[
Here is the list of images that user typed in; $1
Correct the user's list of images based on the list of available images using clossest match.
ONLY use the image from this list: $2
<output format>
user input image:corrected image
</output format>
<output example>
love:lovestruck
joy:joyful
</output example>
Do not output anything else outside of the format specified above.
]]
local sorting_method = 'levenshtein'
-- local sorting_method = 'lcs_with_gap'
local use_simple_llm = false
function create_prompt(images)
local asset_map = {}
for _, list in ipairs(asset_list) do
for i, asset in pairs(list) do
asset_map[asset] = 1
end
end
local all_assets = {}
for asset, _ in pairs(asset_map) do
table.insert(all_assets, asset)
end
local prompt = correction_prompt:gsub("$1", table.concat(images, ", "))
prompt = prompt:gsub("$2", table.concat(all_assets, ", "))
return prompt
end
function string:split( inSplitPattern, outResults )
if not outResults then
outResults = { }
end
local theStart = 1
local theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart )
while theSplitStart do
table.insert( outResults, string.sub( self, theStart, theSplitStart-1 ) )
theStart = theSplitEnd + 1
theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart )
end
table.insert( outResults, string.sub( self, theStart ) )
return outResults
end
function process_response(images, response)
local correction_pairs = response:split("\n")
local images_map = {}
for _, image in pairs(images) do
images_map[image] = 1
end
local correction_map = {}
for key, value in pairs(correction_pairs) do
pair = value:split(":")
if #pair == 2 and images_map[pair[1]] then
correction_map[pair[1]] = pair[2]
end
end
return correction_map
end
function escape(text)
return text:gsub("([^%w])", "%%%1")
end
function translate(pattern)
local codes = {}
for p, c in utf8.codes(pattern) do
table.insert(codes, utf8.char(c))
end
local out = {}
local i = 1
local stars = 0
while i <= #codes do
if codes[i] == "*" then
if i - 1 >= 1 and i + 1 <= #codes then
if codes[i - 1] == "(" and codes[i + 1] == ")" then
table.remove(out)
table.insert(out, '^')
i = i + 2
stars = stars + 1
goto continue
end
end
end
table.insert(out, codes[i])
i = i + 1
::continue::
end
pattern = table.concat(out)
pattern = escape(pattern)
gpattern = pattern:gsub("%%%*", "%%s*.-%%s*")
gpattern = gpattern:gsub("%%%^", "%%s*.-%%s*")
pattern = pattern:gsub("%%%*", "%%s*.-%%s*")
pattern = pattern:gsub("%%%^", "%%s*()(.-)()%%s*")
return gpattern, pattern, stars
end
function print_table(tbl)
for key, value in pairs(tbl) do
if type(value) == "table" then
print(key)
print_table(value)
else
print(key, value)
end
end
end
function edit_distance(str1, str2, cache)
if cache[str1..'-'..str2] then
return cache[str1..'-'..str2]
end
if cache[str2..'-'..str1] then
return cache[str2..'-'..str1]
end
local len1 = #str1
local len2 = #str2
local dp = {}
for i = 0, len1 do
dp[i] = {}
dp[i][0] = i
end
for j = 0, len2 do
dp[0][j] = j
end
for i = 1, len1 do
for j = 1, len2 do
local cost = (str1:sub(i, i) == str2:sub(j, j)) and 0 or 1
dp[i][j] = math.min(
dp[i - 1][j] + 1,
dp[i][j - 1] + 1,
dp[i - 1][j - 1] + cost
)
end
end
cache[str1..'-'..str2] = dp[len1][len2]
return dp[len1][len2]
end
local MAX_WORD_LEN = 100 -- Adjust this as necessary
local inf = math.huge
function lcs_with_gap(small, large, cache)
if cache[small..'-'..large] then
return cache[small..'-'..large]
end
if cache[large..'-'..small] then
return cache[large..'-'..small]
end
local m = #small
local n = #large
local kk = math.min(m,n)
local dp = {}
for i = 0, m do
dp[i] = {}
for j = 0, n do
dp[i][j] = {}
for key = 0, kk do
dp[i][j][key] = {}
for key2 = 0, 1 do
dp[i][j][key][key2] = inf
end
end
end
end
dp[0][0][0][0] = 0
for i = 0, m do
for j = 0, n do
for k = 0, kk do
if k < kk and i < m and j < n and small:sub(i + 1, i + 1) == large:sub(j + 1, j + 1) then
dp[i + 1][j + 1][k + 1][1] = math.min(dp[i + 1][j + 1][k + 1][1], dp[i][j][k][0] + 1, dp[i][j][k][1])
end
if j + 1 <= n then
dp[i][j + 1][k][0] = math.min(dp[i][j + 1][k][0], dp[i][j][k][0], dp[i][j][k][1])
end
if i + 1 <= m then
dp[i + 1][j][k][0] = math.min(dp[i + 1][j][k][0], dp[i][j][k][0], dp[i][j][k][1])
end
end
end
end
for k = kk, 0, -1 do
local res = inf
for i = 0, m do
for j = 0, n do
res = math.min(res, dp[i][j][k][0], dp[i][j][k][1])
end
end
if res ~= inf then
cache[small..'-'..large] = {-k, res}
return {-k, res}
end
end
cache[small..'-'..large] = {inf, inf}
return {inf, inf}
end
function compare_pairs(pair1, pair2)
if pair1[1] < pair2[1] then
return true
elseif pair1[1] > pair2[1] then
return false
else
return pair1[2] < pair2[2]
end
end
function sort_by_lcs(arr, target)
cache = {}
table.sort(arr, function(a, b)
return compare_pairs(lcs_with_gap(a, target, cache), lcs_with_gap(b, target, cache))
end)
end
function sort_by_edit_distance(arr, target)
local cache = {}
table.sort(arr, function(a, b)
return edit_distance(a, target, cache) < edit_distance(b, target, cache)
end)
end
function do_correction(asset_num, asset_name)
local cloned = {}
for i, asset in pairs(asset_list[asset_num]) do
table.insert(cloned, asset)
end
if sorting_method == 'levenshtein' then
sort_by_edit_distance(cloned, asset_name)
else
sort_by_lcs(cloned, asset_name)
end
return cloned[1]
end
function run(triggerId, text)
local lua_gpattern, lua_pattern, stars = translate(pattern)
local results = {}
for start_pos, match, end_pos in text:gmatch("()(" .. lua_gpattern .. ")()") do
print(start_pos, end_pos, match)
table.insert(results, { start_pos=start_pos, end_pos=end_pos , match = { match:match(lua_pattern) }})
end
local captures = {}
for i, match in ipairs(results) do
print("Match " .. i .. ":")
for j = 1, stars do
local st, cap, en = match.match[(j-1)*3+1], match.match[(j-1)*3+2], match.match[(j-1)*3+3]
st = st + match.start_pos - 1
en = en + match.start_pos - 1
table.insert(captures, { num = j, cap = cap, start_pos = st, end_pos = en })
end
end
local images_map = {}
for i, capture in ipairs(captures) do
images_map[capture.cap] = 1
end
local images = {}
for key, _ in pairs(images_map) do
table.insert(images, key)
end
local prompt = create_prompt(images)
print(prompt)
local llm_map = {}
-- if use_simple_llm then
-- local res = simpleLLM(triggerId, prompt):await()
-- print(res.result)
-- if res and res.success then
-- llm_map = process_response(images, res.result)
-- end
-- else
-- local res = LLMMain(triggerId, json.encode({{role="user",content=prompt}})):await()
-- print(res)
-- if res then
-- local decoded = json.decode(res)
-- if decoded.success then
-- llm_map = process_response(images, decoded.result)
-- end
-- end
-- end
-- print(llm_map)
for i, capture in ipairs(captures) do
print("Capture " .. i .. ":")
print("Num: " .. capture.num)
print("Cap: " .. capture.cap)
print("Start: " .. capture.start_pos)
print("End: " .. capture.end_pos)
if llm_map[capture.cap] then
capture.cap = llm_map[capture.cap]
end
print("Suggested: " .. do_correction(capture.num, capture.cap))
end
local pos = 1
local cur = 1
local new_text = ''
while pos <= #text do
if cur <= #captures and pos == captures[cur].start_pos then
new_text = new_text .. do_correction(captures[cur].num, captures[cur].cap)
pos = captures[cur].end_pos
cur = cur + 1
else
new_text = new_text .. text:sub(pos, pos)
pos = pos + 1
end
end
return new_text
end
-- print(create_prompt({"banana", "cherry", "date", "elderberry"}))
-- print_table(process_response({"banana", "cherry", "date", "elderberry"}, "banana:banana\ncherry:cherry\ndate:date\nelderberry:elderberry\n"))
-- print(run(1,"[Current date and time: 1375-05-08 Monday 10:45 | Location: Living_room_Morning | Character: Adair | Emotion: curious | outfit: An elegant navy blue robe adorned with vibrant patterns | Situation: Showing sunho around the house interior | Inner thoughts: I hope sunho feels welcome here. Their eagerness to learn magic is quite endearing.]"))
listenEdit("editOutput", function(triggerId, data)
return run(triggerId, data)
end)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment