use string key for dict
This commit is contained in:
+101
-101
@@ -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
|
||||||
@@ -185,8 +185,8 @@ function decisionMaker(state::T1, additionalinfo, text2textInstructLLM::Function
|
|||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|||||||
+31
-31
@@ -287,16 +287,16 @@ 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
|
||||||
@@ -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
|
||||||
@@ -863,10 +863,10 @@ function compareState(question::String, highValueStateList::Vector{T},
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
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)
|
||||||
@@ -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
@@ -2,7 +2,7 @@ module util
|
|||||||
|
|
||||||
export makekey
|
export makekey
|
||||||
|
|
||||||
makekey(key, indice) = Symbol("$(key)_$indice")
|
makekey(key, indice) = "$(key)_$indice"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user