This commit is contained in:
narawat lamaiin
2025-05-16 10:26:50 +07:00
parent d0c26e52e8
commit 3e79c0bfed
3 changed files with 353 additions and 76 deletions

View File

@@ -97,7 +97,7 @@ julia> output_thoughtDict = Dict(
# Signature # Signature
""" """
function decisionMaker(a::T; recent::Integer=10, maxattempt=10 function decisionMaker(a::T; recentevents::Integer=10, maxattempt=10
) where {T<:agent} ) where {T<:agent}
# lessonDict = copy(JSON3.read("lesson.json")) # lessonDict = copy(JSON3.read("lesson.json"))
@@ -125,35 +125,42 @@ function decisionMaker(a::T; recent::Integer=10, maxattempt=10
# """ # """
# end # end
recent_ind = GeneralUtils.recentElementsIndex(length(a.chathistory), recent; includelatest=true) recentevents_ind = GeneralUtils.recentElementsIndex(length(a.memory[:events]), recentevents;
recentevents = a.memory[:events][recent_ind] includelatest=true)
recentchat = createChatLog(a.chathistory[recent_ind]; index=recent_ind) timeline = createTimeline(a.memory[:events]; eventindex=recentevents_ind)
recentchat_ind = GeneralUtils.recentElementsIndex(length(a.chathistory), recentevents;
includelatest=true)
recentchat = createChatLog(a.chathistory; index=recentchat_ind)
# recentEventsDict = createEventsLog(recentevents; index=recent_ind) # recentEventsDict = createEventsLog(recentevents; index=recent_ind)
#BUG timeline only cover event 1-9 out of 10 events while recentchat cover 1-9 because
# recent_ind is based on chathistory. i should ind based on events. The reason is events always
# have more than chathistory due to CHECKINVENTORY() function which store in events BUT NOT in
# chathistory
timeline = createTimeline(recentevents; eventindex=recent_ind)
# recap as caching # # recap as caching
# query similar result from vectorDB # # query similar result from vectorDB
recapkeys = keys(a.memory[:recap]) # recapkeys = keys(a.memory[:recap])
_recapkeys_vec = [i for i in recapkeys] # _recapkeys_vec = [i for i in recapkeys]
# select recent keys # # select recent keys
_recentRecapKeys = # _recentRecapKeys =
if length(a.memory[:recap]) <= 3 # 1st message is a user's hello msg # if length(a.memory[:recap]) <= 3 # 1st message is a user's hello msg
_recapkeys_vec # _recapkeys_vec
elseif length(a.memory[:recap]) > 3 # elseif length(a.memory[:recap]) > 3
l = length(a.memory[:recap]) # l = length(a.memory[:recap])
_recapkeys_vec[l-2:l] # _recapkeys_vec[l-2:l]
end # end
# get recent recap # # get recent recap
_recentrecap = OrderedDict() # _recentrecap = OrderedDict()
for (k, v) in a.memory[:recap] # for (k, v) in a.memory[:recap]
if k _recentRecapKeys # if k ∈ _recentRecapKeys
_recentrecap[k] = v # _recentrecap[k] = v
end # end
end # end
# recentrecap = GeneralUtils.dictToString_noKey(_recentrecap) # recentrecap = GeneralUtils.dictToString_noKey(_recentrecap)
# similarDecision = a.func[:similarSommelierDecision](recentrecap) # similarDecision = a.func[:similarSommelierDecision](recentrecap)
@@ -415,7 +422,7 @@ function decisionMaker(a::T; recent::Integer=10, maxattempt=10
#[WORKING] #[WORKING]
evaluationdict = evaluator(a, timeline, responsedict, context) evaluationdict = evaluator(a, timeline, responsedict, context)
if evaluationdict[:good_decision] == "no" if evaluationdict[:approved] == "no"
mentor_comment = evaluationdict[:suggestion] mentor_comment = evaluationdict[:suggestion]
errornote = "Your previous attempt was not good enough. Please try again. Here is the mentor's suggestion: $mentor_comment" errornote = "Your previous attempt was not good enough. Please try again. Here is the mentor's suggestion: $mentor_comment"
println("\nERROR YiemAgent decisionMaker() - $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent decisionMaker() - $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
@@ -851,7 +858,7 @@ function evaluator(a::T1, timeline, decisiondict, evaluateecontext
- Do not generate additional thoughts or actions. - Do not generate additional thoughts or actions.
2) decision_evaluation: 2) decision_evaluation:
- Examine how the trainee's decisions align with the store's policies and guidelines before proceeding. - Examine how the trainee's decisions align with the store's policies and guidelines before proceeding.
3) good_decision: Decide whether the decision make sense under the circumstances. Can be "yes" or "no" 3) approved: Decide whether to let the trainee proceed or not. Can be "yes" or "no"
4) suggestion: Based store policy and guidelines, provide a suggestion for the immediate decision step only. 4) suggestion: Based store policy and guidelines, provide a suggestion for the immediate decision step only.
</You should then respond to the user with> </You should then respond to the user with>
@@ -859,14 +866,14 @@ function evaluator(a::T1, timeline, decisiondict, evaluateecontext
{ {
"trajectory_evaluation": "..." "trajectory_evaluation": "..."
"decision_evaluation": "..." "decision_evaluation": "..."
"good_decision": "..." "approved": "..."
"suggestion": "..." "suggestion": "..."
} }
</You should only respond in format as described below> </You should only respond in format as described below>
Let's begin! Let's begin!
""" """
requiredKeys = [:trajectory_evaluation, :decision_evaluation, :good_decision, :suggestion] requiredKeys = [:trajectory_evaluation, :decision_evaluation, :approved, :suggestion]
errornote = "N/A" errornote = "N/A"
for attempt in 1:10 for attempt in 1:10
@@ -1116,7 +1123,7 @@ julia>
""" """
function think(a::T)::NamedTuple{(:actionname, :result),Tuple{String,String}} where {T<:agent} function think(a::T)::NamedTuple{(:actionname, :result),Tuple{String,String}} where {T<:agent}
# a.memory[:recap] = generateSituationReport(a, a.func[:text2textInstructLLM]; skiprecent=0) # a.memory[:recap] = generateSituationReport(a, a.func[:text2textInstructLLM]; skiprecent=0)
thoughtDict = decisionMaker(a; recent=5) thoughtDict = decisionMaker(a; recentevents=5)
actionname = thoughtDict[:action_name] actionname = thoughtDict[:action_name]
actioninput = thoughtDict[:action_input] actioninput = thoughtDict[:action_input]
@@ -1230,30 +1237,39 @@ function think(a::T)::NamedTuple{(:actionname, :result),Tuple{String,String}} wh
end end
function presentbox(a::sommelier, thoughtDict; maxtattempt::Integer=10) function presentbox(a::sommelier, thoughtDict; maxtattempt::Integer=10, recentevents::Integer=10)
recentchat_ind = GeneralUtils.recentElementsIndex(length(a.chathistory), recentevents;
includelatest=true)
recentchat = createChatLog(a.chathistory; index=recentchat_ind)
systemmsg = systemmsg =
""" """
Your profile: <Your role>
Your name is $(a.name). You are a helpful English-speaking assistant, acting as a polite, website-based sommelier for $(a.retailername)'s wine store. Your name is $(a.name). You are a helpful English-speaking assistant, acting as a polite, website-based sommelier for $(a.retailername)'s wine store.
Situation: </Your role>
You have checked the inventory and found wines. <Situation>
Your mission: You have checked the inventory and found wines that may match what the user wants
Present the wines to the customer in a way that keep the conversation smooth and engaging. </Situation>
At each round of conversation, you will be given the following information: <Your mission>
Present the wines to the user in a way that keep the conversation smooth and engaging.
</Your mission>
<At each round of conversation, you will be given the following information>
Name of the wines that needs to be introduced: name of wines you are going to introduce to the user
Database search result: the result of a database search using SQL commands you have found so far Database search result: the result of a database search using SQL commands you have found so far
Chat history: your ongoing conversation with the user </At each round of conversation, you will be given the following information>
Wine name: name of wines you are going to introduce. <You should follow the following guidelines>
You should follow the following guidelines:
- Provide detailed introductions of the wines you've found to the user. - Provide detailed introductions of the wines you've found to the user.
- Explain how the wine could match the user's intention and what its effects might mean for the user's experience. - Explain how the wine could match the user's intention and what its effects might mean for the user's experience.
- If multiple wines are available, highlight their differences and provide a comprehensive comparison of how each option aligns with the user's intention and what the potential effects of each option could mean for the user's experience. - If multiple wines are available, highlight their differences and provide a comprehensive comparison of how each option aligns with the user's intention and what the potential effects of each option could mean for the user's experience.
- Provide your personal recommendation and provide a brief explanation of why you recommend it. - Provide your personal recommendation and provide a brief explanation of why you recommend it.
You should then respond to the user with: </You should follow the following guidelines>
<You should then respond to the user with>
dialogue: Your presentation to the user dialogue: Your presentation to the user
You should only respond in format as described below: </You should then respond to the user with>
<You should only respond in format as described below>
{ {
"dialogue": "..." "dialogue": "..."
} }
</You should only respond in format as described below>
Let's begin! Let's begin!
""" """
@@ -1265,7 +1281,7 @@ function presentbox(a::sommelier, thoughtDict; maxtattempt::Integer=10)
"N/A" "N/A"
end end
chathistory = chatHistoryToText(a.chathistory) # chathistory = chatHistoryToText(a.chathistory)
errornote = "N/A" errornote = "N/A"
response = nothing # placeholder for show when error msg show up response = nothing # placeholder for show when error msg show up
@@ -1279,9 +1295,8 @@ function presentbox(a::sommelier, thoughtDict; maxtattempt::Integer=10)
context = context =
""" """
<context> <context>
Name of the wines that needs to be introduced: $(thoughtDict[:action_input])
Database search result: $database_search_result Database search result: $database_search_result
Chat history: $chathistory
Wine name: $(thoughtDict[:action_input])
P.S. $errornote P.S. $errornote
</context> </context>
""" """
@@ -1291,6 +1306,7 @@ function presentbox(a::sommelier, thoughtDict; maxtattempt::Integer=10)
Dict(:name => "system", :text => systemmsg), Dict(:name => "system", :text => systemmsg),
] ]
unformatPrompt = vcat(unformatPrompt, recentchat)
# put in model format # put in model format
prompt = GeneralUtils.formatLLMtext(unformatPrompt, a.llmFormatName) prompt = GeneralUtils.formatLLMtext(unformatPrompt, a.llmFormatName)
# add info # add info
@@ -1311,7 +1327,7 @@ function presentbox(a::sommelier, thoughtDict; maxtattempt::Integer=10)
try try
responsedict = copy(JSON3.read(response)) responsedict = copy(JSON3.read(response))
catch catch
println("\nERROR YiemAgent presentbox() failed to parse response: $response", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent presentbox() failed to parse response: $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
@@ -1400,6 +1416,176 @@ function presentbox(a::sommelier, thoughtDict; maxtattempt::Integer=10)
end end
error("presentbox() failed to generate a response") error("presentbox() failed to generate a response")
end end
# function presentbox(a::sommelier, thoughtDict; maxtattempt::Integer=10)
# systemmsg =
# """
# Your profile:
# Your name is $(a.name). You are a helpful English-speaking assistant, acting as a polite, website-based sommelier for $(a.retailername)'s wine store.
# Situation:
# You have checked the inventory and found wines.
# Your mission:
# Present the wines to the customer in a way that keep the conversation smooth and engaging.
# At each round of conversation, you will be given the following information:
# Database search result: the result of a database search using SQL commands you have found so far
# Chat history: your ongoing conversation with the user
# Name of the wines that needs to be introduced: name of wines you are going to introduce to the user.
# You should follow the following guidelines:
# - Provide detailed introductions of the wines you've found to the user.
# - Explain how the wine could match the user's intention and what its effects might mean for the user's experience.
# - If multiple wines are available, highlight their differences and provide a comprehensive comparison of how each option aligns with the user's intention and what the potential effects of each option could mean for the user's experience.
# - Provide your personal recommendation and provide a brief explanation of why you recommend it.
# You should then respond to the user with:
# dialogue: Your presentation to the user
# You should only respond in format as described below:
# {
# "dialogue": "..."
# }
# Let's begin!
# """
# requiredKeys = [:dialogue]
# database_search_result =
# if length(a.memory[:shortmem][:db_search_result]) != 0
# availableWineToText(a.memory[:shortmem][:db_search_result])
# else
# "N/A"
# end
# chathistory = chatHistoryToText(a.chathistory)
# errornote = "N/A"
# response = nothing # placeholder for show when error msg show up
# # yourthought = "$(thoughtDict[:thought]) $(thoughtDict[:plan])"
# # yourthought1 = nothing
# for attempt in 1:maxtattempt
# context =
# """
# <context>
# Database search result: $database_search_result
# Chat history: $chathistory
# Name of the wines that needs to be introduced: $(thoughtDict[:action_input])
# P.S. $errornote
# </context>
# """
# unformatPrompt =
# [
# Dict(:name => "system", :text => systemmsg),
# ]
# # put in model format
# prompt = GeneralUtils.formatLLMtext(unformatPrompt, a.llmFormatName)
# # add info
# prompt = prompt * context
# response = a.func[:text2textInstructLLM](prompt; senderId=a.id)
# response = GeneralUtils.deFormatLLMtext(response, a.llmFormatName)
# response = GeneralUtils.remove_french_accents(response)
# # response = replace(response, '$'=>"USD")
# think, response = GeneralUtils.extractthink(response)
# response = replace(response, '*'=>"")
# response = replace(response, '$' => "USD")
# response = replace(response, '`' => "")
# response = replace(response, "<|eot_id|>"=>"")
# responsedict = nothing
# try
# responsedict = copy(JSON3.read(response))
# catch
# println("\nERROR YiemAgent presentbox() failed to parse response: $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue
# end
# # check whether all answer's key points are in responsedict
# _responsedictKey = keys(responsedict)
# responsedictKey = [i for i in _responsedictKey] # convert into a list
# is_requiredKeys_in_responsedictKey = [i ∈ responsedictKey for i in requiredKeys]
# if length(is_requiredKeys_in_responsedictKey) > length(requiredKeys)
# errornote = "Your previous attempt has more key points than answer's required key points."
# println("\nERROR YiemAgent presentbox() $errornote --> $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue
# elseif !all(is_requiredKeys_in_responsedictKey)
# zeroind = findall(x -> x == 0, is_requiredKeys_in_responsedictKey)
# missingkeys = [requiredKeys[i] for i in zeroind]
# errornote = "$missingkeys are missing from your previous response"
# println("\nERROR YiemAgent presentbox() $errornote --> $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue
# end
# # # check whether response has not-allowed words
# # notallowed = ["respond:", "user>", "user:"]
# # detected_kw = GeneralUtils.detect_keyword(notallowed, response)
# # # list all keys that have 1 value in detected_kw dictionary
# # k = [key for (key, value) in detected_kw if value == 1]
# # if length(k) > 0
# # errornote = "In your previous attempt, you have $k in your response which is not allowed."
# # println("\nERROR YiemAgent presentbox() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# # continue
# # end
# # # check whether response has all header
# # detected_kw = GeneralUtils.detect_keyword(header, response)
# # if 0 ∈ values(detected_kw)
# # errornote = "$missingkeys are missing from your previous response"
# # println("\nERROR YiemAgent presentbox() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# # continue
# # elseif sum(values(detected_kw)) > length(header)
# # errornote = "\nYour previous attempt has duplicated points according to the required response format"
# # println("\nERROR YiemAgent presentbox() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# # continue
# # end
# # responsedict = GeneralUtils.textToDict(response, header;
# # dictKey=dictkey, symbolkey=true)
# # check if Context: is in 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
# end
# println("\nYiemAgent presentbox() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# pprintln(Dict(responsedict))
# # 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])
# 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[:outcome] !== nothing && occursin(winename, event[:outcome])
# isWineInEvent = true
# break
# end
# end
# end
# # if wine is mentioned but not in timeline or shortmem,
# # then the agent is not supposed to recommend the wine
# if isWineInEvent == false
# errornote = "Your previous response recommended wines that is not in your inventory which is not allowed"
# println("\nERROR YiemAgent presentbox() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue
# end
# end
# result = responsedict[:dialogue]
# return result
# end
# error("presentbox() failed to generate a response")
# end
@@ -1547,7 +1733,7 @@ function generatechat(a::sommelier, thoughtDict; maxattempt::Integer=10)
response = replace(response, '$' => "USD") response = replace(response, '$' => "USD")
response = replace(response, '`' => "") response = replace(response, '`' => "")
response = replace(response, "<|eot_id|>"=>"") response = replace(response, "<|eot_id|>"=>"")
response = GeneralUtils.remove_french_accents(response)
@@ -1612,35 +1798,39 @@ function generatechat(a::sommelier, thoughtDict; maxattempt::Integer=10)
end end
function generatechat(a::companion; converPartnerName::Union{String, Nothing}=nothing, maxattempt=10) function generatechat(a::companion; recentevents::Integer=10,
converPartnerName::Union{String, Nothing}=nothing, maxattempt=10)
recentchat_ind = GeneralUtils.recentElementsIndex(length(a.chathistory), recentevents;
includelatest=true);
recentchat = createChatLog(a.chathistory; index=recentchat_ind)
response = nothing # placeholder for show when error msg show up response = nothing # placeholder for show when error msg show up
errornote = "N/A" errornote = "N/A"
llmkwargs=Dict(
:num_ctx => 32768,
:temperature => 0.5,
)
for attempt in 1:maxattempt for attempt in 1:maxattempt
if attempt > 1 if attempt > 1
println("\nYiemAgent generatechat() attempt $attempt/$maxattempt ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nYiemAgent generatechat() attempt $attempt/$maxattempt ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
end end
systemmsg = a.systemmsg * "\nP.S. $errornote\n" context =
_prompt = """
[ <context>
Dict(:name => "system", :text => systemmsg), P.S. $errornote
] </context>
for i in a.chathistory """
tempdict = Dict{Symbol, String}()
for j in keys(i)
if j [:timestamp]
tempdict[j] = i[j]
end
end
_prompt = vcat(_prompt, tempdict)
end
unformatPrompt =
[
Dict(:name => "system", :text => a.systemmsg),
]
unformatPrompt = vcat(unformatPrompt, recentchat)
# put in model format # put in model format
_prompt = GeneralUtils.formatLLMtext(_prompt, a.llmFormatName) prompt = GeneralUtils.formatLLMtext(unformatPrompt, a.llmFormatName)
# add info
prompt = prompt * context
# replace user and assistant with partner name # replace user and assistant with partner name
prompt = replace(_prompt, "|>user"=>"|>$(converPartnerName)") prompt = replace(_prompt, "|>user"=>"|>$(converPartnerName)")
@@ -1664,6 +1854,58 @@ function generatechat(a::companion; converPartnerName::Union{String, Nothing}=no
end end
error("generatechat failed to generate a response") error("generatechat failed to generate a response")
end end
# function generatechat(a::companion; converPartnerName::Union{String, Nothing}=nothing, maxattempt=10)
# response = nothing # placeholder for show when error msg show up
# errornote = "N/A"
# llmkwargs=Dict(
# :num_ctx => 32768,
# :temperature => 0.5,
# )
# for attempt in 1:maxattempt
# if attempt > 1
# println("\nYiemAgent generatechat() attempt $attempt/$maxattempt ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# end
# systemmsg = a.systemmsg * "\nP.S. $errornote\n"
# _prompt =
# [
# Dict(:name => "system", :text => systemmsg),
# ]
# for i in a.chathistory
# tempdict = Dict{Symbol, String}()
# for j in keys(i)
# if j ∉ [:timestamp]
# tempdict[j] = i[j]
# end
# end
# _prompt = vcat(_prompt, tempdict)
# end
# # put in model format
# _prompt = GeneralUtils.formatLLMtext(_prompt, a.llmFormatName)
# # replace user and assistant with partner name
# prompt = replace(_prompt, "|>user"=>"|>$(converPartnerName)")
# prompt = replace(prompt, "|>assistant"=>"|>$(a.name)")
# response = a.func[:text2textInstructLLM](prompt; llmkwargs=llmkwargs, senderId=a.id)
# response = replace(response, "<|im_start|>"=> "")
# response = GeneralUtils.deFormatLLMtext(response, a.llmFormatName)
# think, response = GeneralUtils.extractthink(response)
# # check whether LLM just repeat the previous dialogue
# for msg in a.chathistory
# if msg[:text] == response
# errornote = "In your previous attempt, you repeated the previous dialogue. Please try again."
# println("\nYiemAgent generatechat() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue
# end
# end
# return response
# end
# error("generatechat failed to generate a response")
# end
# modify it to work with customer object # modify it to work with customer object

View File

@@ -307,7 +307,7 @@ function checkinventory(a::T1, input::T2
println("\ncheckinventory result ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\ncheckinventory result ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
println(textresult) println(textresult)
#[PENDING] if there is no wine id, it is not a valid result
return (result=textresult, rawresponse=rawresponse, success=true, errormsg=nothing) return (result=textresult, rawresponse=rawresponse, success=true, errormsg=nothing)
end end

View File

@@ -297,20 +297,53 @@ function createTimeline(events::T1; eventindex::Union{UnitRange, Nothing}=nothin
1:length(events) 1:length(events)
end end
# Iterate through events and format each one #[WORKING] Iterate through events and format each one
for (i, event) in zip(ind, events) for i in ind
event = events[i]
# If no outcome exists, format without outcome # If no outcome exists, format without outcome
if event[:outcome] === nothing # if event[:actionname] == "CHATBOX"
timeline *= "Event_$i $(event[:subject])> action_name: $(event[:actionname]), action_input: $(event[:actioninput]), observation: Not done yet.\n" # timeline *= "Event_$i $(event[:subject])> action_name: $(event[:actionname]), action_input: $(event[:actioninput])\n"
# elseif event[:actionname] == "CHECKINVENTORY" && event[:outcome] === nothing
# timeline *= "Event_$i $(event[:subject])> action_name: $(event[:actionname]), action_input: $(event[:actioninput]), observation: Not done yet.\n"
# If outcome exists, include it in formatting # If outcome exists, include it in formatting
else if event[:actionname] == "CHECKINVENTORY"
timeline *= "Event_$i $(event[:subject])> action_name: $(event[:actionname]), action_input: $(event[:actioninput]), observation: $(event[:outcome])\n" timeline *= "Event_$i $(event[:subject])> action_name: $(event[:actionname]), action_input: $(event[:actioninput]), observation: $(event[:outcome])\n"
else
timeline *= "Event_$i $(event[:subject])> action_name: $(event[:actionname]), action_input: $(event[:actioninput])\n"
end end
end end
# Return formatted timeline string # Return formatted timeline string
return timeline return timeline
end end
# function createTimeline(events::T1; eventindex::Union{UnitRange, Nothing}=nothing
# ) where {T1<:AbstractVector}
# # Initialize empty timeline string
# timeline = ""
# # Determine which indices to use - either provided range or full length
# ind =
# if eventindex !== nothing
# [eventindex...]
# else
# 1:length(events)
# end
# # Iterate through events and format each one
# for i in ind
# event = events[i]
# # If no outcome exists, format without outcome
# if event[:outcome] === nothing
# timeline *= "Event_$i $(event[:subject])> action_name: $(event[:actionname]), action_input: $(event[:actioninput]), observation: Not done yet.\n"
# # If outcome exists, include it in formatting
# else
# timeline *= "Event_$i $(event[:subject])> action_name: $(event[:actionname]), action_input: $(event[:actioninput]), observation: $(event[:outcome])\n"
# end
# end
# # Return formatted timeline string
# return timeline
# end
function createEventsLog(events::T1; index::Union{UnitRange, Nothing}=nothing function createEventsLog(events::T1; index::Union{UnitRange, Nothing}=nothing
@@ -327,7 +360,8 @@ function createEventsLog(events::T1; index::Union{UnitRange, Nothing}=nothing
end end
# Iterate through events and format each one # Iterate through events and format each one
for (i, event) in zip(ind, events) for i in ind
event = events[i]
# If no outcome exists, format without outcome # If no outcome exists, format without outcome
if event[:outcome] === nothing if event[:outcome] === nothing
subject = event[:subject] subject = event[:subject]
@@ -362,7 +396,8 @@ function createChatLog(chatdict::T1; index::Union{UnitRange, Nothing}=nothing
end end
# Iterate through events and format each one # Iterate through events and format each one
for (i, event) in zip(ind, chatdict) for i in ind
event = chatdict[i]
subject = event[:name] subject = event[:name]
text = event[:text] text = event[:text]
d = Dict{Symbol, String}(:name=>subject, :text=>text) d = Dict{Symbol, String}(:name=>subject, :text=>text)