diff --git a/src/interface.jl b/src/interface.jl index ace0b3f..926d726 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -1,7 +1,7 @@ module interface -export addNewMessage, conversation, decisionMaker, evaluator, reflector, generatechat - # isterminal, +export addNewMessage, conversation, decisionMaker, evaluator, reflector, generatechat, + generalconversation using JSON3, DataStructures, Dates, UUIDs, HTTP, Random, MQTTClient, PrettyPrinting, Serialization using GeneralUtils, LLMMCTS @@ -816,6 +816,10 @@ end # end +function generalconversation(a::T, userinput::Dict) where {T<:agent} + +end + """ Chat with llm. @@ -869,7 +873,7 @@ julia> response = ChatAgent.conversation(newAgent, "Hi! how are you?") # Signature """ -function conversation(a::T, userinput::Dict) where {T<:agent} +function conversation(a::sommelier, userinput::Dict) println("--> conver 1 ", @__FILE__, " ", @__LINE__) # place holder actionname = nothing @@ -902,7 +906,42 @@ function conversation(a::T, userinput::Dict) where {T<:agent} end println("--> conver 4 ", @__FILE__, " ", @__LINE__) # thought will be added to chat model via context - chatresponse = generatechat(a.memory, a.chathistory, a.text2textInstructLLM) + chatresponse = generatechat(a) + + addNewMessage(a, "assistant", chatresponse) + + push!(a.memory[:events], + eventdict(; + event_description= "the assistant talks to the user.", + timestamp= Dates.now(), + subject= "assistant", + action_or_dialogue= chatresponse, + ) + ) + return chatresponse + end +end + +function conversation(a::companion, userinput::Dict) + chatresponse = nothing + + if userinput[:text] == "newtopic" + clearhistory(a) + return "Okay. What shall we talk about?" + else + # add usermsg to a.chathistory + addNewMessage(a, "user", userinput[:text]) + println("--> conver 2 ", @__FILE__, " ", @__LINE__) + # add user activity to events memory + push!(a.memory[:events], + eventdict(; + event_description= "the user talks to the assistant.", + timestamp= Dates.now(), + subject= "user", + action_or_dialogue= userinput[:text], + ) + ) + chatresponse = generatechat(a) addNewMessage(a, "assistant", chatresponse) @@ -1015,10 +1054,10 @@ julia> # Signature """ -function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::Function) +function generatechat(a::sommelier) systemmsg = """ - Your name is "Jenie". You are a helpful assistant acting as a polite, website-based sommelier for an online wine store. + Your name is $(a.name). You are a helpful assistant acting as a polite, website-based sommelier for an online wine store. You are currently talking with the user. Your goal includes: 1) Help the user select the best wines from your inventory that align with the user's preferences. @@ -1054,13 +1093,13 @@ function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::F """ context = - if length(memory[:shortmem]) > 0 - vectorOfDictToText(memory[:shortmem], withkey=false) + if length(a.memory[:shortmem]) > 0 + vectorOfDictToText(a.memory[:shortmem], withkey=false) else "" end - chathistory = vectorOfDictToText(chathistory) + chathistory = vectorOfDictToText(a.chathistory) errornote = "" response = nothing # placeholder for show when error msg show up @@ -1069,7 +1108,7 @@ function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::F """ Your ongoing conversation with the user: $chathistory $context - Your thoughts: $(memory[:CHATBOX]) + Your thoughts: $(a.memory[:CHATBOX]) $errornote """ @@ -1087,7 +1126,7 @@ function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::F """ try - response = text2textInstructLLM(prompt) + response = a.text2textInstructLLM(prompt) responsedict = GeneralUtils.textToDict(response,["Mentioning_wine", "Chat"], rightmarker=":", symbolkey=true, lowercasekey=true) @@ -1115,7 +1154,7 @@ function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::F pprintln(Dict(responsedict)) # check if LLM recommend wine before checking inventory - isMemEmpty = isempty(memory[:shortmem]) + isMemEmpty = isempty(a.memory[:shortmem]) if occursin("Yes", responsedict[:mentioning_wine]) && isMemEmpty errornote = "Note: You can't recommend wines yet. You must check your inventory before recommending wine to the user." error( "You must check your inventory before recommending wine") @@ -1128,7 +1167,7 @@ function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::F errornote = "" end - memory[:CHATBOX] = "" # delete content because it no longer used. + a.memory[:CHATBOX] = "" # delete content because it no longer used. delete!(responsedict, :mentioning_wine) result = responsedict[:chat] @@ -1146,6 +1185,69 @@ function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::F error("generatechat failed to generate an evaluation") end +function generatechat(a::companion) + systemmsg = + """ + Your name is $(a.name). You are a helpful assistant. + You are currently talking with the user. + Your goal includes: + 1) Help the user as best as you can + + Your responsibility includes: + 1) Given the situation, help the user. + + At each round of conversation, you will be given the current situation: + Your ongoing conversation with the user: ... + Context: ... + + Let's begin! + """ + + chathistory = vectorOfDictToText(a.chathistory) + response = nothing # placeholder for show when error msg show up + + for attempt in 1:10 + usermsg = + """ + Your ongoing conversation with the user: $chathistory + """ + + _prompt = + [ + Dict(:name=> "system", :text=> systemmsg), + Dict(:name=> "user", :text=> usermsg) + ] + + # put in model format + prompt = GeneralUtils.formatLLMtext(_prompt; formatname="llama3instruct") + prompt *= + """ + <|start_header_id|>assistant<|end_header_id|> + """ + + try + response = a.text2textInstructLLM(prompt) + + println("") + println("--> generatechat() ", @__FILE__, " ", @__LINE__) + pprintln(response) + + result = response + + return result + catch e + io = IOBuffer() + showerror(io, e) + errorMsg = String(take!(io)) + st = sprint((io, v) -> show(io, "text/plain", v), stacktrace(catch_backtrace())) + println("") + println("Attempt $attempt. Error occurred: $errorMsg\n$st") + println("") + end + end + error("generatechat failed to generate an evaluation") +end + function generatequestion(a, text2textInstructLLM::Function; recent=nothing)::String # systemmsg = diff --git a/src/type.jl b/src/type.jl index d32741d..6ccfdcd 100644 --- a/src/type.jl +++ b/src/type.jl @@ -1,6 +1,6 @@ module type -export agent, sommelier +export agent, sommelier, companion using Dates, UUIDs, DataStructures, JSON3 using GeneralUtils @@ -10,6 +10,56 @@ using GeneralUtils abstract type agent end +mutable struct companion <: agent + name::String # agent name + id::String # agent id + maxHistoryMsg::Integer # e.g. 21th and earlier messages will get summarized + + """ Memory + Ref: Chat prompt format https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGML/discussions/3 + NO "system" message in chathistory because I want to add it at the inference time + chathistory= [ + Dict(:name=>"user", :text=> "Wassup!", :timestamp=> Dates.now()), + Dict(:name=>"assistant", :text=> "Hi I'm your assistant.", :timestamp=> Dates.now()), + ] + + """ + chathistory::Vector{Dict{Symbol, Any}} + memory::Dict{Symbol, Any} + + # communication function + text2textInstructLLM::Function +end + +function companion( + text2textInstructLLM::Function + ; + name::String= "Assistant", + id::String= string(uuid4()), + maxHistoryMsg::Integer= 20, + chathistory::Vector{Dict{Symbol, String}} = Vector{Dict{Symbol, String}}(), + ) + + memory = Dict{Symbol, Any}( + :chatbox=> "", + :shortmem=> Vector{Dict{Symbol, String}}(), + :events=> Vector{Dict{Symbol, Any}}() + ) + + newAgent = companion( + name, + id, + maxHistoryMsg, + chathistory, + memory, + text2textInstructLLM + ) + + return newAgent +end + + + """ A sommelier agent. # Arguments