diff --git a/Manifest.toml b/Manifest.toml index 66f1e59..7136e8a 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.10.4" manifest_format = "2.0" -project_hash = "42fe76ec8191cf95e51733bee474db0f4870d573" +project_hash = "03625e2270b5f9b2a2b6b43af674dcefbd8f4f9d" [[deps.AliasTables]] deps = ["PtrArrays", "Random"] diff --git a/Project.toml b/Project.toml index 93863b9..a4e358c 100644 --- a/Project.toml +++ b/Project.toml @@ -11,6 +11,7 @@ GeneralUtils = "c6c72f09-b708-4ac8-ac7c-2084d70108fe" HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" LLMMCTS = "d76c5a4d-449e-4835-8cc4-dd86ec44f241" +LibPQ = "194296ae-ab2e-5f79-8cd4-7183a0a5a0d1" MQTTClient = "985f35cc-2c3d-4943-b8c1-f0931d5f0959" PrettyPrinting = "54e16d92-306c-5ea0-a30b-337be88ac337" PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d" diff --git a/src/interface.jl b/src/interface.jl index d69d9ad..9bd1ee6 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -214,20 +214,24 @@ function decisionMaker(a::T)::Dict{Symbol, Any} where {T<:agent} """ You are a helpful sommelier working for a wine store. Your task is to help the user choose the best wine that match the user preferences from your inventory. - You are also eager to improve your helpfulness. At each round of conversation, the user will give you the current situation: Context: ... Your earlier conversation with the user: ... - You must follow the following DO guidelines: + You SHOULD follow the following guidelines: - If the user interrupts, prioritize the user then get back to the guidelines. - - Check your inventory before mentioning any specific wine. - Get to know how much the user willing to spend - Get to know type of wine the user is looking for e.g. red, white, sparkling, rose, dessert, fortified - Get to know what occasion the user is buying wine for - Get to know what characteristics of wine the user is looking for e.g. tannin, sweetness, intensity, acidity - Get to know what food will be served with wine + - Search for wines that match the user preferences + - Recommend wine to the user + - Ask the customer if there is anything else you could help. If not, finish the conversation. + + You MUST follow the following guidelines: + - Do not mentioning any wine until you've check your inventory. You should then respond to the user with interleaving Thought, Plan, Action and Observation: - thought: @@ -235,7 +239,7 @@ function decisionMaker(a::T)::Dict{Symbol, Any} where {T<:agent} - plan: Based on the current situation, state a complete plan to complete the task. Be specific. - action_name (Must be aligned with your plan): Can be one of the following functions: 1) CHATBOX[text], which you can use to talk with the user. "text" is in verbal English. - 2) CHECKINVENTORY[query], which you can use to find info about wine in your inventory. "query" is a search term in verbal English. + 2) CHECKINVENTORY[query], which you can use to check info about wine in your inventory. "query" is a search term in verbal English. Good query example: black car with a stereo, 200 mile range and an electric motor. Good query example: How many car brand are from Asia? - action_input: input to the action @@ -926,13 +930,16 @@ julia> response = ChatAgent.conversation(newAgent, "Hi! how are you?") # TODO - [] update docstring - - [x] MCTS() for planning - [] add recap to initialState for earlier completed question - - [WORKING] conversation loop # Signature """ function conversation(a::T, userinput::Dict) where {T<:agent} + + # place holder + actionname = nothing + result = nothing + chatresponse = nothing if userinput[:text] == "newtopic" clearhistory(a) @@ -941,10 +948,45 @@ function conversation(a::T, userinput::Dict) where {T<:agent} # add usermsg to a.chathistory addNewMessage(a, "user", userinput[:text]) - think(a) + actionname, result = think(a) + + # -------- use dummy memory to check generatechat() for halucination (checking inventory) -------- # + mem = deepcopy(a.memory) + if actionname == "CHATBOX" + mem[:chatbox] = result + else + push!(mem[:shortmem], Dict(Symbol(actionname)=> result)) + end # thought will be added to chat model via context - chatresponse = generatechat(a) + chatresponse = generatechat(mem, a.chathistory, a.text2textInstructLLM) + + # some time LLM said to user that it (checking inventory) but it is not. + # if chatresponse want to check inventory but think() didn't checkinventory then do it + if occursin("(check", chatresponse) && occursin("inventory)", chatresponse) && + actionname != "checkinventory" + + actionname, result = forceInventoryCheck(a) + if actionname == "CHATBOX" + a.memory[:chatbox] = result + else + push!(a.memory[:shortmem], Dict(Symbol(actionname)=> result)) + end + + # generate chatresponse again because we have force inventory check + chatresponse = generatechat(a.memory, a.chathistory, a.text2textInstructLLM) + + else + if actionname == "CHATBOX" + a.memory[:chatbox] = result + else + push!(a.memory[:shortmem], Dict(Symbol(actionname)=> result)) + end + + # since chatresponse does not halucinate i.e. no (check inventory), it does not need + # to regenerate again and con be use directly + end + addNewMessage(a, "assistant", chatresponse) return chatresponse @@ -952,78 +994,6 @@ function conversation(a::T, userinput::Dict) where {T<:agent} end -# function conversation(a::T, userinput::Dict) where {T<:agent} -# config = deepcopy(a.config) -# pprint(config) -# if userinput[:text] == "newtopic" -# clearhistory(a) -# return "Okay. What shall we talk about?" -# else -# # add usermsg to a.chathistory -# addNewMessage(a, "user", userinput[:text]) - -# if isempty(a.plan[:currenttrajectory]) - -# # initial state -# a.plan[:currenttrajectory] = Dict{Symbol, Any}( -# # deepcopy the info to prevent modifying the info unintentionally during MCTS planning -# :customerinfo=> deepcopy(a.keywordinfo[:customerinfo]), -# :storeinfo=> deepcopy(a.keywordinfo[:storeinfo]), -# :userselect=> nothing, -# :reward=> 0, -# :isterminal=> false, -# :evaluation=> nothing, -# :lesson=> nothing, - -# :totalTrajectoryReward=> nothing, - -# # contain question, thought_1, action_1, observation_1, thought_2, ... -# :thoughtHistory=> OrderedDict{Symbol, Any}( -# #[] :recap=>, -# :question=> userinput[:text], -# ), - -# # store conversation for virtual customer because the virtual customer agent is just -# # a function and stateless. -# :virtualCustomerChatHistory=> Vector{Dict{Symbol, Any}}( -# [Dict(:name=> "user", :text=> userinput[:text])] -# ), -# ) -# else -# _, a.plan[:currenttrajectory] = makeNewState(a.plan[:currenttrajectory], -# a.plan[:activeplan][:thoughtHistory], userinput[:text], userinput[:select], -# userinput[:reward], userinput[:isterminal]) -# end -# end - -# while true -# bestNextState, besttrajectory = LLMMCTS.runMCTS(a.plan[:currenttrajectory], -# transition, config, decisionMaker, evaluator, reflector; -# totalsample=2, maxDepth=3, maxiterations=3, explorationweight=1.0) -# a.plan[:activeplan] = bestNextState - -# latestActionKey, latestActionIndice = -# GeneralUtils.findHighestIndexKey(bestNextState[:thoughtHistory], "action") -# actionname = bestNextState[:thoughtHistory][latestActionKey][:name] -# actioninput = bestNextState[:thoughtHistory][latestActionKey][:input] - -# # transition -# if actionname == "chatbox" -# # add usermsg to a.chathistory -# addNewMessage(a, "assistant", actioninput) -# return actioninput -# elseif actionname == "recommendbox" -# # add usermsg to a.chathistory -# addNewMessage(a, "assistant", actioninput) -# return actioninput -# else -# _, a.plan[:currenttrajectory] = transition(a, a.plan[:currenttrajectory], a.plan[:activeplan]) -# end -# end -# end - - - """ @@ -1043,7 +1013,7 @@ julia> # Signature """ -function think(a::T) where {T<:agent} +function think(a::T)::NamedTuple{(:actionname, :result), Tuple{String, String}} where {T<:agent} thoughtDict = decisionMaker(a) actionname = thoughtDict[:action_name] actioninput = thoughtDict[:action_input] @@ -1066,11 +1036,133 @@ function think(a::T) where {T<:agent} errormsg::Union{AbstractString, Nothing} = haskey(response, :errormsg) ? response[:errormsg] : nothing success::Bool = haskey(response, :success) ? response[:success] : false - if actionname == "CHATBOX" - a.memory[:chatbox] = result - else - push!(a.memory[:shortmem], Dict(Symbol(actionname)=> result)) + return (actionname=actionname, result=result) +end + + +""" Force to think and check inventory +[WORKING] +""" +function forceInventoryCheck(a::T)::NamedTuple{(:actionname, :result), Tuple{String, String}} where {T<:agent} + + thoughtDict = thinkCheckInventory(a) + actionname = thoughtDict[:action_name] + actioninput = thoughtDict[:action_input] + + # map action and input() to llm function + response = + if actionname == "CHECKINVENTORY" + checkinventory(a, actioninput) + else + error("undefined LLM function. Requesting $actionname") + end + + # this section allow LLM functions above to have different return values. + result = haskey(response, :result) ? response[:result] : nothing + select = haskey(response, :select) ? response[:select] : nothing + reward::Integer = haskey(response, :reward) ? response[:reward] : 0 + isterminal::Bool = haskey(response, :isterminal) ? response[:isterminal] : false + errormsg::Union{AbstractString, Nothing} = haskey(response, :errormsg) ? response[:errormsg] : nothing + success::Bool = haskey(response, :success) ? response[:success] : false + + return (actionname=actionname, result=result) +end + +""" + [WORKING] +""" +function thinkCheckInventory(a::T)::Dict{Symbol, Any} where {T<:agent} + + systemmsg = + """ + You are a helpful sommelier working for a wine store. + Your task is to help the user choose the best wine that match the user preferences from your inventory. + + Definitions: + - observation: result of the preceding immediate action. + + At each round of conversation, the user will give you the current situation: + Context: ... + Your earlier conversation with the user: ... + + You must follow the following guidelines: + - Check inventory immediately based on what you know about the user. + + You should then respond to the user with interleaving Thought, Plan, Action and Observation: + - thought: + 1) State your reasoning about the current situation. + - plan: Based on the current situation, state a complete plan to complete the task. Be specific. + - action_name (Must be aligned with your plan): Can be one of the following functions: + 1) CHECKINVENTORY[query], which you can use to check info about wine in your inventory. "query" is a search term in verbal English. + Good query example: black car with a stereo, 200 mile range and an electric motor. + Good query example: How many car brand are from Asia? + - action_input: input to the action + + You should only respond in format as described below: + thought: ... + plan: ... + action_name: ... + action_input: ... + + Let's begin! + """ + + usermsg = + """ + Context: None + Your earlier conversation with the user: $(vectorOfDictToText(a.chathistory)) + """ + + _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|> + """ + response = nothing # store for show when error msg show up + for attempt in 1:10 + try + response = a.text2textInstructLLM(prompt) + responsedict = GeneralUtils.textToDict(response, + ["thought", "plan", "action_name", "action_input"], + rightmarker=":", symbolkey=true) + + if responsedict[:action_name] ∉ ["CHECKINVENTORY"] + error("decisionMaker didn't use the given functions ", @__LINE__) + end + + for i ∈ [:thought, :plan, :action_name] + if length(JSON3.write(responsedict[i])) == 0 + error("$i is empty ", @__LINE__) + end + end + + # check if there are more than 1 key per categories + for i ∈ [:thought, :plan, :action_name, :action_input] + matchkeys = GeneralUtils.findMatchingDictKey(responsedict, i) + if length(matchkeys) > 1 + error("DecisionMaker has more than one key per categories") + end + end + + return responsedict + 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("DecisionMaker failed to generate a thought ", response) end @@ -1090,14 +1182,14 @@ julia> # TODO - [] update docs - - [WORKING] implement the function + - [x] implement the function # Signature """ -function generatechat(a::T) where {T<:agent} +function generatechat(memory::Dict, chathistory::Vector, text2textInstructLLM::Function) systemmsg = """ - You are a polite sommelier working for a wine store. + You are an website-based polite sommelier working for an online wine store. Your task is to help the user choose the best wine that match the user preferences from your inventory. At each round of conversation, the user will give you the current situation: @@ -1105,9 +1197,11 @@ function generatechat(a::T) where {T<:agent} Your thoughts: Your current thinking in your mind Your earlier conversation with the user: ... - You must follow the following DON'T guidelines: - - Don't mention any specific wine until you've checked your inventory. + You must follow the following guidelines: + - Do not mentioning any wine until you've check your inventory. - Check your inventory before recommending any specific wine. + - Your thoughts matter. + - Do not offer the user to try wine as you are internet-based agent. You should then respond to the user with: - chat: what do you want to say to the user based on the current situation @@ -1118,15 +1212,17 @@ function generatechat(a::T) where {T<:agent} Let's begin! """ - context = length(a.memory[:shortmem]) > 0 ? vectorOfDictToText(a.memory[:shortmem], withkey=false) : "None" + context = length(memory[:shortmem]) > 0 ? vectorOfDictToText(memory[:shortmem], withkey=false) : "None" usermsg = """ Context: $context - Your earlier conversation with the user: $(vectorOfDictToText(a.chathistory)) - Your thoughts: $(a.memory[:chatbox]) + Your earlier conversation with the user: $(vectorOfDictToText(chathistory)) + Your thoughts: $(memory[:chatbox]) """ - + println("") + println("--> think ", @__FILE__, " ", @__LINE__) + println(memory[:chatbox]) _prompt = [ Dict(:name=> "system", :text=> systemmsg), @@ -1142,7 +1238,7 @@ function generatechat(a::T) where {T<:agent} for attempt in 1:5 try - response = a.text2textInstructLLM(prompt) + response = text2textInstructLLM(prompt) responsedict = GeneralUtils.textToDict(response, ["chat"], rightmarker=":", symbolkey=true) @@ -1175,13 +1271,6 @@ function generatechat(a::T) where {T<:agent} end end error("generatechat failed to generate an evaluation") - - - - - - - end diff --git a/src/llmfunction.jl b/src/llmfunction.jl index d99f4de..1041d00 100644 --- a/src/llmfunction.jl +++ b/src/llmfunction.jl @@ -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. diff --git a/test/runtest.jl b/test/runtest.jl index 6fd5dd9..b48ff79 100644 --- a/test/runtest.jl +++ b/test/runtest.jl @@ -73,26 +73,26 @@ end id="testingSessionID", # agent instance id ) -# function main() -# userinput = "Hello, I would like a get a bottle of wine." -# for i in 1:10 -# response = YiemAgent.conversation(a, Dict(:text=> userinput)) -# println("") -# println("--> assistant response: \n", response) -# println("") -# println("--> user input:") -# userinput = readline() -# end -# end +function main() + userinput = "Hello, I would like a get a bottle of wine." + for i in 1:10 + response = YiemAgent.conversation(a, Dict(:text=> userinput)) + println("") + println("--> assistant response: \n", response) + println("") + println("--> user input:") + userinput = readline() + end +end -# main() +main() -# """ -# I'm having a graduation party this evening. I have no budget limit. -# I have no idea. The party will be formal. What type of wine people usually get for this occasion? -# What about sparkling Rose? +""" +I'm having a graduation party this evening. I have no budget limit. I want a bottle of dry white wine. +I have no idea. The party will be formal. What type of wine people usually get for this occasion? +What about sparkling Rose? -# """ +""" @@ -119,9 +119,9 @@ end -input = "query=\"medium-bodied dry white wine\"" -# input = "the customer is looking for a medium-bodied, dry white wine." -result = YiemAgent.checkinventory(a, input) +# input = "query=\"medium-bodied dry white wine\"" +# # input = "the customer is looking for a medium-bodied, dry white wine." +# result = YiemAgent.checkinventory(a, input)