FIX: testing tool was not showing rag results (#867)
This changeset contains 4 fixes: 1. We were allowing running tests on unsaved tools, this is problematic cause uploads are not yet associated or indexed leading to confusing results. We now only show the test button when tool is saved. 2. We were not properly scoping rag document fragements, this meant that personas and ai tools could get results from other unrelated tools, just to be filtered out later 3. index.search showed options as "optional" but implementation required the second option 4. When testing tools searching through document fragments was not working at all cause we did not properly load the tool
This commit is contained in:
parent
4923837165
commit
12869f2146
|
@ -5,7 +5,7 @@ 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[show 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
|
||||||
|
@ -17,7 +17,7 @@ module DiscourseAi
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
ai_tool = AiTool.new(ai_tool_params.except(:rag_uploads))
|
ai_tool = AiTool.new(ai_tool_params)
|
||||||
ai_tool.created_by_id = current_user.id
|
ai_tool.created_by_id = current_user.id
|
||||||
|
|
||||||
if ai_tool.save
|
if ai_tool.save
|
||||||
|
@ -29,7 +29,7 @@ module DiscourseAi
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
if @ai_tool.update(ai_tool_params.except(:rag_uploads))
|
if @ai_tool.update(ai_tool_params)
|
||||||
RagDocumentFragment.update_target_uploads(@ai_tool, attached_upload_ids)
|
RagDocumentFragment.update_target_uploads(@ai_tool, attached_upload_ids)
|
||||||
render_serialized(@ai_tool, AiCustomToolSerializer)
|
render_serialized(@ai_tool, AiCustomToolSerializer)
|
||||||
else
|
else
|
||||||
|
@ -46,18 +46,13 @@ module DiscourseAi
|
||||||
end
|
end
|
||||||
|
|
||||||
def test
|
def test
|
||||||
if params[:id].present?
|
@ai_tool.assign_attributes(ai_tool_params) if params[:ai_tool]
|
||||||
ai_tool = AiTool.find(params[:id])
|
|
||||||
else
|
|
||||||
ai_tool = AiTool.new(ai_tool_params.except(:rag_uploads))
|
|
||||||
end
|
|
||||||
|
|
||||||
parameters = params[:parameters].to_unsafe_h
|
parameters = params[:parameters].to_unsafe_h
|
||||||
|
|
||||||
# we need an llm so we have a tokenizer
|
# we need an llm so we have a tokenizer
|
||||||
# but will do without if none is available
|
# but will do without if none is available
|
||||||
llm = LlmModel.first&.to_llm
|
llm = LlmModel.first&.to_llm
|
||||||
runner = ai_tool.runner(parameters, llm: llm, bot_user: current_user, context: {})
|
runner = @ai_tool.runner(parameters, llm: llm, bot_user: current_user, context: {})
|
||||||
result = runner.invoke
|
result = runner.invoke
|
||||||
|
|
||||||
if result.is_a?(Hash) && result[:error]
|
if result.is_a?(Hash) && result[:error]
|
||||||
|
@ -74,15 +69,17 @@ module DiscourseAi
|
||||||
private
|
private
|
||||||
|
|
||||||
def attached_upload_ids
|
def attached_upload_ids
|
||||||
ai_tool_params[:rag_uploads].to_a.map { |h| h[:id] }
|
params[:ai_tool][:rag_uploads].to_a.map { |h| h[:id] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_ai_tool
|
def find_ai_tool
|
||||||
@ai_tool = AiTool.find(params[:id])
|
@ai_tool = AiTool.find(params[:id].to_i)
|
||||||
end
|
end
|
||||||
|
|
||||||
def ai_tool_params
|
def ai_tool_params
|
||||||
params.require(:ai_tool).permit(
|
params
|
||||||
|
.require(:ai_tool)
|
||||||
|
.permit(
|
||||||
:name,
|
:name,
|
||||||
:description,
|
:description,
|
||||||
:script,
|
:script,
|
||||||
|
@ -92,6 +89,7 @@ module DiscourseAi
|
||||||
rag_uploads: [:id],
|
rag_uploads: [:id],
|
||||||
parameters: [:name, :type, :description, :required, enum: []],
|
parameters: [:name, :type, :description, :required, enum: []],
|
||||||
)
|
)
|
||||||
|
.except(:rag_uploads)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -232,11 +232,13 @@ export default class AiToolEditor extends Component {
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="control-group ai-tool-editor__action_panel">
|
<div class="control-group ai-tool-editor__action_panel">
|
||||||
|
{{#unless @model.isNew}}
|
||||||
<DButton
|
<DButton
|
||||||
@action={{this.openTestModal}}
|
@action={{this.openTestModal}}
|
||||||
@label="discourse_ai.tools.test"
|
@label="discourse_ai.tools.test"
|
||||||
class="ai-tool-editor__test-button"
|
class="ai-tool-editor__test-button"
|
||||||
/>
|
/>
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
<DButton
|
<DButton
|
||||||
@action={{this.save}}
|
@action={{this.save}}
|
||||||
|
|
|
@ -25,7 +25,7 @@ export default class AiToolTestModal extends Component {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
try {
|
try {
|
||||||
const response = await ajax(
|
const response = await ajax(
|
||||||
"/admin/plugins/discourse-ai/ai-tools/test.json",
|
`/admin/plugins/discourse-ai/ai-tools/${this.args.model.tool.id}/test.json`,
|
||||||
{
|
{
|
||||||
type: "POST",
|
type: "POST",
|
||||||
data: JSON.stringify({
|
data: JSON.stringify({
|
||||||
|
|
|
@ -55,7 +55,9 @@ Discourse::Application.routes.draw do
|
||||||
only: %i[index create show update destroy],
|
only: %i[index create show update destroy],
|
||||||
path: "ai-tools",
|
path: "ai-tools",
|
||||||
controller: "discourse_ai/admin/ai_tools",
|
controller: "discourse_ai/admin/ai_tools",
|
||||||
) { post :test, on: :collection }
|
)
|
||||||
|
|
||||||
|
post "/ai-tools/:id/test", to: "discourse_ai/admin/ai_tools#test"
|
||||||
|
|
||||||
post "/ai-personas/:id/create-user", to: "discourse_ai/admin/ai_personas#create_user"
|
post "/ai-personas/:id/create-user", to: "discourse_ai/admin/ai_personas#create_user"
|
||||||
|
|
||||||
|
|
|
@ -149,6 +149,7 @@ module DiscourseAi
|
||||||
limit: limit,
|
limit: limit,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
)
|
)
|
||||||
|
|
||||||
fragments =
|
fragments =
|
||||||
RagDocumentFragment.where(id: fragment_ids, upload_id: upload_refs).pluck(
|
RagDocumentFragment.where(id: fragment_ids, upload_id: upload_refs).pluck(
|
||||||
:id,
|
:id,
|
||||||
|
@ -174,8 +175,9 @@ module DiscourseAi
|
||||||
def attach_index(mini_racer_context)
|
def attach_index(mini_racer_context)
|
||||||
mini_racer_context.attach(
|
mini_racer_context.attach(
|
||||||
"_index_search",
|
"_index_search",
|
||||||
->(query, options) do
|
->(*params) do
|
||||||
begin
|
begin
|
||||||
|
query, options = params
|
||||||
self.running_attached_function = true
|
self.running_attached_function = true
|
||||||
options ||= {}
|
options ||= {}
|
||||||
options = options.symbolize_keys
|
options = options.symbolize_keys
|
||||||
|
|
|
@ -205,7 +205,10 @@ module DiscourseAi
|
||||||
FROM
|
FROM
|
||||||
#{rag_fragments_table_name}
|
#{rag_fragments_table_name}
|
||||||
INNER JOIN
|
INNER JOIN
|
||||||
rag_document_fragments ON rag_document_fragments.id = rag_document_fragment_id
|
rag_document_fragments ON
|
||||||
|
rag_document_fragments.id = rag_document_fragment_id AND
|
||||||
|
rag_document_fragments.target_id = :target_id AND
|
||||||
|
rag_document_fragments.target_type = :target_type
|
||||||
WHERE
|
WHERE
|
||||||
model_id = #{id} AND strategy_id = #{@strategy.id}
|
model_id = #{id} AND strategy_id = #{@strategy.id}
|
||||||
ORDER BY
|
ORDER BY
|
||||||
|
|
|
@ -137,9 +137,8 @@ RSpec.describe DiscourseAi::Admin::AiToolsController do
|
||||||
|
|
||||||
describe "#test" do
|
describe "#test" do
|
||||||
it "runs an existing tool and returns the result" do
|
it "runs an existing tool and returns the result" do
|
||||||
post "/admin/plugins/discourse-ai/ai-tools/test.json",
|
post "/admin/plugins/discourse-ai/ai-tools/#{ai_tool.id}/test.json",
|
||||||
params: {
|
params: {
|
||||||
id: ai_tool.id,
|
|
||||||
parameters: {
|
parameters: {
|
||||||
input: "Hello, World!",
|
input: "Hello, World!",
|
||||||
},
|
},
|
||||||
|
@ -149,43 +148,36 @@ RSpec.describe DiscourseAi::Admin::AiToolsController do
|
||||||
expect(response.parsed_body["output"]).to eq("input" => "Hello, World!")
|
expect(response.parsed_body["output"]).to eq("input" => "Hello, World!")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "runs a new unsaved tool and returns the result" do
|
it "accept changes to the ai_tool parameters that redefine stuff" do
|
||||||
post "/admin/plugins/discourse-ai/ai-tools/test.json",
|
post "/admin/plugins/discourse-ai/ai-tools/#{ai_tool.id}/test.json",
|
||||||
params: {
|
params: {
|
||||||
ai_tool: {
|
ai_tool: {
|
||||||
name: "New Tool",
|
script: "function invoke(params) { return 'hi there'; }",
|
||||||
description: "A new test tool",
|
|
||||||
script: "function invoke(params) { return 'New test result: ' + params.input; }",
|
|
||||||
parameters: [
|
|
||||||
{ name: "input", type: "string", description: "Input for the new test tool" },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
parameters: {
|
|
||||||
input: "Test input",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
|
||||||
expect(response.parsed_body["output"]).to eq("New test result: Test input")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns an error for invalid tool_id" do
|
|
||||||
post "/admin/plugins/discourse-ai/ai-tools/test.json",
|
|
||||||
params: {
|
|
||||||
id: -1,
|
|
||||||
parameters: {
|
parameters: {
|
||||||
input: "Hello, World!",
|
input: "Hello, World!",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(response.status).to eq(400)
|
expect(response.status).to eq(200)
|
||||||
expect(response.parsed_body["errors"]).to include("Couldn't find AiTool with 'id'=-1")
|
expect(response.parsed_body["output"]).to eq("hi there")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns an error for invalid tool_id" do
|
||||||
|
post "/admin/plugins/discourse-ai/ai-tools/-1/test.json",
|
||||||
|
params: {
|
||||||
|
parameters: {
|
||||||
|
input: "Hello, World!",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response.status).to eq(404)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "handles exceptions during tool execution" do
|
it "handles exceptions during tool execution" do
|
||||||
ai_tool.update!(script: "function invoke(params) { throw new Error('Test error'); }")
|
ai_tool.update!(script: "function invoke(params) { throw new Error('Test error'); }")
|
||||||
|
|
||||||
post "/admin/plugins/discourse-ai/ai-tools/test.json",
|
post "/admin/plugins/discourse-ai/ai-tools/#{ai_tool.id}/test.json",
|
||||||
params: {
|
params: {
|
||||||
id: ai_tool.id,
|
id: ai_tool.id,
|
||||||
parameters: {
|
parameters: {
|
||||||
|
|
|
@ -49,7 +49,8 @@ describe "AI Tool Management", type: :system do
|
||||||
expect(page.first(".parameter-row__required-toggle").checked?).to eq(true)
|
expect(page.first(".parameter-row__required-toggle").checked?).to eq(true)
|
||||||
expect(page.first(".parameter-row__enum-toggle").checked?).to eq(false)
|
expect(page.first(".parameter-row__enum-toggle").checked?).to eq(false)
|
||||||
|
|
||||||
ensure_can_run_test
|
# not allowed to test yet
|
||||||
|
expect(page).not_to have_button(".ai-tool-editor__test-button")
|
||||||
|
|
||||||
expect(page).not_to have_button(".ai-tool-editor__delete")
|
expect(page).not_to have_button(".ai-tool-editor__delete")
|
||||||
find(".ai-tool-editor__save").click
|
find(".ai-tool-editor__save").click
|
||||||
|
|
Loading…
Reference in New Issue