Files
ChatAgent/src/utils.jl
youremail@yourdomain.com 78e99457d0 update
2023-12-22 14:30:57 +00:00

1084 lines
29 KiB
Julia

module utils
export makeSummary, sendReceivePrompt, chunktext, extractStepFromPlan, checkTotalTaskInPlan,
detectCharacters, findDetectedCharacter, extract_number, toolNameBeingCalled,
isUsePlans, conversationSummary, checkReasonableness, replaceHeaders,
addShortMem!, splittext, dictToString, removeHeaders, keepOnlyKeys, experience,
messagesToString, messagesToString_nomark, removeTrailingCharacters, shortMemLatestTask
using UUIDs, Dates, DataStructures
using CommUtils, GeneralUtils
using ..type
#------------------------------------------------------------------------------------------------100
function makeSummary(a::T1, input::T2) where {T1<:agent, T2<:AbstractString}
summary = "Nothing."
prompt =
"""
<|im_start|>system
Input text:
$input
Your job is to determine now whether you can make a summary of the input text by choosing one of following choices:
If you cannot make a summary say, "{No}".
If you can make a summary say, "{Yes}".
<|im_end|>
"""
prompt = replace(prompt, "{input}" => input)
result = sendReceivePrompt(a, prompt)
result = GeneralUtils.getStringBetweenCharacters(result, "{", "}")
if result == "Yes" # seperate summary part
prompt =
"""
<|im_start|>system
Input text:
$input
Your job is to make a concise summary of the input text.
<|im_end|>
"""
result = sendReceivePrompt(a, prompt)
if result[1:1] == "\n"
summary = result[2:end]
else
summary = result
end
end
input_summary = input
@show input_summary
@show summary
return summary
end
"""
Send a msg to registered mqtt topic within mqttClient.
```jldoctest
julia> using JSON3, UUIDs, Dates, FileIO, CommUtils, ChatAgent
julia> newAgent = ChatAgent.agentReact(
"Jene",
mqttClientSpec,
role=:assistant_react,
msgMeta=msgMeta
)
```
"""
function sendReceivePrompt(a::T, prompt::String; max_tokens=256, timeout::Int=120,
temperature::AbstractFloat=0.2) where {T<:agent}
a.msgMeta[:msgId] = "$(uuid4())" # new msg id for each msg
msg = Dict(
:msgMeta=> a.msgMeta,
:txt=> prompt,
:max_tokens=> max_tokens,
:temperature=> temperature,
)
payloadChannel = Channel(1)
# send prompt
CommUtils.request(a.mqttClient, msg)
starttime = Dates.now()
result = nothing
while true
timepass = (Dates.now() - starttime).value / 1000.0
CommUtils.mqttRun(a.mqttClient, payloadChannel)
if isready(payloadChannel)
topic, payload = take!(payloadChannel)
if payload[:msgMeta][:repondToMsgId] == msg[:msgMeta][:msgId]
result = haskey(payload, :txt) ? payload[:txt] : nothing
break
end
elseif timepass <= timeout
# skip, within waiting period
elseif timepass > timeout
println("sendReceivePrompt timeout $timepass/$timeout")
result = nothing
break
else
error("undefined condition. timepass=$timepass timeout=$timeout $(@__LINE__)")
end
end
return result
end
"""
Detect given characters. Output is a list of named tuple of detected char.
```jldoctest
julia> text = "I like to eat apples and use utensils."
julia> characters = ["eat", "use", "i"]
julia> result = detectCharacters(text, characters)
4-element Vector{Any}:
(char = "i", start = 4, stop = 4)
(char = "eat", start = 11, stop = 13)
(char = "use", start = 26, stop = 28)
(char = "i", start = 35, stop = 35)
```
"""
function detectCharacters(text::T1, characters::Vector{T2}) where {T1<:AbstractString, T2<:AbstractString}
result = []
for i in eachindex(text)
for char in characters
l = length(char)
char_startInd = i
char_endInd = i+l-1 # -1 because Julia use inclusive index
if char_endInd > length(text)
# skip
else
try # some time StringIndexError: invalid index [535], valid nearby indices [534]=>'é', [536]=>' '
if text[char_startInd: char_endInd] == char
push!(result, (char=char, start=char_startInd, stop=char_endInd))
end
catch
end
end
end
end
return result
end
"""
Chunk a text into smaller pieces by header.
```jldoctest
julia> using ChatAgent
julia> text = "Plan: First, we need to find out what kind of wine the user wants."
julia> headers = ChatAgent.detectCharacters(text, ["Nope", "sick", "First", "user", "Then", ])
3-element Vector{Any}:
(char = "First", start = 7, stop = 11)
(char = "user", start = 56, stop = 59)
(char = "Then", start = 102, stop = 105)
julia> chunkedtext = ChatAgent.chunktext(text, headers)
OrderedDict{String, String} with 3 entries:
"Act 1:" => " wikisearch"
"Actinput 1:" => " latest AMD GPU"
"Thought 1:" => " I should always think about..."
```
"""
function chunktext(text::T1, headers::T2) where {T1<:AbstractString, T2<:AbstractVector}
result = OrderedDict{String, Any}()
for (i, v) in enumerate(headers)
if i < length(headers)
nextheader = headers[i+1]
body = text[v[:stop]+1: nextheader[:start]-1]
# push!(result, (header=v[:char], body=body))
result[v[:char]] = body
else
body = text[v[:stop]+1: end]
# push!(result, (header=v[:char], body=body))
result[v[:char]] = body
end
end
return result
end
function extractStepFromPlan(a::agent, plan::T, step::Int) where {T<:AbstractString}
prompt =
"""
<|im_start|>system
You are a helpful assistant.
Your job is to extract step $step in the user plan.
Use the following format only:
{copy the step and put it here}
<|im_end|>
<|im_start|>user
$plan
<|im_end|>
<|im_start|>assistant
"""
response = sendReceivePrompt(a, prompt)
return response
end
function checkTotalTaskInPlan(a::agent)
headers = []
for (k, v) in a.memory[:shortterm]
push!(headers, k)
end
# Plan will have number e.g. Plan 3: so I need a way to detect latest Plan
header = nothing
for i in reverse(headers)
if occursin("Plan", i)
header = i
break
end
end
p = a.memory[:shortterm][header]
plan = "Plan: $p"
prompt =
"""
<|im_start|>system
You are a helpful assistant.
Your job is to determine how many steps in a user plan.
Use the following format to answer:
Total step number is {}
<|im_end|>
<|im_start|>user
$plan
<|im_end|>
<|im_start|>assistant
"""
response = sendReceivePrompt(a, prompt)
result = extract_number(response)
return result
end
"""
Find a given character from a vector of named tuple.
Output is character location index inside detectedCharacters
```jldoctest
julia a = [ (char = "i", start = 4, stop = 4)
(char = "eat", start = 11, stop = 13)
(char = "use", start = 26, stop = 28)
(char = "i", start = 35, stop = 35) ]
julia> findDetectedCharacter(a, "i")
[1, 4]
```
"""
function findDetectedCharacter(detectedCharacters, character)
allchar = [i[1] for i in detectedCharacters]
return findall(isequal.(allchar, character))
end
function extract_number(text::T) where {T<:AbstractString}
regex = r"\d+" # regular expression to match one or more digits
match = Base.match(regex, text) # find the first match in the text
if match !== nothing
number = parse(Int, match.match)
return number
else
error("No number found in the text $(@__LINE__)")
end
end
"""
Extract toolname from text.
```jldoctest
julia> text = " internetsearch\n"
julia> tools = Dict(
:internetsearch=>Dict(
:name => "internetsearch",
:description => "Useful for when you need to search the Internet",
:input => "Input should be a search query.",
:output => "",
# :func => internetsearch # function
),
:chatbox=>Dict(
:name => "chatbox",
:description => "Useful for when you need to ask a customer what you need to know or to talk with them.",
:input => "Input should be a conversation to customer.",
:output => "" ,
),
)
julia> toolname = toolNameBeingCalled(text, tools)
```
"""
function toolNameBeingCalled(text::T, tools::Dict) where {T<:AbstractString}
toolNameBeingCalled = nothing
for (k, v) in tools
toolname = String(k)
if contains(text, toolname)
toolNameBeingCalled = toolname
break
end
end
return toolNameBeingCalled
end
# function chooseThinkingMode(a::agentReflex, usermsg::String)
# thinkingmode = nothing
# if length(a.memory[:log]) != 0
# thinkingmode = :continue_thinking
# else
# prompt =
# """
# <|im_start|>system
# {systemMsg}
# You always use tools if there is a chance to impove your response.
# You have access to the following tools:
# {tools}
# Your job is to determine whether you will use tools or actions to response.
# Choose one of the following choices:
# Choice 1: If you don't need to use tools or actions to response to the stimulus say, "{no}".
# Choice 2: If you think the user want to get wine say, "{yes}".
# <|im_end|>
# <|im_start|>user
# {input}
# <|im_end|>
# <|im_start|>assistant
# """
# toollines = ""
# for (toolname, v) in a.tools
# if toolname ∉ ["chatbox", "nothing"]
# toolline = "$toolname: $(v[:description]) $(v[:input]) $(v[:output])\n"
# toollines *= toolline
# end
# end
# prompt = replace(prompt, "{systemMsg}" => a.roles[a.role])
# prompt = replace(prompt, "{tools}" => toollines)
# prompt = replace(prompt, "{input}" => usermsg)
# result = sendReceivePrompt(a, prompt)
# willusetools = GeneralUtils.getStringBetweenCharacters(result, "{", "}")
# thinkingmode = willusetools == "yes" ? :new_thinking : :no_thinking
# end
# return thinkingmode
# end
""" Determine from a user message whether an assistant need to use tools.
Arguments:
a, one of ChatAgent's agent.
Return:
1. true/false # is LLM going to use tools
2. objective # what LLM going to do
"""
function isUsePlans(a::agentReflex)
toollines = ""
for (toolname, v) in a.tools
if toolname ["chatbox"] # LLM will always use chatbox
toolline = "$toolname: $(v[:description]) $(v[:input]) $(v[:output])\n"
toollines *= toolline
end
end
conversation = messagesToString(a.messages)
prompt =
"""
<|system|>
You are a helpful assistant.
You have access to the following tools:
$toollines
Your task is to decide whether you need think thoroughly in order to respond to the user according to your conversation with the user and tools you have.
So for instance the following:
user: Hello!. How are you?
assistant: {No}, the user is greeting me, I could respond right away.
user: "I want a bottle of wine."
assistant: {Yes}, I need to think thoroughly about the user stimulus.
</s>
$conversation
<|assistant|>
"""
# if LLM mentions any tools, use Plan/Thought/Act loop
isuseplan = false
response = sendReceivePrompt(a, prompt, temperature=0.2, max_tokens=64)
response = split(response, "<|assistant|>")[1]
response = split(response, "<|user|>")[1]
for (toolname, v) in a.tools
if occursin("Yes", String(response))
isuseplan = true
break
end
end
if length(a.memory[:shortterm]) != 0
isuseplan = true
end
return isuseplan
end
# function isUsePlans(a::agentReflex)
# toollines = ""
# for (toolname, v) in a.tools
# if toolname ∉ ["chatbox"] # LLM will always use chatbox
# toolline = "$toolname: $(v[:description]) $(v[:input]) $(v[:output])\n"
# toollines *= toolline
# end
# end
# conversation = messagesToString_nomark(a.messages)
# prompt =
# """
# <|im_start|>system
# $(a.roles[a.role])
# You have access to the following tools:
# $toollines
# Your conversation with the user:
# $conversation
# From the conversation, ask yourself what do you intend to do now?
# <|im_end|>
# """
# # if LLM mentions any tools, use Plan/Thought/Act loop
# isuseplan = false
# response = sendReceivePrompt(a, prompt, temperature=0.0)
# response = split(response, "<|im_end|>")[1]
# for (toolname, v) in a.tools
# if occursin(toolname, String(response))
# isuseplan = true
# break
# end
# end
# if length(a.memory[:shortterm]) != 0
# isuseplan = true
# end
# return isuseplan
# end
"""
make a conversation summary.
```jldoctest
julia> conversation = [
Dict(:role=> "user", :content=> "I would like to get a bottle of wine", :timestamp=> Dates.now()),
Dict(:role=> "assistant", :content=> "What kind of Thai dishes are you having?", :timestamp=> Dates.now()),
Dict(:role=> "user", :content=> "It a pad thai.", :timestamp=> Dates.now()),
Dict(:role=> "assistant", :content=> "Is there any special occasion for this event?", :timestamp=> Dates.now()),
Dict(:role=> "user", :content=> "We'll hold a wedding party at the beach.", :timestamp=> Dates.now()),
Dict(:role=> "assistant", :content=> "What is your preferred type of wine?", :timestamp=> Dates.now()),
Dict(:role=> "user", :content=> "I like dry white wine with medium tanins.", :timestamp=> Dates.now()),
Dict(:role=> "assistant", :content=> "What is your preferred price range for this bottle of wine?", :timestamp=> Dates.now()),
Dict(:role=> "user", :content=> "lower than 50 dollars.", :timestamp=> Dates.now()),
Dict(:role=> "assistant", :content=> "Based on your preferences and our stock, I recommend the following two wines for you:
1. Pierre Girardin \"Murgers des Dents de Chien\" - Saint-Aubin 1er Cru (17 USD)
2. Etienne Sauzet'Les Perrieres' - Puligny Montrachet Premier Cru (22 USD)
The first wine, Pierre Girardin \"Murgers des Dents de Chien\" - Saint-Aubin 1er Cru, is a great choice for its affordable price and refreshing taste.
It pairs well with Thai dishes and will be perfect for your beach wedding party.
The second wine, Etienne Sauzet'Les Perrieres' - Puligny Montrachet Premier Cru, offers a more complex flavor profile and slightly higher price point, but still remains within your budget.
Both wines are suitable for serving at 22 C temperature.", :timestamp=> Dates.now()),
]
julia> summary = conversationSummary(conversation)
```
"""
function conversationSummary(a::T) where {T<:agent}
prompt =
"""
<|im_start|>system
You talked with a user earlier.
Now you make a detailed bullet summary of the conversation from your perspective.
You must refers to yourself by "I" in the summary.
Here are the conversation:
{conversation}
<|im_end|>
"""
conversation = ""
summary = ""
if length(a.messages)!= 0
for msg in a.messages[1:end-1]
role = msg[:role]
content = msg[:content]
if role == "user"
conversation *= "$role: $content\n"
elseif role == "assistant"
conversation *= "I: $content\n"
else
error("undefied condition role = $role $(@__LINE__)")
end
end
prompt = replace(prompt, "{conversation}" => conversation)
result = sendReceivePrompt(a, prompt)
summary = result === nothing ? "N/A" : result
summary = split(summary, "<|im_end|>")[1]
if summary[1:1] == "\n"
summary = summary[2:end]
end
end
@show summary
return summary
end
""" Convert a vector of dict into 1-continous string.
Arguments:
vecofdict, a vector of dict
Return:
1-continous string
# Example
```jldoctest
julia> using ChatAgent
julia> agent = ChatAgent.agentReflex("Jene")
julia> agent.messages = [Dict(:role=> "user", :content=> "Hi there."),
Dict(:role=> "assistant", :content=> "Hello! How can I assist you today?"),]
julia> messagesToString(agent.messages)
"<|im_start|>user: Hi there.\n<|im_end|><|im_start|>assistant: Hello! How can I assist you today?\n<|im_end|>"
```
"""
function messagesToString(messages::AbstractVector{T}; addressAIas="assistant") where {T<:AbstractDict}
conversation = ""
if length(messages)!= 0
for msg in messages
role = msg[:role]
content = msg[:content]
nouse = 0
for i in reverse(content)
if i == '\n' || i == ' '
nouse += 1
else
break
end
end
if role == "user"
conversation *= "<|$role|>\n $(content[1:end-nouse])\n</s>"
elseif role == "assistant"
conversation *= "<|$addressAIas|>\n $(content[1:end-nouse])\n</s>"
else
error("undefied condition role = $role $(@__LINE__)")
end
end
else
conversation = "N/A"
end
return conversation
end
# function messagesToString(messages::AbstractVector{T}; addressAIas="assistant") where {T<:AbstractDict}
# conversation = ""
# if length(messages)!= 0
# for msg in messages
# role = msg[:role]
# content = msg[:content]
# nouse = 0
# for i in reverse(content)
# if i == '\n' || i == ' '
# nouse += 1
# else
# break
# end
# end
# if role == "user"
# conversation *= "<|im_start|>$role: $(content[1:end-nouse])\n<|im_end|>"
# elseif role == "assistant"
# conversation *= "<|im_start|>$addressAIas: $(content[1:end-nouse])\n<|im_end|>"
# else
# error("undefied condition role = $role $(@__LINE__)")
# end
# end
# else
# conversation = "N/A"
# end
# return conversation
# end
""" Convert a vector of dict into 1-continous string.
Arguments:
vecofdict, a vector of dict
Return:
1-continous string
# Example
```jldoctest
julia> using ChatAgent
julia> agent = ChatAgent.agentReflex("Jene")
julia> agent.messages = [Dict(:role=> "user", :content=> "Hi there."),
Dict(:role=> "assistant", :content=> "Hello! How can I assist you today?"),]
julia> messagesToString(agent.messages)
"user: Hi there.\nassistant: Hello! How can I assist you today?\n"
```
"""
function messagesToString_nomark(messages::AbstractVector{T}; addressAIas="assistant") where {T<:AbstractDict}
conversation = ""
if length(messages)!= 0
for msg in messages
role = msg[:role]
content = msg[:content]
content = removeTrailingCharacters(content)
if role == "user"
conversation *= "$role: $content\n"
elseif role == "assistant"
conversation *= "$addressAIas: $content\n"
else
error("undefied condition role = $role $(@__LINE__)")
end
end
else
conversation = "N/A"
end
return conversation
end
function dictToString(shortMemory::T;
skiplist::Union{Vector{String}, Vector{Symbol}}=[""]) where {T<:AbstractDict}
s = ""
for (k, v) in shortMemory
if k skiplist
new_v = removeTrailingCharacters(v)
s1 = "$k $new_v\n"
s *= s1
end
end
return s
end
# function dictToString(dict::T;
# skiplist::Union{Array{String}, Array{Symbol}}=[]) where {T<:AbstractDict}
# s = ""
# for (k, v) in dict
# if k ∉ skiplist
# s1 = "$k $v"
# s *= s1
# # ensure a newline seperate each sentences
# if s[end] != "\n"
# s *= "\n"
# end
# end
# end
# return s
# end
""" Remove trailing characters from text.
Arguments:
text, text you want to remove trailing characters
charTobeRemoved, a list of characters to be removed
Return:
text with specified trailing characters removed
# Example
```jldoctest
julia> text = "Hello! How can I assist you today?\n\n "
julia> removelist = ['\n', ' ',]
julia> removeTrailingCharacters(text, charTobeRemoved=removelist)
"Hello! How can I assist you today?"
```
"""
function removeTrailingCharacters(text; charTobeRemoved::AbstractVector{T}=['\n', ' ',]) where {T<:Char}
nouse = 0
for i in reverse(text)
if i charTobeRemoved
nouse += 1
else
break
end
end
return text[1:end-nouse]
end
#TODO
function checkReasonableness(userMsg::String, context::String, tools)
# Ref: https://www.youtube.com/watch?v=XV4IBaZqbps
prompt =
"""
<|im_start|>system
You are a helpful assistant. Your job is to check the reasonableness of user assignments.
If the user assignment can be answered given the tools available say, "This is a reasonable assignment".
If the user assignment cannot be answered then provide some feedback to the user that may improve
their assignment.
Here is the context for the assignment:
{context}
<|im_end|>
<|im_start|>user
{assignment}
<|im_end|>
<|im_start|>assistant
"""
context = "You have access to the following tools:
WineStock: useful for when you need to find info about wine by matching your description, price, name or ID. Input should be a search query with as much details as possible."
prompt = replace(prompt, "{assignment}" => userMsg)
prompt = replace(prompt, "{context}" => context)
output_py = llm(
prompt,
max_tokens=512,
temperature=0.1,
# top_p=top_p,
echo=false,
stop=["</response>", "<<END>>", ],
)
_output_jl = pyconvert(Dict, output_py);
output = pyconvert(Dict, _output_jl["choices"][1]);
output["text"]
end
""" Add chunked text to a short term memory of a chat agent
Arguments:
shortMem = short memory of a chat agent,
chunkedtext = a dict contains text
Return: no return
# Example
```jldoctest
julia> chunkedtext = OrderedDict{String, String}(
"Thought 1:" => " I should always think about...",
"Act 1:" => " wikisearch",
"Actinput 1:" => " latest AMD GPU",)
julia> shortMem = OrderedDict{String, Any}()
julia> addShortMem!(shortMem, chunkedtext)
OrderedDict{String, Any} with 3 entries:
"Thought 1:" => " I should always think about..."
"Act 1:" => " wikisearch"
"Actinput 1:" => " latest AMD GPU"
```
"""
function addShortMem!(shortMem::OrderedDict{String, Any}, chunkedtext::T) where {T<:AbstractDict}
for (k, v) in chunkedtext
shortMem[k] = v
end
return shortMem
end
""" Split text using all keywords in a list. Start spliting from rightmost of the text.
Arguments:
text = a text you want to split
list = a list of keywords you want to split
Return:
a leftmost text after split
# Example
```jldoctest
julia> text = "Consider the type of food, occasion and temperature at the serving location."
julia> list = ["at", "and"]
"Consider the type of food, occasion "
```
"""
function splittext(text, list)
newtext = text
for i in list
newtext = split(newtext, i)[1]
end
return newtext
end
"""
Add step number to header in a text
"""
function addStepNumber(text::T, headers, step::Int) where {T<:AbstractString}
newtext = text
for i in headers
if occursin(i[:char], newtext)
new = replace(i[:char], ":"=> " $step:")
newtext = replace(newtext, i[:char]=>new )
end
end
return newtext
end
function addStepNumber(text::T, headers, step::Int, substep::Int) where {T<:AbstractString}
newtext = text
for i in headers
if occursin(i[:char], newtext)
new = replace(i[:char], ":"=> " $step-$substep:")
newtext = replace(newtext, i[:char]=>new )
end
end
return newtext
end
""" Add step number to header in a text
Arguments:
text = a text you want to split
headers = a list of keywords you want to add step and substep to
step = a number you want to add
Return:
a leftmost text after split
# Example
```jldoctest
julia> text = "Consider the type of food, occasion and temperature at the serving location."
julia> headers = ["Thought", "Act"]
```
"""
function replaceHeaders(text::T, headers, step::Int) where {T<:AbstractString}
newtext = text
for i in headers
header = i[1:end-1] # not include ":"
if occursin(header, newtext)
startind = findfirst(header, newtext)[1]
stopind = findnext(":", newtext, startind+1)[end] #BUG MethodError: no method matching lastindex(::Nothing)
word = newtext[startind: stopind]
newword = "$header $step:"
newtext = replace(newtext, word=> newword)
end
end
return newtext
end
""" Remove headers of specific step from memory.
Arguments:
shortMemory = a short term memory of a ChatAgent's agent
skipHeaders = a list of keys in memory you want to skip
step = a step number you want to remove
Return:
a short term memory
# Example
```jldoctest
julia> shortMemory = OrderedDict(
"user:" => "May I try this one?",
"Plan 1:" => "testing a small portion of icecream",
"Thought 1:" => "I like it.",
"Act 1:" => "chatbox",
"Actinput 1:" => "I get this one.",
"Plan 2:" => "I'm meeting my wife this afternoon",
"Thought 2:" => "I also want it for my wife",
"Act 2:" => "chatbox",
"Actinput 2:" => "I would like to get 2 more",
)
julia> skipHeaders = ["Plan"]
julia> step = 2
julia> removeHeaders(shortMemory, step, skipHeaders)
OrderedDict(
"user:" => "May I try this one?",
"Plan 1:" => "testing a small portion of icecream",
"Thought 1:" => "I like it.",
"Act 1:" => "chatbox",
"Actinput 1:" => "I get this one.",
"Plan 2:" => "I'm meeting my wife this afternoon",
)
```
"""
function removeHeaders(shortMemory::OrderedDict, step::Int,
skipHeaders::Union{Array{String}, Array{Symbol}, Nothing}=nothing)
newdict = similar(shortMemory)
for (k, v) in shortMemory
if occursin("$step", k)
if skipHeaders !== nothing
for i in skipHeaders
if occursin(i, k)
newdict[k] = v
else
# skip, not copy
end
end
else
# no copy
end
else
newdict[k] = v
end
end
return newdict
end
""" Keep only specified keys in a dictionary. All non-specified keys will be removed.
Arguments:
dict = a dictionary
keys = keys you want to keep in a dict
Return:
a dict with all non-specified keys removed
# Example
```jldoctest
julia> dict = OrderedDict(
"user:" => "May I try this one?",
"Plan 1:" => "testing a small portion of icecream",
"Thought 1:" => "I like it.",
"Act 1:" => "chatbox",
"Actinput 1:" => "I get this one.",
"Plan 2:" => "I'm meeting my wife this afternoon",
"Thought 2:" => "I also want it for my wife",
"Act 2:" => "chatbox",
"Actinput 2:" => "I would like to get 2 more",
)
julia> keys = ["user:"]
julia> keepOnlyKeys(dict, keys)
OrderedDict(
"user:" => "May I try this one?",
)
```
"""
function keepOnlyKeys(dict::T1, keys::T2) where {T1<:AbstractDict, T2<:AbstractVector}
newdict = similar(dict)
for (k, v) in dict
if k keys
newdict[k] = v
end
end
return newdict
end
""" Convert experience dict into 1 string for LLM to use.
Arguments:
dict = a dictionary contain past experience
Return:
An experience in 1 string without context keys.
# Example
```jldoctest
julia> dict = OrderedDict{String, Any}(
" This lesson can be applied to various situations => " Gathering accurate and relevant information about the user's preferences, budget, and event details is crucial for providing personalized recommendations.\n"
)
julia> experience(dict)
```
"""
function experience(dict::T) where {T<:AbstractDict}
s = ""
for (k, v) in dict
s *= v
end
return s
end
""" Get the latest step number of short term memory
Arguments:
dict = a dictionary contain past experience
Return:
latest step number
# Example
```jldoctest
julia> dict = OrderedDict(
"Plan 1:" => "1. Ask about the type of food that will be served at the wedding party.")
julia>shortMemLatestTask(dict)
1
"""
function shortMemLatestTask(dict::T) where {T<:AbstractDict}
_latest_step = keys(dict)
_latest_step = [i for i in _latest_step]
_latest_step = _latest_step[end]
latest_step = parse(Int, _latest_step[end-2:end-1])
return latest_step
end
end # end module