generateEmbedding method

Future<List<double>> generateEmbedding(
  1. String text
)

Generate embedding for text via Ollama with retry logic

Implementation

Future<List<double>> generateEmbedding(String text) async {
  const maxRetries = 3;
  const retryDelay = Duration(seconds: 3);

  // Log text size for debugging (only if text is large)
  if (text.length > 10000) {
    _logger.fine('📏 Large text chunk: ${text.length} characters');
  }

  for (var attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await _generateEmbeddingOnce(text);
    } catch (e) {
      // Re-throw ModelNotFoundException immediately (no retry)
      if (e is ModelNotFoundException) {
        rethrow;
      }

      // Check if it's a retryable error
      final isRetryable = _isRetryableError(e);

      if (attempt < maxRetries && isRetryable) {
        // Check if it's a "llama runner process" error - needs longer delay
        final errorStr = e.toString().toLowerCase();
        final isRunnerError = errorStr.contains('llama runner process') ||
            errorStr.contains('no longer running');

        // Wait before retry with exponential backoff
        // Longer delay for runner errors
        final baseDelay = isRunnerError
            ? Duration(seconds: retryDelay.inSeconds * 2)
            : retryDelay;
        final delay =
            Duration(milliseconds: baseDelay.inMilliseconds * attempt);

        final errorPreview = e.toString().length > 150
            ? '${e.toString().substring(0, 150)}...'
            : e.toString();
        _logger.warning(
          '⚠️  Attempt $attempt of $maxRetries failed, retrying in ${delay.inSeconds}s: $errorPreview',
        );
        await Future.delayed(delay);
        _logger.info('🔄 Retrying attempt $attempt of $maxRetries...');
        continue;
      }

      // Last attempt or non-retryable error - rethrow
      if (attempt >= maxRetries) {
        final errorStr = e.toString().toLowerCase();
        final isEofError =
            errorStr.contains('eof') || errorStr.contains('connection');

        _logger.severe(
          '❌ All $maxRetries attempts exhausted. Last error: $e '
          '(text size: ${text.length} chars)',
        );

        if (isEofError) {
          _logger.warning(
            '💡 Tip: EOF errors often indicate Ollama server instability. '
            'Try restarting Ollama: ollama serve',
          );
        }
      }
      rethrow;
    }
  }

  // This should never be reached, but just in case
  throw Exception('Failed to generate embedding after $maxRetries attempts');
}