sendMessage function

Future<Map<String, dynamic>> sendMessage({
  1. required AiProvider provider,
  2. required String apiKey,
  3. String? model,
  4. required List<QuestionsModel> questions,
  5. required List<Map<String, String>> history,
  6. required String userMessage,
})

Implementation

Future<Map<String, dynamic>> sendMessage({
  required AiProvider provider,
  required String apiKey,
  String? model,
  required List<QuestionsModel> questions,
  required List<Map<String, String>> history,
  required String userMessage,
}) async {
  final systemPrompt = buildSystemPrompt(questions);

  String raw;
  try {
    switch (provider) {
      case AiProvider.groq:
        raw = await _callGroq(
          apiKey: apiKey,
          model: model,
          systemPrompt: systemPrompt,
          history: history,
          userMessage: userMessage,
        );
      case AiProvider.deepSeek:
        raw = await _callDeepSeek(
          apiKey: apiKey,
          model: model,
          systemPrompt: systemPrompt,
          history: history,
          userMessage: userMessage,
        );
      case AiProvider.gemini:
        raw = await _callGemini(
          apiKey: apiKey,
          model: model,
          systemPrompt: systemPrompt,
          history: history,
          userMessage: userMessage,
        );
      case AiProvider.openAi:
        raw = await _callOpenAi(
          apiKey: apiKey,
          model: model,
          systemPrompt: systemPrompt,
          history: history,
          userMessage: userMessage,
        );
    }
  } catch (e) {
    return {
      'result': false,
      'isApiError': true,
      'reason': _userFacingApiError(e),
      'nextQuestion': null,
      'done': false,
      'data': null,
    };
  }

  try {
    var parsed = _tryDecodeModelJson(raw);
    if (parsed == null) {
      try {
        // Space out the optional second call so we are less likely to hit burst limits.
        await Future<void>.delayed(const Duration(milliseconds: 600));
        switch (provider) {
          case AiProvider.groq:
            raw = await _callGroq(
              apiKey: apiKey,
              model: model,
              systemPrompt: systemPrompt + _jsonRetrySystemSuffix,
              history: history,
              userMessage: userMessage,
            );
          case AiProvider.deepSeek:
            raw = await _callDeepSeek(
              apiKey: apiKey,
              model: model,
              systemPrompt: systemPrompt + _jsonRetrySystemSuffix,
              history: history,
              userMessage: userMessage,
            );
          case AiProvider.gemini:
            raw = await _callGemini(
              apiKey: apiKey,
              model: model,
              systemPrompt: systemPrompt + _jsonRetrySystemSuffix,
              history: history,
              userMessage: userMessage,
            );
          case AiProvider.openAi:
            raw = await _callOpenAi(
              apiKey: apiKey,
              model: model,
              systemPrompt: systemPrompt + _jsonRetrySystemSuffix,
              history: history,
              userMessage: userMessage,
            );
        }
        parsed = _tryDecodeModelJson(raw);
      } catch (_) {
        // keep parsed == null
      }
    }

    if (parsed == null) {
      return {
        'result': false,
        'isApiError': true,
        'reason': 'The assistant reply could not be read. Please try again.',
        'nextQuestion': null,
        'done': false,
        'data': null,
      };
    }
    return _normalizeAiResponse(parsed);
  } catch (_) {
    return {
      'result': false,
      'isApiError': true,
      'reason': 'Something went wrong, please try again.',
      'nextQuestion': null,
      'done': false,
      'data': null,
    };
  }
}