Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
593e8dc2a5 |
@@ -1,6 +1,3 @@
|
|||||||
< 3.5.0.beta1-dev: bdef136080074a993e7c4f5ca562edc31a8ba756
|
|
||||||
< 3.4.0.beta4-dev: a53719ab8eb071459f215227421b3ea4987e5f87
|
|
||||||
< 3.4.0.beta4-dev: 20612fde52d3f740cad64823ef8aadb0748b567f
|
|
||||||
< 3.4.0.beta3-dev: decf1bb49d737ea15308400f22f89d1d1e71d13d
|
< 3.4.0.beta3-dev: decf1bb49d737ea15308400f22f89d1d1e71d13d
|
||||||
< 3.4.0.beta1-dev: 9d887ad4ace8e33c3fe7dbb39237e882c08b4f0b
|
< 3.4.0.beta1-dev: 9d887ad4ace8e33c3fe7dbb39237e882c08b4f0b
|
||||||
< 3.3.0.beta5-dev: 4d8090002f6dcd8e34d41033606bf131fa221475
|
< 3.3.0.beta5-dev: 4d8090002f6dcd8e34d41033606bf131fa221475
|
||||||
|
|||||||
@@ -2,6 +2,3 @@ node_modules
|
|||||||
/gems
|
/gems
|
||||||
/auto_generated
|
/auto_generated
|
||||||
.env
|
.env
|
||||||
evals/log
|
|
||||||
evals/cases
|
|
||||||
config/eval-llms.local.yml
|
|
||||||
|
|||||||
@@ -2,6 +2,3 @@ inherit_gem:
|
|||||||
rubocop-discourse: stree-compat.yml
|
rubocop-discourse: stree-compat.yml
|
||||||
RSpec/NamedSubject:
|
RSpec/NamedSubject:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Style/GlobalVars:
|
|
||||||
AllowedVariables: [$prometheus_client]
|
|
||||||
|
|||||||
+45
-59
@@ -1,93 +1,79 @@
|
|||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
activesupport (8.0.2)
|
activesupport (7.1.3.4)
|
||||||
base64
|
base64
|
||||||
benchmark (>= 0.3)
|
|
||||||
bigdecimal
|
bigdecimal
|
||||||
concurrent-ruby (~> 1.0, >= 1.3.1)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
connection_pool (>= 2.2.5)
|
connection_pool (>= 2.2.5)
|
||||||
drb
|
drb
|
||||||
i18n (>= 1.6, < 2)
|
i18n (>= 1.6, < 2)
|
||||||
logger (>= 1.4.2)
|
|
||||||
minitest (>= 5.1)
|
minitest (>= 5.1)
|
||||||
securerandom (>= 0.3)
|
mutex_m
|
||||||
tzinfo (~> 2.0, >= 2.0.5)
|
tzinfo (~> 2.0)
|
||||||
uri (>= 0.13.1)
|
|
||||||
ast (2.4.2)
|
ast (2.4.2)
|
||||||
base64 (0.2.0)
|
base64 (0.2.0)
|
||||||
benchmark (0.4.0)
|
bigdecimal (3.1.8)
|
||||||
bigdecimal (3.1.9)
|
concurrent-ruby (1.3.3)
|
||||||
concurrent-ruby (1.3.5)
|
connection_pool (2.4.1)
|
||||||
connection_pool (2.5.0)
|
|
||||||
drb (2.2.1)
|
drb (2.2.1)
|
||||||
i18n (1.14.7)
|
i18n (1.14.5)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
json (2.10.2)
|
json (2.7.2)
|
||||||
language_server-protocol (3.17.0.4)
|
language_server-protocol (3.17.0.3)
|
||||||
lint_roller (1.1.0)
|
minitest (5.24.1)
|
||||||
logger (1.6.6)
|
mutex_m (0.2.0)
|
||||||
minitest (5.25.5)
|
parallel (1.25.1)
|
||||||
parallel (1.26.3)
|
parser (3.3.3.0)
|
||||||
parser (3.3.7.1)
|
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
racc
|
racc
|
||||||
prettier_print (1.2.1)
|
prettier_print (1.2.1)
|
||||||
racc (1.8.1)
|
racc (1.8.0)
|
||||||
rack (3.1.12)
|
rack (3.1.6)
|
||||||
rainbow (3.1.1)
|
rainbow (3.1.1)
|
||||||
regexp_parser (2.10.0)
|
regexp_parser (2.9.2)
|
||||||
rubocop (1.74.0)
|
rexml (3.3.9)
|
||||||
|
rubocop (1.64.1)
|
||||||
json (~> 2.3)
|
json (~> 2.3)
|
||||||
language_server-protocol (~> 3.17.0.2)
|
language_server-protocol (>= 3.17.0)
|
||||||
lint_roller (~> 1.1.0)
|
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
parser (>= 3.3.0.2)
|
parser (>= 3.3.0.2)
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
regexp_parser (>= 2.9.3, < 3.0)
|
regexp_parser (>= 1.8, < 3.0)
|
||||||
rubocop-ast (>= 1.38.0, < 2.0)
|
rexml (>= 3.2.5, < 4.0)
|
||||||
|
rubocop-ast (>= 1.31.1, < 2.0)
|
||||||
ruby-progressbar (~> 1.7)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (>= 2.4.0, < 4.0)
|
unicode-display_width (>= 2.4.0, < 3.0)
|
||||||
rubocop-ast (1.38.1)
|
rubocop-ast (1.31.3)
|
||||||
parser (>= 3.3.1.0)
|
parser (>= 3.3.1.0)
|
||||||
rubocop-capybara (2.22.1)
|
rubocop-capybara (2.21.0)
|
||||||
lint_roller (~> 1.1)
|
rubocop (~> 1.41)
|
||||||
rubocop (~> 1.72, >= 1.72.1)
|
rubocop-discourse (3.8.1)
|
||||||
rubocop-discourse (3.12.1)
|
|
||||||
activesupport (>= 6.1)
|
activesupport (>= 6.1)
|
||||||
lint_roller (>= 1.1.0)
|
rubocop (>= 1.59.0)
|
||||||
rubocop (>= 1.73.2)
|
rubocop-capybara (>= 2.0.0)
|
||||||
rubocop-capybara (>= 2.22.0)
|
rubocop-factory_bot (>= 2.0.0)
|
||||||
rubocop-factory_bot (>= 2.27.0)
|
rubocop-rails (>= 2.25.0)
|
||||||
rubocop-rails (>= 2.30.3)
|
|
||||||
rubocop-rspec (>= 3.0.1)
|
rubocop-rspec (>= 3.0.1)
|
||||||
rubocop-rspec_rails (>= 2.31.0)
|
rubocop-rspec_rails (>= 2.30.0)
|
||||||
rubocop-factory_bot (2.27.1)
|
rubocop-factory_bot (2.26.1)
|
||||||
lint_roller (~> 1.1)
|
rubocop (~> 1.61)
|
||||||
rubocop (~> 1.72, >= 1.72.1)
|
rubocop-rails (2.25.1)
|
||||||
rubocop-rails (2.30.3)
|
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
lint_roller (~> 1.1)
|
|
||||||
rack (>= 1.1)
|
rack (>= 1.1)
|
||||||
rubocop (>= 1.72.1, < 2.0)
|
rubocop (>= 1.33.0, < 2.0)
|
||||||
rubocop-ast (>= 1.38.0, < 2.0)
|
rubocop-ast (>= 1.31.1, < 2.0)
|
||||||
rubocop-rspec (3.5.0)
|
rubocop-rspec (3.0.2)
|
||||||
lint_roller (~> 1.1)
|
rubocop (~> 1.61)
|
||||||
rubocop (~> 1.72, >= 1.72.1)
|
rubocop-rspec_rails (2.30.0)
|
||||||
rubocop-rspec_rails (2.31.0)
|
rubocop (~> 1.61)
|
||||||
lint_roller (~> 1.1)
|
rubocop-rspec (~> 3, >= 3.0.1)
|
||||||
rubocop (~> 1.72, >= 1.72.1)
|
|
||||||
rubocop-rspec (~> 3.5)
|
|
||||||
ruby-progressbar (1.13.0)
|
ruby-progressbar (1.13.0)
|
||||||
securerandom (0.4.1)
|
|
||||||
syntax_tree (6.2.0)
|
syntax_tree (6.2.0)
|
||||||
prettier_print (>= 1.2.0)
|
prettier_print (>= 1.2.0)
|
||||||
tzinfo (2.0.6)
|
tzinfo (2.0.6)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
unicode-display_width (3.1.4)
|
unicode-display_width (2.5.0)
|
||||||
unicode-emoji (~> 4.0, >= 4.0.4)
|
|
||||||
unicode-emoji (4.0.4)
|
|
||||||
uri (1.0.3)
|
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
@@ -97,4 +83,4 @@ DEPENDENCIES
|
|||||||
syntax_tree
|
syntax_tree
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.6.6
|
2.5.10
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) Civilized Discourse Construction Kit, Inc.
|
Copyright (c) 2021 Civilized Discourse Construction Kit
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -3,27 +3,3 @@
|
|||||||
**Plugin Summary**
|
**Plugin Summary**
|
||||||
|
|
||||||
For more information, please see: https://meta.discourse.org/t/discourse-ai/259214?u=falco
|
For more information, please see: https://meta.discourse.org/t/discourse-ai/259214?u=falco
|
||||||
|
|
||||||
### Evals
|
|
||||||
|
|
||||||
The directory `evals` contains AI evals for the Discourse AI plugin.
|
|
||||||
You may create a local config by copying `config/eval-llms.yml` to `config/eval-llms.local.yml` and modifying the values.
|
|
||||||
|
|
||||||
To run them use:
|
|
||||||
|
|
||||||
cd evals
|
|
||||||
./run --help
|
|
||||||
|
|
||||||
```
|
|
||||||
Usage: evals/run [options]
|
|
||||||
-e, --eval NAME Name of the evaluation to run
|
|
||||||
--list-models List models
|
|
||||||
-m, --model NAME Model to evaluate (will eval all models if not specified)
|
|
||||||
-l, --list List evals
|
|
||||||
```
|
|
||||||
|
|
||||||
To run evals you will need to configure API keys in your environment:
|
|
||||||
|
|
||||||
OPENAI_API_KEY=your_openai_api_key
|
|
||||||
ANTHROPIC_API_KEY=your_anthropic_api_key
|
|
||||||
GEMINI_API_KEY=your_gemini_api_key
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"tests": {
|
|
||||||
"requiredPlugins": [
|
|
||||||
"https://github.com/discourse/discourse-prometheus"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
import DiscourseRoute from "discourse/routes/discourse";
|
|
||||||
|
|
||||||
export default class AdminPluginsShowDiscourseAiEmbeddingsEdit extends DiscourseRoute {
|
|
||||||
async model(params) {
|
|
||||||
const allEmbeddings = this.modelFor(
|
|
||||||
"adminPlugins.show.discourse-ai-embeddings"
|
|
||||||
);
|
|
||||||
const id = parseInt(params.id, 10);
|
|
||||||
const record = allEmbeddings.findBy("id", id);
|
|
||||||
record.provider_params = record.provider_params || {};
|
|
||||||
return record;
|
|
||||||
}
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
super.setupController(controller, model);
|
|
||||||
controller.set(
|
|
||||||
"allEmbeddings",
|
|
||||||
this.modelFor("adminPlugins.show.discourse-ai-embeddings")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-17
@@ -1,17 +0,0 @@
|
|||||||
import DiscourseRoute from "discourse/routes/discourse";
|
|
||||||
|
|
||||||
export default class AdminPluginsShowDiscourseAiEmbeddingsNew extends DiscourseRoute {
|
|
||||||
async model() {
|
|
||||||
const record = this.store.createRecord("ai-embedding");
|
|
||||||
record.provider_params = {};
|
|
||||||
return record;
|
|
||||||
}
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
super.setupController(controller, model);
|
|
||||||
controller.set(
|
|
||||||
"allEmbeddings",
|
|
||||||
this.modelFor("adminPlugins.show.discourse-ai-embeddings")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-7
@@ -1,7 +0,0 @@
|
|||||||
import DiscourseRoute from "discourse/routes/discourse";
|
|
||||||
|
|
||||||
export default class DiscourseAiAiEmbeddingsRoute extends DiscourseRoute {
|
|
||||||
model() {
|
|
||||||
return this.store.findAll("ai-embedding");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
|
||||||
export default class AdminPluginsShowDiscourseAiLlmsEdit extends DiscourseRoute {
|
export default class AdminPluginsShowDiscourseAiLlmsShow extends DiscourseRoute {
|
||||||
async model(params) {
|
async model(params) {
|
||||||
const allLlms = this.modelFor("adminPlugins.show.discourse-ai-llms");
|
const allLlms = this.modelFor("adminPlugins.show.discourse-ai-llms");
|
||||||
const id = parseInt(params.id, 10);
|
const id = parseInt(params.id, 10);
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
|
||||||
export default class AdminPluginsShowDiscourseAiPersonasEdit extends DiscourseRoute {
|
export default class AdminPluginsShowDiscourseAiPersonasShow extends DiscourseRoute {
|
||||||
async model(params) {
|
async model(params) {
|
||||||
const allPersonas = this.modelFor(
|
const allPersonas = this.modelFor(
|
||||||
"adminPlugins.show.discourse-ai-personas"
|
"adminPlugins.show.discourse-ai-personas"
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { service } from "@ember/service";
|
|
||||||
import { ajax } from "discourse/lib/ajax";
|
|
||||||
import DiscourseRoute from "discourse/routes/discourse";
|
|
||||||
|
|
||||||
export default class DiscourseAiSpamRoute extends DiscourseRoute {
|
|
||||||
@service store;
|
|
||||||
|
|
||||||
model() {
|
|
||||||
return ajax("/admin/plugins/discourse-ai/ai-spam.json");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-7
@@ -1,10 +1,6 @@
|
|||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
|
||||||
export default class DiscourseAiToolsNewRoute extends DiscourseRoute {
|
export default class DiscourseAiToolsNewRoute extends DiscourseRoute {
|
||||||
beforeModel(transition) {
|
|
||||||
this.preset = transition.to.queryParams.presetId || "empty_tool";
|
|
||||||
}
|
|
||||||
|
|
||||||
async model() {
|
async model() {
|
||||||
return this.store.createRecord("ai-tool");
|
return this.store.createRecord("ai-tool");
|
||||||
}
|
}
|
||||||
@@ -12,10 +8,8 @@ export default class DiscourseAiToolsNewRoute extends DiscourseRoute {
|
|||||||
setupController(controller) {
|
setupController(controller) {
|
||||||
super.setupController(...arguments);
|
super.setupController(...arguments);
|
||||||
const toolsModel = this.modelFor("adminPlugins.show.discourse-ai-tools");
|
const toolsModel = this.modelFor("adminPlugins.show.discourse-ai-tools");
|
||||||
|
|
||||||
controller.set("allTools", toolsModel);
|
controller.set("allTools", toolsModel);
|
||||||
controller.set("presets", toolsModel.resultSetMeta.presets);
|
controller.set("presets", toolsModel.resultSetMeta.presets);
|
||||||
controller.set("llms", toolsModel.resultSetMeta.llms);
|
|
||||||
controller.set("settings", toolsModel.resultSetMeta.settings);
|
|
||||||
controller.set("selectedPreset", this.preset);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -1,6 +1,6 @@
|
|||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
|
||||||
export default class DiscourseAiToolsEditRoute extends DiscourseRoute {
|
export default class DiscourseAiToolsShowRoute extends DiscourseRoute {
|
||||||
async model(params) {
|
async model(params) {
|
||||||
const allTools = this.modelFor("adminPlugins.show.discourse-ai-tools");
|
const allTools = this.modelFor("adminPlugins.show.discourse-ai-tools");
|
||||||
const id = parseInt(params.id, 10);
|
const id = parseInt(params.id, 10);
|
||||||
@@ -14,7 +14,5 @@ export default class DiscourseAiToolsEditRoute extends DiscourseRoute {
|
|||||||
|
|
||||||
controller.set("allTools", toolsModel);
|
controller.set("allTools", toolsModel);
|
||||||
controller.set("presets", toolsModel.resultSetMeta.presets);
|
controller.set("presets", toolsModel.resultSetMeta.presets);
|
||||||
controller.set("llms", toolsModel.resultSetMeta.llms);
|
|
||||||
controller.set("settings", toolsModel.resultSetMeta.settings);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
-4
@@ -1,4 +0,0 @@
|
|||||||
<AiEmbeddingsListEditor
|
|
||||||
@embeddings={{this.allEmbeddings}}
|
|
||||||
@currentEmbedding={{this.model}}
|
|
||||||
/>
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
<AiEmbeddingsListEditor @embeddings={{this.model}} />
|
|
||||||
-4
@@ -1,4 +0,0 @@
|
|||||||
<AiEmbeddingsListEditor
|
|
||||||
@embeddings={{this.allEmbeddings}}
|
|
||||||
@currentEmbedding={{this.model}}
|
|
||||||
/>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<AiSpam @model={{this.model}} />
|
|
||||||
-3
@@ -3,8 +3,5 @@
|
|||||||
@tools={{this.allTools}}
|
@tools={{this.allTools}}
|
||||||
@model={{this.model}}
|
@model={{this.model}}
|
||||||
@presets={{this.presets}}
|
@presets={{this.presets}}
|
||||||
@llms={{this.llms}}
|
|
||||||
@settings={{this.settings}}
|
|
||||||
@selectedPreset={{this.selectedPreset}}
|
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
-2
@@ -3,7 +3,5 @@
|
|||||||
@tools={{this.allTools}}
|
@tools={{this.allTools}}
|
||||||
@model={{this.model}}
|
@model={{this.model}}
|
||||||
@presets={{this.presets}}
|
@presets={{this.presets}}
|
||||||
@llms={{this.llms}}
|
|
||||||
@settings={{this.settings}}
|
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module DiscourseAi
|
|
||||||
module Admin
|
|
||||||
class AiEmbeddingsController < ::Admin::AdminController
|
|
||||||
requires_plugin ::DiscourseAi::PLUGIN_NAME
|
|
||||||
|
|
||||||
def index
|
|
||||||
embedding_defs = EmbeddingDefinition.all.order(:display_name)
|
|
||||||
|
|
||||||
render json: {
|
|
||||||
ai_embeddings:
|
|
||||||
ActiveModel::ArraySerializer.new(
|
|
||||||
embedding_defs,
|
|
||||||
each_serializer: AiEmbeddingDefinitionSerializer,
|
|
||||||
root: false,
|
|
||||||
).as_json,
|
|
||||||
meta: {
|
|
||||||
provider_params: EmbeddingDefinition.provider_params,
|
|
||||||
providers: EmbeddingDefinition.provider_names,
|
|
||||||
distance_functions: EmbeddingDefinition.distance_functions,
|
|
||||||
tokenizers:
|
|
||||||
EmbeddingDefinition.tokenizer_names.map { |tn|
|
|
||||||
{ id: tn, name: tn.split("::").last }
|
|
||||||
},
|
|
||||||
presets: EmbeddingDefinition.presets,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def new
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
embedding_def = EmbeddingDefinition.find(params[:id])
|
|
||||||
render json: AiEmbeddingDefinitionSerializer.new(embedding_def)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
embedding_def = EmbeddingDefinition.new(ai_embeddings_params)
|
|
||||||
|
|
||||||
if embedding_def.save
|
|
||||||
render json: AiEmbeddingDefinitionSerializer.new(embedding_def), status: :created
|
|
||||||
else
|
|
||||||
render_json_error embedding_def
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
embedding_def = EmbeddingDefinition.find(params[:id])
|
|
||||||
|
|
||||||
if embedding_def.seeded?
|
|
||||||
return(
|
|
||||||
render_json_error(I18n.t("discourse_ai.embeddings.cannot_edit_builtin"), status: 403)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
if embedding_def.update(ai_embeddings_params.except(:dimensions))
|
|
||||||
render json: AiEmbeddingDefinitionSerializer.new(embedding_def)
|
|
||||||
else
|
|
||||||
render_json_error embedding_def
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
embedding_def = EmbeddingDefinition.find(params[:id])
|
|
||||||
|
|
||||||
if embedding_def.seeded?
|
|
||||||
return(
|
|
||||||
render_json_error(I18n.t("discourse_ai.embeddings.cannot_edit_builtin"), status: 403)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
if embedding_def.id == SiteSetting.ai_embeddings_selected_model.to_i
|
|
||||||
return render_json_error(I18n.t("discourse_ai.embeddings.delete_failed"), status: 409)
|
|
||||||
end
|
|
||||||
|
|
||||||
if embedding_def.destroy
|
|
||||||
head :no_content
|
|
||||||
else
|
|
||||||
render_json_error embedding_def
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test
|
|
||||||
RateLimiter.new(
|
|
||||||
current_user,
|
|
||||||
"ai_embeddings_test_#{current_user.id}",
|
|
||||||
3,
|
|
||||||
1.minute,
|
|
||||||
).performed!
|
|
||||||
|
|
||||||
embedding_def = EmbeddingDefinition.new(ai_embeddings_params)
|
|
||||||
DiscourseAi::Embeddings::Vector.new(embedding_def).vector_from("this is a test")
|
|
||||||
|
|
||||||
render json: { success: true }
|
|
||||||
rescue Net::HTTPBadResponse => e
|
|
||||||
render json: { success: false, error: e.message }
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def ai_embeddings_params
|
|
||||||
permitted =
|
|
||||||
params.require(:ai_embedding).permit(
|
|
||||||
:display_name,
|
|
||||||
:dimensions,
|
|
||||||
:max_sequence_length,
|
|
||||||
:pg_function,
|
|
||||||
:provider,
|
|
||||||
:url,
|
|
||||||
:api_key,
|
|
||||||
:tokenizer_class,
|
|
||||||
:embed_prompt,
|
|
||||||
:search_prompt,
|
|
||||||
:matryoshka_dimensions,
|
|
||||||
)
|
|
||||||
|
|
||||||
extra_field_names = EmbeddingDefinition.provider_params.dig(permitted[:provider]&.to_sym)
|
|
||||||
if extra_field_names.present?
|
|
||||||
received_prov_params =
|
|
||||||
params.dig(:ai_embedding, :provider_params)&.slice(*extra_field_names.keys)
|
|
||||||
|
|
||||||
if received_prov_params.present?
|
|
||||||
permitted[:provider_params] = received_prov_params.permit!
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
permitted
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module DiscourseAi
|
|
||||||
module Admin
|
|
||||||
class AiLlmQuotasController < ::Admin::AdminController
|
|
||||||
requires_plugin ::DiscourseAi::PLUGIN_NAME
|
|
||||||
|
|
||||||
def index
|
|
||||||
quotas = LlmQuota.includes(:group)
|
|
||||||
|
|
||||||
render json: {
|
|
||||||
quotas:
|
|
||||||
ActiveModel::ArraySerializer.new(quotas, each_serializer: LlmQuotaSerializer),
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
quota = LlmQuota.new(quota_params)
|
|
||||||
|
|
||||||
if quota.save
|
|
||||||
render json: LlmQuotaSerializer.new(quota), status: :created
|
|
||||||
else
|
|
||||||
render_json_error quota
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
quota = LlmQuota.find(params[:id])
|
|
||||||
|
|
||||||
if quota.update(quota_params)
|
|
||||||
render json: LlmQuotaSerializer.new(quota)
|
|
||||||
else
|
|
||||||
render_json_error quota
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
quota = LlmQuota.find(params[:id])
|
|
||||||
quota.destroy!
|
|
||||||
|
|
||||||
head :no_content
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
|
||||||
render json: { error: I18n.t("not_found") }, status: 404
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def quota_params
|
|
||||||
params.require(:quota).permit(
|
|
||||||
:group_id,
|
|
||||||
:llm_model_id,
|
|
||||||
:max_tokens,
|
|
||||||
:max_usages,
|
|
||||||
:duration_seconds,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -6,7 +6,7 @@ module DiscourseAi
|
|||||||
requires_plugin ::DiscourseAi::PLUGIN_NAME
|
requires_plugin ::DiscourseAi::PLUGIN_NAME
|
||||||
|
|
||||||
def index
|
def index
|
||||||
llms = LlmModel.all.includes(:llm_quotas).order(:display_name)
|
llms = LlmModel.all.order(:display_name)
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
ai_llms:
|
ai_llms:
|
||||||
@@ -30,21 +30,13 @@ module DiscourseAi
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def show
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
llm_model = LlmModel.find(params[:id])
|
llm_model = LlmModel.find(params[:id])
|
||||||
render json: LlmModelSerializer.new(llm_model)
|
render json: LlmModelSerializer.new(llm_model)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
llm_model = LlmModel.new(ai_llm_params)
|
llm_model = LlmModel.new(ai_llm_params)
|
||||||
|
|
||||||
# we could do nested attributes but the mechanics are not ideal leading
|
|
||||||
# to lots of complex debugging, this is simpler
|
|
||||||
quota_params.each { |quota| llm_model.llm_quotas.build(quota) } if quota_params
|
|
||||||
|
|
||||||
if llm_model.save
|
if llm_model.save
|
||||||
llm_model.toggle_companion_user
|
llm_model.toggle_companion_user
|
||||||
render json: LlmModelSerializer.new(llm_model), status: :created
|
render json: LlmModelSerializer.new(llm_model), status: :created
|
||||||
@@ -56,25 +48,6 @@ module DiscourseAi
|
|||||||
def update
|
def update
|
||||||
llm_model = LlmModel.find(params[:id])
|
llm_model = LlmModel.find(params[:id])
|
||||||
|
|
||||||
if params[:ai_llm].key?(:llm_quotas)
|
|
||||||
if quota_params
|
|
||||||
existing_quota_group_ids = llm_model.llm_quotas.pluck(:group_id)
|
|
||||||
new_quota_group_ids = quota_params.map { |q| q[:group_id] }
|
|
||||||
|
|
||||||
llm_model
|
|
||||||
.llm_quotas
|
|
||||||
.where(group_id: existing_quota_group_ids - new_quota_group_ids)
|
|
||||||
.destroy_all
|
|
||||||
|
|
||||||
quota_params.each do |quota_param|
|
|
||||||
quota = llm_model.llm_quotas.find_or_initialize_by(group_id: quota_param[:group_id])
|
|
||||||
quota.update!(quota_param)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
llm_model.llm_quotas.destroy_all
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if llm_model.seeded?
|
if llm_model.seeded?
|
||||||
return render_json_error(I18n.t("discourse_ai.llm.cannot_edit_builtin"), status: 403)
|
return render_json_error(I18n.t("discourse_ai.llm.cannot_edit_builtin"), status: 403)
|
||||||
end
|
end
|
||||||
@@ -134,19 +107,6 @@ module DiscourseAi
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def quota_params
|
|
||||||
if params[:ai_llm][:llm_quotas].present?
|
|
||||||
params[:ai_llm][:llm_quotas].map do |quota|
|
|
||||||
mapped = {}
|
|
||||||
mapped[:group_id] = quota[:group_id].to_i
|
|
||||||
mapped[:max_tokens] = quota[:max_tokens].to_i if quota[:max_tokens].present?
|
|
||||||
mapped[:max_usages] = quota[:max_usages].to_i if quota[:max_usages].present?
|
|
||||||
mapped[:duration_seconds] = quota[:duration_seconds].to_i
|
|
||||||
mapped
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def ai_llm_params(updating: nil)
|
def ai_llm_params(updating: nil)
|
||||||
return {} if params[:ai_llm].blank?
|
return {} if params[:ai_llm].blank?
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ module DiscourseAi
|
|||||||
class AiPersonasController < ::Admin::AdminController
|
class AiPersonasController < ::Admin::AdminController
|
||||||
requires_plugin ::DiscourseAi::PLUGIN_NAME
|
requires_plugin ::DiscourseAi::PLUGIN_NAME
|
||||||
|
|
||||||
before_action :find_ai_persona, only: %i[edit update destroy create_user]
|
before_action :find_ai_persona, only: %i[show update destroy create_user]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
ai_personas =
|
ai_personas =
|
||||||
@@ -23,34 +23,17 @@ module DiscourseAi
|
|||||||
.each do |tool|
|
.each do |tool|
|
||||||
tools << {
|
tools << {
|
||||||
id: "custom-#{tool.id}",
|
id: "custom-#{tool.id}",
|
||||||
name:
|
name: I18n.t("discourse_ai.tools.custom_name", name: tool.name.capitalize),
|
||||||
I18n.t(
|
|
||||||
"discourse_ai.tools.custom_name",
|
|
||||||
name: tool.name.capitalize,
|
|
||||||
tool_name: tool.tool_name,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
llms =
|
llms =
|
||||||
DiscourseAi::Configuration::LlmEnumerator.values_for_serialization(
|
DiscourseAi::Configuration::LlmEnumerator.values.map do |hash|
|
||||||
allowed_seeded_llm_ids: SiteSetting.ai_bot_allowed_seeded_models_map,
|
{ id: hash[:value], name: hash[:name] }
|
||||||
)
|
end
|
||||||
render json: {
|
render json: { ai_personas: ai_personas, meta: { tools: tools, llms: llms } }
|
||||||
ai_personas: ai_personas,
|
|
||||||
meta: {
|
|
||||||
tools: tools,
|
|
||||||
llms: llms,
|
|
||||||
settings: {
|
|
||||||
rag_images_enabled: SiteSetting.ai_rag_images_enabled,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def show
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
render json: LocalizedAiPersonaSerializer.new(@ai_persona)
|
render json: LocalizedAiPersonaSerializer.new(@ai_persona)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -196,7 +179,7 @@ module DiscourseAi
|
|||||||
:priority,
|
:priority,
|
||||||
:top_p,
|
:top_p,
|
||||||
:temperature,
|
:temperature,
|
||||||
:default_llm_id,
|
:default_llm,
|
||||||
:user_id,
|
:user_id,
|
||||||
:max_context_posts,
|
:max_context_posts,
|
||||||
:vision_enabled,
|
:vision_enabled,
|
||||||
@@ -204,8 +187,7 @@ module DiscourseAi
|
|||||||
:rag_chunk_tokens,
|
:rag_chunk_tokens,
|
||||||
:rag_chunk_overlap_tokens,
|
:rag_chunk_overlap_tokens,
|
||||||
:rag_conversation_chunks,
|
:rag_conversation_chunks,
|
||||||
:rag_llm_model_id,
|
:question_consolidator_llm,
|
||||||
:question_consolidator_llm_id,
|
|
||||||
:allow_chat_channel_mentions,
|
:allow_chat_channel_mentions,
|
||||||
:allow_chat_direct_messages,
|
:allow_chat_direct_messages,
|
||||||
:allow_topic_mentions,
|
:allow_topic_mentions,
|
||||||
|
|||||||
@@ -1,137 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module DiscourseAi
|
|
||||||
module Admin
|
|
||||||
class AiSpamController < ::Admin::AdminController
|
|
||||||
requires_plugin "discourse-ai"
|
|
||||||
|
|
||||||
def show
|
|
||||||
render json: AiSpamSerializer.new(spam_config, root: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
updated_params = {}
|
|
||||||
if allowed_params.key?(:llm_model_id)
|
|
||||||
llm_model_id = updated_params[:llm_model_id] = allowed_params[:llm_model_id]
|
|
||||||
if llm_model_id.to_i < 0 &&
|
|
||||||
!SiteSetting.ai_spam_detection_model_allowed_seeded_models_map.include?(
|
|
||||||
llm_model_id.to_s,
|
|
||||||
)
|
|
||||||
return(
|
|
||||||
render_json_error(
|
|
||||||
I18n.t("discourse_ai.llm.configuration.invalid_seeded_model"),
|
|
||||||
status: 422,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
updated_params[:data] = {
|
|
||||||
custom_instructions: allowed_params[:custom_instructions],
|
|
||||||
} if allowed_params.key?(:custom_instructions)
|
|
||||||
|
|
||||||
if updated_params.present?
|
|
||||||
# not using upsert cause we will not get the correct validation errors
|
|
||||||
if AiModerationSetting.spam
|
|
||||||
AiModerationSetting.spam.update!(updated_params)
|
|
||||||
else
|
|
||||||
AiModerationSetting.create!(updated_params.merge(setting_type: :spam))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
is_enabled = ActiveModel::Type::Boolean.new.cast(allowed_params[:is_enabled])
|
|
||||||
|
|
||||||
if allowed_params.key?(:is_enabled)
|
|
||||||
if is_enabled && !AiModerationSetting.spam&.llm_model_id
|
|
||||||
return(
|
|
||||||
render_json_error(
|
|
||||||
I18n.t("discourse_ai.llm.configuration.must_select_model"),
|
|
||||||
status: 422,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
SiteSetting.ai_spam_detection_enabled = is_enabled
|
|
||||||
end
|
|
||||||
|
|
||||||
render json: AiSpamSerializer.new(spam_config, root: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test
|
|
||||||
url = params[:post_url].to_s
|
|
||||||
post = nil
|
|
||||||
|
|
||||||
if url.match?(/^\d+$/)
|
|
||||||
post_id = url.to_i
|
|
||||||
post = Post.find_by(id: post_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
route = UrlHelper.rails_route_from_url(url) if !post
|
|
||||||
|
|
||||||
if route
|
|
||||||
if route[:controller] == "topics"
|
|
||||||
post_number = route[:post_number] || 1
|
|
||||||
post = Post.with_deleted.find_by(post_number: post_number, topic_id: route[:topic_id])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
raise Discourse::NotFound if !post
|
|
||||||
|
|
||||||
result =
|
|
||||||
DiscourseAi::AiModeration::SpamScanner.test_post(
|
|
||||||
post,
|
|
||||||
custom_instructions: params[:custom_instructions],
|
|
||||||
llm_id: params[:llm_id],
|
|
||||||
)
|
|
||||||
|
|
||||||
render json: result
|
|
||||||
end
|
|
||||||
|
|
||||||
def fix_errors
|
|
||||||
case params[:error]
|
|
||||||
when "spam_scanner_not_admin"
|
|
||||||
begin
|
|
||||||
DiscourseAi::AiModeration::SpamScanner.fix_spam_scanner_not_admin
|
|
||||||
render json: success_json
|
|
||||||
rescue ActiveRecord::RecordInvalid
|
|
||||||
render_json_error(
|
|
||||||
I18n.t("discourse_ai.spam_detection.bot_user_update_failed"),
|
|
||||||
status: :unprocessable_entity,
|
|
||||||
)
|
|
||||||
rescue StandardError
|
|
||||||
render_json_error(
|
|
||||||
I18n.t("discourse_ai.spam_detection.unexpected"),
|
|
||||||
status: :internal_server_error,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
render_json_error(
|
|
||||||
I18n.t("discourse_ai.spam_detection.invalid_error_type"),
|
|
||||||
status: :bad_request,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def allowed_params
|
|
||||||
params.permit(:is_enabled, :llm_model_id, :custom_instructions)
|
|
||||||
end
|
|
||||||
|
|
||||||
def spam_config
|
|
||||||
spam_config = {
|
|
||||||
enabled: SiteSetting.ai_spam_detection_enabled,
|
|
||||||
settings: AiModerationSetting.spam,
|
|
||||||
}
|
|
||||||
|
|
||||||
spam_config[:stats] = DiscourseAi::AiModeration::SpamReport.generate(min_date: 1.week.ago)
|
|
||||||
|
|
||||||
if spam_config[:stats].scanned_count > 0
|
|
||||||
spam_config[
|
|
||||||
:flagging_username
|
|
||||||
] = DiscourseAi::AiModeration::SpamScanner.flagging_user&.username
|
|
||||||
end
|
|
||||||
spam_config
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -5,17 +5,14 @@ module DiscourseAi
|
|||||||
class AiToolsController < ::Admin::AdminController
|
class AiToolsController < ::Admin::AdminController
|
||||||
requires_plugin ::DiscourseAi::PLUGIN_NAME
|
requires_plugin ::DiscourseAi::PLUGIN_NAME
|
||||||
|
|
||||||
before_action :find_ai_tool, only: %i[test edit update destroy]
|
before_action :find_ai_tool, only: %i[test show update destroy]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
ai_tools = AiTool.all
|
ai_tools = AiTool.all
|
||||||
render_serialized({ ai_tools: ai_tools }, AiCustomToolListSerializer, root: false)
|
render_serialized({ ai_tools: ai_tools }, AiCustomToolListSerializer, root: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
def new
|
def show
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
render_serialized(@ai_tool, AiCustomToolSerializer)
|
render_serialized(@ai_tool, AiCustomToolSerializer)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -84,13 +81,11 @@ module DiscourseAi
|
|||||||
.require(:ai_tool)
|
.require(:ai_tool)
|
||||||
.permit(
|
.permit(
|
||||||
:name,
|
:name,
|
||||||
:tool_name,
|
|
||||||
:description,
|
:description,
|
||||||
:script,
|
:script,
|
||||||
:summary,
|
:summary,
|
||||||
:rag_chunk_tokens,
|
:rag_chunk_tokens,
|
||||||
:rag_chunk_overlap_tokens,
|
:rag_chunk_overlap_tokens,
|
||||||
:rag_llm_model_id,
|
|
||||||
rag_uploads: [:id],
|
rag_uploads: [:id],
|
||||||
parameters: [:name, :type, :description, :required, enum: []],
|
parameters: [:name, :type, :description, :required, enum: []],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ module DiscourseAi
|
|||||||
requires_plugin "discourse-ai"
|
requires_plugin "discourse-ai"
|
||||||
|
|
||||||
def show
|
def show
|
||||||
end
|
|
||||||
|
|
||||||
def report
|
|
||||||
render json: AiUsageSerializer.new(create_report, root: false)
|
render json: AiUsageSerializer.new(create_report, root: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user