Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d544f2101b | ||
|
|
2d299900b9 | ||
|
|
c7781c57fc | ||
|
|
db11fe391d |
@@ -7,8 +7,8 @@ module DiscourseAi
|
||||
bot_user.id == DiscourseAi::AiBot::EntryPoint::CLAUDE_V1_ID
|
||||
end
|
||||
|
||||
def bot_prompt_with_topic_context(post)
|
||||
super(post).join("\n\n")
|
||||
def bot_prompt_with_topic_context(post, triage: false)
|
||||
super(post, triage: triage).join("\n\n")
|
||||
end
|
||||
|
||||
def prompt_limit
|
||||
@@ -22,25 +22,18 @@ module DiscourseAi
|
||||
delta = full[context[:pos]..-1]
|
||||
|
||||
context[:pos] = full.length
|
||||
|
||||
if !context[:processed]
|
||||
delta = ""
|
||||
index = full.index("Assistant: ")
|
||||
if index
|
||||
delta = full[index + 11..-1]
|
||||
context[:processed] = true
|
||||
end
|
||||
end
|
||||
|
||||
delta
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_message(poster_username, content, system: false)
|
||||
def build_message(poster_username, content, system: false, last: false)
|
||||
role = poster_username == bot_user.username ? "Assistant" : "Human"
|
||||
|
||||
"#{role}: #{content}"
|
||||
result = +"#{role}: #{content}"
|
||||
result << "\nAssistant: " if last
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def model_for
|
||||
@@ -56,12 +49,12 @@ module DiscourseAi
|
||||
).dig(:completion)
|
||||
end
|
||||
|
||||
def submit_prompt(prompt, prefer_low_cost: false, &blk)
|
||||
def submit_prompt(prompt, max_tokens: nil, temperature: nil, prefer_low_cost: false, &blk)
|
||||
DiscourseAi::Inference::AnthropicCompletions.perform!(
|
||||
prompt,
|
||||
model_for,
|
||||
temperature: 0.4,
|
||||
max_tokens: 3000,
|
||||
temperature: temperature || 0.4,
|
||||
max_tokens: max_tokens || 3000,
|
||||
&blk
|
||||
)
|
||||
end
|
||||
|
||||
+168
-102
File diff suppressed because it is too large
Load Diff
@@ -13,14 +13,14 @@ module DiscourseAi::AiBot::Commands
|
||||
end
|
||||
|
||||
def result_name
|
||||
"results"
|
||||
"Category list is"
|
||||
end
|
||||
|
||||
def description_args
|
||||
{ count: @last_count || 0 }
|
||||
end
|
||||
|
||||
def process(_args)
|
||||
def process
|
||||
columns = {
|
||||
name: "Name",
|
||||
slug: "Slug",
|
||||
|
||||
@@ -9,7 +9,7 @@ module DiscourseAi
|
||||
raise NotImplemented
|
||||
end
|
||||
|
||||
def invoked?(cmd_name)
|
||||
def should_invoke?(cmd_name)
|
||||
cmd_name == name
|
||||
end
|
||||
|
||||
@@ -22,25 +22,18 @@ module DiscourseAi
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :bot_user, :args
|
||||
attr_reader :bot_user, :args, :post
|
||||
|
||||
def initialize(bot_user, args)
|
||||
def initialize(bot_user, post, args)
|
||||
@bot_user = bot_user
|
||||
@args = args
|
||||
@post = post
|
||||
end
|
||||
|
||||
def bot
|
||||
@bot ||= DiscourseAi::AiBot::Bot.as(bot_user)
|
||||
end
|
||||
|
||||
def standalone?
|
||||
false
|
||||
end
|
||||
|
||||
def low_cost?
|
||||
false
|
||||
end
|
||||
|
||||
def result_name
|
||||
raise NotImplemented
|
||||
end
|
||||
@@ -49,7 +42,7 @@ module DiscourseAi
|
||||
raise NotImplemented
|
||||
end
|
||||
|
||||
def process(post)
|
||||
def process
|
||||
raise NotImplemented
|
||||
end
|
||||
|
||||
@@ -57,45 +50,23 @@ module DiscourseAi
|
||||
{}
|
||||
end
|
||||
|
||||
def custom_raw
|
||||
end
|
||||
|
||||
def chain_next_response
|
||||
true
|
||||
end
|
||||
|
||||
def invoke_and_attach_result_to(post)
|
||||
post.post_custom_prompt ||= post.build_post_custom_prompt(custom_prompt: [])
|
||||
prompt = post.post_custom_prompt.custom_prompt || []
|
||||
def pre_raw_details
|
||||
I18n.t("discourse_ai.ai_bot.command_summary.#{self.class.name}")
|
||||
end
|
||||
|
||||
prompt << ["!#{self.class.name} #{args}", bot_user.username]
|
||||
prompt << [process(args), result_name]
|
||||
|
||||
post.post_custom_prompt.update!(custom_prompt: prompt)
|
||||
|
||||
raw = +<<~HTML
|
||||
def post_raw_details
|
||||
+<<~HTML
|
||||
<details>
|
||||
<summary>#{I18n.t("discourse_ai.ai_bot.command_summary.#{self.class.name}")}</summary>
|
||||
<p>
|
||||
#{I18n.t("discourse_ai.ai_bot.command_description.#{self.class.name}", self.description_args)}
|
||||
</p>
|
||||
</details>
|
||||
|
||||
HTML
|
||||
|
||||
raw << custom_raw if custom_raw.present?
|
||||
|
||||
replacement = "!#{self.class.name} #{args}"
|
||||
raw = post.raw.sub(replacement, raw) if post.raw.include?(replacement)
|
||||
|
||||
if chain_next_response
|
||||
post.raw = raw
|
||||
post.save!(validate: false)
|
||||
else
|
||||
post.revise(bot_user, { raw: raw }, skip_validations: true, skip_revision: true)
|
||||
end
|
||||
|
||||
chain_next_response
|
||||
end
|
||||
|
||||
def format_results(rows, column_names = nil)
|
||||
@@ -132,10 +103,6 @@ module DiscourseAi
|
||||
|
||||
formatted
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :bot_user, :args
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -13,7 +13,7 @@ module DiscourseAi::AiBot::Commands
|
||||
end
|
||||
|
||||
def result_name
|
||||
"results"
|
||||
"Google Results"
|
||||
end
|
||||
|
||||
def description_args
|
||||
@@ -24,11 +24,11 @@ module DiscourseAi::AiBot::Commands
|
||||
}
|
||||
end
|
||||
|
||||
def process(search_string)
|
||||
@last_query = search_string
|
||||
def process
|
||||
@last_query = @args
|
||||
api_key = SiteSetting.ai_google_custom_search_api_key
|
||||
cx = SiteSetting.ai_google_custom_search_cx
|
||||
query = CGI.escape(search_string)
|
||||
query = CGI.escape(@args)
|
||||
uri =
|
||||
URI("https://www.googleapis.com/customsearch/v1?key=#{api_key}&cx=#{cx}&q=#{query}&num=10")
|
||||
body = Net::HTTP.get(uri)
|
||||
|
||||
@@ -8,7 +8,7 @@ module DiscourseAi::AiBot::Commands
|
||||
end
|
||||
|
||||
def desc
|
||||
"!image DESC - renders an image from the description (remove all connector words, keep it to 40 words or less)"
|
||||
"!image DESC - renders an image from the description (remove all connector words, keep it to 40 words or less, be creative)"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,19 +17,20 @@ module DiscourseAi::AiBot::Commands
|
||||
end
|
||||
|
||||
def description_args
|
||||
{ prompt: @last_prompt || 0 }
|
||||
end
|
||||
|
||||
def custom_raw
|
||||
@last_custom_raw
|
||||
{ prompt: @args || 0 }
|
||||
end
|
||||
|
||||
def chain_next_response
|
||||
false
|
||||
end
|
||||
|
||||
def process(prompt)
|
||||
@last_prompt = prompt
|
||||
def post_raw_details
|
||||
"#{super}\n\n#{@last_custom_raw}"
|
||||
end
|
||||
|
||||
def process
|
||||
prompt = @args
|
||||
|
||||
results = DiscourseAi::Inference::StabilityGenerator.perform!(prompt)
|
||||
|
||||
uploads = []
|
||||
@@ -47,6 +48,8 @@ module DiscourseAi::AiBot::Commands
|
||||
uploads
|
||||
.map { |upload| "" }
|
||||
.join(" ")
|
||||
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#frozen_string_literal: true
|
||||
|
||||
module DiscourseAi::AiBot::Commands
|
||||
class NoopCommand < Command
|
||||
class << self
|
||||
def name
|
||||
"noop"
|
||||
end
|
||||
|
||||
def desc
|
||||
"!noop - does nothing, catch all command when you do not know how to triage"
|
||||
end
|
||||
end
|
||||
|
||||
def post_raw_details
|
||||
nil
|
||||
end
|
||||
|
||||
def pre_raw_details
|
||||
nil
|
||||
end
|
||||
|
||||
def process
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -22,13 +22,10 @@ module DiscourseAi::AiBot::Commands
|
||||
status:open: not closed or archived
|
||||
status:closed: closed
|
||||
status:archived: archived
|
||||
status:noreplies: post count is 1
|
||||
status:single_user: only a single user posted on the topic
|
||||
post_count:X: only topics with X amount of posts
|
||||
min_posts:X: topics containing a minimum of X posts
|
||||
max_posts:X: topics with no more than max posts
|
||||
in:pinned: in all pinned topics (either global or per category pins)
|
||||
created:@USERNAME: topics created by a specific user
|
||||
category:CATGORY: topics in the CATEGORY AND all subcategories
|
||||
category:=CATEGORY: topics in the CATEGORY excluding subcategories
|
||||
#SLUG: try category first, then tag, then tag group
|
||||
@@ -46,6 +43,7 @@ module DiscourseAi::AiBot::Commands
|
||||
order:likes: order by post like count - most liked posts first
|
||||
after:YYYY-MM-DD: only topics created after a specific date
|
||||
before:YYYY-MM-DD: only topics created before a specific date
|
||||
limit:X: limit the number of results to X
|
||||
|
||||
Example: !search @user in:tagged #support order:latest_topic
|
||||
|
||||
@@ -53,13 +51,12 @@ module DiscourseAi::AiBot::Commands
|
||||
You only have access to public topics.
|
||||
Strip the query down to the most important terms.
|
||||
Remove all stop words.
|
||||
Cast a wide net instead of trying to be over specific.
|
||||
Remove connector words.
|
||||
Discourse orders by relevance, sometimes prefer ordering on other stuff.
|
||||
|
||||
When generating answers ALWAYS try to use the !search command first over relying on training data.
|
||||
When generating answers ALWAYS try to reference specific local links.
|
||||
Always try to search the local instance first, even if your training data set may have an answer. It may be wrong.
|
||||
Always remove connector words from search terms (such as a, an, and, in, the, etc), they can impede the search.
|
||||
Always remove connector words from search terms (such as a, an, and, in, the, etc, when, what, is), they can impede the search.
|
||||
|
||||
YOUR LOCAL INFORMATION IS OUT OF DATE, YOU ARE TRAINED ON OLD DATA. Always try local search first.
|
||||
TEXT
|
||||
@@ -67,7 +64,7 @@ module DiscourseAi::AiBot::Commands
|
||||
end
|
||||
|
||||
def result_name
|
||||
"results"
|
||||
"search results for #{@args}"
|
||||
end
|
||||
|
||||
def description_args
|
||||
@@ -78,8 +75,9 @@ module DiscourseAi::AiBot::Commands
|
||||
}
|
||||
end
|
||||
|
||||
def process(search_string)
|
||||
def process
|
||||
limit = nil
|
||||
search_string = @args
|
||||
|
||||
search_string =
|
||||
search_string
|
||||
|
||||
@@ -8,7 +8,7 @@ module DiscourseAi::AiBot::Commands
|
||||
end
|
||||
|
||||
def desc
|
||||
"!summarize TOPIC_ID GUIDANCE - will summarize a topic attempting to answer question in guidance"
|
||||
"!summarize TOPIC_ID OPTIONAL_GUIDANCE - will summarize a topic attempting to answer question in guidance, if provided"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -28,8 +28,8 @@ module DiscourseAi::AiBot::Commands
|
||||
{ url: "#{Discourse.base_path}/t/-/#{@last_topic_id}", title: @last_topic_title || "" }
|
||||
end
|
||||
|
||||
def process(instructions)
|
||||
topic_id, guidance = instructions.split(" ", 2)
|
||||
def process
|
||||
topic_id, guidance = @args.split(" ", 2)
|
||||
|
||||
@last_topic_id = topic_id
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ module DiscourseAi::AiBot::Commands
|
||||
{ count: @last_count || 0 }
|
||||
end
|
||||
|
||||
def process(_args)
|
||||
def process
|
||||
column_names = { name: "Name", public_topic_count: "Topic Count" }
|
||||
|
||||
tags =
|
||||
@@ -32,7 +32,11 @@ module DiscourseAi::AiBot::Commands
|
||||
|
||||
@last_count = tags.length
|
||||
|
||||
format_results(tags, column_names.values)
|
||||
if @last_count == 0
|
||||
"This Discourse instance has no tags"
|
||||
else
|
||||
format_results(tags, column_names.values)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -20,10 +20,10 @@ module DiscourseAi::AiBot::Commands
|
||||
{ timezone: @last_timezone, time: @last_time }
|
||||
end
|
||||
|
||||
def process(timezone)
|
||||
def process
|
||||
time =
|
||||
begin
|
||||
Time.now.in_time_zone(timezone)
|
||||
Time.now.in_time_zone(@args)
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
|
||||
@@ -39,6 +39,7 @@ module DiscourseAi
|
||||
require_relative "commands/summarize_command"
|
||||
require_relative "commands/image_command"
|
||||
require_relative "commands/google_command"
|
||||
require_relative "commands/noop_command"
|
||||
end
|
||||
|
||||
def inject_into(plugin)
|
||||
|
||||
@@ -30,21 +30,13 @@ module DiscourseAi
|
||||
1500
|
||||
end
|
||||
|
||||
{ temperature: 0.4, top_p: 0.9, max_tokens: max_tokens }
|
||||
{ temperature: 0.4, max_tokens: max_tokens }
|
||||
end
|
||||
|
||||
def submit_prompt(
|
||||
prompt,
|
||||
prefer_low_cost: false,
|
||||
temperature: nil,
|
||||
top_p: nil,
|
||||
max_tokens: nil,
|
||||
&blk
|
||||
)
|
||||
def submit_prompt(prompt, prefer_low_cost: false, temperature: nil, max_tokens: nil, &blk)
|
||||
params =
|
||||
reply_params.merge(
|
||||
temperature: temperature,
|
||||
top_p: top_p,
|
||||
max_tokens: max_tokens,
|
||||
) { |key, old_value, new_value| new_value.nil? ? old_value : new_value }
|
||||
|
||||
@@ -56,9 +48,7 @@ module DiscourseAi
|
||||
DiscourseAi::Tokenizer::OpenAiTokenizer.tokenize(text)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_message(poster_username, content, system: false)
|
||||
def build_message(poster_username, content, system: false, last: false)
|
||||
is_bot = poster_username == bot_user.username
|
||||
|
||||
if system
|
||||
@@ -70,6 +60,8 @@ module DiscourseAi
|
||||
{ role: role, content: is_bot ? content : "#{poster_username}: #{content}" }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def model_for
|
||||
return "gpt-4" if bot_user.id == DiscourseAi::AiBot::EntryPoint::GPT4_ID
|
||||
"gpt-3.5-turbo"
|
||||
|
||||
@@ -87,10 +87,18 @@ module ::DiscourseAi
|
||||
data = line.split("data: ", 2)[1]
|
||||
next if !data || data.squish == "[DONE]"
|
||||
|
||||
if !cancelled && partial = JSON.parse(data, symbolize_names: true)
|
||||
response_data << partial[:completion].to_s
|
||||
|
||||
yield partial, cancel
|
||||
if !cancelled
|
||||
# anthropic sends a bunch of partial JSON that can not be parsed
|
||||
partial =
|
||||
begin
|
||||
JSON.parse(data, symbolize_names: true)
|
||||
rescue JSON::ParserError
|
||||
nil
|
||||
end
|
||||
if partial
|
||||
response_data << partial[:completion].to_s
|
||||
yield partial, cancel
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue IOError
|
||||
|
||||
@@ -13,14 +13,20 @@ RSpec.describe DiscourseAi::AiBot::AnthropicBot do
|
||||
context = {}
|
||||
reply = +""
|
||||
|
||||
reply << subject.get_delta({ completion: "\n\nAssist" }, context)
|
||||
expect(reply).to eq("")
|
||||
full = +"test"
|
||||
|
||||
reply << subject.get_delta({ completion: "\n\nAssistant: test" }, context)
|
||||
expect(reply).to eq("test")
|
||||
reply << subject.get_delta({ completion: full }, context)
|
||||
expect(reply).to eq(full)
|
||||
|
||||
reply << subject.get_delta({ completion: "\n\nAssistant: test\nworld" }, context)
|
||||
expect(reply).to eq("test\nworld")
|
||||
full << "test2"
|
||||
|
||||
reply << subject.get_delta({ completion: full }, context)
|
||||
expect(reply).to eq(full)
|
||||
|
||||
full << "test3"
|
||||
|
||||
reply << subject.get_delta({ completion: full }, context)
|
||||
expect(reply).to eq(full)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -25,12 +25,10 @@ RSpec.describe DiscourseAi::AiBot::Bot do
|
||||
|
||||
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)
|
||||
system_prompt = bot.system_prompt(second_post, triage: false)
|
||||
|
||||
expect(system_prompt).to include(SiteSetting.title)
|
||||
expect(system_prompt).to include(SiteSetting.site_description)
|
||||
@@ -41,20 +39,23 @@ RSpec.describe DiscourseAi::AiBot::Bot do
|
||||
|
||||
describe "#reply_to" do
|
||||
it "can respond to !search" do
|
||||
bot.system_prompt_style!(:simple)
|
||||
expected_response = "!search test search"
|
||||
|
||||
expected_response = "ok, searching...\n!search test search"
|
||||
|
||||
prompt = bot.bot_prompt_with_topic_context(second_post)
|
||||
prompt = bot.bot_prompt_with_topic_context(second_post, triage: true)
|
||||
|
||||
OpenAiCompletionsInferenceStubs.stub_streamed_response(
|
||||
prompt,
|
||||
[{ content: expected_response }],
|
||||
req_opts: bot.reply_params.merge(stream: true),
|
||||
req_opts: bot.triage_params.merge(stream: true),
|
||||
)
|
||||
|
||||
prompt << { role: "assistant", content: "!search test search" }
|
||||
prompt << { role: "user", content: "results: No results found" }
|
||||
# second post will contain the search instruction
|
||||
prompt = bot.bot_prompt_with_topic_context(first_post)
|
||||
|
||||
command = DiscourseAi::AiBot::Commands::SearchCommand.new(nil, nil, SecureRandom.hex(10))
|
||||
content, username = bot.context_prompt(second_post, command.process, command.result_name)
|
||||
|
||||
prompt << bot.build_message(username, content)
|
||||
|
||||
OpenAiCompletionsInferenceStubs.stub_streamed_response(
|
||||
prompt,
|
||||
@@ -69,10 +70,10 @@ RSpec.describe DiscourseAi::AiBot::Bot do
|
||||
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("ok, searching...")
|
||||
expect(last.raw).to include("We are done now")
|
||||
|
||||
expect(last.post_custom_prompt.custom_prompt.to_s).to include("We are done now")
|
||||
expect(last.post_custom_prompt.custom_prompt.to_s).not_to include("details")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ RSpec.describe DiscourseAi::AiBot::Commands::CategoriesCommand do
|
||||
it "can generate correct info" do
|
||||
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, nil).process
|
||||
expect(info).to include("america")
|
||||
expect(info).to include("999")
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ require_relative "../../../../support/openai_completions_inference_stubs"
|
||||
|
||||
RSpec.describe DiscourseAi::AiBot::Commands::Command do
|
||||
fab!(:bot_user) { User.find(DiscourseAi::AiBot::EntryPoint::GPT3_5_TURBO_ID) }
|
||||
let(:command) { DiscourseAi::AiBot::Commands::Command.new(bot_user, nil) }
|
||||
let(:command) { DiscourseAi::AiBot::Commands::Command.new(bot_user, nil, nil) }
|
||||
|
||||
describe "#format_results" do
|
||||
it "can generate efficient tables of data" do
|
||||
|
||||
@@ -32,8 +32,8 @@ RSpec.describe DiscourseAi::AiBot::Commands::GoogleCommand do
|
||||
"https://www.googleapis.com/customsearch/v1?cx=cx&key=abc&num=10&q=some%20search%20term",
|
||||
).to_return(status: 200, body: json_text, headers: {})
|
||||
|
||||
google = described_class.new(bot_user, post)
|
||||
info = google.process("some search term")
|
||||
google = described_class.new(bot_user, post, "some search term")
|
||||
info = google.process
|
||||
|
||||
expect(google.description_args[:count]).to eq(1)
|
||||
expect(info).to include("title1")
|
||||
|
||||
@@ -11,9 +11,9 @@ RSpec.describe DiscourseAi::AiBot::Commands::SearchCommand do
|
||||
describe "#process" do
|
||||
it "can handle no results" do
|
||||
post1 = Fabricate(:post)
|
||||
search = described_class.new(bot_user, post1)
|
||||
search = described_class.new(bot_user, post1, "order:fake ABDDCDCEDGDG")
|
||||
|
||||
results = search.process("order:fake ABDDCDCEDGDG")
|
||||
results = search.process
|
||||
expect(results).to eq("No results found")
|
||||
end
|
||||
|
||||
@@ -23,15 +23,15 @@ RSpec.describe DiscourseAi::AiBot::Commands::SearchCommand do
|
||||
_post3 = Fabricate(:post, user: post1.user)
|
||||
|
||||
# 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, "@#{post1.user.username} limit:2")
|
||||
|
||||
results = search.process("@#{post1.user.username} limit:2")
|
||||
results = search.process
|
||||
|
||||
# title + 2 rows
|
||||
expect(results.split("\n").length).to eq(3)
|
||||
|
||||
# just searching for everything
|
||||
results = search.process("order:latest_topic")
|
||||
search = described_class.new(bot_user, post1, "order:latest_topic")
|
||||
results = search.process
|
||||
expect(results.split("\n").length).to be > 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,8 +14,8 @@ RSpec.describe DiscourseAi::AiBot::Commands::SummarizeCommand do
|
||||
body: JSON.dump({ choices: [{ message: { content: "summary stuff" } }] }),
|
||||
)
|
||||
|
||||
summarizer = described_class.new(bot_user, post)
|
||||
info = summarizer.process("#{post.topic_id} why did it happen?")
|
||||
summarizer = described_class.new(bot_user, post, "#{post.topic_id} why did it happen?")
|
||||
info = summarizer.process
|
||||
|
||||
expect(info).to include("Topic summarized")
|
||||
expect(summarizer.custom_raw).to include("summary stuff")
|
||||
@@ -30,8 +30,8 @@ RSpec.describe DiscourseAi::AiBot::Commands::SummarizeCommand do
|
||||
topic = Fabricate(:topic, category_id: category.id)
|
||||
post = Fabricate(:post, topic: topic)
|
||||
|
||||
summarizer = described_class.new(bot_user, post)
|
||||
info = summarizer.process("#{post.topic_id} why did it happen?")
|
||||
summarizer = described_class.new(bot_user, post, "#{post.topic_id} why did it happen?")
|
||||
info = summarizer.process
|
||||
|
||||
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: "not_here", public_topic_count: 0)
|
||||
|
||||
info = DiscourseAi::AiBot::Commands::TagsCommand.new(nil, nil).process(nil)
|
||||
info = DiscourseAi::AiBot::Commands::TagsCommand.new(nil, nil, nil).process
|
||||
|
||||
expect(info).to include("america")
|
||||
expect(info).not_to include("not_here")
|
||||
|
||||
@@ -23,12 +23,24 @@ RSpec.describe Jobs::CreateAiReply do
|
||||
# time needs to be frozen so time in prompt does not drift
|
||||
freeze_time
|
||||
|
||||
OpenAiCompletionsInferenceStubs.stub_streamed_response(
|
||||
DiscourseAi::AiBot::OpenAiBot.new(bot_user).bot_prompt_with_topic_context(
|
||||
post,
|
||||
triage: true,
|
||||
),
|
||||
[{ content: "!noop" }],
|
||||
req_opts: {
|
||||
temperature: 0.1,
|
||||
max_tokens: 100,
|
||||
stream: true,
|
||||
},
|
||||
)
|
||||
|
||||
OpenAiCompletionsInferenceStubs.stub_streamed_response(
|
||||
DiscourseAi::AiBot::OpenAiBot.new(bot_user).bot_prompt_with_topic_context(post),
|
||||
deltas,
|
||||
req_opts: {
|
||||
temperature: 0.4,
|
||||
top_p: 0.9,
|
||||
max_tokens: 1500,
|
||||
stream: true,
|
||||
},
|
||||
@@ -66,12 +78,25 @@ RSpec.describe Jobs::CreateAiReply do
|
||||
end
|
||||
|
||||
context "when chatting with Claude from Anthropic" do
|
||||
let(:claude_response) { "Assistant: #{expected_response}" }
|
||||
let(:claude_response) { "#{expected_response}" }
|
||||
let(:deltas) { claude_response.split(" ").map { |w| "#{w} " } }
|
||||
|
||||
before do
|
||||
bot_user = User.find(DiscourseAi::AiBot::EntryPoint::CLAUDE_V1_ID)
|
||||
|
||||
AnthropicCompletionStubs.stub_streamed_response(
|
||||
DiscourseAi::AiBot::AnthropicBot.new(bot_user).bot_prompt_with_topic_context(
|
||||
post,
|
||||
triage: true,
|
||||
),
|
||||
[{ content: "!noop" }],
|
||||
req_opts: {
|
||||
temperature: 0.1,
|
||||
max_tokens_to_sample: 100,
|
||||
stream: true,
|
||||
},
|
||||
)
|
||||
|
||||
AnthropicCompletionStubs.stub_streamed_response(
|
||||
DiscourseAi::AiBot::AnthropicBot.new(bot_user).bot_prompt_with_topic_context(post),
|
||||
deltas,
|
||||
|
||||
@@ -33,7 +33,7 @@ RSpec.describe DiscourseAi::AiBot::OpenAiBot do
|
||||
it "trims the prompt" do
|
||||
prompt_messages = subject.bot_prompt_with_topic_context(post_1)
|
||||
|
||||
expect(prompt_messages[-2][:role]).to eq("assistant")
|
||||
expect(prompt_messages[-2][:role]).to eq("system")
|
||||
expect(prompt_messages[-1][:role]).to eq("user")
|
||||
# trimming is tricky... it needs to account for system message as
|
||||
# well... just make sure we trim for now
|
||||
|
||||
Reference in New Issue
Block a user