31 Commits

Author SHA1 Message Date
narawat lamaiin
c0edf7dadf update 2025-04-04 15:04:02 +07:00
narawat lamaiin
c21f943b12 update 2025-04-01 21:17:15 +07:00
narawat lamaiin
b8fd772a28 update 2025-03-31 21:30:14 +07:00
narawat lamaiin
883f581b2a update 2025-03-22 15:34:00 +07:00
narawat lamaiin
5a890860a6 update 2025-03-22 09:42:51 +07:00
7d5bc14a09 mark new version 2025-03-21 10:13:53 +07:00
ton
37ba3a9d31 Merge pull request 'v0.1.3-dev' (#2) from v0.1.3-dev into main
Reviewed-on: #2
2025-03-21 03:09:16 +00:00
bfadd53033 update 2025-03-21 10:03:08 +07:00
8fc3afe348 update 2025-03-20 16:15:38 +07:00
c60037226a update 2025-03-13 19:11:20 +07:00
narawat lamaiin
db6c9c5f2b update 2025-03-07 13:34:15 +07:00
narawat lamaiin
6504099959 update 2025-01-31 09:50:44 +07:00
724b092bdb update 2025-01-30 21:28:49 +07:00
c56c3d02b0 update 2025-01-29 12:16:01 +07:00
ton
a7f3e29e9c Merge pull request 'WIP v0.1.2-dev' (#1) from v0.1.2-dev into main
Reviewed-on: #1
2025-01-25 07:30:18 +00:00
narawat lamaiin
b8fc23b41e update 2025-01-25 14:21:37 +07:00
narawat lamaiin
cf4cd13b14 update 2025-01-25 13:31:23 +07:00
narawat lamaiin
29adc077d5 update 2025-01-23 19:34:13 +07:00
narawat lamaiin
d89d425885 update 2025-01-21 08:28:26 +07:00
narawat lamaiin
bb81b973d3 update 2025-01-20 18:19:38 +07:00
narawat lamaiin
4197625e57 update 2025-01-17 22:09:48 +07:00
narawat lamaiin
3fdc0adf99 update 2025-01-16 07:40:39 +07:00
narawat lamaiin
c7000f66b8 update 2025-01-15 08:35:25 +07:00
narawat lamaiin
2206831bab update 2025-01-15 06:13:18 +07:00
narawat lamaiin
a29e8049a7 update 2025-01-11 16:57:57 +07:00
narawat lamaiin
944d9eaf2b update 2025-01-10 18:08:21 +07:00
narawat lamaiin
616c159336 update 2025-01-10 08:06:01 +07:00
narawat lamaiin
022cb5caf0 update 2025-01-05 17:41:21 +07:00
cff0d31ae6 update 2025-01-04 16:10:23 +07:00
82167fe006 update 2025-01-04 16:07:18 +07:00
814a0ecc6a update 2024-12-27 20:53:15 +07:00
11 changed files with 2158 additions and 1599 deletions

View File

@@ -1,7 +1,7 @@
name = "YiemAgent" name = "YiemAgent"
uuid = "e012c34b-7f78-48e0-971c-7abb83b6f0a2" uuid = "e012c34b-7f78-48e0-971c-7abb83b6f0a2"
authors = ["narawat lamaiin <narawat@outlook.com>"] authors = ["narawat lamaiin <narawat@outlook.com>"]
version = "0.1.1" version = "0.1.4"
[deps] [deps]
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
@@ -22,6 +22,6 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
[compat] [compat]
DataFrames = "1.7.0" DataFrames = "1.7.0"
GeneralUtils = "0.1.0" GeneralUtils = "0.1, 0.2"
LLMMCTS = "0.1.2" LLMMCTS = "0.1.2"
SQLLLM = "0.2.0" SQLLLM = "0.2.0"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -11,8 +11,8 @@ abstract type agent end
mutable struct companion <: agent mutable struct companion <: agent
name::String # agent name
id::String # agent id id::String # agent id
systemmsg::Union{String, Nothing}
maxHistoryMsg::Integer # e.g. 21th and earlier messages will get summarized maxHistoryMsg::Integer # e.g. 21th and earlier messages will get summarized
""" Memory """ Memory
@@ -34,8 +34,8 @@ end
function companion( function companion(
text2textInstructLLM::Function text2textInstructLLM::Function
; ;
name::String= "Assistant",
id::String= string(uuid4()), id::String= string(uuid4()),
systemmsg::Union{String, Nothing}= nothing,
maxHistoryMsg::Integer= 20, maxHistoryMsg::Integer= 20,
chathistory::Vector{Dict{Symbol, String}} = Vector{Dict{Symbol, String}}(), chathistory::Vector{Dict{Symbol, String}} = Vector{Dict{Symbol, String}}(),
) )
@@ -48,13 +48,13 @@ function companion(
) )
newAgent = companion( newAgent = companion(
name, id,
id, systemmsg,
maxHistoryMsg, maxHistoryMsg,
chathistory, chathistory,
memory, memory,
text2textInstructLLM text2textInstructLLM
) )
return newAgent return newAgent
end end
@@ -146,7 +146,6 @@ mutable struct sommelier <: agent
""" """
chathistory::Vector{Dict{Symbol, Any}} chathistory::Vector{Dict{Symbol, Any}}
memory::Dict{Symbol, Any} memory::Dict{Symbol, Any}
func # NamedTuple of functions func # NamedTuple of functions
end end
@@ -179,14 +178,17 @@ function sommelier(
# ), # ),
) )
memory = Dict{Symbol, Any}( memory = Dict{Symbol, Any}(
:chatbox=> "", :chatbox=> "",
:shortmem=> OrderedDict{Symbol, Any}(), :shortmem=> OrderedDict{Symbol, Any}(
:events=> Vector{Dict{Symbol, Any}}(), :available_wine=> [],
:state=> Dict{Symbol, Any}( :found_wine=> [], # used by decisionMaker(). This is to prevent decisionMaker() keep presenting the same wines
:wine_presented_to_user=> "None", ),
), :events=> Vector{Dict{Symbol, Any}}(),
) :state=> Dict{Symbol, Any}(
),
:recap=> OrderedDict{Symbol, Any}(),
)
newAgent = sommelier( newAgent = sommelier(
name, name,

View File

@@ -1,6 +1,7 @@
module util module util
export clearhistory, addNewMessage, vectorOfDictToText, eventdict, noises export clearhistory, addNewMessage, chatHistoryToText, eventdict, noises, createTimeline,
availableWineToText
using UUIDs, Dates, DataStructures, HTTP, JSON3 using UUIDs, Dates, DataStructures, HTTP, JSON3
using GeneralUtils using GeneralUtils
@@ -106,7 +107,7 @@ function addNewMessage(a::T1, name::String, text::T2;
error("name is not in agent.availableRole $(@__LINE__)") error("name is not in agent.availableRole $(@__LINE__)")
end end
#[] summarize the oldest 10 message #[PENDING] summarize the oldest 10 message
if length(a.chathistory) > maximumMsg if length(a.chathistory) > maximumMsg
summarize(a.chathistory) summarize(a.chathistory)
else else
@@ -121,47 +122,53 @@ This function takes in a vector of dictionaries and outputs a single string wher
# Arguments # Arguments
- `vecd::Vector` - `vecd::Vector`
a vector of dictionaries A vector of dictionaries containing chat messages
- `withkey::Bool` - `withkey::Bool`
whether to include the key in the output text. Default is true Whether to include the name as a prefix in the output text. Default is true
- `range::Union{Nothing,UnitRange,Int}`
Optional range of messages to include. If nothing, includes all messages
# Return # Returns
a string with the formatted dictionaries A formatted string where each line contains either:
- If withkey=true: "name> message\n"
- If withkey=false: "message\n"
# Example # Example
```jldoctest
julia> using Revise julia> using Revise
julia> using GeneralUtils julia> using GeneralUtils
julia> vecd = [Dict(:name => "John", :text => "Hello"), Dict(:name => "Jane", :text => "Goodbye")] julia> vecd = [Dict(:name => "John", :text => "Hello"), Dict(:name => "Jane", :text => "Goodbye")]
julia> GeneralUtils.vectorOfDictToText(vecd, withkey=true) julia> GeneralUtils.vectorOfDictToText(vecd, withkey=true)
"John> Hello\nJane> Goodbye\n" "John> Hello\nJane> Goodbye\n"
``` ```
# Signature
""" """
function vectorOfDictToText(vecd::Vector; withkey=true)::String function chatHistoryToText(vecd::Vector; withkey=true, range=nothing)::String
# Initialize an empty string to hold the final text # Initialize an empty string to hold the final text
text = "" text = ""
# Get the elements within the specified range, or all elements if no range provided
elements = isnothing(range) ? vecd : vecd[range]
# Determine whether to include the key in the output text or not # Determine whether to include the key in the output text or not
if withkey if withkey
# Loop through each dictionary in the input vector # Loop through each dictionary in the input vector
for d in vecd for d in elements
# Extract the 'name' and 'text' keys from the dictionary # Extract the 'name' and 'text' keys from the dictionary
name = d[:name] name = d[:name]
_text = d[:text] _text = d[:text]
# Append the formatted string to the text variable # Append the formatted string to the text variable
text *= "$name> $_text \n" text *= "$name:> $_text \n"
end end
else else
# Loop through each dictionary in the input vector # Loop through each dictionary in the input vector
for d in vecd for d in elements
# Iterate over all key-value pairs in the dictionary # Iterate over all key-value pairs in the dictionary
for (k, v) in d for (k, v) in d
# Append the formatted string to the text variable # Append the formatted string to the text variable
text *= "$v \n" text *= "$v \n"
end end
end end
end end
# Return the final text # Return the final text
@@ -169,11 +176,63 @@ function vectorOfDictToText(vecd::Vector; withkey=true)::String
end end
function availableWineToText(vecd::Vector)::String
# Initialize an empty string to hold the final text
rowtext = ""
# Loop through each dictionary in the input vector
for (i, d) in enumerate(vecd)
# Iterate over all key-value pairs in the dictionary
temp = []
for (k, v) in d
# Append the formatted string to the text variable
t = "$k:$v"
push!(temp, t)
end
_rowtext = join(temp, ',')
rowtext *= "$i) $_rowtext "
end
return rowtext
end
""" Create a dictionary representing an event with optional details.
# Arguments
- `event_description::Union{String, Nothing}`
A description of the event
- `timestamp::Union{DateTime, Nothing}`
The time when the event occurred
- `subject::Union{String, Nothing}`
The subject or entity associated with the event
- `thought::Union{AbstractDict, Nothing}`
Any associated thoughts or metadata
- `actionname::Union{String, Nothing}`
The name of the action performed (e.g., "CHAT", "CHECKINVENTORY")
- `actioninput::Union{String, Nothing}`
Input or parameters for the action
- `location::Union{String, Nothing}`
Where the event took place
- `equipment_used::Union{String, Nothing}`
Equipment involved in the event
- `material_used::Union{String, Nothing}`
Materials used during the event
- `outcome::Union{String, Nothing}`
The result or consequence of the event after action execution
- `note::Union{String, Nothing}`
Additional notes or comments
# Returns
A dictionary with event details as symbol-keyed key-value pairs
"""
function eventdict(; function eventdict(;
event_description::Union{String, Nothing}=nothing, event_description::Union{String, Nothing}=nothing,
timestamp::Union{DateTime, Nothing}=nothing, timestamp::Union{DateTime, Nothing}=nothing,
subject::Union{String, Nothing}=nothing, subject::Union{String, Nothing}=nothing,
action_or_dialogue::Union{String, Nothing}=nothing, thought::Union{AbstractDict, Nothing}=nothing,
actionname::Union{String, Nothing}=nothing, # "CHAT", "CHECKINVENTORY", "PRESENTBOX", etc
actioninput::Union{String, Nothing}=nothing,
location::Union{String, Nothing}=nothing, location::Union{String, Nothing}=nothing,
equipment_used::Union{String, Nothing}=nothing, equipment_used::Union{String, Nothing}=nothing,
material_used::Union{String, Nothing}=nothing, material_used::Union{String, Nothing}=nothing,
@@ -184,7 +243,9 @@ function eventdict(;
:event_description=> event_description, :event_description=> event_description,
:timestamp=> timestamp, :timestamp=> timestamp,
:subject=> subject, :subject=> subject,
:action_or_dialogue=> action_or_dialogue, :thought=> thought,
:actionname=> actionname,
:actioninput=> actioninput,
:location=> location, :location=> location,
:equipment_used=> equipment_used, :equipment_used=> equipment_used,
:material_used=> material_used, :material_used=> material_used,
@@ -194,6 +255,61 @@ function eventdict(;
end end
""" Create a formatted timeline string from a sequence of events.
# Arguments
- `events::T1`
Vector of event dictionaries containing subject, actioninput and optional outcome fields
Each event dictionary should have the following keys:
- :subject - The subject or entity performing the action
- :actioninput - The action or input performed by the subject
- :outcome - (Optional) The result or outcome of the action
# Returns
- `timeline::String`
A formatted string representing the events with their subjects, actions, and optional outcomes
Format: "{index}) {subject}> {actioninput} {outcome}\n" for each event
# Example
events = [
Dict(:subject => "User", :actioninput => "Hello", :outcome => nothing),
Dict(:subject => "Assistant", :actioninput => "Hi there!", :outcome => "with a smile")
]
timeline = createTimeline(events)
# 1) User> Hello
# 2) Assistant> Hi there! with a smile
"""
function createTimeline(events::T1; eventindex::Union{UnitRange, Nothing}=nothing
) where {T1<:AbstractVector}
# Initialize empty timeline string
timeline = ""
# Determine which indices to use - either provided range or full length
ind =
if eventindex !== nothing
[eventindex...]
else
1:length(events)
end
# Iterate through events and format each one
for (i, event) in zip(ind, events)
# If no outcome exists, format without outcome
if event[:outcome] === nothing
timeline *= "Event_$i $(event[:subject])> $(event[:actioninput])\n"
# If outcome exists, include it in formatting
else
timeline *= "Event_$i $(event[:subject])> $(event[:actioninput]) $(event[:outcome])\n"
end
end
# Return formatted timeline string
return timeline
end
# """ Convert a single chat dictionary into LLM model instruct format. # """ Convert a single chat dictionary into LLM model instruct format.

41
test/Manifest.toml Normal file
View File

@@ -0,0 +1,41 @@
# This file is machine-generated - editing it directly is not advised
julia_version = "1.11.4"
manifest_format = "2.0"
project_hash = "71d91126b5a1fb1020e1098d9d492de2a4438fd2"
[[deps.Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
version = "1.11.0"
[[deps.InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
version = "1.11.0"
[[deps.Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
version = "1.11.0"
[[deps.Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
version = "1.11.0"
[[deps.Random]]
deps = ["SHA"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
version = "1.11.0"
[[deps.SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
version = "0.7.0"
[[deps.Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
version = "1.11.0"
[[deps.Test]]
deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
version = "1.11.0"

2
test/Project.toml Normal file
View File

@@ -0,0 +1,2 @@
[deps]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

View File

@@ -27,30 +27,50 @@
"description": "agent role" "description": "agent role"
}, },
"organization": { "organization": {
"value": "yiem_hq", "value": "yiem_branch_1",
"description": "organization name" "description": "organization name"
}, },
"externalservice": { "externalservice": {
"text2textinstruct": { "loadbalancer": {
"mqtttopic": "/loadbalancer/requestingservice", "mqtttopic": "/loadbalancer/requestingservice",
"description": "text to text service with instruct LLM", "description": "text to text service with instruct LLM"
"llminfo": { },
"name": "llama3instruct" "text2textinstruct": {
} "mqtttopic": "/loadbalancer/requestingservice",
}, "description": "text to text service with instruct LLM",
"virtualWineCustomer_1": { "llminfo": {
"mqtttopic": "/virtualenvironment/winecustomer", "name": "llama3instruct"
"description": "text to text service with instruct LLM that act as wine customer", }
"llminfo": { },
"name": "llama3instruct" "virtualWineCustomer_1": {
} "mqtttopic": "/virtualenvironment/winecustomer",
}, "description": "text to text service with instruct LLM that act as wine customer",
"text2textchat": { "llminfo": {
"mqtttopic": "/loadbalancer/requestingservice", "name": "llama3instruct"
"description": "text to text service with instruct LLM", }
"llminfo": { },
"name": "llama3instruct" "text2textchat": {
} "mqtttopic": "/loadbalancer/requestingservice",
} "description": "text to text service with instruct LLM",
"llminfo": {
"name": "llama3instruct"
}
},
"wineDB" : {
"description": "A wine database connection info for LibPQ client",
"host": "192.168.88.12",
"port": 10201,
"dbname": "wineDB",
"user": "yiemtechnologies",
"password": "yiemtechnologies@Postgres_0.0"
},
"SQLVectorDB" : {
"description": "A wine database connection info for LibPQ client",
"host": "192.168.88.12",
"port": 10203,
"dbname": "SQLVectorDB",
"user": "yiemtechnologies",
"password": "yiemtechnologies@Postgres_0.0"
}
} }
} }

View File

@@ -1,9 +0,0 @@
using GeneralUtils
response = "trajectory_evaluation:\nThe trajectory is correct so far. The thought accurately reflects the user's question, and the action taken is a valid attempt to retrieve data from the database that matches the specified criteria.\n\nanswer_evaluation:\nThe observation provides information about two red wines from Bordeaux rive droite in France, which partially answers the question. However, it does not provide a complete answer as it only lists the wine names and characteristics, but does not explicitly state whether there are any other wines that match the criteria.\n\naccepted_as_answer: No\n\nscore: 6\nThe trajectory is mostly correct, but the observation does not fully address the question.\n\nsuggestion: Consider adding more filters or parameters to the database query to retrieve a complete list of wines that match the specified criteria."
responsedict = GeneralUtils.textToDict(response,
["trajectory_evaluation", "answer_evaluation", "accepted_as_answer", "score", "suggestion"],
rightmarker=":", symbolkey=true)

0
test/runtests.jl Normal file
View File

View File

@@ -8,31 +8,41 @@ using Base.Threads
# load config # load config
config = JSON3.read("./test/config.json") config = JSON3.read("/appfolder/app/dev/YiemAgent/test/config.json")
# config = copy(JSON3.read("../mountvolume/config.json")) # config = copy(JSON3.read("../mountvolume/config.json"))
function executeSQL(sql::T) where {T<:AbstractString} function executeSQL(sql::T) where {T<:AbstractString}
DBconnection = LibPQ.Connection("host=192.168.88.12 port=10201 dbname=wineDB user=yiemtechnologies password=yiemtechnologies@Postgres_0.0") host = config[:externalservice][:wineDB][:host]
port = config[:externalservice][:wineDB][:port]
dbname = config[:externalservice][:wineDB][:dbname]
user = config[:externalservice][:wineDB][:user]
password = config[:externalservice][:wineDB][:password]
DBconnection = LibPQ.Connection("host=$host port=$port dbname=$dbname user=$user password=$password")
result = LibPQ.execute(DBconnection, sql) result = LibPQ.execute(DBconnection, sql)
close(DBconnection) close(DBconnection)
return result return result
end end
function executeSQLVectorDB(sql) function executeSQLVectorDB(sql)
DBconnection = LibPQ.Connection("host=192.168.88.12 port=10203 dbname=SQLVectorDB user=yiemtechnologies password=yiemtechnologies@Postgres_0.0") host = config[:externalservice][:SQLVectorDB][:host]
port = config[:externalservice][:SQLVectorDB][:port]
dbname = config[:externalservice][:SQLVectorDB][:dbname]
user = config[:externalservice][:SQLVectorDB][:user]
password = config[:externalservice][:SQLVectorDB][:password]
DBconnection = LibPQ.Connection("host=$host port=$port dbname=$dbname user=$user password=$password")
result = LibPQ.execute(DBconnection, sql) result = LibPQ.execute(DBconnection, sql)
close(DBconnection) close(DBconnection)
return result return result
end end
function text2textInstructLLM(prompt::String) function text2textInstructLLM(prompt::String; maxattempt::Integer=2, modelsize::String="medium")
msgMeta = GeneralUtils.generate_msgMeta( msgMeta = GeneralUtils.generate_msgMeta(
config[:externalservice][:text2textinstruct][:mqtttopic]; config[:externalservice][:loadbalancer][:mqtttopic];
msgPurpose="inference", msgPurpose="inference",
senderName="yiemagent", senderName="yiemagent",
senderId=string(uuid4()), senderId=sessionId,
receiverName="text2textinstruct", receiverName="text2textinstruct_$modelsize",
mqttBrokerAddress=config[:mqttServerInfo][:broker], mqttBrokerAddress=config[:mqttServerInfo][:broker],
mqttBrokerPort=config[:mqttServerInfo][:port], mqttBrokerPort=config[:mqttServerInfo][:port],
) )
@@ -48,8 +58,20 @@ function text2textInstructLLM(prompt::String)
) )
) )
_response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=6000) response = nothing
response = _response[:response][:text] for attempts in 1:maxattempt
_response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=180, maxattempt=maxattempt)
payload = _response[:response]
if _response[:success] && payload[:text] !== nothing
response = _response[:response][:text]
break
else
println("\n<text2textInstructLLM()> attempt $attempts/$maxattempt failed ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
pprintln(outgoingMsg)
println("</text2textInstructLLM()> attempt $attempts/$maxattempt failed ", @__FILE__, ":", @__LINE__, " $(Dates.now())\n")
sleep(3)
end
end
return response return response
end end
@@ -57,11 +79,11 @@ end
# get text embedding from a LLM service # get text embedding from a LLM service
function getEmbedding(text::T) where {T<:AbstractString} function getEmbedding(text::T) where {T<:AbstractString}
msgMeta = GeneralUtils.generate_msgMeta( msgMeta = GeneralUtils.generate_msgMeta(
config[:externalservice][:text2textinstruct][:mqtttopic]; config[:externalservice][:loadbalancer][:mqtttopic];
msgPurpose="embedding", msgPurpose="embedding",
senderName="yiemagent", senderName="yiemagent",
senderId=string(uuid4()), senderId=sessionId,
receiverName="text2textinstruct", receiverName="textembedding",
mqttBrokerAddress=config[:mqttServerInfo][:broker], mqttBrokerAddress=config[:mqttServerInfo][:broker],
mqttBrokerPort=config[:mqttServerInfo][:port], mqttBrokerPort=config[:mqttServerInfo][:port],
) )
@@ -72,18 +94,17 @@ function getEmbedding(text::T) where {T<:AbstractString}
:text => [text] # must be a vector of string :text => [text] # must be a vector of string
) )
) )
response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=6000)
response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=120, maxattempt=3)
embedding = response[:response][:embeddings] embedding = response[:response][:embeddings]
return embedding return embedding
end end
function findSimilarTextFromVectorDB(text::T1, tablename::T2, embeddingColumnName::T3, function findSimilarTextFromVectorDB(text::T1, tablename::T2, embeddingColumnName::T3,
vectorDB::Function; limit::Integer=1 vectorDB::Function; limit::Integer=1
)::DataFrame where {T1<:AbstractString, T2<:AbstractString, T3<:AbstractString} )::DataFrame where {T1<:AbstractString, T2<:AbstractString, T3<:AbstractString}
# get embedding from LLM service # get embedding from LLM service
embedding = getEmbedding(text)[1] embedding = getEmbedding(text)[1]
# check whether there is close enough vector already store in vectorDB. if no, add, else skip # check whether there is close enough vector already store in vectorDB. if no, add, else skip
sql = """ sql = """
SELECT *, $embeddingColumnName <-> '$embedding' as distance SELECT *, $embeddingColumnName <-> '$embedding' as distance
@@ -95,29 +116,29 @@ function findSimilarTextFromVectorDB(text::T1, tablename::T2, embeddingColumnNam
return df return df
end end
function similarSQLVectorDB(query; maxdistance::Integer=100) function similarSQLVectorDB(query; maxdistance::Integer=100)
tablename = "sqlllm_decision_repository" tablename = "sqlllm_decision_repository"
# get embedding of the query # get embedding of the query
df = findSimilarTextFromVectorDB(query, tablename, df = findSimilarTextFromVectorDB(query, tablename,
"function_input_embedding", executeSQLVectorDB) "function_input_embedding", executeSQLVectorDB)
# println(df[1, [:id, :function_output]])
row, col = size(df) row, col = size(df)
distance = row == 0 ? Inf : df[1, :distance] distance = row == 0 ? Inf : df[1, :distance]
# distance = 100 # CHANGE this is for testing only
if row != 0 && distance < maxdistance if row != 0 && distance < maxdistance
# if there is usable SQL, return it. # if there is usable SQL, return it.
output_b64 = df[1, :function_output_base64] # pick the closest match output_b64 = df[1, :function_output_base64] # pick the closest match
output_str = String(base64decode(output_b64)) output_str = String(base64decode(output_b64))
rowid = df[1, :id] rowid = df[1, :id]
println("\n~~~ found similar sql. row id $rowid, distance $distance ", @__FILE__, " ", @__LINE__) println("\n~~~ found similar sql. row id $rowid, distance $distance ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
return (dict=output_str, distance=distance) return (dict=output_str, distance=distance)
else else
println("\n~~~ similar sql not found, max distance $maxdistance ", @__FILE__, " ", @__LINE__) println("\n~~~ similar sql not found, max distance $maxdistance ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
return (dict=nothing, distance=nothing) return (dict=nothing, distance=nothing)
end end
end end
function insertSQLVectorDB(query::T1, SQL::T2; maxdistance::Integer=3) where {T1<:AbstractString, T2<:AbstractString}
function insertSQLVectorDB(query::T1, SQL::T2; maxdistance::Integer=1) where {T1<:AbstractString, T2<:AbstractString}
tablename = "sqlllm_decision_repository" tablename = "sqlllm_decision_repository"
# get embedding of the query # get embedding of the query
# query = state[:thoughtHistory][:question] # query = state[:thoughtHistory][:question]
@@ -134,14 +155,14 @@ function insertSQLVectorDB(query::T1, SQL::T2; maxdistance::Integer=1) where {T1
sql = """ sql = """
INSERT INTO $tablename (function_input, function_output, function_output_base64, function_input_embedding) VALUES ('$query', '$sql_', '$sql_base64', '$query_embedding'); INSERT INTO $tablename (function_input, function_output, function_output_base64, function_input_embedding) VALUES ('$query', '$sql_', '$sql_base64', '$query_embedding');
""" """
println("\n~~~ added new decision to vectorDB ", @__FILE__, " ", @__LINE__) # println("\n~~~ added new decision to vectorDB ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
println(sql) # println(sql)
_ = executeSQLVectorDB(sql) _ = executeSQLVectorDB(sql)
end end
end end
function similarSommelierDecision(recentevents::T1; maxdistance::Integer=5 function similarSommelierDecision(recentevents::T1; maxdistance::Integer=3
)::Union{AbstractDict, Nothing} where {T1<:AbstractString} )::Union{AbstractDict, Nothing} where {T1<:AbstractString}
tablename = "sommelier_decision_repository" tablename = "sommelier_decision_repository"
# find similar # find similar
@@ -214,7 +235,7 @@ a = YiemAgent.sommelier(
) )
while true while true
println("your respond: ") print("\nyour respond: ")
user_answer = readline() user_answer = readline()
response = YiemAgent.conversation(a, Dict(:text=> user_answer)) response = YiemAgent.conversation(a, Dict(:text=> user_answer))
println("\n$response") println("\n$response")
@@ -224,14 +245,13 @@ end
# response = YiemAgent.conversation(a, Dict(:text=> "I want to get a French red wine under 100.")) # response = YiemAgent.conversation(a, Dict(:text=> "I want to get a French red wine under 100."))
"""
hello I want to get a bottle of red wine for my boss. I have a budget around 50 dollars. Show me some options.
I have no idea about his wine taste but he likes spicy food.
"""