mirror of
https://github.com/discourse/discourse-ai.git
synced 2025-06-29 11:02:17 +00:00
FIX: normalize keys in structured output (#1468)
* FIX: normalize keys in structured output Previously we did not validate the hash passed in to structured outputs which could either be string based or symbol base Specifically this broke structured outputs for Gemini in some specific cases. * comment out flake
This commit is contained in:
parent
73768ce920
commit
cc4e9e030f
@ -80,7 +80,7 @@ module DiscourseAi
|
|||||||
tokens: 800_000,
|
tokens: 800_000,
|
||||||
endpoint:
|
endpoint:
|
||||||
"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash",
|
"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash",
|
||||||
display_name: "Gemini 2.5 Pro",
|
display_name: "Gemini 2.5 Flash",
|
||||||
input_cost: 0.30,
|
input_cost: 0.30,
|
||||||
output_cost: 2.50,
|
output_cost: 2.50,
|
||||||
},
|
},
|
||||||
@ -379,6 +379,12 @@ module DiscourseAi
|
|||||||
|
|
||||||
model_params[:temperature] = temperature if temperature
|
model_params[:temperature] = temperature if temperature
|
||||||
model_params[:top_p] = top_p if top_p
|
model_params[:top_p] = top_p if top_p
|
||||||
|
|
||||||
|
# internals expect symbolized keys, so we normalize here
|
||||||
|
response_format =
|
||||||
|
JSON.parse(response_format.to_json, symbolize_names: true) if response_format &&
|
||||||
|
response_format.is_a?(Hash)
|
||||||
|
|
||||||
model_params[:response_format] = response_format if response_format
|
model_params[:response_format] = response_format if response_format
|
||||||
model_params.merge!(extra_model_params) if extra_model_params
|
model_params.merge!(extra_model_params) if extra_model_params
|
||||||
|
|
||||||
|
@ -612,6 +612,7 @@ RSpec.describe DiscourseAi::Completions::Endpoints::Gemini do
|
|||||||
).to_return(status: 200, body: response)
|
).to_return(status: 200, body: response)
|
||||||
|
|
||||||
structured_response = nil
|
structured_response = nil
|
||||||
|
|
||||||
llm.generate("Hello", response_format: schema, user: user) do |partial|
|
llm.generate("Hello", response_format: schema, user: user) do |partial|
|
||||||
structured_response = partial
|
structured_response = partial
|
||||||
end
|
end
|
||||||
@ -626,6 +627,23 @@ RSpec.describe DiscourseAi::Completions::Endpoints::Gemini do
|
|||||||
schema.dig(:json_schema, :schema).except(:additionalProperties),
|
schema.dig(:json_schema, :schema).except(:additionalProperties),
|
||||||
)
|
)
|
||||||
expect(parsed.dig(:generationConfig, :responseMimeType)).to eq("application/json")
|
expect(parsed.dig(:generationConfig, :responseMimeType)).to eq("application/json")
|
||||||
|
|
||||||
|
structured_response = nil
|
||||||
|
# once more but this time lets have the schema as string keys
|
||||||
|
llm.generate("Hello", response_format: schema.as_json, user: user) do |partial|
|
||||||
|
structured_response = partial
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(structured_response.read_buffered_property(:key)).to eq("Hello!\n there")
|
||||||
|
expect(structured_response.read_buffered_property(:num)).to eq(42)
|
||||||
|
|
||||||
|
parsed = JSON.parse(req_body, symbolize_names: true)
|
||||||
|
|
||||||
|
# Verify that schema is passed following Gemini API specs.
|
||||||
|
expect(parsed.dig(:generationConfig, :responseSchema)).to eq(
|
||||||
|
schema.dig(:json_schema, :schema).except(:additionalProperties),
|
||||||
|
)
|
||||||
|
expect(parsed.dig(:generationConfig, :responseMimeType)).to eq("application/json")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -80,7 +80,27 @@ RSpec.describe "AI Composer helper", type: :system, js: true do
|
|||||||
expect(ai_helper_menu).to have_custom_prompt_button_enabled
|
expect(ai_helper_menu).to have_custom_prompt_button_enabled
|
||||||
end
|
end
|
||||||
|
|
||||||
it "replaces the composed message with AI generated content" do
|
xit "replaces the composed message with AI generated content" do
|
||||||
|
# TODO: @keegan - this is a flake
|
||||||
|
# Failure/Error: super
|
||||||
|
|
||||||
|
# Playwright::TimeoutError:
|
||||||
|
# Timeout 11000ms exceeded.
|
||||||
|
# Call log:
|
||||||
|
# - attempting click action
|
||||||
|
# - 2 × waiting for element to be visible, enabled and stable
|
||||||
|
# - - element is not enabled
|
||||||
|
# - - retrying click action
|
||||||
|
# - - waiting 20ms
|
||||||
|
# - 2 × waiting for element to be visible, enabled and stable
|
||||||
|
# - - element is not enabled
|
||||||
|
# - - retrying click action
|
||||||
|
# - - waiting 100ms
|
||||||
|
# - 21 × waiting for element to be visible, enabled and stable
|
||||||
|
# - - element is not enabled
|
||||||
|
# - - retrying click action
|
||||||
|
# - - waiting 500ms
|
||||||
|
|
||||||
trigger_composer_helper(input)
|
trigger_composer_helper(input)
|
||||||
ai_helper_menu.fill_custom_prompt(custom_prompt_input)
|
ai_helper_menu.fill_custom_prompt(custom_prompt_input)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user