Assistant Response response_text is Empty When Using OpenAI API with Flask Application

Hello everyone,

I’m developing a Flask application that interacts with the OpenAI API to manage automated responses from a virtual assistant. However, I’m encountering an issue where the response_text remains empty after processing the assistant’s response events. Here is the general flow of my code:

  1. Flask Endpoint: A user sends a message through a /chat endpoint.

  2. Event Handling: I’m using a class EventHandler that extends OpenAI’s AssistantEventHandler to handle various event types (‘thread.run.requires_action’, ‘thread.message.delta’, ‘thread.message.completed’, etc.).

  3. Response Accumulation: I’m attempting to accumulate the assistant’s response text in self.response_text within the EventHandler class.

  4. Problem Encountered: Despite events appearing to process correctly (according to logs), response_text is empty at the end, resulting in the following error:

2024-08-25 07:54:47 [DEBUG] receive_response_body.complete
2024-08-25 07:54:47 [DEBUG] response_closed.started
2024-08-25 07:54:47 [DEBUG] response_closed.complete
2024-08-25 07:54:47 [DEBUG] Stream finished.
2024-08-25 07:54:47 [DEBUG] Final state of response_text after processing event: ‘’
2024-08-25 07:54:47 [DEBUG] receive_response_body.complete
2024-08-25 07:54:47 [DEBUG] response_closed.started
2024-08-25 07:54:47 [DEBUG] response_closed.complete
2024-08-25 07:54:47 [INFO] [DEBUG] Captured response text (final): ‘’
2024-08-25 07:54:47 [ERROR] Error receiving assistant response: The assistant’s response is empty.

Steps I Have Taken to Debug:

• I reviewed the logs and confirmed that the ‘thread.message.delta’ and ‘thread.message.completed’ events are received, but the text blocks (delta_block.text.value) appear to be empty or are not being accumulated correctly into self.response_text.

• I added detailed logging to verify the content of event.data, but there doesn’t seem to be content to accumulate into response_text.

Could anyone suggest how to ensure that response_text correctly accumulates the assistant’s responses or how to resolve the issue of the empty response_text? I would appreciate any help or suggestions.

// complete code

from flask import Flask, request, jsonify
import os
from openai import OpenAI
from typing_extensions import override
from openai import AssistantEventHandler
import requests
import json
import logging

Configuración inicial

api_key = "yor api key "
assistant_id = "assistant id "
port = 5000

Configuración de registro detallado

logging.basicConfig(filename=‘log.txt’, level=logging.DEBUG,
format=‘%(asctime)s [%(levelname)s] %(message)s’,
datefmt=‘%Y-%m-%d %H:%M:%S’)

logging.info(“Iniciando servidor…”)

if not api_key:
logging.error(“No se encontró la clave de API.”)
raise ValueError(“No se encontró la clave de API. Asegúrate de que ‘api_key’ esté configurada correctamente.”)

client = OpenAI(api_key=api_key)
app = Flask(name)

class EventHandler(AssistantEventHandler):
def init(self):
super().init()
self.run_id = None
self.response_text = “”
self.thread_id = None
logging.info(“EventHandler inicializado.”)

@override
def on_event(self, event):
    logging.debug(f"Evento recibido: {event}")
    logging.debug(f"Estado inicial de response_text: '{self.response_text}'")

    if event.event == 'thread.run.requires_action':
        self.run_id = event.data.id
        self.thread_id = event.data.thread_id
        logging.debug(f"Acción requerida. Run ID: {self.run_id}, Thread ID: {self.thread_id}")
        self.handle_requires_action(event.data, self.run_id)
    elif event.event == 'thread.message.delta':
        if event.data.delta.content:
            for delta_block in event.data.delta.content:
                if delta_block.text and delta_block.text.value:
                    # Limpiar el texto de caracteres invisibles y agregar espacio si es necesario
                    text_value = delta_block.text.value.strip()
                    if self.response_text and not self.response_text.endswith(' '):
                        self.response_text += ' '  # Asegurar espacio entre concatenaciones
                    self.response_text += text_value
                    logging.debug(f"[DEBUG] Delta recibido: '{text_value}'")
            logging.debug(f"[DEBUG] Texto acumulado hasta ahora: '{self.response_text}'")
    elif event.event == 'thread.message.completed':
        logging.debug(f"[DEBUG] Mensaje completado: {event.data.content}")

        # Recolectar el contenido final del mensaje
        for content_block in event.data.content:
            if content_block.text and content_block.text.value:
                text_value = content_block.text.value.strip()
                if text_value not in self.response_text:
                    if self.response_text and not self.response_text.endswith(' '):
                        self.response_text += ' '
                    self.response_text += text_value

        logging.info(f"[INFO] Respuesta final a enviar: '{self.response_text}'")
    elif event.event == 'thread.run.error':
        logging.error(f"Error en el evento: {event.data}")

    logging.debug(f"Estado final de response_text después de procesar evento: '{self.response_text}'")


def handle_requires_action(self, data, run_id):
    logging.debug("Manejando acción requerida.")
    tool_outputs = []
    for tool in data.required_action.submit_tool_outputs.tool_calls:
        logging.debug(f"Procesando herramienta: {tool.function.name}")
        if tool.function.name == "obtener_importe_credito":
            argumentos = json.loads(tool.function.arguments)
            dni = argumentos.get("dni")
            celular = argumentos.get("celular")
            
            logging.debug(f"Verificando crédito con DNI: {dni} y Celular: {celular}")

            url = f"http://xxx.com/offer?Apellido=&Nombre=&CelularDeContacto={celular}
            
            try:
                response = requests.get(url)
                response_data = response.json()
                logging.debug(f"[DEBUG] Respuesta de la API: {response_data}")

                if response_data and 'importe' in response_data[0]:
                    importe_mayor = response_data[0]['importe']
                else:
                    importe_mayor = "0"
                
                tool_output = {
                    "tool_call_id": tool.id,
                    "output": json.dumps({"credit_amount": importe_mayor, "status": "approved", "dni": dni, "celular": celular})
                }
                tool_outputs.append(tool_output)
                logging.debug(f"Crédito procesado con éxito, importe mayor: {importe_mayor}")
            except Exception as e:
                logging.error(f"Error al realizar la solicitud HTTP: {str(e)}")
                tool_output = {
                    "tool_call_id": tool.id,
                    "output": json.dumps({"credit_amount": "0", "status": "error", "dni": dni, "celular": celular})
                }
                tool_outputs.append(tool_output)

    self.submit_tool_outputs(tool_outputs, run_id)

def submit_tool_outputs(self, tool_outputs, run_id):
    logging.debug("Enviando salidas de herramientas.")
    logging.debug(f"Thread ID: {self.thread_id}, Run ID: {run_id}")
    try:
        new_handler = EventHandler()
        with client.beta.threads.runs.submit_tool_outputs_stream(
            thread_id=self.thread_id,
            run_id=run_id,
            tool_outputs=tool_outputs,
            event_handler=new_handler,
        ) as stream:
            for text in stream.text_deltas:
                cleaned_text = text.strip()  # Limpiar el texto recibido
                logging.debug(f"Texto del stream: {cleaned_text}")
            logging.debug("Stream finalizado.")
    except Exception as e:
        logging.error(f"Error al enviar las salidas de herramientas: {str(e)}")

@app.route(‘/create_thread’, methods=[‘POST’])
def create_thread():
logging.info(“Creando nuevo hilo.”)
thread = client.beta.threads.create()
logging.info(f"Hilo creado con ID: {thread.id}")
return jsonify({“thread_id”: thread.id})

@app.route(‘/chat’, methods=[‘POST’])
def chat():
logging.info(“Inicio de nueva conversación.”)
data = request.json
thread_id = data.get(“thread_id”)
user_message = data.get(“message”).strip() # Limpiar el mensaje de entrada

if not user_message:
    logging.error("Faltan parámetros: 'message' es obligatorio.")
    return jsonify({"error": "Faltan parámetros: 'message' es obligatorio."}), 400

try:
    client.beta.threads.messages.create(
        thread_id=thread_id,
        role="user",
        content=user_message
    )
    logging.info(f"Mensaje del usuario agregado al hilo {thread_id}")
except Exception as e:
    logging.error(f"Error al agregar mensaje al hilo: {str(e)}. Creando un nuevo hilo.")
    thread = client.beta.threads.create()
    thread_id = thread.id
    logging.info(f"Nuevo hilo creado con ID: {thread_id}")
    client.beta.threads.messages.create(
        thread_id=thread_id,
        role="user",
        content=user_message
    )

handler = EventHandler()
try:
    with client.beta.threads.runs.stream(
        thread_id=thread_id,
        assistant_id=assistant_id,
        event_handler=handler,
    ) as stream:
        logging.info("Iniciando el stream de la respuesta del asistente...")
        stream.until_done()

    response_text = handler.response_text.strip()
    logging.info(f"[DEBUG] Texto de respuesta capturado (final): '{response_text}'") 
    if not response_text:
        raise ValueError("La respuesta del asistente está vacía.")
except Exception as e:
    logging.error(f"Error al recibir la respuesta del asistente: {str(e)}")
    return jsonify({"error": "Error al recibir la respuesta del asistente."}), 500

return jsonify({"response": response_text, "thread_id": thread_id})

if name == “main”:
logging.info(“Ejecutando servidor Flask.”)
app.run(host=“0.0.0.0”, port=port)

(post deleted by author)