Botframework webchat Directline | How to get rid op 502 error while streaming openai answers

64 views Asked by At

I want to support streaming openai answers in botframework webchat client.
Answers take a long time to be generated which can be perceived as latency.

My approach to streaming openai answers via bot service and webchat
I implemented a waterfall-dialog in my chatbot (Bot Framework v4). The simplified version is like this:
.1 user asks question
.2 A message is send back immediately
.3 a grounded prompt is created
.4 The openai api (gpt-4 turbo) is contacted
.5 While the answer stream is received, I collect the bits and send them in larger portions to webchat (via an event)
.6 In Webchat, the bits are rendered in a component via middelware
.7 the conversation finished

After 15 seconds, I get a 502 error in the console as a reply the the user question (type:message): Failed to send activity: bot timed out.
In the azure portal, channel blade the issues for DirectLine states: There was an error sending this message to your bot: HTTP status code GatewayTimeout

-> Despite the error, the waterfall dialog finishes like it should, The connection with directline is still available,

My questions:
.1 It it true that the turn need to end within 15 seconds to make sure the service does not timeout? .2 What is the risk of ignoring this 502. Since the bot keeps sending events to the user, the UI is good .3 What are my best options in this case.

I did consider using a proactive message but it doesnt feel feasibe because there are events being send all the time to the user. It only takes some time for the turn to end

The waterfalldialog

const {
    ComponentDialog,
    DialogSet,
    DialogTurnStatus,
    TextPrompt,
    WaterfallDialog,
} = require('botbuilder-dialogs');

const ASK_QUESTION_PROMPT = 'ASK_QUESTION_PROMPT';
const WATERFALL_DIALOG = 'WATERFALL_DIALOG';


class RequestProcessing extends ComponentDialog {
    constructor() {
        super();
        this.addDialog(new FeedbackDialog());
        this.addDialog(new TextPrompt(ASK_QUESTION_PROMPT));
        this.addDialog(new WaterfallDialog(WATERFALL_DIALOG, [
            this.askQuestionStep.bind(this),
            this.getAnswerStep.bind(this)
        ]));
        this.initialDialogId = WATERFALL_DIALOG;
    }

    async run(turnContext, accessor) {
        const dialogSet = new DialogSet(accessor);
        dialogSet.add(this);
        const dialogContext = await dialogSet.createContext(turnContext);
        const results = await dialogContext.continueDialog();
        if (results.status === DialogTurnStatus.empty) {
            await dialogContext.beginDialog(this.id);
        }
    }

    async askQuestionStep(stepContext) {
        return await stepContext.prompt(ASK_QUESTION_PROMPT, 'What is your question?');
    }
    async getAnswerStep(stepContext) {
        await helper.callOpenAiApiStreaming(stepContext.context, prompt);
        return await this.endConversation(stepContext);
    }
    async endConversation(stepContext) {
        return await stepContext.endDialog();
    }
}

module.exports.RequestProcessing = RequestProcessing;

callOpenAiApiStreaming

async function callOpenAiApiStreaming(context) {
    const prompt = "prompt";
    const client = new OpenAIClient();
    const deploymentId =;
    const streamContent = []; 

    const events = await client.streamChatCompletions(
        deploymentId,
        prompt);

    let wordBuffer = '';
    let nonSpaceCharacterCount = 0;
    const CHARACTER_LIMIT = 50;

    for await (const event of events) {
        for (const choice of event.choices) {
            if (choice.delta && 'content' in choice.delta) {
                wordBuffer += choice.delta.content;
                nonSpaceCharacterCount += choice.delta.content.replace(/\s/g, '').length;

                if (nonSpaceCharacterCount >= CHARACTER_LIMIT) {
                    const textToSend = wordBuffer.substring(0, CHARACTER_LIMIT);
                    await sendAnswerStreamEvent(context, textToSend); // Aangepast: verwijderde id uit de parameters
                    streamContent.push(textToSend);
                    wordBuffer = wordBuffer.substring(CHARACTER_LIMIT);
                    nonSpaceCharacterCount = wordBuffer.replace(/\s/g, '').length;
                }
            }
        }
    }

    if (nonSpaceCharacterCount > 0) {
        await sendAnswerStreamEvent(context, wordBuffer);
        streamContent.push(wordBuffer);
    }

    return { success: true, functionCall: false, content: streamContent.join(' ') };
}

async function sendAnswerStreamEvent(context, content) {
    const request =
    {
        name: 'answerStream',
        type: 'event',
        value: { content: content }
    };
    await context.sendActivity(request);
}
0

There are 0 answers