fix(openai): error if previous_response_id is set without store: true

Christian Rocha created

Change summary

providers/openai/openai_test.go              |  2 +
providers/openai/responses_language_model.go |  4 +++
providers/openai/responses_params_test.go    | 29 +++++++++++++++++++++
3 files changed, 34 insertions(+), 1 deletion(-)

Detailed changes

providers/openai/openai_test.go 🔗

@@ -3631,6 +3631,7 @@ func TestResponsesGenerate_PreviousResponseIDOption(t *testing.T) {
 		ProviderOptions: fantasy.ProviderOptions{
 			Name: &ResponsesProviderOptions{
 				PreviousResponseID: fantasy.Opt("resp_prev_123"),
+				Store:              fantasy.Opt(true),
 			},
 		},
 	})
@@ -4015,6 +4016,7 @@ func TestResponsesStream_PreviousResponseIDOption(t *testing.T) {
 		ProviderOptions: fantasy.ProviderOptions{
 			Name: &ResponsesProviderOptions{
 				PreviousResponseID: fantasy.Opt("resp_prev_456"),
+				Store:              fantasy.Opt(true),
 			},
 		},
 	})

providers/openai/responses_language_model.go 🔗

@@ -122,6 +122,7 @@ func getResponsesModelConfig(modelID string) responsesModelConfig {
 }
 
 const previousResponseIDHistoryError = "cannot combine previous_response_id with replayed conversation history; use either previous_response_id (server-side chaining) or explicit message replay, not both"
+const previousResponseIDStoreError = "previous_response_id requires store to be true; the current response will not be stored and cannot be used for further chaining"
 
 func (o responsesLanguageModel) prepareParams(call fantasy.Call) (*responses.ResponseNewParams, []fantasy.CallWarning, error) {
 	var warnings []fantasy.CallWarning
@@ -167,6 +168,9 @@ func (o responsesLanguageModel) prepareParams(call fantasy.Call) (*responses.Res
 		if err := validatePreviousResponseIDPrompt(call.Prompt); err != nil {
 			return nil, warnings, err
 		}
+		if openaiOptions.Store == nil || !*openaiOptions.Store {
+			return nil, warnings, errors.New(previousResponseIDStoreError)
+		}
 		params.PreviousResponseID = param.NewOpt(*openaiOptions.PreviousResponseID)
 	}
 

providers/openai/responses_params_test.go 🔗

@@ -66,6 +66,7 @@ func TestPrepareParams_PreviousResponseID(t *testing.T) {
 
 		params, warnings, err := lm.prepareParams(testCall(prompt, &ResponsesProviderOptions{
 			PreviousResponseID: fantasy.Opt("resp_abc123"),
+			Store:              fantasy.Opt(true),
 		}))
 		require.NoError(t, err)
 		require.Empty(t, warnings)
@@ -98,7 +99,10 @@ func TestPrepareParams_PreviousResponseID_Validation(t *testing.T) {
 	t.Parallel()
 
 	lm := testResponsesLM()
-	opts := &ResponsesProviderOptions{PreviousResponseID: fantasy.Opt("resp_abc123")}
+	opts := &ResponsesProviderOptions{
+		PreviousResponseID: fantasy.Opt("resp_abc123"),
+		Store:              fantasy.Opt(true),
+	}
 
 	t.Run("rejects with assistant messages", func(t *testing.T) {
 		t.Parallel()
@@ -141,6 +145,29 @@ func TestPrepareParams_PreviousResponseID_Validation(t *testing.T) {
 		}, opts))
 		require.EqualError(t, err, previousResponseIDHistoryError)
 	})
+
+	t.Run("rejects without store", func(t *testing.T) {
+		t.Parallel()
+
+		_, _, err := lm.prepareParams(testCall(fantasy.Prompt{
+			testTextMessage(fantasy.MessageRoleUser, "hello"),
+		}, &ResponsesProviderOptions{
+			PreviousResponseID: fantasy.Opt("resp_abc123"),
+		}))
+		require.EqualError(t, err, previousResponseIDStoreError)
+	})
+
+	t.Run("rejects with store false", func(t *testing.T) {
+		t.Parallel()
+
+		_, _, err := lm.prepareParams(testCall(fantasy.Prompt{
+			testTextMessage(fantasy.MessageRoleUser, "hello"),
+		}, &ResponsesProviderOptions{
+			PreviousResponseID: fantasy.Opt("resp_abc123"),
+			Store:              fantasy.Opt(false),
+		}))
+		require.EqualError(t, err, previousResponseIDStoreError)
+	})
 }
 
 func TestValidatePreviousResponseIDPrompt(t *testing.T) {