@@ -3874,16 +3874,29 @@ func TestResponsesToPrompt_WebSearchProviderExecutedToolResults(t *testing.T) {
},
}
- input, warnings := toResponsesPrompt(prompt, "system instructions", false)
+ t.Run("store false skips item reference", func(t *testing.T) {
+ t.Parallel()
+
+ input, warnings := toResponsesPrompt(prompt, "system instructions", false)
+
+ require.Empty(t, warnings)
+ require.Len(t, input, 2,
+ "expected user + assistant text when store=false")
+ require.Nil(t, input[0].OfItemReference)
+ require.Nil(t, input[1].OfItemReference)
+ })
+
+ t.Run("store true uses item reference", func(t *testing.T) {
+ t.Parallel()
- require.Empty(t, warnings)
+ input, warnings := toResponsesPrompt(prompt, "system instructions", true)
- // Expected input items: user message, item_reference (for
- // provider-executed tool call; the ToolResultPart is skipped),
- // and assistant text message. System instructions are passed
- // via params.Instructions, not as an input item.
- require.Len(t, input, 3,
- "expected user + item_reference + assistant text")
+ require.Empty(t, warnings)
+ require.Len(t, input, 3,
+ "expected user + item_reference + assistant text when store=true")
+ require.NotNil(t, input[1].OfItemReference)
+ require.Equal(t, "ws_01", input[1].OfItemReference.ID)
+ })
}
func TestResponsesToPrompt_ReasoningWithStore(t *testing.T) {
@@ -3942,19 +3955,19 @@ func TestResponsesToPrompt_ReasoningWithStore(t *testing.T) {
}
})
- t.Run("store false includes reasoning", func(t *testing.T) {
+ t.Run("store false skips reasoning", func(t *testing.T) {
t.Parallel()
input, warnings := toResponsesPrompt(prompt, "system", false)
require.Empty(t, warnings)
- // With store=false: user, reasoning, assistant text,
- // follow-up user.
- require.Len(t, input, 4)
+ // With store=false: user, assistant text, follow-up user.
+ require.Len(t, input, 3)
- // Second item should be the reasoning.
- require.NotNil(t, input[1].OfReasoning)
- require.Equal(t, reasoningItemID, input[1].OfReasoning.ID)
+ for _, item := range input {
+ require.Nil(t, item.OfReasoning,
+ "reasoning items must not appear when store=false")
+ }
})
}
@@ -537,10 +537,16 @@ func toResponsesPrompt(prompt fantasy.Prompt, systemMessageMode string, store bo
}
if toolCallPart.ProviderExecuted {
- // Round-trip provider-executed tools via
- // item_reference, letting the API resolve
- // the stored output item by ID.
- input = append(input, responses.ResponseInputItemParamOfItemReference(toolCallPart.ToolCallID))
+ if store {
+ // Round-trip provider-executed tools via
+ // item_reference, letting the API resolve
+ // the stored output item by ID.
+ input = append(input, responses.ResponseInputItemParamOfItemReference(toolCallPart.ToolCallID))
+ }
+ // When store is disabled, server-side items are
+ // ephemeral and cannot be referenced. Skip the
+ // tool call; results are already omitted for
+ // provider-executed tools.
continue
}
@@ -559,45 +565,13 @@ func toResponsesPrompt(prompt fantasy.Prompt, systemMessageMode string, store bo
// recognised Responses API input type; skip.
continue
case fantasy.ContentTypeReasoning:
- if store {
- // When Store is enabled the API already has the
- // reasoning persisted server-side. Replaying the
- // full OfReasoning item causes a validation error
- // ("reasoning was provided without its required
- // following item") because the API cannot pair the
- // reconstructed reasoning with the output item
- // that followed it.
- continue
- }
- reasoningMetadata := GetReasoningMetadata(c.Options())
- if reasoningMetadata == nil || reasoningMetadata.ItemID == "" {
- continue
- }
- if len(reasoningMetadata.Summary) == 0 && reasoningMetadata.EncryptedContent == nil {
- warnings = append(warnings, fantasy.CallWarning{
- Type: fantasy.CallWarningTypeOther,
- Message: "assistant message reasoning part does is empty",
- })
- continue
- }
- // we want to always send an empty array
- summary := make([]responses.ResponseReasoningItemSummaryParam, 0, len(reasoningMetadata.Summary))
- for _, s := range reasoningMetadata.Summary {
- summary = append(summary, responses.ResponseReasoningItemSummaryParam{
- Type: "summary_text",
- Text: s,
- })
- }
- reasoning := &responses.ResponseReasoningItemParam{
- ID: reasoningMetadata.ItemID,
- Summary: summary,
- }
- if reasoningMetadata.EncryptedContent != nil {
- reasoning.EncryptedContent = param.NewOpt(*reasoningMetadata.EncryptedContent)
- }
- input = append(input, responses.ResponseInputItemUnionParam{
- OfReasoning: reasoning,
- })
+ // Reasoning items are always skipped during replay.
+ // When store is enabled, the API already has them
+ // persisted server-side. When store is disabled, the
+ // item IDs are ephemeral and referencing them causes
+ // "Item not found" errors. In both cases, replaying
+ // reasoning inline is not supported by the API.
+ continue
}
}