module llmfunction
export wikisearch, winestock
using HTTP, JSON3, URIs, Random
using GeneralUtils
using ..type, ..utils
#------------------------------------------------------------------------------------------------100
"""
Search wikipedia.
Arguments:
query (string): The query to search for
Returns:
string: The search result text from wikipedia
```jldoctest
julia> using HTTP, JSON3
julia> result = wikisearch("AMD")
"Advanced Micro Devices, Inc., commonly abbreviated as AMD, is an ..."
```
"""
function wikisearch(a::agentReflex, phrase::T) where {T<:AbstractString}
phrase = phrase[1] == " " ? phrase[2:end] : phrase
# prepare input phrase
if occursin("\"", phrase)
phrase = GeneralUtils.getStringBetweenCharacters(phrase, "\"", "\"")
end
phrase = replace(phrase, "\n"=>"")
url = "https://en.wikipedia.org/w/api.php?action=query&format=json&prop=extracts&titles=$(replace(phrase, " " => "%20"))&exintro=1&explaintext=1"
@show url
response = HTTP.get(url)
json_data = JSON3.read(String(response.body))
page_id = first(keys(json_data["query"]["pages"]))
if page_id == "-1"
return "Sorry, I couldn't find any Wikipedia page for the given phrase."
end
result = nothing
try
result = json_data["query"]["pages"][page_id]["extract"]
wiki = result
@show wiki
catch
result = "No info available for your search query."
end
# if result == ""
# result = "No info available for your search query."
# else
# result = makeSummary(a, result)
# end
return result
end
# function winestock(a::agentReflex, phrase::T) where {T<:AbstractString}
# result =
# """
# I found the following wines
# {
# 1: {
# Chateau: Louis Latou,
# name: Corton-Charlamagne,
# grape variety: chardonnay,
# year: 2014,
# price: 55 USD,
# stock ID: ws-114,
# Description: Corton-Charlemagne 2018 is a powerful, complex wine. Its nose is intense, with notes of white stone fruits such as white peach, fresh hazelnut, vanilla, and almond paste. The wine is full-bodied for the palate, and the vanilla is complemented by aromas of fresh almond and lime blossom. The experience ends with a very fine aromatic aftertaste that has subtle saline notes.,
# },
# 2: {
# Chateau: Beaucastel,
# name: Malbec Argentino,
# grape variety: riesling,
# year: 2016,
# price: 39 USD,
# stock ID: ed-23,
# Description: The quintessence of Château de Beaucastel, Hommage à Jacques Perrin delights us every year, and the 2019 vintage is no exception. To the eye it offers a splendid deep red color, verging on black. Full of power and supremely elegant, the nose is of magnificent aromatic complexity with notes of black fruit and spices that offer all the characteristic expression of Mourvèdre. Perfectly balanced by an incredible freshness, the mouth is eminently elegant with intense and complex aromas of great subtlety, a full, refined texture, subtle tannins of great finesse, and infinite length. A great classic Hommage à Jacques Perrin.,
# }
# }
# """
# return result
# end
#WORKING
"""
Arguments:
Return:
Example:
```jldoctest
julia> using ChatAgent, CommUtils
julia> agent = ChatAgent.agentReflex("Jene")
julia> shorttermMemory = OrderedDict{String, Any}(
"user" => "What's the latest AMD GPU?",
"Plan 1:" => " To answer this question, I will need to search for the latest AMD GPU using the wikisearch tool.\n",
"Act 1:" => " wikisearch\n",
"Actinput 1:" => " amd gpu latest\n",
"Obs 1:" => "No info available for your search query.",
"Act 2:" => " wikisearch\n",
"Actinput 2:" => " amd graphics card latest\n",
"Obs 2:" => "No info available for your search query.")
julia> guideline = "\nEvaluation Guideline:\n1. Check if the user's question has been understood correctly.\n2. Evaluate the tasks taken to provide the information requested by the user.\n3. Assess whether the correct tools were used for the task.\n4. Determine if the user's request was successfully fulfilled.\n5. Identify any potential improvements or alternative approaches that could be used in the future.\n\nThe response should include:\n1. A clear understanding of the user's question.\n2. The tasks taken to provide the information requested by the user.\n3. An evaluation of whether the correct tools were used for the task.\n4. A confirmation or explanation if the user's request was successfully fulfilled.\n5. Any potential improvements or alternative approaches that could be used in the future."
julia> score = grading(agent, guideline, shorttermMemory)
```
"""
function winestock(a::agentReflex, actorResult::NamedTuple)
query = JSON3.write(actorResult[:toolinput])
@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, light-medium bodied
intensity = 3, medium bodied
intensity = 4, medium-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, low-medium tannin
tannin = 3, medium tannin
tannin = 4, medium-high tannin
tannin = 5, high tannin
Acidity level:
acidity = 1, low acidity
acidity = 2, low-medium acidity
acidity = 3, medium acidity
acidity = 4, medium-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.
#
#
# Intensity level:
# intensity = 1, light bodied
# intensity = 2, light-medium bodied
# intensity = 3, medium bodied
# intensity = 4, medium-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, low-medium tannin
# tannin = 3, medium tannin
# tannin = 4, medium-high tannin
# tannin = 5, high tannin
# Acidity level:
# acidity = 1, low acidity
# acidity = 2, low-medium acidity
# acidity = 3, medium acidity
# acidity = 4, medium-high acidity
# acidity = 5, high acidity
#
#
# Consult the conversion table then write a specific SQL command using the info from the user.
# List of keywords not allowed in SQL: ["BETWEEN", "--", "WHEN", "IN"]
# Use the following format:
# Think: How do I map the info in the query to conversion table
# Info map: based on conversion table, map the info in query to appropriate variables
# SQL: write a specific SQL command
#
#
# 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\": {\"max\": \"50 USD\"}}
# SQL: SELECT * FROM White WHERE intensity = 5 AND sweetness = 2 AND acidity = 1 AND tannin = 2 AND price <= 50;
#
#
# 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\": {\"max\": \"22 USD\"}}
# SQL: SELECT * FROM Rose WHERE intensity = 1 AND tannin = 2 AND (sweetness = 1 OR sweetness = 2) AND price <= 22;
#
#
# <|user's info|>
# $(actorResult[:selfaware])
#
# <|assistant|>
# """
# prompt =
# """
# [INST]
#
# 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, light-medium bodied
# intensity = 3, medium bodied
# intensity = 4, medium-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, low-medium tannin
# tannin = 3, medium tannin
# tannin = 4, medium-high tannin
# tannin = 5, high tannin
# Acidity level:
# acidity = 1, low acidity
# acidity = 2, low-medium acidity
# acidity = 3, medium acidity
# acidity = 4, medium-high acidity
# acidity = 5, high acidity
#
#
# The user is planning a wedding party and needs a bottle of wine for the occasion.\n - Thai dishes will be served at the wedding party.\n - The ambient temperature at the serving location is around 22 degrees Celsius.\n - The user prefers a red wine with medium-bodied, dry and low to medium tannin.\n - The user prefers a medium acidity level for the wine.\n - The user's budget for the bottle of wine is around 15 USD.\n\n Info match:\n - Occasion: wedding party\n - Type of food: Thai dishes\n - Ambient temperature: around 22 degrees Celsius\n - Preferred type of wine: red\n - Wine sweetness level: dry\n - Wine intensity level: medium-bodied\n - Wine tannin level: low to medium\n - Wine acidity level: medium\n - Wine price range: around 15 USD\n\n
#
# Consult the conversion table then write a specific SQL command using the query info to search a database table.
# List of keywords not allowed in SQL: ["BETWEEN", "--", "WHEN", "IN"]
# Use the following format:
# Think: How do I map the info in the query to conversion table
# Info map: based on conversion table, map the info in query to appropriate variables
# SQL: write a specific SQL command
# [/INST]
# 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\": {\"max\": \"50 USD\"}}
# SQL: SELECT * FROM White WHERE intensity = 5 AND sweetness = 2 AND acidity = 1 AND tannin = 2 AND price <= 50;
#
# [INST]
#
# $(actorResult[:selfaware])
#
# Consult the conversion table then write a specific SQL command using the query info.
# List of keywords not allowed in SQL: ["BETWEEN", "--", "WHEN", "IN"]
# [/INST]
# """
println("")
@show db_prompt = prompt
_sql = nothing
while true
_sql = sendReceivePrompt(a, prompt, max_tokens=256, temperature=0.2,
stopword=["/n/n", "END", "End", "Obs", "<|", ""])
_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(_sql)
if _sql[i] != ' '
newsql = _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"=>"Blossom_wines"], body)
println("")
@show r
a.memory[:r] = r
result = copy(JSON3.read(r.body))
wines = shuffle(result[1][:result]) # shuffle in case there are more than 1 result
println("")
@show wines
# choose only 2 wines
if length(wines) > 2
println("$(length(wines)) wines found")
wines = wines[1:2]
end
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::agentReflex, query::Dict)
# query = JSON3.write(query)
# @show query
# prompt =
# """
# <|system|>
#
# Your are a helpful assistant.
#
#
# Intensity level:
# intensity = 1, light bodied
# intensity = 2, light-medium bodied
# intensity = 3, medium bodied
# intensity = 4, medium-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, low-medium tannin
# tannin = 3, medium tannin
# tannin = 4, medium-high tannin
# tannin = 5, high tannin
# Acidity level:
# acidity = 1, low acidity
# acidity = 2, low-medium acidity
# acidity = 3, medium acidity
# acidity = 4, medium-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"]
#
#
# query: {\"wine type\": \"white\", \"wine characteristics\": \"full-bodied | off-dry | low acidity | medium tannin\", \"price\": {\"max\": \"50\"}}
# assistant: SELECT * FROM White WHERE intensity = 5 AND sweetness = 2 AND acidity = 1 AND tannin = 3 AND price <= 50;
#
#
# query: {\"wine characteristics\":\"low-bodied | semi-sweet | low-medium tannin\",\"price\":\"22 USD\",\"occasion\":\"anniversary\",\"wine type\":\"Rose\",\"food\":\"American dishes\"}
# assistant: SELECT * FROM Rose WHERE intensity = 1 AND sweetness = 3 AND (tannin = 2 OR tannin = 3) AND price <= 22;
#
#
# <|query|>
# $query
#
# <|assistant|>
# """
# println("")
# @show db_prompt = prompt
# _sql = nothing
# while true
# _sql = sendReceivePrompt(a, prompt, max_tokens=256, temperature=0.2,
# stopword=["/n/n", "END", "End", "Obs", "<|", ""])
# _sql = split(_sql, ";")[1] * ";"
# # 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("illegal SQL command")
# end
# println("")
# @show db_sql = replace(_sql, '\n'=>"")
# # remove any blank character in front of a string
# newsql = nothing
# for i in eachindex(_sql)
# if _sql[i] != ' '
# newsql = _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"=>"Blossom_wines"], body)
# println("")
# @show r
# a.memory[:r] = r
# result = copy(JSON3.read(r.body))
# wines = shuffle(result[1][:result]) # shuffle in case there are more than 1 result
# println("")
# @show wines
# # choose only 2 wines
# if length(wines) > 2
# println("$(length(wines)) wines found")
# wines = wines[1:2]
# end
# result = nothing
# if length(wines) == 0
# result =
# """
# Wine not found.
# """
# 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
end # end module