[Updated]DeterminantAI - chatGPT powered NPCs that you can customize!

Can you add an option to stop talking to a npc?

You can say “stop following me”, “leave me alone” to the NPC and it will stop following and talking to you.

add an option for the npcs to understand teams? who is the enemy, ally and so on

1 Like

It’s definitely working very well Also I did some test with TinyLlama injecting all of the memories and awareness into it and have gotten very good results! It’s same format as Zephyr 7b but has double the Context Window.

function cm.TinyLlama(system,query,prevoutput)
-- Define the API URL and the authorization header
local API_URL = "https://api-inference.huggingface.co/models/TinyLlama/TinyLlama-1.1B-Chat-v1.0"
local headers = {Authorization = bearer}
if prevoutput==nil then 
prevoutput=""
end
if system==nil then
system=testprompt--"You are a Large Language Model made by OpenAI who writes Luau code for ROBLOX!"
end
print(query)
-- Define a function to query the model
local function queryModel(input, temperature)
    -- Create a payload table with the input and the temperature
    local payload = {inputs = input, temperature = temperature}
    -- Encode the payload table into a JSON string
    local payloadJSON = HttpService:JSONEncode(payload)
    -- Send a POST request to the API URL with the header and the payload
    -- Use pcall to catch any errors
    local success, response = pcall(HttpService.PostAsync, HttpService, API_URL, payloadJSON, Enum.HttpContentType.ApplicationJson, false, headers)
    -- Check if the request was successful
    if success then
        -- Decode the response into a table
        -- Use pcall to catch any errors
        local success, responseTable = pcall(HttpService.JSONDecode, HttpService, response)
        -- Check if the decoding was successful
       print(success) print(responseTable)
         if success then
            -- Return the response table
            return responseTable
        else
            -- Return nil and the error message
            return success, responseTable
        end
    else
        -- Return nil and the error message
        return nil, response
    end
end

-- Define a function to format the response
local function formatResponse(str)
    -- Find the assistant response in the string
    local start = string.find(str, "<|assistant|>")
    local finish = string.len(str)
    -- Extract the response
    local response = string.sub(str, start + 13, finish)
    -- Return the response
    return response
end

-- Test the function with an example input
local input = "<|system|>\n"..system.."</s>\n<|user|>\n"..query.."</s>\n<|assistant|>"..prevoutput
local temperature = 2
local output = queryModel(input, temperature)
if output then
  --  output = formatResponse(output[1].generated_text)
 print(output)
   return output    

else
    return 
    --print("Something went wrong.")
end
end

THIS IS A AI GENERATED CONVERSATION
<|user|>
What will you do now that you have awareness and memories about your past experience as the being Angel Nyomi?
<|assistant|>
Angel Nyomi: (smiling) I’m excited to explore my past experiences and learn more about myself. I’ve always felt like I was meant to be a part of this magical realm, and now that I have this newfound awareness, I’m determined to make the most of it. I’ll use my memories to learn more about the history and culture of this world, and I’ll use my powers to help those in need. I’ll also continue to write my story, using my newfound abilities to create a world that’s both beautiful and dangerous.

Magus_ArtStudios: (smiling) I’m glad to hear that you’re excited about exploring your past. I’m sure you’ll find many interesting things to learn and do.

Angel Nyomi: (nodding) I know, and I’m looking forward to it.

Magus_ArtStudios: (smiling) You’re a powerful being, Angel Nyomi. I’m glad to have you as my companion.

Angel Nyomi: (smiling back) I’m glad to have you as my friend, Magus_ArtStudios.

It’s definitely interesting currently I have my local Lua AI providing its response injecting that in the system message of Determinant AI then injecting a summary of the Determinant AIs response to Zephyr 7-B and having it answer the question as well. I’ve noticed it dramatically improves the output of Zephyr which is pretty good on its own. It also plays the text one after another so that is neat too. :slight_smile: Rewrote the memory handler again. (should be the final iteration)
I’ll try to provide a neat template to check out when its complete.
Basically it hold the memories in 2 tables summarizes one table and keeps the other table internally and manages the number of entries in it by stacking the first two and summarizing them if they are too long. So basically when the memories are maxed out it stacks the first memory and second and make a empty space for another entry at the end.Then when the stacked entry gets too long it is summarized.meanwhile this makes sure their are always 4 summarized memories stored in the system message max.

1 Like

Gonna try this again tomorrow. Awesome updates.

:rocket: Update: chat script is now fully compatible with both “Text Chat System” and “Legacy Chat System”. Will continuously fix bugs :bug: and enhance the chat experience. Stay tuned for more updates! :star2:

3 Likes

Here’s some code I used for displaying local chat messages. If you were to run this on the server it would display for all players.

function cm.getRGB(color)
    -- A table that maps color names to RGB values
    local colors = {
        ["red"] = "rgb(255,0,0)",
        ["green"] = "rgb(0,255,0)",
        ["blue"] = "rgb(0,0,255)",
        ["yellow"] = "rgb(255,255,0)",
        ["orange"] = "rgb(255,165,0)",
        ["purple"] = "rgb(128,0,128)",
        ["pink"] = "rgb(255,192,203)",
        ["black"] = "rgb(0,0,0)",
        ["white"] = "rgb(255,255,255)",
        ["brown"] = "rgb(165,42,42)",
        ["gold"] = "rgb(255,215,0)",
        ["silver"] = "rgb(192,192,192)"
    }
    -- Return the RGB value of the color name or nil if not found
    return colors[color]
end
 local TextChat=game:GetService("TextChatService")
        local chatgroup=TextChat.TextChannels.RBXSystem 
pcall(function()    chatgroup:DisplaySystemMessage("<font color=\""..rgbcolor.."\">"..npc.Humanoid.DisplayName..": </font> <font color=\"rgb(255,255,255)\">"..str.."".."</font> ")end) 

Using this code you can display a chat message in the Text channel and it will appear as a regular chat message. Sorry for not posting this sooner!

3 Likes

how long will it take me to get it to say the most terrible awful things

time to get builderman to give me bomb-making instructions! /j
cool plugin tho

I like this! Is there a possible way to make it like a ordering NPC for cafe games?

1 Like

Yes making an NPC for food ordering system and other simulator games is indeed part of the future plan for the plugin!

Response time sometimes go up to 3-5 seconds, I’m trying to find where the problem is.

we have a profanity filter on, let me know what you find :wink:

thanks for all the work you’ve put in!

1 Like

Thanks! I would like to do something neat for your plugin soon. If you’re interested in any of these features let me know and I’ll look at your plugin and right a new implementation of these features.
–Memories with a DataStore stored in lists via UserNameNPCNames
–local Data and time algorithm that provides the local users data and time into the system message
Also these awareness functions I developed this new one called aware.judge.terrain which judges an array of terrain and provides text.
Additionally, I have this more tailored implementation of describing what the character is walking on.

local matsolutions={["LeafyGrass"]="leafy grass",
["WoodPlanks"]="wooden planks",
["CrackedLava"]="cracked lava",
["ground"]="dirt"
}
function aware.judge.terrain(root,radius)

local function detecter(material)
local materel={}
--print(material)
local nummats=0
for i,v in material do 
if material[i] then
if i~="Size" then
--if material[i][1] then
for t,o in material[i] do 
for c,_ in material[i][t] do 
--if --material[i][t][c]~=Enum.Material.Water and
if material[i][t][c]~=Enum.Material.Air then --count all of the terrain
local matstring = string.split(tostring(material[i][t][c]),'Enum.Material.')[2]
if matsolutions[matstring]~=nil then
    matstring= matsolutions[matstring]
 end
if materel[matstring]==nil then
nummats+=1
materel[matstring]=1
else 
materel[matstring]+=1
--table.sort(materel,function(a,b)return a>b end)--sort greatest to 
end

--return true
end
end 
end
end
end
end
--least
print(materel)
--create an array with the keys
local keys = {}
for k in pairs(materel) do
  table.insert(keys, k)
end

--sort the array using a custom comparison function
table.sort(keys, function(a, b) return materel[a] < materel[b] end)

--print the sorted table


--table.sort(materel,function(a,b)return a>b end)--sort greatest to 


--table.sort(materel,function(a,b)return a>b end)--sort greatest to least
local judgeamntstrings = {
  {"a handful of", "a few", "a smattering of", "a sprinkling of"},
  {"a little bit of", "a trace of", "a touch of", "a dash of"},
  {"a sparse amount of", "a scant amount of", "a meager amount of", "a minimal amount of"},
  {"bunches of", "clusters of", "groups of", "packs of"},
  {"a lot of", "heaps of", "a ton of", "loads of"},
  {"a multitude of", "a plethora of", "hordes of", "heaps of"},
  {"a huge quantity of", "a massive amount of", "a colossal amount of", "a prodigious amount of"},
  {"a staggering number of", "an astonishing number of", "a phenomenal number of", "a mind-blowing number of"}
}
 
local judgementstring=""
local index=0
--{1, 2, 3, 4, 5, 6, 8}
local judgmatrix={1,radius,radius*5,radius*10,radius*20,radius*30,radius*40,radius*50}
--for i, k in ipairs(keys) do
--  print(k,)
--end
for i,k in keys do 
index=index+1
if index==nummats then
judgementstring..="and "
end
judgementstring..=aware.judge.amnt( materel[k],judgeamntstrings,judgmatrix).." "..k:lower()
if index~= nummats then
judgementstring..=", "
end
end

return judgementstring
end

local region = Region3.new(root.Position-Vector3.new(radius,8+root.Size.Y,radius),root.Position+Vector3.new(radius,radius,radius))
local material = terrain:ReadVoxels(region, 4)            
-- detecter(material) 
--phraselib.opener[aware.mathrandom(1,#phraselib.opener)]
return  detecter(material) 
end



function aware.get.terrain(root,radius,str)
local phrases = {
  "The environment has",
  "The terrain consists of",
  "The surroundings is characterized by",
  "The landscape features",
  "The ecosystem hosts",
}
if radius~=true then
if str~=nil then
--rewrite the table in the first person context
return phrases[math.random(1,#phrases)].." "..str..". "
end
return phrases[math.random(1,#phrases)].." "..aware.judge.terrain(root,radius)..". "
elseif radius==true then
return  phrases[math.random(1,#phrases)]:lower().." "..str..""
end
end

This is a different function that judges walking, standing state and what the npc is standing on.
It’s pretty general except for this custom variable I use to determine what model the player is standing on.
– local matstring=player.PlayerGui.Chatbot.FloorPlatform.Value

function aware.get.walking(root,player,walkings)
local hum
if root.Parent:FindFirstChild("Humanoid") then   
hum=root.Parent.Humanoid
else 
hum=player.Character.Humanoid
end
	local floormat = hum.FloorMaterial
    local walking="standing" if root.Velocity.Magnitude>1 then walking="walking" end 
	if not floormat then floormat = 'Air'
   return nil    
  end
	local matstring = string.split(tostring(floormat),'Enum.Material.')[2]
--
if matstring~='Air' and matstring~='Plastic' and matstring~="SmoothPlastic" and matstring~="Neon" then
    if matsolutions[matstring]~=nil then
    matstring= matsolutions[matstring]
    end
	local material = "I am "..walking.." on "..matstring:lower()..""
	return material
else
if matstring=="Plastic" or matstring=="SmoothPlatstic" then
                local matstring=player.PlayerGui.Chatbot.FloorPlatform.Value
     if matstring~="" then       
	return "I am "..walking.." on a "..matstring:lower()..""
    else 
if walking then
return "I am "..walking..""
end
 end
end     
 return ""
end
end

Additionally, I wrote these new awareness functions that describe the npcs humanoid description, and outfit!

local judgeheightmatrixSizes = {.3,.4,.5,.6,.7,.8,1,1.3}
local judgehmatrixStrings = {{"super short"}, {"very short"}, {"pretty short"}, {"short"}, {"average"}, {"above average"}, {"tall"},{"very tall"}}
local judgewidthmatrixSizes = {.1,.2,.3,.4,.5,.6,.7,.8,.9,1.3}
local judgewidthmatrixStrings = {{"super narrow"}, {"very narrow"}, {"pretty narrow"}, {"narrow"}, {"average"}, {"above average"}, {"wide"},{"very wide"}, {"super wide"}, {"extremely wide"}}
local judgethicknessmatrixSizes = {.1,.2,.3,.4,.5,.6,.7,.8,.9,1.3}
local judgethicknessmatrixStrings = {{"super thin"}, {"very thin"}, {"pretty thin"}, {"thin"}, {"average thickness"}, {"above average thickness"}, {"thick"},{"very thick"}, {"super thick"}, {"extremely thick"}}
-- Define the table describing sizes and the corresponding strings
local jmatrixSizes = {0.2,.4,.6,.8,1,1.2,1.4,1.6,1.8,2}
local judgehsizematrixStrings = {{"super small"}, {"very small"}, {"pretty small"}, {"small"}, {"average size"}, {"above average size"}, {"large"},{"very large"}, {"super large"}, {"extremely large"}}



-- Define a function that takes a number as a variable and returns the string corresponding to the index of the number
function aware.judge.distribution(number,matrixSizes,matrixStrings)
  -- Check if the number is valid
 -- if type(number) ~= "number" then-- or number <= 0 or number > 1 then
  --  return "Invalid input"
 -- end
  -- Loop through the matrix of height distributions
  for i = 1, #matrixSizes do
    -- Find the index where the number is less than or equal to the matrix value
    if number <= matrixSizes[i] or  i==#matrixSizes then
      -- Return the string at that index
      return matrixStrings[i][1]
    end

  end
end

function aware.judge.human.thickness(number)
    return aware.judge.distribution(number,judgethicknessmatrixSizes,judgethicknessmatrixStrings)
end

function aware.judge.human.size(number)
    return aware.judge.distribution(number,jmatrixSizes,judgehsizematrixStrings)
end

function aware.judge.human.width(number)
    return aware.judge.distribution(number,jmatrixSizes,judgewidthmatrixStrings)
end

function aware.judge.human.height(number)
    return aware.judge.distribution(number, judgeheightmatrixSizes , judgehmatrixStrings)
end

function aware.judge.human.description(humanoid,completeresponse,tenses)
if humanoid:FindFirstChild("BodyHeightScale") then
local vars={
  humanoid["BodyTypeScale"].Value,
  humanoid["BodyProportionScale"].Value,
  humanoid["BodyWidthScale"].Value,
  humanoid["BodyHeightScale"].Value,
  humanoid["BodyDepthScale"].Value,
  humanoid["HeadScale"].Value
}
if tenses==nil then tenses={"I am ","I have a "} end

local descguide= {
 -- ["BodyTypeScale"],
 -- ["BodyProportionScale"],
  ["BodyWidthScale"]=function () return tenses[1],aware.judge.human.width(vars[3]).." in the hips" end,
  ["BodyHeightScale"]=function () return tenses[1],aware.judge.human.height(vars[4]).." in height" end,
  ["BodyDepthScale"]=function () return tenses[1],aware.judge.human.thickness(vars[5]).." in the waist" end,
  ["HeadScale"]=function () return tenses[2],aware.judge.human.size(vars[6]).." head" end,
}
local response={[1]=""}
if completeresponse==nil then completeresponse="If I were to describe my avatar I would say," end
for i,v in descguide do 
local v1,v2=descguide[i]() 
table.insert(response, v1..v2..".")
if v1=="I am " then
completeresponse=completeresponse.." "..v2..","
else
completeresponse=completeresponse.." "..v1..v2.."."
end
end
response[1]=completeresponse
return response
else 
return {[1]=""}
end
end

I don’t know if you find that useful but they are mostly pretty modular functions and are nice examples. I especially like the aware.judge.terrain function.
This humanoid description one is similar to my awareness functions in that it constructs a table of observations about the humanoid description and the first one is the complete observation.

My awareness library is coming along exceptionally! =] I really wanted the AI to know what they look like.

for sure, i’m thinking we’ll need help real soon, let me DM you

You asked for it, you waited for it… it’s finally here! :drum: :drum:

:fire::fire: NEW FEATURE - MULTIPLE AI NPCs :fire::fire:

We’ve launched a new feature that will allow you to spin up multiple AI NPCs in one game, each with their own individual backstories and personalities! :woman: :boy: :girl: :man:

We’ve also fixed some bugs and cleaned up our UI quite a bit. We’re in final stages of testing some additional features such as putting AI NPCs behind a gamepass so you can start monetizing! :money_mouth_face:

Special thanks to @Magus_ArtStudios :dizzy: for helping out and coding some of these features

The feature is still in beta, so please bear with us! Happy creating! :sparkles::sparkles:

4 Likes

Yes I coded all these new features and tools for your to customize the AI with whatever you want! I use the most efficient methods to dynamically load assets of different types to the game via the AI’s various tools!
-AI Can Wear Clothes, hats, Tools from the Catalogue
-Can Change its Character via the catalouge or the Players Character
-Wikipedia Web_Tool that provides the AI with content filtered wikipedia articles and the AI provides a summary
-Memories the AI can remember you forever via ROBLOX datastores!
-Knows the time
-Efficiently load in playlists from the Catalogue.music or sound effects!
-AI knows player names
-Insert Emojis I used ChatGPT to create a accurate Emoji Insertion Model using various techniques and much data preparation including 1200+ Emojis!
Current Emojis Models (TinyEmoji and SmallEmoji)
-Game Pass or Group Pass Each AI can have different Abilities and so maybe you want to limit who can talk to them?
-Intelligent Emotes -I imported 169 animations from the Catalogue played each one and labeled carefully to create a expressive emote model using the ROBLOX created animations

-(In progress) The AI can create other NPCs currently but they do not have any combat AI inside.

image

I also added new developer Tools to the plugin allowing for Automatic UI scaling and easy way to add buttons of different options in addition to a module loader function, and a algorithm handling the variables of all the different features =]

4 Likes

seated issue is fixed in the latest update. you can say “follow me” your npc will jump up from seat.
text to speech is currently not allowed in roblox we will keep an eye on possible solution,

1 Like

It would be cool if several people could talk to the AI. Currently, only one person can talk to the AI

4 Likes