Files
YiemAgent/src/util.jl
narawat lamaiin 6f03c60c59 update
2024-04-28 08:58:20 +07:00

423 lines
8.7 KiB
Julia

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