DEV: simplify command framework (#125)
The command framework had some confusing dispatching where it would dispatch JSON blobs, this meant there was lots of parsing required in every command The refactor handles transforming the args prior to dispatch which makes consuming far simpler This is also general prep to supporting some basic command framework in other llms.
This commit is contained in:
parent
eb7fff3a55
commit
7edb57c005
|
@ -24,7 +24,7 @@ module DiscourseAi::AiBot::Commands
|
||||||
{ count: @last_count || 0 }
|
{ count: @last_count || 0 }
|
||||||
end
|
end
|
||||||
|
|
||||||
def process(_args)
|
def process
|
||||||
columns = {
|
columns = {
|
||||||
name: "Name",
|
name: "Name",
|
||||||
slug: "Slug",
|
slug: "Slug",
|
||||||
|
|
|
@ -106,7 +106,9 @@ module DiscourseAi
|
||||||
post.post_custom_prompt ||= post.build_post_custom_prompt(custom_prompt: [])
|
post.post_custom_prompt ||= post.build_post_custom_prompt(custom_prompt: [])
|
||||||
prompt = post.post_custom_prompt.custom_prompt || []
|
prompt = post.post_custom_prompt.custom_prompt || []
|
||||||
|
|
||||||
prompt << [process(args).to_json, self.class.name, "function"]
|
parsed_args = JSON.parse(args).symbolize_keys
|
||||||
|
|
||||||
|
prompt << [process(**parsed_args).to_json, self.class.name, "function"]
|
||||||
post.post_custom_prompt.update!(custom_prompt: prompt)
|
post.post_custom_prompt.update!(custom_prompt: prompt)
|
||||||
|
|
||||||
raw = +(<<~HTML)
|
raw = +(<<~HTML)
|
||||||
|
|
|
@ -39,13 +39,11 @@ module DiscourseAi::AiBot::Commands
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def process(search_string)
|
def process(query:)
|
||||||
search_string = JSON.parse(search_string)["query"]
|
@last_query = query
|
||||||
|
|
||||||
@last_query = search_string
|
|
||||||
api_key = SiteSetting.ai_google_custom_search_api_key
|
api_key = SiteSetting.ai_google_custom_search_api_key
|
||||||
cx = SiteSetting.ai_google_custom_search_cx
|
cx = SiteSetting.ai_google_custom_search_cx
|
||||||
query = CGI.escape(search_string)
|
query = CGI.escape(query)
|
||||||
uri =
|
uri =
|
||||||
URI("https://www.googleapis.com/customsearch/v1?key=#{api_key}&cx=#{cx}&q=#{query}&num=10")
|
URI("https://www.googleapis.com/customsearch/v1?key=#{api_key}&cx=#{cx}&q=#{query}&num=10")
|
||||||
body = Net::HTTP.get(uri)
|
body = Net::HTTP.get(uri)
|
||||||
|
|
|
@ -43,8 +43,8 @@ module DiscourseAi::AiBot::Commands
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def process(prompt)
|
def process(prompt:)
|
||||||
@last_prompt = prompt = JSON.parse(prompt)["prompt"]
|
@last_prompt = prompt
|
||||||
results = DiscourseAi::Inference::StabilityGenerator.perform!(prompt)
|
results = DiscourseAi::Inference::StabilityGenerator.perform!(prompt)
|
||||||
|
|
||||||
uploads = []
|
uploads = []
|
||||||
|
|
|
@ -93,17 +93,15 @@ module DiscourseAi::AiBot::Commands
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def process(search_args)
|
def process(**search_args)
|
||||||
parsed = JSON.parse(search_args)
|
|
||||||
|
|
||||||
limit = nil
|
limit = nil
|
||||||
|
|
||||||
search_string =
|
search_string =
|
||||||
parsed
|
search_args
|
||||||
.map do |key, value|
|
.map do |key, value|
|
||||||
if key == "search_query"
|
if key == :search_query
|
||||||
value
|
value
|
||||||
elsif key == "limit"
|
elsif key == :limit
|
||||||
limit = value.to_i
|
limit = value.to_i
|
||||||
nil
|
nil
|
||||||
else
|
else
|
||||||
|
|
|
@ -44,9 +44,7 @@ module DiscourseAi::AiBot::Commands
|
||||||
{ url: "#{Discourse.base_path}/t/-/#{@last_topic_id}", title: @last_topic_title || "" }
|
{ url: "#{Discourse.base_path}/t/-/#{@last_topic_id}", title: @last_topic_title || "" }
|
||||||
end
|
end
|
||||||
|
|
||||||
def process(instructions)
|
def process(topic_id:, guidance: nil)
|
||||||
topic_id, guidance = instructions.split(" ", 2)
|
|
||||||
|
|
||||||
@last_topic_id = topic_id
|
@last_topic_id = topic_id
|
||||||
|
|
||||||
topic_id = topic_id.to_i
|
topic_id = topic_id.to_i
|
||||||
|
|
|
@ -24,7 +24,7 @@ module DiscourseAi::AiBot::Commands
|
||||||
{ count: @last_count || 0 }
|
{ count: @last_count || 0 }
|
||||||
end
|
end
|
||||||
|
|
||||||
def process(_args)
|
def process
|
||||||
column_names = { name: "Name", public_topic_count: "Topic Count" }
|
column_names = { name: "Name", public_topic_count: "Topic Count" }
|
||||||
|
|
||||||
tags =
|
tags =
|
||||||
|
|
|
@ -31,9 +31,7 @@ module DiscourseAi::AiBot::Commands
|
||||||
{ timezone: @last_timezone, time: @last_time }
|
{ timezone: @last_timezone, time: @last_time }
|
||||||
end
|
end
|
||||||
|
|
||||||
def process(args)
|
def process(timezone:)
|
||||||
timezone = JSON.parse(args)["timezone"]
|
|
||||||
|
|
||||||
time =
|
time =
|
||||||
begin
|
begin
|
||||||
Time.now.in_time_zone(timezone)
|
Time.now.in_time_zone(timezone)
|
||||||
|
@ -45,7 +43,7 @@ module DiscourseAi::AiBot::Commands
|
||||||
@last_timezone = timezone
|
@last_timezone = timezone
|
||||||
@last_time = time.to_s
|
@last_time = time.to_s
|
||||||
|
|
||||||
{ args: args, time: time.to_s }
|
{ args: { timezone: timezone }, time: time.to_s }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -40,7 +40,7 @@ RSpec.describe DiscourseAi::AiBot::Bot do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#reply_to" do
|
describe "#reply_to" do
|
||||||
it "can respond to !search" do
|
it "can respond to a search command" do
|
||||||
bot.system_prompt_style!(:simple)
|
bot.system_prompt_style!(:simple)
|
||||||
bot.max_commands_per_reply = 2
|
bot.max_commands_per_reply = 2
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ RSpec.describe DiscourseAi::AiBot::Bot do
|
||||||
result =
|
result =
|
||||||
DiscourseAi::AiBot::Commands::SearchCommand
|
DiscourseAi::AiBot::Commands::SearchCommand
|
||||||
.new(nil, nil)
|
.new(nil, nil)
|
||||||
.process({ query: "test search" }.to_json)
|
.process(query: "test search")
|
||||||
.to_json
|
.to_json
|
||||||
|
|
||||||
prompt << { role: "function", content: result, name: "search" }
|
prompt << { role: "function", content: result, name: "search" }
|
||||||
|
|
|
@ -7,7 +7,7 @@ RSpec.describe DiscourseAi::AiBot::Commands::CategoriesCommand do
|
||||||
it "can generate correct info" do
|
it "can generate correct info" do
|
||||||
Fabricate(:category, name: "america", posts_year: 999)
|
Fabricate(:category, name: "america", posts_year: 999)
|
||||||
|
|
||||||
info = DiscourseAi::AiBot::Commands::CategoriesCommand.new(nil, nil).process(nil)
|
info = DiscourseAi::AiBot::Commands::CategoriesCommand.new(nil, nil).process
|
||||||
expect(info.to_s).to include("america")
|
expect(info.to_s).to include("america")
|
||||||
expect(info.to_s).to include("999")
|
expect(info.to_s).to include("999")
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,7 +33,7 @@ RSpec.describe DiscourseAi::AiBot::Commands::GoogleCommand do
|
||||||
).to_return(status: 200, body: json_text, headers: {})
|
).to_return(status: 200, body: json_text, headers: {})
|
||||||
|
|
||||||
google = described_class.new(bot_user, post)
|
google = described_class.new(bot_user, post)
|
||||||
info = google.process({ query: "some search term" }.to_json).to_json
|
info = google.process(query: "some search term").to_json
|
||||||
|
|
||||||
expect(google.description_args[:count]).to eq(1)
|
expect(google.description_args[:count]).to eq(1)
|
||||||
expect(info).to include("title1")
|
expect(info).to include("title1")
|
||||||
|
|
|
@ -13,8 +13,9 @@ RSpec.describe DiscourseAi::AiBot::Commands::SearchCommand do
|
||||||
post1 = Fabricate(:post)
|
post1 = Fabricate(:post)
|
||||||
search = described_class.new(bot_user, post1)
|
search = described_class.new(bot_user, post1)
|
||||||
|
|
||||||
results = search.process({ query: "order:fake ABDDCDCEDGDG" }.to_json)
|
results = search.process(query: "order:fake ABDDCDCEDGDG")
|
||||||
expect(results[:args]).to eq("{\"query\":\"order:fake ABDDCDCEDGDG\"}")
|
|
||||||
|
expect(results[:args]).to eq({ query: "order:fake ABDDCDCEDGDG" })
|
||||||
expect(results[:rows]).to eq([])
|
expect(results[:rows]).to eq([])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -25,7 +26,7 @@ RSpec.describe DiscourseAi::AiBot::Commands::SearchCommand do
|
||||||
|
|
||||||
search = described_class.new(bot_user, post1)
|
search = described_class.new(bot_user, post1)
|
||||||
|
|
||||||
results = search.process({ limit: 1, user: post1.user.username }.to_json)
|
results = search.process(limit: 1, user: post1.user.username)
|
||||||
expect(results[:rows].to_s).to include("/subfolder" + post1.url)
|
expect(results[:rows].to_s).to include("/subfolder" + post1.url)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -37,13 +38,13 @@ RSpec.describe DiscourseAi::AiBot::Commands::SearchCommand do
|
||||||
# search has no built in support for limit: so handle it from the outside
|
# search has no built in support for limit: so handle it from the outside
|
||||||
search = described_class.new(bot_user, post1)
|
search = described_class.new(bot_user, post1)
|
||||||
|
|
||||||
results = search.process({ limit: 2, user: post1.user.username }.to_json)
|
results = search.process(limit: 2, user: post1.user.username)
|
||||||
|
|
||||||
expect(results[:column_names].length).to eq(4)
|
expect(results[:column_names].length).to eq(4)
|
||||||
expect(results[:rows].length).to eq(2)
|
expect(results[:rows].length).to eq(2)
|
||||||
|
|
||||||
# just searching for everything
|
# just searching for everything
|
||||||
results = search.process({ order: "latest_topic" }.to_json)
|
results = search.process(order: "latest_topic")
|
||||||
expect(results[:rows].length).to be > 1
|
expect(results[:rows].length).to be > 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,7 +15,7 @@ RSpec.describe DiscourseAi::AiBot::Commands::SummarizeCommand do
|
||||||
)
|
)
|
||||||
|
|
||||||
summarizer = described_class.new(bot_user, post)
|
summarizer = described_class.new(bot_user, post)
|
||||||
info = summarizer.process("#{post.topic_id} why did it happen?")
|
info = summarizer.process(topic_id: post.topic_id, guidance: "why did it happen?")
|
||||||
|
|
||||||
expect(info).to include("Topic summarized")
|
expect(info).to include("Topic summarized")
|
||||||
expect(summarizer.custom_raw).to include("summary stuff")
|
expect(summarizer.custom_raw).to include("summary stuff")
|
||||||
|
@ -31,7 +31,7 @@ RSpec.describe DiscourseAi::AiBot::Commands::SummarizeCommand do
|
||||||
post = Fabricate(:post, topic: topic)
|
post = Fabricate(:post, topic: topic)
|
||||||
|
|
||||||
summarizer = described_class.new(bot_user, post)
|
summarizer = described_class.new(bot_user, post)
|
||||||
info = summarizer.process("#{post.topic_id} why did it happen?")
|
info = summarizer.process(topic_id: post.topic_id, guidance: "why did it happen?")
|
||||||
|
|
||||||
expect(info).not_to include(post.raw)
|
expect(info).not_to include(post.raw)
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ RSpec.describe DiscourseAi::AiBot::Commands::TagsCommand do
|
||||||
Fabricate(:tag, name: "america", public_topic_count: 100)
|
Fabricate(:tag, name: "america", public_topic_count: 100)
|
||||||
Fabricate(:tag, name: "not_here", public_topic_count: 0)
|
Fabricate(:tag, name: "not_here", public_topic_count: 0)
|
||||||
|
|
||||||
info = DiscourseAi::AiBot::Commands::TagsCommand.new(nil, nil).process(nil)
|
info = DiscourseAi::AiBot::Commands::TagsCommand.new(nil, nil).process
|
||||||
|
|
||||||
expect(info.to_s).to include("america")
|
expect(info.to_s).to include("america")
|
||||||
expect(info.to_s).not_to include("not_here")
|
expect(info.to_s).not_to include("not_here")
|
||||||
|
|
|
@ -7,8 +7,8 @@ RSpec.describe DiscourseAi::AiBot::Commands::TimeCommand do
|
||||||
it "can generate correct info" do
|
it "can generate correct info" do
|
||||||
freeze_time
|
freeze_time
|
||||||
|
|
||||||
args = { timezone: "America/Los_Angeles" }.to_json
|
args = { timezone: "America/Los_Angeles" }
|
||||||
info = DiscourseAi::AiBot::Commands::TimeCommand.new(nil, nil).process(args)
|
info = DiscourseAi::AiBot::Commands::TimeCommand.new(nil, nil).process(**args)
|
||||||
|
|
||||||
expect(info).to eq({ args: args, time: Time.now.in_time_zone("America/Los_Angeles").to_s })
|
expect(info).to eq({ args: args, time: Time.now.in_time_zone("America/Los_Angeles").to_s })
|
||||||
expect(info.to_s).not_to include("not_here")
|
expect(info.to_s).not_to include("not_here")
|
||||||
|
|
Loading…
Reference in New Issue