getVertexAICompletion function

Future<String> getVertexAICompletion(
  1. String textPrompt, {
  2. Uint8List? imageData,
  3. String? imagePath,
  4. String? mimeType,
})

Implementation

Future<String> getVertexAICompletion(
  String textPrompt, {
  Uint8List? imageData,
  String? imagePath,
  String? mimeType,
}) async {
  if (_cactusToken == null) {
    throw Exception('CactusToken not set. Please call CactusVLM.init with cactusToken parameter.');
  }

  const String projectId = 'cactus-v1-452518';
  const String location = 'global';
  const String modelId = 'gemini-2.5-flash-lite-preview-06-17';

  final String endpoint =
      'https://aiplatform.googleapis.com/v1/projects/$projectId/locations/$location/publishers/google/models/$modelId:generateContent';

  final Map<String, String> headers = {
    'Authorization': 'Bearer $_cactusToken',
    'Content-Type': 'application/json',
  };

  List<Map<String, dynamic>> parts = [
    {'text': textPrompt}
  ];

  if (imageData != null || imagePath != null) {
    Uint8List? finalImageData = imageData;
    String? finalMimeType = mimeType;

    if (imagePath != null && finalImageData == null) {
      final file = File(imagePath);
      if (await file.exists()) {
        finalImageData = await file.readAsBytes();

        if (finalMimeType == null) {
          final extension = imagePath.toLowerCase().split('.').last;
          switch (extension) {
            case 'jpg':
            case 'jpeg':
              finalMimeType = 'image/jpeg';
              break;
            case 'png':
              finalMimeType = 'image/png';
              break;
            case 'gif':
              finalMimeType = 'image/gif';
              break;
            case 'webp':
              finalMimeType = 'image/webp';
              break;
            default:
              finalMimeType = 'image/jpeg';
          }
        }
      } else {
        throw Exception('Image file not found: $imagePath');
      }
    }

    if (finalImageData != null) {
      final base64Image = base64Encode(finalImageData);
      parts.add({
        'inline_data': {
          'mime_type': finalMimeType ?? 'image/jpeg',
          'data': base64Image,
        }
      });
    }
  }

  final Map<String, dynamic> requestBody = {
    'contents': {
      'role': 'user',
      'parts': parts,
    }
  };

  try {
    final response = await http.post(
      Uri.parse(endpoint),
      headers: headers,
      body: json.encode(requestBody),
    );

    if (response.statusCode == 200) {
      final dynamic responseBody = json.decode(response.body);

      if (responseBody is Map && responseBody.containsKey('error')) {
        throw Exception('API Error: ${responseBody['error']['message']}');
      }

      dynamic candidates;
      if (responseBody is List) {
        candidates = responseBody;
      } else if (responseBody is Map && responseBody.containsKey('candidates')) {
        candidates = responseBody['candidates'];
      } else {
        throw Exception('Unexpected response format: ${response.body}');
      }

      if (candidates == null || candidates.isEmpty) {
        throw Exception('No candidates in response');
      }

      final content = candidates[0]['content'];
      final parts = content['parts'] as List;
      if (parts.isEmpty) {
        throw Exception('No parts in response');
      }

      final String modelOutput = parts[0]['text'] ?? '';
      return modelOutput;
    } else {
      throw Exception(
          'Failed to call Vertex AI. Status code: ${response.statusCode}\nBody: ${response.body}');
    }
  } catch (e) {
    throw Exception('An error occurred: $e');
  }
}