use string key for dict

This commit is contained in:
2026-06-25 06:16:30 +07:00
parent f5875dcb61
commit bc81033924
4 changed files with 155 additions and 155 deletions
+104 -104
View File
@@ -23,22 +23,22 @@ using ..util, ..llmfunction
A function that handles communication to LLM service A function that handles communication to LLM service
# Return # Return
- `thoughtDict::Dict{Symbol, Any}` - `thoughtDict::Dict{String, Any}`
# Example # Example
```jldoctest ```jldoctest
julia> using SQLLLM, GeneralUtils, UUIDs, DataStructures, PrettyPrinting julia> using SQLLLM, GeneralUtils, UUIDs, DataStructures, PrettyPrinting
julia> state = Dict( julia> state = Dict(
:isterminal => false, "isterminal" => false,
:lesson => nothing, "lesson" => nothing,
:reward => 0, "reward" => 0,
:evaluation => "None", "evaluation" => "None",
:accepted_as_answer => "No", "accepted_as_answer" => "No",
:thoughtHistory => OrderedDict{Symbol, Any}(:question => "How many wines do you have that can be paired with lamb?"), "thoughtHistory" => OrderedDict{String, Any}("question" => "How many wines do you have that can be paired with lamb?"),
:evaluationscore => 0, "evaluationscore" => 0,
:suggestion => "None" "suggestion" => "None"
) )
julia> context = Dict(:tablelist=> "None") julia> context = Dict("tablelist"=> "None")
julia> function text2textInstructLLM(prompt::String) julia> function text2textInstructLLM(prompt::String)
config = Dict( config = Dict(
:mqttServerInfo => Dict( :mqttServerInfo => Dict(
@@ -104,7 +104,7 @@ Dict(
""" """
function decisionMaker(state::T1, additionalinfo, text2textInstructLLM::Function, llmFormatName::String function decisionMaker(state::T1, additionalinfo, text2textInstructLLM::Function, llmFormatName::String
; querySQLVectorDBF::Union{T2, Nothing}=nothing, maxattempt=10 ; querySQLVectorDBF::Union{T2, Nothing}=nothing, maxattempt=10
)::Dict{Symbol, Any} where {T1<:AbstractDict, T2<:Function} )::Dict{String, Any} where {T1<:AbstractDict, T2<:Function}
# lessonDict = # lessonDict =
# if isfile("lesson.json") # if isfile("lesson.json")
@@ -174,8 +174,8 @@ function decisionMaker(state::T1, additionalinfo, text2textInstructLLM::Function
""" """
requiredKeys = [:plan, :action_name, :action_input] requiredKeys = [:plan, :action_name, :action_input]
workprogress = "" workprogress = ""
for (k, v) in state[:thoughtHistory] for (k, v) in state["thoughtHistory"]
if k [:question] if k ["question"]
workprogress *= "$k: $v\n" workprogress *= "$k: $v\n"
end end
end end
@@ -184,11 +184,11 @@ function decisionMaker(state::T1, additionalinfo, text2textInstructLLM::Function
errornote = "N/A" errornote = "N/A"
# provide similar sql only for the first attempt # provide similar sql only for the first attempt
similarSQL_ = "None" similarSQL_ = "None"
if length(state[:thoughtHistory]) == 1 if length(state["thoughtHistory"]) == 1
sql, distance = querySQLVectorDBF(state[:thoughtHistory][:question]) sql, distance = querySQLVectorDBF(state["thoughtHistory"]["question"])
similarSQL_ = sql !== nothing ? sql : "None" similarSQL_ = sql !== nothing ? sql : "None"
end end
for attempt in 1:maxattempt for attempt in 1:maxattempt
@@ -211,7 +211,7 @@ function decisionMaker(state::T1, additionalinfo, text2textInstructLLM::Function
$workprogress $workprogress
</progress> </progress>
<suggestion> This is your mentor's suggestion for the immediately preceding action and observation <suggestion> This is your mentor's suggestion for the immediately preceding action and observation
$(state[:suggestion]) $(state["suggestion"])
</suggestion> </suggestion>
P.S. $errornote P.S. $errornote
</context> </context>
@@ -220,7 +220,7 @@ function decisionMaker(state::T1, additionalinfo, text2textInstructLLM::Function
unformatPrompt = unformatPrompt =
[ [
Dict(:name => "system", :text => systemmsg), Dict(:name => "system", :text => systemmsg),
Dict(:name => "user", :text => state[:thoughtHistory][:question]) Dict("name" => "user", "text" => state["thoughtHistory"]["question"])
] ]
# put in model format # put in model format
@@ -262,31 +262,31 @@ function decisionMaker(state::T1, additionalinfo, text2textInstructLLM::Function
continue continue
end end
delete!(responsedict, :observation) delete!(responsedict, "observation")
# remove backticks Error occurred: MethodError: no method matching occursin(::String, ::Vector{String}) # remove backticks Error occurred: MethodError: no method matching occursin(::String, ::Vector{String})
if occursin("```", responsedict[:action_input]) if occursin("```", responsedict["action_input"])
sql = GeneralUtils.extract_triple_backtick_text(responsedict[:action_input])[1] sql = GeneralUtils.extract_triple_backtick_text(responsedict["action_input"])[1]
if sql[1:4] == "sql\n" if sql[1:4] == "sql\n"
sql = sql[5:end] sql = sql[5:end]
end end
sql = split(sql, ';') # some time there are comments in the sql sql = split(sql, ';') # some time there are comments in the sql
sql = sql[1] * ';' sql = sql[1] * ';'
responsedict[:action_input] = sql responsedict["action_input"] = sql
end end
toollist = ["TABLEINFO", "RUNSQL"] toollist = ["TABLEINFO", "RUNSQL"]
if responsedict[:action_name] toollist if responsedict["action_name"] toollist
errornote = "Your previous attempt has action_name that is not in the tool list" errornote = "Your previous attempt has action_name that is not in the tool list"
println("\nERROR SQLLLM decisionMaker(). Attempt $attempt/$maxattempt. $errornote --(not qualify response)--> $(responsedict[:action_name]) ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR SQLLLM decisionMaker(). Attempt $attempt/$maxattempt. $errornote --(not qualify response)--> $(responsedict["action_name"]) ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
for i in toollist for i in toollist
if occursin(i, responsedict[:action_input]) if occursin(i, responsedict["action_input"])
errornote = "Your previous attempt has action_name in action_input which is not allowed" errornote = "Your previous attempt has action_name in action_input which is not allowed"
println("\nERROR SQLLLM decisionMaker(). Attempt $attempt/$maxattempt. $errornote --(not qualify response)--> $(responsedict[:action_input]) ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR SQLLLM decisionMaker(). Attempt $attempt/$maxattempt. $errornote --(not qualify response)--> $(responsedict["action_input"]) ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
end end
@@ -303,11 +303,11 @@ function decisionMaker(state::T1, additionalinfo, text2textInstructLLM::Function
pprintln(Dict(responsedict)) pprintln(Dict(responsedict))
# store for later training # store for later training
responsedict[:thoughthistory] = state[:thoughtHistory] responsedict["thoughthistory"] = state["thoughtHistory"]
responsedict[:system] = systemmsg responsedict["system"] = systemmsg
responsedict[:prompt] = prompt responsedict["prompt"] = prompt
responsedict[:context] = context responsedict["context"] = context
responsedict[:think] = think responsedict["think"] = think
# # read sessionId # # read sessionId
# sessionid = JSON3.read("/appfolder/app/sessionid.json") # sessionid = JSON3.read("/appfolder/app/sessionid.json")
@@ -339,7 +339,7 @@ end
# function decisionMaker(state::T1, context, text2textInstructLLM::Function, llmFormatName::String # function decisionMaker(state::T1, context, text2textInstructLLM::Function, llmFormatName::String
# ; querySQLVectorDBF::Union{T2, Nothing}=nothing, maxattempt=10 # ; querySQLVectorDBF::Union{T2, Nothing}=nothing, maxattempt=10
# )::Dict{Symbol, Any} where {T1<:AbstractDict, T2<:Function} # )::Dict{String, Any} where {T1<:AbstractDict, T2<:Function}
# # lessonDict = # # lessonDict =
# # if isfile("lesson.json") # # if isfile("lesson.json")
@@ -523,7 +523,7 @@ end
# end # end
# responsedict = GeneralUtils.textToDict(response, header; # responsedict = GeneralUtils.textToDict(response, header;
# dictKey=dictkey, symbolkey=true) # dictKey=dictkey, symbolkey=false)
# delete!(responsedict, :observation) # delete!(responsedict, :observation)
@@ -653,7 +653,7 @@ function evaluator(state::T1, thoughtDict, text2textInstructLLM::Function, llmFo
dictkey = ["trajectory_evaluation", "answer_evaluation", "accepted_as_answer", "score", "suggestion"] dictkey = ["trajectory_evaluation", "answer_evaluation", "accepted_as_answer", "score", "suggestion"]
thoughthistory = "" thoughthistory = ""
for (k, v) in state[:thoughtHistory] for (k, v) in state["thoughtHistory"]
thoughthistory *= "$k: $v\n" thoughthistory *= "$k: $v\n"
end end
@@ -707,39 +707,39 @@ function evaluator(state::T1, thoughtDict, text2textInstructLLM::Function, llmFo
end end
responsedict = GeneralUtils.textToDict(response, header; responsedict = GeneralUtils.textToDict(response, header;
dictKey=dictkey, symbolkey=true) dictKey=dictkey, symbolkey=false)
responsedict[:score] = responsedict[:score][1] # some time "6\nThe trajectories are incomplete" is generated but I only need the number. responsedict["score"] = responsedict["score"][1] # some time "6\nThe trajectories are incomplete" is generated but I only need the number.
try try
responsedict[:score] = parse(Int, responsedict[:score]) # convert string "5" into integer 5 responsedict["score"] = parse(Int, responsedict["score"]) # convert string "5" into integer 5
catch catch
errornote = "Your previous attempt's score has wrong format" errornote = "Your previous attempt's score has wrong format"
println("\nERROR SQLLLM evaluator() Attempt $attempt/$maxattempt. $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR SQLLLM evaluator() Attempt $attempt/$maxattempt. $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
accepted_as_answer::AbstractString = responsedict[:accepted_as_answer] accepted_as_answer::AbstractString = responsedict["accepted_as_answer"]
if accepted_as_answer ["yes", "no"] if accepted_as_answer ["yes", "no"]
errornote = "Your previous attempt's accepted_as_answer has wrong format" errornote = "Your previous attempt's accepted_as_answer has wrong format"
println("\nERROR SQLLLM evaluator() Attempt $attempt/$maxattempt. $errornote --(not qualify response)--> $(responsedict[:accepted_as_answer]) ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR SQLLLM evaluator() Attempt $attempt/$maxattempt. $errornote --(not qualify response)--> $(responsedict["accepted_as_answer"]) ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
# add to state here instead to in transition() because the latter causes julia extension crash (a bug in julia extension) # add to state here instead to in transition() because the latter causes julia extension crash (a bug in julia extension)
state[:evaluation] = "$(responsedict[:trajectory_evaluation]) $(responsedict[:answer_evaluation])" state["evaluation"] = "$(responsedict["trajectory_evaluation"]) $(responsedict["answer_evaluation"])"
state[:evaluationscore] = responsedict[:score] state["evaluationscore"] = responsedict["score"]
state[:accepted_as_answer] = responsedict[:accepted_as_answer] state["accepted_as_answer"] = responsedict["accepted_as_answer"]
state[:suggestion] = responsedict[:suggestion] state["suggestion"] = responsedict["suggestion"]
# mark as terminal state when the answer is achieved # mark as terminal state when the answer is achieved
if accepted_as_answer ["Yes", "yes"] if accepted_as_answer ["Yes", "yes"]
# mark the state as terminal state because the evaluation say so. # mark the state as terminal state because the evaluation say so.
state[:isterminal] = true state["isterminal"] = true
# evaluation score as reward because different answers hold different value for the user. # evaluation score as reward because different answers hold different value for the user.
state[:reward] = responsedict[:score] state["reward"] = responsedict["score"]
end end
println("\nSQLLLM evaluator() ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nSQLLLM evaluator() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
@@ -776,7 +776,7 @@ function evaluator(state::T1, thoughtDict, text2textInstructLLM::Function, llmFo
# end # end
# end # end
return responsedict[:score] return responsedict["score"]
end end
error("Evaluator failed to generate an evaluation, Response: \n$response\n<|End of error|>") error("Evaluator failed to generate an evaluation, Response: \n$response\n<|End of error|>")
end end
@@ -952,14 +952,14 @@ end
```jldoctest ```jldoctest
julia> using SQLLLM, DataStructures julia> using SQLLLM, DataStructures
julia> state = Dict( julia> state = Dict(
:isterminal => false, "isterminal" => false,
:lesson => nothing, "lesson" => nothing,
:reward => 0, "reward" => 0,
:evaluation => "None", "evaluation" => "None",
:accepted_as_answer => "No", "accepted_as_answer" => "No",
:thoughtHistory => OrderedDict{Symbol, Any}(:question => "How many wines do you have that can be paired with lamb?"), "thoughtHistory" => OrderedDict{String, Any}("question" => "How many wines do you have that can be paired with lamb?"),
:evaluationscore => 0, "evaluationscore" => 0,
:suggestion => "None" "suggestion" => "None"
) )
``` ```
@@ -987,18 +987,18 @@ function transition(state::T, args::NamedTuple
# map action and input() to llm function # map action and input() to llm function
response = response =
if thoughtDict[:action_name] == "listalltables" if thoughtDict["action_name"] == "listalltables"
# deepcopy(state[:virtualCustomerChatHistory]) because I want to keep it clean # deepcopy(state["virtualCustomerChatHistory"]) because I want to keep it clean
# so that other simulation start from this same node is not contaminated with actioninput # so that other simulation start from this same node is not contaminated with actioninput
listAllTable_json(executeSQL) listAllTable_json(executeSQL)
elseif thoughtDict[:action_name] == "TABLEINFO" elseif thoughtDict["action_name"] == "TABLEINFO"
input = thoughtDict[:action_input] input = thoughtDict["action_input"]
tableinfo(executeSQL, input) tableinfo(executeSQL, input)
elseif thoughtDict[:action_name] == "RUNSQL" elseif thoughtDict[:action_name] == "RUNSQL"
response = SQLexecution(executeSQL, thoughtDict[:action_input]) response = SQLexecution(executeSQL, thoughtDict["action_input"])
if response[:success] if response["success"]
extracted = extractContent_dataframe(response[:result], text2textInstructLLM, extracted = extractContent_dataframe(response["result"], text2textInstructLLM,
thoughtDict[:action_input], llmFormatName) thoughtDict["action_input"], llmFormatName)
(rawresponse=response[:result], result=extracted, errormsg=nothing, success=true) (rawresponse=response[:result], result=extracted, errormsg=nothing, success=true)
else else
(result=nothing, errormsg=response[:errormsg], success=false) (result=nothing, errormsg=response[:errormsg], success=false)
@@ -1007,12 +1007,12 @@ function transition(state::T, args::NamedTuple
error("undefined LLM function. Requesting $(thoughtDict[:action_name])") error("undefined LLM function. Requesting $(thoughtDict[:action_name])")
end end
# this section allow LLM functions above to have different return values. # this section allow LLM functions above to have different return values.
success::Bool = haskey(response, :success) ? response[:success] : false success::Bool = haskey(response, "success") ? response["success"] : false
result = success ? response[:result] : response[:errormsg] result = success ? response["result"] : response["errormsg"]
rawresponse = haskey(response, :rawresponse) ? response[:rawresponse] : nothing rawresponse = haskey(response, "rawresponse") ? response["rawresponse"] : nothing
select = haskey(response, :select) ? response[:select] : nothing select = haskey(response, "select") ? response["select"] : nothing
reward::Integer = haskey(response, :reward) ? response[:reward] : 0 reward::Integer = haskey(response, "reward") ? response["reward"] : 0
isterminal::Bool = haskey(response, :isterminal) ? response[:isterminal] : false isterminal::Bool = haskey(response, "isterminal") ? response["isterminal"] : false
newNodeKey, newstate = makeNewState(state, thoughtDict, rawresponse, JSON3.write(result), select, reward, isterminal) newNodeKey, newstate = makeNewState(state, thoughtDict, rawresponse, JSON3.write(result), select, reward, isterminal)
progressvalue::Integer = evaluatorF(newstate, thoughtDict, text2textInstructLLM, llmFormatName) progressvalue::Integer = evaluatorF(newstate, thoughtDict, text2textInstructLLM, llmFormatName)
@@ -1124,19 +1124,19 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function;
# do MCTS if no data in the database # do MCTS if no data in the database
# add extra context for Evaluator so that it knows the observation is from seaching a database # add extra context for Evaluator so that it knows the observation is from seaching a database
initialstate = Dict{Symbol, Any}( initialstate = Dict{String, Any}(
:reward=> 0, "reward"=> 0,
:isterminal=> false, "isterminal"=> false,
:evaluation=> "None", "evaluation"=> "None",
:evaluationscore=> 0, "evaluationscore"=> 0,
:suggestion=> "None", "suggestion"=> "None",
:accepted_as_answer=> "No", "accepted_as_answer"=> "No",
:lesson=> nothing, "lesson"=> nothing,
# contain question, thought_1, action_1, observation_1, thought_2, ... # contain question, thought_1, action_1, observation_1, thought_2, ...
:thoughtHistory=> OrderedDict{Symbol, Any}( "thoughtHistory"=> OrderedDict{String, Any}(
#[] :recap=>, #[] :recap=>,
:question=> query, "question"=> query,
), ),
) )
# context = Dict( # context = Dict(
@@ -1272,7 +1272,7 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function;
llmFormatName=llmFormatName llmFormatName=llmFormatName
) )
earlystop(state) = state[:reward] >= 8 ? true : false earlystop(state) = state["reward"] >= 8 ? true : false
root, _, resultState, highValueState = root, _, resultState, highValueState =
LLMMCTS.runMCTS(initialstate, transition, transitionargs; LLMMCTS.runMCTS(initialstate, transition, transitionargs;
@@ -1294,22 +1294,22 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function;
resultState = highValueState[selected] resultState = highValueState[selected]
end end
latestKey, latestInd = GeneralUtils.findHighestIndexKey(resultState[:thoughtHistory], "observation") latestKey, latestInd = GeneralUtils.findHighestIndexKey(resultState[:thoughtHistory], "observation")
action_input = Symbol("action_input_$latestInd") # latest sql action_input = "action_input_$latestInd" # latest sql
sql = resultState[:thoughtHistory][action_input] sql = resultState["thoughtHistory"][action_input]
extractedTableContent = resultState[:thoughtHistory][latestKey] extractedTableContent = resultState["thoughtHistory"][latestKey]
# add to vectorDB only if the answer is achieved and the state is terminal # add to vectorDB only if the answer is achieved and the state is terminal
if insertSQLVectorDB !== nothing && resultState[:isterminal] == true && if insertSQLVectorDB !== nothing && resultState["isterminal"] == true &&
resultState[:rawresponse] !== nothing resultState["rawresponse"] !== nothing
insertSQLVectorDB(resultState[:thoughtHistory][:question], sql) insertSQLVectorDB(resultState["thoughtHistory"]["question"], sql)
end end
if extractedTableContent === nothing if extractedTableContent === nothing
println("\nSQLLLM query() return nothing ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nSQLLLM query() return nothing ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
end end
result = (text=extractedTableContent, rawresponse=resultState[:rawresponse]) result = (text=extractedTableContent, rawresponse=resultState["rawresponse"])
return result return result
end end
@@ -1330,32 +1330,32 @@ julia>
""" """
function makeNewState(currentstate::T1, thoughtDict::T4, rawresponse, response::T2, select::Union{T3, Nothing}, function makeNewState(currentstate::T1, thoughtDict::T4, rawresponse, response::T2, select::Union{T3, Nothing},
reward::T3, isterminal::Bool reward::T3, isterminal::Bool
)::NamedTuple{(:newNodeKey, :newstate), Tuple{String, Dict{Symbol, <:Any}}} where {T1<:AbstractDict, T2<:AbstractString, T3<:Number, T4<:AbstractDict} )::NamedTuple{(:newNodeKey, :newstate), Tuple{String, Dict{String, <:Any}}} where {T1<:AbstractDict, T2<:AbstractString, T3<:Number, T4<:AbstractDict}
keys = [:plan, :action_name, :action_input, :observation] keys = [:plan, :action_name, :action_input, :observation]
# latestKeys = [] # latestKeys = []
currentstate_latestKey, currentstate_latestIndice = currentstate_latestKey, currentstate_latestIndice =
GeneralUtils.findHighestIndexKey(currentstate[:thoughtHistory], keys[1]) GeneralUtils.findHighestIndexKey(currentstate["thoughtHistory"], keys[1])
nextindice = currentstate_latestKey !== nothing ? currentstate_latestIndice + 1 : 1 nextindice = currentstate_latestKey !== nothing ? currentstate_latestIndice + 1 : 1
# currentstate_latestKey == :NA ? 1 : currentstate_latestIndice + 1 # currentstate_latestKey == "NA" ? 1 : currentstate_latestIndice + 1
currentstate_latestKey = makekey.(keys, nextindice) currentstate_latestKey = makekey.(keys, nextindice)
# add Thought, action, observation to thoughtHistory # add Thought, action, observation to thoughtHistory
newstate = deepcopy(currentstate) newstate = deepcopy(currentstate)
for (x, y) in zip(keys, currentstate_latestKey) for (x, y) in zip(keys, currentstate_latestKey)
if x != :observation if x != "observation"
newstate[:thoughtHistory][y] = thoughtDict[Symbol(x)] newstate["thoughtHistory"][y] = thoughtDict[x]
else else
newstate[:thoughtHistory][y] = response newstate["thoughtHistory"][y] = response
end end
end end
newstate[:reward] = reward newstate["reward"] = reward
newstate[:select] = select newstate["select"] = select
newstate[:isterminal] = isterminal newstate["isterminal"] = isterminal
newstate[:rawresponse] = rawresponse # whatever return from action newstate["rawresponse"] = rawresponse # whatever return from action
newNodeKey = GeneralUtils.uuid4snakecase() newNodeKey = GeneralUtils.uuid4snakecase()
@@ -1429,8 +1429,8 @@ function generatequestion(state::T1, context, text2textInstructLLM::Function,
dictkey = ["q1"] dictkey = ["q1"]
workprogress = "" workprogress = ""
for (k, v) in state[:thoughtHistory] for (k, v) in state["thoughtHistory"]
if k [:query] if k ["query"]
workprogress *= "$k: $v\n" workprogress *= "$k: $v\n"
end end
end end
@@ -1441,8 +1441,8 @@ function generatequestion(state::T1, context, text2textInstructLLM::Function,
for attempt in 1:maxattempt for attempt in 1:maxattempt
usermsg = usermsg =
""" """
$(context[:tablelist]) $(context["tablelist"])
User query: $(state[:thoughtHistory][:question]) User query: $(state["thoughtHistory"]["question"])
Example: $similarSQL Example: $similarSQL
Your work progress: $workprogress Your work progress: $workprogress
P.S. $errornote P.S. $errornote
@@ -1474,8 +1474,8 @@ function generatequestion(state::T1, context, text2textInstructLLM::Function,
end end
responsedict = GeneralUtils.textToDict(response, header; responsedict = GeneralUtils.textToDict(response, header;
dictKey=dictkey, symbolkey=true) dictKey=dictkey, symbolkey=false)
response = "Q1: " * responsedict[:q1] response = "Q1: " * responsedict["q1"]
println("\nSQLLLM generatequestion() ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nSQLLLM generatequestion() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
pprintln(Dict(responsedict)) pprintln(Dict(responsedict))
return response return response
+49 -49
View File
@@ -287,17 +287,17 @@ function getdata_transition(state::T, args::NamedTuple
# decisionMaker::Function = args[:decisionMaker] # decisionMaker::Function = args[:decisionMaker]
# evaluator::Function = args[:evaluator] # evaluator::Function = args[:evaluator]
# reflector::Function = args[:reflector] # reflector::Function = args[:reflector]
context = args[:context] context = args["context"]
executeSQL::Function = args[:executeSQL] executeSQL::Function = args["executeSQL"]
text2textInstructLLM::Function = args[:text2textInstructLLM] text2textInstructLLM::Function = args["text2textInstructLLM"]
thought, sql = thought, sql =
if state[:code] !== nothing if state["code"] !== nothing
result = getdata_decisionMaker(state, context, text2textInstructLLM) result = getdata_decisionMaker(state, context, text2textInstructLLM)
result[:thought], result[:code] result["thought"], result["code"]
else else
nothing, state[:question] nothing, state["question"]
end end
# make new state # make new state
newNodeKey = GeneralUtils.uuid4snakecase() newNodeKey = GeneralUtils.uuid4snakecase()
@@ -314,15 +314,15 @@ function getdata_transition(state::T, args::NamedTuple
isterminal=false) isterminal=false)
end end
println("getdata_transition() 1 ", @__FILE__, " ", @__LINE__) println("getdata_transition() 1 ", @__FILE__, " ", @__LINE__)
newstate[:code] = sql newstate["code"] = sql
newstate[:response] = response newstate["response"] = response
newstate[:errorexplain] = thought newstate["errorexplain"] = thought
newstate[:errormsg] = errormsg newstate["errormsg"] = errormsg
newstate[:reward] = reward newstate["reward"] = reward
newstate[:isterminal] = isterminal newstate["isterminal"] = isterminal
if response !== nothing if response !== nothing
extracted = extractContent_dataframe(response, context, text2textInstructLLM) extracted = extractContent_dataframe(response, context, text2textInstructLLM)
newstate[:response] = extracted newstate["response"] = extracted
end end
println("getdata_transition() 2 ", @__FILE__, " ", @__LINE__) println("getdata_transition() 2 ", @__FILE__, " ", @__LINE__)
stateevaluation = "None" stateevaluation = "None"
@@ -389,10 +389,10 @@ function getdata_decisionMaker(state::Dict, context::Dict, text2textInstructLLM:
for attempt in 1:10 for attempt in 1:10
usermsg = """ usermsg = """
Context: Context:
$(context[:mentionedTableInfo]) $(context["mentionedTableInfo"])
User intention: $(context[:userintention]) User intention: $(context["userintention"])
Code executed from the last round: $(state[:code]) Code executed from the last round: $(state["code"])
Execution error: $(state[:errormsg]) Execution error: $(state["errormsg"])
$noise $noise
$note_flag $note_flag
""" """
@@ -414,13 +414,13 @@ function getdata_decisionMaker(state::Dict, context::Dict, text2textInstructLLM:
dictkey = ["plan", "code"] dictkey = ["plan", "code"]
responsedict = GeneralUtils.textToDict(response, header; responsedict = GeneralUtils.textToDict(response, header;
dictKey=dictkey, symbolkey=true) dictKey=dictkey, symbolkey=false)
_code = responsedict[:code] _code = responsedict["code"]
code = strip(_code) code = strip(_code)
if length(code) < 2 if length(code) < 2
error("No code available.") error("No code available.")
elseif code == state[:code] elseif code == state["code"]
error("generated code is the same as earlier.") error("generated code is the same as earlier.")
else else
end end
@@ -440,7 +440,7 @@ function getdata_decisionMaker(state::Dict, context::Dict, text2textInstructLLM:
println("\n~~~ getdata_decisionMaker() ", @__FILE__, " ", @__LINE__) println("\n~~~ getdata_decisionMaker() ", @__FILE__, " ", @__LINE__)
pprintln(Dict(responsedict)) pprintln(Dict(responsedict))
return (thought=responsedict[:comprehension], code=code, success=true, errormsg=nothing) return (thought=responsedict["comprehension"], code=code, success=true, errormsg=nothing)
catch e catch e
io = IOBuffer() io = IOBuffer()
showerror(io, e) showerror(io, e)
@@ -651,12 +651,12 @@ function extractContent_dataframe(df::DataFrame, text2textInstructLLM::Function,
end end
responsedict = GeneralUtils.textToDict(response, header; responsedict = GeneralUtils.textToDict(response, header;
dictKey=dictkey, symbolkey=true) dictKey=dictkey, symbolkey=false)
# result = dfstr # result = dfstr
result = result =
""" """
Summary: $(responsedict[:search_summary]) Summary: $(responsedict["search_summary"])
More details: $dfstr More details: $dfstr
""" """
@@ -778,8 +778,8 @@ function getTableNameFromSQL(sql::T, text2textInstructLLM::Function,
response = text2textInstructLLM(prompt, modelsize="medium") response = text2textInstructLLM(prompt, modelsize="medium")
response = GeneralUtils.deFormatLLMtext(response, llmFormatName) response = GeneralUtils.deFormatLLMtext(response, llmFormatName)
responsedict = GeneralUtils.textToDict(response, header; responsedict = GeneralUtils.textToDict(response, header;
dictKey=dictkey, symbolkey=true) dictKey=dictkey, symbolkey=false)
response = copy(JSON3.read(responsedict[:table_name])) response = copy(JSON3.read(responsedict["table_name"]))
return response return response
catch e catch e
@@ -862,21 +862,21 @@ function compareState(question::String, highValueStateList::Vector{T},
Let's begin! Let's begin!
""" """
potentialSolution = [] potentialSolution = []
keys = [:action_input, :observation] keys = ["action_input", "observation"]
# extract the last action_name, action_input, observation of each state in highValueStateList and store them in a dictionary then push into potentialSolution # extract the last action_name, action_input, observation of each state in highValueStateList and store them in a dictionary then push into potentialSolution
for state in highValueStateList for state in highValueStateList
thoughtHistory = state[:thoughtHistory] thoughtHistory = state["thoughtHistory"]
_, currentstate_latestIndice = _, currentstate_latestIndice =
GeneralUtils.findHighestIndexKey(thoughtHistory, keys[1]) GeneralUtils.findHighestIndexKey(thoughtHistory, keys[1])
latestKeys = makekey.(keys, currentstate_latestIndice) latestKeys = makekey.(keys, currentstate_latestIndice)
d = Dict() d = Dict()
# get the last action_name, action_input, observation of currentstate # get the last action_name, action_input, observation of currentstate
for (i,v) in enumerate(keys) for (i,v) in enumerate(keys)
d[v] = thoughtHistory[latestKeys[i]] d[v] = thoughtHistory[latestKeys[i]]
end end
push!(potentialSolution, d) push!(potentialSolution, d)
end end
""" """
# put potential solutions from potentialSolution into the following form # put potential solutions from potentialSolution into the following form
@@ -944,11 +944,11 @@ function compareState(question::String, highValueStateList::Vector{T},
continue continue
end end
responsedict = GeneralUtils.textToDict(response, header; dictKey=dictkey, symbolkey=true) responsedict = GeneralUtils.textToDict(response, header; dictKey=dictkey, symbolkey=false)
responsedict[:selected_response_number] = responsedict[:selected_response_number][1] # some time "6\nThe trajectories are incomplete" is generated but I only need the number. responsedict["selected_response_number"] = responsedict["selected_response_number"][1] # some time "6\nThe trajectories are incomplete" is generated but I only need the number.
try try
responsedict[:selected_response_number] = parse(Int, responsedict[:selected_response_number]) # convert string "5" into integer 5 responsedict["selected_response_number"] = parse(Int, responsedict["selected_response_number"]) # convert string "5" into integer 5
catch catch
errornote = "In your previous attempt, Selected_response_number was not a number. It must be a number." errornote = "In your previous attempt, Selected_response_number was not a number. It must be a number."
println("\nERROR SQLLLM compareState() Attempt $attempt. $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR SQLLLM compareState() Attempt $attempt. $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
@@ -958,7 +958,7 @@ function compareState(question::String, highValueStateList::Vector{T},
println("\n~~~ compareState() ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\n~~~ compareState() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
pprintln(Dict(responsedict)) pprintln(Dict(responsedict))
return responsedict[:selected_response_number] return responsedict["selected_response_number"]
end end
error("compareState() failed to generate an evaluation, Response: \n$response\n<|End of error|>", @__FILE__, ":", @__LINE__, " $(Dates.now())") error("compareState() failed to generate an evaluation, Response: \n$response\n<|End of error|>", @__FILE__, ":", @__LINE__, " $(Dates.now())")
end end
+1 -1
View File
@@ -2,7 +2,7 @@ module util
export makekey export makekey
makekey(key, indice) = Symbol("$(key)_$indice") makekey(key, indice) = "$(key)_$indice"
+1 -1
View File
@@ -353,7 +353,7 @@ SELECT * FROM wine WHERE wine_type = 'red' AND country = 'France' AND sweetness
# "The user's question is to search the database for wines that have a type of \"white\", are from \"France\", and have a sweetness level of 1. The thought is correct in identifying the conditions needed to filter the wine table. The action taken is to execute a SQL query to retrieve the desired data, which is also correct. The observation provides a search summary and two search results that match the user's question. Each result includes details about the wine such as ID, name, brand, manufacturer, region, country, type, grape variety, serving temperature, intensity, sweetness, tannin, and acidity.", # "The user's question is to search the database for wines that have a type of \"white\", are from \"France\", and have a sweetness level of 1. The thought is correct in identifying the conditions needed to filter the wine table. The action taken is to execute a SQL query to retrieve the desired data, which is also correct. The observation provides a search summary and two search results that match the user's question. Each result includes details about the wine such as ID, name, brand, manufacturer, region, country, type, grape variety, serving temperature, intensity, sweetness, tannin, and acidity.",
# :accepted_as_answer => "Yes", # :accepted_as_answer => "Yes",
# :thoughtHistory => # :thoughtHistory =>
# OrderedDict{Symbol, Any}(:question => "Search the database for wine_type: white, country: France, sweetness: 1", :thought_1 => "The user wants to search the database for wines that have a type of \"white\", are from \"France\", and have a sweetness level of 1. To achieve this, we need to filter the wine table based on these conditions.", :action_name_1 => "GETDATA", :action_input_1 => "SELECT * FROM wine WHERE wine.wine_type = 'white' AND wine.country = 'France' AND wine.sweetness = 1;", :observation_1 => "\"Search summary: The resulting table represents wines.\\nSearch result: 1) wine_id: 5b6b6df9-d87c-4f33-8995-7249c2ecc917, wine_name: corton-charlemagne grand cru, brand: domaine des croix, manufacturer: domaine des croix, region: bourgogne, country: France, wine_type: white, grape_variety: cote de beaune blanc, serving_temperature: 11 to 13 Celsius, intensity: 4, sweetness: 1, tannin: missing, acidity: 3, fizziness: missing\\n2) wine_id: 1ad27d16-ef64-4907-acf1-40631630c143, wine_name: puligny-montrachet 1er cru 'les demoiselles', brand: amiot guy, manufacturer: amiot guy, region: bourgogne, country: France, wine_type: white, grape_variety: cote de beaune blanc, serving_temperature: 11 to 13 Celsius, intensity: 4, sweetness: 1, tannin: missing, acidity: 3, fizziness: missing\\n\\n\""), # OrderedDict{String, Any}("question" => "Search the database for wine_type: white, country: France, sweetness: 1", "thought_1" => "The user wants to search the database for wines that have a type of \"white\", are from \"France\", and have a sweetness level of 1. To achieve this, we need to filter the wine table based on these conditions.", "action_name_1" => "GETDATA", "action_input_1" => "SELECT * FROM wine WHERE wine.wine_type = 'white' AND wine.country = 'France' AND wine.sweetness = 1;", "observation_1" => "\"Search summary: The resulting table represents wines.\\nSearch result: 1) wine_id: 5b6b6df9-d87c-4f33-8995-7249c2ecc917, wine_name: corton-charlemagne grand cru, brand: domaine des croix, manufacturer: domaine des croix, region: bourgogne, country: France, wine_type: white, grape_variety: cote de beaune blanc, serving_temperature: 11 to 13 Celsius, intensity: 4, sweetness: 1, tannin: missing, acidity: 3, fizziness: missing\\n2) wine_id: 1ad27d16-ef64-4907-acf1-40631630c143, wine_name: puligny-montrachet 1er cru 'les demoiselles', brand: amiot guy, manufacturer: amiot guy, region: bourgogne, country: France, wine_type: white, grape_variety: cote de beaune blanc, serving_temperature: 11 to 13 Celsius, intensity: 4, sweetness: 1, tannin: missing, acidity: 3, fizziness: missing\\n\\n\""),
# :evaluationscore => 9, # :evaluationscore => 9,
# :select => nothing, # :select => nothing,
# :suggestion => "None") # :suggestion => "None")