update
This commit is contained in:
+5
-5
@@ -5,7 +5,7 @@ export yiemAgent, _agent_loop, OpenAiToUserMessage
|
|||||||
using JSON, DataStructures, Dates, UUIDs, HTTP, Random, PrettyPrinting, Serialization,
|
using JSON, DataStructures, Dates, UUIDs, HTTP, Random, PrettyPrinting, Serialization,
|
||||||
DataFrames, Base.Threads
|
DataFrames, Base.Threads
|
||||||
using GeneralUtils
|
using GeneralUtils
|
||||||
using ..type, ..utils
|
using ..type, ..utils, ..toolRegistry
|
||||||
|
|
||||||
# ---------------------------------------------- 100 --------------------------------------------- #
|
# ---------------------------------------------- 100 --------------------------------------------- #
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ on `inputChannel` and `followUpChannel` channels concurrently.
|
|||||||
"""
|
"""
|
||||||
function yiemAgent(
|
function yiemAgent(
|
||||||
toolsFolderPath::String,
|
toolsFolderPath::String,
|
||||||
llmCall::Function,
|
llmCall,
|
||||||
;
|
;
|
||||||
systemPrompt::String="You are helpful assistant.",
|
systemPrompt::String="You are helpful assistant.",
|
||||||
model=nothing,
|
model=nothing,
|
||||||
@@ -107,12 +107,12 @@ function yiemAgent(
|
|||||||
outputChannel = Channel(16)
|
outputChannel = Channel(16)
|
||||||
|
|
||||||
# load tools from toolsFolderPath
|
# load tools from toolsFolderPath
|
||||||
toolStore = YiemAgent.toolStore(name="myagent")
|
toolStore1 = toolStore(name="myagent")
|
||||||
loadTools(toolStore, toolsFolderPath)
|
loadTools(toolStore1, toolsFolderPath)
|
||||||
|
|
||||||
# Create struct with a placeholder task, then spawn and replace it
|
# Create struct with a placeholder task, then spawn and replace it
|
||||||
agent = yiemAgent(
|
agent = yiemAgent(
|
||||||
agentState(systemPrompt, model, getTools(toolStore), messages),
|
agentState(systemPrompt, model, getTools(toolStore1), messages),
|
||||||
inputChannel,
|
inputChannel,
|
||||||
followUp,
|
followUp,
|
||||||
outputChannel,
|
outputChannel,
|
||||||
|
|||||||
+16
-16
@@ -144,7 +144,7 @@ assistantMessage("assistant", [textContent("Hello!")], "", "", "gpt-4", ..., "en
|
|||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
function assistantMessage(; role="assistant", content=Vector{messageContent}(),
|
function assistantMessage(; role="assistant", content=Vector{messageContent}(),
|
||||||
api="", provider="", model="", usage=llmUsage(0, 0), stopReason="end_turn",
|
api="", provider="", model=nothing, usage=llmUsage(0, 0), stopReason="end_turn",
|
||||||
errorMessage=nothing, timestamp=now())
|
errorMessage=nothing, timestamp=now())
|
||||||
return assistantMessage(role, content, api, provider, model, usage, stopReason, errorMessage, timestamp)
|
return assistantMessage(role, content, api, provider, model, usage, stopReason, errorMessage, timestamp)
|
||||||
end
|
end
|
||||||
@@ -309,7 +309,7 @@ end
|
|||||||
|
|
||||||
mutable struct agentState # Mutable runtime state of an agent
|
mutable struct agentState # Mutable runtime state of an agent
|
||||||
systemPrompt::String # System prompt for the agent
|
systemPrompt::String # System prompt for the agent
|
||||||
model::llmModel # LLM model to use
|
model::Union{llmModel, Nothing} # LLM model to use
|
||||||
tools::OrderedDict{String, agentTool} # Available tools keyed by name, insertion-ordered
|
tools::OrderedDict{String, agentTool} # Available tools keyed by name, insertion-ordered
|
||||||
|
|
||||||
# messages history includes userMessage, assistantMessage, toolResultMessage. NO system prompt
|
# messages history includes userMessage, assistantMessage, toolResultMessage. NO system prompt
|
||||||
@@ -341,21 +341,21 @@ julia> state = agentState(systemPrompt="You are a helpful assistant")
|
|||||||
agentState("You are a helpful assistant", OrderedDict{String, agentTool}(), agentMessage[], String[], nothing)
|
agentState("You are a helpful assistant", OrderedDict{String, agentTool}(), agentMessage[], String[], nothing)
|
||||||
"""
|
"""
|
||||||
function agentState(
|
function agentState(
|
||||||
systemPrompt::String="",
|
systemPrompt::String="",
|
||||||
model::llmModel=llmModel{String}("", "", "unknown", "unknown", "", false, String[],
|
model::llmModel=llmModel{String}("", "unknown", "unknown", "", false, String[],
|
||||||
modelCost(0.0, 0.0, 0.0, 0.0), 0, 0),
|
modelCost(0.0, 0.0, 0.0, 0.0), 0, 0),
|
||||||
tools::OrderedDict{String, agentTool}=OrderedDict{String, agentTool}(),
|
tools::OrderedDict{String, agentTool}=OrderedDict{String, agentTool}(),
|
||||||
messages::Vector{agentMessage}=agentMessage[],
|
messages::Vector{agentMessage}=agentMessage[],
|
||||||
)
|
)
|
||||||
agentState(
|
agentState(
|
||||||
systemPrompt,
|
systemPrompt,
|
||||||
model,
|
model,
|
||||||
deepcopy(tools),
|
deepcopy(tools),
|
||||||
deepcopy(messages),
|
deepcopy(messages),
|
||||||
Vector{String}(),
|
Vector{String}(),
|
||||||
false,
|
false,
|
||||||
nothing,
|
nothing,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+276
-355
@@ -1,232 +1,293 @@
|
|||||||
using JSON, Dates, UUIDs, PrettyPrinting, LibPQ, Base64, DataFrames, DataStructures, HTTP, Base64,
|
using JSON, Dates, UUIDs, PrettyPrinting, LibPQ, Base64, DataFrames, DataStructures, HTTP, Base64,
|
||||||
NATS, Base.Threads
|
NATS, Base.Threads
|
||||||
using YiemAgent, GeneralUtils, msghandler
|
using YiemAgent, GeneralUtils
|
||||||
|
|
||||||
function text2text_instruct_llm(sender_id::String, openai_msg::Dict{String, Any})
|
struct text2textInstructLLM
|
||||||
payloads = [("msg", openai_msg, "dictionary")] # List of tuples
|
natsConn::NATS.Connection
|
||||||
_, msg_envelope_json_str = msghandler.smartpack(
|
topic::String
|
||||||
config["externalservice"]["servicesloadbalancer"]["nats"],
|
senderID::String
|
||||||
payloads;
|
fileserver_url::String
|
||||||
sender_id=sender_id,
|
end
|
||||||
msg_purpose="text2text",
|
|
||||||
broker_url=config["nats_server_info"]["url"],
|
|
||||||
fileserver_url=config["externalservice"]["fileserver"]["url"])
|
|
||||||
|
|
||||||
reply = NATS.request(agent_conn,
|
function (t::text2textInstructLLM)(openai_msg::Dict{String, Any})
|
||||||
config["externalservice"]["servicesloadbalancer"]["nats"],
|
payloads = [("msg", openai_msg, "dictionary")] # List of tuples
|
||||||
msg_envelope_json_str, timeout=120)
|
_, msg_envelope_json_str = msghandler.smartpack(
|
||||||
|
t.topic,
|
||||||
|
payloads;
|
||||||
|
sender_id=t.senderID,
|
||||||
|
msg_purpose="text2text",
|
||||||
|
fileserver_url=t.fileserver_url)
|
||||||
|
|
||||||
incoming_env_json_str = String(reply.payload)
|
reply = NATS.request(t.natsConn, t.topic, msg_envelope_json_str, timeout=180)
|
||||||
incoming_env = msghandler.smartunpack(incoming_env_json_str)
|
|
||||||
_llm_response = incoming_env["payloads"][1][2]
|
|
||||||
llm_response = _llm_response["choices"][1]["message"]["content"]
|
|
||||||
return llm_response
|
|
||||||
end
|
|
||||||
|
|
||||||
""" get a single text embedding from a LLM service
|
incoming_env_json_str = String(reply.payload)
|
||||||
Example
|
incoming_env = msghandler.smartunpack(incoming_env_json_str)
|
||||||
text = ["hello"]
|
_llm_response = incoming_env["payloads"][1][2]
|
||||||
embedding = get_embedding(text)
|
llm_response = _llm_response["choices"][1]["message"]["content"]
|
||||||
"""
|
return llm_response
|
||||||
function get_embedding(text::AbstractArray{String})
|
end
|
||||||
documents_dict = Dict("documents" => text)
|
|
||||||
payloads = [("documents", documents_dict, "dictionary")]
|
|
||||||
_, msg_envelope_json_str = msghandler.smartpack(
|
|
||||||
config["externalservice"]["servicesloadbalancer"]["nats"],
|
|
||||||
payloads;
|
|
||||||
msg_purpose="embedding",
|
|
||||||
broker_url=config["nats_server_info"]["url"],
|
|
||||||
fileserver_url=config["externalservice"]["fileserver"]["url"])
|
|
||||||
|
|
||||||
reply = NATS.request(agent_conn,
|
|
||||||
config["externalservice"]["servicesloadbalancer"]["nats"],
|
|
||||||
msg_envelope_json_str, timeout=120)
|
|
||||||
incoming_env_json_str = String(reply.payload)
|
|
||||||
incoming_env = msghandler.smartunpack(incoming_env_json_str)
|
|
||||||
embedding_response = incoming_env["payloads"][1][2]
|
|
||||||
|
|
||||||
return embedding_response
|
# function get_embedding(text::AbstractArray{String})
|
||||||
end
|
# documents_dict = Dict("documents" => text)
|
||||||
|
# payloads = [("documents", documents_dict, "dictionary")]
|
||||||
|
# _, msg_envelope_json_str = msghandler.smartpack(
|
||||||
|
# config["externalservice"]["servicesloadbalancer"]["nats"],
|
||||||
|
# payloads;
|
||||||
|
# msg_purpose="embedding",
|
||||||
|
# broker_url=config["nats_server_info"]["url"],
|
||||||
|
# fileserver_url=config["externalservice"]["fileserver"]["url"])
|
||||||
|
|
||||||
""" sql = "SELECT * FROM wine;"
|
# reply = NATS.request(agent_conn,
|
||||||
result = execute_sql_winedb(sql)
|
# config["externalservice"]["servicesloadbalancer"]["nats"],
|
||||||
"""
|
# msg_envelope_json_str, timeout=120)
|
||||||
function execute_sql_winedb(sql::T) where {T<:AbstractString}
|
# incoming_env_json_str = String(reply.payload)
|
||||||
host_url, _port = split(config["externalservice"]["sommpanion_db"]["url"], ':')
|
# incoming_env = msghandler.smartunpack(incoming_env_json_str)
|
||||||
port = parse(Int, _port)
|
# embedding_response = incoming_env["payloads"][1][2]
|
||||||
dbname = "winedb"
|
|
||||||
user = config["externalservice"]["sommpanion_db"]["user"]
|
# return embedding_response
|
||||||
password = config["externalservice"]["sommpanion_db"]["password"]
|
# end
|
||||||
db_connection = LibPQ.Connection("host=$host_url port=$port dbname=$dbname user=$user password=$password")
|
|
||||||
result = nothing
|
|
||||||
try
|
# """ sql = "SELECT * FROM wine;"
|
||||||
result = LibPQ.execute(db_connection, sql)
|
# result = execute_sql_winedb(sql)
|
||||||
catch e
|
# """
|
||||||
LibPQ.close(db_connection)
|
# function execute_sql_winedb(sql::T) where {T<:AbstractString}
|
||||||
end
|
# host_url, _port = split(config["externalservice"]["sommpanion_db"]["url"], ':')
|
||||||
|
# port = parse(Int, _port)
|
||||||
|
# dbname = "winedb"
|
||||||
|
# user = config["externalservice"]["sommpanion_db"]["user"]
|
||||||
|
# password = config["externalservice"]["sommpanion_db"]["password"]
|
||||||
|
# db_connection = LibPQ.Connection("host=$host_url port=$port dbname=$dbname user=$user password=$password")
|
||||||
|
# result = nothing
|
||||||
|
# try
|
||||||
|
# result = LibPQ.execute(db_connection, sql)
|
||||||
|
# catch e
|
||||||
|
# LibPQ.close(db_connection)
|
||||||
|
# end
|
||||||
|
|
||||||
|
# LibPQ.close(db_connection)
|
||||||
|
# return result
|
||||||
|
# end
|
||||||
|
|
||||||
|
# """ find similar sql from vector database
|
||||||
|
# sql = "SELECT * FROM wine;"
|
||||||
|
# result, distance = similar_sql_vectordb(sql)
|
||||||
|
# """
|
||||||
|
# function similar_sql_vectordb(sql::T; maxdistance::Number=1) where {T<:AbstractString}
|
||||||
|
# tablename = "sqlllm_decision_repository"
|
||||||
|
# # get embedding of the query
|
||||||
|
# df = find_similar_text_from_vectordb(sql, tablename,
|
||||||
|
# "function_input_embedding", execute_sql_vectordb)
|
||||||
|
# # println(df[1, [:id, :function_output]])
|
||||||
|
# row, col = size(df)
|
||||||
|
# distance = row == 0 ? Inf : df[1, :distance]
|
||||||
|
# if row != 0 && distance < maxdistance
|
||||||
|
# # if there is usable SQL, return it.
|
||||||
|
# output_b64 = df[1, :function_output_base64] # pick the closest match
|
||||||
|
# output_str = String(base64decode(output_b64))
|
||||||
|
# rowid = df[1, :id]
|
||||||
|
# println("\n--| similar sql found. row id $rowid, distance $distance ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||||
|
# pprintln(output_str)
|
||||||
|
# return (result=output_str, distance=distance)
|
||||||
|
# else
|
||||||
|
# println("\n--| similar sql not found, max distance $maxdistance ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||||
|
# return (result=nothing, distance=nothing)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
# """ insert query and sql into vector database
|
||||||
|
# query = "get all wines from wine table"
|
||||||
|
# sql = "SELECT * FROM wine;"
|
||||||
|
# insert_sql_vectordb(query, sql)
|
||||||
|
# """
|
||||||
|
# function insert_sql_vectordb(query::T1, SQL::T2; maxdistance::Number=3
|
||||||
|
# ) where {T1<:AbstractString, T2<:AbstractString}
|
||||||
|
|
||||||
|
# tablename = "sqlllm_decision_repository"
|
||||||
|
# # get embedding of the query
|
||||||
|
# # query = state[:thoughtHistory][:question]
|
||||||
|
# df = find_similar_text_from_vectordb(query, tablename,
|
||||||
|
# "function_input_embedding", execute_sql_vectordb)
|
||||||
|
# row, col = size(df)
|
||||||
|
# distance = row == 0 ? Inf : df[1, :distance]
|
||||||
|
# if row == 0 || distance > maxdistance # no close enough SQL stored in the database
|
||||||
|
# _query_embedding = get_embedding([query])
|
||||||
|
# _query_embedding = GeneralUtils.dictify(_query_embedding)
|
||||||
|
# # println("\n--- _query_embedding() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
||||||
|
# # println(_query_embedding)
|
||||||
|
# # println("---\n")
|
||||||
|
# query_embedding = _query_embedding["data"][1]["embedding"]
|
||||||
|
# query = replace(query, "'" => "")
|
||||||
|
# sql_base64 = base64encode(SQL)
|
||||||
|
# sql_ = replace(SQL, "'" => "")
|
||||||
|
|
||||||
|
# sql =
|
||||||
|
# """
|
||||||
|
# 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__, " $(Dates.now())")
|
||||||
|
# # println(sql)
|
||||||
|
# _ = execute_sql_vectordb(sql)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
# """ execute sql against vectordb
|
||||||
|
# sql = "SELECT * FROM wine;"
|
||||||
|
# result = execute_sql_vectordb(sql)
|
||||||
|
# """
|
||||||
|
# function execute_sql_vectordb(sql::T) where {T<:AbstractString}
|
||||||
|
# host_url, _port = split(config["externalservice"]["sommpanion_vectordb"]["url"], ':')
|
||||||
|
# port = parse(Int, _port)
|
||||||
|
# dbname = config["externalservice"]["sommpanion_vectordb"]["dbname"]
|
||||||
|
# user = config["externalservice"]["sommpanion_vectordb"]["user"]
|
||||||
|
# password = config["externalservice"]["sommpanion_vectordb"]["password"]
|
||||||
|
# DBconnection = LibPQ.Connection("host=$host_url port=$port dbname=$dbname user=$user password=$password")
|
||||||
|
# result = LibPQ.execute(DBconnection, sql)
|
||||||
|
# close(DBconnection)
|
||||||
|
# return result
|
||||||
|
# end
|
||||||
|
|
||||||
|
# """ search similar decision llm made from vectordb
|
||||||
|
# """
|
||||||
|
# function similar_sommelier_decision(recentevents::T1; maxdistance::Integer=3
|
||||||
|
# )::Union{AbstractDict, Nothing} where {T1<:AbstractString}
|
||||||
|
|
||||||
|
# tablename = "sommelier_decision_repository"
|
||||||
|
# # find similar
|
||||||
|
# df = find_similar_text_from_vectordb(recentevents, tablename,
|
||||||
|
# "function_input_embedding", execute_sql_vectordb)
|
||||||
|
# row, col = size(df)
|
||||||
|
# distance = row == 0 ? Inf : df[1, :distance]
|
||||||
|
# if row != 0 && distance < maxdistance
|
||||||
|
# # if there is usable decision, return it.
|
||||||
|
# rowid = df[1, :id]
|
||||||
|
# println("\n--| found similar decision. row id $rowid, distance $distance ", @__FILE__, " ", @__LINE__)
|
||||||
|
# output_b64 = df[1, :function_output_base64] # pick the closest match
|
||||||
|
# _output_str = String(base64decode(output_b64))
|
||||||
|
# output = copy(JSON.read(_output_str))
|
||||||
|
# return output
|
||||||
|
# else
|
||||||
|
# println("\n--| similar decision not found, max distance $maxdistance ", @__FILE__, " ", @__LINE__)
|
||||||
|
# return nothing
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
# """ search similar text from vectordb
|
||||||
|
# """
|
||||||
|
# function find_similar_text_from_vectordb(text::T1, tablename::T2, embeddingColumnName::T3,
|
||||||
|
# vectorDB::Function; limit::Integer=1
|
||||||
|
# )::DataFrame where {T1<:AbstractString, T2<:AbstractString, T3<:AbstractString}
|
||||||
|
# # get embedding from LLM service
|
||||||
|
# _embedding = get_embedding([text])
|
||||||
|
# _embedding = _embedding["data"][1]["embedding"]
|
||||||
|
# _embedding = "$_embedding"
|
||||||
|
|
||||||
|
# embedding = _embedding[4:end] # remove 'Any' from Any[...]
|
||||||
|
|
||||||
|
# # check whether there is close enough vector already store in vectorDB. if no, add, else skip
|
||||||
|
# sql = """
|
||||||
|
# SELECT *, $embeddingColumnName <-> '$embedding' as distance
|
||||||
|
# FROM $tablename
|
||||||
|
# ORDER BY distance LIMIT $limit;
|
||||||
|
# """
|
||||||
|
# response = vectorDB(sql)
|
||||||
|
# df = DataFrame(response)
|
||||||
|
|
||||||
|
# return df
|
||||||
|
# end
|
||||||
|
|
||||||
|
# """ insert decision llm made to vectordb
|
||||||
|
# """
|
||||||
|
# function insert_sommelier_decision(recentevents::T1, decision::T2; maxdistance::Integer=5
|
||||||
|
# ) where {T1<:AbstractString, T2<:AbstractDict}
|
||||||
|
# tablename = "sommelier_decision_repository"
|
||||||
|
# # find similar
|
||||||
|
# df = find_similar_text_from_vectordb(recentevents, tablename,
|
||||||
|
# "function_input_embedding", execute_sql_vectordb)
|
||||||
|
# row, col = size(df)
|
||||||
|
# distance = row == 0 ? Inf : df[1, :distance]
|
||||||
|
# if row == 0 || distance > maxdistance # no close enough SQL stored in the database
|
||||||
|
# _embedding = get_embedding([recentevents])[1]
|
||||||
|
# recentevents_embedding = _embedding["data"][1]["embedding"]
|
||||||
|
# recentevents = replace(recentevents, "'" => "")
|
||||||
|
# decision_json = JSON.json(decision)
|
||||||
|
# decision_base64 = base64encode(decision_json)
|
||||||
|
# decision = replace(decision_json, "'" => "")
|
||||||
|
|
||||||
LibPQ.close(db_connection)
|
# sql =
|
||||||
return result
|
# """
|
||||||
end
|
# INSERT INTO $tablename (function_input, function_output, function_output_base64, function_input_embedding) VALUES ('$recentevents', '$decision', '$decision_base64', '$recentevents_embedding');
|
||||||
|
# """
|
||||||
|
# println("\n--| added new decision to vectorDB ", @__FILE__, " ", @__LINE__)
|
||||||
|
# println(sql)
|
||||||
|
# _ = execute_sql_vectordb(sql)
|
||||||
|
# else
|
||||||
|
# println("--| similar decision previously cached, distance $distance ", @__FILE__, " ", @__LINE__)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
""" find similar sql from vector database
|
# function find_related_tables_for_user_question(question::String; top_row_num::Integer=20)
|
||||||
sql = "SELECT * FROM wine;"
|
|
||||||
result, distance = similar_sql_vectordb(sql)
|
|
||||||
"""
|
|
||||||
function similar_sql_vectordb(sql::T; maxdistance::Number=0.2) where {T<:AbstractString}
|
|
||||||
tablename = "sqlllm_decision_repository"
|
|
||||||
# get embedding of the query
|
|
||||||
df = find_similar_text_from_vectordb(sql, tablename,
|
|
||||||
"function_input_embedding", execute_sql_vectordb)
|
|
||||||
# println(df[1, [:id, :function_output]])
|
|
||||||
row, col = size(df)
|
|
||||||
distance = row == 0 ? Inf : df[1, :distance]
|
|
||||||
if row != 0 && distance < maxdistance
|
|
||||||
# if there is usable SQL, return it.
|
|
||||||
output_b64 = df[1, :function_output_base64] # pick the closest match
|
|
||||||
output_str = String(base64decode(output_b64))
|
|
||||||
rowid = df[1, :id]
|
|
||||||
println("\n--| similar sql found. row id $rowid, distance $distance ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
|
||||||
pprintln(output_str)
|
|
||||||
return (result=output_str, distance=distance)
|
|
||||||
else
|
|
||||||
println("\n--| similar sql not found, max distance $maxdistance ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
|
||||||
return (result=nothing, distance=nothing)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
""" insert query and sql into vector database
|
# metadata_df = GeneralUtils.extract_column_metadata(pg_conn_str)
|
||||||
query = "get all wines from wine table"
|
# embedding_ready = GeneralUtils.generate_embedding_payloads(metadata_df)
|
||||||
sql = "SELECT * FROM wine;"
|
|
||||||
insert_sql_vectordb(query, sql)
|
|
||||||
"""
|
|
||||||
function insert_sql_vectordb(query::T1, SQL::T2; maxdistance::Number=3
|
|
||||||
) where {T1<:AbstractString, T2<:AbstractString}
|
|
||||||
|
|
||||||
tablename = "sqlllm_decision_repository"
|
|
||||||
# get embedding of the query
|
|
||||||
# query = state[:thoughtHistory][:question]
|
|
||||||
df = find_similar_text_from_vectordb(query, tablename,
|
|
||||||
"function_input_embedding", execute_sql_vectordb)
|
|
||||||
row, col = size(df)
|
|
||||||
distance = row == 0 ? Inf : df[1, :distance]
|
|
||||||
if row == 0 || distance > maxdistance # no close enough SQL stored in the database
|
|
||||||
_query_embedding = get_embedding([query])
|
|
||||||
_query_embedding = GeneralUtils.dictify(_query_embedding)
|
|
||||||
# println("\n--- _query_embedding() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
|
|
||||||
# println(_query_embedding)
|
|
||||||
# println("---\n")
|
|
||||||
query_embedding = _query_embedding["data"][1]["embedding"]
|
|
||||||
query = replace(query, "'" => "")
|
|
||||||
sql_base64 = base64encode(SQL)
|
|
||||||
sql_ = replace(SQL, "'" => "")
|
|
||||||
|
|
||||||
sql =
|
# # use only text content
|
||||||
"""
|
# embedding_ready_2 = [i["text_content"] for i in embedding_ready]
|
||||||
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__, " $(Dates.now())")
|
|
||||||
# println(sql)
|
|
||||||
_ = execute_sql_vectordb(sql)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
""" execute sql against vectordb
|
# table_embedding = get_embedding(embedding_ready_2)
|
||||||
sql = "SELECT * FROM wine;"
|
|
||||||
result = execute_sql_vectordb(sql)
|
|
||||||
"""
|
|
||||||
function execute_sql_vectordb(sql::T) where {T<:AbstractString}
|
|
||||||
host_url, _port = split(config["externalservice"]["sommpanion_vectordb"]["url"], ':')
|
|
||||||
port = parse(Int, _port)
|
|
||||||
dbname = config["externalservice"]["sommpanion_vectordb"]["dbname"]
|
|
||||||
user = config["externalservice"]["sommpanion_vectordb"]["user"]
|
|
||||||
password = config["externalservice"]["sommpanion_vectordb"]["password"]
|
|
||||||
DBconnection = LibPQ.Connection("host=$host_url port=$port dbname=$dbname user=$user password=$password")
|
|
||||||
result = LibPQ.execute(DBconnection, sql)
|
|
||||||
close(DBconnection)
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
""" search similar decision llm made from vectordb
|
# _user_question_embedding = get_embedding([question])
|
||||||
"""
|
# user_question_embedding = Float64.(_user_question_embedding["data"][1]["embedding"])
|
||||||
function similar_sommelier_decision(recentevents::T1; maxdistance::Integer=3
|
# user_question_similarity = []
|
||||||
)::Union{AbstractDict, Nothing} where {T1<:AbstractString}
|
|
||||||
|
|
||||||
tablename = "sommelier_decision_repository"
|
# for i in table_embedding["data"]
|
||||||
# find similar
|
# i_data = i["embedding"]
|
||||||
df = find_similar_text_from_vectordb(recentevents, tablename,
|
# i_float = Float64.(i_data)
|
||||||
"function_input_embedding", execute_sql_vectordb)
|
# r = 1 - Distances.cosine_dist(i_float, user_question_embedding)
|
||||||
row, col = size(df)
|
# push!(user_question_similarity, r)
|
||||||
distance = row == 0 ? Inf : df[1, :distance]
|
# end
|
||||||
if row != 0 && distance < maxdistance
|
|
||||||
# if there is usable decision, return it.
|
|
||||||
rowid = df[1, :id]
|
|
||||||
println("\n--| found similar decision. row id $rowid, distance $distance ", @__FILE__, " ", @__LINE__)
|
|
||||||
output_b64 = df[1, :function_output_base64] # pick the closest match
|
|
||||||
_output_str = String(base64decode(output_b64))
|
|
||||||
output = copy(JSON.read(_output_str))
|
|
||||||
return output
|
|
||||||
else
|
|
||||||
println("\n--| similar decision not found, max distance $maxdistance ", @__FILE__, " ", @__LINE__)
|
|
||||||
return nothing
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
""" search similar text from vectordb
|
# new_df = hcat(metadata_df, DataFrame(user_question_similarity = user_question_similarity))
|
||||||
"""
|
# sorted_df = sort(new_df, :user_question_similarity, rev=true) # sort max to min
|
||||||
function find_similar_text_from_vectordb(text::T1, tablename::T2, embeddingColumnName::T3,
|
# _top_20_tables = unique(sorted_df[1:top_row_num, :table_name])
|
||||||
vectorDB::Function; limit::Integer=1
|
# top_20_tables = [i for i in _top_20_tables] # convert to Vector{String}
|
||||||
)::DataFrame where {T1<:AbstractString, T2<:AbstractString, T3<:AbstractString}
|
# g, id_to_table, table_to_id = GeneralUtils.harvest_db_undirected_schema_graph(pg_conn_str)
|
||||||
# get embedding from LLM service
|
# table_relationship = GeneralUtils.resolve_semantic_cluster(top_20_tables, g, table_to_id, id_to_table)
|
||||||
_embedding = get_embedding([text])
|
|
||||||
_embedding = _embedding["data"][1]["embedding"]
|
|
||||||
_embedding = "$_embedding"
|
|
||||||
|
|
||||||
embedding = _embedding[4:end]
|
# # tables that I should put schema in LLM context
|
||||||
|
# return table_relationship
|
||||||
|
# end
|
||||||
|
|
||||||
# check whether there is close enough vector already store in vectorDB. if no, add, else skip
|
|
||||||
sql = """
|
|
||||||
SELECT *, $embeddingColumnName <-> '$embedding' as distance
|
|
||||||
FROM $tablename
|
|
||||||
ORDER BY distance LIMIT $limit;
|
|
||||||
"""
|
|
||||||
response = vectorDB(sql)
|
|
||||||
df = DataFrame(response)
|
|
||||||
|
|
||||||
return df
|
# function prepareContext(state::agentState)::agentContext
|
||||||
end
|
|
||||||
|
# #TODO filter tools from state.tools based on user intend in user message and tool description
|
||||||
|
# filteredTools = state.tools
|
||||||
|
|
||||||
|
# #TODO add filtered tools to the current system prompt / modify systemPrompt here
|
||||||
|
# preparedSystemPrompt = state.systemPrompt
|
||||||
|
|
||||||
|
# #TODO add system prompt, adjust/modify and inject additional context into messages
|
||||||
|
# preparedMessages = deepcopy(state.messages) # messages that will be send to LLM
|
||||||
|
|
||||||
|
# agentCtx = agentContext(preparedSystemPrompt, preparedMessages, filteredTools)
|
||||||
|
|
||||||
|
# return agentCtx
|
||||||
|
# end
|
||||||
|
|
||||||
|
# function formatMsgForLLM(ctx::agentContext)::Dict{String, Any}
|
||||||
|
|
||||||
|
# end
|
||||||
|
|
||||||
""" insert decision llm made to vectordb
|
|
||||||
"""
|
|
||||||
function insert_sommelier_decision(recentevents::T1, decision::T2; maxdistance::Integer=5
|
|
||||||
) where {T1<:AbstractString, T2<:AbstractDict}
|
|
||||||
tablename = "sommelier_decision_repository"
|
|
||||||
# find similar
|
|
||||||
df = find_similar_text_from_vectordb(recentevents, tablename,
|
|
||||||
"function_input_embedding", execute_sql_vectordb)
|
|
||||||
row, col = size(df)
|
|
||||||
distance = row == 0 ? Inf : df[1, :distance]
|
|
||||||
if row == 0 || distance > maxdistance # no close enough SQL stored in the database
|
|
||||||
_embedding = get_embedding([recentevents])[1]
|
|
||||||
recentevents_embedding = _embedding["data"][1]["embedding"]
|
|
||||||
recentevents = replace(recentevents, "'" => "")
|
|
||||||
decision_json = JSON.json(decision)
|
|
||||||
decision_base64 = base64encode(decision_json)
|
|
||||||
decision = replace(decision_json, "'" => "")
|
|
||||||
|
|
||||||
sql =
|
|
||||||
"""
|
|
||||||
INSERT INTO $tablename (function_input, function_output, function_output_base64, function_input_embedding) VALUES ('$recentevents', '$decision', '$decision_base64', '$recentevents_embedding');
|
|
||||||
"""
|
|
||||||
println("\n--| added new decision to vectorDB ", @__FILE__, " ", @__LINE__)
|
|
||||||
println(sql)
|
|
||||||
_ = execute_sql_vectordb(sql)
|
|
||||||
else
|
|
||||||
println("--| similar decision previously cached, distance $distance ", @__FILE__, " ", @__LINE__)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
config = JSON.parsefile("./appconfig.json")
|
config = JSON.parsefile("./appconfig.json")
|
||||||
|
host_url, _port = split(config["externalservice"]["sommpanion_db"]["url"], ':')
|
||||||
|
port = parse(Int, _port)
|
||||||
|
dbname = "winedb"
|
||||||
|
user = config["externalservice"]["sommpanion_db"]["user"]
|
||||||
|
password = config["externalservice"]["sommpanion_db"]["password"]
|
||||||
|
pg_conn_str = "host=$host_url port=$port dbname=$dbname user=$user password=$password"
|
||||||
sessionId = "0"
|
sessionId = "0"
|
||||||
backend_session_topic = "sommpanion.testsubject"
|
backend_session_topic = "sommpanion.testsubject"
|
||||||
agent_ch = Channel(8)
|
agent_ch = Channel(8)
|
||||||
@@ -235,159 +296,19 @@ agent_conn = NATS.connect(config["nats_server_info"]["url"])
|
|||||||
sub2 = NATS.subscribe(agent_conn, backend_session_topic) do msg
|
sub2 = NATS.subscribe(agent_conn, backend_session_topic) do msg
|
||||||
put!(agent_ch, msg)
|
put!(agent_ch, msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
agent_context = YiemAgent.agentcontext(
|
|
||||||
text2text_instruct_llm,
|
|
||||||
get_embedding,
|
|
||||||
execute_sql_winedb,
|
|
||||||
similar_sql_vectordb,
|
|
||||||
insert_sql_vectordb,
|
|
||||||
similar_sommelier_decision,
|
|
||||||
insert_sommelier_decision
|
|
||||||
)
|
|
||||||
|
|
||||||
# can't instantiate
|
|
||||||
agent = YiemAgent.sommelier(
|
|
||||||
agent_context;
|
|
||||||
name="Janie",
|
|
||||||
id=sessionId, # agent instance id
|
|
||||||
retailername="Yiem Wine Ltd.",
|
|
||||||
llmFormatName=""
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
image1_path = "test/large_image.png"
|
|
||||||
image1_bytes = read(image1_path)
|
|
||||||
image1_base64_string = base64encode(image1_bytes)
|
|
||||||
mime_type = "image/png"
|
|
||||||
data1_uri = "data:$(mime_type);base64,$(image1_base64_string)"
|
|
||||||
|
|
||||||
# 1. Read local file and encode to base64 string
|
|
||||||
image2_path = "test/small_image.png"
|
|
||||||
image2_bytes = read(image2_path)
|
|
||||||
image2_base64_string = base64encode(image2_bytes)
|
|
||||||
mime_type = "image/png"
|
|
||||||
data2_uri = "data:$(mime_type);base64,$(image2_base64_string)"
|
|
||||||
|
|
||||||
# 3. Construct payload with the Data URI
|
|
||||||
message = Dict(
|
|
||||||
"role" => "user",
|
|
||||||
"content" => [
|
|
||||||
Dict("type" => "text", "text" => "Do you know type of wine in the image?"),
|
|
||||||
Dict(
|
|
||||||
"type" => "image_url",
|
|
||||||
"image_url" => Dict("url" => data1_uri)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
result = YiemAgent.conversation(agent; userinput=message)
|
|
||||||
println("\n$result")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# message = Dict(
|
|
||||||
# "role" => "user",
|
|
||||||
# "content" => [
|
|
||||||
# Dict("type" => "text", "text" =>
|
|
||||||
# "
|
|
||||||
# เป็นงานเลี้ยงทั่วไป
|
|
||||||
# "),
|
|
||||||
# ]
|
|
||||||
# )
|
|
||||||
|
|
||||||
# result = YiemAgent.conversation(agent; userinput=message)
|
|
||||||
# println("\n$result")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# message = Dict(
|
|
||||||
# "role" => "user",
|
|
||||||
# "content" => [
|
|
||||||
# Dict("type" => "text", "text" => "no thanks. that's all"),
|
|
||||||
# ]
|
|
||||||
# )
|
|
||||||
|
|
||||||
# result = YiemAgent.conversation(agent; userinput=message)
|
|
||||||
# println("\n$result")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# message = Dict(
|
|
||||||
# "role" => "user",
|
|
||||||
# "content" => [
|
|
||||||
# Dict("type" => "text", "text" => "What about this wine?"),
|
|
||||||
# Dict(
|
|
||||||
# "type" => "image_url",
|
|
||||||
# "image_url" => Dict("url" => data2_uri)
|
|
||||||
# )
|
|
||||||
# ]
|
|
||||||
# )
|
|
||||||
|
|
||||||
# result = YiemAgent.conversation(agent; userinput=message)
|
|
||||||
# println("\n$result")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#WORKING load tools
|
||||||
|
text2text_llm = text2textInstructLLM(agent_conn,
|
||||||
|
config["externalservice"]["servicesloadbalancer"]["nats"],
|
||||||
|
"sender",
|
||||||
|
config["externalservice"]["fileserver"]["url"])
|
||||||
|
|
||||||
|
agent = YiemAgent.yiemAgent(
|
||||||
|
"/home/ton/docker-apps/sommpanion/agent-backend/tools",
|
||||||
|
text2text_llm
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user