diff --git a/app/controllers/settings/hostings_controller.rb b/app/controllers/settings/hostings_controller.rb index e2a577bfc..e6ac154b8 100644 --- a/app/controllers/settings/hostings_controller.rb +++ b/app/controllers/settings/hostings_controller.rb @@ -200,6 +200,12 @@ def update redirect_to settings_hosting_path, notice: t(".success") rescue Setting::ValidationError => error + # Preserve user-submitted OpenAI config so the form re-renders with their + # input intact (issue #1824). The form auto-submits on blur, so a partial + # entry (e.g. URI base before model) hits validation and would otherwise + # be wiped because the view reads from the unchanged Setting.* values. + @openai_uri_base_input = hosting_params[:openai_uri_base] if hosting_params.key?(:openai_uri_base) + @openai_model_input = hosting_params[:openai_model] if hosting_params.key?(:openai_model) flash.now[:alert] = error.message render :show, status: :unprocessable_entity end diff --git a/app/views/settings/hostings/_openai_settings.html.erb b/app/views/settings/hostings/_openai_settings.html.erb index b54dd7671..a1920e38b 100644 --- a/app/views/settings/hostings/_openai_settings.html.erb +++ b/app/views/settings/hostings/_openai_settings.html.erb @@ -30,7 +30,7 @@ <%= form.text_field :openai_uri_base, label: t(".uri_base_label"), placeholder: t(".uri_base_placeholder"), - value: Setting.openai_uri_base, + value: @openai_uri_base_input || Setting.openai_uri_base, autocomplete: "off", autocapitalize: "none", spellcheck: "false", @@ -41,7 +41,7 @@ <%= form.text_field :openai_model, label: t(".model_label"), placeholder: t(".model_placeholder"), - value: Setting.openai_model, + value: @openai_model_input || Setting.openai_model, autocomplete: "off", autocapitalize: "none", spellcheck: "false", diff --git a/test/controllers/settings/hostings_controller_test.rb b/test/controllers/settings/hostings_controller_test.rb index 4ebe20f87..e4ecca9bb 100644 --- a/test/controllers/settings/hostings_controller_test.rb +++ b/test/controllers/settings/hostings_controller_test.rb @@ -95,6 +95,55 @@ class Settings::HostingsControllerTest < ActionDispatch::IntegrationTest end end + # Regression: issue #1824. The OpenAI form auto-submits on blur, so entering + # the URI base before the model fires a partial submit that fails validation. + # The re-rendered form must show the user's submitted URI base — not the + # still-blank saved value — so they can finish typing the model. + test "preserves submitted openai uri base in form when validation fails" do + with_self_hosting do + Setting.openai_uri_base = nil + Setting.openai_model = "" + + patch settings_hosting_url, params: { setting: { openai_uri_base: "https://api.example.com/v1" } } + + assert_response :unprocessable_entity + assert_select "input[name=?]", "setting[openai_uri_base]" do |inputs| + assert_equal "https://api.example.com/v1", inputs.first["value"] + end + end + ensure + Setting.openai_uri_base = nil + Setting.openai_model = nil + end + + # PR #1862 review (jjmata): symmetric coverage for the model field. When the + # user changes the URI base and clears the model in the same auto-submit, the + # cross-field validation fails — the re-rendered model input must reflect the + # user's submitted (cleared) value, not silently revert to the saved model. + test "preserves submitted openai model in form when validation fails" do + with_self_hosting do + Setting.openai_uri_base = "https://saved.example.com/v1" + Setting.openai_model = "saved-model" + + patch settings_hosting_url, params: { setting: { + openai_uri_base: "https://new.example.com/v1", + openai_model: "" + } } + + assert_response :unprocessable_entity + assert_select "input[name=?]", "setting[openai_uri_base]" do |inputs| + assert_equal "https://new.example.com/v1", inputs.first["value"] + end + assert_select "input[name=?]", "setting[openai_model]" do |inputs| + assert_not_equal "saved-model", inputs.first["value"].to_s, + "model field must reflect the submitted (cleared) value, not the saved model" + end + end + ensure + Setting.openai_uri_base = nil + Setting.openai_model = nil + end + test "can update openai model alone when self hosting is enabled" do with_self_hosting do patch settings_hosting_url, params: { setting: { openai_model: "gpt-4" } }