# frozen_string_literal: true module DiscourseAi module Completions module Dialects class XmlTools def initialize(tools) @raw_tools = tools end def translated_tools result = +"" raw_tools.each do |tool| parameters = +"" if tool.parameters.present? tool.parameters.each do |parameter| parameters << <<~PARAMETER #{parameter.name} #{parameter.type} #{parameter.description} #{parameter.required} PARAMETER if parameter.item_type parameters << "#{parameter.item_type}\n" end parameters << "#{parameter.enum.join(",")}\n" if parameter.enum parameters << "\n" end end result << <<~TOOLS #{tool.name} #{tool.description} #{parameters} TOOLS end result end def instructions return "" if raw_tools.blank? @instructions ||= begin has_arrays = raw_tools.any? { |tool| tool.parameters&.any? { |p| p.type == "array" } } (<<~TEXT).strip #{tool_preamble(include_array_tip: has_arrays)} #{translated_tools} TEXT end end DONE_MESSAGE = "Regardless of what you think, REPLY IMMEDIATELY, WITHOUT MAKING ANY FURTHER TOOL CALLS, YOU ARE OUT OF TOOL CALL QUOTA!" def from_raw_tool(raw_message) result = (<<~TEXT).strip #{raw_message[:name] || raw_message[:id]} #{raw_message[:content]} TEXT if @injecting_done "#{result}\n\n#{DONE_MESSAGE}" else result end end def from_raw_tool_call(raw_message) parsed = JSON.parse(raw_message[:content], symbolize_names: true) parameters = +"" if parsed[:arguments] parameters << "\n" parsed[:arguments].each { |k, v| parameters << "<#{k}>#{v}\n" } parameters << "\n" end (<<~TEXT).strip #{raw_message[:name] || parsed[:name]} #{parameters} TEXT end def inject_done(&blk) @injecting_done = true blk.call ensure @injecting_done = false end private attr_reader :raw_tools def tool_preamble(include_array_tip: true) array_tip = if include_array_tip <<~TEXT If a parameter type is an array, return an array of values. For example: <$PARAMETER_NAME>["one","two","three"] TEXT else "" end <<~TEXT 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. $TOOL_NAME <$PARAMETER_NAME>$PARAMETER_VALUE ... #{array_tip} If you wish to call multiple function in one reply, wrap multiple block in a single block. - Always prefer to lead with tool calls, if you need to execute any. - Avoid all niceties prior to tool calls, Eg: "Let me look this up for you.." etc. - DO NOT encode HTML entities in tool calls. You may use for encoding if required. Here are the complete list of tools available: TEXT end end end end end