Compare commits
10 Commits
4ae2f4d036
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7b78758ee | ||
|
|
95598c2409 | ||
|
|
2de1e7d6d8 | ||
|
|
51381abc73 | ||
|
|
5965784c7e | ||
|
|
1fea682d6e | ||
|
|
1513fec11a | ||
|
|
5ac191153b | ||
|
|
1ee362f1e5 | ||
|
|
e38ae31d72 |
7
previousVersion/0.0.7/CondaPkg.toml
Executable file
7
previousVersion/0.0.7/CondaPkg.toml
Executable file
@@ -0,0 +1,7 @@
|
||||
channels = ["anaconda", "conda-forge", "pytorch"]
|
||||
|
||||
[deps]
|
||||
python = ">=3.8,<3.11"
|
||||
|
||||
[pip.deps]
|
||||
langchain = ""
|
||||
1171
previousVersion/0.0.7/Manifest.toml
Executable file
1171
previousVersion/0.0.7/Manifest.toml
Executable file
File diff suppressed because it is too large
Load Diff
17
previousVersion/0.0.7/Project.toml
Executable file
17
previousVersion/0.0.7/Project.toml
Executable file
@@ -0,0 +1,17 @@
|
||||
name = "ChatAgent"
|
||||
uuid = "cff63402-b71f-455f-804d-24489fc61e5e"
|
||||
authors = ["narawat <narawat@gmail.com>"]
|
||||
version = "0.1.0"
|
||||
|
||||
[deps]
|
||||
CommUtils = "646cbe82-3d4a-47b2-9440-2e80a472ca20"
|
||||
CondaPkg = "992eb4ea-22a4-4c89-a5bb-47a3300528ab"
|
||||
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
|
||||
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
|
||||
GeneralUtils = "c6c72f09-b708-4ac8-ac7c-2084d70108fe"
|
||||
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
|
||||
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
|
||||
PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d"
|
||||
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||
URIs = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
|
||||
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
|
||||
101
previousVersion/0.0.7/src/ChatAgent.jl
Executable file
101
previousVersion/0.0.7/src/ChatAgent.jl
Executable file
@@ -0,0 +1,101 @@
|
||||
module ChatAgent
|
||||
|
||||
# export agent, addNewMessage, clearMessage
|
||||
|
||||
|
||||
""" Order by dependencies of each file. The 1st included file must not depend on any other
|
||||
files and each file can only depend on the file included before it.
|
||||
"""
|
||||
|
||||
include("type.jl")
|
||||
using .type
|
||||
|
||||
include("utils.jl")
|
||||
using .utils
|
||||
|
||||
include("llmfunction.jl")
|
||||
using .llmfunction
|
||||
|
||||
include("interface.jl")
|
||||
using .interface
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------------------------100
|
||||
|
||||
""" version 0.0.5
|
||||
Todo:
|
||||
[WORKING] add formulateUserRespond to AI tools
|
||||
|
||||
Change from version: 0.0.4
|
||||
-
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
end # module ChatAgent
|
||||
2162
previousVersion/0.0.7/src/interface.jl
Executable file
2162
previousVersion/0.0.7/src/interface.jl
Executable file
File diff suppressed because it is too large
Load Diff
368
previousVersion/0.0.7/src/llmfunction.jl
Normal file
368
previousVersion/0.0.7/src/llmfunction.jl
Normal file
@@ -0,0 +1,368 @@
|
||||
module llmfunction
|
||||
|
||||
export wikisearch, winestock, askbox
|
||||
|
||||
using HTTP, JSON3, URIs, Random
|
||||
using GeneralUtils
|
||||
using ..type, ..utils
|
||||
#------------------------------------------------------------------------------------------------100
|
||||
|
||||
""" Search wikipedia.
|
||||
|
||||
Arguments\n
|
||||
query {string} : The query to search for
|
||||
|
||||
Returns\n
|
||||
result {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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
""" Search wine in stock.
|
||||
|
||||
Arguments\n
|
||||
a : one of ChatAgent's agent.
|
||||
Return\n
|
||||
A JSON string of available wine
|
||||
|
||||
Example\n
|
||||
```jldoctest
|
||||
julia> using ChatAgent, CommUtils
|
||||
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\"}, }"
|
||||
```
|
||||
"""
|
||||
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|>
|
||||
# <About yourself>
|
||||
# Your are a helpful assistant.
|
||||
# </About yourself>
|
||||
# <You have the following conversion table>
|
||||
# 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
|
||||
# </You have the following conversion table>
|
||||
# <Your job>
|
||||
# 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
|
||||
# </Your job>
|
||||
# <Example 1>
|
||||
# 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;
|
||||
# </Example 1>
|
||||
# <Example 2>
|
||||
# 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;
|
||||
# </Example 2>
|
||||
# </s>
|
||||
# <|query|>
|
||||
# $query
|
||||
# </s>
|
||||
# <|assistant|>
|
||||
# """
|
||||
|
||||
prompt =
|
||||
"""
|
||||
<s>
|
||||
<|system|>
|
||||
<About yourself>
|
||||
Your are a helpful assistant.
|
||||
</About yourself>
|
||||
<You have the following conversion table>
|
||||
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 = 1
|
||||
off-dry = 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
|
||||
</You have the following conversion table>
|
||||
<Your job>
|
||||
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
|
||||
</Your job>
|
||||
</|system|>
|
||||
<Example 1>
|
||||
<query>
|
||||
{\"wine type\": \"white\", \"wine characteristics\": \"full-bodied | off-dry | low acidity | low to medium tannin\", \"price\": {\"max\": \"50\"}}
|
||||
</query>
|
||||
<|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 White WHERE intensity = 5 AND sweetness = 2 AND acidity = 1 AND tannin = 2 AND price <= 50;
|
||||
</|assistant|>
|
||||
</Example 1>
|
||||
<Example 2>
|
||||
<query>
|
||||
{\"wine characteristics\":\"low-bodied | a little sweet | low-medium tannin\",\"price\":\"22 USD\",\"occasion\":\"anniversary\",\"wine type\":\"Rose\",\"food\":\"American dishes\"}
|
||||
</query>
|
||||
<|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 Rose WHERE intensity = 1 AND tannin = 2 AND (sweetness = 1 OR sweetness = 2) AND price <= 22 AND food = American;
|
||||
</|assistant|>
|
||||
</Example 2>
|
||||
</s>
|
||||
<query>
|
||||
$query
|
||||
</query>
|
||||
<|assistant|>
|
||||
"""
|
||||
|
||||
println("")
|
||||
@show db_prompt = prompt
|
||||
_sql = nothing
|
||||
while true
|
||||
_sql = sendReceivePrompt(a, prompt, max_tokens=256, temperature=0.4,
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
|
||||
""" Get the first JSON string questions.
|
||||
|
||||
Arguments\n
|
||||
a : one of ChatAgent's agent.
|
||||
input {JSON string} : message to the user
|
||||
|
||||
Return\n
|
||||
a single message to the user
|
||||
|
||||
Example\n
|
||||
```jldoctest
|
||||
julia> input = "{\"Q1\": \"How are you doing?\", \"Q2\": \"How may I help you?\"}"
|
||||
julia> askbox(input)
|
||||
"How are you doing?"
|
||||
```
|
||||
"""
|
||||
function askbox(input::String)
|
||||
dict = GeneralUtils.JSON3read_stringKey(input)
|
||||
_keylist = keys(dict)
|
||||
keylist = [key for key in _keylist]
|
||||
return dict[keylist[1]]
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
end # end module
|
||||
342
previousVersion/0.0.7/src/type.jl
Normal file
342
previousVersion/0.0.7/src/type.jl
Normal file
@@ -0,0 +1,342 @@
|
||||
module type
|
||||
|
||||
export agent, agentReflex, newAgentMemory
|
||||
|
||||
using Dates, UUIDs, DataStructures
|
||||
using CommUtils
|
||||
|
||||
#------------------------------------------------------------------------------------------------100
|
||||
|
||||
function newAgentMemory()
|
||||
memory::Dict{Any, Any} = Dict(
|
||||
:shortterm=> OrderedDict{String, Any}(),
|
||||
:longterm=> OrderedDict{String, Any}(),
|
||||
:log=> OrderedDict{String, Any}(), # span from user stimulus -> multiples attempts -> final respond
|
||||
:keyword=> Dict{String, Any}(),
|
||||
)
|
||||
return memory
|
||||
end
|
||||
|
||||
|
||||
abstract type agent end
|
||||
|
||||
""" A LLM agent with self reflect capabilities.
|
||||
|
||||
Example:
|
||||
```jldoctest
|
||||
julia> using ChatAgent
|
||||
julia> mqttClientSpec = (
|
||||
clientName= "someclient", # name of this client
|
||||
clientID= "$(uuid4())",
|
||||
broker= "mqtt.yiem.ai",
|
||||
pubtopic= (imgAI="img/api/v0.0.1/gpu/request",
|
||||
txtAI="txt/api/v0.1.0/gpu/request"), # this is where LLM server located
|
||||
subtopic= (imgAI="agent/api/v0.1.0/img/respond",
|
||||
txtAI="agent/api/v0.1.0/txt/respond"), # this is where this agent located
|
||||
keepalive= 30,
|
||||
)
|
||||
julia> msgMeta = Dict(
|
||||
:msgPurpose=> "updateStatus",
|
||||
:from=> "agent",
|
||||
:to=> "llmAI",
|
||||
:requestrespond=> "request",
|
||||
:sendto=> "", # destination topic
|
||||
:replyTo=> "agent/api/v0.1.0/txt/respond", # requester ask responder to send reply to this topic
|
||||
:repondToMsgId=> "", # responder is responding to this msg id
|
||||
:taskstatus=> "", # "complete", "fail", "waiting" or other status
|
||||
:timestamp=> Dates.now(),
|
||||
:msgId=> "$(uuid4())",
|
||||
)
|
||||
julia> tools=Dict(
|
||||
:chatbox=>Dict(
|
||||
:name => "chatbox",
|
||||
:description => "Useful only for when you need to ask the user for more info or context. Do not ask the user their own question.",
|
||||
:input => "Input should be a text.",
|
||||
:output => "" ,
|
||||
:func => nothing,
|
||||
),
|
||||
:wikisearch=>Dict(
|
||||
:name => "wikisearch",
|
||||
:description => "Useful for when you need to search an encyclopedia.",
|
||||
:input => "Input is keywords and not a question.",
|
||||
:output => "",
|
||||
:func => ChatAgent.wikisearch, # put function here
|
||||
),
|
||||
:winestock=>Dict(
|
||||
:name => "wineStock",
|
||||
:description => "useful for when you need to search your wine stock by wine description, price, name or ID.",
|
||||
:input => "Input is a search query.",
|
||||
:output => "Output are Wine name, description, price and ID" ,
|
||||
:func => ChatAgent.winestock,
|
||||
),
|
||||
)
|
||||
julia> agent = ChatAgent.agentReflex(
|
||||
"Jene",
|
||||
role=:assistant,
|
||||
mqttClientSpec=mqttClientSpec,
|
||||
msgMeta=msgMeta,
|
||||
tools=tools
|
||||
)
|
||||
```
|
||||
"""
|
||||
@kwdef mutable struct agentReflex <: agent
|
||||
availableRole::AbstractVector = ["system", "user", "assistant"]
|
||||
agentName::String = "Jene" # ex. Jene
|
||||
maxUserMsg::Int = 30
|
||||
earlierConversation::String = "N/A" # summary of earlier conversation
|
||||
mqttClient::Union{mqttClient, Nothing} = nothing
|
||||
msgMeta::Union{Dict, Nothing} = nothing
|
||||
|
||||
""" Dict(Role=> Content) ; Role can be system, user, assistant
|
||||
Example:
|
||||
messages=[
|
||||
Dict(:role=>"system", :content=> "You are a helpful assistant."),
|
||||
Dict(:role=>"assistant", :content=> "How may I help you"),
|
||||
Dict(:role=>"user", :content=> "Hello, how are you"),
|
||||
]
|
||||
"""
|
||||
role::Symbol = :assistant
|
||||
roles::Dict = Dict(:assistant => "You are a helpful assistant.",)
|
||||
|
||||
# Ref: Chat prompt format https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGML/discussions/3
|
||||
# messages= [Dict(:role=>"system", :content=> "", :timestamp=> Dates.now()),]
|
||||
messages = Vector{Dict{Symbol, Any}}()
|
||||
tools::Union{Dict, Nothing} = nothing
|
||||
newplan::Bool = false # if true, new plan will be generated
|
||||
attemptlimit::Int = 5 # thinking round limit
|
||||
attempt::Int = 1 # attempted number
|
||||
task::Int = 1 # task number
|
||||
env::AbstractString = "N/A"
|
||||
thinkingFormat::Union{Dict, Nothing} = nothing
|
||||
roleSpecificInstruction::Union{Dict, Nothing} = nothing
|
||||
memory = newAgentMemory()
|
||||
|
||||
# LLM function related
|
||||
winestockResult = ""
|
||||
end
|
||||
|
||||
function agentReflex(
|
||||
agentName::String;
|
||||
mqttClientSpec::NamedTuple=(
|
||||
clientName= "someclient", # name of this client
|
||||
clientID= "$(uuid4())",
|
||||
broker= "mqtt.yiem.ai",
|
||||
pubtopic= (imgAI="img/api/v0.0.1/gpu/request",
|
||||
txtAI="txt/api/v0.1.0/gpu/request"),
|
||||
subtopic= (imgAI="agent/api/v0.1.0/img/respond",
|
||||
txtAI="agent/api/v0.1.0/txt/respond"),
|
||||
keepalive= 30,),
|
||||
role::Symbol=:assistant,
|
||||
roles::Dict=Dict(
|
||||
:assistant =>
|
||||
"""
|
||||
You are a helpful assistant.
|
||||
""",
|
||||
:sommelier =>
|
||||
"""
|
||||
You are a helpful sommelier at a wine retailer.
|
||||
You helps users by searching wine that match the user preferences from your stock.
|
||||
""",
|
||||
),
|
||||
roleSpecificInstruction::Dict=Dict(
|
||||
:assistant => "",
|
||||
# :sommelier =>
|
||||
# """
|
||||
# Required info you need for wine recommendation:
|
||||
# - occasion: ask the user
|
||||
# - type of food that will be served with wine: ask the user
|
||||
# - type of wine (we have Rose, White, Red, Rose and Sparkling): ask the user
|
||||
# - wine sweetness level (dry to very sweet)
|
||||
# - wine intensity level (light to full bodied)
|
||||
# - wine tannin level (low to high)
|
||||
# - wine acidity level (low to high)
|
||||
# - wine price range: ask the user
|
||||
# - wines we have in stock: use winestock tool
|
||||
# """
|
||||
:sommelier =>
|
||||
"""
|
||||
<Required wine info>
|
||||
You need to gather all of the following info sequentially before you can recommend wine:
|
||||
1. "wine budget"
|
||||
2. "wine type" (rose, white, red, sparkling, dessert)
|
||||
3. "food pairing" that will be served with wine
|
||||
4. "wine sweetness level" (dry to very sweet)
|
||||
5. "wine intensity level" (light to full bodied)
|
||||
6. "wine tannin level" (low to high)
|
||||
7. "wine acidity level" (low to high)
|
||||
8. wine you have in stock that match the user preferences
|
||||
</Required wine info>
|
||||
"""
|
||||
),
|
||||
|
||||
thinkingFormat::Dict=Dict(
|
||||
:react=>
|
||||
"""
|
||||
Use the following format:
|
||||
Question: the input question your user is asking and you must answer
|
||||
Plan: first you should always think about the question and the info you have thoroughly then extract and devise a complete plan to find the answer (pay attention to variables and their corresponding numerals).
|
||||
Thought: ask yourself do you have all the info you need? And what to do according to the plan (pay attention to correct numeral calculation and commonsense).
|
||||
Act: the tool that match your thought, should be one of {toolnames}
|
||||
Actinput: the input to the action (pay attention to the tool's input)
|
||||
Obs: the result of the action
|
||||
... (this Plan/Thought/Act/Actinput/Obs can repeat N times until you know the answer.)
|
||||
Thought: I think I know the answer
|
||||
Answer: Answer of the original question
|
||||
|
||||
Begin!""",
|
||||
:planner=>
|
||||
"""
|
||||
Use the following format:
|
||||
Stimulus: the input user gives to you and you must respond
|
||||
Plan: first you should always think about the stimulus, the info you need and the info you have thoroughly then extract and devise a step by step plan (pay attention to correct numeral calculation and commonsense).
|
||||
P.S.1 each step should be a single action.
|
||||
""",
|
||||
:actor=>
|
||||
"""
|
||||
<Your job>
|
||||
Use the following format:
|
||||
Thought: based on the plan and the recap of the plan, what to do? (pay attention to correct numeral calculation and commonsense).
|
||||
Act: an action to take based on your thought, must be one of [{toolnames}]
|
||||
Actinput: your input to the action based on your thought (pay attention to the tool's input)
|
||||
Obs: observed result of the action
|
||||
|
||||
P.S.1 ask the user one by one question.
|
||||
P.S.2 ask the user what you want to know if you didn't ask yet.
|
||||
</Your job>
|
||||
""",
|
||||
:actorOriginal=>
|
||||
"""
|
||||
Use the following format:
|
||||
Thought: you should always think about do you have all the required info and what to do according to step {step} of the plan and the info you have (pay attention to correct numeral calculation and commonsense).
|
||||
Act: the action to take that match your thought, should be one of [{toolnames}]
|
||||
Actinput: the input to the action (pay attention to the tool's input)
|
||||
Obs: the result of the action
|
||||
""",
|
||||
),
|
||||
tools::Dict=Dict(
|
||||
:chatbox=>Dict(
|
||||
:name => "chatbox",
|
||||
:description => "Useful for when you need to communicate with the user.",
|
||||
:input => "Input should be a conversation to the user.",
|
||||
:output => "" ,
|
||||
:func => nothing,
|
||||
),
|
||||
# :wikisearch=>Dict(
|
||||
# :name => "wikisearch",
|
||||
# :description => "Useful for when you need to search an encyclopedia",
|
||||
# :input => "Input is keywords and not a question.",
|
||||
# :output => "",
|
||||
# :func => wikisearch, # put function here
|
||||
# ),
|
||||
# :wineStock=>Dict(
|
||||
# :name => "wineStock",
|
||||
# :description => "useful for when you need to search for wine by your description, price, name or ID.",
|
||||
# :input => "Input should be a search query with as much details as possible.",
|
||||
# :output => "" ,
|
||||
# :func => nothing,
|
||||
# ),
|
||||
# :NTHING=>Dict(
|
||||
# :name => "NTHING",
|
||||
# :description => "useful for when you don't need to use tools or actions",
|
||||
# :input => "No input is needed",
|
||||
# :output => "" ,
|
||||
# :func => nothing,
|
||||
# ),
|
||||
),
|
||||
msgMeta::Dict=Dict(
|
||||
:msgPurpose=> "updateStatus",
|
||||
:from=> "chatbothub",
|
||||
:to=> "llmAI",
|
||||
:requestrespond=> "request",
|
||||
:sendto=> "", # destination topic
|
||||
:replyTo=> "agent/api/v0.1.0/txt/respond", # requester ask responder to send reply to this topic
|
||||
:repondToMsgId=> "", # responder is responding to this msg id
|
||||
:taskstatus=> "", # "complete", "fail", "waiting" or other status
|
||||
:timestamp=> Dates.now(),
|
||||
:msgId=> "$(uuid4())",
|
||||
),
|
||||
availableRole::AbstractArray=["system", "user", "assistant"],
|
||||
maxUserMsg::Int=10,)
|
||||
|
||||
newAgent = agentReflex()
|
||||
newAgent.availableRole = availableRole
|
||||
newAgent.maxUserMsg = maxUserMsg
|
||||
newAgent.mqttClient = CommUtils.mqttClient(mqttClientSpec)
|
||||
newAgent.msgMeta = msgMeta
|
||||
newAgent.tools = tools
|
||||
newAgent.role = role
|
||||
newAgent.roles = roles
|
||||
newAgent.thinkingFormat = thinkingFormat
|
||||
newAgent.roleSpecificInstruction = roleSpecificInstruction
|
||||
newAgent.agentName = agentName
|
||||
|
||||
return newAgent
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
end # end module
|
||||
|
||||
|
||||
|
||||
1049
previousVersion/0.0.7/src/utils.jl
Normal file
1049
previousVersion/0.0.7/src/utils.jl
Normal file
File diff suppressed because it is too large
Load Diff
@@ -83,12 +83,11 @@ function clearMessage(a::T) where {T<:agent}
|
||||
break
|
||||
end
|
||||
end
|
||||
memory::Dict{Symbol, Any} = Dict(
|
||||
:shortterm=> OrderedDict{String, Any}(),
|
||||
:longterm=> OrderedDict{String, Any}(),
|
||||
:log=> OrderedDict{String, Any}(), # span from user stimulus -> multiples attempts -> final respond
|
||||
)
|
||||
|
||||
a.memory = newAgentMemory()
|
||||
|
||||
@show a.messages
|
||||
@show a.memory
|
||||
end
|
||||
|
||||
function removeLatestMsg(a::T) where {T<:agent}
|
||||
@@ -200,9 +199,43 @@ function planner_mistral_openorca(a::agentReflex)
|
||||
$(a.roleSpecificInstruction[a.role])
|
||||
"""
|
||||
|
||||
# assistant_plan_prompt =
|
||||
# """
|
||||
# <s>
|
||||
# <|system|>
|
||||
# <About yourself>
|
||||
# $aboutYourself
|
||||
# </About yourself>
|
||||
# <You have access to the following tools>
|
||||
# $toollines
|
||||
# </You have access to the following tools
|
||||
# <Your earlier work>
|
||||
# $shorttermMemory
|
||||
# </Your earlier work>
|
||||
# <Your job>
|
||||
# Plan: first you should always think about your conversation with the user and your earlier work thoroughly then extract and devise a complete, task by task, plan to achieve your objective (pay attention to correct numeral calculation and commonsense).
|
||||
# Keyword memory: using JSON format, list all variables in the plan you need to find out
|
||||
# P.S.1 each task of the plan should be a single action.
|
||||
# </Your job>
|
||||
# <Example>
|
||||
# Plan:
|
||||
# 1. Ask the user about how many miles per day they drive
|
||||
# 2. Ask the user about what stuff they usually carry with
|
||||
# 3. Ask the user about preferred type of car they want to buy (sedan, sport, SUV, etc)
|
||||
# 4. Ask the user about their price range
|
||||
# 5. Use inventory tool to find cars that match the user's preferences and are within their price range
|
||||
# 6. Use finalanswer tool to present the recommended car to the user.
|
||||
# Keyword memory: {"mile per day": null, "carry item": null, "car type": null, "price range": null}
|
||||
# </Example>
|
||||
# </s>
|
||||
# $conversation
|
||||
# <|assistant|>
|
||||
# Plan:
|
||||
# """
|
||||
|
||||
assistant_plan_prompt =
|
||||
"""
|
||||
<|system|>
|
||||
<|im_start|>system
|
||||
<About yourself>
|
||||
$aboutYourself
|
||||
</About yourself>
|
||||
@@ -222,17 +255,18 @@ function planner_mistral_openorca(a::agentReflex)
|
||||
1. Ask the user about how many miles per day they drive
|
||||
2. Ask the user about what stuff they usually carry with
|
||||
3. Ask the user about preferred type of car they want to buy (sedan, sport, SUV, etc)
|
||||
8. Ask the user about their price range
|
||||
9. Use inventory tool to find cars that match the user's preferences and are within their price range
|
||||
10. Use finalanswer tool to present the recommended car to the user.
|
||||
4. Ask the user about their price range
|
||||
5. Use inventory tool to find cars that match the user's preferences and are within their price range
|
||||
6. Use finalanswer tool to present the recommended car to the user.
|
||||
Keyword memory: {"mile per day": null, "carry item": null, "car type": null, "price range": null}
|
||||
</Example>
|
||||
</s>
|
||||
<|im_end|>
|
||||
$conversation
|
||||
<|assistant|>
|
||||
<|im_start|>assistant
|
||||
Plan:
|
||||
"""
|
||||
|
||||
|
||||
response = sendReceivePrompt(a, assistant_plan_prompt, max_tokens=1024, temperature=0.1,
|
||||
timeout=180, stopword=["<|user|>", "</"])
|
||||
response = split(response, "<|")[1]
|
||||
@@ -350,9 +384,9 @@ function selfAwareness(a::agentReflex)
|
||||
</About yourself>
|
||||
<Your job>
|
||||
Use the following format strictly:
|
||||
Info extraction: from the latest observed result, breakdown all info into smaller one and repeat every important info thoroughly
|
||||
Info mapping: based on extracted info, explicitly state what each info could match which keyword memory's key
|
||||
Info matching: using JSON format, what key in my memory matches which info (Donot use nested JSON)
|
||||
Info extraction: break the latest observed result down to the smallest unit of information then repeat every important word
|
||||
Info mapping: based on extracted info, explicitly state what each info could possibly match which keyword memory's key
|
||||
Info matching: using JSON format, state every info relates to what key in my memory matches which value (Donot use nested JSON)
|
||||
</Your job>
|
||||
</|system|>
|
||||
<Example>
|
||||
@@ -386,7 +420,7 @@ function selfAwareness(a::agentReflex)
|
||||
<|assistant|>
|
||||
Info extraction:
|
||||
"""
|
||||
response = sendReceivePrompt(a, prompt, max_tokens=1024, temperature=0.2, timeout=180,
|
||||
response = sendReceivePrompt(a, prompt, max_tokens=1024, temperature=0.4, timeout=180,
|
||||
stopword=["/n/n", "END", "End", "Obs", "<|", "</"])
|
||||
response = split(response, "<|")[1]
|
||||
response = split(response, "</")[1]
|
||||
@@ -574,7 +608,7 @@ function keywordMemoryToPlanMatching(a::agentReflex)
|
||||
<|assistant|>
|
||||
Info mapping:
|
||||
"""
|
||||
response = sendReceivePrompt(a, prompt, max_tokens=1024, temperature=0.2, timeout=180,
|
||||
response = sendReceivePrompt(a, prompt, max_tokens=1024, temperature=0.4, timeout=180,
|
||||
stopword=["/n/n", "END", "End", "Obs", "<|", "</"])
|
||||
response = split(response, "<|")[1]
|
||||
response = split(response, "</")[1]
|
||||
@@ -906,7 +940,7 @@ function actor_mistral_openorca(a::agentReflex, selfaware=nothing)
|
||||
keywordmemory = ""
|
||||
for (k, v) in a.memory[:keyword]
|
||||
if v === nothing
|
||||
keywordmemory *= "- The user preferred $k is null \n"
|
||||
keywordmemory *= "- I don't know the user preferred $k yet \n"
|
||||
else
|
||||
keywordmemory *= "- The user preferred $k is $v \n"
|
||||
end
|
||||
@@ -924,9 +958,6 @@ function actor_mistral_openorca(a::agentReflex, selfaware=nothing)
|
||||
Recall: repeat each of the user info, if you didn't know (indicate by null) say, which info I didn't know
|
||||
"""
|
||||
|
||||
#WORKING actor donot call final reapond after found matched wines because the current prompt
|
||||
# does not contain a.memory[:shortterm] and Thought: do not look for mached wine. Therefore, LLM
|
||||
# did not see matched wine info
|
||||
prompt_1 =
|
||||
"""
|
||||
<s>
|
||||
@@ -940,7 +971,7 @@ function actor_mistral_openorca(a::agentReflex, selfaware=nothing)
|
||||
</You have access to the following tools>
|
||||
<Your job>
|
||||
Use the following format:
|
||||
Thought: check the user info thoroughly, if you don't know some info (indicates by null keyword) you must address it first. Otherwise, you must think about what is the immediate next step to do according to the plan (PS. 1. pay attention to correct numeral calculation and commonsense 2. you must assume nothing)
|
||||
Thought: there are some user's info I still don't know, I must address it as the immediate next step. (PS. 1. pay attention to correct numeral calculation and commonsense 2. you must assume nothing 3. do not ask for confirmation)
|
||||
Act: based on your thought what action to choose?, must be one of [{toolnames}].
|
||||
Actinput: your input to the action using JSON format (pay attention to the tool's input)
|
||||
Obs: observed result of the action
|
||||
@@ -2010,7 +2041,7 @@ end
|
||||
Return\n
|
||||
result : a string of LLM readout from keyword memory
|
||||
|
||||
Example:
|
||||
Example
|
||||
```jldoctest
|
||||
julia> using ChatAgent, CommUtils
|
||||
julia> a = ChatAgent.agentReflex("Jene")
|
||||
|
||||
@@ -5,7 +5,7 @@ export wikisearch, winestock, askbox
|
||||
using HTTP, JSON3, URIs, Random
|
||||
using GeneralUtils
|
||||
using ..type, ..utils
|
||||
#------------------------------------------------------------------------------------------------100
|
||||
# ---------------------------------------------- 100 --------------------------------------------- #
|
||||
|
||||
""" Search wikipedia.
|
||||
|
||||
@@ -218,7 +218,7 @@ function winestock(a::agentReflex, input::NamedTuple)
|
||||
<|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 White WHERE intensity = 5 AND sweetness = 2 AND acidity = 1 AND tannin = 2 AND price <= 50;
|
||||
SQL: SELECT * FROM white WHERE intensity = 5 AND sweetness = 2 AND acidity = 1 AND tannin = 2 AND price <= 50;
|
||||
</|assistant|>
|
||||
</Example 1>
|
||||
<Example 2>
|
||||
@@ -228,7 +228,7 @@ function winestock(a::agentReflex, input::NamedTuple)
|
||||
<|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 Rose WHERE intensity = 1 AND tannin = 2 AND (sweetness = 1 OR sweetness = 2) AND price <= 22 AND food = American;
|
||||
SQL: SELECT * FROM rose WHERE intensity = 1 AND tannin = 2 AND (sweetness = 1 OR sweetness = 2) AND price <= 22 AND food = American;
|
||||
</|assistant|>
|
||||
</Example 2>
|
||||
</s>
|
||||
@@ -242,7 +242,7 @@ function winestock(a::agentReflex, input::NamedTuple)
|
||||
@show db_prompt = prompt
|
||||
_sql = nothing
|
||||
while true
|
||||
_sql = sendReceivePrompt(a, prompt, max_tokens=256, temperature=0.2,
|
||||
_sql = sendReceivePrompt(a, prompt, max_tokens=256, temperature=0.4,
|
||||
stopword=["/n/n", "END", "End", "Obs", "<|", "</"])
|
||||
_sql = split(_sql, ";")[1] * ";"
|
||||
@show _sql
|
||||
|
||||
35
src/type.jl
35
src/type.jl
@@ -1,12 +1,22 @@
|
||||
module type
|
||||
|
||||
export agent, agentReflex
|
||||
export agent, agentReflex, newAgentMemory
|
||||
|
||||
using Dates, UUIDs, DataStructures
|
||||
using CommUtils
|
||||
|
||||
#------------------------------------------------------------------------------------------------100
|
||||
|
||||
function newAgentMemory()
|
||||
memory::Dict{Any, Any} = Dict(
|
||||
:shortterm=> OrderedDict{String, Any}(),
|
||||
:longterm=> OrderedDict{String, Any}(),
|
||||
:log=> OrderedDict{String, Any}(), # span from user stimulus -> multiples attempts -> final respond
|
||||
:keyword=> Dict{String, Any}(),
|
||||
)
|
||||
return memory
|
||||
end
|
||||
|
||||
|
||||
abstract type agent end
|
||||
|
||||
@@ -99,12 +109,7 @@ julia> agent = ChatAgent.agentReflex(
|
||||
env::AbstractString = "N/A"
|
||||
thinkingFormat::Union{Dict, Nothing} = nothing
|
||||
roleSpecificInstruction::Union{Dict, Nothing} = nothing
|
||||
memory::Dict{Any, Any} = Dict(
|
||||
:shortterm=> OrderedDict{String, Any}(),
|
||||
:longterm=> OrderedDict{String, Any}(),
|
||||
:log=> OrderedDict{String, Any}(), # span from user stimulus -> multiples attempts -> final respond
|
||||
:keyword=> Dict{String, Any}(),
|
||||
)
|
||||
memory = newAgentMemory()
|
||||
|
||||
# LLM function related
|
||||
winestockResult = ""
|
||||
@@ -150,15 +155,17 @@ function agentReflex(
|
||||
# """
|
||||
:sommelier =>
|
||||
"""
|
||||
<Required wine info>
|
||||
You need to gather all of the following info sequentially before you can recommend wine:
|
||||
1. wine budget
|
||||
2. wine type (rose, white, red, sparkling, dessert)
|
||||
3. paring food that will be served with wine
|
||||
4. wine sweetness level (dry to very sweet)
|
||||
5. wine intensity level (light to full bodied)
|
||||
6. wine tannin level (low to high)
|
||||
7. wine acidity level (low to high)
|
||||
1. "wine budget"
|
||||
2. "wine type" (rose, white, red, sparkling, dessert)
|
||||
3. "food pairing" that will be served with wine
|
||||
4. "wine sweetness level" (dry to very sweet)
|
||||
5. "wine intensity level" (light to full bodied)
|
||||
6. "wine tannin level" (low to high)
|
||||
7. "wine acidity level" (low to high)
|
||||
8. wine you have in stock that match the user preferences
|
||||
</Required wine info>
|
||||
"""
|
||||
),
|
||||
|
||||
|
||||
41
src/utils.jl
41
src/utils.jl
@@ -386,6 +386,8 @@ function isUsePlans(a::agentReflex)
|
||||
isuseplan = true
|
||||
elseif a.role == :assistant
|
||||
isuseplan = false
|
||||
elseif a.role == :sommelier
|
||||
isuseplan = true
|
||||
else
|
||||
# if LLM mentions any tools, use Plan/Thought/Act loop
|
||||
response = sendReceivePrompt(a, prompt, temperature=0.2, max_tokens=64, stopword=["<|", "</"])
|
||||
@@ -469,7 +471,7 @@ end
|
||||
julia> messagesToString(agent.messages)
|
||||
"<|im_start|>user: Hi there.\n<|im_end|><|im_start|>assistant: Hello! How can I assist you today?\n<|im_end|>"
|
||||
```
|
||||
"""
|
||||
"""
|
||||
function messagesToString(messages::AbstractVector{T}; addressAIas="assistant") where {T<:AbstractDict}
|
||||
conversation = ""
|
||||
if length(messages)!= 0
|
||||
@@ -487,9 +489,9 @@ function messagesToString(messages::AbstractVector{T}; addressAIas="assistant")
|
||||
end
|
||||
|
||||
if role == "user"
|
||||
conversation *= "<|$role|>\n $(content[1:end-nouse])\n</|$role|>"
|
||||
conversation *= "<|im_start|>$role\n $(content[1:end-nouse])\n<|im_end|>"
|
||||
elseif role == "assistant"
|
||||
conversation *= "<|$addressAIas|>\n $(content[1:end-nouse])\n</|$addressAIas|>"
|
||||
conversation *= "<|im_start|>$addressAIas\n $(content[1:end-nouse])\n<|im_end|>"
|
||||
else
|
||||
error("undefied condition role = $role $(@__LINE__)")
|
||||
end
|
||||
@@ -500,6 +502,7 @@ function messagesToString(messages::AbstractVector{T}; addressAIas="assistant")
|
||||
|
||||
return conversation
|
||||
end
|
||||
|
||||
# function messagesToString(messages::AbstractVector{T}; addressAIas="assistant") where {T<:AbstractDict}
|
||||
# conversation = ""
|
||||
# if length(messages)!= 0
|
||||
@@ -517,9 +520,9 @@ end
|
||||
# end
|
||||
|
||||
# if role == "user"
|
||||
# conversation *= "<|$role|>\n $(content[1:end-nouse])\n</s>"
|
||||
# conversation *= "<|$role|>\n $(content[1:end-nouse])\n</|$role|>"
|
||||
# elseif role == "assistant"
|
||||
# conversation *= "<|$addressAIas|>\n $(content[1:end-nouse])\n</s>"
|
||||
# conversation *= "<|$addressAIas|>\n $(content[1:end-nouse])\n</|$addressAIas|>"
|
||||
# else
|
||||
# error("undefied condition role = $role $(@__LINE__)")
|
||||
# end
|
||||
@@ -532,6 +535,7 @@ end
|
||||
# end
|
||||
|
||||
|
||||
|
||||
""" Convert a vector of dict into 1-continous string.
|
||||
|
||||
Arguments:
|
||||
@@ -974,13 +978,21 @@ end
|
||||
|
||||
"""
|
||||
function keywordMemoryUpdate!(keywordmemory::AbstractDict, newinfo::AbstractDict)
|
||||
|
||||
for (k, v) in newinfo
|
||||
k = String(k)
|
||||
if v === nothing && haskey(keywordmemory, k) && keywordmemory[k] !== nothing
|
||||
# skip
|
||||
|
||||
# some time input keyword is different than dict's key thus it skip update
|
||||
# e.x. input key "tannin level" => "low to medium"
|
||||
# dict key "wine tannin level" => "low to medium"
|
||||
similar_k = checkSimilarKey(keywordmemory, k)
|
||||
k = similar_k === nothing ? k : similar_k
|
||||
|
||||
if v === nothing && haskey(keywordmemory, similar_k) && keywordmemory[similar_k] !== nothing
|
||||
# do not write value nothing if the key already has value
|
||||
else
|
||||
if haskey(keywordmemory, k)
|
||||
println("before k $k v $(keywordmemory[k])") #BUG this function write null to a non-empty key
|
||||
println("before k $k v $(keywordmemory[k])")
|
||||
end
|
||||
println("write k $k v $v")
|
||||
keywordmemory[k] = v
|
||||
@@ -991,7 +1003,18 @@ function keywordMemoryUpdate!(keywordmemory::AbstractDict, newinfo::AbstractDict
|
||||
end
|
||||
|
||||
|
||||
|
||||
function checkSimilarKey(dict::AbstractDict, key::AbstractString)
|
||||
similar_k = nothing
|
||||
key = replace(key, "_" => " ")
|
||||
key = replace(key, "-" => " ")
|
||||
for (k, v) in dict
|
||||
if occursin(key, String(k))
|
||||
similar_k = k
|
||||
break
|
||||
end
|
||||
end
|
||||
return similar_k
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user