423 lines
8.7 KiB
Julia
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 |