update
This commit is contained in:
@@ -250,8 +250,7 @@ function decisionMaker(state::T1, context, text2textInstructLLM::Function,
|
||||
error("DecisionMaker has more than one key per categories")
|
||||
end
|
||||
end
|
||||
println("--> SQLLLM decisionMaker() ", @__FILE__, " ", @__LINE__)
|
||||
pprintln(Dict(responsedict))
|
||||
|
||||
return responsedict
|
||||
catch e
|
||||
io = IOBuffer()
|
||||
@@ -499,7 +498,7 @@ julia>
|
||||
function evaluator(state::T1, text2textInstructLLM::Function;
|
||||
addSQLVectorDB::Union{Function, Nothing}=nothing
|
||||
) where {T1<:AbstractDict}
|
||||
println("Evaluating state", @__FILE__, " ", @__LINE__)
|
||||
|
||||
# systemmsg =
|
||||
# """
|
||||
# You are a helpful assistant that analyzes agent's trajectories to find solutions and observations (i.e., the results of actions) to answer the user's questions.
|
||||
@@ -737,14 +736,16 @@ function evaluator(state::T1, text2textInstructLLM::Function;
|
||||
# mark as terminal state when the answer is achieved
|
||||
if accepted_as_answer == "Yes"
|
||||
state[:isterminal] = true
|
||||
state[:reward] = 1
|
||||
|
||||
# user score as reward because different answers hold different value for the user.
|
||||
state[:reward] = responsedict[:score]
|
||||
|
||||
#add to vectorDB
|
||||
if addSQLVectorDB !== nothing
|
||||
addSQLVectorDB(state)
|
||||
end
|
||||
end
|
||||
println("--> 5 Evaluator ", @__FILE__, " ", @__LINE__)
|
||||
println("~~~ 5 Evaluator() ", @__FILE__, " ", @__LINE__)
|
||||
pprintln(Dict(responsedict))
|
||||
|
||||
return responsedict[:score]
|
||||
@@ -953,7 +954,7 @@ julia> state = Dict(
|
||||
|
||||
# TODO
|
||||
- [] add embedding of newstate and store in newstate[:embedding]
|
||||
|
||||
- [WORKING] should getdata() return isterminal?
|
||||
# Signature
|
||||
"""
|
||||
function transition(state::T, args::NamedTuple
|
||||
@@ -992,17 +993,13 @@ function transition(state::T, args::NamedTuple
|
||||
else
|
||||
error("undefined LLM function. Requesting $actionname")
|
||||
end
|
||||
|
||||
# this section allow LLM functions above to have different return values.
|
||||
result = haskey(response, :result) ? response[:result] : nothing
|
||||
success::Bool = haskey(response, :success) ? response[:success] : false
|
||||
result = success ? response[:result] : response[:errormsg]
|
||||
select = haskey(response, :select) ? response[:select] : nothing
|
||||
reward::Integer = haskey(response, :reward) ? response[:reward] : 0
|
||||
isterminal::Bool = haskey(response, :isterminal) ? response[:isterminal] : false
|
||||
errormsg::Union{AbstractString, Nothing} = haskey(response, :errormsg) ? response[:errormsg] : nothing
|
||||
success::Bool = haskey(response, :success) ? response[:success] : false
|
||||
|
||||
newNodeKey, newstate = makeNewState(state, thoughtDict, JSON3.write(result), select, reward, isterminal)
|
||||
println("SQLLLM transition() 1 ", @__FILE__, " ", @__LINE__)
|
||||
progressvalue::Integer = evaluatorF(newstate, text2textInstructLLM;
|
||||
addSQLVectorDB=addSQLVectorDBF)
|
||||
|
||||
@@ -1090,14 +1087,14 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function;
|
||||
addSQLVectorDB::Union{Function, Nothing}=nothing,
|
||||
querySQLVectorDB::Union{Function, Nothing}=nothing
|
||||
)::String where {T<:AbstractString}
|
||||
#[WORKING] 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
|
||||
query = "Search the database for {$query}"
|
||||
initialstate = Dict{Symbol, Any}(
|
||||
:reward=> 0,
|
||||
:isterminal=> false,
|
||||
:evaluation=> "None",
|
||||
:suggestion=> "None",
|
||||
:evaluationscore=> 0,
|
||||
:suggestion=> "None",
|
||||
:accepted_as_answer=> "No",
|
||||
:lesson=> nothing,
|
||||
|
||||
@@ -1121,10 +1118,13 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function;
|
||||
addSQLVectorDB=addSQLVectorDB,
|
||||
)
|
||||
|
||||
_, result = LLMMCTS.runMCTS(initialstate, transition, transitionargs;
|
||||
totalsample=1, maxdepth=3, maxiterations=1, explorationweight=1.0)
|
||||
latestKey, _ = GeneralUtils.findHighestIndexKey(result[:thoughtHistory], "observation")
|
||||
resulttext = result[:thoughtHistory][latestKey]
|
||||
earlystop(state) = state[:reward] >= 8 ? true : false
|
||||
|
||||
_, resultState = LLMMCTS.runMCTS(initialstate, transition, transitionargs;
|
||||
totalsample=1, maxdepth=3, maxiterations=3, explorationweight=1.0,
|
||||
earlystop=earlystop)
|
||||
latestKey, _ = GeneralUtils.findHighestIndexKey(resultState[:thoughtHistory], "observation")
|
||||
resulttext = resultState[:thoughtHistory][latestKey]
|
||||
|
||||
return resulttext
|
||||
end
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
module llmfunction
|
||||
|
||||
export listAllTable_json, listAllTable_str, tableinfo, getdata, finalAnswerBox,
|
||||
getTableNameFromSQL, extractContent_dataframe, SQLexecution
|
||||
export listAllTable_json, listAllTable_str, tableinfo, getdata, finalAnswerBox,
|
||||
getTableNameFromSQL, extractContent_dataframe, SQLexecution
|
||||
|
||||
using HTTP, JSON3, URIs, Random, PrettyPrinting, UUIDs, LibPQ, Tables, DataFrames, CSV,
|
||||
DataStructures, StatsBase
|
||||
using HTTP, JSON3, URIs, Random, PrettyPrinting, UUIDs, LibPQ, Tables, DataFrames, CSV,
|
||||
DataStructures, StatsBase
|
||||
using GeneralUtils, LLMMCTS
|
||||
using ..util
|
||||
|
||||
@@ -36,25 +36,24 @@ julia> result = response[:result]
|
||||
# Signature
|
||||
"""
|
||||
function listAllTable_json(executeSQL::Function
|
||||
)::NamedTuple{(:result, :success), Tuple{DataFrame, Bool}}
|
||||
)::NamedTuple{(:result, :success),Tuple{DataFrame,Bool}}
|
||||
|
||||
sql =
|
||||
"""
|
||||
SELECT
|
||||
table_name,
|
||||
obj_description(relfilenode, 'pg_class') AS table_comment,
|
||||
string_agg(column_name || ' (' || data_type || ')', ', ') AS columns
|
||||
FROM
|
||||
information_schema.columns
|
||||
JOIN
|
||||
pg_class ON table_name = relname
|
||||
WHERE
|
||||
table_schema = 'public'
|
||||
GROUP BY
|
||||
table_name, relfilenode
|
||||
ORDER BY
|
||||
table_name;
|
||||
"""
|
||||
sql = """
|
||||
SELECT
|
||||
table_name,
|
||||
obj_description(relfilenode, 'pg_class') AS table_comment,
|
||||
string_agg(column_name || ' (' || data_type || ')', ', ') AS columns
|
||||
FROM
|
||||
information_schema.columns
|
||||
JOIN
|
||||
pg_class ON table_name = relname
|
||||
WHERE
|
||||
table_schema = 'public'
|
||||
GROUP BY
|
||||
table_name, relfilenode
|
||||
ORDER BY
|
||||
table_name;
|
||||
"""
|
||||
|
||||
result = executeSQL(sql)
|
||||
df = DataFrame(result)
|
||||
@@ -65,24 +64,23 @@ end
|
||||
|
||||
|
||||
function listAllTable_str(executeSQL::Function
|
||||
)::NamedTuple{(:result, :success), Tuple{String, Bool}}
|
||||
sql =
|
||||
"""
|
||||
SELECT
|
||||
table_name,
|
||||
obj_description(relfilenode, 'pg_class') AS table_comment,
|
||||
string_agg(column_name || ' (' || data_type || ')', ', ') AS columns
|
||||
FROM
|
||||
information_schema.columns
|
||||
JOIN
|
||||
pg_class ON table_name = relname
|
||||
WHERE
|
||||
table_schema = 'public'
|
||||
GROUP BY
|
||||
table_name, relfilenode
|
||||
ORDER BY
|
||||
table_name;
|
||||
"""
|
||||
)::NamedTuple{(:result, :success),Tuple{String,Bool}}
|
||||
sql = """
|
||||
SELECT
|
||||
table_name,
|
||||
obj_description(relfilenode, 'pg_class') AS table_comment,
|
||||
string_agg(column_name || ' (' || data_type || ')', ', ') AS columns
|
||||
FROM
|
||||
information_schema.columns
|
||||
JOIN
|
||||
pg_class ON table_name = relname
|
||||
WHERE
|
||||
table_schema = 'public'
|
||||
GROUP BY
|
||||
table_name, relfilenode
|
||||
ORDER BY
|
||||
table_name;
|
||||
"""
|
||||
result = executeSQL(sql)
|
||||
df = DataFrame(result)
|
||||
tableinfo = "Here are a list of available tables in the database (each row is in this format: table name; table comment; table columns): \n"
|
||||
@@ -109,20 +107,19 @@ end
|
||||
"""
|
||||
|
||||
|
||||
function tableinfo_str(executeSQL::Function, tablename::String)::NamedTuple{(:result, :success), Tuple{String, Bool}}
|
||||
|
||||
sql =
|
||||
"""
|
||||
SELECT
|
||||
column_name,
|
||||
data_type,
|
||||
col_description(format('%s.%s', table_schema, table_name)::regclass::oid, ordinal_position) AS column_comment
|
||||
FROM
|
||||
information_schema.columns
|
||||
WHERE
|
||||
table_name = '$tablename'
|
||||
AND table_schema = 'public';
|
||||
"""
|
||||
function tableinfo_str(executeSQL::Function, tablename::String)::NamedTuple{(:result, :success),Tuple{String,Bool}}
|
||||
|
||||
sql = """
|
||||
SELECT
|
||||
column_name,
|
||||
data_type,
|
||||
col_description(format('%s.%s', table_schema, table_name)::regclass::oid, ordinal_position) AS column_comment
|
||||
FROM
|
||||
information_schema.columns
|
||||
WHERE
|
||||
table_name = '$tablename'
|
||||
AND table_schema = 'public';
|
||||
"""
|
||||
|
||||
result = executeSQL(sql)
|
||||
df = DataFrame(result)
|
||||
@@ -167,18 +164,17 @@ julia> result = response[:result]
|
||||
# Signature
|
||||
"""
|
||||
function tableinfo(executeSQL::Function, tablenames::T
|
||||
)::NamedTuple{(:result,), Tuple{String}} where {T<:AbstractVector}
|
||||
)::NamedTuple{(:result,),Tuple{String}} where {T<:AbstractVector}
|
||||
# list all tables in a database
|
||||
sql =
|
||||
"""
|
||||
SELECT pg_namespace.nspname AS schema_name,
|
||||
relname AS table_name,
|
||||
pg_catalog.obj_description(pg_class.oid) AS comment
|
||||
FROM pg_class
|
||||
INNER JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
|
||||
WHERE pg_namespace.nspname = 'public' -- Replace 'public' with your desired schema
|
||||
AND pg_class.relkind IN ('r', 't');
|
||||
"""
|
||||
sql = """
|
||||
SELECT pg_namespace.nspname AS schema_name,
|
||||
relname AS table_name,
|
||||
pg_catalog.obj_description(pg_class.oid) AS comment
|
||||
FROM pg_class
|
||||
INNER JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
|
||||
WHERE pg_namespace.nspname = 'public' -- Replace 'public' with your desired schema
|
||||
AND pg_class.relkind IN ('r', 't');
|
||||
"""
|
||||
|
||||
_result = executeSQL(sql)
|
||||
df = DataFrame(_result)
|
||||
@@ -193,8 +189,7 @@ function tableinfo(executeSQL::Function, tablenames::T
|
||||
end
|
||||
end
|
||||
if !isempty(notExistingTable)
|
||||
result =
|
||||
"Error, the following tables does not exist in the database: $(JSON3.write(notExistingTable))"
|
||||
result = "Error, the following tables does not exist in the database: $(JSON3.write(notExistingTable))"
|
||||
return (result=result,)
|
||||
end
|
||||
|
||||
@@ -221,53 +216,70 @@ end
|
||||
A connection object connected to the database
|
||||
- `text2textInstructLLM::Function`
|
||||
A function that handles communication to LLM service.
|
||||
|
||||
|
||||
# Return
|
||||
- `NamedTuple{(:result, :errormsg, success), Tuple{String, String, Bool}}`
|
||||
|
||||
# TODO
|
||||
- [x] getdata directly using sql execute
|
||||
|
||||
# Signature
|
||||
"""
|
||||
function getdata(query::T, context::Union{Dict, Nothing}, executeSQL::Function,
|
||||
function getdata(query::T, context::Union{Dict,Nothing}, executeSQL::Function,
|
||||
text2textInstructLLM::Function;
|
||||
)::NamedTuple{(:result, :errormsg, :success), Tuple{String, Union{String, Nothing}, Bool}} where {T<:AbstractString}
|
||||
) where {T<:AbstractString}
|
||||
|
||||
# get table info here because it'll be called only 1-time. If this function is in
|
||||
# getdata_decisionMaker(), it'll be called everytime
|
||||
mentionedtable = getTableNameFromSQL(query, text2textInstructLLM)
|
||||
mentionedTableInfo = tableinfo(executeSQL, mentionedtable)[:result]
|
||||
context[:mentionedTableInfo] = mentionedTableInfo
|
||||
|
||||
initialstate = Dict{Symbol, Any}(
|
||||
:reward=> 0,
|
||||
:isterminal=> false,
|
||||
:evaluation=> nothing,
|
||||
:errormsg=> nothing,
|
||||
:errorexplain=> nothing,
|
||||
|
||||
:question=> query,
|
||||
:code=> nothing,
|
||||
:response=> nothing,
|
||||
)
|
||||
|
||||
transitionargs = (
|
||||
# decisionMaker=getdata_decisionMaker,
|
||||
# evaluator=getdata_evaluator,
|
||||
# reflector=getdata_reflector,
|
||||
context=context,
|
||||
executeSQL=executeSQL,
|
||||
text2textInstructLLM=text2textInstructLLM
|
||||
)
|
||||
result_1, result_2 = LLMMCTS.runMCTS(initialstate, getdata_transition, transitionargs;
|
||||
totalsample=1, maxdepth=3, maxiterations=1, explorationweight=1.0)
|
||||
|
||||
if result_2[:isterminal] == true
|
||||
return (result=result_2[:response], errormsg=nothing, success=true) # succues=true to finish getdata()
|
||||
response = SQLexecution(executeSQL, query)
|
||||
if response[:success]
|
||||
extracted = extractContent_dataframe(response[:result], context, text2textInstructLLM)
|
||||
response_ = (result=extracted, errormsg=nothing, success=true)
|
||||
return response_
|
||||
else
|
||||
# return (response="Failed to act with the following error message: $(result_2[:errorexplain])", select=nothing, reward=0, success=false)
|
||||
return (result="Failed to get the data. $(result_1[:errormsg])",
|
||||
errormsg=result_1[:errormsg], success=false)
|
||||
response_ = (result=nothing, errormsg=response[:errormsg], success=false)
|
||||
return response_
|
||||
end
|
||||
end
|
||||
# function getdata(query::T, context::Union{Dict, Nothing}, executeSQL::Function,
|
||||
# text2textInstructLLM::Function;
|
||||
# )::NamedTuple{(:result, :errormsg, :success), Tuple{String, Union{String, Nothing}, Bool}} where {T<:AbstractString}
|
||||
|
||||
# # get table info here because it'll be called only 1-time. If this function is in
|
||||
# # getdata_decisionMaker(), it'll be called everytime
|
||||
# mentionedtable = getTableNameFromSQL(query, text2textInstructLLM)
|
||||
# mentionedTableInfo = tableinfo(executeSQL, mentionedtable)[:result]
|
||||
# context[:mentionedTableInfo] = mentionedTableInfo
|
||||
|
||||
# initialstate = Dict{Symbol, Any}(
|
||||
# :reward=> 0,
|
||||
# :isterminal=> false,
|
||||
# :evaluation=> nothing,
|
||||
# :errormsg=> nothing,
|
||||
# :errorexplain=> nothing,
|
||||
|
||||
# :question=> query,
|
||||
# :code=> nothing,
|
||||
# :response=> nothing,
|
||||
# )
|
||||
|
||||
# transitionargs = (
|
||||
# # decisionMaker=getdata_decisionMaker,
|
||||
# # evaluator=getdata_evaluator,
|
||||
# # reflector=getdata_reflector,
|
||||
# context=context,
|
||||
# executeSQL=executeSQL,
|
||||
# text2textInstructLLM=text2textInstructLLM
|
||||
# )
|
||||
# result_1, result_2 = LLMMCTS.runMCTS(initialstate, getdata_transition, transitionargs;
|
||||
# totalsample=1, maxdepth=3, maxiterations=1, explorationweight=1.0)
|
||||
|
||||
# if result_2[:isterminal] == true
|
||||
# return (result=result_2[:response], errormsg=nothing, success=true) # succues=true to finish getdata()
|
||||
# else
|
||||
# # return (response="Failed to act with the following error message: $(result_2[:errorexplain])", select=nothing, reward=0, success=false)
|
||||
# return (result="Failed to get the data. $(result_1[:errormsg])",
|
||||
# errormsg=result_1[:errormsg], success=false)
|
||||
# end
|
||||
# end
|
||||
|
||||
|
||||
"""
|
||||
@@ -290,7 +302,7 @@ julia>
|
||||
# Signature
|
||||
"""
|
||||
function getdata_evaluator(newstate, config)
|
||||
|
||||
|
||||
return (evaluation="None", score=0)
|
||||
end
|
||||
|
||||
@@ -309,9 +321,9 @@ end
|
||||
# Signature
|
||||
"""
|
||||
function getdata_transition(state::T, args::NamedTuple
|
||||
)::NamedTuple{(:newNodeKey, :newstate, :progressvalue), Tuple{String, T, Integer}} where {T<:AbstractDict}
|
||||
)::NamedTuple{(:newNodeKey, :newstate, :progressvalue),Tuple{String,T,Integer}} where {T<:AbstractDict}
|
||||
|
||||
|
||||
|
||||
# decisionMaker::Function = args[:decisionMaker]
|
||||
# evaluator::Function = args[:evaluator]
|
||||
# reflector::Function = args[:reflector]
|
||||
@@ -320,26 +332,26 @@ function getdata_transition(state::T, args::NamedTuple
|
||||
text2textInstructLLM::Function = args[:text2textInstructLLM]
|
||||
|
||||
thought, sql =
|
||||
if state[:code] !== nothing
|
||||
result = getdata_decisionMaker(state, context, text2textInstructLLM)
|
||||
result[:thought], result[:code]
|
||||
else
|
||||
nothing, state[:question]
|
||||
end
|
||||
if state[:code] !== nothing
|
||||
result = getdata_decisionMaker(state, context, text2textInstructLLM)
|
||||
result[:thought], result[:code]
|
||||
else
|
||||
nothing, state[:question]
|
||||
end
|
||||
|
||||
# make new state
|
||||
newNodeKey = GeneralUtils.uuid4snakecase()
|
||||
newstate = deepcopy(state)
|
||||
|
||||
|
||||
response, success, errormsg, reward, isterminal =
|
||||
if sql !== nothing
|
||||
response, success, errormsg, reward, isterminal = SQLexecution(executeSQL, sql)
|
||||
else
|
||||
(result= nothing,
|
||||
success= false,
|
||||
errormsg= "SQL execution failed. An unexpected error occurred. Please try again.",
|
||||
reward=0,
|
||||
isterminal=false)
|
||||
(result=nothing,
|
||||
success=false,
|
||||
errormsg="SQL execution failed. An unexpected error occurred. Please try again.",
|
||||
reward=0,
|
||||
isterminal=false)
|
||||
end
|
||||
println("getdata_transition() 1 ", @__FILE__, " ", @__LINE__)
|
||||
newstate[:code] = sql
|
||||
@@ -376,10 +388,10 @@ end
|
||||
# Signature
|
||||
"""
|
||||
function getdata_decisionMaker(state::Dict, context::Dict, text2textInstructLLM::Function
|
||||
)::NamedTuple{(:thought, :code, :success, :errormsg), Tuple{Union{String, Nothing}, Union{String, Nothing}, Bool, Union{String, Nothing}}}
|
||||
)::NamedTuple{(:thought, :code, :success, :errormsg),Tuple{Union{String,Nothing},Union{String,Nothing},Bool,Union{String,Nothing}}}
|
||||
|
||||
Hints = "None"
|
||||
|
||||
|
||||
# """
|
||||
# Here are some useful SQL programs:
|
||||
# $usefulSQL
|
||||
@@ -414,78 +426,75 @@ function getdata_decisionMaker(state::Dict, context::Dict, text2textInstructLLM:
|
||||
# 2) ...
|
||||
# ...
|
||||
# code: ...
|
||||
|
||||
|
||||
# Let's begin!
|
||||
# """
|
||||
|
||||
systemmsg =
|
||||
"""
|
||||
You are an assistant helping the user to execute SQL code from the user's query.
|
||||
systemmsg = """
|
||||
You are an assistant helping the user to execute SQL code from the user's query.
|
||||
|
||||
At each round of conversation, the user will give you:
|
||||
Context: ...
|
||||
User intention: ...
|
||||
Code executed from the last round: ...
|
||||
Execution error: execution error of the last round code.
|
||||
At each round of conversation, the user will give you:
|
||||
Context: ...
|
||||
User intention: ...
|
||||
Code executed from the last round: ...
|
||||
Execution error: execution error of the last round code.
|
||||
|
||||
You should consider the following guidelines:
|
||||
- Text information in the database is sometimes stored in lower case. If your search returns empty, try using lower case to search.
|
||||
You should consider the following guidelines:
|
||||
- Text information in the database is sometimes stored in lower case. If your search returns empty, try using lower case to search.
|
||||
|
||||
You should then respond to the user with:
|
||||
1) Understanding:
|
||||
- State your understanding about the current situation.
|
||||
2) Reasoning:
|
||||
- State your step by step reasoning about the current situation.
|
||||
3) Plan: Step-by-step instructions of how to complete the task.
|
||||
- Focus on improving the code from the last round.
|
||||
- Do not create any table in the database.
|
||||
4) Code:
|
||||
- Write new improved code.
|
||||
- Do not wrap the code and no comment as it will be executed directly without any modification against the database.
|
||||
You should then respond to the user with:
|
||||
1) Understanding:
|
||||
- State your understanding about the current situation.
|
||||
2) Reasoning:
|
||||
- State your step by step reasoning about the current situation.
|
||||
3) Plan: Step-by-step instructions of how to complete the task.
|
||||
- Focus on improving the code from the last round.
|
||||
- Do not create any table in the database.
|
||||
4) Code:
|
||||
- Write new improved code.
|
||||
- Do not wrap the code and no comment as it will be executed directly without any modification against the database.
|
||||
|
||||
You should only respond in format as described below and nothing more:
|
||||
Understanding: ...
|
||||
Reasoning: ...
|
||||
Plan:
|
||||
1) ...
|
||||
2) ...
|
||||
...
|
||||
Code: ...
|
||||
|
||||
Let's begin!
|
||||
"""
|
||||
You should only respond in format as described below and nothing more:
|
||||
Understanding: ...
|
||||
Reasoning: ...
|
||||
Plan:
|
||||
1) ...
|
||||
2) ...
|
||||
...
|
||||
Code: ...
|
||||
|
||||
Let's begin!
|
||||
"""
|
||||
|
||||
noise = ""
|
||||
note_flag = ""
|
||||
for attempt in 1:10
|
||||
usermsg =
|
||||
"""
|
||||
Context:
|
||||
$(context[:mentionedTableInfo])
|
||||
User intention: $(context[:userintention])
|
||||
Code executed from the last round: $(state[:code])
|
||||
Execution error: $(state[:errormsg])
|
||||
$noise
|
||||
$note_flag
|
||||
"""
|
||||
|
||||
_prompt =
|
||||
[
|
||||
Dict(:name=> "system", :text=> systemmsg),
|
||||
Dict(:name=> "user", :text=> usermsg)
|
||||
]
|
||||
usermsg = """
|
||||
Context:
|
||||
$(context[:mentionedTableInfo])
|
||||
User intention: $(context[:userintention])
|
||||
Code executed from the last round: $(state[:code])
|
||||
Execution error: $(state[:errormsg])
|
||||
$noise
|
||||
$note_flag
|
||||
"""
|
||||
|
||||
_prompt =
|
||||
[
|
||||
Dict(:name => "system", :text => systemmsg),
|
||||
Dict(:name => "user", :text => usermsg)
|
||||
]
|
||||
|
||||
# put in model format
|
||||
prompt = GeneralUtils.formatLLMtext(_prompt; formatname="llama3instruct")
|
||||
prompt *=
|
||||
"""
|
||||
<|start_header_id|>assistant<|end_header_id|>
|
||||
"""
|
||||
prompt *= """
|
||||
<|start_header_id|>assistant<|end_header_id|>
|
||||
"""
|
||||
try
|
||||
response = text2textInstructLLM(prompt)
|
||||
responsedict = GeneralUtils.textToDict(response,
|
||||
["Understanding", "Reasoning", "Plan", "Code"];
|
||||
rightmarker=":", symbolkey=true, lowercasekey=true)
|
||||
["Understanding", "Reasoning", "Plan", "Code"];
|
||||
rightmarker=":", symbolkey=true, lowercasekey=true)
|
||||
_code = responsedict[:code]
|
||||
code = strip(_code)
|
||||
|
||||
@@ -509,7 +518,7 @@ function getdata_decisionMaker(state::Dict, context::Dict, text2textInstructLLM:
|
||||
else
|
||||
end
|
||||
|
||||
println("--> getdata_decisionMaker() ", @__FILE__, " ", @__LINE__)
|
||||
println("~~~ getdata_decisionMaker() ", @__FILE__, " ", @__LINE__)
|
||||
pprintln(Dict(responsedict))
|
||||
return (thought=responsedict[:reasoning], code=code, success=true, errormsg=nothing)
|
||||
catch e
|
||||
@@ -523,7 +532,7 @@ function getdata_decisionMaker(state::Dict, context::Dict, text2textInstructLLM:
|
||||
end
|
||||
end
|
||||
return (thought=nothing, code=nothing, success=false,
|
||||
errormsg="Failed to generate SQL after numerous attempts.")
|
||||
errormsg="Failed to generate SQL after numerous attempts.")
|
||||
end
|
||||
|
||||
""" Execute a given SQL.
|
||||
@@ -553,7 +562,7 @@ julia> response = SQLLLM.SQLexecution(executeSQL, sql)
|
||||
"""
|
||||
# function SQLexecution(executeSQL::Function, sql::T
|
||||
# )::NamedTuple{(:result, :success, :errormsg, :reward, :isterminal), Tuple{Union{DataFrame, Nothing}, Bool, Union{String, Nothing}, Integer, Bool}} where {T<:AbstractString}
|
||||
# println("--> 1-01 ", @__FILE__, " ", @__LINE__)
|
||||
# println("~~~ 1-01 ", @__FILE__, " ", @__LINE__)
|
||||
# #XXX dummy SQL. use for testing
|
||||
# # sql = "SELECT w.wine_name FROM wine w JOIN wine_food wf ON w.wine_id = wf.wine_id JOIN food f ON wf.food_id = f.food_id WHERE f.\"food_name\" = 'lamb';"
|
||||
# # sql = " SELECT w.wine_name FROM wine w JOIN food f ON f.food_name = 'lamb' JOIN wine_food wf ON w.wine_id = wf.wine_id AND f.food_id = wf.food_id GROUP BY w.wine_name ORDER BY COUNT(DISTINCT w.wine_id) DESC;"
|
||||
@@ -567,37 +576,37 @@ julia> response = SQLLLM.SQLexecution(executeSQL, sql)
|
||||
|
||||
# # add LIMIT to the SQL to prevent loading large data
|
||||
# sql = strip(sql)
|
||||
# println("--> SQL 1", @__FILE__, " ", @__LINE__)
|
||||
# println("~~~ SQL 1", @__FILE__, " ", @__LINE__)
|
||||
# println(sql)
|
||||
# println("--> 1-02 ", @__FILE__, " ", @__LINE__)
|
||||
# println("~~~ 1-02 ", @__FILE__, " ", @__LINE__)
|
||||
|
||||
# if sql[end] != ';'
|
||||
# errorMsg = "Error, SQL execution failed because it does not ended with ';'"
|
||||
# return (result=nothing, success=false, errormsg=errorMsg, reward=0, isterminal=false)
|
||||
# end
|
||||
# println("--> 1-03 ", @__FILE__, " ", @__LINE__)
|
||||
# println("~~~ 1-03 ", @__FILE__, " ", @__LINE__)
|
||||
# if !occursin("LIMIT", sql)
|
||||
# # sql = sql[1:end-1] * " LIMIT 100;"
|
||||
# sql = sql[1:end-1] * " ORDER BY RANDOM() LIMIT 2;"
|
||||
# end
|
||||
|
||||
# println("--> SQL 2", @__FILE__, " ", @__LINE__)
|
||||
# println("~~~ SQL 2", @__FILE__, " ", @__LINE__)
|
||||
# println(sql)
|
||||
# println("--> 1-1 ", @__FILE__, " ", @__LINE__)
|
||||
# println("~~~ 1-1 ", @__FILE__, " ", @__LINE__)
|
||||
# result = executeSQL(sql)
|
||||
# println("--> 1-2 ", @__FILE__, " ", @__LINE__)
|
||||
# println("~~~ 1-2 ", @__FILE__, " ", @__LINE__)
|
||||
# df = DataFrame(result)
|
||||
# println("--> raw df ", df)
|
||||
# println("~~~ raw df ", df)
|
||||
# tablesize = size(df)
|
||||
# println("--> df size ", tablesize)
|
||||
# println("--> 6 ", @__FILE__, " ", @__LINE__)
|
||||
# println("~~~ df size ", tablesize)
|
||||
# println("~~~ 6 ", @__FILE__, " ", @__LINE__)
|
||||
# row = tablesize[1]
|
||||
# println("--> 7 ", @__FILE__, " ", @__LINE__)
|
||||
# println("~~~ 7 ", @__FILE__, " ", @__LINE__)
|
||||
# if row == 0 # if 0 row
|
||||
# errorMsg = "The resulting table has 0 row. Possible causes: 1) SQL is incorrect 2) There is no data that match your search criteria."
|
||||
# return (result=nothing, success=false, errormsg=errorMsg, reward=0, isterminal=false)
|
||||
# end
|
||||
# println("--> 8 ", @__FILE__, " ", @__LINE__)
|
||||
# println("~~~ 8 ", @__FILE__, " ", @__LINE__)
|
||||
# df1 =
|
||||
# if row > 2
|
||||
# # ramdom row to pick
|
||||
@@ -606,13 +615,13 @@ julia> response = SQLLLM.SQLexecution(executeSQL, sql)
|
||||
# df
|
||||
# end
|
||||
|
||||
# println("--> SQLexecution result ", @__FILE__, " ", @__LINE__)
|
||||
# println("~~~ SQLexecution result ", @__FILE__, " ", @__LINE__)
|
||||
# println(df1)
|
||||
# return (result=df1, success=true, errormsg=nothing, reward=1, isterminal=true)
|
||||
# end
|
||||
function SQLexecution(executeSQL::Function, sql::T
|
||||
)::NamedTuple{(:result, :success, :errormsg, :reward, :isterminal), Tuple{Union{DataFrame, Nothing}, Bool, Union{String, Nothing}, Integer, Bool}} where {T<:AbstractString}
|
||||
|
||||
) where {T<:AbstractString}
|
||||
|
||||
try
|
||||
#XXX dummy SQL. use for testing
|
||||
# sql = "SELECT w.wine_name FROM wine w JOIN wine_food wf ON w.wine_id = wf.wine_id JOIN food f ON wf.food_id = f.food_id WHERE f.\"food_name\" = 'lamb';"
|
||||
@@ -635,7 +644,7 @@ function SQLexecution(executeSQL::Function, sql::T
|
||||
else
|
||||
error("Error, SQL execution failed because it does not ended with ';'")
|
||||
end
|
||||
println("--> SQL ", @__FILE__, " ", @__LINE__)
|
||||
println("~~~ SQL ", @__FILE__, " ", @__LINE__)
|
||||
println(sql)
|
||||
|
||||
result = executeSQL(sql)
|
||||
@@ -649,24 +658,28 @@ function SQLexecution(executeSQL::Function, sql::T
|
||||
error("SQL execution failed. An unexpected error occurred. Please try again.")
|
||||
end
|
||||
|
||||
df1 =
|
||||
if row > 2
|
||||
# ramdom row to pick
|
||||
df[sample(1:nrow(df), 2, replace=false), :] # random select 2 rows from df
|
||||
else
|
||||
df
|
||||
end
|
||||
df1 =
|
||||
if row > 2
|
||||
# ramdom row to pick
|
||||
df[sample(1:nrow(df), 2, replace=false), :] # random select 2 rows from df
|
||||
else
|
||||
df
|
||||
end
|
||||
|
||||
println("--> SQLexecution() ", @__FILE__, " ", @__LINE__)
|
||||
println("~~~ SQLexecution() ", @__FILE__, " ", @__LINE__)
|
||||
println(df1)
|
||||
return (result=df1, success=true, errormsg=nothing, reward=1, isterminal=true)
|
||||
return (result=df1, success=true, errormsg=nothing)
|
||||
catch e
|
||||
println("~~~ Error SQLexecution() 2 ", @__FILE__, " ", @__LINE__)
|
||||
io = IOBuffer()
|
||||
showerror(io, e)
|
||||
errorMsg = String(take!(io))
|
||||
st = sprint((io, v) -> show(io, "text/plain", v), stacktrace(catch_backtrace()))
|
||||
println(errorMsg)
|
||||
return (result=nothing, success=false, errormsg=errorMsg, reward=0, isterminal=false)
|
||||
println("~~~ Error SQLexecution() 2.1 ", @__FILE__, " ", @__LINE__)
|
||||
response = (result=nothing, success=false, errormsg=errorMsg)
|
||||
println("~~~ Error SQLexecution() 2.2 ", @__FILE__, " ", @__LINE__)
|
||||
return response
|
||||
end
|
||||
end
|
||||
|
||||
@@ -687,100 +700,90 @@ end
|
||||
# Signature
|
||||
"""
|
||||
function extractContent_dataframe(df::DataFrame, context::Dict, text2textInstructLLM::Function
|
||||
)::String
|
||||
|
||||
)::String
|
||||
tablesize = size(df)
|
||||
row = tablesize[1]
|
||||
column = tablesize[2]
|
||||
|
||||
#[PENDING] Since selected column depend on the question, there should be a better way to select column on the fly, not hard coded like this.
|
||||
df1 =
|
||||
if column > 10 # assuming if columns > 10, agent is getting wine info but the info is too much
|
||||
selectedcolumn = ["wine_id",
|
||||
"wine_name",
|
||||
"brand",
|
||||
"manufacturer",
|
||||
"region",
|
||||
"country",
|
||||
"wine_type",
|
||||
"grape_variety",
|
||||
"serving_temperature",
|
||||
"intensity",
|
||||
"sweetness",
|
||||
"tannin",
|
||||
"acidity",
|
||||
"fizziness",
|
||||
"tasting_notes"]
|
||||
df1 = df[:, selectedcolumn]
|
||||
else
|
||||
df
|
||||
end
|
||||
# df1 =
|
||||
# if column > 10 # assuming if columns > 10, agent is getting wine info but the info is too much
|
||||
# selectedcolumn = ["wine_id",
|
||||
# "wine_name",
|
||||
# "winery",
|
||||
# "region",
|
||||
# "country",
|
||||
# "wine_type",
|
||||
# "grape",
|
||||
# "serving_temperature",
|
||||
# "intensity",
|
||||
# "sweetness",
|
||||
# "tannin",
|
||||
# "acidity",
|
||||
# "fizziness",
|
||||
# "tasting_notes"]
|
||||
# df1 = df[:, selectedcolumn]
|
||||
# else
|
||||
# df
|
||||
# end
|
||||
|
||||
df1 = df
|
||||
|
||||
dfstr = dfToString(df1)
|
||||
# println("--> df string")
|
||||
# println(dfstr)
|
||||
|
||||
systemmsg =
|
||||
"""
|
||||
You are an assistant that readouts the resulting table after the user executing SQL command.
|
||||
systemmsg = """
|
||||
You are an assistant that readouts the resulting table after the user executing SQL command.
|
||||
|
||||
At each round of conversation, the user will give you:
|
||||
- User intention: ...
|
||||
- Resulting table dimension: ...
|
||||
- Resulting table: The resulting table after executing the user's intention.
|
||||
At each round of conversation, the user will give you:
|
||||
- User intention: ...
|
||||
- Resulting table dimension: ...
|
||||
- Resulting table: The resulting table after executing the user's intention.
|
||||
|
||||
You should then respond to the user with:
|
||||
- About_resulting_table:
|
||||
1) What is the resulting table represent?
|
||||
- Search_summary:
|
||||
1) Summarize the table's content based on the user intension in verbal English.
|
||||
Here are some example:
|
||||
Bad example (you are not Summarize the table content): there are 2 columns in the table i.e. "cash" and "number".
|
||||
2) Do not generate additional text.
|
||||
You should then respond to the user with:
|
||||
- About_resulting_table:
|
||||
1) What is the resulting table represent?
|
||||
- Search_summary:
|
||||
1) Summarize the table's content based on the user intension in verbal English.
|
||||
Here are some example:
|
||||
Bad example (you are not Summarize the table content): there are 2 columns in the table i.e. "cash" and "number".
|
||||
2) Do not generate additional text.
|
||||
|
||||
You should only respond in format as described below:
|
||||
About_resulting_table: ...
|
||||
Search_summary: ...
|
||||
|
||||
Let's begin!
|
||||
"""
|
||||
You should only respond in format as described below:
|
||||
About_resulting_table: ...
|
||||
Search_summary: ...
|
||||
|
||||
usermsg =
|
||||
"""
|
||||
User intention: $(context[:userintention])
|
||||
Resulting table: $dfstr
|
||||
"""
|
||||
|
||||
_prompt =
|
||||
[
|
||||
Dict(:name=> "system", :text=> systemmsg),
|
||||
Dict(:name=> "user", :text=> usermsg)
|
||||
]
|
||||
Let's begin!
|
||||
"""
|
||||
usermsg = """
|
||||
User intention: $(context[:userintention])
|
||||
Resulting table: $dfstr
|
||||
"""
|
||||
_prompt =
|
||||
[
|
||||
Dict(:name => "system", :text => systemmsg),
|
||||
Dict(:name => "user", :text => usermsg)
|
||||
]
|
||||
|
||||
# put in model format
|
||||
prompt = GeneralUtils.formatLLMtext(_prompt; formatname="llama3instruct")
|
||||
prompt *=
|
||||
"""
|
||||
<|start_header_id|>assistant<|end_header_id|>
|
||||
"""
|
||||
|
||||
prompt *= """
|
||||
<|start_header_id|>assistant<|end_header_id|>
|
||||
"""
|
||||
for i in 1:5
|
||||
response = text2textInstructLLM(prompt)
|
||||
responsedict = GeneralUtils.textToDict(response, ["About_resulting_table", "Search_summary"],
|
||||
rightmarker=":", symbolkey=true)
|
||||
responsedict = GeneralUtils.textToDict(response, ["About_resulting_table", "Search_summary"],
|
||||
rightmarker=":", symbolkey=true)
|
||||
|
||||
# result = dfstr
|
||||
result =
|
||||
"""
|
||||
Summary: $(responsedict[:Search_summary])
|
||||
More details: $dfstr
|
||||
"""
|
||||
result = """
|
||||
Summary: $(responsedict[:Search_summary])
|
||||
More details: $dfstr
|
||||
"""
|
||||
|
||||
if row > 2
|
||||
result *= "There are many more rows, but they are truncated because there are too many of them."
|
||||
end
|
||||
|
||||
println("--> extractContent_dataframe() ", @__FILE__, " ", @__LINE__)
|
||||
println("~~~ extractContent_dataframe() ", @__FILE__, " ", @__LINE__)
|
||||
println(result)
|
||||
|
||||
return result
|
||||
@@ -856,47 +859,44 @@ julia> result = SQLLLM.getTableNameFromSQL(sql, text2textInstructLLM)
|
||||
# Signature
|
||||
"""
|
||||
function getTableNameFromSQL(sql::T, text2textInstructLLM::Function)::Vector{String} where {T<:AbstractString}
|
||||
systemmsg =
|
||||
"""
|
||||
Extract table name out of the user query.
|
||||
systemmsg = """
|
||||
Extract table name out of the user query.
|
||||
|
||||
At each round of conversation, the user will give you:
|
||||
Query: ...
|
||||
At each round of conversation, the user will give you:
|
||||
Query: ...
|
||||
|
||||
You should then respond to the user with:
|
||||
- table_name: a list of table name that the user mentioned in the query.
|
||||
For example, ["color", "type"]
|
||||
You should then respond to the user with:
|
||||
- table_name: a list of table name that the user mentioned in the query.
|
||||
For example, ["color", "type"]
|
||||
|
||||
You must only respond in format as described below:
|
||||
table_name: ["...", "...", ...]
|
||||
|
||||
Let's begin!
|
||||
"""
|
||||
You must only respond in format as described below:
|
||||
table_name: ["...", "...", ...]
|
||||
|
||||
usermsg =
|
||||
"""
|
||||
Query: $sql
|
||||
"""
|
||||
|
||||
_prompt =
|
||||
[
|
||||
Dict(:name=> "system", :text=> systemmsg),
|
||||
Dict(:name=> "user", :text=> usermsg)
|
||||
]
|
||||
Let's begin!
|
||||
"""
|
||||
|
||||
usermsg = """
|
||||
Query: $sql
|
||||
"""
|
||||
|
||||
_prompt =
|
||||
[
|
||||
Dict(:name => "system", :text => systemmsg),
|
||||
Dict(:name => "user", :text => usermsg)
|
||||
]
|
||||
|
||||
# put in model format
|
||||
prompt = GeneralUtils.formatLLMtext(_prompt; formatname="llama3instruct")
|
||||
prompt *=
|
||||
"""
|
||||
<|start_header_id|>assistant<|end_header_id|>
|
||||
"""
|
||||
prompt *= """
|
||||
<|start_header_id|>assistant<|end_header_id|>
|
||||
"""
|
||||
|
||||
for attempt in 1:5
|
||||
try
|
||||
response = text2textInstructLLM(prompt)
|
||||
responsedict = GeneralUtils.textToDict(response,
|
||||
["table_name"],
|
||||
rightmarker=":", symbolkey=true)
|
||||
["table_name"],
|
||||
rightmarker=":", symbolkey=true)
|
||||
response = copy(JSON3.read(responsedict[:table_name]))
|
||||
|
||||
return response
|
||||
|
||||
Reference in New Issue
Block a user