This commit is contained in:
narawat lamaiin
2024-07-31 06:52:44 +07:00
parent 792accc619
commit 70e55f0306
5 changed files with 262 additions and 319 deletions

View File

@@ -9,81 +9,6 @@ using ..type, ..util
# ---------------------------------------------- 100 --------------------------------------------- #
"""
# Arguments
# Return
# Example
```jldoctest
julia>
```
# TODO
- [] update docstring
- [WORKING] implement the function
# Signature
"""
function userChatbox(a::T1, input::T2) where {T1<:agent, T2<:AbstractString}
error("--> userChatbox")
# put in model format
virtualWineCustomer = a.config[:externalservice][:virtualWineCustomer_1]
llminfo = virtualWineCustomer[:llminfo]
formattedinput =
if llminfo[:name] == "llama3instruct"
formatLLMtext_llama3instruct("assistant", input)
else
error("llm model name is not defied yet $(@__LINE__)")
end
# send formatted input to user using GeneralUtils.sendReceiveMqttMsg
# return response
end
"""
# Arguments
# Return
# Example
```jldoctest
julia>
```
# TODO
- [] update docstring
- [PENDING] implement the function
# Signature
"""
function userRecommendbox(a::T1, input::T2) where {T1<:agent, T2<:AbstractString}
error("--> userRecommendbox")
# put in model format
virtualWineCustomer = a.config[:externalservice][:virtualWineCustomer_1]
llminfo = virtualWineCustomer[:llminfo]
formattedinput =
if llminfo[:name] == "llama3instruct"
formatLLMtext_llama3instruct("assistant", input)
else
error("llm model name is not defied yet $(@__LINE__)")
end
# send formatted input to user using GeneralUtils.sendReceiveMqttMsg
# return response
end
""" Chatbox for chatting with virtual wine customer.
@@ -363,15 +288,15 @@ julia> result = checkinventory(agent, input)
# Signature
"""
function checkinventory(a::T1, input::T2
)::Union{Tuple{String, Number, Number, Bool}, Tuple{String, Nothing, Number, Bool}} where {T1<:agent, T2<:AbstractString}
)::NamedTuple{(:result, :success, :errormsg), Tuple{String, Bool, Union{String, Nothing}}} where {T1<:agent, T2<:AbstractString}
wineattributes_1 = extractWineAttributes_1(a, input)
wineattributes_2 = extractWineAttributes_2(a, input)
inventoryquery = "$wineattributes_1, $wineattributes_2"
println("--> checkinventory: $inventoryquery")
result = SQLLLM.query(inventoryquery, a.executeSQL, a.text2textInstructLLM)
return result
return (result=result, success=true, errormsg=nothing)
end
@@ -399,7 +324,7 @@ function extractWineAttributes_1(a::T1, input::T2
systemmsg =
"""
As an helpful sommelier, your task is to fill the user's preference form based on the user query.
As an helpful sommelier, your task is to fill out the user's preference form by copying the corresponding words from the user's query.
At each round of conversation, the user will give you the current situation:
User query: ...
@@ -415,14 +340,14 @@ function extractWineAttributes_1(a::T1, input::T2
You should then respond to the user with the following points:
- wine_type: Can be one of: red, white, sparkling, rose, dessert or fortified
- price: ...
- price: Must be an integer representing the cost of the wine.
- occasion: ...
- food_to_be_paired_with_wine: food that will be served with wine
- country: wine's country of origin
- grape_variety: ...
- wine_notes:
Bad example (these words are not wine notes - descriptive words): dry, sour, full bodied, etc.
Good example (these words are wine notes - descriptive words): floral, citrus, earthy, fruity, tropical, nutty, etc.
- tasting_notes: dry, full bodied or something similar
- flavor: what the wine tast like? e.g. floral, citrus, earthy, fruity, tropical, nutty or something similar
You should only respond in the form as described below:
wine_type: ...
@@ -431,7 +356,8 @@ function extractWineAttributes_1(a::T1, input::T2
food_to_be_paired_with_wine: ...
country: ...
grape_variety: ...
wine_notes: ...
tasting_notes: ...
flavor: ...
Let's begin!
"""
@@ -454,7 +380,7 @@ function extractWineAttributes_1(a::T1, input::T2
<|start_header_id|>assistant<|end_header_id|>
"""
attributes = ["wine_type", "price", "occasion", "food_to_be_paired_with_wine", "country", "grape_variety"]
attributes = ["wine_type", "price", "occasion", "food_to_be_paired_with_wine", "country", "grape_variety", "tasting_notes", "flavor"]
for attempt in 1:5
try
@@ -467,6 +393,10 @@ function extractWineAttributes_1(a::T1, input::T2
end
end
#[TESTING] remove tasting_notes because the database didn't prepare to be search using it
responsedict[:tasting_notes] = "NA"
responsedict[:flavor] = "NA"
result = ""
for (k, v) in responsedict
if !occursin("NA", v)
@@ -474,6 +404,9 @@ function extractWineAttributes_1(a::T1, input::T2
end
end
#[WORKING] remove halucination. "highend dry white wine" --> "wine_type: white, occasion: special occasion, food_to_be_paired_with_wine: seafood, fish, country: France, Italy, USA, grape_variety: Chardonnay, Sauvignon Blanc, Pinot Grigio\nwine_notes: citrus, green apple, floral"
result = result[1:end-2] # remove the ending ", "
return result
@@ -498,29 +431,29 @@ function extractWineAttributes_2(a::T1, input::T2
"""
Conversion Table:
Intensity level:
1: May correspond to "light-bodied" or a similar description.
2: May correspond to "med light", "medium light" or a similar description.
3: May correspond to "medium" or a similar description.
4: May correspond to "med full", "medium full" or a similar description.
5: May correspond to "full" or a similar description.
1 to 2: May correspond to "light-bodied" or a similar description.
2 to 3: May correspond to "med light bodied", "medium light" or a similar description.
3 to 4: May correspond to "medium bodied" or a similar description.
4 to 5: May correspond to "med full bodied", "medium full" or a similar description.
4 to 5: May correspond to "full bodied" or a similar description.
Sweetness level:
1: May correspond to "dry", "no sweet" or a similar description.
2: May correspond to "off dry", "less sweet" or a similar description.
3: May correspond to "semi sweet" or a similar description.
4: May correspond to "sweet" or a similar description.
5: May correspond to "very sweet" or a similar description.
1 to 2: May correspond to "dry", "no sweet" or a similar description.
2 to 3: May correspond to "off dry", "less sweet" or a similar description.
3 to 4: May correspond to "semi sweet" or a similar description.
4 to 5: May correspond to "sweet" or a similar description.
4 to 5: May correspond to "very sweet" or a similar description.
Tannin level:
1: May correspond to "low tannin" or a similar description.
2: May correspond to "semi low tannin" or a similar description.
3: May correspond to "medium tannin" or a similar description.
4: May correspond to "semi high tannin" or a similar description.
5: May correspond to "high tannin" or a similar description.
1 to 2: May correspond to "low tannin" or a similar description.
2 to 3: May correspond to "semi low tannin" or a similar description.
3 to 4: May correspond to "medium tannin" or a similar description.
4 to 5: May correspond to "semi high tannin" or a similar description.
4 to 5: May correspond to "high tannin" or a similar description.
Acidity level:
1: May correspond to "low acidity" or a similar description.
2: May correspond to "semi low acidity" or a similar description.
3: May correspond to "medium acidity" or a similar description.
4: May correspond to "semi high acidity" or a similar description.
5: May correspond to "high acidity" or a similar description.
1 to 2: May correspond to "low acidity" or a similar description.
2 to 3: May correspond to "semi low acidity" or a similar description.
3 to 4: May correspond to "medium acidity" or a similar description.
4 to 5: May correspond to "semi high acidity" or a similar description.
4 to 5: May correspond to "high acidity" or a similar description.
"""
systemmsg =
@@ -541,10 +474,10 @@ function extractWineAttributes_2(a::T1, input::T2
3) Do not generate other comments.
You should then respond to the user with the following points:
- sweetness: S where S is an integer indicating sweetness level
- acidity: D where D is an integer indicating acidity level
- tannin: T where T is an integer indicating tannin level
- intensity: I where I is an integer indicating intensity level
- sweetness: S where S are integers represent the range of sweetness levels e.g. 1-2
- acidity: D where D are integers represent the range of acidity level e.g. 3-4
- tannin: T where T are integers represent the range of tannin level e.g. 4-5
- intensity: I where I are integers represent the range of intensity level e.g. 3-4
You should only respond in the form as described below:
sweetness: ...
@@ -587,6 +520,13 @@ function extractWineAttributes_2(a::T1, input::T2
end
end
# some time LLM think the user mentioning acidity and tannin but actually didn't
for (k, v) in responsedict
if k [:acidity, :tannin] && !occursin(string(k), input)
responsedict[k] = "NA"
end
end
result = ""
for (k, v) in responsedict
if !occursin("NA", v)
@@ -610,93 +550,6 @@ function extractWineAttributes_2(a::T1, input::T2
error("wineattributes_wordToNumber() failed to get a response")
end
# function recheckWineAttributes(a::T1, input::T2
# )::String where {T1<:agent, T2<:AbstractString}
# systemmsg =
# """
# As an helpful sommelier, your task is to check what preferences the user mentioned in the query.
# At each round of conversation, the user will give you the current situation:
# User query: ...
# The preferences are:
# - wine type: red, white, sparkling, rose, dessert, fortified
# - price
# - occasion
# - food to be paired with wine
# - country
# - grape_variety
# - tasting_notes
# - sweetness
# - acidity
# - tannin
# - intensity: wine body e.g. full bodied, light bodied
# You should then respond to the user with the following points:
# 1) mentioned_preferences: list all the preferences in the user's query.
# Good example: mentioned_preferences: "price", "country", "wine type"
# You should only respond in the form as described below:
# mentioned_preferences: ...
# Let's begin!
# """
# usermsg =
# """
# User query: $input
# """
# _prompt =
# [
# Dict(:name=> "system", :text=> systemmsg),
# Dict(:name=> "user", :text=> usermsg)
# ]
# # put in model format
# prompt = GeneralUtils.formatLLMtext(_prompt, "llama3instruct")
# prompt *=
# """
# <|start_header_id|>assistant<|end_header_id|>
# """
# attributes = ["mentioned_preferences"]
# for attempt in 1:5
# try
# response = a.text2textInstructLLM(prompt)
# responsedict = GeneralUtils.textToDict(response, attributes, rightmarker=":", symbolkey=true)
# for i ∈ attributes
# if length(JSON3.write(responsedict[Symbol(i)])) == 0
# error("$i is empty ", @__LINE__)
# end
# end
# result = ""
# for (k, v) in responsedict
# if !occursin("NA", v)
# result *= "$k: $v, "
# end
# end
# result = result[1:end-2] # remove the ending ", "
# return result
# catch e
# io = IOBuffer()
# showerror(io, e)
# errorMsg = String(take!(io))
# st = sprint((io, v) -> show(io, "text/plain", v), stacktrace(catch_backtrace()))
# println("")
# println("Attempt $attempt. Error occurred: $errorMsg\n$st")
# println("")
# end
# end
# error("wineattributes_wordToNumber() failed to get a response")
# end
""" Attemp to correct LLM response's incorrect JSON response.