diff --git a/src/YiemAgent.jl b/src/YiemAgent.jl
index 7632efd..b42f6f5 100644
--- a/src/YiemAgent.jl
+++ b/src/YiemAgent.jl
@@ -1,6 +1,6 @@
module YiemAgent
- # export agent, addNewMessage, clearMessage
+ # export agent
""" Order by dependencies of each file. The 1st included file must not depend on any other
diff --git a/src/interface.jl b/src/interface.jl
index e47db12..499fdbe 100644
--- a/src/interface.jl
+++ b/src/interface.jl
@@ -127,7 +127,7 @@ function decisionMaker(a::T1, state::T2)::Dict{Symbol, Any} where {T1<:agent, T2
Thought can reason about the current situation, and Action can be three types:
1) winestock[query], which you can use to find wine in your inventory. The more input data the better.
2) chatbox[text], which you can use to interact with the user.
- 3) recommendation[answer], which returns your wine reccommendation to the user.
+ 3) reccommendbox[answer], which returns your wine reccommendation to the user.
$responseformat
@@ -186,9 +186,18 @@ function decisionMaker(a::T1, state::T2)::Dict{Symbol, Any} where {T1<:agent, T2
_response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg)
_thoughtJsonStr = _response[:response][:text]
- thoughtJsonStr = jsoncorrection(a, _thoughtJsonStr, responseformat)
+ expectedJsonExample =
+ """
+ Here is an expected JSON format:
+ {
+ "Thought": "...",
+ "Action": {"name": "...", "input": "..."},
+ "Observation": "..."
+ }
+ """
+ thoughtJsonStr = jsoncorrection(a, _thoughtJsonStr, expectedJsonExample)
thoughtDict = copy(JSON3.read(thoughtJsonStr))
- pprint(thoughtDict)
+
return thoughtDict
end
@@ -219,10 +228,7 @@ function progressValueEstimator(a::T1, state::T2)::Tuple{String, Integer} where
"""
You should only respond in JSON format as describe below:
{
- "Thought_1": "reasoning 1",
- "Action_1": {"name": "action to take", "input": "Action input"},
- "Observation_1": "result of the action",
- "Evaluation_1": {"evaluation": "your evaluation", "score": your evaluation score}
+ "Evaluation": {"evaluation": "your evaluation", "score": your evaluation score}
}
"""
@@ -233,7 +239,7 @@ function progressValueEstimator(a::T1, state::T2)::Tuple{String, Integer} where
the current situation and actions that can be three types:
1) winestock[query], which you can use to find wine in your inventory.
2) chatbox[text], which you can use to interact with the user.
- 3) finish[answer], which returns your wine reccommendation to the user.
+ 3) reccommendbox[answer], which returns your wine reccommendation to the user.
Given a question and a trajectory, evaluate its correctness and provide your reasoning and
analysis in detail. Focus on the latest thought, action, and observation. Incomplete trajectories
@@ -250,14 +256,17 @@ function progressValueEstimator(a::T1, state::T2)::Tuple{String, Integer} where
"Thought_2": "But there is only 1 model that has the feature customer wanted.",
"Thought_3": "I should check our inventory first to see if we have it.",
"Action_1": {"name": "inventory", "input": "Yiem model A"},
- "Observation_1": "Yiem model A is in stock.",
- "Evaluation_1": {"evaluation": "This trajectory is correct as it is reasonable to check an inventory for info provided in the question.
+ "Observation_1": "Yiem model A is in stock."
+ }
+ {
+ "Evaluation": {"evaluation": "This trajectory is correct as it is reasonable to check an inventory for info provided in the question.
It is also better to have simple searches corresponding to a single entity, making this the best action.",
"score": 10}
}
Let's begin!:
$(JSON3.write(state[:thoughtHistory]))
+ {Evaluation
"""
# apply LLM specific instruct format
@@ -292,11 +301,16 @@ function progressValueEstimator(a::T1, state::T2)::Tuple{String, Integer} where
_response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg)
_thoughtJsonStr = _response[:response][:text]
- thoughtJsonStr = jsoncorrection(a, _thoughtJsonStr, responseformat)
+ expectedJsonExample =
+ """
+ Here is an expected JSON format:
+ {
+ "Evaluation": {"evaluation": "...", "score": ...}
+ }
+ """
+ thoughtJsonStr = jsoncorrection(a, _thoughtJsonStr, expectedJsonExample)
thoughtDict = copy(JSON3.read(thoughtJsonStr))
- latestEvaluationKey, _ =
- GeneralUtils.findHighestIndexKey(thoughtDict, "Evaluation")
- evaluation = thoughtDict[latestEvaluationKey]
+ evaluation = thoughtDict[:Evaluation]
return evaluation[:evaluation], evaluation[:score]
end
diff --git a/src/llmfunction.jl b/src/llmfunction.jl
index 51d3ec2..ae25718 100644
--- a/src/llmfunction.jl
+++ b/src/llmfunction.jl
@@ -1,6 +1,7 @@
module llmfunction
-export virtualWineCustomerChatbox, jsoncorrection
+export virtualWineCustomerChatbox, jsoncorrection, winestock,
+ virtualWineCustomerReccommendbox
using HTTP, JSON3, URIs, Random
using GeneralUtils
@@ -46,6 +47,67 @@ function chatbox(a::T1, input::T2) where {T1<:agent, T2<:AbstractString}
end
+""" Chatbox for chatting with virtual wine customer.
+
+# Arguments
+ - `a::T1`
+ one of Yiem's agent
+ - `input::T2`
+ text to be send to virtual wine customer
+
+# Return
+ - `response::String`
+ response of virtual wine customer
+# Example
+```jldoctest
+julia>
+```
+
+# TODO
+ - [] update docstring
+ - [] add reccommend() to compare wine
+
+# Signature
+"""
+function virtualWineCustomerReccommendbox(a::T1, input::T2)::String where {T1<:agent, T2<:AbstractString}
+
+ input = "I reccomment Zeno crown vista"
+
+ # put in model format
+ virtualWineCustomer = a.config[:externalservice][:virtualWineCustomer_1]
+ llminfo = virtualWineCustomer[:llminfo]
+ prompt =
+ 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
+ msgMeta = GeneralUtils.generate_msgMeta(
+ virtualWineCustomer[:mqtttopic],
+ senderName= "virtualWineCustomerReccommendbox",
+ senderId= a.id,
+ receiverName= "virtualWineCustomer",
+ mqttBroker= a.config[:mqttServerInfo][:broker],
+ mqttBrokerPort= a.config[:mqttServerInfo][:port],
+ msgId = "dummyid" #CHANGE remove after testing finished
+ )
+
+ outgoingMsg = Dict(
+ :msgMeta=> msgMeta,
+ :payload=> Dict(
+ :text=> prompt,
+ )
+ )
+ @show outgoingMsg
+ result = GeneralUtils.sendReceiveMqttMsg(outgoingMsg)
+ response = result[:response][:text]
+
+ return response
+end
+
+
""" Chatbox for chatting with virtual wine customer.
@@ -104,263 +166,43 @@ end
""" Search wine in stock.
- Arguments\n
- a : one of ChatAgent's agent.
- Return\n
- A JSON string of available wine
+# Arguments
+ - `a::T1`
+ one of ChatAgent's agent.
+ - `input::T2`
+# Return
+ A JSON string of available wine
- Example\n
- ```jldoctest
- julia> using ChatAgent
- julia> agent = ChatAgent.agentReflex("Jene")
- julia> input = "{\"food\": \"pizza\", \"occasion\": \"anniversary\"}"
- julia> result = winestock(agent, input)
- "{"wine 1": {\"Winery\": \"Pichon Baron\", \"wine name\": \"Pauillac (Grand Cru Classé)\", \"grape variety\": \"Cabernet Sauvignon\", \"year\": 2010, \"price\": \"125 USD\", \"stock ID\": \"ar-17\"}, }"
- ```
+# Example
+```jldoctest
+julia> using ChatAgent
+julia> agent = ChatAgent.agentReflex("Jene")
+julia> input = "{\"food\": \"pizza\", \"occasion\": \"anniversary\"}"
+julia> result = winestock(agent, input)
+"{"wine 1": {\"Winery\": \"Pichon Baron\", \"wine name\": \"Pauillac (Grand Cru Classé)\", \"grape variety\": \"Cabernet Sauvignon\", \"year\": 2010, \"price\": \"125 USD\", \"stock ID\": \"ar-17\"}, }"
+```
+
+# TODO
+ [] update docs
+ [] implement the function
+
+# Signature
"""
-# function winestock(a::agentReflex, input::NamedTuple)
-# println("")
-# @show input
-
-# wineSearchCriteria = GeneralUtils.JSON3read_stringKey(input[:toolinput])
-# newDict = Dict{String,Any}()
-# for (k,v) in wineSearchCriteria
-# println("k $k v $v")
-# newDict[string(k)] = v
-# end
-
-# #TODO temporary delete key "food pairing from a dict
-# newDict = deepcopy(a.memory[:keyword])
-# delete!(newDict, "food pairing")
-
-# query = JSON3.write(newDict)
-
-# println("")
-# @show query
-
-# # prompt =
-# # """
-# # <|system|>
-# #
-# # Your are a helpful assistant.
-# #
-# #
-# # Database table name by wine type:
-# # Red = table for wine type "red"
-# # White = table for wine type "white"
-# # Sparkling = table for wine type "sparkling"
-# # Rose = table for wine type "rose"
-# # Dessert = table for wine type "dessert"
-# # Fortified = table for wine type "fortified"
-# # Intensity level:
-# # intensity = 1, light bodied
-# # intensity = 2, semi-light bodied
-# # intensity = 3, medium bodied
-# # intensity = 4, semi-full bodied
-# # intensity = 5, full bodied
-# # Sweetness level:
-# # sweetness = 1, dry
-# # sweetness = 2, off-dry
-# # sweetness = 3, semi-sweet
-# # sweetness = 4, sweet
-# # sweetness = 5, very sweet
-# # Tannin level:
-# # tannin = 1, low tannin
-# # tannin = 2, semi-low tannin
-# # tannin = 3, medium tannin
-# # tannin = 4, semi-high tannin
-# # tannin = 5, high tannin
-# # Acidity level:
-# # acidity = 1, low acidity
-# # acidity = 2, semi-low acidity
-# # acidity = 3, medium acidity
-# # acidity = 4, semi-high acidity
-# # acidity = 5, high acidity
-# #
-# #
-# # Consult the conversion table then write a specific SQL command using only available info from a JSON-format query.
-# # List of keywords not allowed in SQL: ["BETWEEN", "--", "WHEN", "IN"]
-
-# # Use the following format:
-# # Info map: based on conversion table, map the info in query to appropriate variables
-# # SQL: write a specific SQL command
-# #
-# #
-# # query: {\"wine type\": \"white\", \"wine characteristics\": \"full-bodied | off-dry | low acidity | low to medium tannin\", \"price\": {\"max\": \"50\"}}
-# # Think: 1) low to medium tannin is not explicitly stated, but assuming it falls within the range of low-medium tannin.
-# # Info map: {\"wine type\": \"white\", \"intensity\": 5, \"sweetness\": 2, \"tannin\": 2, \"acidity\": 1, \"price\": 50}
-# # SQL: SELECT * FROM White WHERE intensity = 5 AND sweetness = 2 AND acidity = 1 AND tannin = 2 AND price <= 50;
-# #
-# #
-# # query: {\"wine characteristics\":\"low-bodied | a little sweet | low-medium tannin\",\"price\":\"22 USD\",\"occasion\":\"anniversary\",\"wine type\":\"Rose\",\"food\":\"American dishes\"}
-# # Think: 1) medium sweet is not explicitly stated, but assuming it falls within the range of dry and off-dry.
-# # Info map: {\"wine type\": \"Rose\", \"intensity\": 1, \"sweetness\": 3, \"tannin\": 2, \"acidity\": 3, \"price\": 22, \"food\":\"American dishes\"}
-# # SQL: SELECT * FROM Rose WHERE intensity = 1 AND tannin = 2 AND (sweetness = 1 OR sweetness = 2) AND price <= 22 AND food = American;
-# #
-# #
-# # <|query|>
-# # $query
-# #
-# # <|assistant|>
-# # """
-
-# prompt =
-# """
-#
-# <|system|>
-#
-# Your are a helpful assistant.
-#
-#
-# Database table name by wine type:
-# Red = table for wine type "red"
-# White = table for wine type "white"
-# Sparkling = table for wine type "sparkling"
-# Rose = table for wine type "rose"
-# Dessert = table for wine type "dessert"
-# Fortified = table for wine type "fortified"
-# Intensity level:
-# light bodied = 1
-# semi-light bodied = 2
-# medium bodied = 3
-# semi-full bodied = 4
-# full bodied = 5
-# Sweetness level:
-# dry or low = 1
-# off-dry or semi-low = 2
-# semi-sweet = 3
-# sweet = 4
-# very sweet = 5
-# Tannin level:
-# low tannin = 1
-# semi-low tannin = 2
-# medium tannin = 3
-# semi-high tannin = 4
-# high tannin = 4
-# Acidity level:
-# low acidity = 1
-# semi-low acidity = 2
-# medium acidity = 3
-# semi-high acidity = 4
-# high acidity = 5
-#
-#
-# Write a specific SQL command from a query using a conversion table
-# List of keywords not allowed the command: ["BETWEEN", "--", "WHEN", "IN"]
-
-# Use the following format:
-# Info map: based on conversion table, map the info in query to appropriate variables
-# SQL: write a specific SQL command
-#
-# |system|>
-#
-#
-# {\"wine type\": \"white\", \"wine characteristics\": \"full-bodied | off-dry | low acidity | low to medium tannin\", \"price\": {\"max\": \"50\"}}
-#
-# <|assistant|>
-# Think: 1) low to medium tannin is not explicitly stated, but assuming it falls within the range of low-medium tannin.
-# Info map: {\"wine type\": \"white\", \"intensity\": 5, \"sweetness\": 2, \"tannin\": 2, \"acidity\": 1, \"price\": 50}
-# SQL: SELECT * FROM wines WHERE wine_type = "red" AND intensity = 5 AND sweetness = 2 AND acidity = 1 AND tannin = 2 AND price <= 50;
-# |assistant|>
-#
-#
-#
-# {\"wine characteristics\":\"low-bodied | a little sweet | low-medium tannin\",\"price\":\"22 USD\",\"occasion\":\"anniversary\",\"wine type\":\"Rose\",\"food\":\"American dishes\"}
-#
-# <|assistant|>
-# Think: 1) medium sweet is not explicitly stated, but assuming it falls within the range of dry and off-dry.
-# Info map: {\"wine type\": \"Rose\", \"intensity\": 1, \"sweetness\": 3, \"tannin\": 2, \"acidity\": 3, \"price\": 22, \"food\":\"American dishes\"}
-# SQL: SELECT * FROM wines WHERE wine_type = "white" AND intensity = 1 AND tannin = 2 AND (sweetness = 1 OR sweetness = 2) AND price <= 22 AND food = American;
-# |assistant|>
-#
-#
-#
-# $query
-#
-# <|assistant|>
-# """
-
-# println("")
-# @show db_prompt = prompt
-# _sql = nothing
-# while true
-# _sql = sendReceivePrompt(a, prompt, a.config[:text2text][:mqtttopic],
-# max_tokens=1024, temperature=0.4, timeout=180,
-# stopword=["Thought:", "Obs:", "<|system|>", "", "<|end|>", "<|user|>"])
-# _sql = split(_sql, ";")[1] * ";"
-# @show _sql
-# # check for valid SQL command
-# check_1 = occursin("BETWEEN", _sql)
-# check_2 = occursin("--", _sql)
-# check_3 = occursin("IN", _sql)
-
-# if check_1 == false && check_2 == false && check_3 == false
-# break
-# end
-# println("invalid SQL command")
-# end
-
-# _sql = split(_sql, "SQL:")[end]
-# println("")
-# @show db_sql = replace(_sql, '\n'=>"")
-
-# # remove any blank character in front of a string
-# newsql = nothing
-# for i in eachindex(db_sql)
-# if db_sql[i] != ' '
-# newsql = db_sql[i:end]
-# break
-# end
-# end
-
-
-# body = newsql
-# uri = URI(scheme="http", host="192.168.88.12", port="9010", path="/sql", userinfo="root:root")
-# r = HTTP.request("POST", uri, ["Accept" => "application/json", "NS"=>"yiem", "DB"=>"wines"], body)
-
-# # a.memory[:r] = r
-# result = copy(JSON3.read(r.body))
-
-
-# wines = shuffle(result[1][:result]) # shuffle in case there are more than 1 result
-
-
-# # choose only 2 wines
-# if length(wines) > 2
-# println("$(length(wines)) wines found")
-# wines = wines[1:2]
-# end
-
-# println("")
-# @show wines
-
-# result = nothing
-# if length(wines) == 0
-# result =
-# """
-# No wine match my search query.
-# """
-# else
-# # write wines dictionary in to string
-# wines_str = ""
-# for (i, wine) in enumerate(wines)
-# winename = wine[:wine_name]
-# wines_str *= "$i: $(JSON3.write(wines[i])),"
-# end
-
-# result =
-# """
-# I found the following wines in our stock:
-# {
-# $wines_str
-# }
-# """
-# end
-
-# @show result
-
-# return result
-# end
+function winestock(a::T1, input::T2) where {T1<:agent, T2<:AbstractString}
+ winesStr =
+ """
+ 1: El Enemigo Cabernet Franc 2019
+ 2: Tantara Chardonnay 2017
+ """
+ result =
+ """
+ I found the following wines in our stock:
+ {
+ $winesStr
+ }
+ """
+ return result
+end
""" Attemp to correct LLM response's incorrect JSON response.
@@ -389,26 +231,30 @@ function jsoncorrection(a::T1, input::T2,
correctJsonExample::T3) where {T1<:agent, T2<:AbstractString, T3<:AbstractString}
attemptround = 0
- incorrectjson = input
+ incorrectjson = deepcopy(input)
correctjson = nothing
while true
attemptround += 1
if attemptround <= 5
try
- JSON3.read(incorrectjson)
+ d = copy(JSON3.read(incorrectjson))
correctjson = incorrectjson
break
- catch
+ catch e
@warn "Attempting correct JSON string. $attemptround"
+ incorrectjson = deepcopy(input)
_prompt =
"""
- Your goal is to correct a given incorrect JSON string.
+ Your goal are:
+ 1) Use the info why the given JSON string failed to load and provide a corrected version that can be loaded by Python's json.load function.
+ 2) The user need Corrected JSON string only. Do not provide any other info.
$correctJsonExample
-
- Incorrect JSON:
- $incorrectjson
- Corrention:
+
+ Let's begin!
+ Given JSON string: $incorrectjson
+ The given JSON string failed to load previously because: $e
+ Corrected JSON string:
"""
# apply LLM specific instruct format
@@ -449,10 +295,77 @@ function jsoncorrection(a::T1, input::T2,
break
end
end
- @show correctjson
+
return correctjson
end
+# function jsoncorrection(a::T1, input::T2,
+# correctJsonExample::T3) where {T1<:agent, T2<:AbstractString, T3<:AbstractString}
+# attemptround = 0
+# incorrectjson = deepcopy(input)
+# correctjson = nothing
+# while true
+# attemptround += 1
+# if attemptround <= 5
+# try
+# JSON3.read(incorrectjson)
+# correctjson = incorrectjson
+# break
+# catch
+# @warn "Attempting correct JSON string. $attemptround"
+# incorrectjson = deepcopy(input)
+# _prompt =
+# """
+# Your goal is to correct a given incorrect JSON format while retaining original content.
+
+# $correctJsonExample
+
+# Incorrect JSON:
+# $incorrectjson
+# Corrention:
+# """
+
+# # apply LLM specific instruct format
+# externalService = a.config[:externalservice][:text2textinstruct]
+# llminfo = externalService[:llminfo]
+# prompt =
+# if llminfo[:name] == "llama3instruct"
+# formatLLMtext_llama3instruct("system", _prompt)
+# else
+# error("llm model name is not defied yet $(@__LINE__)")
+# end
+
+# # send formatted input to user using GeneralUtils.sendReceiveMqttMsg
+# msgMeta = GeneralUtils.generate_msgMeta(
+# externalService[:mqtttopic],
+# senderName= "jsoncorrection",
+# senderId= a.id,
+# receiverName= "text2textinstruct",
+# mqttBroker= a.config[:mqttServerInfo][:broker],
+# mqttBrokerPort= a.config[:mqttServerInfo][:port],
+# )
+
+# outgoingMsg = Dict(
+# :msgMeta=> msgMeta,
+# :payload=> Dict(
+# :text=> prompt,
+# :kwargs=> Dict(
+# :max_tokens=> 512,
+# :stop=> ["<|eot_id|>"],
+# )
+# )
+# )
+# result = GeneralUtils.sendReceiveMqttMsg(outgoingMsg)
+# incorrectjson = result[:response][:text]
+# end
+# else
+# error("Can't fix JSON string")
+# break
+# end
+# end
+
+# return correctjson
+# end
diff --git a/src/mcts.jl b/src/mcts.jl
index 9e748a7..fc71341 100644
--- a/src/mcts.jl
+++ b/src/mcts.jl
@@ -47,7 +47,7 @@ julia> state = Dict(
# Signature
"""
-struct MCTSNode{T<:AbstractDict}
+mutable struct MCTSNode{T<:AbstractDict}
nodekey::String
state::T
visits::Integer
@@ -272,14 +272,14 @@ julia> thoughtDict = Dict(
```
# TODO
- - [PENDING] add other actions
+ - [x] add other actions
- [] add embedding of newstate and store in newstate[:embedding]
# Signature
"""
function MCTStransition(a::T1, state::T2, thoughtDict::T3, isterminal::Function
)::Tuple{String, Dict{Symbol, <:Any}, Bool, <:Number} where {T1<:agent, T2<:AbstractDict, T3<:AbstractDict}
- pprint(thoughtDict)
+
actionname = thoughtDict[:Action][:name]
actioninput = thoughtDict[:Action][:input]
@@ -288,11 +288,11 @@ function MCTStransition(a::T1, state::T2, thoughtDict::T3, isterminal::Function
if actionname == "chatbox"
virtualWineCustomerChatbox(a, actioninput) # virtual customer
elseif actionname == "winestock"
-
- elseif actionname == "finish"
-
+ winestock(a, actioninput)
+ elseif actionname == "reccommendbox"
+ virtualWineCustomerReccommendbox(a, actioninput)
else
-
+ error("undefined LLM function. Requesting $actionname")
end
latestThoughtKey, latestThoughtIndice = GeneralUtils.findHighestIndexKey(state[:thoughtHistory],
diff --git a/test/prompttest_1.jl b/test/prompttest_1.jl
new file mode 100644
index 0000000..8840822
--- /dev/null
+++ b/test/prompttest_1.jl
@@ -0,0 +1,96 @@
+using Revise # remove when this package is completed
+using YiemAgent, GeneralUtils, JSON3, MQTTClient, Dates, UUIDs
+using Base.Threads
+
+# ---------------------------------------------- 100 --------------------------------------------- #
+
+config = copy(JSON3.read("config.json"))
+
+instanceInternalTopic = config[:serviceInternalTopic][:mqtttopic] * "/1"
+
+client, connection = MakeConnection(config[:mqttServerInfo][:broker],
+ config[:mqttServerInfo][:port])
+
+receiveUserMsgChannel = Channel{Dict}(4)
+receiveInternalMsgChannel = Channel{Dict}(4)
+
+msgMeta = GeneralUtils.generate_msgMeta(
+ "N/A",
+ replyTopic = config[:servicetopic][:mqtttopic] # ask frontend reply to this instance_chat_topic
+ )
+
+agentConfig = Dict(
+ :mqttServerInfo=> config[:mqttServerInfo],
+ :receivemsg=> Dict(
+ :prompt=> config[:servicetopic][:mqtttopic], # topic to receive prompt i.e. frontend send msg to this topic
+ :internal=> instanceInternalTopic,
+ ),
+ :externalservice=> config[:externalservice],
+)
+
+# Instantiate an agent
+tools=Dict( # update input format
+ "askbox"=> Dict(
+ :description => "Useful for when you need to ask the user for more context. Do not ask the user their own question.",
+ :input => """Input is a text in JSON format.{\"Q1\": \"How are you doing?\", \"Q2\": \"How may I help you?\"}""",
+ :output => "" ,
+ :func => nothing,
+ ),
+ # "winestock"=> Dict(
+ # :description => "A handy tool for searching wine in your inventory that match the user preferences.",
+ # :input => """Input is a JSON-formatted string that contains a detailed and precise search query.{\"wine type\": \"rose\", \"price\": \"max 35\", \"sweetness level\": \"sweet\", \"intensity level\": \"light bodied\", \"Tannin level\": \"low\", \"Acidity level\": \"low\"}""",
+ # :output => """