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
|
requires_plugin ::DiscourseAi::PLUGIN_NAME
|
||||||
|
|
||||||
def index
|
def index
|
||||||
llms = LlmModel.all
|
llms = LlmModel.all.order(:display_name)
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
ai_llms:
|
ai_llms:
|
||||||
|
|
|
@ -18,9 +18,7 @@ class LlmModel < ActiveRecord::Base
|
||||||
provider: "vllm",
|
provider: "vllm",
|
||||||
tokenizer: "DiscourseAi::Tokenizer::MixtralTokenizer",
|
tokenizer: "DiscourseAi::Tokenizer::MixtralTokenizer",
|
||||||
url: RESERVED_VLLM_SRV_URL,
|
url: RESERVED_VLLM_SRV_URL,
|
||||||
vllm_key: "",
|
max_prompt_tokens: 8000,
|
||||||
user_id: nil,
|
|
||||||
enabled_chat_bot: false,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
record.save(validate: false) # Ignore reserved URL validation
|
record.save(validate: false) # Ignore reserved URL validation
|
||||||
|
@ -55,7 +53,8 @@ class LlmModel < ActiveRecord::Base
|
||||||
new_user.save!(validate: false)
|
new_user.save!(validate: false)
|
||||||
self.update!(user: new_user)
|
self.update!(user: new_user)
|
||||||
else
|
else
|
||||||
user.update!(active: true)
|
user.active = true
|
||||||
|
user.save!(validate: false)
|
||||||
end
|
end
|
||||||
elsif user
|
elsif user
|
||||||
# will include deleted
|
# will include deleted
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import Component from "@glimmer/component";
|
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 { 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 icon from "discourse-common/helpers/d-icon";
|
||||||
import i18n from "discourse-common/helpers/i18n";
|
import i18n from "discourse-common/helpers/i18n";
|
||||||
import I18n from "discourse-i18n";
|
import I18n from "discourse-i18n";
|
||||||
|
@ -11,6 +15,21 @@ export default class AiLlmsListEditor extends Component {
|
||||||
return this.args.llms.length !== 0;
|
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>
|
<template>
|
||||||
<section class="ai-llms-list-editor admin-detail pull-left">
|
<section class="ai-llms-list-editor admin-detail pull-left">
|
||||||
{{#if @currentLlm}}
|
{{#if @currentLlm}}
|
||||||
|
@ -35,6 +54,7 @@ export default class AiLlmsListEditor extends Component {
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{i18n "discourse_ai.llms.display_name"}}</th>
|
<th>{{i18n "discourse_ai.llms.display_name"}}</th>
|
||||||
<th>{{i18n "discourse_ai.llms.provider"}}</th>
|
<th>{{i18n "discourse_ai.llms.provider"}}</th>
|
||||||
|
<th>{{i18n "discourse_ai.llms.enabled_chat_bot"}}</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -45,6 +65,12 @@ export default class AiLlmsListEditor extends Component {
|
||||||
<td>{{i18n
|
<td>{{i18n
|
||||||
(concat "discourse_ai.llms.providers." llm.provider)
|
(concat "discourse_ai.llms.providers." llm.provider)
|
||||||
}}</td>
|
}}</td>
|
||||||
|
<td>
|
||||||
|
<DToggleSwitch
|
||||||
|
@state={{llm.enabled_chat_bot}}
|
||||||
|
{{on "click" (fn this.toggleEnabledChatBot llm)}}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<LinkTo
|
<LinkTo
|
||||||
@route="adminPlugins.show.discourse-ai-llms.show"
|
@route="adminPlugins.show.discourse-ai-llms.show"
|
||||||
|
|
|
@ -210,7 +210,7 @@ en:
|
||||||
max_prompt_tokens: "Number of tokens for the prompt"
|
max_prompt_tokens: "Number of tokens for the prompt"
|
||||||
url: "URL of the service hosting the model"
|
url: "URL of the service hosting the model"
|
||||||
api_key: "API Key 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"
|
save: "Save"
|
||||||
edit: "Edit"
|
edit: "Edit"
|
||||||
saved: "LLM Model Saved"
|
saved: "LLM Model Saved"
|
||||||
|
|
|
@ -207,6 +207,10 @@ en:
|
||||||
summarizing: "Summarizing topic"
|
summarizing: "Summarizing topic"
|
||||||
searching: "Searching for: '%{query}'"
|
searching: "Searching for: '%{query}'"
|
||||||
tool_options:
|
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:
|
||||||
search_private:
|
search_private:
|
||||||
name: "Search Private"
|
name: "Search Private"
|
||||||
|
|
|
@ -40,7 +40,7 @@ class SeedOssModels < ActiveRecord::Migration[7.0]
|
||||||
reserved: srv_reserved_url,
|
reserved: srv_reserved_url,
|
||||||
).first
|
).first
|
||||||
|
|
||||||
if vllm_srv.present? && srv.record.nil?
|
if vllm_srv.present? && srv_record.nil?
|
||||||
url = "https://vllm.shadowed-by-srv.invalid"
|
url = "https://vllm.shadowed-by-srv.invalid"
|
||||||
name = "mistralai/Mixtral"
|
name = "mistralai/Mixtral"
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,10 @@ module DiscourseAi
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.accepted_options
|
||||||
|
[option(:read_private, type: :boolean)]
|
||||||
|
end
|
||||||
|
|
||||||
def self.name
|
def self.name
|
||||||
"read"
|
"read"
|
||||||
end
|
end
|
||||||
|
@ -44,20 +48,18 @@ module DiscourseAi
|
||||||
|
|
||||||
def invoke
|
def invoke
|
||||||
not_found = { topic_id: topic_id, description: "Topic not found" }
|
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 = ""
|
@title = ""
|
||||||
|
|
||||||
topic = Topic.find_by(id: topic_id.to_i)
|
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
|
@title = topic.title
|
||||||
|
|
||||||
posts =
|
posts =
|
||||||
Post
|
Post.secured(guardian).where(topic_id: topic_id).order(:post_number).limit(MAX_POSTS)
|
||||||
.secured(Guardian.new)
|
|
||||||
.where(topic_id: topic_id)
|
|
||||||
.order(:post_number)
|
|
||||||
.limit(MAX_POSTS)
|
|
||||||
|
|
||||||
post_number = 1
|
post_number = 1
|
||||||
post_number = post_numbers.first if post_numbers.present?
|
post_number = post_numbers.first if post_numbers.present?
|
||||||
|
|
|
@ -72,7 +72,7 @@ module DiscourseAi
|
||||||
if val
|
if val
|
||||||
case option.type
|
case option.type
|
||||||
when :boolean
|
when :boolean
|
||||||
val = val == "true"
|
val = (val.to_s == "true")
|
||||||
when :integer
|
when :integer
|
||||||
val = val.to_i
|
val = val.to_i
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,6 +30,52 @@ RSpec.describe DiscourseAi::AiBot::Tools::Read do
|
||||||
before { SiteSetting.ai_bot_enabled = true }
|
before { SiteSetting.ai_bot_enabled = true }
|
||||||
|
|
||||||
describe "#process" do
|
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
|
it "can read specific posts" do
|
||||||
tool =
|
tool =
|
||||||
described_class.new(
|
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.name).to eq("Test Persona")
|
||||||
expect(persona.description).to eq("I am a 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.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
|
end
|
||||||
|
|
||||||
it "will not allow deletion or editing of system personas" do
|
it "will not allow deletion or editing of system personas" do
|
||||||
|
|
Loading…
Reference in New Issue