FEATURE: allow access to private topics if tool permits (#673)
Previously read tool only had access to public topics, this allows access to all topics user has access to, if admin opts for the option Also - Fixes VLLM migration - Display which llms have bot enabled
This commit is contained in:
parent
3c45335936
commit
0d6d9a6ef5
|
@ -6,7 +6,7 @@ module DiscourseAi
|
|||
requires_plugin ::DiscourseAi::PLUGIN_NAME
|
||||
|
||||
def index
|
||||
llms = LlmModel.all
|
||||
llms = LlmModel.all.order(:display_name)
|
||||
|
||||
render json: {
|
||||
ai_llms:
|
||||
|
|
|
@ -18,9 +18,7 @@ class LlmModel < ActiveRecord::Base
|
|||
provider: "vllm",
|
||||
tokenizer: "DiscourseAi::Tokenizer::MixtralTokenizer",
|
||||
url: RESERVED_VLLM_SRV_URL,
|
||||
vllm_key: "",
|
||||
user_id: nil,
|
||||
enabled_chat_bot: false,
|
||||
max_prompt_tokens: 8000,
|
||||
)
|
||||
|
||||
record.save(validate: false) # Ignore reserved URL validation
|
||||
|
@ -55,7 +53,8 @@ class LlmModel < ActiveRecord::Base
|
|||
new_user.save!(validate: false)
|
||||
self.update!(user: new_user)
|
||||
else
|
||||
user.update!(active: true)
|
||||
user.active = true
|
||||
user.save!(validate: false)
|
||||
end
|
||||
elsif user
|
||||
# will include deleted
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { concat } from "@ember/helper";
|
||||
import { concat, fn } from "@ember/helper";
|
||||
import { on } from "@ember/modifier";
|
||||
import { action } from "@ember/object";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import DToggleSwitch from "discourse/components/d-toggle-switch";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import I18n from "discourse-i18n";
|
||||
|
@ -11,6 +15,21 @@ export default class AiLlmsListEditor extends Component {
|
|||
return this.args.llms.length !== 0;
|
||||
}
|
||||
|
||||
@action
|
||||
async toggleEnabledChatBot(llm) {
|
||||
const oldValue = llm.enabled_chat_bot;
|
||||
const newValue = !oldValue;
|
||||
try {
|
||||
llm.set("enabled_chat_bot", newValue);
|
||||
await llm.update({
|
||||
enabled_chat_bot: newValue,
|
||||
});
|
||||
} catch (err) {
|
||||
llm.set("enabled_chat_bot", oldValue);
|
||||
popupAjaxError(err);
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
<section class="ai-llms-list-editor admin-detail pull-left">
|
||||
{{#if @currentLlm}}
|
||||
|
@ -35,6 +54,7 @@ export default class AiLlmsListEditor extends Component {
|
|||
<tr>
|
||||
<th>{{i18n "discourse_ai.llms.display_name"}}</th>
|
||||
<th>{{i18n "discourse_ai.llms.provider"}}</th>
|
||||
<th>{{i18n "discourse_ai.llms.enabled_chat_bot"}}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -45,6 +65,12 @@ export default class AiLlmsListEditor extends Component {
|
|||
<td>{{i18n
|
||||
(concat "discourse_ai.llms.providers." llm.provider)
|
||||
}}</td>
|
||||
<td>
|
||||
<DToggleSwitch
|
||||
@state={{llm.enabled_chat_bot}}
|
||||
{{on "click" (fn this.toggleEnabledChatBot llm)}}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<LinkTo
|
||||
@route="adminPlugins.show.discourse-ai-llms.show"
|
||||
|
|
|
@ -210,7 +210,7 @@ en:
|
|||
max_prompt_tokens: "Number of tokens for the prompt"
|
||||
url: "URL of the service hosting the model"
|
||||
api_key: "API Key of the service hosting the model"
|
||||
enabled_chat_bot: "Allow Companion user to act as an AI Bot"
|
||||
enabled_chat_bot: "Allow AI Bot"
|
||||
save: "Save"
|
||||
edit: "Edit"
|
||||
saved: "LLM Model Saved"
|
||||
|
|
|
@ -207,6 +207,10 @@ en:
|
|||
summarizing: "Summarizing topic"
|
||||
searching: "Searching for: '%{query}'"
|
||||
tool_options:
|
||||
read:
|
||||
read_private:
|
||||
name: "Read Private"
|
||||
description: "Allow access to all topics user has access to (by default only public topics are included)"
|
||||
search:
|
||||
search_private:
|
||||
name: "Search Private"
|
||||
|
|
|
@ -40,7 +40,7 @@ class SeedOssModels < ActiveRecord::Migration[7.0]
|
|||
reserved: srv_reserved_url,
|
||||
).first
|
||||
|
||||
if vllm_srv.present? && srv.record.nil?
|
||||
if vllm_srv.present? && srv_record.nil?
|
||||
url = "https://vllm.shadowed-by-srv.invalid"
|
||||
name = "mistralai/Mixtral"
|
||||
|
||||
|
|
|
@ -28,6 +28,10 @@ module DiscourseAi
|
|||
}
|
||||
end
|
||||
|
||||
def self.accepted_options
|
||||
[option(:read_private, type: :boolean)]
|
||||
end
|
||||
|
||||
def self.name
|
||||
"read"
|
||||
end
|
||||
|
@ -44,20 +48,18 @@ module DiscourseAi
|
|||
|
||||
def invoke
|
||||
not_found = { topic_id: topic_id, description: "Topic not found" }
|
||||
guardian = Guardian.new(context[:user]) if options[:read_private] && context[:user]
|
||||
guardian ||= Guardian.new
|
||||
|
||||
@title = ""
|
||||
|
||||
topic = Topic.find_by(id: topic_id.to_i)
|
||||
return not_found if !topic || !Guardian.new.can_see?(topic)
|
||||
return not_found if !topic || !guardian.can_see?(topic)
|
||||
|
||||
@title = topic.title
|
||||
|
||||
posts =
|
||||
Post
|
||||
.secured(Guardian.new)
|
||||
.where(topic_id: topic_id)
|
||||
.order(:post_number)
|
||||
.limit(MAX_POSTS)
|
||||
Post.secured(guardian).where(topic_id: topic_id).order(:post_number).limit(MAX_POSTS)
|
||||
|
||||
post_number = 1
|
||||
post_number = post_numbers.first if post_numbers.present?
|
||||
|
|
|
@ -72,7 +72,7 @@ module DiscourseAi
|
|||
if val
|
||||
case option.type
|
||||
when :boolean
|
||||
val = val == "true"
|
||||
val = (val.to_s == "true")
|
||||
when :integer
|
||||
val = val.to_i
|
||||
end
|
||||
|
|
|
@ -30,6 +30,52 @@ RSpec.describe DiscourseAi::AiBot::Tools::Read do
|
|||
before { SiteSetting.ai_bot_enabled = true }
|
||||
|
||||
describe "#process" do
|
||||
it "can read private topics if allowed to" do
|
||||
category = topic_with_tags.category
|
||||
category.set_permissions(Group::AUTO_GROUPS[:staff] => :full)
|
||||
category.save!
|
||||
|
||||
tool =
|
||||
described_class.new(
|
||||
{ topic_id: topic_with_tags.id, post_numbers: [post1.post_number] },
|
||||
bot_user: bot_user,
|
||||
llm: llm,
|
||||
)
|
||||
results = tool.invoke
|
||||
|
||||
expect(results[:description]).to eq("Topic not found")
|
||||
|
||||
admin = Fabricate(:admin)
|
||||
|
||||
tool =
|
||||
described_class.new(
|
||||
{ topic_id: topic_with_tags.id, post_numbers: [post1.post_number] },
|
||||
bot_user: bot_user,
|
||||
llm: llm,
|
||||
persona_options: {
|
||||
"read_private" => true,
|
||||
},
|
||||
context: {
|
||||
user: admin,
|
||||
},
|
||||
)
|
||||
results = tool.invoke
|
||||
expect(results[:content]).to include("hello there")
|
||||
|
||||
tool =
|
||||
described_class.new(
|
||||
{ topic_id: topic_with_tags.id, post_numbers: [post1.post_number] },
|
||||
bot_user: bot_user,
|
||||
llm: llm,
|
||||
context: {
|
||||
user: admin,
|
||||
},
|
||||
)
|
||||
|
||||
results = tool.invoke
|
||||
expect(results[:description]).to eq("Topic not found")
|
||||
end
|
||||
|
||||
it "can read specific posts" do
|
||||
tool =
|
||||
described_class.new(
|
||||
|
|
|
@ -53,7 +53,7 @@ RSpec.describe "AI personas", type: :system, js: true do
|
|||
expect(persona.name).to eq("Test Persona")
|
||||
expect(persona.description).to eq("I am a test persona")
|
||||
expect(persona.system_prompt).to eq("You are a helpful bot")
|
||||
expect(persona.tools).to eq(["Read"])
|
||||
expect(persona.tools).to eq([["Read", { "read_private" => nil }]])
|
||||
end
|
||||
|
||||
it "will not allow deletion or editing of system personas" do
|
||||
|
|
Loading…
Reference in New Issue