update
This commit is contained in:
832
src/interface.jl
832
src/interface.jl
File diff suppressed because it is too large
Load Diff
@@ -339,7 +339,7 @@ function extractWineAttributes_1(a::T1, input::T2; maxattempt=10
|
||||
As a helpful sommelier, your task is to extract the user information from the user's query as much as possible to fill out user's preference form.
|
||||
|
||||
At each round of conversation, the user will give you the following:
|
||||
User's query: ...
|
||||
- The query: the query provided by the user.
|
||||
|
||||
You must follow the following guidelines:
|
||||
- If specific information required in the preference form is not available in the query or there isn't any, mark with "N/A" to indicate this.
|
||||
@@ -347,121 +347,167 @@ function extractWineAttributes_1(a::T1, input::T2; maxattempt=10
|
||||
- Do not generate other comments.
|
||||
|
||||
You should then respond to the user with:
|
||||
Wine_name: name of the wine
|
||||
Winery: name of the winery
|
||||
Vintage: the year of the wine
|
||||
Region: a region (NOT a country) where the wine is produced, such as Burgundy, Napa Valley, etc
|
||||
Country: a country where wine is produced. Can be "Austria", "Australia", "France", "Germany", "Italy", "Portugal", "Spain", "United States"
|
||||
Wine_type: can be one of: "red", "white", "sparkling", "rose", "dessert" or "fortified"
|
||||
Grape_varietal: the name of the primary grape used to make the wine
|
||||
Tasting_notes: a word describe the wine's flavor, such as "butter", "oak", "fruity", "raspberry", "earthy", "floral", etc
|
||||
Wine_price_range: price range of wine. Example: For price 10-20, price range will be "10 to 20". For price 100, price range will be 0 to 100.
|
||||
Occasion: the occasion the user is having the wine for
|
||||
Food_to_be_paired_with_wine: food that the user will be served with the wine such as poultry, fish, steak, etc
|
||||
wine_name: name of the wine
|
||||
winery: name of the winery
|
||||
vintage: the year of the wine
|
||||
region: a region (NOT a country) where the wine is produced, such as Burgundy, Napa Valley, etc
|
||||
country: a country where wine is produced. Can be "Austria", "Australia", "France", "Germany", "Italy", "Portugal", "Spain", "United States"
|
||||
wine_type: can be one of: "red", "white", "sparkling", "rose", "dessert" or "fortified"
|
||||
grape_varietal: the name of the primary grape used to make the wine
|
||||
tasting_notes: a word describe the wine's flavor, such as "butter", "oak", "fruity", "raspberry", "earthy", "floral", etc
|
||||
wine_price_min: minimum price range of wine. Example: For wine price 20, wine_price_min will be 0. For wine price 10 to 100, wine_price_min will be 10.
|
||||
wine_price_max: maximum price range of wine. Example: For wine price 20, wine_price_max will be 20. For wine price 10 to 100, wine_price_max will be 100.
|
||||
occasion: the occasion the user is having the wine for
|
||||
food_to_be_paired_with_wine: food that the user will be served with the wine such as poultry, fish, steak, etc
|
||||
|
||||
You should only respond in format as described below:
|
||||
Wine_name: ...
|
||||
Winery: ...
|
||||
Vintage: ...
|
||||
Region: ...
|
||||
Country: ...
|
||||
Wine_type:
|
||||
Grape_varietal: ...
|
||||
Tasting_notes: ...
|
||||
Wine_price_range: ...
|
||||
Occasion: ...
|
||||
Food_to_be_paired_with_wine: ...
|
||||
You should only respond in JSON format as described below:
|
||||
{
|
||||
"wine_name": "...",
|
||||
"winery": "...",
|
||||
"vintage": "...",
|
||||
"region": "...",
|
||||
"country": "...",
|
||||
"wine_type": "...",
|
||||
"grape_varietal": "...",
|
||||
"tasting_notes": "...",
|
||||
"wine_price_min": "...",
|
||||
"wine_price_max": "...",
|
||||
"occasion": "...",
|
||||
"food_to_be_paired_with_wine": "..."
|
||||
}
|
||||
|
||||
Here are some example:
|
||||
|
||||
User's query: red, Chenin Blanc, Riesling, 20 USD from Tuscany, Italy or Napa Valley, USA
|
||||
Wine_name: N/A. Winery: N/A. Vintage: N/A. Region: Tuscany, Napa Valley. Country: Italy, United States. Wine_type: red, white. Grape_varietal: Chenin Blanc, Riesling. Tasting_notes: citrus. Wine_price_range: 0 to 20. Occasion: N/A. Food_to_be_paired_with_wine: N/A
|
||||
{
|
||||
"wine_name": "N/A",
|
||||
"winery": "N/A",
|
||||
"vintage": "N/A",
|
||||
"region": "Tuscany, Napa Valley",
|
||||
"country": "Italy, United States",
|
||||
"wine_type": "red, white",
|
||||
"grape_varietal": "Chenin Blanc, Riesling",
|
||||
"tasting_notes": "citrus",
|
||||
"wine_price_min": "0",
|
||||
"wine_price_max": "20",
|
||||
"occasion": "N/A",
|
||||
"food_to_be_paired_with_wine": "N/A"
|
||||
}
|
||||
|
||||
User's query: Domaine du Collier Saumur Blanc 2019, France, white, Merlot
|
||||
Winery: Domaine du Collier. Wine_name: Saumur Blanc. Vintage: 2019. Region: Saumur. Country: France. Wine_type: white. Grape_varietal: Merlot. Tasting_notes: plum. Wine_price_range: N/A. Occasion: N/A. Food_to_be_paired_with_wine: N/A.
|
||||
{
|
||||
"wine_name": "Saumur Blanc",
|
||||
"winery": "Domaine du Collier",
|
||||
"vintage": "2019",
|
||||
"region": "Saumur",
|
||||
"country": "France",
|
||||
"wine_type": "white",
|
||||
"grape_varietal": "Merlot",
|
||||
"tasting_notes": "plum",
|
||||
"wine_price_min": "N/A",
|
||||
"wine_price_max": "N/A",
|
||||
"occasion": "N/A",
|
||||
"food_to_be_paired_with_wine": "N/A"
|
||||
}
|
||||
|
||||
Let's begin!
|
||||
"""
|
||||
|
||||
header = ["Wine_name:", "Winery:", "Vintage:", "Region:", "Country:", "Wine_type:", "Grape_varietal:", "Tasting_notes:", "Wine_price_range:", "Occasion:", "Food_to_be_paired_with_wine:"]
|
||||
dictkey = ["wine_name", "winery", "vintage", "region", "country", "wine_type", "grape_varietal", "tasting_notes", "wine_price_range", "occasion", "food_to_be_paired_with_wine"]
|
||||
requiredKeys = [:wine_name, :winery, :vintage, :region, :country, :wine_type, :grape_varietal, :tasting_notes, :wine_price_min, :wine_price_max, :occasion, :food_to_be_paired_with_wine]
|
||||
errornote = "N/A"
|
||||
|
||||
llmkwargs=Dict(
|
||||
:num_ctx => 32768,
|
||||
:temperature => 0.2,
|
||||
)
|
||||
|
||||
for attempt in 1:maxattempt
|
||||
usermsg =
|
||||
"""
|
||||
$input
|
||||
"""
|
||||
assistantinfo =
|
||||
context =
|
||||
"""
|
||||
<information>
|
||||
<context>
|
||||
P.S. $errornote
|
||||
</information>
|
||||
</context>
|
||||
/no_think
|
||||
"""
|
||||
|
||||
_prompt =
|
||||
unformatPrompt =
|
||||
[
|
||||
Dict(:name=> "system", :text=> systemmsg),
|
||||
Dict(:name=> "user", :text=> usermsg)
|
||||
]
|
||||
|
||||
# put in model format
|
||||
prompt = GeneralUtils.formatLLMtext(_prompt, a.llmFormatName)
|
||||
prompt = GeneralUtils.formatLLMtext(unformatPrompt, a.llmFormatName)
|
||||
# add info
|
||||
prompt = prompt * assistantinfo
|
||||
prompt = prompt * context
|
||||
|
||||
response = a.func[:text2textInstructLLM](prompt;
|
||||
modelsize="medium", llmkwargs=llmkwargs, senderId=a.id)
|
||||
response = GeneralUtils.remove_french_accents(response)
|
||||
response = a.func[:text2textInstructLLM](prompt; modelsize="medium", senderId=a.id)
|
||||
response = GeneralUtils.deFormatLLMtext(response, a.llmFormatName)
|
||||
response = GeneralUtils.remove_french_accents(response)
|
||||
think, response = GeneralUtils.extractthink(response)
|
||||
|
||||
# check whether response has all header
|
||||
detected_kw = GeneralUtils.detect_keyword(header, response)
|
||||
kwvalue = [i for i in values(detected_kw)]
|
||||
zeroind = findall(x -> x == 0, kwvalue)
|
||||
missingkeys = [header[i] for i in zeroind]
|
||||
if 0 ∈ values(detected_kw)
|
||||
errornote = "$missingkeys are missing from your previous response"
|
||||
println("\nERROR YiemAgent decisionMaker() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
continue
|
||||
elseif sum(values(detected_kw)) > length(header)
|
||||
errornote = "Your previous attempt has duplicated points"
|
||||
println("\nERROR YiemAgent decisionMaker() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
responsedict = nothing
|
||||
try
|
||||
responsedict = copy(JSON3.read(response))
|
||||
catch
|
||||
println("\nERROR YiemAgent extractWineAttributes_1() failed to parse response: $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
continue
|
||||
end
|
||||
|
||||
# check whether response has all answer's key points
|
||||
detected_kw = GeneralUtils.detect_keyword(header, response)
|
||||
if 0 ∈ values(detected_kw)
|
||||
errornote = "In your previous attempts, the response does not have all answer's key points"
|
||||
println("\nYiemAgent extractWineAttributes_1() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
# check whether all answer's key points are in responsedict
|
||||
_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 extractWineAttributes_1() $errornote --> $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
continue
|
||||
elseif sum(values(detected_kw)) > length(header)
|
||||
errornote = "In your previous attempts, the response has duplicated answer's key points"
|
||||
println("\nYiemAgent extractWineAttributes_1() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
println(response)
|
||||
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 extractWineAttributes_1() $errornote --> $response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
continue
|
||||
end
|
||||
responsedict = GeneralUtils.textToDict(response, header;
|
||||
dictKey=dictkey, symbolkey=true)
|
||||
|
||||
# # check whether response has all header
|
||||
# detected_kw = GeneralUtils.detect_keyword(header, response)
|
||||
# kwvalue = [i for i in values(detected_kw)]
|
||||
# zeroind = findall(x -> x == 0, kwvalue)
|
||||
# missingkeys = [header[i] for i in zeroind]
|
||||
# if 0 ∈ values(detected_kw)
|
||||
# errornote = "$missingkeys are missing from your previous response"
|
||||
# println("\nERROR YiemAgent decisionMaker() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
# continue
|
||||
# elseif sum(values(detected_kw)) > length(header)
|
||||
# errornote = "Your previous attempt has duplicated points"
|
||||
# println("\nERROR YiemAgent decisionMaker() $errornote:\n$response ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
# continue
|
||||
# end
|
||||
|
||||
# # check whether response has all answer's key points
|
||||
# detected_kw = GeneralUtils.detect_keyword(header, response)
|
||||
# if 0 ∈ values(detected_kw)
|
||||
# errornote = "In your previous attempts, the response does not have all answer's key points"
|
||||
# println("\nYiemAgent extractWineAttributes_1() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
# continue
|
||||
# elseif sum(values(detected_kw)) > length(header)
|
||||
# errornote = "In your previous attempts, the response has duplicated answer's key points"
|
||||
# println("\nYiemAgent extractWineAttributes_1() Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||
# println(response)
|
||||
# continue
|
||||
# end
|
||||
# responsedict = GeneralUtils.textToDict(response, header;
|
||||
# dictKey=dictkey, symbolkey=true)
|
||||
|
||||
delete!(responsedict, :thought)
|
||||
delete!(responsedict, :tasting_notes)
|
||||
delete!(responsedict, :occasion)
|
||||
delete!(responsedict, :food_to_be_paired_with_wine)
|
||||
|
||||
println(@__FILE__, " ", @__LINE__)
|
||||
pprintln(responsedict)
|
||||
|
||||
# check if winery, wine_name, region, country, wine_type, grape_varietal's value are in the query because sometime AI halucinates
|
||||
checkFlag = false
|
||||
for i in dictkey
|
||||
for i in requiredKeys
|
||||
j = Symbol(i)
|
||||
if j ∉ [:thought, :tasting_notes, :occasion, :food_to_be_paired_with_wine]
|
||||
# in case j is wine_price it needs to be checked differently because its value is ranged
|
||||
@@ -759,7 +805,8 @@ function paraphrase(text2textInstructLLM::Function, text::String)
|
||||
|
||||
Let's begin!
|
||||
"""
|
||||
|
||||
#[WORKING] use JSON3 the same as extractWineAttributes_1 is better
|
||||
#[WORKING] change this function to use the same format use decisionMater
|
||||
header = ["Paraphrase:"]
|
||||
dictkey = ["paraphrase"]
|
||||
|
||||
|
||||
@@ -193,8 +193,7 @@ function sommelier(
|
||||
"""
|
||||
memory = Dict{Symbol, Any}(
|
||||
:shortmem=> OrderedDict{Symbol, Any}(
|
||||
:available_wine=> [],
|
||||
:found_wine=> [], # used by decisionMaker(). This is to prevent decisionMaker() keep presenting the same wines
|
||||
:db_search_result=> Any[],
|
||||
),
|
||||
:events=> Vector{Dict{Symbol, Any}}(),
|
||||
:state=> Dict{Symbol, Any}(
|
||||
|
||||
42
src/util.jl
42
src/util.jl
@@ -1,7 +1,7 @@
|
||||
module util
|
||||
|
||||
export clearhistory, addNewMessage, chatHistoryToText, eventdict, noises, createTimeline,
|
||||
availableWineToText, createEventsLog
|
||||
availableWineToText, createEventsLog, createChatLog
|
||||
|
||||
using UUIDs, Dates, DataStructures, HTTP, JSON3
|
||||
using GeneralUtils
|
||||
@@ -301,10 +301,10 @@ function createTimeline(events::T1; eventindex::Union{UnitRange, Nothing}=nothin
|
||||
for (i, event) in zip(ind, events)
|
||||
# If no outcome exists, format without outcome
|
||||
if event[:outcome] === nothing
|
||||
timeline *= "Event_$i $(event[:subject])> $(event[:actioninput])\n"
|
||||
timeline *= "Event_$i $(event[:subject])> action_name: $(event[:actionname]), action_input: $(event[:actioninput]), observation: Not done yet.\n"
|
||||
# If outcome exists, include it in formatting
|
||||
else
|
||||
timeline *= "Event_$i $(event[:subject])> $(event[:actioninput]) $(event[:outcome])\n"
|
||||
timeline *= "Event_$i $(event[:subject])> action_name: $(event[:actionname]), action_input: $(event[:actioninput]), observation: $(event[:outcome])\n"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -313,15 +313,15 @@ function createTimeline(events::T1; eventindex::Union{UnitRange, Nothing}=nothin
|
||||
end
|
||||
|
||||
|
||||
function createEventsLog(events::T1; eventindex::Union{UnitRange, Nothing}=nothing
|
||||
function createEventsLog(events::T1; index::Union{UnitRange, Nothing}=nothing
|
||||
) where {T1<:AbstractVector}
|
||||
# Initialize empty log array
|
||||
log = Dict{Symbol, String}[]
|
||||
|
||||
# Determine which indices to use - either provided range or full length
|
||||
ind =
|
||||
if eventindex !== nothing
|
||||
[eventindex...]
|
||||
if index !== nothing
|
||||
[index...]
|
||||
else
|
||||
1:length(events)
|
||||
end
|
||||
@@ -338,7 +338,7 @@ function createEventsLog(events::T1; eventindex::Union{UnitRange, Nothing}=nothi
|
||||
subject = event[:subject]
|
||||
actioninput = event[:actioninput]
|
||||
outcome = event[:outcome]
|
||||
str = "$subject: $actioninput $outcome"
|
||||
str = "Action: $actioninput Outcome: $outcome"
|
||||
d = Dict{Symbol, String}(:name=>subject, :text=>str)
|
||||
push!(log, d)
|
||||
end
|
||||
@@ -348,6 +348,31 @@ function createEventsLog(events::T1; eventindex::Union{UnitRange, Nothing}=nothi
|
||||
end
|
||||
|
||||
|
||||
function createChatLog(chatdict::T1; index::Union{UnitRange, Nothing}=nothing
|
||||
) where {T1<:AbstractVector}
|
||||
# Initialize empty log array
|
||||
log = Dict{Symbol, String}[]
|
||||
|
||||
# Determine which indices to use - either provided range or full length
|
||||
ind =
|
||||
if index !== nothing
|
||||
[index...]
|
||||
else
|
||||
1:length(chatdict)
|
||||
end
|
||||
|
||||
# Iterate through events and format each one
|
||||
for (i, event) in zip(ind, chatdict)
|
||||
subject = event[:name]
|
||||
text = event[:text]
|
||||
d = Dict{Symbol, String}(:name=>subject, :text=>text)
|
||||
push!(log, d)
|
||||
end
|
||||
|
||||
return log
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -382,9 +407,6 @@ end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user