discourse-ai/lib/ai_bot/tools/tool.rb

284 lines
7.3 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
module DiscourseAi
module AiBot
module Tools
class Tool
# Why 30 mega bytes?
# This general limit is mainly a security feature to avoid tools
# forcing infinite downloads or causing memory exhaustion.
# The limit is somewhat arbitrary and can be increased in future if needed.
MAX_RESPONSE_BODY_LENGTH = 30.megabyte
class << self
def signature
raise NotImplemented
end
def name
raise NotImplemented
end
FEATURE: custom user defined tools (#677) Introduces custom AI tools functionality. 1. Why it was added: The PR adds the ability to create, manage, and use custom AI tools within the Discourse AI system. This feature allows for more flexibility and extensibility in the AI capabilities of the platform. 2. What it does: - Introduces a new `AiTool` model for storing custom AI tools - Adds CRUD (Create, Read, Update, Delete) operations for AI tools - Implements a tool runner system for executing custom tool scripts - Integrates custom tools with existing AI personas - Provides a user interface for managing custom tools in the admin panel 3. Possible use cases: - Creating custom tools for specific tasks or integrations (stock quotes, currency conversion etc...) - Allowing administrators to add new functionalities to AI assistants without modifying core code - Implementing domain-specific tools for particular communities or industries 4. Code structure: The PR introduces several new files and modifies existing ones: a. Models: - `app/models/ai_tool.rb`: Defines the AiTool model - `app/serializers/ai_custom_tool_serializer.rb`: Serializer for AI tools b. Controllers: - `app/controllers/discourse_ai/admin/ai_tools_controller.rb`: Handles CRUD operations for AI tools c. Views and Components: - New Ember.js components for tool management in the admin interface - Updates to existing AI persona management components to support custom tools d. Core functionality: - `lib/ai_bot/tool_runner.rb`: Implements the custom tool execution system - `lib/ai_bot/tools/custom.rb`: Defines the custom tool class e. Routes and configurations: - Updates to route configurations to include new AI tool management pages f. Migrations: - `db/migrate/20240618080148_create_ai_tools.rb`: Creates the ai_tools table g. Tests: - New test files for AI tool functionality and integration The PR integrates the custom tools system with the existing AI persona framework, allowing personas to use both built-in and custom tools. It also includes safety measures such as timeouts and HTTP request limits to prevent misuse of custom tools. Overall, this PR significantly enhances the flexibility and extensibility of the Discourse AI system by allowing administrators to create and manage custom AI tools tailored to their specific needs. Co-authored-by: Martin Brennan <martin@discourse.org>
2024-06-27 03:27:40 -04:00
def custom?
false
end
def accepted_options
[]
end
def option(name, type:)
Option.new(tool: self, name: name, type: type)
end
def help
I18n.t("discourse_ai.ai_bot.tool_help.#{signature[:name]}")
end
def custom_system_message
nil
end
end
attr_accessor :custom_raw
attr_reader :tool_call_id, :persona_options, :bot_user, :llm, :context, :parameters
def initialize(
parameters,
tool_call_id: "",
persona_options: {},
bot_user:,
llm:,
context: {}
)
@parameters = parameters
@tool_call_id = tool_call_id
@persona_options = persona_options
@bot_user = bot_user
@llm = llm
@context = context
end
def name
self.class.name
end
def summary
I18n.t("discourse_ai.ai_bot.tool_summary.#{name}")
end
def details
I18n.t("discourse_ai.ai_bot.tool_description.#{name}", description_args)
end
def help
I18n.t("discourse_ai.ai_bot.tool_help.#{name}")
end
def options
result = HashWithIndifferentAccess.new
self.class.accepted_options.each do |option|
val = @persona_options[option.name]
if val
case option.type
when :boolean
val = (val.to_s == "true")
when :integer
val = val.to_i
end
result[option.name] = val
end
end
result
end
def chain_next_response?
true
end
def standalone?
false
end
protected
def fetch_default_branch(repo)
api_url = "https://api.github.com/repos/#{repo}"
response_code = "unknown error"
repo_data = nil
send_http_request(
api_url,
headers: {
"Accept" => "application/vnd.github.v3+json",
},
authenticate_github: true,
) do |response|
response_code = response.code
if response_code == "200"
begin
repo_data = JSON.parse(read_response_body(response))
rescue JSON::ParserError
response_code = "500 - JSON parse error"
end
end
end
response_code == "200" ? repo_data["default_branch"] : "main"
end
FEATURE: custom user defined tools (#677) Introduces custom AI tools functionality. 1. Why it was added: The PR adds the ability to create, manage, and use custom AI tools within the Discourse AI system. This feature allows for more flexibility and extensibility in the AI capabilities of the platform. 2. What it does: - Introduces a new `AiTool` model for storing custom AI tools - Adds CRUD (Create, Read, Update, Delete) operations for AI tools - Implements a tool runner system for executing custom tool scripts - Integrates custom tools with existing AI personas - Provides a user interface for managing custom tools in the admin panel 3. Possible use cases: - Creating custom tools for specific tasks or integrations (stock quotes, currency conversion etc...) - Allowing administrators to add new functionalities to AI assistants without modifying core code - Implementing domain-specific tools for particular communities or industries 4. Code structure: The PR introduces several new files and modifies existing ones: a. Models: - `app/models/ai_tool.rb`: Defines the AiTool model - `app/serializers/ai_custom_tool_serializer.rb`: Serializer for AI tools b. Controllers: - `app/controllers/discourse_ai/admin/ai_tools_controller.rb`: Handles CRUD operations for AI tools c. Views and Components: - New Ember.js components for tool management in the admin interface - Updates to existing AI persona management components to support custom tools d. Core functionality: - `lib/ai_bot/tool_runner.rb`: Implements the custom tool execution system - `lib/ai_bot/tools/custom.rb`: Defines the custom tool class e. Routes and configurations: - Updates to route configurations to include new AI tool management pages f. Migrations: - `db/migrate/20240618080148_create_ai_tools.rb`: Creates the ai_tools table g. Tests: - New test files for AI tool functionality and integration The PR integrates the custom tools system with the existing AI persona framework, allowing personas to use both built-in and custom tools. It also includes safety measures such as timeouts and HTTP request limits to prevent misuse of custom tools. Overall, this PR significantly enhances the flexibility and extensibility of the Discourse AI system by allowing administrators to create and manage custom AI tools tailored to their specific needs. Co-authored-by: Martin Brennan <martin@discourse.org>
2024-06-27 03:27:40 -04:00
def send_http_request(
url,
headers: {},
authenticate_github: false,
follow_redirects: false,
method: :get,
body: nil,
&blk
)
self.class.send_http_request(
url,
headers: headers,
authenticate_github: authenticate_github,
follow_redirects: follow_redirects,
method: method,
body: body,
&blk
)
end
def self.send_http_request(
url,
headers: {},
authenticate_github: false,
follow_redirects: false,
method: :get,
body: nil
)
raise "Expecting caller to use a block" if !block_given?
uri = nil
url = UrlHelper.normalized_encode(url)
uri =
begin
URI.parse(url)
rescue StandardError
nil
end
return if !uri
if follow_redirects
fd =
FinalDestination.new(
url,
validate_uri: true,
max_redirects: 5,
follow_canonical: true,
)
uri = fd.resolve
end
return if uri.blank?
FEATURE: custom user defined tools (#677) Introduces custom AI tools functionality. 1. Why it was added: The PR adds the ability to create, manage, and use custom AI tools within the Discourse AI system. This feature allows for more flexibility and extensibility in the AI capabilities of the platform. 2. What it does: - Introduces a new `AiTool` model for storing custom AI tools - Adds CRUD (Create, Read, Update, Delete) operations for AI tools - Implements a tool runner system for executing custom tool scripts - Integrates custom tools with existing AI personas - Provides a user interface for managing custom tools in the admin panel 3. Possible use cases: - Creating custom tools for specific tasks or integrations (stock quotes, currency conversion etc...) - Allowing administrators to add new functionalities to AI assistants without modifying core code - Implementing domain-specific tools for particular communities or industries 4. Code structure: The PR introduces several new files and modifies existing ones: a. Models: - `app/models/ai_tool.rb`: Defines the AiTool model - `app/serializers/ai_custom_tool_serializer.rb`: Serializer for AI tools b. Controllers: - `app/controllers/discourse_ai/admin/ai_tools_controller.rb`: Handles CRUD operations for AI tools c. Views and Components: - New Ember.js components for tool management in the admin interface - Updates to existing AI persona management components to support custom tools d. Core functionality: - `lib/ai_bot/tool_runner.rb`: Implements the custom tool execution system - `lib/ai_bot/tools/custom.rb`: Defines the custom tool class e. Routes and configurations: - Updates to route configurations to include new AI tool management pages f. Migrations: - `db/migrate/20240618080148_create_ai_tools.rb`: Creates the ai_tools table g. Tests: - New test files for AI tool functionality and integration The PR integrates the custom tools system with the existing AI persona framework, allowing personas to use both built-in and custom tools. It also includes safety measures such as timeouts and HTTP request limits to prevent misuse of custom tools. Overall, this PR significantly enhances the flexibility and extensibility of the Discourse AI system by allowing administrators to create and manage custom AI tools tailored to their specific needs. Co-authored-by: Martin Brennan <martin@discourse.org>
2024-06-27 03:27:40 -04:00
request = nil
if method == :get
request = FinalDestination::HTTP::Get.new(uri)
elsif method == :post
request = FinalDestination::HTTP::Post.new(uri)
end
raise ArgumentError, "Invalid method: #{method}" if !request
request.body = body if body
request["User-Agent"] = DiscourseAi::AiBot::USER_AGENT
headers.each { |k, v| request[k] = v }
if authenticate_github && SiteSetting.ai_bot_github_access_token.present?
request["Authorization"] = "Bearer #{SiteSetting.ai_bot_github_access_token}"
end
FinalDestination::HTTP.start(uri.hostname, uri.port, use_ssl: uri.port != 80) do |http|
http.request(request) { |response| yield response }
end
end
def self.read_response_body(response, max_length: nil)
max_length ||= MAX_RESPONSE_BODY_LENGTH
body = +""
response.read_body do |chunk|
body << chunk
break if body.bytesize > max_length
end
if body.bytesize > max_length
body[0...max_length].scrub
else
body.scrub
end
end
def read_response_body(response, max_length: nil)
self.class.read_response_body(response, max_length: max_length)
end
def truncate(text, llm:, percent_length: nil, max_length: nil)
if !percent_length && !max_length
raise ArgumentError, "You must provide either percent_length or max_length"
end
target = llm.max_prompt_tokens
target = (target * percent_length).to_i if percent_length
if max_length
target = max_length if target > max_length
end
llm.tokenizer.truncate(text, target)
end
def accepted_options
[]
end
def option(name, type:)
Option.new(tool: self, name: name, type: type)
end
def description_args
{}
end
def format_results(rows, column_names = nil, args: nil)
rows = rows&.map { |row| yield row } if block_given?
if !column_names
index = -1
column_indexes = {}
rows =
rows&.map do |data|
new_row = []
data.each do |key, value|
found_index = column_indexes[key.to_s] ||= (index += 1)
new_row[found_index] = value
end
new_row
end
column_names = column_indexes.keys
end
# this is not the most efficient format
# however this is needed cause GPT 3.5 / 4 was steered using JSON
result = { column_names: column_names, rows: rows }
result[:args] = args if args
result
end
end
end
end
end