work in progress

This commit is contained in:
Sam Saffron 2024-11-07 10:15:35 +11:00
parent fd7ccfd0ab
commit 30ebe562ea
No known key found for this signature in database
GPG Key ID: B9606168D2FFD9F5
10 changed files with 45 additions and 55 deletions

View File

@ -190,7 +190,7 @@ module DiscourseAi
update_blk.call("", cancel, build_placeholder(tool.summary, "")) update_blk.call("", cancel, build_placeholder(tool.summary, ""))
result = result =
tool.invoke do |progress| tool.invoke(**tool.parameters) do |progress|
placeholder = build_placeholder(tool.summary, progress) placeholder = build_placeholder(tool.summary, progress)
update_blk.call("", cancel, placeholder) update_blk.call("", cancel, placeholder)
end end

View File

@ -243,14 +243,16 @@ module DiscourseAi
arguments[name.to_sym] = value if value arguments[name.to_sym] = value if value
end end
tool_klass.new( instance = tool_klass.new(
arguments,
tool_call_id: function_id || function_name, tool_call_id: function_id || function_name,
persona_options: options[tool_klass].to_h, persona_options: options[tool_klass].to_h,
bot_user: bot_user, bot_user: bot_user,
llm: llm, llm: llm,
context: context, context: context,
) )
instance.parameters = arguments
instance
end end
def strip_quotes(value) def strip_quotes(value)

View File

@ -35,7 +35,8 @@ module DiscourseAi
super(*args, **kwargs) super(*args, **kwargs)
end end
def invoke def invoke(**parameters)
@parameters = parameters
result = runner.invoke result = runner.invoke
if runner.custom_raw if runner.custom_raw
self.custom_raw = runner.custom_raw self.custom_raw = runner.custom_raw

View File

@ -4,6 +4,8 @@ module DiscourseAi
module AiBot module AiBot
module Tools module Tools
class DallE < Tool class DallE < Tool
attr_reader :prompts, :aspect_ratio
def self.signature def self.signature
{ {
name: name, name: name,
@ -32,19 +34,14 @@ module DiscourseAi
"dall_e" "dall_e"
end end
def prompts
parameters[:prompts]
end
def aspect_ratio
parameters[:aspect_ratio]
end
def chain_next_response? def chain_next_response?
false false
end end
def invoke def invoke(prompts:, aspect_ratio: "square")
@prompts = prompts
@aspect_ratio = aspect_ratio
# max 4 prompts # max 4 prompts
max_prompts = prompts.take(4) max_prompts = prompts.take(4)
progress = prompts.first progress = prompts.first

View File

@ -4,6 +4,8 @@ module DiscourseAi
module AiBot module AiBot
module Tools module Tools
class Google < Tool class Google < Tool
attr_reader :query
def self.signature def self.signature
{ {
name: name, name: name,
@ -27,12 +29,9 @@ module DiscourseAi
[option(:base_query, type: :string)] [option(:base_query, type: :string)]
end end
def query def invoke(query:)
parameters[:query].to_s.strip query = query.to_s.strip
end @query = query
def invoke
query = self.query
yield(query) yield(query)

View File

@ -4,6 +4,8 @@ module DiscourseAi
module AiBot module AiBot
module Tools module Tools
class Time < Tool class Time < Tool
attr_reader :timezone
def self.signature def self.signature
{ {
name: name, name: name,
@ -23,11 +25,8 @@ module DiscourseAi
"time" "time"
end end
def timezone def invoke(timezone:)
parameters[:timezone].to_s @timezone = timezone
end
def invoke
time = time =
begin begin
::Time.now.in_time_zone(timezone) ::Time.now.in_time_zone(timezone)
@ -37,7 +36,6 @@ module DiscourseAi
time = ::Time.now if !time time = ::Time.now if !time
@last_time = time.to_s @last_time = time.to_s
{ args: { timezone: timezone }, time: time.to_s } { args: { timezone: timezone }, time: time.to_s }
end end

View File

@ -40,18 +40,16 @@ module DiscourseAi
end end
end end
attr_accessor :custom_raw attr_accessor :custom_raw, :parameters
attr_reader :tool_call_id, :persona_options, :bot_user, :llm, :context, :parameters attr_reader :tool_call_id, :persona_options, :bot_user, :llm, :context
def initialize( def initialize(
parameters,
tool_call_id: "", tool_call_id: "",
persona_options: {}, persona_options: {},
bot_user:, bot_user:,
llm:, llm:,
context: {} context: {}
) )
@parameters = parameters
@tool_call_id = tool_call_id @tool_call_id = tool_call_id
@persona_options = persona_options @persona_options = persona_options
@bot_user = bot_user @bot_user = bot_user

View File

@ -4,6 +4,8 @@ module DiscourseAi
module AiBot module AiBot
module Tools module Tools
class WebBrowser < Tool class WebBrowser < Tool
attr_reader :url
def self.signature def self.signature
{ {
name: name, name: name,
@ -24,15 +26,9 @@ module DiscourseAi
"web_browser" "web_browser"
end end
def url def invoke(url:)
return @url if defined?(@url) url = "https://#{url}" if !url.start_with?("http")
@url = parameters[:url] @url = url
@url = "https://#{@url}" if !@url.start_with?("http")
@url
end
def invoke
send_http_request(url, follow_redirects: true) do |response| send_http_request(url, follow_redirects: true) do |response|
if response.code == "200" if response.code == "200"
html = read_response_body(response) html = read_response_body(response)

View File

@ -5,7 +5,7 @@ RSpec.describe DiscourseAi::AiBot::Tools::Google do
let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) } let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) }
let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") }
let(:progress_blk) { Proc.new {} } let(:progress_blk) { Proc.new {} }
let(:search) { described_class.new({ query: "some search term" }, bot_user: bot_user, llm: llm) } let(:search) { described_class.new(bot_user: bot_user, llm: llm) }
before do before do
SiteSetting.ai_bot_enabled = true SiteSetting.ai_bot_enabled = true
@ -22,7 +22,7 @@ RSpec.describe DiscourseAi::AiBot::Tools::Google do
"https://www.googleapis.com/customsearch/v1?cx=cx&key=abc&num=10&q=some%20search%20term", "https://www.googleapis.com/customsearch/v1?cx=cx&key=abc&num=10&q=some%20search%20term",
).to_return(status: 200, body: json_text, headers: {}) ).to_return(status: 200, body: json_text, headers: {})
info = search.invoke(&progress_blk).to_json info = search.invoke(query: "some search term", &progress_blk).to_json
expect(search.results_count).to eq(0) expect(search.results_count).to eq(0)
expect(info).to_not include("oops") expect(info).to_not include("oops")
@ -33,7 +33,6 @@ RSpec.describe DiscourseAi::AiBot::Tools::Google do
search = search =
described_class.new( described_class.new(
{ query: "some search term" },
bot_user: bot_user, bot_user: bot_user,
llm: llm, llm: llm,
persona_options: { persona_options: {
@ -48,7 +47,7 @@ RSpec.describe DiscourseAi::AiBot::Tools::Google do
"https://www.googleapis.com/customsearch/v1?cx=cx&key=abc&num=10&q=site:discourse.org%20some%20search%20term", "https://www.googleapis.com/customsearch/v1?cx=cx&key=abc&num=10&q=site:discourse.org%20some%20search%20term",
).to_return(status: 200, body: json_text, headers: {}) ).to_return(status: 200, body: json_text, headers: {})
search.invoke(&progress_blk) search.invoke(query: "some search term", &progress_blk)
end end
it "can generate correct info" do it "can generate correct info" do
@ -80,7 +79,7 @@ RSpec.describe DiscourseAi::AiBot::Tools::Google do
"https://www.googleapis.com/customsearch/v1?cx=cx&key=abc&num=10&q=some%20search%20term", "https://www.googleapis.com/customsearch/v1?cx=cx&key=abc&num=10&q=some%20search%20term",
).to_return(status: 200, body: json_text, headers: {}) ).to_return(status: 200, body: json_text, headers: {})
info = search.invoke(&progress_blk).to_json info = search.invoke(query: "some search term", &progress_blk).to_json
expect(search.results_count).to eq(2) expect(search.results_count).to eq(2)
expect(info).to include("title1") expect(info).to include("title1")

View File

@ -19,8 +19,8 @@ RSpec.describe DiscourseAi::AiBot::Tools::WebBrowser do
"<html><head><title>Test</title></head><body><p>This is a simplified version of the webpage content.</p></body></html>", "<html><head><title>Test</title></head><body><p>This is a simplified version of the webpage content.</p></body></html>",
) )
tool = described_class.new({ url: url }, bot_user: bot_user, llm: llm) tool = described_class.new(bot_user: bot_user, llm: llm)
result = tool.invoke result = tool.invoke(url: url)
expect(result).to have_key(:text) expect(result).to have_key(:text)
expect(result[:text]).to eq(processed_text) expect(result[:text]).to eq(processed_text)
@ -33,8 +33,8 @@ RSpec.describe DiscourseAi::AiBot::Tools::WebBrowser do
# Simulating a failed request # Simulating a failed request
stub_request(:get, url).to_return(status: [500, "Internal Server Error"]) stub_request(:get, url).to_return(status: [500, "Internal Server Error"])
tool = described_class.new({ url: url }, bot_user: bot_user, llm: llm) tool = described_class.new(bot_user: bot_user, llm: llm)
result = tool.invoke result = tool.invoke(url: url)
expect(result).to have_key(:error) expect(result).to have_key(:error)
expect(result[:error]).to include("Failed to retrieve the web page") expect(result[:error]).to include("Failed to retrieve the web page")
@ -48,8 +48,8 @@ RSpec.describe DiscourseAi::AiBot::Tools::WebBrowser do
simple_html = "<html><body><p>Simple content.</p></body></html>" simple_html = "<html><body><p>Simple content.</p></body></html>"
stub_request(:get, url).to_return(status: 200, body: simple_html) stub_request(:get, url).to_return(status: 200, body: simple_html)
tool = described_class.new({ url: url }, bot_user: bot_user, llm: llm) tool = described_class.new(bot_user: bot_user, llm: llm)
result = tool.invoke result = tool.invoke(url: url)
expect(result[:text]).to eq("Simple content.") expect(result[:text]).to eq("Simple content.")
end end
@ -59,8 +59,8 @@ RSpec.describe DiscourseAi::AiBot::Tools::WebBrowser do
"<html><head><script>console.log('Ignore me')</script></head><body><style>body { background-color: #000; }</style><p>Only relevant content here.</p></body></html>" "<html><head><script>console.log('Ignore me')</script></head><body><style>body { background-color: #000; }</style><p>Only relevant content here.</p></body></html>"
stub_request(:get, url).to_return(status: 200, body: complex_html) stub_request(:get, url).to_return(status: 200, body: complex_html)
tool = described_class.new({ url: url }, bot_user: bot_user, llm: llm) tool = described_class.new(bot_user: bot_user, llm: llm)
result = tool.invoke result = tool.invoke(url: url)
expect(result[:text]).to eq("Only relevant content here.") expect(result[:text]).to eq("Only relevant content here.")
end end
@ -70,8 +70,8 @@ RSpec.describe DiscourseAi::AiBot::Tools::WebBrowser do
"<html><body><div><section><p>Nested paragraph 1.</p></section><section><p>Nested paragraph 2.</p></section></div></body></html>" "<html><body><div><section><p>Nested paragraph 1.</p></section><section><p>Nested paragraph 2.</p></section></div></body></html>"
stub_request(:get, url).to_return(status: 200, body: nested_html) stub_request(:get, url).to_return(status: 200, body: nested_html)
tool = described_class.new({ url: url }, bot_user: bot_user, llm: llm) tool = described_class.new(bot_user: bot_user, llm: llm)
result = tool.invoke result = tool.invoke(url: url)
expect(result[:text]).to eq("Nested paragraph 1. Nested paragraph 2.") expect(result[:text]).to eq("Nested paragraph 1. Nested paragraph 2.")
end end
@ -86,8 +86,8 @@ RSpec.describe DiscourseAi::AiBot::Tools::WebBrowser do
stub_request(:get, initial_url).to_return(status: 302, headers: { "Location" => final_url }) stub_request(:get, initial_url).to_return(status: 302, headers: { "Location" => final_url })
stub_request(:get, final_url).to_return(status: 200, body: redirect_html) stub_request(:get, final_url).to_return(status: 200, body: redirect_html)
tool = described_class.new({ url: initial_url }, bot_user: bot_user, llm: llm) tool = described_class.new(bot_user: bot_user, llm: llm)
result = tool.invoke result = tool.invoke(url: initial_url)
expect(result[:url]).to eq(final_url) expect(result[:url]).to eq(final_url)
expect(result[:text]).to eq("Redirected content.") expect(result[:text]).to eq("Redirected content.")