module util export clearhistory, addNewMessage, chatHistoryToText, eventdict, noises, createTimeline, availableWineToText using UUIDs, Dates, DataStructures, HTTP, JSON3 using GeneralUtils using ..type # ---------------------------------------------- 100 --------------------------------------------- # """ Clear agent chat history. # Arguments - `a::agent` an agent # Return - nothing # Example ```jldoctest julia> using YiemAgent, MQTTClient, GeneralUtils julia> client, connection = MakeConnection("test.mosquitto.org", 1883) julia> connect(client, connection) julia> msgMeta = GeneralUtils.generate_msgMeta("testtopic") julia> agentConfig = Dict( :receiveprompt=>Dict( :mqtttopic=> "testtopic/receive", ), :receiveinternal=>Dict( :mqtttopic=> "testtopic/internal", ), :text2text=>Dict( :mqtttopic=> "testtopic/text2text", ), ) julia> a = YiemAgent.sommelier( client, msgMeta, agentConfig, ) julia> YiemAgent.addNewMessage(a, "user", "hello") julia> YiemAgent.clearhistory(a) ``` # TODO - [PENDING] clear memory # Signature """ function clearhistory(a::T) where {T<:agent} empty!(a.chathistory) empty!(a.memory[:shortmem]) empty!(a.memory[:events]) a.memory[:chatbox] = "" end """ Add new message to agent. Arguments\n ----- a::agent an agent role::String message sender role i.e. system, user or assistant text::String message text Return\n ----- nothing Example\n ----- ```jldoctest julia> using YiemAgent, MQTTClient, GeneralUtils julia> client, connection = MakeConnection("test.mosquitto.org", 1883) julia> connect(client, connection) julia> msgMeta = GeneralUtils.generate_msgMeta("testtopic") julia> agentConfig = Dict( :receiveprompt=>Dict( :mqtttopic=> "testtopic/receive", ), :receiveinternal=>Dict( :mqtttopic=> "testtopic/internal", ), :text2text=>Dict( :mqtttopic=> "testtopic/text2text", ), ) julia> a = YiemAgent.sommelier( client, msgMeta, agentConfig, ) julia> YiemAgent.addNewMessage(a, "user", "hello") ``` Signature\n ----- """ function addNewMessage(a::T1, name::String, text::T2; maximumMsg::Integer=20) where {T1<:agent, T2<:AbstractString} if name ∉ ["system", "user", "assistant"] # guard against typo error("name is not in agent.availableRole $(@__LINE__)") end #[PENDING] summarize the oldest 10 message if length(a.chathistory) > maximumMsg summarize(a.chathistory) else d = Dict(:name=> name, :text=> text, :timestamp=> Dates.now()) push!(a.chathistory, d) end end """ Converts a vector of dictionaries to a formatted string. This function takes in a vector of dictionaries and outputs a single string where each dictionary's keys are prefixed by their values. # Arguments - `vecd::Vector` a vector of dictionaries - `withkey::Bool` whether to include the key in the output text. Default is true # Return a string with the formatted dictionaries # Example ```jldoctest julia> using Revise julia> using GeneralUtils julia> vecd = [Dict(:name => "John", :text => "Hello"), Dict(:name => "Jane", :text => "Goodbye")] julia> GeneralUtils.vectorOfDictToText(vecd, withkey=true) "John> Hello\nJane> Goodbye\n" ``` # Signature """ function chatHistoryToText(vecd::Vector; withkey=true)::String # Initialize an empty string to hold the final text text = "" # Determine whether to include the key in the output text or not if withkey # Loop through each dictionary in the input vector for d in vecd # Extract the 'name' and 'text' keys from the dictionary name = d[:name] _text = d[:text] # Append the formatted string to the text variable text *= "$name> $_text \n" end else # Loop through each dictionary in the input vector for d in vecd # Iterate over all key-value pairs in the dictionary for (k, v) in d # Append the formatted string to the text variable text *= "$v \n" end end end # Return the final text return text end function availableWineToText(vecd::Vector)::String # Initialize an empty string to hold the final text rowtext = "" # Loop through each dictionary in the input vector for (i, d) in enumerate(vecd) # Iterate over all key-value pairs in the dictionary temp = [] for (k, v) in d # Append the formatted string to the text variable t = "$k:$v" push!(temp, t) end _rowtext = join(temp, ',') rowtext *= "$i) $_rowtext " end return rowtext end function eventdict(; event_description::Union{String, Nothing}=nothing, timestamp::Union{DateTime, Nothing}=nothing, subject::Union{String, Nothing}=nothing, thought::Union{AbstractDict, Nothing}=nothing, actionname::Union{String, Nothing}=nothing, # "CHAT", "CHECKINVENTORY", "PRESENTBOX", etc actioninput::Union{String, Nothing}=nothing, location::Union{String, Nothing}=nothing, equipment_used::Union{String, Nothing}=nothing, material_used::Union{String, Nothing}=nothing, outcome::Union{String, Nothing}=nothing, note::Union{String, Nothing}=nothing, ) return Dict{Symbol, Any}( :event_description=> event_description, :timestamp=> timestamp, :subject=> subject, :thought=> thought, :actionname=> actionname, :actioninput=> actioninput, :location=> location, :equipment_used=> equipment_used, :material_used=> material_used, :outcome=> outcome, :note=> note, ) end function createTimeline(memory::T1; skiprecent::Integer=0) where {T1<:AbstractVector} events = memory[1:end-skiprecent] timeline = "" for (i, event) in enumerate(events) if event[:outcome] === nothing timeline *= "$i) $(event[:subject])> $(event[:actioninput])\n" else timeline *= "$i) $(event[:subject])> $(event[:actioninput]) $(event[:outcome])\n" end end return timeline end # """ Convert a single chat dictionary into LLM model instruct format. # # Llama 3 instruct format example # <|system|> # You are a helpful AI assistant.<|end|> # <|user|> # I am going to Paris, what should I see?<|end|> # <|assistant|> # Paris, the capital of France, is known for its stunning architecture, art museums."<|end|> # <|user|> # What is so great about #1?<|end|> # <|assistant|> # # Arguments # - `name::T` # message owner name e.f. "system", "user" or "assistant" # - `text::T` # # Return # - `formattedtext::String` # text formatted to model format # # Example # ```jldoctest # julia> using Revise # julia> using YiemAgent # julia> d = Dict(:name=> "system",:text=> "You are a helpful, respectful and honest assistant.",) # julia> formattedtext = YiemAgent.formatLLMtext_phi3instruct(d[:name], d[:text]) # ``` # Signature # """ # function formatLLMtext_phi3instruct(name::T, text::T) where {T<:AbstractString} # formattedtext = # """ # <|$name|> # $text<|end|>\n # """ # return formattedtext # end # """ Convert a single chat dictionary into LLM model instruct format. # # Llama 3 instruct format example # <|begin_of_text|> # <|start_header_id|>system<|end_header_id|> # You are a helpful assistant. # <|eot_id|> # <|start_header_id|>user<|end_header_id|> # Get me an icecream. # <|eot_id|> # <|start_header_id|>assistant<|end_header_id|> # Go buy it yourself at 7-11. # <|eot_id|> # # Arguments # - `name::T` # message owner name e.f. "system", "user" or "assistant" # - `text::T` # # Return # - `formattedtext::String` # text formatted to model format # # Example # ```jldoctest # julia> using Revise # julia> using YiemAgent # julia> d = Dict(:name=> "system",:text=> "You are a helpful, respectful and honest assistant.",) # julia> formattedtext = YiemAgent.formatLLMtext_llama3instruct(d[:name], d[:text]) # "<|begin_of_text|>\n <|start_header_id|>system<|end_header_id|>\n You are a helpful, respectful and honest assistant.\n <|eot_id|>\n" # ``` # Signature # """ # function formatLLMtext_llama3instruct(name::T, text::T) where {T<:AbstractString} # formattedtext = # if name == "system" # """ # <|begin_of_text|> # <|start_header_id|>$name<|end_header_id|> # $text # <|eot_id|> # """ # else # """ # <|start_header_id|>$name<|end_header_id|> # $text # <|eot_id|> # """ # end # return formattedtext # end # """ Convert a chat messages in vector of dictionary into LLM model instruct format. # # Arguments # - `messages::Vector{Dict{Symbol, T}}` # message owner name e.f. "system", "user" or "assistant" # - `formatname::T` # format name to be used # # Return # - `formattedtext::String` # text formatted to model format # # Example # ```jldoctest # julia> using Revise # julia> using YiemAgent # julia> chatmessage = [ # Dict(:name=> "system",:text=> "You are a helpful, respectful and honest assistant.",), # Dict(:name=> "user",:text=> "list me all planets in our solar system.",), # Dict(:name=> "assistant",:text=> "I'm sorry. I don't know. You tell me.",), # ] # julia> formattedtext = YiemAgent.formatLLMtext(chatmessage, "llama3instruct") # "<|begin_of_text|>\n <|start_header_id|>system<|end_header_id|>\n You are a helpful, respectful and honest assistant.\n <|eot_id|>\n <|start_header_id|>user<|end_header_id|>\n list me all planets in our solar system.\n <|eot_id|>\n <|start_header_id|>assistant<|end_header_id|>\n I'm sorry. I don't know. You tell me.\n <|eot_id|>\n" # ``` # # Signature # """ # function formatLLMtext(messages::Vector{Dict{Symbol, T}}, # formatname::String="llama3instruct") where {T<:Any} # f = if formatname == "llama3instruct" # formatLLMtext_llama3instruct # elseif formatname == "mistral" # # not define yet # elseif formatname == "phi3instruct" # formatLLMtext_phi3instruct # else # error("$formatname template not define yet") # end # str = "" # for t in messages # str *= f(t[:name], t[:text]) # end # # add <|assistant|> so that the model don't generate it and I don't need to clean it up later # if formatname == "phi3instruct" # str *= "<|assistant|>\n" # end # return str # end # """ # Arguments\n # ----- # Return\n # ----- # Example\n # ----- # ```jldoctest # julia> # ``` # TODO\n # ----- # [] update docstring # [PENDING] implement the function # Signature\n # ----- # """ # function iterativeprompting(a::T, prompt::String, verification::Function) where {T<:agent} # msgMeta = GeneralUtils.generate_msgMeta( # a.config[:externalService][:text2textinstruct], # senderName= "iterativeprompting", # senderId= a.id, # receiverName= "text2textinstruct", # ) # outgoingMsg = Dict( # :msgMeta=> msgMeta, # :payload=> Dict( # :text=> prompt, # ) # ) # success = nothing # result = nothing # critique = "" # # iteration loop # while true # # send prompt to LLM # response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg) # error("--> iterativeprompting") # # check for correctness and get feedback # success, _critique = verification(response) # if success # result = response # break # else # # add critique to prompt # critique *= _critique * "\n" # replace!(prompt, "Critique: ..." => "Critique: $critique") # end # end # return (success=success, result=result) # end end # module util