# frozen_string_literal: true module DiscourseAi module Completions module Dialects class CohereTools def initialize(tools) @raw_tools = tools end def tool_results(messages) pairs = [] current_pair = nil messages.each do |msg| if current_pair == nil && msg[:type] == :tool_call current_pair = [msg] elsif current_pair && msg[:type] == :tool current_pair << msg pairs << current_pair current_pair = nil else current_pair = nil end end pairs.map do |call, result| params = JSON.parse(call[:content])["arguments"] { call: { name: call[:name] == "search" ? "search_local" : call[:name], parameters: params, generation_id: call[:id], }, outputs: [JSON.parse(result[:content])], } end end def translated_tools raw_tools.map do |t| tool = t.dup tool[:parameter_definitions] = t[:parameters] .to_a .reduce({}) do |memo, p| name = p[:name] memo[name] = { description: p[:description], type: cohere_type(p[:type], p[:item_type]), required: p[:required], } memo[name][:default] = p[:default] if p[:default] memo end { name: tool[:name] == "search" ? "search_local" : tool[:name], description: tool[:description], parameter_definitions: tool[:parameter_definitions], } end end def instructions "" # Noop. Tools are listed separate. end private attr_reader :raw_tools def cohere_type(type, item_type) case type when "string" "str" when "number" item_type == "integer" ? "int" : "float" when "boolean" "bool" when "object" item_type ? "Dict[#{item_type}]" : "Dict" when "array" item_type ? "List[#{item_type}]" : "List" else type end end end end end end