STET

agent_patch

runs/2026-02-21__03-36-16__gpt-5-1-codex-mini/flux-pr-828/flux-pr-828.1-of-1.2026-03-05__17-48-26__gpt-5-1-codex-mini/agent-logs/agent.patch

10446 bytes

Back to adjudication
diff --git a/app/pkg/astminifier/astminifier.go b/app/pkg/astminifier/astminifier.go
new file mode 100644
index 0000000..1a577b5
--- /dev/null
+++ b/app/pkg/astminifier/astminifier.go
@@ -0,0 +1,202 @@
+package astminifier
+
+import (
+	"bytes"
+	"fmt"
+
+	"github.com/wundergraph/graphql-go-tools/pkg/ast"
+)
+
+const fragmentNamePrefix = "AstMinifierFragment"
+
+// Minify rewrites duplicated inline fragment selection sets into shared named fragments.
+// It returns true when any replacements were applied.
+func Minify(document *ast.Document) bool {
+	groups := make(map[string][]inlineFragmentEntry)
+	signatureCache := make(map[int]string)
+	builder := selectionSetSignatureBuilder{document: document}
+
+	for inlineRef := range document.InlineFragments {
+		fragment := document.InlineFragments[inlineRef]
+		if !fragment.HasSelections || fragment.TypeCondition.Type == -1 {
+			continue
+		}
+		setRef := fragment.SelectionSet
+		if setRef < 0 {
+			continue
+		}
+		signature, exists := signatureCache[setRef]
+		if !exists {
+			signature = builder.signature(setRef)
+			signatureCache[setRef] = signature
+		}
+		typeName := document.InlineFragmentTypeConditionNameString(inlineRef)
+		if typeName == "" {
+			continue
+		}
+		key := typeName + "|" + signature
+		groups[key] = append(groups[key], inlineFragmentEntry{
+			inlineRef:     inlineRef,
+			selectionSet:  setRef,
+			typeCondition: fragment.TypeCondition.Type,
+		})
+	}
+
+	fragmentCounter := 0
+	minified := false
+
+	for _, entries := range groups {
+		if len(entries) <= 1 {
+			continue
+		}
+		minified = true
+		fragmentName := uniqueFragmentName(document, &fragmentCounter)
+
+		sample := entries[0]
+		selectionCopy := document.CopySelectionSet(sample.selectionSet)
+		document.FragmentDefinitions = append(document.FragmentDefinitions, ast.FragmentDefinition{
+			Name:          docInputAppendString(document, fragmentName),
+			TypeCondition: ast.TypeCondition{Type: sample.typeCondition},
+			HasSelections: true,
+			SelectionSet:  selectionCopy,
+		})
+		fragDefRef := len(document.FragmentDefinitions) - 1
+		document.AddRootNode(ast.Node{Kind: ast.NodeKindFragmentDefinition, Ref: fragDefRef})
+
+		for _, entry := range entries {
+			spreadRef := document.AddFragmentSpread(ast.FragmentSpread{
+				FragmentName: docInputAppendString(document, fragmentName),
+			})
+			selectionRef := document.AddSelectionToDocument(ast.Selection{
+				Kind: ast.SelectionKindFragmentSpread,
+				Ref:  spreadRef,
+			})
+			set := entry.selectionSet
+			document.SelectionSets[set].SelectionRefs = document.SelectionSets[set].SelectionRefs[:0]
+			document.SelectionSets[set].SelectionRefs = append(document.SelectionSets[set].SelectionRefs, selectionRef)
+		}
+	}
+
+	return minified
+}
+
+type inlineFragmentEntry struct {
+	inlineRef     int
+	selectionSet  int
+	typeCondition int
+}
+
+func uniqueFragmentName(document *ast.Document, counter *int) string {
+	for {
+		name := fmt.Sprintf("%s%d", fragmentNamePrefix, *counter)
+		*counter++
+		if _, exists := document.FragmentDefinitionRef([]byte(name)); !exists {
+			return name
+		}
+	}
+}
+
+func docInputAppendString(document *ast.Document, input string) ast.ByteSliceReference {
+	return document.Input.AppendInputString(input)
+}
+
+type selectionSetSignatureBuilder struct {
+	document *ast.Document
+	buffer   bytes.Buffer
+}
+
+func (s *selectionSetSignatureBuilder) signature(setRef int) string {
+	s.buffer.Reset()
+	s.appendSelectionSet(setRef)
+	return s.buffer.String()
+}
+
+func (s *selectionSetSignatureBuilder) appendSelectionSet(setRef int) {
+	if setRef < 0 {
+		return
+	}
+	s.buffer.WriteByte('{')
+	selectionRefs := s.document.SelectionSets[setRef].SelectionRefs
+	for i, ref := range selectionRefs {
+		if i > 0 {
+			s.buffer.WriteByte(',')
+		}
+		s.appendSelection(ref)
+	}
+	s.buffer.WriteByte('}')
+}
+
+func (s *selectionSetSignatureBuilder) appendSelection(ref int) {
+	selection := s.document.Selections[ref]
+	switch selection.Kind {
+	case ast.SelectionKindField:
+		s.appendField(selection.Ref)
+	case ast.SelectionKindInlineFragment:
+		s.appendInlineFragment(selection.Ref)
+	case ast.SelectionKindFragmentSpread:
+		s.appendFragmentSpread(selection.Ref)
+	}
+}
+
+func (s *selectionSetSignatureBuilder) appendField(fieldRef int) {
+	if s.document.FieldAliasIsDefined(fieldRef) {
+		s.buffer.Write(s.document.FieldAliasBytes(fieldRef))
+		s.buffer.WriteByte(':')
+	}
+	s.buffer.Write(s.document.FieldNameBytes(fieldRef))
+	if s.document.Fields[fieldRef].HasArguments {
+		s.appendArguments(s.document.Fields[fieldRef].Arguments.Refs)
+	}
+	if s.document.Fields[fieldRef].HasDirectives {
+		s.appendDirectives(s.document.Fields[fieldRef].Directives.Refs)
+	}
+	if s.document.Fields[fieldRef].HasSelections {
+		s.appendSelectionSet(s.document.Fields[fieldRef].SelectionSet)
+	}
+}
+
+func (s *selectionSetSignatureBuilder) appendArguments(argumentRefs []int) {
+	s.buffer.WriteByte('(')
+	for i, ref := range argumentRefs {
+		if i > 0 {
+			s.buffer.WriteByte(',')
+		}
+		s.document.PrintArgument(ref, &s.buffer)
+	}
+	s.buffer.WriteByte(')')
+}
+
+func (s *selectionSetSignatureBuilder) appendDirectives(directiveRefs []int) {
+	for i, ref := range directiveRefs {
+		if i > 0 {
+			s.buffer.WriteByte(',')
+		}
+		s.document.PrintDirective(ref, &s.buffer)
+	}
+}
+
+func (s *selectionSetSignatureBuilder) appendInlineFragment(ref int) {
+	s.buffer.WriteString("inlineFragment")
+	typeName := s.document.InlineFragmentTypeConditionName(ref)
+	if len(typeName) > 0 {
+		s.buffer.WriteByte('(')
+		s.buffer.Write(typeName)
+		s.buffer.WriteByte(')')
+	}
+	if s.document.InlineFragmentHasDirectives(ref) {
+		s.appendDirectives(s.document.InlineFragments[ref].Directives.Refs)
+	}
+	if s.document.InlineFragments[ref].HasSelections {
+		s.appendSelectionSet(s.document.InlineFragments[ref].SelectionSet)
+	}
+}
+
+func (s *selectionSetSignatureBuilder) appendFragmentSpread(ref int) {
+	s.buffer.WriteString("fragmentSpread")
+	s.buffer.WriteByte('(')
+	s.buffer.Write(s.document.FragmentSpreadNameBytes(ref))
+	s.buffer.WriteByte(')')
+	if s.document.FragmentSpreads[ref].HasDirectives {
+		s.appendDirectives(s.document.FragmentSpreads[ref].Directives.Refs)
+	}
+}
diff --git a/app/pkg/astminifier/astminifier_test.go b/app/pkg/astminifier/astminifier_test.go
new file mode 100644
index 0000000..f0bca14
--- /dev/null
+++ b/app/pkg/astminifier/astminifier_test.go
@@ -0,0 +1,56 @@
+package astminifier
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	"github.com/wundergraph/graphql-go-tools/pkg/astprinter"
+	"github.com/wundergraph/graphql-go-tools/pkg/internal/unsafeparser"
+)
+
+func TestMinify_ReusesInlineFragmentSelections(t *testing.T) {
+	document := unsafeparser.ParseGraphqlDocumentString(`
+		query {
+			hero {
+				... on Human {
+					id
+					name
+				}
+				... on Human {
+					id
+					name
+				}
+				... on Human {
+					id
+					name
+				}
+			}
+		}
+	`)
+
+	require.True(t, Minify(&document))
+	query, err := astprinter.PrintString(&document, nil)
+	require.NoError(t, err)
+	require.Contains(t, query, "fragment AstMinifierFragment0 on Human")
+	require.Contains(t, query, "...AstMinifierFragment0")
+	require.Equal(t, 4, strings.Count(query, "AstMinifierFragment0"))
+}
+
+func TestMinify_DifferentInlineFragmentTypesNoChange(t *testing.T) {
+	document := unsafeparser.ParseGraphqlDocumentString(`
+		query {
+			hero {
+				... on Human {
+					id
+				}
+				... on Droid {
+					id
+				}
+			}
+		}
+	`)
+
+	require.False(t, Minify(&document))
+}
diff --git a/tmp/agent-patch-flux-pr-828.1-of-1.2026-03-05__17-48-26__gpt-5-1-codex-mini/app/pkg/execution/datasource/datasource_graphql.go b/app/pkg/execution/datasource/datasource_graphql.go
index 6be0133..fad7e1a 100644
--- a/tmp/agent-patch-flux-pr-828.1-of-1.2026-03-05__17-48-26__gpt-5-1-codex-mini/app/pkg/execution/datasource/datasource_graphql.go
+++ b/app/pkg/execution/datasource/datasource_graphql.go
@@ -13,6 +13,7 @@ import (
 
 	"github.com/wundergraph/graphql-go-tools/pkg/ast"
 	"github.com/wundergraph/graphql-go-tools/pkg/astimport"
+	"github.com/wundergraph/graphql-go-tools/pkg/astminifier"
 	"github.com/wundergraph/graphql-go-tools/pkg/astprinter"
 	"github.com/wundergraph/graphql-go-tools/pkg/internal/unsafebytes"
 	"github.com/wundergraph/graphql-go-tools/pkg/lexer/literal"
@@ -35,6 +36,8 @@ type GraphQLDataSourceConfig struct {
 	URL string `bson:"url" json:"url"`
 	// Method is the http.Method of the upstream, defaults to POST (optional)
 	Method *string `bson:"method" json:"method"`
+	// MinifyOperation enables operation minification before the request is sent.
+	MinifyOperation bool `bson:"minify_operation" json:"minify_operation"`
 }
 
 type GraphQLDataSourcePlanner struct {
@@ -296,13 +299,29 @@ func (g *GraphQLDataSourcePlanner) LeaveField(ref int) {
 		g.Walker.StopWithInternalErr(err)
 		return
 	}
+	query := buff.Bytes()
+
+	if g.dataSourceConfiguration.MinifyOperation {
+		if astminifier.Minify(g.resolveDocument) {
+			var minified bytes.Buffer
+			err = astprinter.Print(g.resolveDocument, nil, &minified)
+			if err != nil {
+				g.Walker.StopWithInternalErr(err)
+				return
+			}
+			minifiedQuery := minified.Bytes()
+			if len(minifiedQuery) < len(query) {
+				query = minifiedQuery
+			}
+		}
+	}
 	g.Args = append(g.Args, &StaticVariableArgument{
 		Name:  literal.URL,
 		Value: []byte(g.dataSourceConfiguration.URL),
 	})
 	g.Args = append(g.Args, &StaticVariableArgument{
 		Name:  literal.QUERY,
-		Value: buff.Bytes(),
+		Value: query,
 	})
 	if g.dataSourceConfiguration.Method == nil {
 		g.Args = append(g.Args, &StaticVariableArgument{
diff --git a/tmp/agent-patch-flux-pr-828.1-of-1.2026-03-05__17-48-26__gpt-5-1-codex-mini/app/pkg/execution/datasource_graphql_test.go b/app/pkg/execution/datasource_graphql_test.go
index f8e5657..862f823 100644
--- a/tmp/agent-patch-flux-pr-828.1-of-1.2026-03-05__17-48-26__gpt-5-1-codex-mini/app/pkg/execution/datasource_graphql_test.go
+++ b/app/pkg/execution/datasource_graphql_test.go
@@ -233,6 +233,7 @@ func TestGraphqlDataSource_WithPlanning(t *testing.T) {
 			expectedResponseBody: `{ "data": { "country": { "code": "DE", "name": "Germany" }, "continent": { "code": "EU", "name": "Europe" } } }`,
 		}),
 	)
+
 }
 
 func upstreamGraphqlServer(t *testing.T, assertRequestBody bool, expectedRequestBody string, response string) *httptest.Server {