# frozen_string_literal: true
module ::DiscourseAi
module Inference
class FunctionList
def initialize
@functions = []
end
def <<(function)
@functions << function
end
def parse_prompt(prompt)
xml = prompt.sub(%r{(.*)}m, '\1')
if xml.present?
parsed = []
Nokogiri
.XML(xml)
.xpath("//invoke")
.each do |invoke_node|
function = { name: invoke_node.xpath("//tool_name").text, arguments: {} }
parsed << function
invoke_node
.xpath("//parameters")
.children
.each do |parameters_node|
if parameters_node.is_a?(Nokogiri::XML::Element) && name = parameters_node.name
function[:arguments][name.to_sym] = parameters_node.text
end
end
end
coerce_arguments!(parsed)
end
end
def coerce_arguments!(parsed)
parsed.each do |function_call|
arguments = function_call[:arguments]
function = @functions.find { |f| f.name == function_call[:name] }
next if !function
arguments.each do |name, value|
parameter = function.parameters.find { |p| p[:name].to_s == name.to_s }
if !parameter
arguments.delete(name)
next
end
type = parameter[:type]
if type == "array"
begin
arguments[name] = JSON.parse(value)
rescue JSON::ParserError
# maybe LLM chose a different shape for the array
arguments[name] = value.to_s.split("\n").map(&:strip).reject(&:blank?)
end
elsif type == "integer"
arguments[name] = value.to_i
elsif type == "float"
arguments[name] = value.to_f
end
end
end
parsed
end
def system_prompt
tools = +""
@functions.each do |function|
parameters = +""
if function.parameters.present?
parameters << "\n"
function.parameters.each do |parameter|
parameters << <<~PARAMETER
#{parameter[:name]}
#{parameter[:type]}
#{parameter[:description]}
#{parameter[:required]}
PARAMETER
parameters << "#{parameter[:enum].join(",")}\n" if parameter[:enum]
parameters << "\n"
end
end
tools << <<~TOOLS
#{function.name}
#{function.description}
#{parameters}
TOOLS
end
<<~PROMPT
In this environment you have access to a set of tools you can use to answer the user's question.
You may call them like this. Only invoke one function at a time and wait for the results before invoking another function:
$TOOL_NAME
<$PARAMETER_NAME>$PARAMETER_VALUE$PARAMETER_NAME>
...
Here are the tools available:
#{tools}
PROMPT
end
end
end
end