module util export clearhistory, addNewMessage, formatLLMtext, syntaxcheck_json, iterativeprompting, formatLLMtext_llama3instruct, formatLLMtext_phi3instruct using UUIDs, Dates, DataStructures, HTTP, MQTTClient, JSON3 using GeneralUtils using ..type # ---------------------------------------------- 100 --------------------------------------------- # """ Clear agent chat history. Arguments\n ----- a::agent an agent 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") julia> YiemAgent.clearhistory(a) ``` Signature\n ----- """ function clearhistory(a::T) where {T<:agent} empty!(a.chathistory) empty!(a.mctstree) empty!(a.plan[:activeplan]) empty!(a.plan[:currenttrajectory]) 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 #[] 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 """ 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|>\n """ else """ <|start_header_id|>$name<|end_header_id|> $text <|eot_id|>\n """ 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 """ Check JSON format correctness and provide feedback Arguments\n ----- jsonstring::T Return\n ----- (correct, critique) Example\n ----- ```jldoctest julia> ``` TODO\n ----- [] update docstring [WORKING] implement the function Signature\n ----- """ function syntaxcheck_json(jsonstring::T)::NamedTuple where {T<:AbstractString} error("--> syntaxcheck_json") success, result, errormsg, st = GeneralUtils.showstracktrace(JSON3.read, jsonstring) if !success # gives feedback else end return (success, critique) end """ Arguments\n ----- Return\n ----- Example\n ----- ```jldoctest julia> ``` TODO\n ----- [] update docstring [WORKING] 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