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.
118 lines
3.3 KiB
Ruby
118 lines
3.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require_relative "../../../support/openai_completions_inference_stubs"
|
|
|
|
RSpec.describe DiscourseAi::AiBot::Bot do
|
|
fab!(:bot_user) { User.find(DiscourseAi::AiBot::EntryPoint::GPT4_ID) }
|
|
fab!(:bot) { described_class.as(bot_user) }
|
|
|
|
fab!(:user) { Fabricate(:user) }
|
|
fab!(:pm) do
|
|
Fabricate(
|
|
:private_message_topic,
|
|
title: "This is my special PM",
|
|
user: user,
|
|
topic_allowed_users: [
|
|
Fabricate.build(:topic_allowed_user, user: user),
|
|
Fabricate.build(:topic_allowed_user, user: bot_user),
|
|
],
|
|
)
|
|
end
|
|
fab!(:first_post) { Fabricate(:post, topic: pm, user: user, raw: "This is a reply by the user") }
|
|
fab!(:second_post) do
|
|
Fabricate(:post, topic: pm, user: user, raw: "This is a second reply by the user")
|
|
end
|
|
|
|
describe "#system_prompt" do
|
|
it "includes relevant context in system prompt" do
|
|
bot.system_prompt_style!(:standard)
|
|
|
|
SiteSetting.title = "My Forum"
|
|
SiteSetting.site_description = "My Forum Description"
|
|
|
|
system_prompt = bot.system_prompt(second_post)
|
|
|
|
expect(system_prompt).to include(SiteSetting.title)
|
|
expect(system_prompt).to include(SiteSetting.site_description)
|
|
|
|
expect(system_prompt).to include(user.username)
|
|
end
|
|
end
|
|
|
|
describe "#reply_to" do
|
|
it "can respond to a search command" do
|
|
bot.system_prompt_style!(:simple)
|
|
bot.max_commands_per_reply = 2
|
|
|
|
expected_response = {
|
|
function_call: {
|
|
name: "search",
|
|
arguments: { query: "test search" }.to_json,
|
|
},
|
|
}
|
|
|
|
prompt = bot.bot_prompt_with_topic_context(second_post)
|
|
|
|
req_opts = bot.reply_params.merge({ functions: bot.available_functions, stream: true })
|
|
|
|
OpenAiCompletionsInferenceStubs.stub_streamed_response(
|
|
prompt,
|
|
[expected_response],
|
|
model: bot.model_for,
|
|
req_opts: req_opts,
|
|
)
|
|
|
|
result =
|
|
DiscourseAi::AiBot::Commands::SearchCommand
|
|
.new(nil, nil)
|
|
.process(query: "test search")
|
|
.to_json
|
|
|
|
prompt << { role: "function", content: result, name: "search" }
|
|
|
|
OpenAiCompletionsInferenceStubs.stub_streamed_response(
|
|
prompt,
|
|
[content: "I found nothing, sorry"],
|
|
model: bot.model_for,
|
|
req_opts: req_opts,
|
|
)
|
|
|
|
bot.reply_to(second_post)
|
|
|
|
last = second_post.topic.posts.order("id desc").first
|
|
|
|
expect(last.raw).to include("<details>")
|
|
expect(last.raw).to include("<summary>Search</summary>")
|
|
expect(last.raw).not_to include("translation missing")
|
|
expect(last.raw).to include("I found nothing")
|
|
|
|
expect(last.post_custom_prompt.custom_prompt).to eq(
|
|
[[result, "search", "function"], ["I found nothing, sorry", bot_user.username]],
|
|
)
|
|
end
|
|
end
|
|
|
|
describe "#update_pm_title" do
|
|
let(:expected_response) { "This is a suggested title" }
|
|
|
|
before { SiteSetting.min_personal_message_post_length = 5 }
|
|
|
|
it "updates the title using bot suggestions" do
|
|
OpenAiCompletionsInferenceStubs.stub_response(
|
|
bot.title_prompt(second_post),
|
|
expected_response,
|
|
model: bot.model_for,
|
|
req_opts: {
|
|
temperature: 0.7,
|
|
top_p: 0.9,
|
|
max_tokens: 40,
|
|
},
|
|
)
|
|
|
|
bot.update_pm_title(second_post)
|
|
|
|
expect(pm.reload.title).to eq(expected_response)
|
|
end
|
|
end
|
|
end
|