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
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 {