FIX: improve function call parsing (#613)
- support " / ' wrapped values - coerce integer to integer - enforce enum at boundary
This commit is contained in:
parent
64f41454ad
commit
0069256efd
|
@ -208,6 +208,15 @@ module DiscourseAi
|
||||||
rescue JSON::ParserError
|
rescue JSON::ParserError
|
||||||
[value.to_s]
|
[value.to_s]
|
||||||
end
|
end
|
||||||
|
elsif param[:type] == "string" && value
|
||||||
|
value = strip_quotes(value).to_s
|
||||||
|
elsif param[:type] == "integer" && value
|
||||||
|
value = strip_quotes(value).to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
if param[:enum] && value && !param[:enum].include?(value)
|
||||||
|
# invalid enum value
|
||||||
|
value = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
arguments[name.to_sym] = value if value
|
arguments[name.to_sym] = value if value
|
||||||
|
@ -223,6 +232,20 @@ module DiscourseAi
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def strip_quotes(value)
|
||||||
|
if value.is_a?(String)
|
||||||
|
if value.start_with?('"') && value.end_with?('"')
|
||||||
|
value = value[1..-2]
|
||||||
|
elsif value.start_with?("'") && value.end_with?("'")
|
||||||
|
value = value[1..-2]
|
||||||
|
else
|
||||||
|
value
|
||||||
|
end
|
||||||
|
else
|
||||||
|
value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def rag_fragments_prompt(conversation_context, llm:, user:)
|
def rag_fragments_prompt(conversation_context, llm:, user:)
|
||||||
upload_refs =
|
upload_refs =
|
||||||
UploadReference.where(target_id: id, target_type: "AiPersona").pluck(:upload_id)
|
UploadReference.where(target_id: id, target_type: "AiPersona").pluck(:upload_id)
|
||||||
|
|
|
@ -121,7 +121,8 @@ module DiscourseAi
|
||||||
i = 0
|
i = 0
|
||||||
while decoded
|
while decoded
|
||||||
parsed = JSON.parse(decoded.payload.string)
|
parsed = JSON.parse(decoded.payload.string)
|
||||||
messages << Base64.decode64(parsed["bytes"])
|
# perhaps some control message we can just ignore
|
||||||
|
messages << Base64.decode64(parsed["bytes"]) if parsed && parsed["bytes"]
|
||||||
|
|
||||||
decoded, _done = @decoder.decode_chunk
|
decoded, _done = @decoder.decode_chunk
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,110 @@ RSpec.describe DiscourseAi::AiBot::Personas::Persona do
|
||||||
expect(tools.find { |t| t[:name] == "image" }).to be_nil
|
expect(tools.find { |t| t[:name] == "image" }).to be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "can parse string that are wrapped in quotes" do
|
||||||
|
SiteSetting.ai_stability_api_key = "123"
|
||||||
|
xml = <<~XML
|
||||||
|
<function_calls>
|
||||||
|
<invoke>
|
||||||
|
<tool_name>image</tool_name>
|
||||||
|
<tool_id>call_JtYQMful5QKqw97XFsHzPweB</tool_id>
|
||||||
|
<parameters>
|
||||||
|
<prompts>["cat oil painting", "big car"]</prompts>
|
||||||
|
<aspect_ratio>"16:9"</aspect_ratio>
|
||||||
|
</parameters>
|
||||||
|
</invoke>
|
||||||
|
<invoke>
|
||||||
|
<tool_name>image</tool_name>
|
||||||
|
<tool_id>call_JtYQMful5QKqw97XFsHzPweB</tool_id>
|
||||||
|
<parameters>
|
||||||
|
<prompts>["cat oil painting", "big car"]</prompts>
|
||||||
|
<aspect_ratio>'16:9'</aspect_ratio>
|
||||||
|
</parameters>
|
||||||
|
</invoke>
|
||||||
|
</function_calls>
|
||||||
|
XML
|
||||||
|
|
||||||
|
image1, image2 =
|
||||||
|
tools =
|
||||||
|
DiscourseAi::AiBot::Personas::Artist.new.find_tools(
|
||||||
|
xml,
|
||||||
|
bot_user: nil,
|
||||||
|
llm: nil,
|
||||||
|
context: nil,
|
||||||
|
)
|
||||||
|
expect(image1.parameters[:prompts]).to eq(["cat oil painting", "big car"])
|
||||||
|
expect(image1.parameters[:aspect_ratio]).to eq("16:9")
|
||||||
|
expect(image2.parameters[:aspect_ratio]).to eq("16:9")
|
||||||
|
|
||||||
|
expect(tools.length).to eq(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "enforces enums" do
|
||||||
|
xml = <<~XML
|
||||||
|
<function_calls>
|
||||||
|
<invoke>
|
||||||
|
<tool_name>search</tool_name>
|
||||||
|
<tool_id>call_JtYQMful5QKqw97XFsHzPweB</tool_id>
|
||||||
|
<parameters>
|
||||||
|
<max_posts>"3.2"</max_posts>
|
||||||
|
<status>cow</status>
|
||||||
|
<foo>bar</foo>
|
||||||
|
</parameters>
|
||||||
|
</invoke>
|
||||||
|
<invoke>
|
||||||
|
<tool_name>search</tool_name>
|
||||||
|
<tool_id>call_JtYQMful5QKqw97XFsHzPweB</tool_id>
|
||||||
|
<parameters>
|
||||||
|
<max_posts>"3.2"</max_posts>
|
||||||
|
<status>open</status>
|
||||||
|
<foo>bar</foo>
|
||||||
|
</parameters>
|
||||||
|
</invoke>
|
||||||
|
</function_calls>
|
||||||
|
XML
|
||||||
|
|
||||||
|
search1, search2 =
|
||||||
|
tools =
|
||||||
|
DiscourseAi::AiBot::Personas::General.new.find_tools(
|
||||||
|
xml,
|
||||||
|
bot_user: nil,
|
||||||
|
llm: nil,
|
||||||
|
context: nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(search1.parameters.key?(:status)).to eq(false)
|
||||||
|
expect(search2.parameters[:status]).to eq("open")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can coerce integers" do
|
||||||
|
xml = <<~XML
|
||||||
|
<function_calls>
|
||||||
|
<invoke>
|
||||||
|
<tool_name>search</tool_name>
|
||||||
|
<tool_id>call_JtYQMful5QKqw97XFsHzPweB</tool_id>
|
||||||
|
<parameters>
|
||||||
|
<max_posts>"3.2"</max_posts>
|
||||||
|
<search_query>hello world</search_query>
|
||||||
|
<foo>bar</foo>
|
||||||
|
</parameters>
|
||||||
|
</invoke>
|
||||||
|
</function_calls>
|
||||||
|
XML
|
||||||
|
|
||||||
|
search, =
|
||||||
|
tools =
|
||||||
|
DiscourseAi::AiBot::Personas::General.new.find_tools(
|
||||||
|
xml,
|
||||||
|
bot_user: nil,
|
||||||
|
llm: nil,
|
||||||
|
context: nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(search.parameters[:max_posts]).to eq(3)
|
||||||
|
expect(search.parameters[:search_query]).to eq("hello world")
|
||||||
|
expect(search.parameters.key?(:foo)).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
it "can correctly parse arrays in tools" do
|
it "can correctly parse arrays in tools" do
|
||||||
SiteSetting.ai_openai_api_key = "123"
|
SiteSetting.ai_openai_api_key = "123"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue