fit method

List<AiChatMessage> fit(
  1. List<AiChatMessage> messages
)

Trims messages to fit, dropping the oldest turns first.

Order is preserved and the newest messages are always kept, because a conversation without its most recent turn is useless. Returns the input unchanged when it already fits.

Implementation

List<AiChatMessage> fit(List<AiChatMessage> messages) {
  if (messages.isEmpty) return const [];
  if (fits(messages)) return List.of(messages);

  final system = <AiChatMessage>[
    if (keepSystemMessages)
      for (final m in messages)
        if (m.role == AiRole.system) m,
  ];
  final rest = [
    for (final m in messages)
      if (!(keepSystemMessages && m.role == AiRole.system)) m,
  ];

  final systemCost = estimateConversationTokens(system);
  var budget = availableTokens - systemCost;

  // Walk backwards from the newest message, keeping whatever fits.
  final kept = <AiChatMessage>[];
  for (var i = rest.length - 1; i >= 0; i--) {
    final cost = estimateMessageTokens(rest[i]);
    final isProtected = kept.length < minimumRecentMessages;
    if (cost <= budget || isProtected) {
      kept.insert(0, rest[i]);
      budget -= cost;
    } else {
      // Everything older than this is dropped.
      break;
    }
  }

  return [...system, ...kept];
}