partialCompactConversation method

Future<CompactionResult> partialCompactConversation({
  1. required List<Message> allMessages,
  2. required int pivotIndex,
  3. required String systemPrompt,
  4. String? userFeedback,
  5. PartialCompactDirection direction = PartialCompactDirection.from,
  6. OnCompactProgress? onProgress,
})

Partial compaction around a selected message index.

direction controls which side of the pivot is summarized:

Implementation

Future<CompactionResult> partialCompactConversation({
  required List<Message> allMessages,
  required int pivotIndex,
  required String systemPrompt,
  String? userFeedback,
  PartialCompactDirection direction = PartialCompactDirection.from,
  OnCompactProgress? onProgress,
}) async {
  final messagesToSummarize =
      direction == PartialCompactDirection.upTo
          ? allMessages.sublist(0, pivotIndex)
          : allMessages.sublist(pivotIndex);

  final messagesToKeep =
      direction == PartialCompactDirection.upTo
          ? allMessages.sublist(pivotIndex)
          : allMessages.sublist(0, pivotIndex);

  if (messagesToSummarize.isEmpty) {
    final desc =
        direction == PartialCompactDirection.upTo ? 'before' : 'after';
    throw CompactionException(
      'Nothing to summarize $desc the selected message.',
    );
  }

  final preTokenCount = estimateTokenCount(allMessages);

  onProgress?.call(
    const CompactProgressEvent(
      type: CompactProgressType.hooksStart,
      hookType: CompactHookType.preCompact,
    ),
  );
  onProgress?.call(
    const CompactProgressEvent(type: CompactProgressType.compactStart),
  );

  try {
    final stripped = stripImagesFromMessages(messagesToSummarize);

    final String? customInstructions =
        userFeedback != null ? 'User context: $userFeedback' : null;

    final summary = await _generateSummary(
      messages: stripped,
      systemPrompt: systemPrompt,
      customInstructions: customInstructions,
    );

    if (summary.isEmpty) {
      throw CompactionException(
        'Failed to generate conversation summary — response did not '
        'contain valid text content',
      );
    }

    final summaryMsg = Message.user(
      _formatCompactSummary(summary, false),
    );

    // Build the compacted messages in the correct order.
    final List<Message> compactedMessages;
    if (direction == PartialCompactDirection.upTo) {
      compactedMessages = [summaryMsg, ...messagesToKeep];
    } else {
      compactedMessages = [...messagesToKeep, summaryMsg];
    }

    _consecutiveFailures = 0;

    onProgress?.call(
      const CompactProgressEvent(
        type: CompactProgressType.hooksStart,
        hookType: CompactHookType.postCompact,
      ),
    );

    return CompactionResult(
      compactedMessages: compactedMessages,
      summary: summary,
      preCompactTokenCount: preTokenCount,
      postCompactTokenCount: estimateTokenCount(compactedMessages),
      strategy: CompactionStrategy.manual,
      messagesToKeep: messagesToKeep,
    );
  } catch (e) {
    _consecutiveFailures++;
    rethrow;
  } finally {
    onProgress?.call(
      const CompactProgressEvent(type: CompactProgressType.compactEnd),
    );
  }
}