def get_or_make_app(
    cid: str,
    selector: int = 0,
    feedback_mode=feedback_schema.FeedbackMode.DEFERRED,
) -> mod_tru_chain.TruChain:
    """
    Create a new app for the given conversation id `cid` or return an existing
    one. Return the new or existing app. `selector` determines which app
    variant to return.
    """

    # NOTE(piotrm): Unsure about the thread safety of the various components so
    # making new ones for each conversation.

    if cid in convos:
        return convos[cid]

    if selector not in app_versions:
        selector = 0

    app_version = app_versions[selector]

    pp.pprint(f"Starting a new conversation with {app_version}.")

    # Embedding needed for Pinecone vector db.
    embedding = OpenAIEmbeddings(model="text-embedding-ada-002")  # 1536 dims
    docsearch = Pinecone.from_existing_index(
        index_name="llmdemo", embedding=embedding
    )

    retriever = docsearch.as_retriever()

    if "filtered" in app_version:
        # Better contexts fix, filter contexts with relevance:
        retriever = langchain_app.WithFeedbackFilterDocuments.of_retriever(
            retriever=retriever, feedback=f_context_relevance, threshold=0.5
        )

    # LLM for completing prompts, and other tasks.
    llm = OpenAI(temperature=0, max_tokens=128)

    # Conversation memory.
    memory = ConversationSummaryBufferMemory(
        max_token_limit=650,
        llm=llm,
        memory_key="chat_history",
        output_key="answer",
    )

    # Conversational app puts it all together.
    app = ConversationalRetrievalChain.from_llm(
        llm=llm,
        retriever=retriever,
        verbose=verb,
        return_source_documents=True,
        memory=memory,
        get_chat_history=ident,
        max_tokens_limit=4096,
    )

    # Need to copy these otherwise various chains will feature templates that
    # point to the same objects.
    app.combine_docs_chain.llm_chain.prompt = (
        app.combine_docs_chain.llm_chain.prompt.copy()
    )
    app.combine_docs_chain.document_prompt = (
        app.combine_docs_chain.document_prompt.copy()
    )

    if "lang" in app_version:
        # Language mismatch fix:
        app.combine_docs_chain.llm_chain.prompt.template = (
            "Use the following pieces of context to answer the question at the end "
            "in the same language as the question. If you don't know the answer, "
            "just say that you don't know, don't try to make up an answer.\n"
            "\n"
            "{context}\n"
            "\n"
            "Question: {question}\n"
            "Helpful Answer: "
        )

    elif "relevance" in app_version:
        # Contexts fix

        # whitespace important in "Contexts! "
        app.combine_docs_chain.llm_chain.prompt.template = (
            "Use only the relevant contexts to answer the question at the end "
            ". Some pieces of context may not be relevant. If you don't know the answer, "
            "just say that you don't know, don't try to make up an answer.\n"
            "\n"
            "Contexts: \n"
            "{context}\n"
            "\n"
            "Question: {question}\n"
            "Helpful Answer: "
        )

        # "\t" important here:
        app.combine_docs_chain.document_prompt.template = (
            "\tContext: {page_content}"
        )

    # Trulens instrumentation.
    tc = mod_tru_chain.TruChain(
        app=app,
        app_version=app_version,
        feedbacks=[f_lang_match, f_qa_relevance, f_context_relevance],
        feedback_mode=feedback_mode,
    )

    convos[cid] = tc

    return tc

    Create a new app for the given conversation id `cid` or return an existing
    one. Return the new or existing app. `selector` determines which app
    variant to return.
    
examples/experimental/end2end_apps/trubot/trubot.py