Merge pull request 'v0.1.4' (#3) from v0.1.4 into main

Reviewed-on: #3
This commit is contained in:
ton
2025-04-04 08:14:57 +00:00
8 changed files with 1012 additions and 499 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.3" version = "0.1.4"
[deps] [deps]
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"

File diff suppressed because it is too large Load Diff

View File

@@ -291,20 +291,20 @@ julia> result = checkinventory(agent, input)
function checkinventory(a::T1, input::T2 function checkinventory(a::T1, input::T2
) where {T1<:agent, T2<:AbstractString} ) where {T1<:agent, T2<:AbstractString}
println("\n~~~ checkinventory order: $input ", Dates.now(), " ", @__FILE__, " ", @__LINE__) println("\ncheckinventory order: $input ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
wineattributes_1 = extractWineAttributes_1(a, input) wineattributes_1 = extractWineAttributes_1(a, input)
wineattributes_2 = extractWineAttributes_2(a, input) wineattributes_2 = extractWineAttributes_2(a, input)
_inventoryquery = "retailer name: $(a.retailername), $wineattributes_1, $wineattributes_2" _inventoryquery = "retailer name: $(a.retailername), $wineattributes_1, $wineattributes_2"
inventoryquery = "Retrieves winery, wine_name, vintage, region, country, wine_type, grape, serving_temperature, sweetness, intensity, tannin, acidity, tasting_notes, price and currency of wines that match the following criteria - {$_inventoryquery}" inventoryquery = "Retrieves winery, wine_name, vintage, region, country, wine_type, grape, serving_temperature, sweetness, intensity, tannin, acidity, tasting_notes, price and currency of wines that match the following criteria - {$_inventoryquery}"
println("~~~ checkinventory input: $inventoryquery ", Dates.now(), " ", @__FILE__, " ", @__LINE__) println("\ncheckinventory input: $inventoryquery ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# add suppport for similarSQLVectorDB # add suppport for similarSQLVectorDB
textresult, rawresponse = SQLLLM.query(inventoryquery, a.func[:executeSQL], textresult, rawresponse = SQLLLM.query(inventoryquery, a.func[:executeSQL],
a.func[:text2textInstructLLM], a.func[:text2textInstructLLM],
insertSQLVectorDB=a.func[:insertSQLVectorDB], insertSQLVectorDB=a.func[:insertSQLVectorDB],
similarSQLVectorDB=a.func[:similarSQLVectorDB]) similarSQLVectorDB=a.func[:similarSQLVectorDB])
println("\n~~~ checkinventory result ", Dates.now(), " ", @__FILE__, " ", @__LINE__) println("\ncheckinventory result ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
println(textresult) println(textresult)
return (result=textresult, rawresponse=rawresponse, success=true, errormsg=nothing) return (result=textresult, rawresponse=rawresponse, success=true, errormsg=nothing)
@@ -345,7 +345,7 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
- Do not generate other comments. - Do not generate other comments.
You should then respond to the user with: You should then respond to the user with:
Comprehension: state your understanding of the current situation Thought: state your understanding of the current situation
Wine_name: name of the wine Wine_name: name of the wine
Winery: name of the winery Winery: name of the winery
Vintage: the year of the wine Vintage: the year of the wine
@@ -359,7 +359,7 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
Food_to_be_paired_with_wine: food that the user will be served with the wine such as poultry, fish, steak, etc Food_to_be_paired_with_wine: food that the user will be served with the wine such as poultry, fish, steak, etc
You should only respond in format as described below: You should only respond in format as described below:
Comprehension: ... Thought: ...
Wine_name: ... Wine_name: ...
Winery: ... Winery: ...
Vintage: ... Vintage: ...
@@ -376,17 +376,23 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
User's query: red, Chenin Blanc, Riesling, 20 USD User's query: red, Chenin Blanc, Riesling, 20 USD
{"reasoning": ..., "winery": "NA", "wine_name": "NA", "vintage": "NA", "region": "NA", "country": "NA", "wine_type": "red, white", "grape_varietal": "Chenin Blanc, Riesling", "tasting_notes": "NA", "wine_price": "0-20", "occasion": "NA", "food_to_be_paired_with_wine": "NA"} {"reasoning": ..., "winery": "NA", "wine_name": "NA", "vintage": "NA", "region": "NA", "country": "NA", "wine_type": "red, white", "grape_varietal": "Chenin Blanc, Riesling", "tasting_notes": "NA", "wine_price": "0-20", "occasion": "NA", "food_to_be_paired_with_wine": "NA"}
User's query: Domaine du Collier Saumur Blanc 2019, France, white, Chenin Blanc User's query: Domaine du Collier Saumur Blanc 2019, France, white, Merlot
{"reasoning": ..., "winery": "Domaine du Collier", "wine_name": "Saumur Blanc", "vintage": "2019", "region": "Saumur", "country": "France", "wine_type": "white", "grape_varietal": "Chenin Blanc", "tasting_notes": "NA", "wine_price": "NA", "occasion": "NA", "food_to_be_paired_with_wine": "NA"} {"reasoning": ..., "winery": "Domaine du Collier", "wine_name": "Saumur Blanc", "vintage": "2019", "region": "Saumur", "country": "France", "wine_type": "white", "grape_varietal": "Merlot", "tasting_notes": "NA", "wine_price": "NA", "occasion": "NA", "food_to_be_paired_with_wine": "NA"}
Let's begin! Let's begin!
""" """
header = ["Comprehension:", "Wine_name:", "Winery:", "Vintage:", "Region:", "Country:", "Wine_type:", "Grape_varietal:", "Tasting_notes:", "Wine_price:", "Occasion:", "Food_to_be_paired_with_wine:"] header = ["Thought:", "Wine_name:", "Winery:", "Vintage:", "Region:", "Country:", "Wine_type:", "Grape_varietal:", "Tasting_notes:", "Wine_price:", "Occasion:", "Food_to_be_paired_with_wine:"]
dictkey = ["comprehension", "wine_name", "winery", "vintage", "region", "country", "wine_type", "grape_varietal", "tasting_notes", "wine_price", "occasion", "food_to_be_paired_with_wine"] dictkey = ["thought", "wine_name", "winery", "vintage", "region", "country", "wine_type", "grape_varietal", "tasting_notes", "wine_price", "occasion", "food_to_be_paired_with_wine"]
errornote = "" errornote = ""
for attempt in 1:5 for attempt in 1:10
#[WORKING] I should add generatequestion()
if attempt > 1
println("\nYiemAgent extractWineAttributes_1() attempt $attempt/10 ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
end
usermsg = usermsg =
""" """
User's query: $input User's query: $input
@@ -409,7 +415,7 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
for word in header for word in header
if !occursin(word, response) if !occursin(word, response)
errornote = "$word attribute is missing in previous attempts" errornote = "$word attribute is missing in previous attempts"
println("Attempt $attempt $errornote ", Dates.now(), " ", @__FILE__, " ", @__LINE__) println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
checkFlag = true checkFlag = true
break break
end end
@@ -418,7 +424,7 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
# check whether response has all header # check whether response has all header
detected_kw = GeneralUtils.detect_keyword(header, response) detected_kw = GeneralUtils.detect_keyword(header, response)
if sum(values(detected_kw)) < length(header) if 0 values(detected_kw)
errornote = "\nYiemAgent extractWineAttributes_1() response does not have all header" errornote = "\nYiemAgent extractWineAttributes_1() response does not have all header"
continue continue
elseif sum(values(detected_kw)) > length(header) elseif sum(values(detected_kw)) > length(header)
@@ -428,7 +434,7 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
responsedict = GeneralUtils.textToDict(response, header; responsedict = GeneralUtils.textToDict(response, header;
dictKey=dictkey, symbolkey=true) dictKey=dictkey, symbolkey=true)
delete!(responsedict, :comprehension) delete!(responsedict, :thought)
delete!(responsedict, :tasting_notes) delete!(responsedict, :tasting_notes)
delete!(responsedict, :occasion) delete!(responsedict, :occasion)
delete!(responsedict, :food_to_be_paired_with_wine) delete!(responsedict, :food_to_be_paired_with_wine)
@@ -440,14 +446,14 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
checkFlag = false checkFlag = false
for i in dictkey for i in dictkey
j = Symbol(i) j = Symbol(i)
if j [:comprehension, :tasting_notes, :occasion, :food_to_be_paired_with_wine] if j [:thought, :tasting_notes, :occasion, :food_to_be_paired_with_wine]
# in case j is wine_price it needs to be checked differently because its value is ranged # in case j is wine_price it needs to be checked differently because its value is ranged
if j == :wine_price if j == :wine_price
if responsedict[:wine_price] != "NA" if responsedict[:wine_price] != "NA"
# check whether wine_price is in ranged number # check whether wine_price is in ranged number
if !occursin('-', responsedict[:wine_price]) if !occursin('-', responsedict[:wine_price])
errornote = "wine_price must be a range number" errornote = "wine_price must be a range number"
println("Attempt $attempt $errornote ", Dates.now(), " ", @__FILE__, " ", @__LINE__) println("ERROR YiemAgent extractWineAttributes_1() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
checkFlag = true checkFlag = true
break break
end end
@@ -462,7 +468,7 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
# price range like 100-100 is not good # price range like 100-100 is not good
if minprice == maxprice if minprice == maxprice
errornote = "wine_price with minimum equals to maximum is not valid" errornote = "wine_price with minimum equals to maximum is not valid"
println("Attempt $attempt $errornote ", Dates.now(), " ", @__FILE__, " ", @__LINE__) println("ERROR YiemAgent extractWineAttributes_1() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
checkFlag = true checkFlag = true
break break
end end
@@ -478,14 +484,14 @@ function extractWineAttributes_1(a::T1, input::T2)::String where {T1<:agent, T2<
content = [content] content = [content]
end end
for x in content #check whether price are mentioned in the input # for x in content #check whether price are mentioned in the input
if !occursin("NA", responsedict[j]) && !occursin(x, input) # if !occursin("NA", responsedict[j]) && !occursin(x, input)
errornote = "$x is not mentioned in the user query, you must only use the info from the query." # errornote = "$x is not mentioned in the user query, you must only use the info from the query."
println("Attempt $attempt $errornote ", Dates.now(), " ", @__FILE__, " ", @__LINE__) # println("ERROR YiemAgent extractWineAttributes_1() $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
checkFlag == true # checkFlag == true
break # break
end # end
end # end
end end
end end
end end
@@ -640,7 +646,7 @@ function extractWineAttributes_2(a::T1, input::T2)::String where {T1<:agent, T2<
# check whether response has all header # check whether response has all header
detected_kw = GeneralUtils.detect_keyword(header, response) detected_kw = GeneralUtils.detect_keyword(header, response)
if sum(values(detected_kw)) < length(header) if 0 values(detected_kw)
errornote = "\nYiemAgent extractWineAttributes_2() response does not have all header" errornote = "\nYiemAgent extractWineAttributes_2() response does not have all header"
continue continue
elseif sum(values(detected_kw)) > length(header) elseif sum(values(detected_kw)) > length(header)
@@ -657,7 +663,7 @@ function extractWineAttributes_2(a::T1, input::T2)::String where {T1<:agent, T2<
value = responsedict[keyword] value = responsedict[keyword]
if value != "NA" && !occursin(value, input) if value != "NA" && !occursin(value, input)
errornote = "WARNING. Keyword $keyword: $value does not appear in the input. You must use information from the input only" errornote = "WARNING. Keyword $keyword: $value does not appear in the input. You must use information from the input only"
println("Attempt $attempt $errornote ", Dates.now(), " ", @__FILE__, " ", @__LINE__) println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
@@ -673,7 +679,7 @@ function extractWineAttributes_2(a::T1, input::T2)::String where {T1<:agent, T2<
if !occursin("keyword", string(k)) if !occursin("keyword", string(k))
if v !== "NA" && (!occursin('-', v) || length(v) > 5) if v !== "NA" && (!occursin('-', v) || length(v) > 5)
errornote = "WARNING: The non-range value {$k: $v} is not allowed. It should be specified in a range format, i.e. min-max." errornote = "WARNING: The non-range value {$k: $v} is not allowed. It should be specified in a range format, i.e. min-max."
println("Attempt $attempt $errornote ", Dates.now(), " ", @__FILE__, " ", @__LINE__) println("Attempt $attempt $errornote ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
continue continue
end end
end end
@@ -731,7 +737,7 @@ function paraphrase(text2textInstructLLM::Function, text::String)
- N/A - N/A
You should then respond to the user with: You should then respond to the user with:
1) Paraphrase: Paraphrased text Paraphrase: Paraphrased text
You should only respond in format as described below: You should only respond in format as described below:
Paraphrase: ... Paraphrase: ...
@@ -739,6 +745,9 @@ function paraphrase(text2textInstructLLM::Function, text::String)
Let's begin! Let's begin!
""" """
header = ["Paraphrase:"]
dictkey = ["paraphrase"]
errornote = "" errornote = ""
response = nothing # placeholder for show when error msg show up response = nothing # placeholder for show when error msg show up
@@ -756,17 +765,14 @@ function paraphrase(text2textInstructLLM::Function, text::String)
] ]
# put in model format # put in model format
prompt = GeneralUtils.formatLLMtext(_prompt; formatname="llama3instruct") prompt = GeneralUtils.formatLLMtext(_prompt; formatname="qwen")
prompt *= """
<|start_header_id|>assistant<|end_header_id|>
"""
try try
response = text2textInstructLLM(prompt) response = text2textInstructLLM(prompt)
# sometime the model response like this "here's how I would respond: ..." # sometime the model response like this "here's how I would respond: ..."
if occursin("respond:", response) if occursin("respond:", response)
errornote = "You don't need to intro your response" errornote = "You don't need to intro your response"
error("\n~~~ paraphrase() response contain : ", Dates.now(), " ", @__FILE__, " ", @__LINE__) error("\nparaphrase() response contain : ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
end end
response = GeneralUtils.remove_french_accents(response) response = GeneralUtils.remove_french_accents(response)
response = replace(response, '*'=>"") response = replace(response, '*'=>"")
@@ -774,14 +780,22 @@ function paraphrase(text2textInstructLLM::Function, text::String)
response = replace(response, '`' => "") response = replace(response, '`' => "")
response = GeneralUtils.remove_french_accents(response) response = GeneralUtils.remove_french_accents(response)
header = ["Paraphrase:"] # check whether response has all header
dictkey = ["paraphrase"] detected_kw = GeneralUtils.detect_keyword(header, response)
if 0 values(detected_kw)
errornote = "\nYiemAgent paraphrase() response does not have all header"
continue
elseif sum(values(detected_kw)) > length(header)
errornote = "\nnYiemAgent paraphrase() response has duplicated header"
continue
end
responsedict = GeneralUtils.textToDict(response, header; responsedict = GeneralUtils.textToDict(response, header;
dictKey=dictkey, symbolkey=true) dictKey=dictkey, symbolkey=true)
for i [:paraphrase] for i [:paraphrase]
if length(JSON3.write(responsedict[i])) == 0 if length(JSON3.write(responsedict[i])) == 0
error("$i is empty ", Dates.now(), " ", @__FILE__, " ", @__LINE__) error("$i is empty ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
end end
end end
@@ -793,7 +807,7 @@ function paraphrase(text2textInstructLLM::Function, text::String)
end end
end end
println("\n~~~ paraphrase() ", Dates.now(), " ", @__FILE__, " ", @__LINE__) println("\nparaphrase() ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
pprintln(Dict(responsedict)) pprintln(Dict(responsedict))
result = responsedict[:paraphrase] result = responsedict[:paraphrase]
@@ -804,10 +818,10 @@ function paraphrase(text2textInstructLLM::Function, text::String)
showerror(io, e) showerror(io, e)
errorMsg = String(take!(io)) errorMsg = String(take!(io))
st = sprint((io, v) -> show(io, "text/plain", v), stacktrace(catch_backtrace())) st = sprint((io, v) -> show(io, "text/plain", v), stacktrace(catch_backtrace()))
println("\nAttempt $attempt. Error occurred: $errorMsg\n$st ", Dates.now(), " ", @__FILE__, " ", @__LINE__) println("\nAttempt $attempt. Error occurred: $errorMsg\n$st ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
end end
end end
error("generatechat failed to generate a response") error("paraphrase() failed to generate a response")
end end
@@ -970,7 +984,7 @@ end
# ] # ]
# # put in model format # # put in model format
# prompt = GeneralUtils.formatLLMtext(_prompt; formatname="llama3instruct") # prompt = GeneralUtils.formatLLMtext(_prompt; formatname="qwen")
# prompt *= # prompt *=
# """ # """
# <|start_header_id|>assistant<|end_header_id|> # <|start_header_id|>assistant<|end_header_id|>
@@ -1002,7 +1016,7 @@ end
# state[:isterminal] = true # state[:isterminal] = true
# state[:reward] = 1 # state[:reward] = 1
# end # end
# println("--> 5 Evaluator ", Dates.now(), " ", @__FILE__, " ", @__LINE__) # println("--> 5 Evaluator ", @__FILE__, ":", @__LINE__, " $(Dates.now())")
# pprintln(Dict(responsedict)) # pprintln(Dict(responsedict))
# return responsedict[:score] # return responsedict[:score]
# catch e # catch e

View File

@@ -122,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 chatHistoryToText(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
@@ -191,6 +197,35 @@ 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,
@@ -220,24 +255,62 @@ function eventdict(;
end end
function createTimeline(memory::T1; skiprecent::Integer=0) where {T1<:AbstractVector} """ Create a formatted timeline string from a sequence of events.
events = memory[1:end-skiprecent]
# 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 = "" timeline = ""
for (i, event) in enumerate(events)
if event[:outcome] === nothing # Determine which indices to use - either provided range or full length
timeline *= "$i) $(event[:subject])> $(event[:actioninput])\n" ind =
if eventindex !== nothing
[eventindex...]
else else
timeline *= "$i) $(event[:subject])> $(event[:actioninput]) $(event[:outcome])\n" 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
end end
# Return formatted timeline string
return timeline return timeline
end end
# """ Convert a single chat dictionary into LLM model instruct format. # """ Convert a single chat dictionary into LLM model instruct format.
# # Llama 3 instruct format example # # Llama 3 instruct format example

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,7 +27,7 @@
"description": "agent role" "description": "agent role"
}, },
"organization": { "organization": {
"value": "yiem_hq", "value": "yiem_branch_1",
"description": "organization name" "description": "organization name"
}, },
"externalservice": { "externalservice": {

View File

@@ -36,13 +36,13 @@ function executeSQLVectorDB(sql)
return result return result
end end
function text2textInstructLLM(prompt::String; maxattempt=3) function text2textInstructLLM(prompt::String; maxattempt::Integer=2, modelsize::String="medium")
msgMeta = GeneralUtils.generate_msgMeta( msgMeta = GeneralUtils.generate_msgMeta(
config[:externalservice][:loadbalancer][:mqtttopic]; config[:externalservice][:loadbalancer][:mqtttopic];
msgPurpose="inference", msgPurpose="inference",
senderName="yiemagent", senderName="yiemagent",
senderId=sessionId, senderId=sessionId,
receiverName="text2textinstruct_small", receiverName="text2textinstruct_$modelsize",
mqttBrokerAddress=config[:mqttServerInfo][:broker], mqttBrokerAddress=config[:mqttServerInfo][:broker],
mqttBrokerPort=config[:mqttServerInfo][:port], mqttBrokerPort=config[:mqttServerInfo][:port],
) )
@@ -60,7 +60,7 @@ function text2textInstructLLM(prompt::String; maxattempt=3)
response = nothing response = nothing
for attempts in 1:maxattempt for attempts in 1:maxattempt
_response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=300, maxattempt=maxattempt) _response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=180, maxattempt=maxattempt)
payload = _response[:response] payload = _response[:response]
if _response[:success] && payload[:text] !== nothing if _response[:success] && payload[:text] !== nothing
response = _response[:response][:text] response = _response[:response][:text]
@@ -83,7 +83,7 @@ function getEmbedding(text::T) where {T<:AbstractString}
msgPurpose="embedding", msgPurpose="embedding",
senderName="yiemagent", senderName="yiemagent",
senderId=sessionId, senderId=sessionId,
receiverName="text2textinstruct_small", receiverName="textembedding",
mqttBrokerAddress=config[:mqttServerInfo][:broker], mqttBrokerAddress=config[:mqttServerInfo][:broker],
mqttBrokerPort=config[:mqttServerInfo][:port], mqttBrokerPort=config[:mqttServerInfo][:port],
) )
@@ -94,7 +94,8 @@ 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=120)
response = GeneralUtils.sendReceiveMqttMsg(outgoingMsg; timeout=120, maxattempt=3)
embedding = response[:response][:embeddings] embedding = response[:response][:embeddings]
return embedding return embedding
end end
@@ -161,7 +162,7 @@ function insertSQLVectorDB(query::T1, SQL::T2; maxdistance::Integer=3) where {T1
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
@@ -234,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")
@@ -244,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.
"""