@@ -1047,19 +1047,18 @@ func fitMessages(systemPrompt string, msgs []schema.Message, maxLength int) ([]s
10471047 // Convert to messagefit.Message. Track where each entry's text lives
10481048 // (plain Content or a multi-modal text part) so the fitted text can be
10491049 // written back to the right field. Entries with no text at all
1050- // (image-only turns) have hadText=false and are preserved even though
1051- // messagefit represents them with an empty Content .
1050+ // (image-only turns) carry an empty Content in messagefit and survive
1051+ // fitting when kept .
10521052 type fitSource struct {
10531053 copiedIdx int // index into copied; -1 for the synthetic system prompt
10541054 multiIdx int // -1 means the text lives in Content
1055- hadText bool
10561055 }
10571056 all := make ([]messagefit.Message , 0 , 1 + len (copied ))
10581057 sources := make ([]fitSource , 0 , 1 + len (copied ))
10591058
10601059 if systemPrompt != "" {
10611060 all = append (all , messagefit.Message {Role : "system" , Content : systemPrompt })
1062- sources = append (sources , fitSource {copiedIdx : - 1 , multiIdx : 0 , hadText : true })
1061+ sources = append (sources , fitSource {copiedIdx : - 1 , multiIdx : 0 })
10631062 }
10641063
10651064 for i := range copied {
@@ -1085,42 +1084,39 @@ func fitMessages(systemPrompt string, msgs []schema.Message, maxLength int) ([]s
10851084 }
10861085 }
10871086 all = append (all , messagefit.Message {Role : string (copied [i ].Role ), Content : text })
1088- sources = append (sources , fitSource {copiedIdx : i , multiIdx : multiIdx , hadText : hadText })
1087+ sources = append (sources , fitSource {copiedIdx : i , multiIdx : multiIdx })
10891088 }
10901089
10911090 // Use 97% of effective context as the token budget.
10921091 budget := contextFitBudget (maxLength )
1093- messagefit .Fit (all , budget )
1092+ kept , keptIdx , _ := messagefit .Fit (all , budget )
10941093
1095- // Convert back to []schema.Message. messagefit marks dropped entries by
1096- // emptying their Content; image-only entries were already empty before
1097- // fitting and are always preserved.
1098- result := make ([]schema.Message , 0 , len (all ))
1099- for i := range all {
1094+ // Convert back to []schema.Message. messagefit.Fit reports exactly which
1095+ // entries are kept (keptIdx); dropped entries are simply absent, so no
1096+ // empty-content sentinel is needed and image-only turns are preserved.
1097+ result := make ([]schema.Message , 0 , len (kept ))
1098+ for j , i := range keptIdx {
11001099 src := sources [i ]
1101- if all [i ].Content == "" && src .hadText {
1102- continue // dropped by fitter
1103- }
11041100 if src .copiedIdx < 0 {
1105- result = append (result , schema.Message {Role : schema .System , Content : all [ i ].Content })
1101+ result = append (result , schema.Message {Role : schema .System , Content : kept [ j ].Content })
11061102 continue
11071103 }
11081104 m := copied [src .copiedIdx ]
11091105 if src .multiIdx >= 0 && src .multiIdx < len (m .UserInputMultiContent ) {
1110- m .UserInputMultiContent [src .multiIdx ].Text = all [ i ].Content
1106+ m .UserInputMultiContent [src .multiIdx ].Text = kept [ j ].Content
11111107 // Drop any additional text parts: their content was folded into
11121108 // the first part before fitting, so keeping them would re-introduce
11131109 // text outside the token budget.
11141110 keptParts := m .UserInputMultiContent [:0 ]
1115- for j , part := range m .UserInputMultiContent {
1116- if part .Type == schema .ChatMessagePartTypeText && j != src .multiIdx {
1111+ for k , part := range m .UserInputMultiContent {
1112+ if part .Type == schema .ChatMessagePartTypeText && k != src .multiIdx {
11171113 continue
11181114 }
11191115 keptParts = append (keptParts , part )
11201116 }
11211117 m .UserInputMultiContent = keptParts
1122- } else if all [ i ].Content != "" {
1123- m .Content = all [ i ].Content
1118+ } else if kept [ j ].Content != "" {
1119+ m .Content = kept [ j ].Content
11241120 }
11251121 result = append (result , m )
11261122 }
0 commit comments