use Dict string key

This commit is contained in:
2026-06-24 20:46:24 +07:00
parent de20764610
commit 9c22cf2e31
4 changed files with 226 additions and 228 deletions
+85 -85
View File
@@ -64,29 +64,29 @@ end
# Example
```jldoctest
julia> config = Dict(
:mqttServerInfo => Dict(
:description => "mqtt server info",
:port => 1883,
:broker => "mqtt.yiem.cc"
),
:externalservice => Dict(
"text"2textinstruct => Dict(
:mqtttopic => "/loadbalancer/requestingservice",
:description => "text to text service with instruct LLM",
:llminfo => Dict(
"name" => "llama3instruct"
)
),
)
)
"mqttServerInfo" => Dict(
"description" => "mqtt server info",
"port" => 1883,
"broker" => "mqtt.yiem.cc"
),
"externalservice" => Dict(
"text2textinstruct" => Dict(
"mqtttopic" => "/loadbalancer/requestingservice",
"description" => "text to text service with instruct LLM",
"llminfo" => Dict(
"name" => "llama3instruct"
)
),
)
)
julia> output_thoughtDict = Dict(
:thought_1 => "The customer wants to buy a bottle of wine. This is a good start!",
:action_1 => Dict{String, Any}(
:action=>"CHATBOX",
:input=>"What occasion are you buying the wine for?"
"thought_1" => "The customer wants to buy a bottle of wine. This is a good start!",
"action_1" => Dict{String, Any}(
"action"=>"CHATBOX",
"input"=>"What occasion are you buying the wine for?"
),
:observation_1 => ""
"observation_1" => ""
)
```
- [] update docstring
@@ -123,7 +123,7 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
# end
recentevents_ind = GeneralUtils.recentElementsIndex(
length(a.memory[:events]), recentevents; includelatest=true)
length(a.memory["events"]), recentevents; includelatest=true)
requiredKeys = ["plan", "actionname", "actioninput"]
@@ -203,8 +203,8 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
# check whether the wine is in event
isWineInEvent = false
for winename in mentioned_winery
for event in a.memory[:events]
if event[:observation] !== nothing && occursin(winename, event[:observation])
for event in a.memory["events"]
if event["observation"] !== nothing && occursin(winename, event["observation"])
isWineInEvent = true
break
end
@@ -237,19 +237,19 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
end
# store for later training
responsedict[:system] = systemmsg
responsedict[:unformatPrompt] = unformatPrompt
responsedict[:prompt] = prompt
responsedict[:context] = context
responsedict[:think] = think
responsedict[:response] = response
# responsedict[:QandA] = QandA
responsedict["system"] = systemmsg
responsedict["unformatPrompt"] = unformatPrompt
responsedict["prompt"] = prompt
responsedict["context"] = context
responsedict["think"] = think
responsedict["response"] = response
# responsedict["QandA"] = QandA
# check whether there is a file path exists before writing to it
if !haskey(a.memory["shortmem"], :decisionlog)
a.memory["shortmem"][:decisionlog] = [responsedict]
if !haskey(a.memory["shortmem"], "decisionlog")
a.memory["shortmem"]["decisionlog"] = [responsedict]
else
push!(a.memory["shortmem"][:decisionlog], responsedict)
push!(a.memory["shortmem"]["decisionlog"], responsedict)
end
# # save to filename ./log/decisionlog.txt
@@ -275,7 +275,7 @@ function decisionMaker(a::T; recentevents::Integer=20, maxattempt=10
# println("\nYiemAgent decisionMaker() saved to disk is done. agent $(a.id)")
responsedict[:prompt] = prompt
responsedict["prompt"] = prompt
return responsedict
end
error("DecisionMaker failed to generate a thought ", response)
@@ -531,7 +531,7 @@ function conversation(a::Union{companion, virtualcustomer}, userinput::Dict;
addNewMessage(a, "user", userinput["text"]; maximumMsg=maximumMsg)
# add user activity to events memory
push!(a.memory[:events],
push!(a.memory["events"],
eventdict(;
event_description="the user talks to the assistant.",
timestamp=Dates.now(),
@@ -544,7 +544,7 @@ function conversation(a::Union{companion, virtualcustomer}, userinput::Dict;
addNewMessage(a, "assistant", chatresponse; maximumMsg=maximumMsg)
push!(a.memory[:events],
push!(a.memory["events"],
eventdict(;
event_description="the assistant talks to the user.",
timestamp=Dates.now(),
@@ -592,18 +592,18 @@ function think(a::T)::namedTuple{(:actionname, :result),Tuple{String,String}} wh
end
# this section allow LLM functions above to have different return values.
result = haskey(response, :result) ? response[:result] : nothing
rawresponse = haskey(response, :rawresponse) ? response[:rawresponse] : nothing
select = haskey(response, :select) ? response[:select] : nothing
reward::Integer = haskey(response, :reward) ? response[:reward] : 0
isterminal::Bool = haskey(response, :isterminal) ? response[:isterminal] : false
errormsg::Union{AbstractString,Nothing} = haskey(response, :errormsg) ? response[:errormsg] : nothing
success::Bool = haskey(response, :success) ? response[:success] : false
result = haskey(response, "result") ? response["result"] : nothing
rawresponse = haskey(response, "rawresponse") ? response["rawresponse"] : nothing
select = haskey(response, "select") ? response["select"] : nothing
reward::Integer = haskey(response, "reward") ? response["reward"] : 0
isterminal::Bool = haskey(response, "isterminal") ? response["isterminal"] : false
errormsg::Union{AbstractString,Nothing} = haskey(response, "errormsg") ? response["errormsg"] : nothing
success::Bool = haskey(response, "success") ? response["success"] : false
# # manage memory (pass msg to generatechat)
# if actionname ∈ ["CHATBOX", "PRESENTBOX", "ENDCONVERSATION"]
# chatresponse = generatechat(a, thoughtDict)
# push!(a.memory[:events],
# push!(a.memory["events"],
# eventdict(;
# event_description="the assistant talks to the user.",
# timestamp=Dates.now(),
@@ -615,7 +615,7 @@ function think(a::T)::namedTuple{(:actionname, :result),Tuple{String,String}} wh
# )
# result = chatresponse
if actionname ["CHATBOX"]
push!(a.memory[:events],
push!(a.memory["events"],
eventdict(;
event_description="an assistant talks to the user.",
timestamp=Dates.now(),
@@ -628,7 +628,7 @@ function think(a::T)::namedTuple{(:actionname, :result),Tuple{String,String}} wh
result = actioninput
elseif actionname ["ENDCONVERSATION"]
chatresponse = generatechat(a, thoughtDict)
push!(a.memory[:events],
push!(a.memory["events"],
eventdict(;
event_description="an assistant talks to the user to end conversation.",
timestamp=Dates.now(),
@@ -641,7 +641,7 @@ function think(a::T)::namedTuple{(:actionname, :result),Tuple{String,String}} wh
result = chatresponse
elseif actionname ["PRESENTBOX"]
chatresponse = presentbox(a, thoughtDict)
push!(a.memory[:events],
push!(a.memory["events"],
eventdict(;
event_description="the assistant presents wines to the user.",
timestamp=Dates.now(),
@@ -656,12 +656,12 @@ function think(a::T)::namedTuple{(:actionname, :result),Tuple{String,String}} wh
elseif actionname == "CHECKWINE"
if rawresponse !== nothing
vd = GeneralUtils.dfToVectorDict(rawresponse) # comes in dataframe format
# a.memory["shortmem"][:found_wine] = vd # used by decisionMaker() as a short note
#[PENDING] a.memory["shortmem"][:available_wine] should be OrderedDict instead of vector dict so that no duplicate item are listed?
if length(a.memory["shortmem"][:db_search_result]) != 0
a.memory["shortmem"][:db_search_result] = vcat(a.memory["shortmem"][:db_search_result], vd)
# a.memory["shortmem"]["found_wine"] = vd # used by decisionMaker() as a short note
#[PENDING] a.memory["shortmem"]["available_wine"] should be OrderedDict instead of vector dict so that no duplicate item are listed?
if length(a.memory["shortmem"]["db_search_result"]) != 0
a.memory["shortmem"]["db_search_result"] = vcat(a.memory["shortmem"]["db_search_result"], vd)
else
a.memory["shortmem"][:db_search_result] = vd
a.memory["shortmem"]["db_search_result"] = vd
end
# # add to scratchpad
@@ -680,7 +680,7 @@ function think(a::T)::namedTuple{(:actionname, :result),Tuple{String,String}} wh
# """
end
push!(a.memory[:events],
push!(a.memory["events"],
eventdict(;
event_description= "the assistant searched the database.",
timestamp= Dates.now(),
@@ -803,7 +803,7 @@ function presentbox(a::sommelier, thoughtDict; maxtattempt::Integer=10, recentev
end
# check if Context: is in dialogue
if occursin("Context:", responsedict[:dialogue])
if occursin("Context:", responsedict["dialogue"])
errornote = "Your previous response contains 'Context:' which is not allowed"
println("\nERROR YiemAgent presentbox() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
@@ -815,15 +815,15 @@ function presentbox(a::sommelier, thoughtDict; maxtattempt::Integer=10, recentev
# check whether an agent recommend wines before checking inventory or recommend wines
# outside its inventory
# ask LLM whether there are any winery mentioned in the response
mentioned_winery = detectWineryName(a, responsedict[:dialogue])
mentioned_winery = detectWineryName(a, responsedict["dialogue"])
if mentioned_winery != "None"
mentioned_winery = String.(strip.(split(mentioned_winery, ",")))
# check whether the wine is in event
isWineInEvent = false
for winename in mentioned_winery
for event in a.memory[:events]
if event[:observation] !== nothing && occursin(winename, event[:observation])
for event in a.memory["events"]
if event["observation"] !== nothing && occursin(winename, event["observation"])
isWineInEvent = true
break
end
@@ -850,7 +850,7 @@ function presentbox(a::sommelier, thoughtDict; maxtattempt::Integer=10, recentev
end
end
result = responsedict[:dialogue]
result = responsedict["dialogue"]
return result
end
error("presentbox() failed to generate a response")
@@ -913,10 +913,10 @@ function generatechat(a::sommelier, thoughtDict; maxattempt::Integer=10)
"""
requiredKeys = [:dialogue]
# a.memory["shortmem"][:available_wine] is a vector of dictionary
# a.memory["shortmem"]["available_wine"] is a vector of dictionary
# context =
# if length(a.memory["shortmem"][:available_wine]) != 0
# "Wines previously found in your inventory: $(availableWineToText(a.memory["shortmem"][:available_wine]))"
# if length(a.memory["shortmem"]["available_wine"]) != 0
# "Wines previously found in your inventory: $(availableWineToText(a.memory["shortmem"]["available_wine"]))"
# else
# "N/A"
# end
@@ -1003,8 +1003,8 @@ function generatechat(a::sommelier, thoughtDict; maxattempt::Integer=10)
# check whether the wine is in event
isWineInEvent = false
for winename in mentioned_winery
for event in a.memory[:events]
if event[:observation] !== nothing && occursin(winename, event[:observation])
for event in a.memory["events"]
if event["observation"] !== nothing && occursin(winename, event["observation"])
isWineInEvent = true
break
end
@@ -1018,7 +1018,7 @@ function generatechat(a::sommelier, thoughtDict; maxattempt::Integer=10)
continue
end
end
result = responsedict[:dialogue]
result = responsedict["dialogue"]
return result
end
@@ -1088,15 +1088,15 @@ function generatechat(a::virtualcustomer;
converPartnerName::Union{String, Nothing}=nothing, maxattempt=10, recentEventNum=10
)
recent_ind = GeneralUtils.recentElementsIndex(length(a.memory[:events]), recentEventNum; includelatest=true)
recentEventsDict = createEventsLog(a.memory[:events]; index=recent_ind)
recent_ind = GeneralUtils.recentElementsIndex(length(a.memory["events"]), recentEventNum; includelatest=true)
recentEventsDict = createEventsLog(a.memory["events"]; index=recent_ind)
response = nothing # placeholder for show when error msg show up
errornote = "N/A"
header = ["Dialogue:", "Role:"]
dictkey = ["dialogue", "role"]
llmkwargs=Dict(
:num_ctx => 32768,
:temperature => 0.5,
"num_ctx" => 32768,
"temperature" => 0.5,
)
for attempt in 1:maxattempt
@@ -1141,15 +1141,15 @@ function generatechat(a::virtualcustomer;
responsedict = GeneralUtils.textToDict(response, header;
dictKey=dictkey, symbolkey=true)
if responsedict[:role] == "no"
errornote = "In your previous attempt you said $(responsedict[:dialogue]) which you, as a customer of a wine store, are not supposed to speak."
if responsedict["role"] == "no"
errornote = "In your previous attempt you said $(responsedict["dialogue"]) which you, as a customer of a wine store, are not supposed to speak."
println("\nYiemAgent generatechat() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
end
# check if the dialogue is the same as the previous one
if length(responsedict[:dialogue]) != 0 && responsedict[:dialogue] == a.chathistory[end]["text"]
errornote = "In your previous attempt you said $(responsedict[:dialogue]) which was the same as the previous one."
if length(responsedict["dialogue"]) != 0 && responsedict["dialogue"] == a.chathistory[end]["text"]
errornote = "In your previous attempt you said $(responsedict["dialogue"]) which was the same as the previous one."
println("\nYiemAgent generatechat() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
end
@@ -1157,7 +1157,7 @@ function generatechat(a::virtualcustomer;
# check whether LLM just repeat the previous dialogue
dublicate = false
for msg in a.chathistory
if msg["text"] == responsedict[:dialogue]
if msg["text"] == responsedict["dialogue"]
errornote = "In your previous attempt, you repeated the earlier dialogue. Please try again."
println("\nYiemAgent generatechat() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
dublicate = true
@@ -1170,7 +1170,7 @@ function generatechat(a::virtualcustomer;
# println("\n$prompt", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# println("\n $response")
return responsedict[:dialogue]
return responsedict["dialogue"]
end
error("generatechat failed to generate a response")
end
@@ -1285,12 +1285,12 @@ function generatequestion(a, text2textInstructLLM::Function, timeline)::String
dictkey = ["q1"]
# context =
# if length(a.memory["shortmem"][:available_wine]) != 0
# "Available wines you've found in your inventory so far: $(availableWineToText(a.memory["shortmem"][:available_wine]))"
# if length(a.memory["shortmem"]["available_wine"]) != 0
# "Available wines you've found in your inventory so far: $(availableWineToText(a.memory["shortmem"]["available_wine"]))"
# else
# "N/A"
# end
database_search_result = a.memory["shortmem"][:db_search_result]
database_search_result = a.memory["shortmem"]["db_search_result"]
# recent_ind = GeneralUtils.recentElementsIndex(length(a.memory[:events]), recent)
# recentevents = a.memory[:events][recent_ind]
@@ -1357,8 +1357,8 @@ function generatequestion(a, text2textInstructLLM::Function, timeline)::String
# check whether the wine is in event
isWineInEvent = false
for winename in mentioned_winery
for event in a.memory[:events]
if event[:observation] !== nothing && occursin(winename, event[:observation])
for event in a.memory["events"]
if event["observation"] !== nothing && occursin(winename, event["observation"])
isWineInEvent = true
break
end
@@ -1402,7 +1402,7 @@ function generatequestion(a, text2textInstructLLM::Function, timeline)::String
responsedict = GeneralUtils.textToDict(response, header;
dictKey=dictkey, symbolkey=true)
response = "Q1: " * responsedict[:q1]
response = "Q1: " * responsedict["q1"]
println("\nYiemAgent generatequestion() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
try pprintln(response) catch e println(response) end
@@ -1446,11 +1446,11 @@ function generateSituationReport(a, text2textInstructLLM::Function; skiprecent::
Let's begin!
"""
header = ["Event_$i:" for i in eachindex(a.memory[:events])]
dictkey = lowercase.(["Event_$i" for i in eachindex(a.memory[:events])])
header = ["Event_$i:" for i in eachindex(a.memory["events"])]
dictkey = lowercase.(["Event_$i" for i in eachindex(a.memory["events"])])
ind = GeneralUtils.nonRecentElementsIndex(length(a.memory[:events]), skiprecent)
events = a.memory[:events][ind]
ind = GeneralUtils.nonRecentElementsIndex(length(a.memory["events"]), skiprecent)
events = a.memory["events"][ind]
timeline = createTimeline(events)
errornote = "N/A"
@@ -1570,7 +1570,7 @@ function detectWineryName(a, text)
responsedict = GeneralUtils.textToDict(response, header;
dictKey=dictkey, symbolkey=true)
result = responsedict[:winery_names]
result = responsedict["winery_names"]
return result
end