async generateResponse()

in firestore-genai-chatbot/functions/src/generative-client/vertex_ai.ts [65:135]


  async generateResponse(
    history: Message[],
    latestApiMessage: ApiMessage,
    options: GeminiChatOptions
  ) {
    if (!this.client) {
      throw new Error('Client not initialized.');
    }

    let result;
    const generativeModel = this.client.preview.getGenerativeModel({
      model: this.modelName,
    });

    const contents = [...this.messagesToApi(history), latestApiMessage];

    const request: GenerateContentRequest = {
      contents,
      generationConfig: {
        topK: options.topK,
        topP: options.topP,
        temperature: options.temperature,
        candidateCount: options.candidateCount,
        maxOutputTokens: options.maxOutputTokens,
      },
      safetySettings: options.safetySettings,
    };
    try {
      const responseStream =
        await generativeModel.generateContentStream(request);

      // TODO: we can stream now!
      const aggregatedResponse = await responseStream.response;

      result = aggregatedResponse;
      // result = await chatSession.sendMessage(latestApiMessage.parts[0].text);
    } catch (e) {
      logger.error(e);
      // TODO: the error message provided exposes the API key, so we should handle this/ get the Gemini team to fix it their side.
      throw new Error(
        'Failed to generate response, see function logs for more details.'
      );
    }

    if (
      !result.candidates ||
      !Array.isArray(result.candidates) ||
      result.candidates.length === 0
    ) {
      // TODO: handle blocked responses
      throw new Error('No candidates returned');
    }

    const candidates = result.candidates.filter(c => {
      return (
        c &&
        c.content &&
        c.content.parts &&
        c.content.parts.length > 0 &&
        c.content.parts[0].text &&
        typeof c.content.parts[0].text === 'string'
      );
    });

    return {
      response: candidates[0]!.content!.parts![0].text!,
      candidates: candidates?.map(c => c.content!.parts![0].text!) ?? [],
      safetyMetadata: result.promptFeedback,
      history,
    };
  }