40 Commits

Author SHA1 Message Date
ton cf71f56771 Merge pull request 'update' (#8) from v0.2.8-add_return_msg into v0.2.8
Reviewed-on: #8
2026-07-15 04:51:26 +00:00
ton 0fcf8c8669 update 2026-07-15 11:50:59 +07:00
ton c27a7008fe Merge pull request 'v0.2.8-add_return_msg' (#6) from v0.2.8-add_return_msg into v0.2.8
Reviewed-on: #6
2026-07-15 04:50:25 +00:00
ton 480ecf3e74 update 2026-07-14 18:09:10 +07:00
ton c80c4ca65d update 2026-07-13 10:35:29 +07:00
ton 57a8e9cafc update 2026-07-13 08:17:32 +07:00
ton 831014cabf Merge pull request 'update' (#5) from v0.2.7 into main
Reviewed-on: #5
2026-07-10 10:44:57 +00:00
ton 243e3fe10b update 2026-07-10 17:44:34 +07:00
ton c594a34e4e Merge pull request 'v0.2.6' (#4) from v0.2.6 into main
Reviewed-on: #4
2026-07-09 13:18:30 +00:00
ton 2efb016646 up version 2026-07-09 20:18:01 +07:00
ton 216a8bdabb Merge pull request 'v0.2.6-fix_too_many_decision' (#3) from v0.2.6-fix_too_many_decision into v0.2.6
Reviewed-on: #3
2026-07-09 13:16:22 +00:00
ton cbaa480e84 update evaluator 2026-07-09 19:44:37 +07:00
ton 6cb4073e29 update 2026-07-09 19:27:56 +07:00
ton 42b8f5bdb1 update 2026-07-09 06:53:14 +07:00
ton 35f1482228 md system prompt 2026-07-07 07:55:10 +07:00
ton b4cac4f383 update 2026-07-04 14:20:20 +07:00
ton 685ee7a48f update 2026-07-04 13:53:55 +07:00
ton b55ae31e5b update 2026-07-04 13:49:19 +07:00
ton e8e1764bb4 Merge pull request 'v0.3.0' (#2) from v0.3.0 into main
Reviewed-on: #2
2026-07-04 06:11:15 +00:00
ton e0b3ffa8e3 Merge pull request 'v0.3.0-use_openai_format' (#1) from v0.3.0-use_openai_format into v0.3.0
Reviewed-on: #1
2026-07-04 06:10:43 +00:00
ton ec9f44e5a1 update 2026-07-04 13:09:37 +07:00
ton 6b3f8620e1 update 2026-07-04 12:46:25 +07:00
ton 9cd37317d7 update 2026-07-02 18:31:00 +07:00
ton 1577d7ae25 update 2026-07-02 18:25:56 +07:00
ton c4e255ec2a update 2026-07-01 21:35:48 +07:00
ton 681a91a0ca update 2026-06-29 21:03:18 +07:00
ton c085a445e2 update 2026-06-29 20:55:52 +07:00
ton bc81033924 use string key for dict 2026-06-25 06:16:30 +07:00
ton f5875dcb61 update 2026-06-24 21:04:09 +07:00
ton 69208b2ffc update 2026-06-21 08:14:16 +07:00
ton a4227ec165 update 2025-07-23 18:31:38 +07:00
narawat lamaiin 21416f4b13 update 2025-06-15 08:59:10 +07:00
narawat lamaiin ff4db039ab update 2025-06-03 10:08:17 +07:00
narawat lamaiin b3537a83e0 update 2025-05-18 17:21:55 +07:00
narawat lamaiin 0a0e36d86a update 2025-05-17 21:36:40 +07:00
narawat lamaiin 8c5b1b6938 update 2025-05-14 21:21:30 +07:00
narawat lamaiin aeda7e0baf update 2025-05-06 06:49:21 +07:00
narawat lamaiin 2541223bbb update 2025-05-04 20:56:55 +07:00
narawat lamaiin c8f5983620 update 2025-05-04 13:30:08 +07:00
narawat lamaiin 5112701dc2 update 2025-05-01 07:59:18 +07:00
12 changed files with 1260 additions and 973 deletions
+387 -211
View File
File diff suppressed because it is too large Load Diff
+5 -2
View File
@@ -1,7 +1,7 @@
name = "SQLLLM" name = "SQLLLM"
uuid = "2ebc79c7-cc10-4a3a-9665-d2e1d61e63d3" uuid = "2ebc79c7-cc10-4a3a-9665-d2e1d61e63d3"
version = "0.2.8"
authors = ["narawat lamaiin <narawat@outlook.com>"] authors = ["narawat lamaiin <narawat@outlook.com>"]
version = "0.2.4"
[deps] [deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
@@ -11,7 +11,7 @@ Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
GeneralUtils = "c6c72f09-b708-4ac8-ac7c-2084d70108fe" GeneralUtils = "c6c72f09-b708-4ac8-ac7c-2084d70108fe"
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
LLMMCTS = "d76c5a4d-449e-4835-8cc4-dd86ec44f241" LLMMCTS = "d76c5a4d-449e-4835-8cc4-dd86ec44f241"
LibPQ = "194296ae-ab2e-5f79-8cd4-7183a0a5a0d1" LibPQ = "194296ae-ab2e-5f79-8cd4-7183a0a5a0d1"
PrettyPrinting = "54e16d92-306c-5ea0-a30b-337be88ac337" PrettyPrinting = "54e16d92-306c-5ea0-a30b-337be88ac337"
@@ -24,3 +24,6 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
[compat] [compat]
Dates = "1.11.0" Dates = "1.11.0"
GeneralUtils = "0.4.0 - 0.9.0"
JSON = "1.6.1"
LLMMCTS = "0.1.5 - 0.9.0"
+82
View File
@@ -0,0 +1,82 @@
BIN
View File
Binary file not shown.
+22
View File
@@ -0,0 +1,22 @@
d = OrderedDict{String, Any}("1" => Dict{String, Any}("action_name" => "RUNSQL", "action_input" => "SELECT w.winery, w.wine_name, w.wine_id, w.vintage, w.region, w.country, w.wine_type, w.grape, w.serving_temperature, w.sweetness, w.intensity, w.tannin, w.acidity, w.tasting_notes, rw.price, rw.currency, w.image_url FROM wine w JOIN retailer_wine rw ON w.wine_id = rw.wine_id JOIN retailer r ON rw.retailer_id = r.retailer_id WHERE w.wine_name ILIKE 'Montrachet Grand Cru' AND w.winery ILIKE 'Domaine Jacques Prieur' AND w.region ILIKE 'Montrachet' AND r.retailer_id = 'f54eab6b-7650-4448-b009-c53f3efbcc3b';", "action_result" => "No records found. Try loosening your search criteria.", "plan" => "The user wants to retrieve specific details about a wine, including its characteristics and pricing at a specific retailer, based on several criteria: wine name, winery, region, retailer name, and retailer ID. I need to join the 'wine', 'retailer_wine', and 'retailer' tables. I will use the wine attributes (name, winery, region) and the retailer ID to filter the results and select all requested fields, using ILIKE for text matching as per guidelines."), "2" => Dict{String, Any}("action_name" => "RUNSQL", "action_input" => "SELECT retailer_name FROM retailer WHERE retailer_id = 'f54eab6b-7650-4448-b009-c53f3efbcc3b';", "action_result" => "1) retailer_name: Yiem Wines Ltd\n", "plan" => "The previous query failed with no records found, indicating that the combination of all constraints (wine name, winery, region, and retailer ID) is too restrictive. To proceed, I will first verify the retailer's name using the provided ID. Then, I will execute a new query, joining the 'wine', 'retailer_wine', and 'retailer' tables, filtering only by the wine name ('Montrachet Grand Cru') and the specific retailer ID ('f54eab6b-7650-4448-b009-c53f3efbcc3b'), removing the 'winery' and 'region' constraints to see if any matching records exist at that retailer."))
+388 -452
View File
File diff suppressed because it is too large Load Diff
+174 -174
View File
@@ -3,7 +3,7 @@ module llmfunction
export listAllTable_json, listAllTable_str, tableinfo, getdata, finalAnswerBox, export listAllTable_json, listAllTable_str, tableinfo, getdata, finalAnswerBox,
getTableNameFromSQL, extractContent_dataframe, SQLexecution, compareState getTableNameFromSQL, extractContent_dataframe, SQLexecution, compareState
using HTTP, JSON3, URIs, Random, PrettyPrinting, UUIDs, LibPQ, Tables, DataFrames, CSV, using HTTP, JSON, URIs, Random, PrettyPrinting, UUIDs, LibPQ, Tables, DataFrames, CSV,
DataStructures, StatsBase, Dates DataStructures, StatsBase, Dates
using GeneralUtils, LLMMCTS using GeneralUtils, LLMMCTS
using ..util using ..util
@@ -189,7 +189,7 @@ function tableinfo(executeSQL::Function, tablenames::T
end end
end end
if !isempty(notExistingTable) 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: $(JSON.json(notExistingTable))"
return (result=result,) return (result=result,)
end end
@@ -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"
@@ -347,8 +347,9 @@ end
# Signature # Signature
""" """
function getdata_decisionMaker(state::Dict, context::Dict, text2textInstructLLM::Function 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}}} llmFormatName::String
)::NamedTuple{(:thought, :code, :success, :errormsg),Tuple{Union{String,Nothing},Union{String,Nothing},Bool,Union{String,Nothing}}}
Hints = "None" Hints = "None"
@@ -366,17 +367,14 @@ function getdata_decisionMaker(state::Dict, context::Dict, text2textInstructLLM:
- Text information in the database is sometimes stored in lower case. If your search returns empty, try using lower case to search. - 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: You should then respond to the user with:
1) Comprehension: 1) Plan: Step-by-step instructions of how to complete the task.
- State your comprehension 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. - Focus on improving the code from the last round.
- Do not create any table in the database. - Do not create any table in the database.
4) Code: 2) Code:
- Write new improved 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. - 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: You should only respond in format as described below and nothing more:
Comprehension: ...
Plan: Plan:
1) ... 1) ...
2) ... 2) ...
@@ -391,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
""" """
@@ -406,22 +404,23 @@ function getdata_decisionMaker(state::Dict, context::Dict, text2textInstructLLM:
] ]
# put in model format # put in model format
prompt = GeneralUtils.formatLLMtext(_prompt, "granite3") prompt = GeneralUtils.formatLLMtext(_prompt, llmFormatName)
try try
response = text2textInstructLLM(prompt, modelsize="medium") response = text2textInstructLLM(prompt, modelsize="medium")
response = GeneralUtils.deFormatLLMtext(response, "granite3") response = GeneralUtils.deFormatLLMtext(response, llmFormatName)
think, response = GeneralUtils.extractthink(response)
header = ["Comprehension:", "Plan:", "Code:"] header = ["Plan:", "Code:"]
dictkey = ["comprehension", "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
@@ -441,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)
@@ -482,20 +481,9 @@ julia> response = SQLLLM.SQLexecution(executeSQL, sql)
# Signature # Signature
""" """
function SQLexecution(executeSQL::Function, sql::T function SQLexecution(executeSQL::Function, sql::T
) where {T<:AbstractString} )::NamedTuple where {T<:AbstractString}
try 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';"
# 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;"
# sql = " SELECT COUNT(DISTINCT wf.wine_id) 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 ILIKE '%lamb%'"
#XXX use for package testing, remove when done
# ans = "1.schilfwein zweigelt 2.cabernet sauvignon reserve limited edition"
# ans = "There are 1500 wines that can be paired with lamb."
# ans = "1500"
# return (response=ans, errormsg=nothing, reward=1, isterminal=true)
# add LIMIT to the SQL to prevent loading large data # add LIMIT to the SQL to prevent loading large data
sql = strip(sql) sql = strip(sql)
@@ -509,20 +497,15 @@ function SQLexecution(executeSQL::Function, sql::T
else else
sql = sql * ";" sql = sql * ";"
end end
println("\n~~~ SQLexecution() SQL: ", @__FILE__, " ", @__LINE__)
println(sql)
result = executeSQL(sql) result = executeSQL(sql)
df = DataFrame(result) df = DataFrame(result)
tablesize = size(df) tablesize = size(df)
row, column = tablesize row, column = tablesize
if row == 0 if row == 0
error("The resulting table has 0 row. Possible causes: 1) Your search criteria might be too specific. Relaxing some conditions could yield better results. Remember, you can always refine your search later. 2) There could be a typo in your search query. 3) You might be searching in the wrong place.") return (result_str="No records found. Try loosening your search criteria.", result_raw=nothing, success=true, errormsg=nothing)
elseif column > 30 elseif column > 30
error("SQL execution failed. An unexpected error occurred. Please try again.") return (result_str="There are more than 30 columns. Please be more specific.", result_raw=df, success=true, errormsg=nothing)
end else
df1 = df1 =
if row > 2 if row > 2
# ramdom row to pick # ramdom row to pick
@@ -530,18 +513,20 @@ function SQLexecution(executeSQL::Function, sql::T
else else
df df
end end
result = GeneralUtils.dfToString(df1)
println("\n~~~ SQLexecution() result: ", @__FILE__, " ", @__LINE__) # println("\n~~~ SQLexecution() result: ", @__FILE__, " ", @__LINE__)
println(df1) # println(sql)
return (result=df1, success=true, errormsg=nothing) # println(df1)
# println("\n")
return (result_str=result, result_raw=df1, success=true, errormsg=nothing)
end
catch e catch e
io = IOBuffer() io = IOBuffer()
showerror(io, e) showerror(io, e)
errorMsg = String(take!(io)) errorMsg = String(take!(io))
st = sprint((io, v) -> show(io, "text/plain", v), stacktrace(catch_backtrace())) st = sprint((io, v) -> show(io, "text/plain", v), stacktrace(catch_backtrace()))
println(errorMsg) println(errorMsg)
response = (result=nothing, success=false, errormsg=errorMsg) return (result_str=nothing, result_raw=nothing, success=false, errormsg=errorMsg)
return response
end end
end end
@@ -560,9 +545,10 @@ end
- `result::String` - `result::String`
# Signature # Signature
""" """ #PENDING
function extractContent_dataframe(df::DataFrame, text2textInstructLLM::Function, action::String function extractContent_dataframe(df::DataFrame, text2textInstructLLM::Function, action::String,
)::String llmFormatName::String
)::String
tablesize = size(df) tablesize = size(df)
row = tablesize[1] row = tablesize[1]
column = tablesize[2] column = tablesize[2]
@@ -628,32 +614,34 @@ function extractContent_dataframe(df::DataFrame, text2textInstructLLM::Function,
] ]
# put in model format # put in model format
prompt = GeneralUtils.formatLLMtext(_prompt, "granite3") prompt = GeneralUtils.formatLLMtext(_prompt, llmFormatName)
header = ["About_resulting_table:", "Search_summary:"] header = ["About_resulting_table:", "Search_summary:"]
dictkey = ["about_resulting_table", "search_summary"] dictkey = ["about_resulting_table", "search_summary"]
for i in 1:5 for i in 1:5
response = text2textInstructLLM(prompt, modelsize="medium") response = text2textInstructLLM("ramdom_id", prompt)
response = GeneralUtils.deFormatLLMtext(response, "granite3") response = GeneralUtils.deFormatLLMtext(response, llmFormatName)
think, response = GeneralUtils.extractthink(response)
kw = [] # check whether response has all header
# use for loop and detect_keyword function to get the exact variation of each keyword in the text then push to kw list detected_kw = GeneralUtils.detectKeywordVariation(header, response)
for keyword in header missingkeys = [k for (k, v) in detected_kw if v === nothing]
detected = GeneralUtils.detect_keyword(keyword, response) if !isempty(missingkeys)
push!(kw, detected) errornote = "$missingkeys are missing from your previous response"
end println("\nERROR SQLLLM extractContent_dataframe() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
if nothing kw continue
println("Some keywords are missing, Required keywords=$header, Response keywords=$kw ", @__FILE__, ":", @__LINE__, " $(Dates.now())") elseif sum([length(i) for i in values(detected_kw)]) > length(header)
continue # try again next loop errornote = "\nYour previous attempt has duplicated points according to the required response format"
println("\nERROR SQLLLM extractContent_dataframe() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
end end
responsedict = GeneralUtils.textToDict(response, header; responsedict = GeneralUtils.textToDict(response, header;
dictKey=dictkey, symbolkey=true) dictKey=dictkey, symbolkey=false)
# result = dfstr
result = result =
""" """
Summary: $(responsedict[:search_summary]) Summary: $(responsedict["search_summary"])
More details: $dfstr More details: $dfstr
""" """
@@ -736,7 +724,9 @@ julia> result = SQLLLM.getTableNameFromSQL(sql, text2textInstructLLM)
# Signature # Signature
""" """
function getTableNameFromSQL(sql::T, text2textInstructLLM::Function)::Vector{String} where {T<:AbstractString} function getTableNameFromSQL(sql::T, text2textInstructLLM::Function,
llmFormatName::String
)::Vector{String} where {T<:AbstractString}
systemmsg = """ systemmsg = """
Extract table name out of the user query. Extract table name out of the user query.
@@ -764,17 +754,17 @@ function getTableNameFromSQL(sql::T, text2textInstructLLM::Function)::Vector{Str
] ]
# put in model format # put in model format
prompt = GeneralUtils.formatLLMtext(_prompt, "granite3") prompt = GeneralUtils.formatLLMtext(_prompt, llmFormatName)
header = ["Table_name:"] header = ["Table_name:"]
dictkey = ["table_name"] dictkey = ["table_name"]
for attempt in 1:5 for attempt in 1:5
try try
response = text2textInstructLLM(prompt, modelsize="medium") response = text2textInstructLLM(prompt, modelsize="medium")
response = GeneralUtils.deFormatLLMtext(response, "granite3") 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(JSON.parse(responsedict["table_name"]))
return response return response
catch e catch e
@@ -816,148 +806,158 @@ julia>
# Notes # Notes
- The function makes up to 10 attempts to get a valid response from the LLM - The function makes up to 10 attempts to get a valid response from the LLM
- Each state in highValueStateList should contain a thoughtHistory with action_input and observation - Each state in highValueStateList should contain a action_history with action_input and observation
- The LLM evaluates attempts based on accuracy and relevance to the original question - The LLM evaluates attempts based on accuracy and relevance to the original question
""" """
function compareState(question::String, highValueStateList::Vector{T}, function compareState(question::String, highValueStateList::Vector{T},
text2textInstructLLM::Function)::Integer where {T<:AbstractDict} text2textInstructLLM::Function; maxattempt::Integer=10
)::Integer where {T<:AbstractDict}
systemmsg = systemmsg =
""" """
<Your profile> # Your profile:
- You are a helpful assistant - You are a helpful assistant
</Your profile>
<Situation> # Situation:
The user has made multiple attempts to solve the question, resulting in various answers - The user has made multiple attempts to solve the question, resulting in various answers
<Your mission>
# Your mission:
- Identify and select the most accurate and relevant response from these multiple results for the user - Identify and select the most accurate and relevant response from these multiple results for the user
</Your mission>
<At each round of conversation, you will be given the following> # At each round of conversation, you will be given the following:
Question: the question the user is trying to answer Question: the question the user is trying to answer
Attempt: the user's attempted actions and their corresponding results Attempt: the user's attempted actions and their corresponding results
</At each round of conversation, you will be given the following>
<You should then respond to the user with the following>
Comparison: a comparison of all results from all attempts
Rationale: a brief explanation of why the selected response is the most accurate and relevant
Selected_response_number: the number the selected response in the list of results (e.g., 1, 2, 3, ...)
</You should then respond to the user with the following>
<You should only respond in format as described below>
Comparison: ...
Rationale: ...
Selected_response_number: ...
</You should only respond in format as described below>
<Here are some examples>
User's question: "How many German wines do you have?"
Attempt 1:
Action: SELECT COUNT(*) FROM wines WHERE country = 'Germany'
Result: 100 wines
Attempt 2:
Action: SELECT COUNT(*) FROM wines WHERE country = 'Germany' AND type = 'Red'
Result: 50 red wines
Comparison: The second attempt counts only German red wines while the first attempt includes all German wines.
Rationale: The user is asking for the number of German wines without specifying a type, so the most accurate response is the first attempt because it includes all German wines.
Selected_response_number:1
</Here are some examples>
Let's begin! # You should then respond to the user with the following:
1) "comparison", detailed comparison of all results from all attempts from various aspects.
2) "rationale", a brief explanation of why the selected response is the most accurate and relevant
3) "selected_response_number", the number the selected response in the list of results (e.g., 1, 2, 3, ...)
# you should only respond in JSON format as described below
"comparison": "..."
"rationale": "..."
"selected_response_number": "..."
# Here are some examples:
Question: "How many German wines do you have?"
Attempt 1)
action_name: RUNSQL
action_input: SELECT COUNT(*) FROM wines WHERE country = 'Germany'
action_result: 100 wines
Attempt 2)
action_name: RUNSQL
action_input: SELECT COUNT(*) FROM wines WHERE country = 'Germany' AND type = 'Red'
action_result: 50 red wines
"comparison": "The second attempt counts only German red wines while the first attempt includes all German wines."
"rationale": "The user is asking for the number of German wines without specifying a type, so the most accurate response is the first attempt because it includes all German wines."
"selected_response_number": "1"
""" """
requiredKeys = ["comparison", "rationale", "selected_response_number"]
potentialSolution = [] potentialSolution = []
keys = [:action_input, :observation] includekeys = ["action_name", "action_input", "action_result"]
# 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] action_history = state["action_history"]
_, currentstate_latestIndice = latestKeys = [i for i in keys(action_history)][end]
GeneralUtils.findHighestIndexKey(thoughtHistory, keys[1])
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(includekeys)
d[v] = thoughtHistory[latestKeys[i]] latest_action = action_history[latestKeys]
d[v] = latest_action[v]
end end
push!(potentialSolution, d) push!(potentialSolution, d)
end end
println("\n")
@show potentialSolution
println("--- ", @__FILE__, @__LINE__)
""" """
# put potential solutions from potentialSolution into the following form # put potential solutions from potentialSolution into the following form
Attempt 1 Attempt 1)
action_name: action_name:
action_input: action_input:
observation: action_result:
Attempt 2 Attempt 2)
action_name: action_name:
action_input: action_input:
observation: action_result:
... ...
""" """
potentialSolutionStr = "" potentialSolutionStr = ""
for (i, state) in enumerate(potentialSolution) for (i, state) in enumerate(potentialSolution)
potentialSolutionStr *= "Attempt $i\n" potentialSolutionStr *= "Attempt $i)\n"
for k in keys for k in includekeys
potentialSolutionStr *= "$k: $(state[k])\n" potentialSolutionStr *= "$k: $(state[k])\n"
println("")
end end
end end
errornote = ""
for attempt in 1:10
errorFlag = false
usermsg = usermsg =
""" """
Question: $question Question: $question
Attempts: $potentialSolutionStr $potentialSolutionStr
P.S. $errornote
""" """
_prompt = msg = Dict(
[ "model" => "gemma-4-E4B-it-UD-Q4_K_XL",
Dict(:name=> "system", :text=> systemmsg), "messages" => [
Dict(:name=> "user", :text=> usermsg) Dict(
"role" => "system",
"content" => [
Dict("type" => "text", "text" => systemmsg),
] ]
),
Dict(
"role" => "user",
"content" => [
Dict("type" => "text", "text" => usermsg),
]
),
],
"temperature" => 0.7
)
# put in model format for attempt in 1:maxattempt
prompt = GeneralUtils.formatLLMtext(_prompt, "granite3") response = text2textInstructLLM("random_id", msg)
response = GeneralUtils.clean_json_response(response)
header = ["Comparison:", "Rationale:", "Selected_response_number:"] response = GeneralUtils.remove_french_accents(response)
dictkey = ["comparison", "rationale", "selected_response_number"] think, response = GeneralUtils.extractthink(response)
response = String(split(response, ", action_result")[1]) # in case LLM generate action_result key which it isn't supposed to
response = text2textInstructLLM(prompt, modelsize="medium") response = strip(response)
responsedict = nothing
# sometime LLM output something like **Comprehension**: which is not expected
response = replace(response, "**"=>"")
response = replace(response, "***"=>"")
response = GeneralUtils.deFormatLLMtext(response, "granite3")
# make sure every header is in the response
for i in header
detected = GeneralUtils.detect_keyword(i, response)
if detected === nothing
errornote = "Your previous attempt didn't provide $i"
errorFlag = true
end
end
if errorFlag
println("\nERROR SQLLLM compareState() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue # skip to the next iteration
end
responsedict = GeneralUtils.textToDict(response, header; dictKey=dictkey, symbolkey=true)
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 = JSON.parse(response)
responsedict = GeneralUtils.dictify(_responsedict, keytype=String, sort_order=requiredKeys)
catch
println("\nERROR SQLLLM evaluator() failed to parse response: $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
end
# check whether all answer's key points are in responsedict
ispass, errormsg = GeneralUtils.checkAgentResponse_JSON(responsedict, requiredKeys)
if !ispass
errornote = errormsg
println("\nERROR SQLLLM evaluator() $errornote --(not qualify response)> $responsedict ", @__FILE__, ":", @__LINE__, " $(Dates.now())\n")
continue
end
try
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())")
continue continue
end end
println("\n~~~ compareState() ", @__FILE__, ":", @__LINE__, " $(Dates.now())") # println("\n~~~ compareState() ")
pprintln(Dict(responsedict)) # pprintln(Dict(responsedict))
# println("---\n", @__FILE__, ":", @__LINE__)
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"
+36 -29
View File
@@ -1,34 +1,41 @@
""" """
Default system message template: # -------------------------------- Default system message template ------------------------------- #
<Your role> <Your role>
- You are a helpful assistant - You are a helpful assistant
</Your role> </Your role>
<Situation> <Situation>
- Describe the current situation - Describe the current situation
</Situation> Ex. The world use enormous energy from non-sustainable sources. This leads to climate change.
<Your vision> </Situation>
<Your vision>
- state your vision of how the situation will evolve, what would you want the situation to evolve into - state your vision of how the situation will evolve, what would you want the situation to evolve into
</Your vision> Ex. To be the leading innovator in sustainable technology by 2030, transforming global energy systems.
<Your mission> </Your vision>
<Your mission>
- state the goal - state the goal
</Your mission> Ex. Empowering communities through clean energy solutions to create a sustainable future.
<Your mission's objective includes> </Your mission>
- Break the goal into smaller steps <Your mission's objective includes>
</Your mission's objective includes> - Specific, measurable, and time-bound goals that directly support the mission.
<Your responsibility includes> Ex. Launch 50 solar-powered water purification systems in 3 regions by 2025.
</Your mission's objective includes>
<Your responsibility includes>
- state the mini goals that fall under your responsibility - state the mini goals that fall under your responsibility
</Your responsibility includes> </Your responsibility includes>
<Your responsibility does NOT includes> <Your responsibility does NOT includes>
- -
</Your responsibility does NOT includes> </Your responsibility does NOT includes>
<At each round of conversation, you will be given the following information> <At each round of conversation, you will be given the following information>
</At each round of conversation, you will be given the following information>
<You must follow the following guidelines>
- -
</You must follow the following guidelines> </At each round of conversation, you will be given the following information>
<You should then respond to the user with interleaving Comprehension, Plan, Action_name, Action_input> <You must follow the following policy>
-
</You must follow the following policy>
<You should follow the following guidelines>
-
</You should follow the following guidelines>
<You should then respond to the user with interleaving Comprehension, Plan, Action_name, Action_input>
Comprehension: State your comprehension about the current situation. Comprehension: State your comprehension about the current situation.
Plan: Given the current circumstances, outline a detailed, step-by-step plan to accomplish the task. Be specific. Plan: Given the current circumstances, outline a detailed, step-by-step plan to accomplish the task. Be specific.
Action_name: (Typically corresponds to the execution of the first step in your plan) Can be one of the following function names: Action_name: (Typically corresponds to the execution of the first step in your plan) Can be one of the following function names:
@@ -36,18 +43,18 @@ Default system message template:
- CHECKRESOURCES which you can use to check resources - CHECKRESOURCES which you can use to check resources
- IMPLEMENT which you can use to implement the solution - IMPLEMENT which you can use to implement the solution
Action_input: Detail the input for the action. Action_input: Detail the input for the action.
</You should then respond to the user with interleaving Comprehension, Plan, Action_name, Action_input> </You should then respond to the user with interleaving Comprehension, Plan, Action_name, Action_input>
<You should only respond in format as described below> <You should only respond in format as described below>
Comprehension: ... Comprehension: ...
Plan: ... Plan: ...
Action_name: ... Action_name: ...
Action_input: ... Action_input: ...
</You should only respond in format as described below> </You should only respond in format as described below>
<Here are some examples> <Here are some examples>
</Here are some examples> </Here are some examples>
Let's begin! Let's begin!
@@ -57,7 +64,7 @@ Default system message template:
Example: # ------------------------------------------- Example: ------------------------------------------- #
<Your profile> <Your profile>
- You are a founder of a tech startup - You are a founder of a tech startup
+81 -20
View File
@@ -1,9 +1,9 @@
using Revise using Revise
using LibPQ, Dates, JSON3, PrettyPrinting, UUIDs, DataFrames, DataStructures, Base64 using LibPQ, Dates, JSON, PrettyPrinting, UUIDs, DataFrames, DataStructures, Base64
using GeneralUtils, SQLLLM using GeneralUtils, SQLLLM
config = copy(JSON3.read("/appfolder/mountvolume/appdata/config.json")) config = JSON.parse("/appfolder/app/dev/YiemAgent/test/config.json")
function executeSQL(sql::T) where {T<:AbstractString} function executeSQL(sql::T) where {T<:AbstractString}
host = config[:externalservice][:wineDB][:host] host = config[:externalservice][:wineDB][:host]
@@ -29,13 +29,19 @@ function executeSQLVectorDB(sql)
return result return result
end end
function text2textInstructLLM(prompt::String; maxattempt=3) function text2textInstructLLM(prompt::String; maxattempt::Integer=3, modelsize::String="medium",
senderId=GeneralUtils.uuid4snakecase(), timeout=180,
llmkwargs=Dict(
:num_ctx => 32768,
:temperature => 0.5,
)
)
msgMeta = GeneralUtils.generate_msgMeta( msgMeta = GeneralUtils.generate_msgMeta(
config[:externalservice][:loadbalancer][:mqtttopic]; config[:externalservice][:loadbalancer][:mqtttopic];
msgPurpose="inference", msgPurpose="inference",
senderName="yiemagent", senderName="yiemagent",
senderId=sessionId, senderId=senderId,
receiverName="text2textinstruct_small", receiverName="text2textinstruct_$modelsize",
mqttBrokerAddress=config[:mqttServerInfo][:broker], mqttBrokerAddress=config[:mqttServerInfo][:broker],
mqttBrokerPort=config[:mqttServerInfo][:port], mqttBrokerPort=config[:mqttServerInfo][:port],
) )
@@ -44,16 +50,13 @@ function text2textInstructLLM(prompt::String; maxattempt=3)
:msgMeta => msgMeta, :msgMeta => msgMeta,
:payload => Dict( :payload => Dict(
:text => prompt, :text => prompt,
:kwargs => Dict( :kwargs => llmkwargs
:num_ctx => 16384,
:temperature => 0.2,
)
) )
) )
response = nothing response = nothing
for attempts in 1:maxattempt for attempts in 1:maxattempt
_response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=300, maxattempt=2) _response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=timeout, maxattempt=maxattempt)
payload = _response[:response] payload = _response[:response]
if _response[:success] && payload[:text] !== nothing if _response[:success] && payload[:text] !== nothing
response = _response[:response][:text] response = _response[:response][:text]
@@ -76,7 +79,7 @@ function getEmbedding(text::T) where {T<:AbstractString}
msgPurpose="embedding", msgPurpose="embedding",
senderName="yiemagent", senderName="yiemagent",
senderId=sessionId, senderId=sessionId,
receiverName="text2textinstruct_small", receiverName="textembedding",
mqttBrokerAddress=config[:mqttServerInfo][:broker], mqttBrokerAddress=config[:mqttServerInfo][:broker],
mqttBrokerPort=config[:mqttServerInfo][:port], mqttBrokerPort=config[:mqttServerInfo][:port],
) )
@@ -87,7 +90,8 @@ function getEmbedding(text::T) where {T<:AbstractString}
:text => [text] # must be a vector of string :text => [text] # must be a vector of string
) )
) )
response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=120)
response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=120, maxattempt=3)
embedding = response[:response][:embeddings] embedding = response[:response][:embeddings]
return embedding return embedding
end end
@@ -108,7 +112,6 @@ function findSimilarTextFromVectorDB(text::T1, tablename::T2, embeddingColumnNam
return df return df
end end
function similarSQLVectorDB(query; maxdistance::Integer=100) function similarSQLVectorDB(query; maxdistance::Integer=100)
tablename = "sqlllm_decision_repository" tablename = "sqlllm_decision_repository"
# get embedding of the query # get embedding of the query
@@ -131,11 +134,10 @@ function similarSQLVectorDB(query; maxdistance::Integer=100)
end end
end end
function insertSQLVectorDB(query::T1, SQL::T2; maxdistance::Integer=3) where {T1<:AbstractString, T2<:AbstractString} function insertSQLVectorDB(query::T1, SQL::T2; maxdistance::Integer=3) where {T1<:AbstractString, T2<:AbstractString}
tablename = "sqlllm_decision_repository" tablename = "sqlllm_decision_repository"
# get embedding of the query # get embedding of the query
# query = state[:thoughtHistory][:question] # query = state[:action_history][:question]
df = findSimilarTextFromVectorDB(query, tablename, df = findSimilarTextFromVectorDB(query, tablename,
"function_input_embedding", executeSQLVectorDB) "function_input_embedding", executeSQLVectorDB)
row, col = size(df) row, col = size(df)
@@ -155,18 +157,77 @@ function insertSQLVectorDB(query::T1, SQL::T2; maxdistance::Integer=3) where {T1
end end
end end
sessionId = "555"
function similarSommelierDecision(recentevents::T1; maxdistance::Integer=3
)::Union{AbstractDict, Nothing} where {T1<:AbstractString}
tablename = "sommelier_decision_repository"
# find similar
println("\n~~~ search vectorDB for this: $recentevents ", @__FILE__, " ", @__LINE__)
df = findSimilarTextFromVectorDB(recentevents, tablename,
"function_input_embedding", executeSQLVectorDB)
row, col = size(df)
distance = row == 0 ? Inf : df[1, :distance]
if row != 0 && distance < maxdistance
# if there is usable decision, return it.
rowid = df[1, :id]
println("\n~~~ found similar decision. row id $rowid, distance $distance ", @__FILE__, " ", @__LINE__)
output_b64 = df[1, :function_output_base64] # pick the closest match
_output_str = String(base64decode(output_b64))
output = copy(JSON.parse(_output_str))
return output
else
println("\n~~~ similar decision not found, max distance $maxdistance ", @__FILE__, " ", @__LINE__)
return nothing
end
end
function insertSommelierDecision(recentevents::T1, decision::T2; maxdistance::Integer=5
) where {T1<:AbstractString, T2<:AbstractDict}
tablename = "sommelier_decision_repository"
# find similar
df = findSimilarTextFromVectorDB(recentevents, tablename,
"function_input_embedding", executeSQLVectorDB)
row, col = size(df)
distance = row == 0 ? Inf : df[1, :distance]
if row == 0 || distance > maxdistance # no close enough SQL stored in the database
recentevents_embedding = getEmbedding(recentevents)[1]
recentevents = replace(recentevents, "'" => "")
decision_json = JSON.json(decision)
decision_base64 = base64encode(decision_json)
decision = replace(decision_json, "'" => "")
sql = """
INSERT INTO $tablename (function_input, function_output, function_output_base64, function_input_embedding) VALUES ('$recentevents', '$decision', '$decision_base64', '$recentevents_embedding');
"""
println("\n~~~ added new decision to vectorDB ", @__FILE__, " ", @__LINE__)
println(sql)
_ = executeSQLVectorDB(sql)
else
println("~~~ similar decision previously cached, distance $distance ", @__FILE__, " ", @__LINE__)
end
end
sessionId = GeneralUtils.uuid4snakecase()
d = Dict(:id => sessionId)
filepath = "/appfolder/app/sessionid.json"
open(filepath, "w") do io
JSON.pretty(io, d)
end
# query = "How many German wines do you have?" # query = "How many German wines do you have?"
# highValueStateList = copy(JSON3.read("/appfolder/app/highValueState_1.json")) # highValueStateList = copy(JSON.parse("/appfolder/app/highValueState_1.json"))
# selectedState = SQLLLM.compareState(query, highValueStateList, text2textInstructLLM) # selectedState = SQLLLM.compareState(query, highValueStateList, text2textInstructLLM)
# query = Dict(:text=> "How many wines from France do you have that can be paired with lamb?") # query = Dict(:text=> "How many wines from France do you have that can be paired with lamb?")
query = "How many French wines from Yiem store under 100 dollars do you have?" # query = "How many French wines from Yiem store under 100 dollars do you have?"
# query = "retailer: Yiem, wine_type: red, sweetness: 1-2, intensity: 4-5, wine price: 20-40" # query = "retailer: Yiem, wine_type: red, sweetness: 1-2, intensity: 4-5, wine price: 20-40"
query = "from Yiem retailer, red wine from France. price 100 to 1000 USD. sweetness: 1-2, intensity: 4-5"
# query = "wine_type: white, country: United States, sweetness: 1-2, tannin: 3, food to be served with wine: pizza" # query = "wine_type: white, country: United States, sweetness: 1-2, tannin: 3, food to be served with wine: pizza"
# query = "wine_type: white, country: Austria, food to be served with wine: pork" # query = "wine_type: white, country: Austria, food to be served with wine: pork"
# query = "wine price: less than 25, wine_type: rose, country: France, sweetness: 2, tannin: 3, food to be served with wine: pizza" # query = "wine price: less than 25, wine_type: rose, country: France, sweetness: 2, tannin: 3, food to be served with wine: pizza"
@@ -291,8 +352,8 @@ SELECT * FROM wine WHERE wine_type = 'red' AND country = 'France' AND sweetness
# :evaluation => # :evaluation =>
# "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 => # :action_history =>
# 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")
+2 -2
View File
@@ -1,5 +1,5 @@
using Revise using Revise
using LibPQ, JSON3, PrettyPrinting, UUIDs, DataFrames, DataStructures, Dates, MQTTClient, Random using LibPQ, JSON, PrettyPrinting, UUIDs, DataFrames, DataStructures, Dates, MQTTClient, Random
using SQLLLM, GeneralUtils using SQLLLM, GeneralUtils
@@ -39,7 +39,7 @@ df = DataFrame(result)
config = copy(JSON3.read("config.json")) config = copy(JSON.parse("config.json"))
msgMeta = GeneralUtils.generate_msgMeta( msgMeta = GeneralUtils.generate_msgMeta(
config[:externalservice][:text2textinstruct][:mqtttopic]; config[:externalservice][:text2textinstruct][:mqtttopic];
+2 -2
View File
@@ -1,8 +1,8 @@
# using Revise # using Revise
# using SQLLLM, LLMMCTS, DataStructures, JSON3 # using SQLLLM, LLMMCTS, DataStructures, JSON
# query = "How many German wines do you have?" # query = "How many German wines do you have?"
# highValueStateList = copy(JSON3.read("/appfolder/app/highValueState_1.json")) # highValueStateList = copy(JSON.parse("/appfolder/app/highValueState_1.json"))
# selectedState = SQLLLM.compareState(query, highValueStateList) # selectedState = SQLLLM.compareState(query, highValueStateList)