# frozen_string_literal: true module DiscourseAi module Agents module Tools class Custom < Tool def self.class_instance(tool_id) klass = Class.new(self) klass.tool_id = tool_id klass end def self.custom? true end def self.tool_id @tool_id end def self.tool_id=(tool_id) @tool_id = tool_id end def self.signature AiTool.find(tool_id).signature end # Backwards compatibility: if tool_name is not set (existing custom tools), use name def self.name name, tool_name = AiTool.where(id: tool_id).pluck(:name, :tool_name).first tool_name.presence || name end def self.has_custom_context? # note on safety, this can be cached safely, we bump the whole agent cache when an ai tool is saved # which will expire this class return @has_custom_context if defined?(@has_custom_context) @has_custom_context = false ai_tool = AiTool.find_by(id: tool_id) if ai_tool.script.include?("customContext") runner = ai_tool.runner({}, llm: nil, bot_user: nil, context: nil) @has_custom_context = runner.has_custom_context? end @has_custom_context end def self.inject_prompt(prompt:, context:, agent:) if has_custom_context? ai_tool = AiTool.find_by(id: tool_id) if ai_tool runner = ai_tool.runner({}, llm: nil, bot_user: nil, context: context) custom_context = runner.custom_context if custom_context.present? last_message = prompt.messages.last last_message[:content] = "#{custom_context}\n\n#{last_message[:content]}" end end end end def initialize(*args, **kwargs) @chain_next_response = true super(*args, **kwargs) end def invoke result = runner.invoke if runner.custom_raw self.custom_raw = runner.custom_raw @chain_next_response = false end result end def runner @runner ||= ai_tool.runner(parameters, llm: llm, bot_user: bot_user, context: context) end def ai_tool @ai_tool ||= AiTool.find(self.class.tool_id) end def summary ai_tool.summary end def details runner.details end def chain_next_response? !!@chain_next_response end def help # I do not think this is called, but lets make sure raise "Not implemented" end end end end end