# 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 ... Here are the tools available: #{tools} PROMPT end end end end