Compare commits

10 Commits

Author SHA1 Message Date
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
4 changed files with 685 additions and 340 deletions

View File

@@ -102,8 +102,8 @@ Dict(
# Signature # Signature
""" """
function decisionMaker(state::T1, context, text2textInstructLLM::Function, function decisionMaker(state::T1, additionalinfo, text2textInstructLLM::Function, llmFormatName::String
; querySQLVectorDBF::Union{T2, Nothing}=nothing ; querySQLVectorDBF::Union{T2, Nothing}=nothing, maxattempt=10
)::Dict{Symbol, Any} where {T1<:AbstractDict, T2<:Function} )::Dict{Symbol, Any} where {T1<:AbstractDict, T2<:Function}
# lessonDict = # lessonDict =
@@ -135,19 +135,14 @@ function decisionMaker(state::T1, context, text2textInstructLLM::Function,
systemmsg = systemmsg =
""" """
You are a helpful assistant that find the data from a database to satisfy the user's query. You are a helpful assistant that find the data from a database to satisfy the user's question.
You are also eager to improve your helpfulness. You are working under your mentor supervision and you are also eager to improve your helpfulness.
For your information: For your information:
- Observation: Result of the immediately preceding action - Observation: Result of the immediately preceding action
At each round of conversation, the user will give you the following: At each round of conversation, you will be given the following information:
User Query: ... context: additional information about the current situation
Example: ...
Your Q&A: ...
Your work progress: ...
Evaluation: Evaluation of the immediately preceding action and observation
Suggestion: Suggestion for the immediately preceding action and observation
You must follow the following guidelines: You must follow the following guidelines:
- Keep SQL queries focused only on the provided information. - Keep SQL queries focused only on the provided information.
@@ -156,28 +151,28 @@ function decisionMaker(state::T1, context, text2textInstructLLM::Function,
- Do not create any table in the database - Do not create any table in the database
- A junction table can be used to link tables together. Another use case is for filtering data. - A junction table can be used to link tables together. Another use case is for filtering data.
- If you can't find a single table that can be used to answer the user's query, try joining multiple tables to see if you can obtain the answer. - If you can't find a single table that can be used to answer the user's query, try joining multiple tables to see if you can obtain the answer.
- If you are unable to find the requested information, kindly inform the user, "The current data in our database does not provide the specific answer to your query".
- Text information in the database usually stored in lower case. If your search returns empty, try using lower case to search. - Text information in the database usually stored in lower case. If your search returns empty, try using lower case to search.
- If there is no search result from the database, remove the restrictive criteria until a search result is available, and proceed from there.
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:
Comprehension: state your comprehension about the current situation. 1) 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. 2) action_name: (Typically corresponds to the execution of the first step in your plan)
Action_name: (Typically corresponds to the execution of the first step in your plan)
Can be one of the following function names: Can be one of the following function names:
- RUNSQL, which you can use to execute SQL against the database. Action_input for this function must be a single SQL query to be executed against the database. - RUNSQL, which you can use to execute SQL against the database. Action_input for this function must be a single SQL query to be executed against the database.
For more effective text search, it's necessary to use case-insensitivity and the ILIKE operator. For more effective text search, it's necessary to use case-insensitivity and the ILIKE operator.
Do not wrap the SQL as it will be executed against the database directly and SQL must be ended with ';'. Do not wrap the SQL as it will be executed against the database directly and SQL must be ended with ';'.
4) Action_input: Input to the action 3) action_input: Input to the action
You should only respond in format as described below: You should only respond in JSON format as described below:
Comprehension: ... {
Plan: ... "plan": "...",
Action_name: ... "action_name": "...",
Action_input: ... "action_input": "..."
}
Let's begin! Let's begin!
""" """
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]
@@ -195,105 +190,77 @@ function decisionMaker(state::T1, context, text2textInstructLLM::Function,
similarSQL_ = sql !== nothing ? sql : "None" similarSQL_ = sql !== nothing ? sql : "None"
end end
header = ["Comprehension:", "Plan:", "Action_name:", "Action_input:"]
dictkey = ["comprehension", "plan", "action_name", "action_input"]
llmkwargs=Dict( for attempt in 1:maxattempt
:num_ctx => 32768,
:temperature => 0.1,
)
for attempt in 1:10 # QandA = generatequestion(state, context, text2textInstructLLM, llmFormatName; similarSQL=similarSQL_)
if attempt > 1
println("\nERROR SQLLLM decisionMaker() attempt $attempt/10 ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
llmkwargs[:temperature] = 0.1 * attempt
end
QandA = generatequestion(state, context, text2textInstructLLM; similarSQL=similarSQL_) context =
usermsg =
""" """
$(context[:tablelist]) <context>
User query: $(state[:thoughtHistory][:question]) <table_schema> This is schema of tables in the database:
Example: $similarSQL_ $(additionalinfo[:tablelist])
Your Q&A: $QandA </table_schema>
Your work progress: $workprogress <most_relevant_SQL> The closest known SQL for this question is:
Evaluation: $(state[:evaluation]) $similarSQL_
Suggestion: $(state[:suggestion]) </most_relevant_SQL>
P.S. $errornote <query_result_of_most_relevant_SQL> This is the query result when executing the most_relevant_SQL against a database. You can use this to see how the data are stored.
winery: Chateau Montelena, wine_name: The Montelena Estate Cabernet Sauvignon, wine_id: 97264f71-007c-4cce-a3fe-2cc88fba4d05, vintage: 2017, region: Napa Valley, country: United States, wine_type: red, grape: Cabernet Sauvignon, serving_temperature: 15 to 18 Celsius, sweetness: 1, intensity: 5, tannin: 4, acidity: 4, tasting_notes: oak, vanilla, tobacco, blackberry, plum, black cherry, leather, earthy, smoke, price: 19.95, currency: USD
</query_result_of_most_relevant_SQL>
<progress> your work progress so far:
$workprogress
</progress>
<suggestion> This is your mentor's suggestion for the immediately preceding action and observation
$(state[:suggestion])
</suggestion>
P.S. $errornote
</context>
""" """
_prompt = unformatPrompt =
[ [
Dict(:name=> "system", :text=> systemmsg), Dict(:name => "system", :text => systemmsg),
Dict(:name=> "user", :text=> usermsg) Dict(:name => "user", :text => state[:thoughtHistory][:question])
] ]
# put in model format # put in model format
prompt = GeneralUtils.formatLLMtext(_prompt, "granite3") prompt = GeneralUtils.formatLLMtext(unformatPrompt, llmFormatName)
response = text2textInstructLLM(prompt; llmkwargs=llmkwargs) # add info
response = GeneralUtils.deFormatLLMtext(response, "granite3") prompt = prompt * context
response = text2textInstructLLM(prompt)
response = GeneralUtils.deFormatLLMtext(response, llmFormatName)
think, response = GeneralUtils.extractthink(response)
# LLM tends to generate observation given that it is in the input # if occursin("NULL", response)
response = # errornote = "\nYour previous attempt contain NULL. It is not allowed in your response"
if occursin("observation:", response) # println("\nERROR SQLLLM decisionMaker(). Attempt $attempt/$maxattempt. $errornote --(not qualify response)--> \n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
string(split(response, "observation:")[1]) # continue
elseif occursin("Observation:", response)
string(split(response, "Observation:")[1])
elseif occursin("observation_", response)
string(split(response, "observation_")[1])
elseif occursin("Observation_", response)
string(split(response, "Observation_")[1])
else
response
end
# sometime LLM output something like **Comprehension**: which is not expected
response = replace(response, "**"=>"")
response = replace(response, "***"=>"")
# some time LLM output Plan_1: so we need to detect and replace topic numbering
regex = r"_[0-1000]+:"
matches = collect(eachmatch(regex, response))
for m in matches
response = replace(response, string(m.match)=>":")
end
if occursin("NULL", response)
errornote = "\nYour previous attempt was NULL. This is not allowed"
println("\nERROR SQLLLM decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
end
# # detect if there are more than 1 key per categories
# wordcount = GeneralUtils.countGivenWords(response, header)
# duplicateKeywordFlag = false
# for (i, v) in enumerate(wordcount)
# keyword = header[i]
# keywordNumber = v
# if keywordNumber > 1
# errornote = "\nSQL query has duplicated keyword, $keyword"
# println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# duplicateKeywordFlag = true
# break
# end
# end # end
# duplicateKeywordFlag == true ? continue : nothing
# check whether response has all header responsedict = nothing
detected_kw = GeneralUtils.detect_keyword(header, response) try
if 0 values(detected_kw) responsedict = copy(JSON3.read(response))
errornote = "\nYour previous attempt did not have all points according to the required response format" catch
println("\nERROR SQLLLM decisionMaker() $errornote \n$response", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR YiemAgent generatechat() failed to parse response: $response", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
elseif sum(values(detected_kw)) > length(header)
errornote = "\nYour previous attempt has duplicated points according to the required response format"
println("\nERROR SQLLLM decisionMaker() $errornote \n$response", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
responsedict = GeneralUtils.textToDict(response, header; # check whether all answer's key points are in responsedict
dictKey=dictkey, symbolkey=true) _responsedictKey = keys(responsedict)
responsedictKey = [i for i in _responsedictKey] # convert into a list
is_requiredKeys_in_responsedictKey = [i responsedictKey for i in requiredKeys]
if length(is_requiredKeys_in_responsedictKey) > length(requiredKeys)
errornote = "Your previous attempt has more key points than answer's required key points."
println("\nERROR YiemAgent generatechat() $errornote --(not qualify response)--> $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
elseif !all(is_requiredKeys_in_responsedictKey)
zeroind = findall(x -> x == 0, is_requiredKeys_in_responsedictKey)
missingkeys = [requiredKeys[i] for i in zeroind]
errornote = "$missingkeys are missing from your previous response"
println("\nERROR YiemAgent generatechat() $errornote --(not qualify response)--> $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue
end
delete!(responsedict, :observation) delete!(responsedict, :observation)
@@ -311,35 +278,296 @@ function decisionMaker(state::T1, context, text2textInstructLLM::Function,
toollist = ["TABLEINFO", "RUNSQL"] toollist = ["TABLEINFO", "RUNSQL"]
if responsedict[:action_name] toollist if responsedict[:action_name] toollist
errornote = "\nYour 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() $errornote ", @__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 = "\nYour 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() $errornote ", @__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
for i Symbol.(dictkey) # for i ∈ Symbol.(dictkey)
if length(JSON3.write(responsedict[i])) == 0 # if length(JSON3.write(responsedict[i])) == 0
errornote = "\nYour previous attempt has empty value for $i" # errornote = "Your previous attempt has empty value for $i"
println("\nERROR SQLLLM decisionMaker() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") # println("\nERROR SQLLLM decisionMaker(). Attempt $attempt/$maxattempt. $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue # continue
end # end
end # end
state[:decisionMaker] = responsedict println("\nSQLLLM decisionMaker() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
pprintln(Dict(responsedict))
# store for later training
responsedict[:thoughthistory] = state[:thoughtHistory]
responsedict[:system] = systemmsg
responsedict[:prompt] = prompt
responsedict[:context] = context
responsedict[:think] = think
# # read sessionId
# sessionid = JSON3.read("/appfolder/app/sessionid.json")
# # save to filename ./log/decisionlog.txt
# println("saving SQLLLM decisionMaker() to disk")
# filename = "agent_decision_log_$(sessionid[:id]).json"
# filepath = "/appfolder/app/log/$filename"
# # check whether there is a file path exists before writing to it
# if !isfile(filepath)
# decisionlist = [responsedict]
# println("Creating file $filepath")
# open(filepath, "w") do io
# JSON3.pretty(io, decisionlist)
# end
# else
# # read the file and append new data
# decisionlist = copy(JSON3.read(filepath))
# push!(decisionlist, responsedict)
# println("Appending new data to file $filepath")
# open(filepath, "w") do io
# JSON3.pretty(io, decisionlist)
# end
# end
return responsedict return responsedict
end end
error("SQLLLM DecisionMaker() failed to generate a thought \n", response) error("SQLLLM DecisionMaker() failed to generate a thought \n", response)
end end
# function decisionMaker(state::T1, context, text2textInstructLLM::Function, llmFormatName::String
# ; querySQLVectorDBF::Union{T2, Nothing}=nothing, maxattempt=10
# )::Dict{Symbol, Any} where {T1<:AbstractDict, T2<:Function}
# # lessonDict =
# # if isfile("lesson.json")
# # lessonDict = copy(JSON3.read("lesson.json"))
# # else
# # lessonDict = nothing
# # end
# # lessonDict = nothing
# # lesson =
# # if lessonDict === nothing
# # ""
# # else
# # """
# # You have attempted to help the user before and failed, either because your reasoning for the
# # recommendation was incorrect or your response did not exactly match the user expectation.
# # The following lesson(s) give a plan to avoid failing to help the user in the same way you
# # did previously. Use them to improve your strategy to help the user.
# # Here are some lessons in JSON format:
# # $(JSON3.write(lessonDict))
# # When providing the thought and action for the current trial, that into account these failed
# # trajectories and make sure not to repeat the same mistakes and incorrect answers.
# # """
# # end
# systemmsg =
# """
# You are a helpful assistant that find the data from a database to satisfy the user's query.
# You are also eager to improve your helpfulness.
# For your information:
# - Observation: Result of the immediately preceding action
# At each round of conversation, the user will give you the following:
# User Query: ...
# Example: ...
# Your Q&A: ...
# Your work progress: ...
# Evaluation: Evaluation of the immediately preceding action and observation
# Suggestion: Suggestion for the immediately preceding action and observation
# You must follow the following guidelines:
# - Keep SQL queries focused only on the provided information.
# You should follow the following guidelines:
# - Do not create any table in the database
# - A junction table can be used to link tables together. Another use case is for filtering data.
# - If you can't find a single table that can be used to answer the user's query, try joining multiple tables to see if you can obtain the answer.
# - If you are unable to find the requested information, kindly inform the user, "The current data in our database does not provide the specific answer to your query".
# - Text information in the database usually stored in lower case. If your search returns empty, try using lower case to search.
# You should then respond to the user with interleaving Comprehension, Plan, Action_name, Action_input:
# 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:
# - RUNSQL, which you can use to execute SQL against the database. Action_input for this function must be a single SQL query to be executed against the database.
# For more effective text search, it's necessary to use case-insensitivity and the ILIKE operator.
# Do not wrap the SQL as it will be executed against the database directly and SQL must be ended with ';'.
# 4) Action_input: Input to the action
# You should only respond in format as described below:
# Plan: ...
# Action_name: ...
# Action_input: ...
# Let's begin!
# """
# workprogress = ""
# for (k, v) in state[:thoughtHistory]
# if k ∉ [:question]
# workprogress *= "$k: $v\n"
# end
# end
# response = nothing # store for show when error msg show up
# errornote = "N/A"
# # provide similar sql only for the first attempt
# similarSQL_ = "None"
# if length(state[:thoughtHistory]) == 1
# sql, distance = querySQLVectorDBF(state[:thoughtHistory][:question])
# similarSQL_ = sql !== nothing ? sql : "None"
# end
# header = ["Plan:", "Action_name:", "Action_input:"]
# dictkey = ["plan", "action_name", "action_input"]
# llmkwargs=Dict(
# :num_ctx => 32768,
# :temperature => 0.5,
# )
# for attempt in 1:maxattempt
# QandA = generatequestion(state, context, text2textInstructLLM, llmFormatName; similarSQL=similarSQL_)
# usermsg =
# """
# $(context[:tablelist])
# User query: $(state[:thoughtHistory][:question])
# Example: $similarSQL_
# Your Q&A: $QandA
# Your work progress: $workprogress
# Evaluation: $(state[:evaluation])
# Suggestion: $(state[:suggestion])
# P.S. $errornote
# """
# _prompt =
# [
# Dict(:name=> "system", :text=> systemmsg),
# Dict(:name=> "user", :text=> usermsg)
# ]
# # put in model format
# prompt = GeneralUtils.formatLLMtext(_prompt, llmFormatName)
# response = text2textInstructLLM(prompt; llmkwargs=llmkwargs)
# response = GeneralUtils.deFormatLLMtext(response, llmFormatName)
# think, response = GeneralUtils.extractthink(response)
# # LLM tends to generate observation given that it is in the input
# response =
# if occursin("observation:", response)
# string(split(response, "observation:")[1])
# elseif occursin("Observation:", response)
# string(split(response, "Observation:")[1])
# elseif occursin("observation_", response)
# string(split(response, "observation_")[1])
# elseif occursin("Observation_", response)
# string(split(response, "Observation_")[1])
# else
# response
# end
# # sometime LLM output something like **Comprehension**: which is not expected
# response = replace(response, "**"=>"")
# response = replace(response, "***"=>"")
# # some time LLM output Plan_1: so we need to detect and replace topic numbering
# regex = r"_[0-1000]+:"
# matches = collect(eachmatch(regex, response))
# for m in matches
# response = replace(response, string(m.match)=>":")
# end
# if occursin("NULL", response)
# errornote = "\nYour previous attempt was NULL. This is not allowed"
# println("\nERROR SQLLLM decisionMaker(). Attempt $attempt/$maxattempt. $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue
# end
# # # detect if there are more than 1 key per categories
# # wordcount = GeneralUtils.countGivenWords(response, header)
# # duplicateKeywordFlag = false
# # for (i, v) in enumerate(wordcount)
# # keyword = header[i]
# # keywordNumber = v
# # if keywordNumber > 1
# # errornote = "\nSQL query has duplicated keyword, $keyword"
# # println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# # duplicateKeywordFlag = true
# # break
# # end
# # end
# # duplicateKeywordFlag == true ? continue : nothing
# # check whether response has all header
# detected_kw = GeneralUtils.detect_keyword(header, response)
# if 0 ∈ values(detected_kw)
# errornote = "\nYour previous attempt did not have all points according to the required response format"
# println("\nERROR SQLLLM decisionMaker(). Attempt $attempt/$maxattempt. $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue
# elseif sum(values(detected_kw)) > length(header)
# errornote = "\nYour previous attempt has duplicated points according to the required response format"
# println("\nERROR SQLLLM decisionMaker(). Attempt $attempt/$maxattempt. $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue
# end
# responsedict = GeneralUtils.textToDict(response, header;
# dictKey=dictkey, symbolkey=true)
# delete!(responsedict, :observation)
# # remove backticks Error occurred: MethodError: no method matching occursin(::String, ::Vector{String})
# if occursin("```", responsedict[:action_input])
# sql = GeneralUtils.extract_triple_backtick_text(responsedict[:action_input])[1]
# if sql[1:4] == "sql\n"
# sql = sql[5:end]
# end
# sql = split(sql, ';') # some time there are comments in the sql
# sql = sql[1] * ';'
# responsedict[:action_input] = sql
# end
# toollist = ["TABLEINFO", "RUNSQL"]
# if responsedict[:action_name] ∉ toollist
# errornote = "Your previous attempt has action_name that is not in the tool list"
# println("\nERROR SQLLLM decisionMaker(). Attempt $attempt/$maxattempt. $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue
# end
# for i in toollist
# if occursin(i, responsedict[:action_input])
# errornote = "Your previous attempt has action_name in action_input which is not allowed"
# println("\nERROR SQLLLM decisionMaker(). Attempt $attempt/$maxattempt. $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue
# end
# end
# for i ∈ Symbol.(dictkey)
# if length(JSON3.write(responsedict[i])) == 0
# errornote = "Your previous attempt has empty value for $i"
# println("\nERROR SQLLLM decisionMaker(). Attempt $attempt/$maxattempt. $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# continue
# end
# end
# state[:decisionMaker] = responsedict
# return responsedict
# end
# error("SQLLLM DecisionMaker() failed to generate a thought \n", response)
# end
""" Assigns a scalar value to each new child node to be used for selec- """ Assigns a scalar value to each new child node to be used for selec-
tion and backpropagation. This value effectively quantifies the agent's progress in task completion, tion and backpropagation. This value effectively quantifies the agent's progress in task completion,
@@ -361,7 +589,8 @@ julia>
# Signature # Signature
""" """
function evaluator(state::T1, text2textInstructLLM::Function; maxattempt=10 function evaluator(state::T1, thoughtDict, text2textInstructLLM::Function, llmFormatName::String;
maxattempt=10
) where {T1<:AbstractDict} ) where {T1<:AbstractDict}
systemmsg = systemmsg =
@@ -370,8 +599,6 @@ function evaluator(state::T1, text2textInstructLLM::Function; maxattempt=10
Definitions: Definitions:
"question" is the user's question "question" is the user's question
"understanding" is agent's understanding about the current situation
"reasoning" is agent's step-by-step reasoning about the current situation
"plan" is agent's plan to complete the task from the current situation "plan" is agent's plan to complete the task from the current situation
"action_name" is the name of the action taken, which can be one of the following functions: "action_name" is the name of the action taken, which can be one of the following functions:
- RUNSQL, which you can use to execute SQL against the database. Action_input for this function must be a single SQL query to be executed against the database. - RUNSQL, which you can use to execute SQL against the database. Action_input for this function must be a single SQL query to be executed against the database.
@@ -380,16 +607,14 @@ function evaluator(state::T1, text2textInstructLLM::Function; maxattempt=10
"action_input" is the input to the action "action_input" is the input to the action
"observation" is result of the preceding immediate action "observation" is result of the preceding immediate action
<At each round of conversation, the user will give you> At each round of conversation, you will be given the following information:
Trajectory: ... trajectory: A history of how you worked on the question chronologically
Error_note: error note from your previous attempt evaluatee_context: The context that evaluatee use to make a decision
</At each round of conversation, the user will give you>
<You must follow the following guidelines> You must follow the following guidelines:
- When the search returns no result, validate whether the SQL query makes sense before accepting it as a valid answer. - When the search returns no result, validate whether the SQL query makes sense before accepting it as a valid answer.
</You must follow the following guidelines>
<You should then respond to the user with> You should then respond to the user with:
1) Trajectory_evaluation: Analyze the trajectory of a solution to answer the user's original question. 1) Trajectory_evaluation: Analyze the trajectory of a solution to answer the user's original question.
- Evaluate the correctness of each section and the overall trajectory based on the given question. - Evaluate the correctness of each section and the overall trajectory based on the given question.
- Provide detailed reasoning and analysis, focusing on the latest thought, action, and observation. - Provide detailed reasoning and analysis, focusing on the latest thought, action, and observation.
@@ -397,7 +622,7 @@ function evaluator(state::T1, text2textInstructLLM::Function; maxattempt=10
- Do not generate additional thoughts or actions. - Do not generate additional thoughts or actions.
2) Answer_evaluation: 2) Answer_evaluation:
- Focus only on the matter mentioned in the question and comprehensively analyze how the latest observation's details addresses the question - Focus only on the matter mentioned in the question and comprehensively analyze how the latest observation's details addresses the question
3) Accepted_as_answer: Decide whether the latest observation's content answers the question. Can be "Yes" or "No" 3) Accepted_as_answer: Decide whether the latest observation's content answers the question. Can be "yes" or "no"
Bad example (The observation didn't answers the question): Bad example (The observation didn't answers the question):
question: Find cars with 4 wheels. question: Find cars with 4 wheels.
observation: There are an apple in the table. observation: There are an apple in the table.
@@ -407,66 +632,77 @@ function evaluator(state::T1, text2textInstructLLM::Function; maxattempt=10
4) Score: Correctness score s where s is a single integer between 0 to 9. 4) Score: Correctness score s where s is a single integer between 0 to 9.
For example: For example:
- 0 indicates that both the trajectory is incorrect, failed or errors and the observation is incorrect or failed - 0 indicates that both the trajectory is incorrect, failed or errors and the observation is incorrect or failed
- 4 indicates that the trajectory are correct but the observation is incorrect or failed - 4 indicates that the trajectory are correct, but no results are returned.
- 5 indicates that the trajectory are correct, but no results are returned. - 5 indicates that the trajectory are correct but the observation is incorrect or failed
- 6 indicates that the trajectory are correct, but the observation's content doesn't directly answer the question - 6 indicates that the trajectory are correct, but the observation's content doesn't directly answer the question
- 8 indicates that both the trajectory are correct, and the observation's content directly answers the question. - 8 indicates that both the trajectory are correct, and the observation's content directly answers the question.
- 9 indicates a perfect perfomance. Both the trajectory are correct, and the observation's content directly answers the question, surpassing your expectations. - 9 indicates a perfect perfomance. Both the trajectory are correct, and the observation's content directly answers the question, surpassing your expectations.
5) Suggestion: if accepted_as_answer is "No", provide suggestion. 5) Suggestion: what are the possible reason of this outcome, what can one learn from it and what suggestion can made?
</You should then respond to the user with>
<You should only respond in format as described below> You should only respond in format as described below:
Trajectory_evaluation: ... Trajectory_evaluation: ...
Answer_evaluation: ... Answer_evaluation: ...
Accepted_as_answer: ... Accepted_as_answer: ...
Score: ... Score: ...
Suggestion: ... Suggestion: ...
</You should only respond in format as described below>
Let's begin! Let's begin!
""" """
#[WORKING] add what I should think --> this will be the think for decisionMaker()
header = ["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
errornote = "" errornote = "N/A"
for attempt in 1:maxattempt for attempt in 1:maxattempt
usermsg = usermsg =
""" """
Trajectory: $thoughthistory <trajectory>
P.S. $errornote $thoughthistory
</trajectory>
"""
context =
"""
<context>
<evaluatee_context>
thoughtDict[:context]
</evaluatee_context>
P.S. $errornote
</context>
""" """
_prompt = unformatPrompt =
[ [
Dict(:name=> "system", :text=> systemmsg), Dict(:name => "system", :text => systemmsg),
Dict(:name=> "user", :text=> usermsg) Dict(:name => "user", :text => usermsg)
] ]
# put in model format # put in model format
prompt = GeneralUtils.formatLLMtext(_prompt, "granite3") prompt = GeneralUtils.formatLLMtext(unformatPrompt, llmFormatName)
# add info
header = ["Trajectory_evaluation:", "Answer_evaluation:", "Accepted_as_answer:", "Score:", "Suggestion:"] prompt = prompt * context
dictkey = ["trajectory_evaluation", "answer_evaluation", "accepted_as_answer", "score", "suggestion"]
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)
# sometime LLM output something like **Comprehension**: which is not expected # sometime LLM output something like **Comprehension**: which is not expected
response = replace(response, "**"=>"") response = replace(response, "**"=>"")
response = replace(response, "***"=>"") response = replace(response, "***"=>"")
# check whether response has all header # check whether response has all header
detected_kw = GeneralUtils.detect_keyword(header, response) detected_kw = GeneralUtils.detectKeywordVariation(header, response)
if 0 values(detected_kw) missingkeys = [k for (k, v) in detected_kw if v === nothing]
errornote = "Your previous attempt does not have all answer points" if !isempty(missingkeys)
println("\nERROR SQLLLM evaluator() Attempt $attempt/$maxattempt. $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") errornote = "$missingkeys are missing from your previous response"
println("\nERROR SQLLLM extractContent_dataframe() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
elseif sum(values(detected_kw)) > length(header) elseif sum([length(i) for i in values(detected_kw)]) > length(header)
errornote = "Your previous attempt has duplicated answer point" errornote = "\nYour previous attempt has duplicated points according to the required response format"
println("\nERROR SQLLLM evaluator() Attempt $attempt/$maxattempt. $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR SQLLLM extractContent_dataframe() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
@@ -484,9 +720,9 @@ function evaluator(state::T1, text2textInstructLLM::Function; maxattempt=10
accepted_as_answer::AbstractString = responsedict[:accepted_as_answer] accepted_as_answer::AbstractString = responsedict[:accepted_as_answer]
if accepted_as_answer ["Yes", "No"] # [PENDING] add errornote into the prompt 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 ", @__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
@@ -497,7 +733,7 @@ function evaluator(state::T1, text2textInstructLLM::Function; maxattempt=10
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" 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
@@ -505,9 +741,41 @@ function evaluator(state::T1, text2textInstructLLM::Function; maxattempt=10
# 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("\nEvaluator() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
println("\nSQLLLM evaluator() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
pprintln(Dict(responsedict)) pprintln(Dict(responsedict))
# # store for later training
# responsedict[:thoughthistory] = state[:thoughtHistory]
# responsedict[:system] = systemmsg
# responsedict[:usermsg] = usermsg
# responsedict[:prompt] = prompt
# responsedict[:context] = context
# responsedict[:think] = think
# # read sessionId
# sessionid = JSON3.read("/appfolder/app/sessionid.json")
# # save to filename ./log/decisionlog.txt
# println("saving SQLLLM evaluator() to disk")
# filename = "agent_evaluator_log_$(sessionid[:id]).json"
# filepath = "/appfolder/app/log/$filename"
# # check whether there is a file path exists before writing to it
# if !isfile(filepath)
# decisionlist = [responsedict]
# println("Creating file $filepath")
# open(filepath, "w") do io
# JSON3.pretty(io, decisionlist)
# end
# else
# # read the file and append new data
# decisionlist = copy(JSON3.read(filepath))
# push!(decisionlist, responsedict)
# println("Appending new data to file $filepath")
# open(filepath, "w") do io
# JSON3.pretty(io, decisionlist)
# 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|>")
@@ -705,15 +973,17 @@ function transition(state::T, args::NamedTuple
decisionMakerF::Function = args[:decisionMaker] decisionMakerF::Function = args[:decisionMaker]
evaluatorF::Function = args[:evaluator] evaluatorF::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]
insertSQLVectorDB::Function = args[:insertSQLVectorDB] # insertSQLVectorDB::Function = args[:insertSQLVectorDB]
querySQLVectorDBF::Function = args[:querySQLVectorDB] querySQLVectorDBF::Function = args[:querySQLVectorDB]
llmFormatName::String = args[:llmFormatName]
# getting SQL from vectorDB # getting SQL from vectorDB
thoughtDict = decisionMakerF(state, context, text2textInstructLLM; querySQLVectorDBF) thoughtDict = decisionMakerF(state, context, text2textInstructLLM, llmFormatName;
querySQLVectorDBF)
# map action and input() to llm function # map action and input() to llm function
response = response =
@@ -727,7 +997,8 @@ function transition(state::T, args::NamedTuple
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, thoughtDict[:action_input]) extracted = extractContent_dataframe(response[:result], text2textInstructLLM,
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)
@@ -743,7 +1014,7 @@ function transition(state::T, args::NamedTuple
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, text2textInstructLLM) progressvalue::Integer = evaluatorF(newstate, thoughtDict, text2textInstructLLM, llmFormatName)
return (newNodeKey=newNodeKey, newstate=newstate, progressvalue=progressvalue) return (newNodeKey=newNodeKey, newstate=newstate, progressvalue=progressvalue)
end end
@@ -835,6 +1106,7 @@ julia> println(result)
function query(query::T, executeSQL::Function, text2textInstructLLM::Function; function query(query::T, executeSQL::Function, text2textInstructLLM::Function;
insertSQLVectorDB::Union{Function, Nothing}=nothing, insertSQLVectorDB::Union{Function, Nothing}=nothing,
similarSQLVectorDB::Union{Function, Nothing}=nothing, similarSQLVectorDB::Union{Function, Nothing}=nothing,
llmFormatName="qwen3"
)::NamedTuple{(:text, :rawresponse), Tuple{Any, Any}} where {T<:AbstractString} )::NamedTuple{(:text, :rawresponse), Tuple{Any, Any}} where {T<:AbstractString}
# use similarSQLVectorDB to find similar SQL for the query # use similarSQLVectorDB to find similar SQL for the query
@@ -844,7 +1116,8 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function;
response = SQLexecution(executeSQL, sql) response = SQLexecution(executeSQL, sql)
if response[:success] if response[:success]
# intention = Dict(:intention=> "$(thoughtDict[:plan])") # intention = Dict(:intention=> "$(thoughtDict[:plan])")
extracted = extractContent_dataframe(response[:result], text2textInstructLLM, sql) extracted = extractContent_dataframe(response[:result], text2textInstructLLM, sql,
llmFormatName)
return (text=extracted, rawresponse=response[:result]) return (text=extracted, rawresponse=response[:result])
end end
end end
@@ -873,7 +1146,6 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function;
context = Dict( context = Dict(
:tablelist => :tablelist =>
""" """
Here are SQL that used to create tables in the database:
create table customer ( create table customer (
customer_id uuid primary key default gen_random_uuid (), customer_id uuid primary key default gen_random_uuid (),
customer_firstname varchar(128), customer_firstname varchar(128),
@@ -936,7 +1208,7 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function;
wine_name varchar(128) not null, wine_name varchar(128) not null,
winery varchar(128) not null, winery varchar(128) not null,
vintage integer not null, vintage integer not null,
region varchar(128) not null, region varchar(128) not null, -- A field used to store the name of a wine-producing region, such as Napa Valley (California), Bordeaux, Champagne, Tuscany, etc.
country varchar(128) not null, country varchar(128) not null,
wine_type varchar(128) not null, wine_type varchar(128) not null,
grape varchar(128) not null, grape varchar(128) not null,
@@ -946,7 +1218,7 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function;
tannin integer, tannin integer,
acidity integer, acidity integer,
fizziness integer, fizziness integer,
tasting_notes text, tasting_notes text, -- A field used to record the distinctive flavors of wine such as floral, citrus, apple, earthy, daisy, etc.
note text, note text,
other_attributes jsonb, other_attributes jsonb,
@@ -997,15 +1269,16 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function;
text2textInstructLLM=text2textInstructLLM, text2textInstructLLM=text2textInstructLLM,
querySQLVectorDB=similarSQLVectorDB, querySQLVectorDB=similarSQLVectorDB,
insertSQLVectorDB=insertSQLVectorDB, insertSQLVectorDB=insertSQLVectorDB,
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;
horizontalSampleExpansionPhase=3, horizontalSampleExpansionPhase=1,
horizontalSampleSimulationPhase=3, horizontalSampleSimulationPhase=1,
maxSimulationDepth=5, maxSimulationDepth=1,
maxiterations=1, maxiterations=1,
explorationweight=1.0, explorationweight=1.0,
earlystop=earlystop, earlystop=earlystop,
@@ -1013,17 +1286,17 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function;
multithread=false) multithread=false)
# compare all high value state answer then select the best one # compare all high value state answer then select the best one
if length(highValueState) > 0 if length(highValueState) > 1
# open("/appfolder/app/highValueState.json", "w") do io # open("/appfolder/app/highValueState.json", "w") do io
# JSON3.pretty(io, highValueState) # JSON3.pretty(io, highValueState)
# end # end
selected = compareState(query, highValueState, text2textInstructLLM) selected = compareState(query, highValueState, text2textInstructLLM, llmFormatName)
resultState = highValueState[selected] #BUG compareState() select 0 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 = Symbol("action_input_$latestInd") # latest sql
sql = resultState[:thoughtHistory][action_input] sql = resultState[:thoughtHistory][action_input]
extracted = 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 &&
@@ -1032,11 +1305,11 @@ function query(query::T, executeSQL::Function, text2textInstructLLM::Function;
insertSQLVectorDB(resultState[:thoughtHistory][:question], sql) insertSQLVectorDB(resultState[:thoughtHistory][:question], sql)
end end
if extracted === nothing if extractedTableContent === nothing
println("query() return nothing") println("\nSQLLLM query() return nothing ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
end end
result = (text=extracted, rawresponse=resultState[:rawresponse]) result = (text=extractedTableContent, rawresponse=resultState[:rawresponse])
return result return result
end end
@@ -1059,7 +1332,7 @@ function makeNewState(currentstate::T1, thoughtDict::T4, rawresponse, response::
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{Symbol, <:Any}}} where {T1<:AbstractDict, T2<:AbstractString, T3<:Number, T4<:AbstractDict}
keys = [:comprehension, :action_name, :action_input, :observation] keys = [:plan, :action_name, :action_input, :observation]
# latestKeys = [] # latestKeys = []
currentstate_latestKey, currentstate_latestIndice = currentstate_latestKey, currentstate_latestIndice =
@@ -1090,8 +1363,9 @@ function makeNewState(currentstate::T1, thoughtDict::T4, rawresponse, response::
end end
function generatequestion(state::T1, context, text2textInstructLLM::Function; function generatequestion(state::T1, context, text2textInstructLLM::Function,
similarSQL::Union{T2, Nothing}=nothing, maxattempt=10 llmFormatName::String;
similarSQL::Union{T2, Nothing}=nothing, maxattempt=10,
)::String where {T1<:AbstractDict, T2<:AbstractString} )::String where {T1<:AbstractDict, T2<:AbstractString}
similarSQL = similarSQL =
@@ -1122,37 +1396,37 @@ function generatequestion(state::T1, context, text2textInstructLLM::Function;
4) Do not generate any question or comments at the end. 4) Do not generate any question or comments at the end.
You should follow the following guidelines: You should follow the following guidelines:
- When querying data in the database, start with broad search terms and refine your query later for more precise results. - If there is no search result from the database, remove the restrictive criteria until a search result is available, and proceed from there.
You should then respond to the user with: You should then respond to the user with:
1) Understanding: 1) Q: Given the situation, "ask yourself" about the situation at least three, but no more than five, questions.
- State your understanding about the current situation. 2) A: Given the situation, "answer to yourself" the best you can.
2) Q: Given the situation, "ask yourself" about the situation at least five, but no more than ten, questions.
3) A: Given the situation, "answer to yourself" the best you can.
- Do not generate any text after the last answer. - Do not generate any text after the last answer.
You must only respond in format as described below: You must only respond in format as described below:
Understanding: ...
Q1: ... Q1: ...
A1: ... A1: ...
Q2: ... Q2: ...
A2: ... A2: ...
Q3: ...
A3: ...
... ...
Here are some examples: Here are some examples:
Q: What information in the hints is not necessary based on the query? Q: What information in the hints is not necessary based on the query?
A: Country is not specified in the query thus it should not be included in an SQL A: Country is not specified in the query thus it should not be included in an SQL
Q: How can I modify a SQL example to fit my specific query needs? Q: How can I modify a SQL example to fit my specific query needs?
A: ... A: ...
Q: Why the query failed?
A: ...
Q: What criteria become more restrictive as the search scope broadens and can be remove?
A: In the "2019 Toyota Camry hybrid" search query, "2019" represents the most restrictive criteria because it narrows the data scope to a specific year, whereas "Toyota" and "Camry" are broader categories that allow for more general results.
Q: What works and what not previously?
A: ...
Let's begin! Let's begin!
""" """
header = ["Understanding:", "Q1:"] header = ["Q1:"]
dictkey = ["understanding", "q1"] dictkey = ["q1"]
workprogress = "" workprogress = ""
for (k, v) in state[:thoughtHistory] for (k, v) in state[:thoughtHistory]
@@ -1162,7 +1436,7 @@ function generatequestion(state::T1, context, text2textInstructLLM::Function;
end end
response = nothing # store for show when error msg show up response = nothing # store for show when error msg show up
errornote = "" errornote = "N/A"
for attempt in 1:maxattempt for attempt in 1:maxattempt
usermsg = usermsg =
@@ -1172,6 +1446,7 @@ function generatequestion(state::T1, context, text2textInstructLLM::Function;
Example: $similarSQL Example: $similarSQL
Your work progress: $workprogress Your work progress: $workprogress
P.S. $errornote P.S. $errornote
/no_think
""" """
_prompt = _prompt =
@@ -1181,10 +1456,11 @@ function generatequestion(state::T1, context, text2textInstructLLM::Function;
] ]
# put in model format # put in model format
prompt = GeneralUtils.formatLLMtext(_prompt, "granite3") prompt = GeneralUtils.formatLLMtext(_prompt, llmFormatName)
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)
# check if response is valid # check if response is valid
q_number = count("Q", response) q_number = count("Q", response)

View File

@@ -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) ...
@@ -406,13 +404,14 @@ 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=true)
@@ -518,9 +517,9 @@ function SQLexecution(executeSQL::Function, sql::T
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.") error("\nThe resulting table has 0 row. Please try again.")
elseif column > 30 elseif column > 30
error("SQL execution failed. An unexpected error occurred. Please try again.") error("\nSQL execution failed. An unexpected error occurred. Please try again.")
end end
df1 = df1 =
@@ -561,8 +560,9 @@ end
# Signature # Signature
""" """
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,23 +628,26 @@ 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(prompt, modelsize="medium")
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;
@@ -736,7 +739,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,14 +769,14 @@ 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=true)
response = copy(JSON3.read(responsedict[:table_name])) response = copy(JSON3.read(responsedict[:table_name]))
@@ -820,44 +825,39 @@ julia>
- 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, llmFormatName::String
)::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:
<You should then respond to the user with the following> Comparison: detailed comparison of all results from all attempts from various aspects.
Comparison: a comparison of all results from all attempts
Rationale: a brief explanation of why the selected response is the most accurate and relevant 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, ...) 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:
<You should only respond in format as described below>
Comparison: ... Comparison: ...
Rationale: ... Rationale: ...
Selected_response_number: ... Selected_response_number: ...
</You should only respond in format as described below> Here are some examples:
<Here are some examples> User's question: "How many German wines do you have?"
User's question: "How many German wines do you have?" Attempt 1)
Attempt 1: Action: SELECT COUNT(*) FROM wines WHERE country = 'Germany'
Action: SELECT COUNT(*) FROM wines WHERE country = 'Germany' Result: 100 wines
Result: 100 wines Attempt 2)
Attempt 2: Action: SELECT COUNT(*) FROM wines WHERE country = 'Germany' AND type = 'Red'
Action: SELECT COUNT(*) FROM wines WHERE country = 'Germany' AND type = 'Red' Result: 50 red wines
Result: 50 red wines Comparison: The second attempt counts only German red wines while the first attempt includes all German 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.
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
Selected_response_number:1
</Here are some examples>
Let's begin! Let's begin!
""" """
@@ -880,26 +880,26 @@ function compareState(question::String, highValueStateList::Vector{T},
""" """
# 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: observation:
Attempt 2 Attempt 2)
action_name: action_name:`
action_input: action_input:
observation: observation:`
... ...
""" """
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 keys
potentialSolutionStr *= "$k: $(state[k])\n" potentialSolutionStr *= "$k: $(state[k])\n"
println("") println("")
end end
end end
errornote = "" errornote = "N/A"
for attempt in 1:10 for attempt in 1:10
errorFlag = false errorFlag = false
@@ -918,7 +918,7 @@ function compareState(question::String, highValueStateList::Vector{T},
] ]
# put in model format # put in model format
prompt = GeneralUtils.formatLLMtext(_prompt, "granite3") prompt = GeneralUtils.formatLLMtext(_prompt, llmFormatName)
header = ["Comparison:", "Rationale:", "Selected_response_number:"] header = ["Comparison:", "Rationale:", "Selected_response_number:"]
dictkey = ["comparison", "rationale", "selected_response_number"] dictkey = ["comparison", "rationale", "selected_response_number"]
@@ -928,19 +928,20 @@ function compareState(question::String, highValueStateList::Vector{T},
# sometime LLM output something like **Comprehension**: which is not expected # sometime LLM output something like **Comprehension**: which is not expected
response = replace(response, "**"=>"") response = replace(response, "**"=>"")
response = replace(response, "***"=>"") response = replace(response, "***"=>"")
response = GeneralUtils.deFormatLLMtext(response, "granite3") response = GeneralUtils.deFormatLLMtext(response, llmFormatName)
think, response = GeneralUtils.extractthink(response)
# make sure every header is in the response # check whether response has all header
for i in header detected_kw = GeneralUtils.detectKeywordVariation(header, response)
detected = GeneralUtils.detect_keyword(i, response) missingkeys = [k for (k, v) in detected_kw if v === nothing]
if detected === nothing if !isempty(missingkeys)
errornote = "Your previous attempt didn't provide $i" errornote = "$missingkeys are missing from your previous response"
errorFlag = true println("\nERROR SQLLLM extractContent_dataframe() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
end continue
end elseif sum([length(i) for i in values(detected_kw)]) > length(header)
if errorFlag errornote = "\nYour previous attempt has duplicated points according to the required response format"
println("\nERROR SQLLLM compareState() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())") println("\nERROR SQLLLM extractContent_dataframe() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue # skip to the next iteration continue
end end
responsedict = GeneralUtils.textToDict(response, header; dictKey=dictkey, symbolkey=true) responsedict = GeneralUtils.textToDict(response, header; dictKey=dictkey, symbolkey=true)

View File

@@ -1,53 +1,60 @@
""" """
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>
- state your vision of how the situation will evolve, what would you want the situation to evolve into <Your vision>
</Your vision> - state your vision of how the situation will evolve, what would you want the situation to evolve into
<Your mission> Ex. To be the leading innovator in sustainable technology by 2030, transforming global energy systems.
- state the goal </Your vision>
</Your mission> <Your mission>
<Your mission's objective includes> - state the goal
- Break the goal into smaller steps Ex. Empowering communities through clean energy solutions to create a sustainable future.
</Your mission's objective includes> </Your mission>
<Your responsibility includes> <Your mission's objective includes>
- state the mini goals that fall under your responsibility - 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 responsibility does NOT includes> </Your mission's objective includes>
<Your responsibility includes>
- state the mini goals that fall under your responsibility
</Your responsibility 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 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.
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:
- CHATBOX which you can use to talk with the user. The input is your intentions for the dialogue. Be specific.
- CHECKRESOURCES which you can use to check resources
- IMPLEMENT which you can use to implement the solution
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 only respond in format as described below>
Comprehension: ...
Plan: ...
Action_name: ...
Action_input: ...
</You should only respond in format as described below>
<Here are some examples>
</At each round of conversation, you will be given the following information> </Here are some examples>
<You must follow the following guidelines>
-
</You must 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.
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:
- CHATBOX which you can use to talk with the user. The input is your intentions for the dialogue. Be specific.
- CHECKRESOURCES which you can use to check resources
- IMPLEMENT which you can use to implement the solution
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 only respond in format as described below>
Comprehension: ...
Plan: ...
Action_name: ...
Action_input: ...
</You should only respond in format as described below>
<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

View File

@@ -3,7 +3,7 @@ using LibPQ, Dates, JSON3, PrettyPrinting, UUIDs, DataFrames, DataStructures, Ba
using GeneralUtils, SQLLLM using GeneralUtils, SQLLLM
config = copy(JSON3.read("/appfolder/mountvolume/appdata/config.json")) config = JSON3.read("/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,7 +134,6 @@ 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
@@ -155,7 +157,65 @@ 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(JSON3.read(_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 = JSON3.write(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
JSON3.pretty(io, d)
end
# query = "How many German wines do you have?" # query = "How many German wines do you have?"
@@ -165,8 +225,9 @@ sessionId = "555"
# 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"