update
This commit is contained in:
@@ -10,6 +10,9 @@ module ChatAgent
|
||||
include("type.jl")
|
||||
using .type
|
||||
|
||||
include("utils.jl")
|
||||
using .utils
|
||||
|
||||
include("llmfunction.jl")
|
||||
using .llmfunction
|
||||
|
||||
|
||||
651
src/interface.jl
651
src/interface.jl
@@ -2,13 +2,11 @@ module interface
|
||||
|
||||
|
||||
export agentReact, agentReflex,
|
||||
addNewMessage, clearMessage, removeLatestMsg, generatePrompt_tokenPrefix,
|
||||
generatePrompt_tokenSuffix, conversation, work, detectCharacters, chunktext,
|
||||
findDetectedCharacter, wikisearch, sendReceivePrompt, extractStepFromPlan
|
||||
addNewMessage, clearMessage, removeLatestMsg, conversation
|
||||
|
||||
using JSON3, DataStructures, Dates, UUIDs, HTTP
|
||||
using CommUtils, GeneralUtils
|
||||
using ..type
|
||||
using ..type, ..utils
|
||||
|
||||
# ---------------------------------------------------------------------------- #
|
||||
# pythoncall setting #
|
||||
@@ -575,8 +573,8 @@ function work(a::T, prompt::String, maxround::Int=3) where {T<:agent}
|
||||
return respond
|
||||
end
|
||||
|
||||
function conversation(a::agentReflex, usermsg::String; thinkingroundlimit::Int=3)
|
||||
a.thinkingroundlimit = thinkingroundlimit
|
||||
function conversation(a::agentReflex, usermsg::String; attemptlimit::Int=3)
|
||||
a.attemptlimit = attemptlimit
|
||||
respond = nothing
|
||||
|
||||
# determine thinking mode
|
||||
@@ -600,7 +598,7 @@ function conversation(a::agentReflex, usermsg::String; thinkingroundlimit::Int=3
|
||||
return respond
|
||||
end
|
||||
|
||||
#WORKING
|
||||
|
||||
function work(a::agentReflex, usermsg::String)
|
||||
if a.thinkingmode == :new_thinking
|
||||
a.earlierConversation = conversationSummary(a)
|
||||
@@ -613,9 +611,14 @@ function work(a::agentReflex, usermsg::String)
|
||||
error("undefined condition thinkingmode = $thinkingmode")
|
||||
end
|
||||
|
||||
while true
|
||||
while true # Work loop
|
||||
# plan
|
||||
a.attempt += 1
|
||||
if a.attempt <= a.attemptlimit
|
||||
|
||||
else # attempt limit reached
|
||||
|
||||
end
|
||||
@show a.attempt
|
||||
@show usermsg
|
||||
logmsg = "<|im_start|>user:\nStimulus: $usermsg\n<|im_end|>\n"
|
||||
@@ -628,47 +631,12 @@ function work(a::agentReflex, usermsg::String)
|
||||
respond = sendReceivePrompt(a, prompt)
|
||||
plan = split(respond, "<|im_end|>")[1]
|
||||
plan = split(plan, "Response:")[1]
|
||||
|
||||
@show plan
|
||||
totalsteps = checkTotalStepInPlan(a, plan)
|
||||
logmsg = "<|im_start|>assistant:\n$plan\n"
|
||||
_plan = replace(plan, "Plan:"=>"Plan $(a.attempt):")
|
||||
logmsg = "<|im_start|>assistant:\n$_plan\n"
|
||||
a.memory[:shortterm] *= logmsg
|
||||
result = actor(a, plan)
|
||||
#WORKING
|
||||
|
||||
a.step = 0
|
||||
while true # execute step by step
|
||||
a.step += 1
|
||||
if a.step <= totalsteps
|
||||
stepdetail = extractStepFromPlan(a, plan, a.step)
|
||||
@show stepdetail
|
||||
prompt = actor_mistral_openorca(a, stepdetail)
|
||||
respond = sendReceivePrompt(a, prompt)
|
||||
respond = split(respond, "<|im_end|>")[1]
|
||||
@show respond
|
||||
#WORKING extract Act/ActInput
|
||||
headerToDetect = ["Question:", "Plan:", "Thought:", "Act:", "ActInput:", "Obs:", "...", "Answer:",
|
||||
"Conclusion:", "Summary:"]
|
||||
headers = detectCharacters(respond, headerToDetect)
|
||||
@show headers
|
||||
chunkedtext = chunktext(respond, headers)
|
||||
@show chunkedtext
|
||||
toolname = toolNameBeingCalled(chunkedtext["Act:"], a.tools)
|
||||
@show toolname
|
||||
toolinput = chunkedtext["ActInput:"]
|
||||
|
||||
@show toolinput
|
||||
error("work done 1")
|
||||
else #TODO finish all steps
|
||||
|
||||
|
||||
|
||||
|
||||
error("work done 2")
|
||||
break
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
# evaluate
|
||||
|
||||
|
||||
@@ -677,552 +645,69 @@ function work(a::agentReflex, usermsg::String)
|
||||
end
|
||||
|
||||
|
||||
function actor(a::agentReflex, usermsg::String)
|
||||
function actor(a::agentReflex, plan::T) where {T<:AbstractString}
|
||||
actorState = nothing
|
||||
@show plan
|
||||
totalsteps = checkTotalStepInPlan(a, plan)
|
||||
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
make a conversation summary.
|
||||
```jldoctest
|
||||
julia> conversation = [
|
||||
Dict(:role=> "user", :content=> "I would like to get a bottle of wine", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "assistant", :content=> "What kind of Thai dishes are you having?", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "user", :content=> "It a pad thai.", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "assistant", :content=> "Is there any special occasion for this event?", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "user", :content=> "We'll hold a wedding party at the beach.", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "assistant", :content=> "What is your preferred type of wine?", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "user", :content=> "I like dry white wine with medium tanins.", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "assistant", :content=> "What is your preferred price range for this bottle of wine?", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "user", :content=> "lower than 50 dollars.", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "assistant", :content=> "Based on your preferences and our stock, I recommend the following two wines for you:
|
||||
1. Pierre Girardin \"Murgers des Dents de Chien\" - Saint-Aubin 1er Cru (17 USD)
|
||||
2. Etienne Sauzet'Les Perrieres' - Puligny Montrachet Premier Cru (22 USD)
|
||||
The first wine, Pierre Girardin \"Murgers des Dents de Chien\" - Saint-Aubin 1er Cru, is a great choice for its affordable price and refreshing taste.
|
||||
It pairs well with Thai dishes and will be perfect for your beach wedding party.
|
||||
The second wine, Etienne Sauzet'Les Perrieres' - Puligny Montrachet Premier Cru, offers a more complex flavor profile and slightly higher price point, but still remains within your budget.
|
||||
Both wines are suitable for serving at 22 C temperature.", :timestamp=> Dates.now()),
|
||||
]
|
||||
|
||||
julia> summary = conversationSummary(conversation)
|
||||
```
|
||||
"""
|
||||
function conversationSummary(a::T) where {T<:agent}
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
You talked with a user earlier.
|
||||
Now you make a detailed bullet summary of the conversation from your perspective.
|
||||
Use "I" when refers to yourself in the summary.
|
||||
|
||||
Here are the conversation:
|
||||
{conversation}
|
||||
<|im_end|>
|
||||
|
||||
"""
|
||||
conversation = ""
|
||||
summary = "nothing"
|
||||
if length(a.messages)!= 0
|
||||
for msg in a.messages
|
||||
role = msg[:role]
|
||||
content = msg[:content]
|
||||
|
||||
if role == "user"
|
||||
conversation *= "$role: $content\n"
|
||||
elseif role == "assistant"
|
||||
conversation *= "I: $content\n"
|
||||
else
|
||||
error("undefied condition role = $role")
|
||||
end
|
||||
end
|
||||
prompt = replace(prompt, "{conversation}" => conversation)
|
||||
result = sendReceivePrompt(a, prompt)
|
||||
summary = result === nothing ? "nothing" : result
|
||||
summary = replace(summary, "<|im_end|>" => "")
|
||||
if summary[1:1] == "\n"
|
||||
summary = summary[2:end]
|
||||
end
|
||||
end
|
||||
println("conversation summary: $summary")
|
||||
return summary
|
||||
end
|
||||
|
||||
function makeSummary(a::T1, input::T2) where {T1<:agent, T2<:AbstractString}
|
||||
summary = "Nothing."
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
Your need to determine now whether you can make a summary of user's text.
|
||||
|
||||
You have the following choices:
|
||||
If you cannot make a summary say, "{no}".
|
||||
If you can make a summary say, "{yes}".
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
{input}
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
prompt = replace(prompt, "{input}" => usermsg)
|
||||
result = sendReceivePrompt(a, prompt)
|
||||
result = GeneralUtils.getStringBetweenCharacters(result, "{", "}")
|
||||
if result == "yes"
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
You are a helpful assistant.
|
||||
Your job is to make a concise summary of user's text.
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
{input}
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
prompt = replace(prompt, "{input}" => input)
|
||||
result = sendReceivePrompt(a, prompt)
|
||||
summary = replace(result, "<|im_end|>" => "")
|
||||
if summary[1:1] == "\n"
|
||||
summary = summary[2:end]
|
||||
end
|
||||
end
|
||||
|
||||
return summary
|
||||
end
|
||||
|
||||
function chooseThinkingMode(a::T, usermsg::String) where {T<:agent}
|
||||
thinkingMode = nothing
|
||||
if a.thought != "nothing"
|
||||
thinkingMode = :continue_thinking
|
||||
else
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
{systemMsg}
|
||||
You have access to the following tools:
|
||||
{tools}
|
||||
Your need to determine now whether you will use tools or actions to answer the assignment.
|
||||
|
||||
You have the following choices:
|
||||
If you don't need tools or actions to fininsh the assignment say, "{no}".
|
||||
If you need tools or actions to finish the assignment say, "{yes}".
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
{input}
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
toollines = ""
|
||||
for (toolname, v) in a.tools
|
||||
if toolname ∉ ["chatbox", "nothing"]
|
||||
toolline = "$toolname: $(v[:description]) $(v[:input]) $(v[:output])\n"
|
||||
toollines *= toolline
|
||||
end
|
||||
end
|
||||
prompt = replace(prompt, "{systemMsg}" => a.roles[a.role])
|
||||
prompt = replace(prompt, "{tools}" => toollines)
|
||||
prompt = replace(prompt, "{input}" => usermsg)
|
||||
result = sendReceivePrompt(a, prompt)
|
||||
willusetools = GeneralUtils.getStringBetweenCharacters(result, "{", "}")
|
||||
thinkingMode = willusetools == "yes" ? :thinking : :no_thinking
|
||||
end
|
||||
|
||||
return thinkingMode
|
||||
end
|
||||
|
||||
function chooseThinkingMode(a::agentReflex, usermsg::String)
|
||||
thinkingmode = nothing
|
||||
if length(a.thoughtlog) != 0
|
||||
thinkingmode = :continue_thinking
|
||||
else
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
{systemMsg}
|
||||
You always use tools if there is a chance to impove your respond.
|
||||
You have access to the following tools:
|
||||
{tools}
|
||||
Your job is to determine whether you will use tools or actions to respond.
|
||||
|
||||
Choose one of the following choices:
|
||||
If you don't need tools or actions to respond to the stimulus say, "{no}".
|
||||
If you need tools or actions to respond to the stimulus say, "{yes}".
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
{input}
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
toollines = ""
|
||||
for (toolname, v) in a.tools
|
||||
if toolname ∉ ["chatbox", "nothing"]
|
||||
toolline = "$toolname: $(v[:description]) $(v[:input]) $(v[:output])\n"
|
||||
toollines *= toolline
|
||||
end
|
||||
end
|
||||
prompt = replace(prompt, "{systemMsg}" => a.roles[a.role])
|
||||
prompt = replace(prompt, "{tools}" => toollines)
|
||||
prompt = replace(prompt, "{input}" => usermsg)
|
||||
result = sendReceivePrompt(a, prompt)
|
||||
willusetools = GeneralUtils.getStringBetweenCharacters(result, "{", "}")
|
||||
thinkingmode = willusetools == "yes" ? :new_thinking : :no_thinking
|
||||
end
|
||||
|
||||
return thinkingmode
|
||||
end
|
||||
|
||||
# function identifyUserIntention(a::T, usermsg::String) where {T<:agent}
|
||||
# prompt =
|
||||
# """
|
||||
# <|im_start|>system
|
||||
# You are a helpful assistant. Your job is to determine intention of the question.
|
||||
|
||||
# You have the following choices:
|
||||
# If the user question is about general conversation say, "{chat}".
|
||||
# If the user question is about getting wine say, "{wine}".
|
||||
# <|im_end|>
|
||||
|
||||
# Here are the context for the question:
|
||||
# {context}
|
||||
|
||||
# <|im_start|>user
|
||||
# {input}
|
||||
# <|im_end|>
|
||||
# <|im_start|>assistant
|
||||
|
||||
# """
|
||||
# prompt = replace(prompt, "{context}" => "")
|
||||
# prompt = replace(prompt, "{input}" => usermsg)
|
||||
|
||||
# result = sendReceivePrompt(a, prompt)
|
||||
# answer = result === nothing ? nothing : GeneralUtils.getStringBetweenCharacters(result, "{", "}")
|
||||
|
||||
# return answer
|
||||
# end
|
||||
|
||||
"""
|
||||
Send a msg to registered mqtt topic within mqttClient.
|
||||
|
||||
```jldoctest
|
||||
julia> using JSON3, UUIDs, Dates, FileIO, CommUtils, 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"),
|
||||
subtopic= (imgAI="agent/api/v0.1.0/img/respond",
|
||||
txtAI="agent/api/v0.1.0/txt/respond"),
|
||||
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> newAgent = ChatAgent.agentReact(
|
||||
"Jene",
|
||||
mqttClientSpec,
|
||||
role=:assistant_react,
|
||||
msgMeta=msgMeta
|
||||
)
|
||||
```
|
||||
"""
|
||||
function sendReceivePrompt(a::T, prompt::String; timeout::Int=120) where {T<:agent}
|
||||
a.msgMeta[:msgId] = "$(uuid4())" # new msg id for each msg
|
||||
msg = Dict(
|
||||
:msgMeta=> a.msgMeta,
|
||||
:txt=> prompt,
|
||||
)
|
||||
payloadChannel = Channel(1)
|
||||
|
||||
# send prompt
|
||||
CommUtils.request(a.mqttClient, msg)
|
||||
starttime = Dates.now()
|
||||
result = nothing
|
||||
a.step = 0
|
||||
while true # Actor loop
|
||||
a.step += 1
|
||||
@show a.step
|
||||
if a.step <= totalsteps
|
||||
stepdetail = extractStepFromPlan(a, plan, a.step)
|
||||
prompt = actor_mistral_openorca(a, stepdetail)
|
||||
@show prompt
|
||||
respond = sendReceivePrompt(a, prompt)
|
||||
respond = split(respond, "<|im_end|>")[1]
|
||||
@show respond
|
||||
headerToDetect = ["Question:", "Plan:", "Thought:", "Act:", "ActInput:", "Obs:", "...", "Answer:",
|
||||
"Conclusion:", "Summary:"]
|
||||
headers = detectCharacters(respond, headerToDetect)
|
||||
|
||||
while true
|
||||
timepass = (Dates.now() - starttime).value / 1000.0
|
||||
CommUtils.mqttRun(a.mqttClient, payloadChannel)
|
||||
if isready(payloadChannel)
|
||||
topic, payload = take!(payloadChannel)
|
||||
if payload[:msgMeta][:repondToMsgId] == msg[:msgMeta][:msgId]
|
||||
result = haskey(payload, :txt) ? payload[:txt] : nothing
|
||||
# add to memory
|
||||
_respond = addStepNumber(respond, headers, a.step)
|
||||
a.memory[:shortterm] *= _respond
|
||||
|
||||
chunkedtext = chunktext(respond, headers)
|
||||
toolname = toolNameBeingCalled(chunkedtext["Act:"], a.tools)
|
||||
toolinput = chunkedtext["ActInput:"]
|
||||
@show toolname
|
||||
@show toolinput
|
||||
|
||||
#WORKING
|
||||
if toolname == "chatbox" # chat with user
|
||||
# a.memory[:shortterm] *= toolinput
|
||||
respond = toolinput
|
||||
_ = addNewMessage(a, "assistant", respond)
|
||||
result = respond
|
||||
error("actor done 0")
|
||||
actorState = "chatbox"
|
||||
break
|
||||
else # function call
|
||||
f = a.tools[Symbol(toolname)][:func]
|
||||
result = f(toolinput)
|
||||
result = "Obs $(a.step): $result\n"
|
||||
a.memory[:shortterm] *= result
|
||||
error("actor done 1")
|
||||
end
|
||||
elseif timepass <= timeout
|
||||
# skip, within waiting period
|
||||
elseif timepass > timeout
|
||||
println("sendReceivePrompt timeout $timepass/$timeout")
|
||||
result = nothing
|
||||
break
|
||||
else
|
||||
error("undefined condition. timepass=$timepass timeout=$timeout $(@__LINE__)")
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
"""
|
||||
Extract toolname from text.
|
||||
```jldoctest
|
||||
julia> text = " internetsearch\n"
|
||||
julia> tools = Dict(
|
||||
:internetsearch=>Dict(
|
||||
:name => "internetsearch",
|
||||
:description => "Useful for when you need to search the Internet",
|
||||
:input => "Input should be a search query.",
|
||||
:output => "",
|
||||
# :func => internetsearch # function
|
||||
),
|
||||
:chatbox=>Dict(
|
||||
:name => "chatbox",
|
||||
:description => "Useful for when you need to ask a customer what you need to know or to talk with them.",
|
||||
:input => "Input should be a conversation to customer.",
|
||||
:output => "" ,
|
||||
),
|
||||
)
|
||||
julia> toolname = toolNameBeingCalled(text, tools)
|
||||
```
|
||||
"""
|
||||
function toolNameBeingCalled(text::T, tools::Dict) where {T<:AbstractString}
|
||||
toolNameBeingCalled = nothing
|
||||
for (k, v) in tools
|
||||
toolname = String(k)
|
||||
if contains(text, toolname)
|
||||
toolNameBeingCalled = toolname
|
||||
else #TODO finish all steps
|
||||
|
||||
|
||||
|
||||
actorState = "all steps done"
|
||||
error("actor done 2")
|
||||
break
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
return toolNameBeingCalled
|
||||
error("actor done 3")
|
||||
return actorState, result
|
||||
end
|
||||
|
||||
#TODO
|
||||
function checkReasonableness(userMsg::String, context::String, tools)
|
||||
# Ref: https://www.youtube.com/watch?v=XV4IBaZqbps
|
||||
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
You are a helpful assistant. Your job is to check the reasonableness of user assignments.
|
||||
If the user assignment can be answered given the tools available say, "This is a reasonable assignment".
|
||||
If the user assignment cannot be answered then provide some feedback to the user that may improve
|
||||
their assignment.
|
||||
|
||||
Here is the context for the assignment:
|
||||
{context}
|
||||
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
{assignment}
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
|
||||
context = "You have access to the following tools:
|
||||
WineStock: useful for when you need to find info about wine by matching your description, price, name or ID. Input should be a search query with as much details as possible."
|
||||
prompt = replace(prompt, "{assignment}" => userMsg)
|
||||
prompt = replace(prompt, "{context}" => context)
|
||||
|
||||
output_py = llm(
|
||||
prompt,
|
||||
max_tokens=512,
|
||||
temperature=0.1,
|
||||
# top_p=top_p,
|
||||
echo=false,
|
||||
stop=["</response>", "<<END>>", ],
|
||||
)
|
||||
_output_jl = pyconvert(Dict, output_py);
|
||||
output = pyconvert(Dict, _output_jl["choices"][1]);
|
||||
output["text"]
|
||||
|
||||
end
|
||||
|
||||
"""
|
||||
Detect given characters. Output is a list of named tuple of detected char.
|
||||
|
||||
```jldoctest
|
||||
julia> text = "I like to eat apples and use utensils."
|
||||
julia> characters = ["eat", "use", "i"]
|
||||
julia> result = detectCharacters(text, characters)
|
||||
4-element Vector{Any}:
|
||||
(char = "i", start = 4, stop = 4)
|
||||
(char = "eat", start = 11, stop = 13)
|
||||
(char = "use", start = 26, stop = 28)
|
||||
(char = "i", start = 35, stop = 35)
|
||||
```
|
||||
"""
|
||||
function detectCharacters(text::T1, characters::Vector{T2}) where {T1<:AbstractString, T2<:AbstractString}
|
||||
result = []
|
||||
for i in eachindex(text)
|
||||
for char in characters
|
||||
l = length(char)
|
||||
char_startInd = i
|
||||
char_endInd = i+l-1 # -1 because Julia use inclusive index
|
||||
|
||||
if char_endInd > length(text)
|
||||
# skip
|
||||
else
|
||||
try # some time StringIndexError: invalid index [535], valid nearby indices [534]=>'é', [536]=>' '
|
||||
if text[char_startInd: char_endInd] == char
|
||||
push!(result, (char=char, start=char_startInd, stop=char_endInd))
|
||||
end
|
||||
catch
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
"""
|
||||
Find a given character from a vector of named tuple.
|
||||
Output is character location index inside detectedCharacters
|
||||
|
||||
```jldoctest
|
||||
julia a = [ (char = "i", start = 4, stop = 4)
|
||||
(char = "eat", start = 11, stop = 13)
|
||||
(char = "use", start = 26, stop = 28)
|
||||
(char = "i", start = 35, stop = 35) ]
|
||||
julia> findDetectedCharacter(a, "i")
|
||||
[1, 4]
|
||||
```
|
||||
"""
|
||||
function findDetectedCharacter(detectedCharacters, character)
|
||||
allchar = [i[1] for i in detectedCharacters]
|
||||
return findall(isequal.(allchar, character))
|
||||
end
|
||||
|
||||
"""
|
||||
Chunk a text into smaller pieces by header.
|
||||
```jldoctest
|
||||
julia> using ChatAgent
|
||||
julia> text = "Plan: First, we need to find out what kind of wine the user wants."
|
||||
julia> headers = ChatAgent.detectCharacters(text, ["Nope", "sick", "First", "user", "Then", ])
|
||||
3-element Vector{Any}:
|
||||
(char = "First", start = 7, stop = 11)
|
||||
(char = "user", start = 56, stop = 59)
|
||||
(char = "Then", start = 102, stop = 105)
|
||||
julia> chunkedtext = ChatAgent.chunktext(text, headers)
|
||||
2-element Vector{Any}:
|
||||
(header = "First", body = ", we need to find out what kind of wine the ")
|
||||
(header = "user", body = " wants.")
|
||||
```
|
||||
"""
|
||||
function chunktext(text::T, headers) where {T<:AbstractString}
|
||||
result = Dict()
|
||||
|
||||
for (i, v) in enumerate(headers)
|
||||
if i < length(headers)
|
||||
nextheader = headers[i+1]
|
||||
body = text[v[:stop]+1: nextheader[:start]-1]
|
||||
# push!(result, (header=v[:char], body=body))
|
||||
result[v[:char]] = body
|
||||
else
|
||||
body = text[v[:stop]+1: end]
|
||||
# push!(result, (header=v[:char], body=body))
|
||||
result[v[:char]] = body
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
# function chunktext(text::T, headers) where {T<:AbstractString}
|
||||
# result = []
|
||||
|
||||
# for (i, v) in enumerate(headers)
|
||||
# if i < length(headers)
|
||||
# nextheader = headers[i+1]
|
||||
# body = text[v[:stop]+1: nextheader[:start]-1]
|
||||
# push!(result, (header=v[:char], body=body))
|
||||
# else
|
||||
# body = text[v[:stop]+1: end]
|
||||
# push!(result, (header=v[:char], body=body))
|
||||
# end
|
||||
# end
|
||||
|
||||
# return result
|
||||
# end
|
||||
|
||||
|
||||
function extractStepFromPlan(a::agent, plan::T, step::Int) where {T<:AbstractString}
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
You are a helpful assistant.
|
||||
Your job is to extract step $step in the user plan.
|
||||
|
||||
Use the following format only:
|
||||
{copy the step and put it here}
|
||||
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
$plan
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
|
||||
respond = sendReceivePrompt(a, prompt)
|
||||
|
||||
return respond
|
||||
end
|
||||
|
||||
function checkTotalStepInPlan(a::agent, plan::T) where {T<:AbstractString}
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
You are a helpful assistant.
|
||||
Your job is to determine how many steps in a user plan.
|
||||
|
||||
Use the following format to answer:
|
||||
Total step number is {}
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
$plan
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
respond = sendReceivePrompt(a, prompt)
|
||||
result = extract_number(respond)
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
function extract_number(text::T) where {T<:AbstractString}
|
||||
regex = r"\d+" # regular expression to match one or more digits
|
||||
match = Base.match(regex, text) # find the first match in the text
|
||||
if match !== nothing
|
||||
number = parse(Int, match.match)
|
||||
return number
|
||||
else
|
||||
error("No number found in the text")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@ module llmfunction
|
||||
export wikisearch
|
||||
|
||||
using HTTP, JSON3
|
||||
|
||||
using GeneralUtils
|
||||
using ..utils
|
||||
#------------------------------------------------------------------------------------------------100
|
||||
|
||||
"""
|
||||
@@ -24,7 +25,7 @@ function wikisearch(phrase::T) where {T<:AbstractString}
|
||||
phrase = phrase[1] == " " ? phrase[2:end] : phrase
|
||||
# prepare input phrase
|
||||
if occursin("\"", phrase)
|
||||
phrase = GeneralUtils.getStringBetweenCharacters(toolinput, "\"", "\"")
|
||||
phrase = GeneralUtils.getStringBetweenCharacters(phrase, "\"", "\"")
|
||||
end
|
||||
phrase = replace(phrase, "\n"=>"")
|
||||
|
||||
@@ -45,6 +46,10 @@ function wikisearch(phrase::T) where {T<:AbstractString}
|
||||
if result == ""
|
||||
result = "No info available."
|
||||
end
|
||||
|
||||
if result != "No info available." #TODO for use with wikisearch(). Not good for other tools
|
||||
result = makeSummary(a, result)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
@@ -99,7 +104,6 @@ end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ abstract type agent end
|
||||
thoughtlog::String = "" # logs unfinished thoughts
|
||||
attempt::Int = 0 # attempted number
|
||||
step::Int = 0 # step number
|
||||
thinkingroundlimit::Int = 5 # thinking round limit
|
||||
attemptlimit::Int = 5 # thinking round limit
|
||||
thinkingmode::Symbol = :no_thinking
|
||||
thinkingFormat::Union{Dict, Nothing} = nothing
|
||||
memory::Dict = Dict(
|
||||
|
||||
568
src/utils.jl
Normal file
568
src/utils.jl
Normal file
@@ -0,0 +1,568 @@
|
||||
module utils
|
||||
|
||||
export makeSummary, sendReceivePrompt, chunktext, extractStepFromPlan, checkTotalStepInPlan,
|
||||
detectCharacters, findDetectedCharacter, extract_number, toolNameBeingCalled,
|
||||
chooseThinkingMode, conversationSummary, checkReasonableness, addStepNumber
|
||||
|
||||
using UUIDs, Dates
|
||||
using CommUtils, GeneralUtils
|
||||
using ..type
|
||||
|
||||
#------------------------------------------------------------------------------------------------100
|
||||
|
||||
function makeSummary(a::T1, input::T2) where {T1<:agent, T2<:AbstractString}
|
||||
summary = "Nothing."
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
Your need to determine now whether you can make a summary of user's text.
|
||||
|
||||
You have the following choices:
|
||||
If you cannot make a summary say, "{no}".
|
||||
If you can make a summary say, "{yes}".
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
{input}
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
prompt = replace(prompt, "{input}" => usermsg)
|
||||
result = sendReceivePrompt(a, prompt)
|
||||
result = GeneralUtils.getStringBetweenCharacters(result, "{", "}")
|
||||
if result == "yes"
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
You are a helpful assistant.
|
||||
Your job is to make a concise summary of user's text.
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
{input}
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
prompt = replace(prompt, "{input}" => input)
|
||||
result = sendReceivePrompt(a, prompt)
|
||||
summary = replace(result, "<|im_end|>" => "")
|
||||
if summary[1:1] == "\n"
|
||||
summary = summary[2:end]
|
||||
end
|
||||
end
|
||||
|
||||
return summary
|
||||
end
|
||||
|
||||
"""
|
||||
Send a msg to registered mqtt topic within mqttClient.
|
||||
|
||||
```jldoctest
|
||||
julia> using JSON3, UUIDs, Dates, FileIO, CommUtils, 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"),
|
||||
subtopic= (imgAI="agent/api/v0.1.0/img/respond",
|
||||
txtAI="agent/api/v0.1.0/txt/respond"),
|
||||
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> newAgent = ChatAgent.agentReact(
|
||||
"Jene",
|
||||
mqttClientSpec,
|
||||
role=:assistant_react,
|
||||
msgMeta=msgMeta
|
||||
)
|
||||
```
|
||||
"""
|
||||
function sendReceivePrompt(a::T, prompt::String; timeout::Int=120) where {T<:agent}
|
||||
a.msgMeta[:msgId] = "$(uuid4())" # new msg id for each msg
|
||||
msg = Dict(
|
||||
:msgMeta=> a.msgMeta,
|
||||
:txt=> prompt,
|
||||
)
|
||||
payloadChannel = Channel(1)
|
||||
|
||||
# send prompt
|
||||
CommUtils.request(a.mqttClient, msg)
|
||||
starttime = Dates.now()
|
||||
result = nothing
|
||||
|
||||
while true
|
||||
timepass = (Dates.now() - starttime).value / 1000.0
|
||||
CommUtils.mqttRun(a.mqttClient, payloadChannel)
|
||||
if isready(payloadChannel)
|
||||
topic, payload = take!(payloadChannel)
|
||||
if payload[:msgMeta][:repondToMsgId] == msg[:msgMeta][:msgId]
|
||||
result = haskey(payload, :txt) ? payload[:txt] : nothing
|
||||
break
|
||||
end
|
||||
elseif timepass <= timeout
|
||||
# skip, within waiting period
|
||||
elseif timepass > timeout
|
||||
println("sendReceivePrompt timeout $timepass/$timeout")
|
||||
result = nothing
|
||||
break
|
||||
else
|
||||
error("undefined condition. timepass=$timepass timeout=$timeout $(@__LINE__)")
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
Chunk a text into smaller pieces by header.
|
||||
```jldoctest
|
||||
julia> using ChatAgent
|
||||
julia> text = "Plan: First, we need to find out what kind of wine the user wants."
|
||||
julia> headers = ChatAgent.detectCharacters(text, ["Nope", "sick", "First", "user", "Then", ])
|
||||
3-element Vector{Any}:
|
||||
(char = "First", start = 7, stop = 11)
|
||||
(char = "user", start = 56, stop = 59)
|
||||
(char = "Then", start = 102, stop = 105)
|
||||
julia> chunkedtext = ChatAgent.chunktext(text, headers)
|
||||
2-element Vector{Any}:
|
||||
(header = "First", body = ", we need to find out what kind of wine the ")
|
||||
(header = "user", body = " wants.")
|
||||
```
|
||||
"""
|
||||
function chunktext(text::T, headers) where {T<:AbstractString}
|
||||
result = Dict()
|
||||
|
||||
for (i, v) in enumerate(headers)
|
||||
if i < length(headers)
|
||||
nextheader = headers[i+1]
|
||||
body = text[v[:stop]+1: nextheader[:start]-1]
|
||||
# push!(result, (header=v[:char], body=body))
|
||||
result[v[:char]] = body
|
||||
else
|
||||
body = text[v[:stop]+1: end]
|
||||
# push!(result, (header=v[:char], body=body))
|
||||
result[v[:char]] = body
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
function extractStepFromPlan(a::agent, plan::T, step::Int) where {T<:AbstractString}
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
You are a helpful assistant.
|
||||
Your job is to extract step $step in the user plan.
|
||||
|
||||
Use the following format only:
|
||||
{copy the step and put it here}
|
||||
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
$plan
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
|
||||
respond = sendReceivePrompt(a, prompt)
|
||||
|
||||
return respond
|
||||
end
|
||||
|
||||
function checkTotalStepInPlan(a::agent, plan::T) where {T<:AbstractString}
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
You are a helpful assistant.
|
||||
Your job is to determine how many steps in a user plan.
|
||||
|
||||
Use the following format to answer:
|
||||
Total step number is {}
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
$plan
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
respond = sendReceivePrompt(a, prompt)
|
||||
result = extract_number(respond)
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Detect given characters. Output is a list of named tuple of detected char.
|
||||
|
||||
```jldoctest
|
||||
julia> text = "I like to eat apples and use utensils."
|
||||
julia> characters = ["eat", "use", "i"]
|
||||
julia> result = detectCharacters(text, characters)
|
||||
4-element Vector{Any}:
|
||||
(char = "i", start = 4, stop = 4)
|
||||
(char = "eat", start = 11, stop = 13)
|
||||
(char = "use", start = 26, stop = 28)
|
||||
(char = "i", start = 35, stop = 35)
|
||||
```
|
||||
"""
|
||||
function detectCharacters(text::T1, characters::Vector{T2}) where {T1<:AbstractString, T2<:AbstractString}
|
||||
result = []
|
||||
for i in eachindex(text)
|
||||
for char in characters
|
||||
l = length(char)
|
||||
char_startInd = i
|
||||
char_endInd = i+l-1 # -1 because Julia use inclusive index
|
||||
|
||||
if char_endInd > length(text)
|
||||
# skip
|
||||
else
|
||||
try # some time StringIndexError: invalid index [535], valid nearby indices [534]=>'é', [536]=>' '
|
||||
if text[char_startInd: char_endInd] == char
|
||||
push!(result, (char=char, start=char_startInd, stop=char_endInd))
|
||||
end
|
||||
catch
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
"""
|
||||
Find a given character from a vector of named tuple.
|
||||
Output is character location index inside detectedCharacters
|
||||
|
||||
```jldoctest
|
||||
julia a = [ (char = "i", start = 4, stop = 4)
|
||||
(char = "eat", start = 11, stop = 13)
|
||||
(char = "use", start = 26, stop = 28)
|
||||
(char = "i", start = 35, stop = 35) ]
|
||||
julia> findDetectedCharacter(a, "i")
|
||||
[1, 4]
|
||||
```
|
||||
"""
|
||||
function findDetectedCharacter(detectedCharacters, character)
|
||||
allchar = [i[1] for i in detectedCharacters]
|
||||
return findall(isequal.(allchar, character))
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function extract_number(text::T) where {T<:AbstractString}
|
||||
regex = r"\d+" # regular expression to match one or more digits
|
||||
match = Base.match(regex, text) # find the first match in the text
|
||||
if match !== nothing
|
||||
number = parse(Int, match.match)
|
||||
return number
|
||||
else
|
||||
error("No number found in the text")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Extract toolname from text.
|
||||
```jldoctest
|
||||
julia> text = " internetsearch\n"
|
||||
julia> tools = Dict(
|
||||
:internetsearch=>Dict(
|
||||
:name => "internetsearch",
|
||||
:description => "Useful for when you need to search the Internet",
|
||||
:input => "Input should be a search query.",
|
||||
:output => "",
|
||||
# :func => internetsearch # function
|
||||
),
|
||||
:chatbox=>Dict(
|
||||
:name => "chatbox",
|
||||
:description => "Useful for when you need to ask a customer what you need to know or to talk with them.",
|
||||
:input => "Input should be a conversation to customer.",
|
||||
:output => "" ,
|
||||
),
|
||||
)
|
||||
julia> toolname = toolNameBeingCalled(text, tools)
|
||||
```
|
||||
"""
|
||||
function toolNameBeingCalled(text::T, tools::Dict) where {T<:AbstractString}
|
||||
toolNameBeingCalled = nothing
|
||||
for (k, v) in tools
|
||||
toolname = String(k)
|
||||
if contains(text, toolname)
|
||||
toolNameBeingCalled = toolname
|
||||
break
|
||||
end
|
||||
end
|
||||
return toolNameBeingCalled
|
||||
end
|
||||
|
||||
|
||||
|
||||
function chooseThinkingMode(a::agentReflex, usermsg::String)
|
||||
thinkingmode = nothing
|
||||
if length(a.thoughtlog) != 0
|
||||
thinkingmode = :continue_thinking
|
||||
else
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
{systemMsg}
|
||||
You always use tools if there is a chance to impove your respond.
|
||||
You have access to the following tools:
|
||||
{tools}
|
||||
Your job is to determine whether you will use tools or actions to respond.
|
||||
|
||||
Choose one of the following choices:
|
||||
If you don't need tools or actions to respond to the stimulus say, "{no}".
|
||||
If you need tools or actions to respond to the stimulus say, "{yes}".
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
{input}
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
toollines = ""
|
||||
for (toolname, v) in a.tools
|
||||
if toolname ∉ ["chatbox", "nothing"]
|
||||
toolline = "$toolname: $(v[:description]) $(v[:input]) $(v[:output])\n"
|
||||
toollines *= toolline
|
||||
end
|
||||
end
|
||||
prompt = replace(prompt, "{systemMsg}" => a.roles[a.role])
|
||||
prompt = replace(prompt, "{tools}" => toollines)
|
||||
prompt = replace(prompt, "{input}" => usermsg)
|
||||
result = sendReceivePrompt(a, prompt)
|
||||
willusetools = GeneralUtils.getStringBetweenCharacters(result, "{", "}")
|
||||
thinkingmode = willusetools == "yes" ? :new_thinking : :no_thinking
|
||||
end
|
||||
|
||||
return thinkingmode
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
make a conversation summary.
|
||||
```jldoctest
|
||||
julia> conversation = [
|
||||
Dict(:role=> "user", :content=> "I would like to get a bottle of wine", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "assistant", :content=> "What kind of Thai dishes are you having?", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "user", :content=> "It a pad thai.", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "assistant", :content=> "Is there any special occasion for this event?", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "user", :content=> "We'll hold a wedding party at the beach.", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "assistant", :content=> "What is your preferred type of wine?", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "user", :content=> "I like dry white wine with medium tanins.", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "assistant", :content=> "What is your preferred price range for this bottle of wine?", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "user", :content=> "lower than 50 dollars.", :timestamp=> Dates.now()),
|
||||
Dict(:role=> "assistant", :content=> "Based on your preferences and our stock, I recommend the following two wines for you:
|
||||
1. Pierre Girardin \"Murgers des Dents de Chien\" - Saint-Aubin 1er Cru (17 USD)
|
||||
2. Etienne Sauzet'Les Perrieres' - Puligny Montrachet Premier Cru (22 USD)
|
||||
The first wine, Pierre Girardin \"Murgers des Dents de Chien\" - Saint-Aubin 1er Cru, is a great choice for its affordable price and refreshing taste.
|
||||
It pairs well with Thai dishes and will be perfect for your beach wedding party.
|
||||
The second wine, Etienne Sauzet'Les Perrieres' - Puligny Montrachet Premier Cru, offers a more complex flavor profile and slightly higher price point, but still remains within your budget.
|
||||
Both wines are suitable for serving at 22 C temperature.", :timestamp=> Dates.now()),
|
||||
]
|
||||
|
||||
julia> summary = conversationSummary(conversation)
|
||||
```
|
||||
"""
|
||||
function conversationSummary(a::T) where {T<:agent}
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
You talked with a user earlier.
|
||||
Now you make a detailed bullet summary of the conversation from your perspective.
|
||||
You must refers to yourself by "I" in the summary.
|
||||
|
||||
Here are the conversation:
|
||||
{conversation}
|
||||
<|im_end|>
|
||||
|
||||
"""
|
||||
conversation = ""
|
||||
summary = "nothing"
|
||||
if length(a.messages)!= 0
|
||||
for msg in a.messages
|
||||
role = msg[:role]
|
||||
content = msg[:content]
|
||||
|
||||
if role == "user"
|
||||
conversation *= "$role: $content\n"
|
||||
elseif role == "assistant"
|
||||
conversation *= "I: $content\n"
|
||||
else
|
||||
error("undefied condition role = $role")
|
||||
end
|
||||
end
|
||||
prompt = replace(prompt, "{conversation}" => conversation)
|
||||
result = sendReceivePrompt(a, prompt)
|
||||
summary = result === nothing ? "nothing" : result
|
||||
summary = replace(summary, "<|im_end|>" => "")
|
||||
if summary[1:1] == "\n"
|
||||
summary = summary[2:end]
|
||||
end
|
||||
end
|
||||
println("conversation summary: $summary")
|
||||
return summary
|
||||
end
|
||||
|
||||
|
||||
|
||||
#TODO
|
||||
function checkReasonableness(userMsg::String, context::String, tools)
|
||||
# Ref: https://www.youtube.com/watch?v=XV4IBaZqbps
|
||||
|
||||
prompt =
|
||||
"""
|
||||
<|im_start|>system
|
||||
You are a helpful assistant. Your job is to check the reasonableness of user assignments.
|
||||
If the user assignment can be answered given the tools available say, "This is a reasonable assignment".
|
||||
If the user assignment cannot be answered then provide some feedback to the user that may improve
|
||||
their assignment.
|
||||
|
||||
Here is the context for the assignment:
|
||||
{context}
|
||||
|
||||
<|im_end|>
|
||||
|
||||
<|im_start|>user
|
||||
{assignment}
|
||||
<|im_end|>
|
||||
<|im_start|>assistant
|
||||
|
||||
"""
|
||||
|
||||
context = "You have access to the following tools:
|
||||
WineStock: useful for when you need to find info about wine by matching your description, price, name or ID. Input should be a search query with as much details as possible."
|
||||
prompt = replace(prompt, "{assignment}" => userMsg)
|
||||
prompt = replace(prompt, "{context}" => context)
|
||||
|
||||
output_py = llm(
|
||||
prompt,
|
||||
max_tokens=512,
|
||||
temperature=0.1,
|
||||
# top_p=top_p,
|
||||
echo=false,
|
||||
stop=["</response>", "<<END>>", ],
|
||||
)
|
||||
_output_jl = pyconvert(Dict, output_py);
|
||||
output = pyconvert(Dict, _output_jl["choices"][1]);
|
||||
output["text"]
|
||||
|
||||
end
|
||||
|
||||
"""
|
||||
Add step number to header in a text
|
||||
"""
|
||||
function addStepNumber(text::T, headers, step::Int) where {T<:AbstractString}
|
||||
newtext = text
|
||||
for i in headers
|
||||
if occursin(i[:char], newtext)
|
||||
new = replace(i[:char], ":"=> " $step:")
|
||||
newtext = replace(newtext, i[:char]=>new )
|
||||
end
|
||||
end
|
||||
return newtext
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
end # end module
|
||||
Reference in New Issue
Block a user