496 lines
12 KiB
Julia
496 lines
12 KiB
Julia
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 |