@manager.route('/completion_lease', methods=['POST']) #### refectoring the route
@login_required
def completion_lease():
print('LOLLLLLLLLL')
"""
API route to handle the completion of a conversation in LISA - Lease context.
"""
req = request.json
# Debug: Log incoming payload for reference
# print('Lease payload:', req)

# Filter and prepare messages for processing
msg = []
for m in req["messages"]:
    if m["role"] == "system":
        continue
    if m["role"] == "assistant" and not msg:
        continue
    msg.append({"role": m["role"], "content": m["content"]})

# Extract the last user message for processing
test_que = msg[-1]['content']

print("Here is the test question ------",test_que)
doc_name = extract_from_quotes(test_que)
print('-----------------------------------',doc_name)

# Call the separate function to classify the message
matched_prompt = classify_message(test_que)
print("##################################################",matched_prompt)

if matched_prompt == 'Quantitative Chat':
    # Debug: Log matched prompt for debugging
    print('Matched prompt, processing document retrieval')

    # Retrieve conversation and dialog details
    e, conv = ConversationService.get_by_id(req["conversation_id"])
    e, dia = DialogService.get_by_id(conv.dialog_id)
    print('Conversation details:', e)
    print('Dialog KB IDs:', dia.kb_ids)

    # Fetch knowledgebase items based on dialog's KB IDs
    kbs = KnowledgebaseService.get_by_ids(dia.kb_ids)
    # print('Fetched knowledgebase:', kbs)

    # Retrieve documents by knowledgebase ID
    docs, tol = DocumentService.get_by_kb_id(dia.kb_ids, 1, 20, "create_time", True, "")
    docs_combined = [{"count": index + 1, "doc_id": doc["id"], "doc_name": doc["name"]} for index, doc in enumerate(docs)]
    print('Documents retrieved:', docs_combined)

    # Append the latest message and assistant response
    conv.message.append(deepcopy(msg[-1]))
    resp_msg = 'Here are the documents requested'
    conv.message.append({"role": "assistant", "content": resp_msg})

    # Add document references to the conversation
    conv.reference.append({"chunks": [], "doc_aggs": docs_combined})
    ConversationService.update_by_id(conv.id, conv.to_dict())

    # Final output to return
    final_output = {
        'answer': resp_msg,
        'reference': {
            'total': tol,
            'chunks': [],
            'doc_aggs': docs_combined
        }
    }

    # Streaming response if requested
    def generate_response():
        yield "data:" + json.dumps({"retcode": 0, "retmsg": "", "data": final_output}, ensure_ascii=False) + "\n\n"
        yield "data:" + json.dumps({"retcode": 0, "retmsg": "", "data": True}, ensure_ascii=False) + "\n\n"

    if req.get("stream", True):
        resp = Response(generate_response(), mimetype="text/event-stream")
        resp.headers.add("Cache-Control", "no-cache")
        resp.headers.add("Connection", "keep-alive")
        resp.headers.add("X-Accel-Buffering", "no")
        resp.headers.add("Content-Type", "text/event-stream; charset=utf-8")
        return resp
    else:
        return get_json_result(data=final_output)

elif matched_prompt == 'General Chat':
    try:
        # Debug: No prompt match, proceed with alternate logic
        print('No prompt match, proceeding with non-matched flow.')
        # Search for the extracted document name
        def get_doc_id_by_name(doc_name, docs_combined):
            for doc in docs_combined:
                if doc['doc_name'] == doc_name:
                    return doc['doc_id']
            return None
        # Retrieve the conversation and dialog details
        e, conv = ConversationService.get_by_id(req["conversation_id"])
        if not e:
            return get_data_error_result(retmsg="Conversation not found!")
        
        conv.message.append(deepcopy(msg[-1]))
        e, dia = DialogService.get_by_id(conv.dialog_id)
        print('Dialog KB IDs:', dia.kb_ids)

        # Fetch documents by knowledgebase ID
        docs, tol = DocumentService.get_by_kb_id(dia.kb_ids, 1, 20, "create_time", True, "")
        docs_combined = [{"count": index + 1, "doc_id": doc["id"], "doc_name": doc["name"]} for index, doc in enumerate(docs)]
        print('Documents combined:', docs_combined)

        
        
        doc_name_to_search = doc_name[0]
        print("****************************** doc name to search",doc_name_to_search)
        getteddoc_id = get_doc_id_by_name(doc_name_to_search, docs_combined)
        print('Document ID retrieved:', getteddoc_id)
        print('checkpoint 1\n')
        if not e:
            return get_data_error_result(retmsg="Dialog not found!")
        # Remove unnecessary keys from the request
        del req["conversation_id"]
        del req["messages"]
        print('checkpoint 2\n')
        # Initialize conversation references if not present
        if not conv.reference:
            conv.reference = []
        conv.message.append({"role": "assistant", "content": ""})
        conv.reference.append({"chunks": [], "doc_aggs": []})
        print('checkpoint 3\n')
        def fillin_conv(ans):
            """ Helper function to fill the assistant's response """
            nonlocal conv
            if not conv.reference:
                conv.reference.append(ans["reference"])
            else:
                conv.reference[-1] = ans["reference"]
            conv.message[-1] = {"role": "assistant", "content": ans["answer"]}
        print('checkpoint 4\n')
        print("Diaaaaaaaaaaaaaaaaaa=",dia)
        print("Msgggggggggggggggggg=",msg)
        # Streaming responses handling
        def stream():
            try:
                print('checkpoint 5\n')
                for ans in chat(dia, msg, stream=True, doc_ids=getteddoc_id, **req):
                    fillin_conv(ans)
                    yield "data:" + json.dumps({"retcode": 0, "retmsg": "", "data": ans}, ensure_ascii=False) + "\n\n"
                ConversationService.update_by_id(conv.id, conv.to_dict())
            except Exception as e:
                print('checkpoint 7\n')
                yield "data:" + json.dumps({"retcode": 500, "retmsg": str(e),
                                            "data": {"answer": "**ERROR**: " + str(e), "reference": []}},
                                            ensure_ascii=False) + "\n\n"
            yield "data:" + json.dumps({"retcode": 0, "retmsg": "", "data": True}, ensure_ascii=False) + "\n\n"

        if req.get("stream", True):
            print('checkpoint 0111\n')
            resp = Response(stream(), mimetype="text/event-stream")
            resp.headers.add("Cache-Control", "no-cache")
            resp.headers.add("Connection", "keep-alive")
            resp.headers.add("X-Accel-Buffering", "no")
            resp.headers.add("Content-Type", "text/event-stream; charset=utf-8")
            return resp
        else:
            print('checkpoint 0222\n')
            answer = None
            print('checkpoint 8\n')
            for ans in chat(dia, msg, **req):
                answer = ans
                fillin_conv(ans)
                ConversationService.update_by_id(conv.id, conv.to_dict())
                break
            return get_json_result(data=answer)

    except Exception as e:
        return server_error_response(e)

################################################################
##################### PLAN #################################
################################################################

@manager.route('/completion_plan', methods=['POST'])
@login_required
def completion_plan():
"""
API route to handle the completion of a conversation with Lisa-Plan.
"""
print('helllo')
req = request.json

# Filter and prepare messages for processing
msg = []
for m in req["messages"]:
    if m["role"] == "system":
        continue
    if m["role"] == "assistant" and not msg:
        continue
    msg.append({"role": m["role"], "content": m["content"]})
# print('MSG---->',msg)
try:
    
    # Retrieve the conversation by ID
    e, conv = ConversationService.get_by_id(req["conversation_id"])
    if not e:
        return get_data_error_result(retmsg="Conversation not found!")

    # Append the last user message to the conversation
    conv.message.append(deepcopy(msg[-1]))

    # Retrieve the dialog by ID
    e, dia = DialogService.get_by_id(conv.dialog_id)
    if not e:
        return get_data_error_result(retmsg="Dialog not found!")


    # Remove unnecessary keys from the request
    del req["conversation_id"]
    del req["messages"]

    # Initialize the assistant's response in the conversation
    if not conv.reference:
        conv.reference = []
    conv.message.append({"role": "assistant", "content": ""})
    conv.reference.append({"chunks": [], "doc_aggs": []})

    def fillin_conv(ans):
        """
        Helper function to fill in the assistant's response in the conversation.
        """
        nonlocal conv
        conv.message[-1] = {"role": "assistant", "content": ans["answer"]}

    def stream():
        """
        Generator function to handle streaming responses.
        """
        nonlocal dia, msg, req, conv
        try:
            # API call to get the assistant's response
            print("conntttttt->>>>",conv)
            # payload = {"query": msg[-1]["content"]}
            payload = {
                "query": msg[-1]["content"],
                "conversation_history": [m for m in conv.message if m["role"] != "system"]
            }
            # print('payyyyyyload',payload)
            response = requests.post("http://127.0.0.1:5000/query", json=payload)
            response_data = response.json()
            ans = {
                "answer": response_data,
                "reference": []
            }
            fillin_conv(ans)
            yield "data:" + json.dumps({"retcode": 0, "retmsg": "", "data": ans}, ensure_ascii=False) + "\n\n"
            ConversationService.update_by_id(conv.id, conv.to_dict())
        except Exception as e:
            yield "data:" + json.dumps({"retcode": 500, "retmsg": str(e),
                                        "data": {"answer": "**ERROR**: " + str(e), "reference": []}},
                                       ensure_ascii=False) + "\n\n"
        yield "data:" + json.dumps({"retcode": 0, "retmsg": "", "data": True}, ensure_ascii=False) + "\n\n"

    if req.get("stream") is True:
        # Handle streaming responses
        resp = Response(stream(), mimetype="text/event-stream")
        resp.headers.add("Cache-Control", "no-cache")
        resp.headers.add("Connection", "keep-alive")
        resp.headers.add("X-Accel-Buffering", "no")
        resp.headers.add("Content-Type", "text/event-stream; charset=utf-8")
        return resp

    elif req.get("stream") is False:
        # Handle non-streaming responses
        # payload = {"query": msg[-1]["content"]}
        payload = {
                "query": msg[-1]["content"],
                "conversation_history": [m for m in conv.message if m["role"] != "system"]
            }
        # print('payyyyyyload',payload)
        response = requests.post("http://127.0.0.1:5000/query", json=payload)
        response_data = response.json()
        ans = {
            "answer": response_data,
            "reference": []
        }
        fillin_conv(ans)
        ConversationService.update_by_id(conv.id, conv.to_dict())
        return get_json_result(data=response_data)

except Exception as e:
    return server_error_response(e)


here plan functionhas external endpoint connected to stream just like that i want to connect external endpoint to my lease code

and the code for endpoint is this

# main.py
import os
import tempfile
import threading
from typing import List

import faiss
import numpy as np
import uvicorn
from fastapi import FastAPI, File, HTTPException, UploadFile
from fastapi.responses import JSONResponse
from langchain.chains.question_answering import load_qa_chain
from langchain.docstore.document import Document
from langchain.embeddings import HuggingFaceBgeEmbeddings
from langchain.llms import Ollama
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import FAISS
from paddleocr import PaddleOCR
from pdf2image import convert_from_path
from pydantic import BaseModel

# Initialize FastAPI app
app = FastAPI()

# Global variables and locks
INDEX_PATH = "/home/ec2-user/lisa/api/apps/pdfs/faiss_index"
UPLOAD_DIRECTORY = "/home/ec2-user/lisa/api/apps/pdfs/uploaded"
OUTPUT_DIRECTORY = "/home/ec2-user/lisa/api/apps/pdfs/output"

db_lock = threading.Lock()
ocr_lock = threading.Lock()

# Initialize PaddleOCR
ocr = PaddleOCR(use_angle_cls=True, lang='en', use_gpu=False)

# Initialize embeddings globally
embeddings = HuggingFaceBgeEmbeddings()

# Load or initialize the FAISS index
db = None
if os.path.exists(INDEX_PATH):
    try:
        db = FAISS.load_local(INDEX_PATH, embeddings, allow_dangerous_deserialization=True)
        print("FAISS index loaded successfully.")
    except Exception as e:
        print(f"Error loading FAISS index: {e}")
        db = None
else:
    print("No FAISS index found. It will be created after uploading documents.")

# Pydantic model for query input
class QueryRequest(BaseModel):
    query: str

# Helper function to perform OCR on a PDF
def perform_ocr_on_pdf(pdf_path):
    print(f"Processing PDF: {pdf_path}")

    try:
        images = convert_from_path(pdf_path)
    except Exception as e:
        print(f"Error converting {pdf_path} to images: {e}")
        return ""

    full_text = ''

    for i, image in enumerate(images):
        with tempfile.NamedTemporaryFile(delete=False, suffix='.jpg') as temp_image:
            image.save(temp_image.name, 'JPEG')

            try:
                with ocr_lock:
                    result = ocr.ocr(temp_image.name, cls=True)

                if result is None or len(result) == 0:
                    print(f"Warning: OCR returned no results for page {i+1} of {pdf_path}")
                    continue

                page_text = ''
                for line in result:
                    if line is None:
                        print(f"Warning: Empty line in OCR result for page {i+1} of {pdf_path}")
                        continue
                    for word_info in line:
                        if word_info is None or len(word_info) < 2:
                            print(f"Warning: Invalid word_info in OCR result for page {i+1} of {pdf_path}")
                            continue
                        page_text += word_info[1][0] + ' '
                    page_text += '\n'

                full_text += f'--- Page {i+1} ---\n'
                full_text += page_text + '\n'

            except Exception as e:
                print(f"Error processing page {i+1} of {pdf_path}: {e}")

        os.unlink(temp_image.name)
        print(f'Processed page {i+1} of {pdf_path}')

    if not full_text:
        print(f"Warning: No text extracted from {pdf_path}")

    return full_text

# Endpoint to upload and process PDF files
@app.post("/upload")
def upload_files(files: List[UploadFile] = File(...)):
    try:
        os.makedirs(UPLOAD_DIRECTORY, exist_ok=True)
        os.makedirs(OUTPUT_DIRECTORY, exist_ok=True)

        # Save uploaded files
        uploaded_file_paths = []

        for file in files:
            if not file.filename.lower().endswith('.pdf'):
                raise HTTPException(status_code=400, detail=f"File {file.filename} is not a PDF.")
            file_location = os.path.join(UPLOAD_DIRECTORY, file.filename)
            with open(file_location, "wb") as f:
                f.write(file.file.read())
            uploaded_file_paths.append(file_location)

        # Process PDFs
        new_documents = []
        for pdf_path in uploaded_file_paths:
            extracted_text = perform_ocr_on_pdf(pdf_path)
            if extracted_text:
                # Save extracted text to file
                output_file = os.path.join(OUTPUT_DIRECTORY, f"{os.path.splitext(os.path.basename(pdf_path))[0]}.txt")
                with open(output_file, 'w', encoding='utf-8') as f:
                    f.write(extracted_text)

                # Create a Document object with the extracted text
                doc = Document(page_content=extracted_text, metadata={"source": os.path.basename(pdf_path)})
                new_documents.append(doc)

        # Update the FAISS index
        if new_documents:
            # Split documents into chunks
            text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
            texts = text_splitter.split_documents(new_documents)

            # Update the FAISS index with threading lock
            global db
            with db_lock:
                if db is None:
                    # Create a new FAISS index
                    db = FAISS.from_documents(texts, embeddings)
                else:
                    # Add new documents to existing index
                    db.add_documents(texts)

                # Save the FAISS index
                db.save_local(INDEX_PATH)

            return {"message": "Files uploaded and processed successfully."}
        else:
            return {"message": "No text extracted from uploaded files."}

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# Endpoint to query the FAISS index
@app.post("/query")
def query_documents(request: QueryRequest):
    global db

    if db is None:
        raise HTTPException(status_code=400, detail="No FAISS index found. Please upload documents first.")

    try:
        with db_lock:
            # Retrieve relevant documents
            docs = db.similarity_search(request.query, k=20)

        # Use Ollama for question answering
        llm = Ollama(base_url="https://artglobal-lisa.hf.space", model="phi3.5:latest")
        chain = load_qa_chain(llm, chain_type="stuff")

        # Run the chain
        result = chain({"input_documents": docs, "question": request.query}, return_only_outputs=True)

        return {"answer": result['output_text']}

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# Run the app using Uvicorn
if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8003)