normalizeContentParts converts the response content into a slice of normalizedContentPart values. Text payloads are bounded to MaxMessagePartTextLength runes per part; tool-call arguments are similarly bounded. File data is never stored. Unlike the stream path which caps total accumulated text at m
(content fantasy.ResponseContent)
| 1099 | // contain a fixed number of discrete content parts, each |
| 1100 | // independently bounded by MaxMessagePartTextLength. |
| 1101 | func normalizeContentParts(content fantasy.ResponseContent) []normalizedContentPart { |
| 1102 | result := make([]normalizedContentPart, 0, len(content)) |
| 1103 | for _, c := range content { |
| 1104 | if isNilInterfaceValue(c) { |
| 1105 | continue |
| 1106 | } |
| 1107 | np := normalizedContentPart{ |
| 1108 | Type: canonicalContentType(string(c.GetType())), |
| 1109 | } |
| 1110 | switch v := c.(type) { |
| 1111 | case fantasy.TextContent: |
| 1112 | np.Text = boundText(v.Text) |
| 1113 | np.TextLength = utf8.RuneCountInString(v.Text) |
| 1114 | case *fantasy.TextContent: |
| 1115 | np.Text = boundText(v.Text) |
| 1116 | np.TextLength = utf8.RuneCountInString(v.Text) |
| 1117 | case fantasy.ReasoningContent: |
| 1118 | np.Text = boundText(v.Text) |
| 1119 | np.TextLength = utf8.RuneCountInString(v.Text) |
| 1120 | case *fantasy.ReasoningContent: |
| 1121 | np.Text = boundText(v.Text) |
| 1122 | np.TextLength = utf8.RuneCountInString(v.Text) |
| 1123 | case fantasy.ToolCallContent: |
| 1124 | np.ToolCallID = v.ToolCallID |
| 1125 | np.ToolName = v.ToolName |
| 1126 | np.Arguments = boundText(v.Input) |
| 1127 | np.InputLength = utf8.RuneCountInString(v.Input) |
| 1128 | case *fantasy.ToolCallContent: |
| 1129 | np.ToolCallID = v.ToolCallID |
| 1130 | np.ToolName = v.ToolName |
| 1131 | np.Arguments = boundText(v.Input) |
| 1132 | np.InputLength = utf8.RuneCountInString(v.Input) |
| 1133 | case fantasy.FileContent: |
| 1134 | np.MediaType = v.MediaType |
| 1135 | case *fantasy.FileContent: |
| 1136 | np.MediaType = v.MediaType |
| 1137 | case fantasy.SourceContent: |
| 1138 | np.SourceType = string(v.SourceType) |
| 1139 | np.Title = v.Title |
| 1140 | np.URL = v.URL |
| 1141 | case *fantasy.SourceContent: |
| 1142 | np.SourceType = string(v.SourceType) |
| 1143 | np.Title = v.Title |
| 1144 | np.URL = v.URL |
| 1145 | case fantasy.ToolResultContent: |
| 1146 | np.ToolCallID = v.ToolCallID |
| 1147 | np.ToolName = v.ToolName |
| 1148 | np.Result = normalizeToolResultOutput(v.Result) |
| 1149 | case *fantasy.ToolResultContent: |
| 1150 | if v != nil { |
| 1151 | np.ToolCallID = v.ToolCallID |
| 1152 | np.ToolName = v.ToolName |
| 1153 | np.Result = normalizeToolResultOutput(v.Result) |
| 1154 | } |
| 1155 | } |
| 1156 | result = append(result, np) |
| 1157 | } |
| 1158 | return result |