addMessage method

Future<void> addMessage(
  1. String role,
  2. String content, {
  3. List<Map<String, dynamic>>? toolCalls,
  4. String? toolCallId,
})

Adds a new message to short-term memory and optionally persists it.

Transferred tool calls from VanturaAgent should be passed here via toolCalls.

Implementation

Future<void> addMessage(
  String role,
  String content, {
  List<Map<String, dynamic>>? toolCalls,
  String? toolCallId,
}) async {
  if (role.isEmpty && (toolCalls == null || toolCalls.isEmpty)) {
    logger.warning(
      'Attempted to add invalid message to memory',
      tag: 'MEMORY',
      extra: {'role': role, 'content_length': content.length},
    );
    return;
  }

  final message = VanturaMessage(
    role: MessageRole.values.byName(role),
    content: content.isEmpty ? null : content,
    toolCalls: toolCalls,
    toolCallId: toolCallId,
  );

  // Save to persistence if available
  if (persistence != null) {
    await persistence!.saveMessage(
      role,
      content,
      toolCalls: toolCalls,
      toolCallId: toolCallId,
    );
  }

  _shortMemory.add(message);
  _cachedMessages = null; // Invalidate cache
  logger.debug(
    'Added message to short-term memory',
    tag: 'MEMORY',
    extra: {
      'role': role,
      'content_length': content.length,
      'has_tools': toolCalls != null,
      'short_count': _shortMemory.length,
    },
  );

  if (_shortMemory.length > shortLimit) {
    logger.info(
      'Short-term memory limit reached, summarizing to long-term memory',
      tag: 'MEMORY',
      extra: {'short_count': _shortMemory.length},
    );

    // We keep the last few messages to avoid breaking tool call/response pairs
    // and to maintain immediate conversation context.
    // For very small limits (e.g. in tests), we adjust the keep count.
    final int keepCount = shortLimit > 4 ? 4 : (shortLimit > 1 ? 2 : 1);

    if (_shortMemory.length <= keepCount) return;

    final messagesToSummarize =
        _shortMemory.take(_shortMemory.length - keepCount).toList();

    final summary = await _summarizeMessages(messagesToSummarize);

    final summaryMsg = VanturaMessage(
      role: MessageRole.system,
      content: 'Historical context: $summary',
      isSummary: true,
    );

    // Save summary to persistence
    if (persistence != null) {
      await persistence!.saveMessage(
        'system',
        'Historical context: $summary',
        isSummary: true,
      );
      // Prune old non-summarized messages from persistence to save space
      await persistence!.deleteOldMessages(shortLimit);
    }

    _longMemory.add(summaryMsg);

    // Remove only the summarized messages from short-term memory
    for (int i = 0; i < messagesToSummarize.length; i++) {
      _shortMemory.removeFirst();
    }

    _cachedMessages = null; // Invalidate cache
    logger.info(
      'Added summary to long-term memory and pruned short-term',
      tag: 'MEMORY',
      extra: {
        'long_count': _longMemory.length,
        'short_count': _shortMemory.length,
        'summary_length': summary.length,
      },
    );

    // Prune long memory if needed
    if (_longMemory.length > longLimit) {
      final removed = _longMemory.removeAt(0);
      _cachedMessages = null; // Invalidate cache
      logger.info(
        'Pruned oldest long-term memory entry',
        tag: 'MEMORY',
        extra: {
          'removed_length': removed.content?.length ?? 0,
          'long_count': _longMemory.length,
        },
      );
    }
  }
}