langchain-ai / langchain

๐Ÿฆœ๐Ÿ”— Build context-aware reasoning applications
https://python.langchain.com
MIT License
94.86k stars 15.36k forks source link

Dict validation error #14337

Closed Vignesh1489 closed 7 months ago

Vignesh1489 commented 11 months ago

System Info

Trying to execute the chatbot script with sagemaker endpoint of LLAMA2 llm model getting dict validation error for RetrievalQA

Request: def retreiveFromLL(userQuery: str) -> QueryResponse: pre_prompt = """[INST] <>

You are a helpful, respectful and honest assistant. Answer exactly in detail from the context

<</SYS>>
Answer the question below from context below :

"""
prompt = pre_prompt + "CONTEXT:\n\n{context}\n" +"Question : {question}" + "[\INST]"
llama_prompt = PromptTemplate(template=prompt, input_variables=["context", "question"])
chain_type_kwargs = {"prompt": llama_prompt}

embeddings = SentenceTransformerEmbeddings(model_name=EMBEDDING_MODEL)

# Initialize PGVector index
vector_db = PGVector(
    embedding_function=embeddings,
    collection_name='CSE_runbooks',
    connection_string=CONNECTION_STRING,
)
print("**Invoking PGVector")

    # Custom ContentHandler to handle input and output to the SageMaker Endpoint
class LlamaChatContentHandler(LLMContentHandler):
    content_type = "application/json"
    accepts = "application/json"

    def transform_input(self, inputs: str, model_kwargs: Dict = {}) -> bytes:
        payload = {
    "inputs": pre_prompt,
            "parameters": {"max_new_tokens":2000, "top_p":0.9, "temperature":0.1}}
        input_str = ' '.join(inputs)
        input_str = json.dumps(payload)
        print(payload)
        return input_str.encode("utf-8")

    def transform_output(self, output: bytes) -> str:
        response_json = json.loads(output.read().decode("utf-8"))
        content = response_json[0]["generated_text"]
        return content
# Initialize SagemakerEndpoint
print("Invoking LLM SageMaker Endpoint")
llm = SagemakerEndpoint(
    endpoint_name=LLAMA2_ENDPOINT,
    region_name=AWS_REGION,
    content_handler=LlamaChatContentHandler(),
    callbacks=[StreamingStdOutCallbackHandler()],
    endpoint_kwargs={"CustomAttributes": "accept_eula=true"},
)
print(llm)    

# Create a RetrievalQA instance with Pinecone as the retriever
query = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=vector_db, return_source_documents=True, chain_type_kwargs=chain_type_kwargs)

print("**Invoking query")
result = query({"query": userQuery})
response = result["result"]

Error: Traceback (most recent call last): File "/home/ec2-user/.local/lib/python3.9/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 534, in _run_script exec(code, module.dict) File "/home/ec2-user/milvus/qa_UI.py", line 26, in userResponse = getLLMResponse(user_input) File "/home/ec2-user/milvus/getLLMResponse1.py", line 37, in getLLMResponse userResponse = retreiveFromLL(userQuery) File "/home/ec2-user/milvus/getLLMResponse1.py", line 97, in retreiveFromLL query = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=vector_db, return_source_documents=True, chain_type_kwargs=chain_type_kwargs) File "/home/ec2-user/.local/lib/python3.9/site-packages/langchain/chains/retrieval_qa/base.py", line 103, in from_chain_type return cls(combine_documents_chain=combine_documents_chain, kwargs) File "/home/ec2-user/.local/lib/python3.9/site-packages/langchain/load/serializable.py", line 97, in init super().init(kwargs) File "/home/ec2-user/.local/lib/python3.9/site-packages/pydantic/v1/main.py", line 341, in init raise validation_error pydantic.v1.error_wrappers.ValidationError: 1 validation error for RetrievalQA retriever value is not a valid dict (type=type_error.dict)

Who can help?

No response

Information

Related Components

Reproduction

Execute the code

Expected behavior

response from LLM

dosubot[bot] commented 11 months ago

๐Ÿค–

Hello @Vignesh1489! I'm Dosu, a bot that assists with troubleshooting, answering questions, and guiding contributors. While we wait for a human maintainer to join us, I'll do my best to help you out. Let's get started!

Based on the error message you provided, it seems like the retriever argument you're passing to the RetrievalQA.from_chain_type method is not a valid dictionary. In the LangChain framework, the retriever attribute of the RetrievalQA class is expected to be an instance of the BaseRetriever class or any of its subclasses. You can see this in the source code.

Here is the relevant code snippet:

class RetrievalQA(BaseRetrievalQA):
    """Chain for question-answering against an index."""

    retriever: BaseRetriever = Field(exclude=True)

In your case, you're passing vector_db as the retriever argument, which is an instance of PGVector. Make sure that PGVector is a subclass of BaseRetriever.

Also, the error could be related to the transform_input or transform_output methods of the ContentHandlerBase class or its subclasses. These methods expect specific types of arguments. The transform_input method expects prompt to be a string and model_kwargs to be a dictionary. The transform_output method expects a bytes object as input and returns a string. If the data being passed to these methods is not of the correct type, this could be the source of the error. You can see this in the source code.

Here is the relevant code snippet:

class ContentHandlerBase(Generic[INPUT_TYPE, OUTPUT_TYPE]):
    @abstractmethod
    def transform_input(self, prompt: INPUT_TYPE, model_kwargs: Dict) -> bytes:
        """Transforms the input to a format that model can accept
        as the request Body. Should return bytes or seekable file
        like object in the format specified in the content_type
        request header.
        """

    @abstractmethod
    def transform_output(self, output: bytes) -> OUTPUT_TYPE:
        """Transforms the output from the model to string that
        the LLM class expects.
        """

Please check the data being passed to these methods to ensure it is of the correct type. If you're still facing issues, could you provide more specific information about the data being passed to these methods and the exact line where the error is occurring? This would help in providing a more precise solution.

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a ๐Ÿ‘ if this is helpful and ๐Ÿ‘Ž if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

Vignesh1489 commented 11 months ago

@dosu-bot RetrievalQA.from_llm is valid dictionary?

dosubot[bot] commented 11 months ago

๐Ÿค–

Based on the information provided, it seems that the PGVector instance is not a valid argument for the retriever parameter in the RetrievalQA.from_llm method of the LangChain framework. The retriever parameter expects an instance of BaseRetriever or its subclasses, as indicated in the RetrievalQA class definition:

class RetrievalQA(BaseRetrievalQA):
    """Chain for question-answering against an index."""

    retriever: BaseRetriever = Field(exclude=True)

(Source: libs/langchain/langchain/chains/retrieval_qa/base.py)

The PGVector class in the LangChain framework is used for creating and managing collections of documents in a PostgreSQL database, where each document is associated with a vector representation (embedding). This class provides functionality for adding documents to the collection, searching for similar documents based on their embeddings, and managing the collection metadata.

However, the PGVector class provides the as_retriever method, which returns a Retriever object. This Retriever object should be a valid argument for the retriever parameter in the RetrievalQA.from_llm method. Here's how you can modify your code:

vector_db = PGVector(
    embedding_function=embeddings,
    collection_name='CSE_runbooks',
    connection_string=CONNECTION_STRING,
)

retriever = vector_db.as_retriever()

query = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever, return_source_documents=True, chain_type_kwargs=chain_type_kwargs)

In this modified code, the as_retriever method is called on the vector_db object to create a Retriever object, which is then passed as the retriever argument to the RetrievalQA.from_chain_type method.

I hope this helps! If you have any further questions or if this solution doesn't work for you, please provide more details about your use case and the error you're encountering.

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a ๐Ÿ‘ if this is helpful and ๐Ÿ‘Ž if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

Vignesh1489 commented 11 months ago

@dosu-bot retriever value is not getting passed to transform_input

`def retreiveFromLL(userQuery: str) -> QueryResponse: pre_prompt = """[INST] <>

You are a helpful, respectful and honest assistant. Answer exactly in detail from the context

<</SYS>>
Answer the question below from context below :

"""
prompt = pre_prompt + "CONTEXT:\n\n{context}\n" +"Question : {question}" + "[\INST]"
llama_prompt = PromptTemplate(template=prompt, input_variables=["context", "question"])
chain_type_kwargs = {"prompt": llama_prompt}

embeddings = SentenceTransformerEmbeddings(model_name=EMBEDDING_MODEL)

# Initialize PGVector index
vector_db = PGVector(
    embedding_function=embeddings,
    collection_name='CSE_runbooks',
    connection_string=CONNECTION_STRING,

) print("**Invoking PGVector")

    # Custom ContentHandler to handle input and output to the SageMaker Endpoint
class LlamaChatContentHandler(LLMContentHandler):
    content_type = "application/json"
    accepts = "application/json"

    def transform_input(self, inputs: str, model_kwargs: Dict = {}) -> bytes:
        payload = {
    "inputs": pre_prompt,
            "parameters": {"max_new_tokens":2000, "top_p":0.9, "temperature":0.1}}
        input_str = ' '.join(inputs)
        input_str = json.dumps(payload)
        print(payload)
        return input_str.encode("utf-8")

    def transform_output(self, output: bytes) -> str:
        response_json = json.loads(output.read().decode("utf-8"))
        content = response_json[0]["generated_text"]
        return content
# Initialize SagemakerEndpoint
print("Invoking LLM SageMaker Endpoint")
llm = SagemakerEndpoint(
    endpoint_name=LLAMA2_ENDPOINT,
    region_name=AWS_REGION,
    content_handler=LlamaChatContentHandler(),
    callbacks=[StreamingStdOutCallbackHandler()],
    endpoint_kwargs={"CustomAttributes": "accept_eula=true"},
)
print(llm)
retriever = vector_db.as_retriever(search_type="similarity", search_kwargs={'k': 3})

query = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever, return_source_documents=True, chain_type_kwargs=chain_type_kwargs)   

# Create a RetrievalQA instance with Pinecone as the retriever
#query = RetrievalQA.from_llm(llm=llm, chain_type="stuff", retriever=vector_db, return_source_documents=True, chain_type_kwargs=chain_type_kwargs)

print("**Invoking query")
result = query({"query": userQuery})
response = result["result"]`
dosubot[bot] commented 11 months ago

๐Ÿค–

Based on the code you've shared, it seems like the retriever value is not being passed to the transform_input method because it's not being used in a chain of functions that includes transform_input. In the LangChain framework, if you want to use a function in a chain, you need to wrap it in a RunnablePassthrough object. This is because the LangChain framework uses the | operator to chain functions together, and this operator is overloaded in the RunnablePassthrough class to allow for function chaining.

Here's how you can modify your code to fix this issue:

chain = {
    "context": {"x": RunnablePassthrough()} | rewriter | RunnablePassthrough(retriever),
    "question": RunnablePassthrough()
    } | prompt | model | StrOutputParser()

In this code, RunnablePassthrough(retriever) wraps the retriever function in a RunnablePassthrough object, allowing it to be used in the chain.

You can find more information about how the RunnablePassthrough class works in the LangChain framework in the source code. The RunnablePassthrough class is a runnable that passes through inputs unchanged or with additional keys. It behaves almost like the identity function, but it can be configured to add additional keys to the output if the input is a dictionary.

I hope this helps! If you have any other questions, feel free to ask.

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a ๐Ÿ‘ if this is helpful and ๐Ÿ‘Ž if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

Vignesh1489 commented 11 months ago

@dosu-bot when trying to use SagemakerEndpointEmbeddings facing issue

Source code: `def build_chain():

# Custom ContentHandler to handle input and output to the SageMaker Endpoint
class LlamaChatContentHandler(LLMContentHandler):
    content_type = "application/json"
    accepts = "application/json"

    def transform_input(self, inputs: str, model_kwargs: Dict = {}) -> bytes:
        payload = {
    "inputs": f"[INST] <<SYS>> You are a helpful, respectful, and honest assistant. Answer exactly in detail from the context. Answer the question below from the context below <<SYS>> {inputs} [/INST] ",
            "parameters": {"max_new_tokens":2000, "top_p":0.9, "temperature":0.1}}
        input_str = ' '.join(inputs)
        input_str = json.dumps(payload)
        print(payload)
        return input_str.encode("utf-8")

    def transform_output(self, output: bytes) -> str:
        response_json = json.loads(output.read().decode("utf-8"))
        content = response_json[0]["generated_text"]
        return content

content_handler = LlamaChatContentHandler()

embeddings = SagemakerEndpointEmbeddings(
# credentials_profile_name="credentials-profile-name",
    endpoint_name= endpoint,
    region_name="us-east-1",
    content_handler=content_handler,

)

Initialize PGVector index

vector_db = PGVector(
    embedding_function=embeddings,
    collection_name='CSE_runbooks',
    connection_string=CONNECTION_STRING,
)
print("**Invoking PGVector")

# Initialize SagemakerEndpoint
print("Invoking LLM SageMaker Endpoint")
llm = SagemakerEndpoint(
    endpoint_name=LLAMA2_ENDPOINT,
    region_name=AWS_REGION,
    content_handler=LlamaChatContentHandler(),
    callbacks=[StreamingStdOutCallbackHandler()],
    endpoint_kwargs={"CustomAttributes": "accept_eula=true"},
)
print(llm)

`

Error: File "/home/ec2-user/.local/lib/python3.9/site-packages/pydantic/v1/main.py", line 341, in init raise validation_error pydantic.v1.error_wrappers.ValidationError: 1 validation error for SagemakerEndpointEmbeddings content_handler instance of EmbeddingsContentHandler expected (type=type_error.arbitrary_type; expected_arbitrary_type=EmbeddingsContentHandler)

dosubot[bot] commented 11 months ago

๐Ÿค–

Based on the error message you're receiving, it seems that the SagemakerEndpointEmbeddings class in the LangChain framework expects an instance of EmbeddingsContentHandler for the content_handler parameter. Your LlamaChatContentHandler class, which is a subclass of LLMContentHandler, is not compatible with this requirement.

To resolve this issue, you need to create a new content handler class that inherits from EmbeddingsContentHandler and implements the required methods. Here's an example:

from langchain.embeddings.sagemaker_endpoint import EmbeddingsContentHandler
import json
from typing import List, Dict

class MyEmbeddingsContentHandler(EmbeddingsContentHandler):
    content_type = "application/json"
    accepts = "application/json"

    def transform_input(self, prompts: List[str], model_kwargs: Dict) -> bytes:
        input_str = json.dumps({prompts: prompts, **model_kwargs})
        return input_str.encode('utf-8')

    def transform_output(self, output: bytes) -> List[List[float]]:
        response_json = json.loads(output.read().decode("utf-8"))
        return response_json["vectors"]

Then, you can use this new content handler with SagemakerEndpointEmbeddings:

se = SagemakerEndpointEmbeddings(
    endpoint_name=endpoint_name,
    region_name=region_name,
    credentials_profile_name=credentials_profile_name,
    content_handler=MyEmbeddingsContentHandler()
)

This way, you're providing a content handler that is compatible with SagemakerEndpointEmbeddings and can handle the specific input and output formats of your application.

You can find more information about this in the SagemakerEndpointEmbeddings class in the LangChain repository.

Please note that the EmbeddingsContentHandler class is designed to handle the specific input and output formats required by the SagemakerEndpointEmbeddings class. If your application requires a different format, you may need to adjust the transform_input and transform_output methods accordingly.

Let me know if this helps or if you need further assistance.

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a ๐Ÿ‘ if this is helpful and ๐Ÿ‘Ž if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

Vignesh1489 commented 11 months ago

@dosu-bot

Could you please let me know how to use SagemakerEndpointEmbeddings & SagemakerEndpoint for single content handler.

dosubot[bot] commented 11 months ago

๐Ÿค–

Based on the error message you're receiving, it seems that the SagemakerEndpointEmbeddings class expects an instance of EmbeddingsContentHandler as its content_handler argument, while the SagemakerEndpoint class expects an instance of LLMContentHandler.

The content_handler argument is used to specify how the input and output data should be transformed when making requests to the SageMaker endpoint. The EmbeddingsContentHandler and LLMContentHandler classes likely define different transformations suitable for the specific tasks they are designed for.

Given this, it might not be possible to use a single content handler for both SagemakerEndpointEmbeddings and SagemakerEndpoint if they require different data transformations. However, you could potentially create a new content handler class that inherits from both EmbeddingsContentHandler and LLMContentHandler and implements the necessary transformations for both tasks. Here's an example of how you might do this:

class CombinedContentHandler(EmbeddingsContentHandler, LLMContentHandler):
    # Implement the necessary transformations here
    ...

content_handler = CombinedContentHandler()

embeddings = SagemakerEndpointEmbeddings(
    endpoint_name=endpoint,
    region_name="us-east-1",
    content_handler=content_handler,
)

llm = SagemakerEndpoint(
    endpoint_name=LLAMA2_ENDPOINT,
    region_name=AWS_REGION,
    content_handler=content_handler,
    callbacks=[StreamingStdOutCallbackHandler()],
    endpoint_kwargs={"CustomAttributes": "accept_eula=true"},
)

Please note that this is just a suggestion and might not work depending on the specific requirements of the EmbeddingsContentHandler and LLMContentHandler classes. If this doesn't work, you might need to use separate content handlers for SagemakerEndpointEmbeddings and SagemakerEndpoint.

I hope this helps! If you have any further questions, feel free to ask.

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a ๐Ÿ‘ if this is helpful and ๐Ÿ‘Ž if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

Vignesh1489 commented 11 months ago

@dosu-bot while using combined content handler for embeddings and llm getting this error

An error occurred: Error raised by inference endpoint: An error occurred (ModelError) when calling the InvokeEndpoint operation: Received client error (400) from primary with message "{ "code": 400, "type": "InternalServerException", "message": "Input payload contains an undocumented parameters {\u0027inputs\u0027, \u0027parameters\u0027}. Documented parameters include [\u0027text_inputs\u0027, \u0027normalize\u0027]." } ". See https://us-east-1.console.aws.amazon.com/cloudwatch/home?region=us-east-1#logEventViewer:group=/aws/sagemaker/Endpoints/jumpstart-dft-hf-textembedding-all-minilm-l6-v2 in account 193829050127 for more information.

dosubot[bot] commented 11 months ago

๐Ÿค–

Based on the error message you provided, it seems that the payload being sent to the SageMaker endpoint contains parameters (inputs and parameters) that are not recognized by the endpoint. The endpoint expects parameters text_inputs and normalize.

This issue has been encountered before in the LangChain repository. Here are two similar issues that were resolved:

  1. AWS Sagemaker - The solution here was to add CustomAttributes: 'accept_eula=true' to the clientOptions in the SageMakerEndpoint instantiation. However, this may not be directly related to your issue.

  2. DOC: Under Modules/LLM/Integrations, the Sagemaker Endpoint integration example the "ContentHandler()" is giving an error - The solution here was to modify the "ContentHandler" class, specifically the "transform_input" and "transform_output" functions. The "transform_input" function was changed to use text_inputs instead of inputs.

To resolve your issue, you can try modifying the payload dictionary in the _call method of the MosaicML class in the LangChain codebase. Here's how you can do it:

def _call(
    self,
    prompt: str,
    stop: Optional[List[str]] = None,
    run_manager: Optional[CallbackManagerForLLMRun] = None,
    is_retry: bool = False,
    **kwargs: Any,
) -> str:
    """Call out to a MosaicML LLM inference endpoint.

    Args:
        prompt: The prompt to pass into the model.
        stop: Optional list of stop words to use when generating.

    Returns:
        The string generated by the model.

    Example:
        .. code-block:: python

            response = mosaic_llm("Tell me a joke.")
    """
    _model_kwargs = self.model_kwargs or {}

    prompt = self._transform_prompt(prompt)

    # Modify the payload dictionary here
    payload = {"text_inputs": [prompt]}
    payload.update(_model_kwargs)
    payload.update(kwargs)

    # Rest of the code...

This change replaces inputs with text_inputs in the payload dictionary. If you also need to include the normalize parameter, you can add it to the payload dictionary as needed.

You can find the MosaicML class in the libs/langchain/langchain/llms/mosaicml.py file in the LangChain repository.

I hope this helps! If you have any further questions or if the issue persists, please provide more details about your setup and the steps you're taking so I can assist you better.

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a ๐Ÿ‘ if this is helpful and ๐Ÿ‘Ž if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

Vignesh1489 commented 11 months ago

@dosu-bot when different content handlers is used for embedding and llm getting unhashable type: 'list' error

` def build_chain():

class MyEmbeddingsContentHandler(EmbeddingsContentHandler):
    content_type = "application/json"
    accepts = "application/json"

    def transform_input(self, prompts: str, model_kwargs: Dict = {}) -> bytes:
        input_str = json.dumps({prompts: prompts, **model_kwargs})
        return input_str.encode('utf-8')

    def transform_output(self, output: bytes) -> List[List[float]]:
        response_json = json.loads(output.read().decode("utf-8"))
        return response_json["vectors"]

content_handler = MyEmbeddingsContentHandler()

embeddings = SagemakerEndpointEmbeddings(
# credentials_profile_name="credentials-profile-name",
    endpoint_name="endpoint",
    region_name="us-east-3",
    content_handler=content_handler,

) print(embeddings)

Initialize PGVector index

vector_db = PGVector(
    embedding_function=embeddings,
    collection_name='CSE_runbooks',
    connection_string=CONNECTION_STRING,
)
print(vector_db)
# Custom ContentHandler to handle input and output to the SageMaker Endpoint
class LlamaChatContentHandler(LLMContentHandler):
    content_type = "application/json"
    accepts = "application/json"

    def transform_input(self, text_inputs: str, model_kwargs: Dict = {}) -> bytes:
        payload = {
    "inputs": f"[INST] <<SYS>> You are a helpful, respectful, and honest assistant. Answer exactly in detail from the context. Answer the question below from the context below <<SYS>> {inputs} [/INST] ",
            "parameters": {"max_new_tokens":2000, "top_p":0.9, "temperature":0.1}}
        input_str = ' '.join(text_inputs)
        input_str = json.dumps(payload)
        print(payload)
        return input_str.encode("utf-8")

    def transform_output(self, output: bytes) -> str:
        response_json = json.loads(output.read().decode("utf-8"))
        content = response_json[0]["generated_text"]
        return content

content_handler = LlamaChatContentHandler()

# Initialize SagemakerEndpoint
print("Invoking LLM SageMaker Endpoint")
llm = SagemakerEndpoint(
    endpoint_name=LLAMA2_ENDPOINT,
    region_name=AWS_REGION,
    content_handler=content_handler,
    callbacks=[StreamingStdOutCallbackHandler()],
    endpoint_kwargs={"CustomAttributes": "accept_eula=true"},
)
print(llm)`
dosubot[bot] commented 11 months ago

๐Ÿค–

Based on the code you've shared, it seems like the "unhashable type: 'list'" error is likely due to the use of mutable data types, such as a list, as a key in a dictionary or as an element in a set. In Python, only immutable data types can be hashed and used as keys in a dictionary or elements in a set.

In your transform_input method within the MyEmbeddingsContentHandler class, you're creating a dictionary with {prompts: prompts, **model_kwargs}. If prompts is a list, this could be the source of the error. The same applies to the transform_input method in the LlamaChatContentHandler class.

To resolve this issue, you should ensure that you're only using immutable data types (like strings, integers, tuples, etc.) as keys in your dictionaries. If you need to use a list as a key, you could convert it to a tuple, which is an immutable data type.

Here's an example of how you might modify the transform_input method to avoid this error:

def transform_input(self, prompts: List[str], model_kwargs: Dict) -> bytes:
    # Convert lists to tuples before using them as keys
    model_kwargs = {tuple(k) if isinstance(k, list) else k: v for k, v in model_kwargs.items()}
    input_str = json.dumps({tuple(prompts): prompts, **model_kwargs})
    return input_str.encode('utf-8')

And similarly for the transform_output method:

def transform_output(self, output: bytes) -> List[List[float]]:
    response_json = json.loads(output.read().decode("utf-8"))
    # Convert tuples back to lists after retrieving them from the dictionary
    response_json = {list(k) if isinstance(k, tuple) else k: v for k, v in response_json.items()}
    return response_json["vectors"]

Please note that this is a general solution and might need to be adapted based on the specific structure and requirements of your code.

As for the expected input and output types for the transform_input and transform_output methods in the EmbeddingsContentHandler and LLMContentHandler classes, the EmbeddingsContentHandler class's transform_input method expects a list of strings (List[str]) and a dictionary (Dict) as input and returns bytes. The transform_output method expects bytes as input and returns a list of lists of floats (List[List[float]]). The LLMContentHandler class is not defined in the provided context, so I cannot provide the expected input and output types for its transform_input and transform_output methods.

I hope this helps! If you have any further questions or if something is unclear, please let me know.

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a ๐Ÿ‘ if this is helpful and ๐Ÿ‘Ž if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

Vignesh1489 commented 10 months ago

@dosu-bot

Single content handler for embedding & LLM text generation will work fine or not? Also please debug the code to deploy in aws Lambda as api.

class ContentHandler(EmbeddingsContentHandler): content_type = "application/json" accepts = "application/json"

def transform_input(self, inputs: list[str], model_kwargs: Dict) -> bytes:
    input_str = json.dumps({"text_inputs": inputs, **model_kwargs})
    return input_str.encode("utf-8")

def transform_output(self, output: bytes) -> List[List[float]]:
    response_json = json.loads(output.read().decode("utf-8"))
    return response_json["embedding"]

content_handler = ContentHandler()

embeddings_init = SagemakerEndpointEmbeddings( endpoint_name="endpoint", region_name="us-east-1", content_handler=content_handler, )

def build_chain():

Initialize Sentence Transformer

embeddings = embeddings_init
print(embeddings)

# Initialize PGVector index
vector_db = PGVector(
    embedding_function=embeddings,
    collection_name='CSE_runbooks',
    connection_string=CONNECTION_STRING,
)
print("**Invoking PGVector")

# Custom ContentHandler to handle input and output to the SageMaker Endpoint
class LlamaChatContentHandler(LLMContentHandler):
    content_type = "application/json"
    accepts = "application/json"

    def transform_input(self, inputs: str, model_kwargs: Dict = {}) -> bytes:
        payload = {
            "inputs": f"[INST] <<SYS>> You are a helpful, respectful, and honest assistant. Answer exactly in detail from the context. Answer the question below from the context below <<SYS>> {inputs} [/INST] ",
            "parameters": {"max_new_tokens":2000, "top_p":0.9, "temperature":0.1}
        }
        input_str = ' '.join(inputs)
        input_str = json.dumps(payload)
        print(payload)
        return input_str.encode("utf-8")

    def transform_output(self, output: bytes) -> str:
        response_json = json.loads(output.read().decode("utf-8"))
        content = response_json[0]["generated_text"]
        return content

# Initialize SagemakerEndpoint
print("Invoking LLM SageMaker Endpoint")
llm = SagemakerEndpoint(
    endpoint_name=LLAMA2_ENDPOINT,
    region_name=AWS_REGION,
    retriever=PGVector.as_retriever(vector_db, search_type="similarity", zsearch_kwargs={"k": 2}),
    content_handler=LlamaChatContentHandler(),
    callbacks=[StreamingStdOutCallbackHandler()],
    endpoint_kwargs={"CustomAttributes": "accept_eula=true"},
)
print(llm)
return llm

def run_chain(chain, prompt: str):

Ensure 'result' is defined before accessing its elements

result, sources_document = chain({"question": prompt})

# Access source documents from 'result' if available
sources_docs = result.get("source_documents", [])

# Update source documents
modified_source_documents = []
for doc in sources_docs:
    metadata = doc.metadata
    if 'source' in metadata:
        original_source = metadata['source']
        if original_source.endswith('.md'):
            # Modify the source value
            modified_source = "https://github.com/main/" + original_source.split('/')[-1]
            metadata['source'] = modified_source
    modified_source_documents.append(doc)

# Get modified source links
sources_document = [doc.metadata.get('source', '') for doc in modified_source_documents]

return result, sources_document

def lambda_handler(event, context):

Extract user input from the Lambda event

user_input = event.get("question")

# Build the Langchain chatbot chain
chain = build_chain()

# Run the Langchain chain with user input
result, sources_document = run_chain(chain, user_input)

# Prepare the Lambda response
response = {
    "statusCode": 200,
    "body": json.dumps({"response": result, "source_document": sources_document}),
}

return response
dosubot[bot] commented 10 months ago

๐Ÿค–

Hello @dosubot[bot]! I'm Dosu, a friendly bot here to assist you. I can help you debug issues, answer your queries, and guide you to become a contributor while we're waiting for a human maintainer to respond. Let's tackle some code together! ๐Ÿš€

The "unhashable type: 'list'" error typically occurs when you try to use a mutable data type, such as a list, as a key in a dictionary or as an element in a set. In Python, only immutable data types can be hashed and used as keys in a dictionary or elements in a set.

In your code, the error might be occurring because you're trying to use a list as a key in the model_kwargs or endpoint_kwargs dictionaries, or in the transform_input or transform_output methods of the EmbeddingsContentHandler class.

To resolve this issue, you should ensure that you're only using immutable data types (like strings, integers, tuples, etc.) as keys in your dictionaries. If you need to use a list as a key, you could convert it to a tuple, which is an immutable data type.

Here's an example of how you might modify the transform_input method to avoid this error:

def transform_input(self, prompts: List[str], model_kwargs: Dict) -> bytes:
    # Convert lists to tuples before using them as keys
    model_kwargs = {tuple(k) if isinstance(k, list) else k: v for k, v in model_kwargs.items()}
    input_str = json.dumps({prompts: prompts, **model_kwargs})
    return input_str.encode('utf-8')

And similarly for the transform_output method:

def transform_output(self, output: bytes) -> List[List[float]]:
    response_json = json.loads(output.read().decode("utf-8"))
    # Convert tuples back to lists after retrieving them from the dictionary
    response_json = {list(k) if isinstance(k, tuple) else k: v for k, v in response_json.items()}
    return response_json["vectors"]

Please note that this is a general solution and might need to be adapted based on the specific structure and requirements of your code.

Regarding the expected type for the prompts parameter in the transform_input method of the EmbeddingsContentHandler class, it should be a list of strings (List[str]). This is evident from the example provided in the context:

class ContentHandler(EmbeddingsContentHandler):
    content_type = "application/json"
    accepts = "application/json"

    def transform_input(self, prompts: List[str], model_kwargs: Dict) -> bytes:
        input_str = json.dumps({prompts: prompts, **model_kwargs})
        return input_str.encode('utf-8')

In the transform_input method, prompts is annotated as List[str], indicating that it should be a list of strings.

I hope this helps! If you have any other questions or need further clarification, feel free to ask.

-Dosu

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a ๐Ÿ‘ if this is helpful and ๐Ÿ‘Ž if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

Vignesh1489 commented 10 months ago

@dosu-bot

can you please debug the code to get the input from handler and execute llama2 llm via sagemaker endpoint.

`import json import json import os from typing import Dict from dotenv import load_dotenv from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler

from langchain.chains import ConversationalRetrievalChain

from langchain.chains.retrieval_qa.base import RetrievalQA from langchain.llms.sagemaker_endpoint import LLMContentHandler from langchain.vectorstores.pgvector import PGVector from langchain.llms import SagemakerEndpoint from Embeddings import embeddings_init

Load environment variables

load_dotenv()

Define constants

AWS_REGION = os.environ.get('AWS_REGION', 'us-east-1') LLAMA2_ENDPOINT = os.environ.get('LLAMA2_ENDPOINT', 'xxxxxxxxx') MAX_HISTORY_LENGTH = int(os.environ.get('MAX_HISTORY_LENGTH', '10'))

CONNECTION_STRING = PGVector.connection_string_from_db_params( driver=os.environ.get("PGVECTOR_DRIVER", "psycopg2"), host=os.environ.get("PGVECTOR_HOST", "postgres.xxxxxxx.us-east-1.rds.amazonaws.com"), port=int(os.environ.get("PGVECTOR_PORT", "5432")), database=os.environ.get("PGVECTOR_DATABASE", "postgres"), user=os.environ.get("PGVECTOR_USER", "postgres"), password=os.environ.get("PGVECTOR_PASSWORD", "xxxxx"), )

def handler(event, context):

print("Extract user input from the Lambda event")
user_input = event.get("question")

try:
    print("Initialize Sentence Transformer")
    embeddings = embeddings_init
    print("Initialize PGVector index")
    vector_db = PGVector(
        embedding_function=embeddings,
        collection_name='xxxxxx',
        connection_string=CONNECTION_STRING,
    )
    print("**Invoking PGVector")

    # Rest of the code remains unchanged...

except Exception as e:
    print(f"Error initializing PGVector index: {e}")
    raise  # Re-raise the exception to propagate it up

# Custom ContentHandler to handle input and output to the SageMaker Endpoint
class LlamaChatContentHandler(LLMContentHandler):
    content_type = "application/json"
    accepts = "application/json"

    def transform_input(self, inputs: str, model_kwargs: Dict = {}) -> bytes:
        payload = {
            "inputs": f"[INST] <<SYS>> You are a helpful, respectful, and honest assistant. Answer exactly in detail from the context. Answer the question below from the context below <<SYS>> {inputs} [/INST] ",
            "parameters": {"max_new_tokens": 2000, "top_p": 0.9, "temperature": 0.1}}
        input_str = ' '.join(inputs)
        input_str = json.dumps(payload)
        print(payload)
        return input_str.encode("utf-8")

    def transform_output(self, output: bytes) -> str:
        response_json = json.loads(output.read().decode("utf-8"))
        content = response_json[0]["generated_text"]
        return content

# Initialize SagemakerEndpoint
print("Invoking LLM SageMaker Endpoint")

llm = SagemakerEndpoint(
    endpoint_name=LLAMA2_ENDPOINT,
    region_name=AWS_REGION,
    content_handler=LlamaChatContentHandler(),
    callbacks=[StreamingStdOutCallbackHandler()],
    endpoint_kwargs={"CustomAttributes": "accept_eula=true"},
)
# Langchain chain for Conversation
qa_chain = RetrievalQA.from_chain_type(llm=llm, 
                              chain_type="stuff", 
                              retriever=PGVector.as_retriever(vector_db, search_type="similarity", zsearch_kwargs={"k": 2}),
                              return_source_documents=True)

llm_response = qa_chain(user_input)

print(llm_response)

`

dosubot[bot] commented 10 months ago

๐Ÿค–

Based on the context provided, your Python script seems to be correctly using the LangChain framework to get input from a handler and execute a llama2 LLM via a Sagemaker endpoint.

The SagemakerEndpoint parameters are correctly set up, including endpoint_name and region_name. The ContentHandler class, which is used to transform the input and output for the LLM, is also correctly defined.

The load_qa_chain function is used to load the question answering chain with the Sagemaker endpoint and the prompt template. Finally, the chain is correctly called with the input documents and the question, and asks for only the outputs to be returned.

Here are the key parts of the script that demonstrate this:

  1. Setting up the SagemakerEndpoint parameters:
SagemakerEndpoint(
    endpoint_name=LLAMA2_ENDPOINT,
    region_name=AWS_REGION,
    content_handler=LlamaChatContentHandler(),
    callbacks=[StreamingStdOutCallbackHandler()],
    endpoint_kwargs={"CustomAttributes": "accept_eula=true"},
)
  1. Setting up the ContentHandler class:
class LlamaChatContentHandler(LLMContentHandler):
    content_type = "application/json"
    accepts = "application/json"

    def transform_input(self, inputs: str, model_kwargs: Dict = {}) -> bytes:
        payload = {
            "inputs": f"[INST] <<SYS>> You are a helpful, respectful, and honest assistant. Answer exactly in detail from the context. Answer the question below from the context below <<SYS>> {inputs} [/INST] ",
            "parameters": {"max_new_tokens": 2000, "top_p": 0.9, "temperature": 0.1}}
        input_str = ' '.join(inputs)
        input_str = json.dumps(payload)
        print(payload)
        return input_str.encode("utf-8")

    def transform_output(self, output: bytes) -> str:
        response_json = json.loads(output.read().decode("utf-8"))
        content = response_json[0]["generated_text"]
        return content
  1. Loading the question answering chain:
qa_chain = RetrievalQA.from_chain_type(llm=llm, 
                              chain_type="stuff", 
                              retriever=PGVector.as_retriever(vector_db, search_type="similarity", zsearch_kwargs={"k": 2}),
                              return_source_documents=True)
  1. Calling the chain:
llm_response = qa_chain(user_input)

The LLMContentHandler class in the LangChain framework is used to handle the transformation of input and output for the LLM. It interacts with the SagemakerEndpoint by transforming the input into a format that the LLM can understand and transforming the output from the LLM into a format that can be used by the rest of the application.

For more information, you can refer to the Sagemaker integration notebook in the LangChain repository.

If you're still facing issues or if there's a specific error message you're encountering, please provide more details so I can assist you better.

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a ๐Ÿ‘ if this is helpful and ๐Ÿ‘Ž if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

Vignesh1489 commented 10 months ago

@dosu-bot Not sure how to add content handler for the normal retrievalQA. Here is my code.

`import streamlit as st from langchain.vectorstores.milvus import Milvus from langchain.chains.retrieval_qa.base import RetrievalQA from langchain.prompts.prompt import PromptTemplate from langchain.llms import CTransformers from langchain.embeddings import HuggingFaceEmbeddings

prompt_template = """ Use the following pieces of information to answer the user's question. If you don't know the answer, just say that you don't know, don't try to make up an answer.

Context: {context} Question: {question}

Only return the helpful answer below and nothing else. Helpful answer: """

PROMPT = PromptTemplate(template=prompt_template, input_variables=["context", "question"]) chain_type_kwargs = {"prompt": PROMPT}

def download_hugging_face_embeddings(): embeddings = HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2') return embeddings

def initialize_milvus():

Initialize Milvus vector store

milvus_host = "127.0.0.1"
milvus_port = "19530"
collection_name = "csr_runbooks"

embeddings = download_hugging_face_embeddings()
vector_db = Milvus(embedding_function=embeddings, collection_name=collection_name, connection_args={"host": milvus_host, "port": milvus_port})
retriever = Milvus.as_retriever(vector_db, search_type="similarity", search_kwargs={'k': 3})

return retriever

def update_source_documents(source_documents): modified_documents = [] for document in source_documents: metadata = document.metadata if 'source' in metadata: original_source = metadata['source'] if original_source.endswith('.md'):

Modify the source value

            modified_source = "https://github.com/nike-map/runbooks_cse/blob/main/runbooks/snowflake/Snowflake_L2_Support/" + original_source.split('/')[-1]
            metadata['source'] = modified_source
    modified_documents.append(document)
return modified_documents

def main(): retriever = initialize_milvus()

# Load your llama model here
llm = CTransformers(model="/Users/VDhana/Downloads/llama-2-7b-chat.ggmlv3.q4_0.bin",
                    model_type="llama",
                    config={'max_new_tokens': 512, 'temperature': 0.1})

# Create a RetrievalQA instance
query = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever, return_source_documents=True, chain_type_kwargs=chain_type_kwargs)

# Define Streamlit input field
user_input = st.text_input("Enter your query:")

# Check if user wants to exit
if user_input.lower() == 'exit':
    st.write('Exiting')
    st.stop()

# Check if user input is empty
if user_input:
    result = query({"query": user_input})

    # Update the source documents
    result["source_documents"] = update_source_documents(result["source_documents"])

    st.write("Response:", result["result"])
    st.write("Source Documents:", result["source_documents"])

if name == 'main': main()`

dosubot[bot] commented 10 months ago

๐Ÿค–

To add a content handler for the RetrievalQA in the LangChain framework, you can create a subclass of RetrievalQA and override the _get_docs and _aget_docs methods. These methods are responsible for retrieving the relevant documents based on the input question.

Here is an example of how you might do this:

from langchain.chains.retrieval_qa.base import RetrievalQA
from langchain_core.documents import Document
from langchain.callbacks.manager import CallbackManagerForChainRun, AsyncCallbackManagerForChainRun
from typing import List

class CustomRetrievalQA(RetrievalQA):
    def _get_docs(
        self,
        question: str,
        *,
        run_manager: CallbackManagerForChainRun,
    ) -> List[Document]:
        """Custom method to get docs."""
        # Implement your custom logic here
        pass

    async def _aget_docs(
        self,
        question: str,
        *,
        run_manager: AsyncCallbackManagerForChainRun,
    ) -> List[Document]:
        """Custom async method to get docs."""
        # Implement your custom logic here
        pass

In the above code, replace # Implement your custom logic here with your own logic for retrieving documents. The run_manager parameter is an instance of CallbackManagerForChainRun or AsyncCallbackManagerForChainRun that you can use to manage callbacks during the execution of the chain.

You can find more details about the RetrievalQA class in the LangChain repository.

The RetrievalQA class in the LangChain framework is used for creating a question-answering system. It retrieves relevant information from a given set of documents based on the question asked. You can find an example of how to use the RetrievalQA class in the LangChain integration tests.

I hope this helps! If you have any other questions, feel free to ask.

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a ๐Ÿ‘ if this is helpful and ๐Ÿ‘Ž if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

Vignesh1489 commented 10 months ago

@dosu-bot facing the following error

Error: value is not a valid dict (type=type_error.dict) Traceback (most recent call last): ย ย File "/var/task/lambda.py", line 82, in handler ย ย ย ย qa_chain = RetrievalQA.from_chain_type(llm=llm, ย ย File "/var/lang/lib/python3.11/site-packages/langchain/chains/retrieval_qa/base.py", line 100, in from_chain_type ย ย ย ย combine_documents_chain = load_qa_chain( ย ย File "/var/lang/lib/python3.11/site-packages/langchain/chains/question_answering/init.py", line 249, in load_qa_chain ย ย ย ย return loader_mapping[chain_type]( ย ย File "/var/lang/lib/python3.11/site-packages/langchain/chains/question_answering/init.py", line 73, in _load_stuff_chain ย ย ย ย llm_chain = LLMChain( ย ย File "/var/lang/lib/python3.11/site-packages/langchain/load/serializable.py", line 97, in init ย ย ย ย super().init(**kwargs) ย ย File "pydantic/main.py", line 341, in pydantic.main.BaseModel.init

Code: ` def handler(event, context):

print("Extract user input from the Lambda event")
inputs = event.get("question")

print("Initialize Sentence Transformer")
embeddings = embeddings_init

print("Initialize PGVector index")
vector_db = PGVector(
        embedding_function=embeddings,
        collection_name='CSE_runbooks',
        connection_string=CONNECTION_STRING,
    )
print("**Invoking PGVector")

print("Custom ContentHandler to handle input and output to the SageMaker Endpoint")
class LlamaChatContentHandler(LLMContentHandler):
    content_type = "application/json"
    accepts = "application/json"

    def transform_input(self, inputs: str, model_kwargs: Dict = {}) -> bytes:
        payload = {
            "inputs": f"[INST] <<SYS>> You are a helpful, respectful, and honest assistant. Answer exactly in detail from the context. Answer the question below from the context below <<SYS>> {inputs} [/INST] ",
            "parameters": {"max_new_tokens": 2000, "top_p": 0.9, "temperature": 0.1}}
        input_str = ' '.join(inputs)
        input_str = json.dumps(payload)
        print("inside content handler")
        print(payload)
        return input_str.encode("utf-8")

    def transform_output(self, output: bytes) -> str:
        response_json = json.loads(output.read().decode("utf-8"))
        content = response_json[0]["generated_text"]
        return content

# Initialize SagemakerEndpoint
print("Invoking LLM SageMaker Endpoint")

llm = SagemakerEndpoint(
    endpoint_name=LLAMA2_ENDPOINT,
    region_name=AWS_REGION,
    content_handler=LlamaChatContentHandler(),
    callbacks=[StreamingStdOutCallbackHandler()],
    endpoint_kwargs={"CustomAttributes": "accept_eula=true"},
)
print(llm)
retriever = PGVector.as_retriever(vector_db, search_type="similarity", search_kwargs={"k": 2}),
print(retriever)
print("Langchain chain for Conversation")
qa_chain = RetrievalQA.from_chain_type(llm=llm, 
                              chain_type="stuff", 
                              retriever=retriever,
                              chain_type_kwargs={"prompt": inputs},
                              return_source_documents=True)
print(qa_chain)

`

dosubot[bot] commented 10 months ago

๐Ÿค–

Based on the error message you're receiving, it seems like the issue is related to the retriever parameter in the RetrievalQA.from_chain_type method. The error "value is not a valid dict (type=type_error.dict)" typically occurs when you're trying to initialize a Pydantic model (in this case, a RetrievalQA instance) with a value that's not a dictionary.

In the RetrievalQA class, the retriever field is defined as a BaseRetriever instance and is excluded from the model schema. This means that when you're initializing a RetrievalQA instance, you need to provide a BaseRetriever instance for the retriever field.

In your code, you're passing a tuple to the retriever parameter because of the trailing comma in this line:

retriever = PGVector.as_retriever(vector_db, search_type="similarity", search_kwargs={"k": 2}),

This is likely causing the error. You should remove the trailing comma to pass the BaseRetriever instance directly:

retriever = PGVector.as_retriever(vector_db, search_type="similarity", search_kwargs={"k": 2})

Also, ensure that the LlamaChatContentHandler is a subclass of BaseRetriever and that you're passing an instance of it to the RetrievalQA initializer.

Here's an example of how you might do this:

from langchain.llms import OpenAI
from langchain.chains import RetrievalQA
from langchain_core.retrievers import BaseRetriever

class LlamaChatContentHandler(BaseRetriever):
    # Your custom implementation here

retriever = LlamaChatContentHandler(sagemaker_endpoint=...)
retrievalQA = RetrievalQA.from_llm(llm=OpenAI(), retriever=retriever)

In this example, LlamaChatContentHandler is a subclass of BaseRetriever that you've implemented to handle content from a SagemakerEndpoint. You create an instance of LlamaChatContentHandler with your SagemakerEndpoint, and then pass that instance to the RetrievalQA.from_llm method.

For more details, you can refer to the RetrievalQA class definition in the LangChain repository.

I hope this helps! If you have any other questions or if something is unclear, please let me know.

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a ๐Ÿ‘ if this is helpful and ๐Ÿ‘Ž if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

Vignesh1489 commented 10 months ago

@dosu-bot can you please provide me a code with lambda input, content handler for sagemaker invoke endpoint with ConversationalRetrievalChain vectordb and return response to lambda

dosubot[bot] commented 10 months ago

๐Ÿค–

Based on the information provided, you can use the ConversationalRetrievalChain in the LangChain framework to develop a Lambda function that invokes a Sagemaker endpoint. Here's a basic example of how you might structure your Lambda function:

import boto3
import json
from langchain.chains import ConversationalRetrievalChain

def lambda_handler(event, context):
    # Initialize your ConversationalRetrievalChain
    chain = ConversationalRetrievalChain(...)

    # Extract the chat history and new question from the event
    chat_history = event['chat_history']
    question = event['question']

    # Use the chain to generate a response
    response = chain(chat_history, question)

    # Invoke the Sagemaker endpoint
    client = boto3.client('sagemaker-runtime')
    response = client.invoke_endpoint(
        EndpointName='YourSagemakerEndpoint',
        Body=json.dumps(response),
        ContentType='application/json',
    )

    # Extract the result from the Sagemaker response
    result = json.loads(response['Body'].read().decode())

    # Return the result to the Lambda function
    return {
        'statusCode': 200,
        'body': json.dumps(result)
    }

In this example, the lambda_handler function is the entry point for your Lambda function. It takes an event and a context as input. The event should be a dictionary that contains the chat_history and question for the ConversationalRetrievalChain.

The ConversationalRetrievalChain is used to generate a response based on the chat_history and question. This response is then passed to a Sagemaker endpoint using the boto3 client.

The result from the Sagemaker endpoint is extracted and returned by the Lambda function. Please replace 'YourSagemakerEndpoint' with the name of your actual Sagemaker endpoint.

Please note that this is a basic example and you might need to adjust it according to your specific use case. For example, you might need to handle errors and edge cases, or adjust how the ConversationalRetrievalChain is initialized and used.

For more information on how to use the ConversationalRetrievalChain, you can refer to the LangChain repository.

For more information on how to invoke a Sagemaker endpoint from a Lambda function, you can refer to the AWS documentation.

I hope this helps! If you have any further questions, feel free to ask.

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a ๐Ÿ‘ if this is helpful and ๐Ÿ‘Ž if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

Vignesh1489 commented 10 months ago

@dosu-bot

please provide me the lambda function for the following code with chat history & prompt along with chain

` import json import os from typing import Dict from dotenv import load_dotenv from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler from langchain.chains import ConversationalRetrievalChain from langchain.embeddings import SentenceTransformerEmbeddings from langchain.llms.sagemaker_endpoint import LLMContentHandler from langchain.vectorstores import FAISS from langchain.vectorstores.pgvector import PGVector from langchain.prompts import PromptTemplate from langchain.llms import SagemakerEndpoint from Embeddings import embeddings_init

from Stream1 import embeddings_init

Load environment variables

load_dotenv()

Define constants

AWS_REGION = os.environ.get('AWS_REGION', 'us-east-1') EMBEDDING_MODEL = os.environ.get('EMBEDDING_MODEL', 'all-MiniLM-L6-v2') LLAMA2_ENDPOINT = os.environ.get('LLAMA2_ENDPOINT', 'xxxxxxxxx') MAX_HISTORY_LENGTH = int(os.environ.get('MAX_HISTORY_LENGTH', '10'))

CONNECTION_STRING = PGVector.connection_string_from_db_params( driver=os.environ.get("PGVECTOR_DRIVER", "psycopg2"), host=os.environ.get("PGVECTOR_HOST", "postgres.xxxxxxxx.us-east-1.rds.amazonaws.com"), port=int(os.environ.get("PGVECTOR_PORT", "5432")), database=os.environ.get("PGVECTOR_DATABASE", "postgres"), user=os.environ.get("PGVECTOR_USER", "xxxxx"), password=os.environ.get("PGVECTOR_PASSWORD", "xxxxxx"), )

def build_chain():

Initialize Sentence Transformer

embeddings = embeddings_init
print(embeddings)
# Initialize PGVector index
vector_db = PGVector(
    embedding_function=embeddings,
    collection_name='xxxxxx',
    connection_string=CONNECTION_STRING,
)
print("**Invoking PGVector")

# Custom ContentHandler to handle input and output to the SageMaker Endpoint
class LlamaChatContentHandler(LLMContentHandler):
    content_type = "application/json"
    accepts = "application/json"

    def transform_input(self, inputs: str, model_kwargs: Dict = {}) -> bytes:
        payload = {
    "inputs": f"[INST] <<SYS>> You are a helpful, respectful, and honest assistant. Answer exactly in detail from the context. Answer the question below from the context below <<SYS>> {inputs} [/INST] ",
            "parameters": {"max_new_tokens":1500, "top_p":0.9, "temperature":0.1}}
        input_str = ' '.join(inputs)
        input_str = json.dumps(payload)
        print(payload)
        return input_str.encode("utf-8")

    def transform_output(self, output: bytes) -> str:
        response_json = json.loads(output.read().decode("utf-8"))
        content = response_json[0]["generated_text"]
        return content

# Initialize SagemakerEndpoint
print("Invoking LLM SageMaker Endpoint")
llm = SagemakerEndpoint(
    endpoint_name=LLAMA2_ENDPOINT,
    region_name=AWS_REGION,
    content_handler=LlamaChatContentHandler(),
    callbacks=[StreamingStdOutCallbackHandler()],
    endpoint_kwargs={"CustomAttributes": "accept_eula=true"},
)
print(llm)

def get_chat_history(inputs) -> str:
    res = []
    for _i in inputs:
        if _i.get("role") == "user":
            user_content = _i.get("content")
        if _i.get("role") == "assistant":
            assistant_content = _i.get("content")
            res.append(f"user:{user_content}\nassistant:{assistant_content}")
    return "\n".join(res)

condense_qa_template = """
Given the following conversation and a follow up question, rephrase the follow up question
to be a standalone question.

Chat History:
{chat_history}
Follow Up Input: {question}
Standalone question:"""
standalone_question_prompt = PromptTemplate.from_template(
    condense_qa_template,
)

# Langchain chain for Conversation
qa = ConversationalRetrievalChain.from_llm(
    llm=llm,
    retriever=PGVector.as_retriever(vector_db, search_type="similarity", zsearch_kwargs={"k": 2}),
    condense_question_prompt=standalone_question_prompt,
    return_source_documents=True,
    get_chat_history=get_chat_history,
    # verbose=True,
)
return qa

def run_chain(chain, prompt: str, history=[]):

Ensure 'result' is defined before accessing its elements

    result = chain({"question": prompt, "chat_history": history})

    # Access source documents from 'result' if available
    sources_docs = result.get("source_documents", [])

    # Update source documents
    modified_source_documents = []
    for doc in sources_docs:
        metadata = doc.metadata
        if 'source' in metadata:
            original_source = metadata['source']
            if original_source.endswith('.md'):
                # Modify the source value
                modified_source = "https://github.com/nike-eap-cse/Q-A-Runbooks/blob/main/" + original_source.split('/')[-1]
                metadata['source'] = modified_source
        modified_source_documents.append(doc)

    # Get modified source links
    sources_links = [doc.metadata.get('source', '') for doc in modified_source_documents]

    return result, sources_links

`

dosubot[bot] commented 10 months ago

๐Ÿค–

Hey there @dosubot[bot]! ๐Ÿค– I'm Dosu, your friendly neighborhood bot. I'm here to assist you in troubleshooting, answering any questions you may have, and even guiding you on your journey to becoming a contributor. While we wait for a human maintainer, feel free to lean on me for support! Let's tackle your issue together.

Based on the information you've provided and the structure of the LangChain framework, you can create a Lambda function that uses a content handler to invoke a Sagemaker endpoint with a ConversationalRetrievalChain and a VectorDB. Here's a simplified example:

from langchain_core.runnables import LambdaFunction
from langchain_core.llms import SagemakerEndpoint
from langchain_core.content_handlers import ContentHandler
from langchain_core.chains import ConversationalRetrievalChain
from langchain_core.vector_dbs import VectorDB

# Define your content handler
class MyContentHandler(ContentHandler):
    content_type = "application/json"
    accepts = "application/json"

    def transform_input(self, prompt: str, model_kwargs: Dict) -> bytes:
        input_str = json.dumps({"inputs": prompt, "parameters": model_kwargs})
        return input_str.encode("utf-8")

    def transform_output(self, output: bytes) -> str:
        response_json = json.loads(output.read().decode("utf-8"))
        return response_json[0]["generated_text"]

# Initialize your content handler
content_handler = MyContentHandler()

# Initialize your Sagemaker endpoint
sagemaker_endpoint = SagemakerEndpoint(
    endpoint_name="your-endpoint-name",
    credentials_profile_name="your-credentials-profile-name",
    region_name="your-region-name",
    model_kwargs={"temperature": 1e-10},
    content_handler=content_handler,
)

# Initialize your ConversationalRetrievalChain
conversational_retrieval_chain = ConversationalRetrievalChain(
    llm=sagemaker_endpoint,
    vector_db=VectorDB("your-vector-db-name"),
)

# Define your Lambda function
lambda_func = LambdaFunction(
    func=lambda input_dict: conversational_retrieval_chain.run(input_dict['prompt']),
)

# Now you can use lambda_func.run() to execute your Lambda function
response = lambda_func.run({'prompt': 'your-prompt'})

In this example, lambda_func is a LambdaFunction instance that takes a dictionary with a 'prompt' key as input and returns the result of running the ConversationalRetrievalChain. The ConversationalRetrievalChain uses the SagemakerEndpoint and VectorDB instances, which are initialized with the MyContentHandler and "your-vector-db-name", respectively.

Please replace "your-endpoint-name", "your-credentials-profile-name", "your-region-name", "your-vector-db-name", and 'your-prompt' with your actual values.

This is a simplified example and the actual code may need to be adjusted based on your specific requirements. If you encounter any issues or need further assistance, please provide more details about the problem you're facing.

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a ๐Ÿ‘ if this is helpful and ๐Ÿ‘Ž if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.

Vignesh1489 commented 10 months ago

@dosu-bot

getting request timed out error while triggering from lambda

` PROMPT = PromptTemplate( template=prompt_template, input_variables=["context", "question"] )

    class ContentHandler(LLMContentHandler):
        content_type = "application/json"
        accepts = "application/json"

        def transform_input(self, inputs: str, model_kwargs: Dict = {}) -> bytes:
            input_str = json.dumps(
                {"inputs": [
                    [
                        {
                            "role": "system",
                            "content": ""
                        },
                        {
                            "role": "user",
                            "content": inputs
                        }
                    ]],
                 "parameters": {**model_kwargs}
                 })
            return input_str.encode("utf-8")

        def transform_output(self, output: bytes) -> str:
            response_json = json.loads(output.read().decode("utf-8"))
            content = response_json[0]["generated_text"]
            return content

    content_handler = ContentHandler()

    print("Initialize SagemakerEndpoint")
    print("Invoking LLM SageMaker Endpoint")
    llm = SagemakerEndpoint(
        endpoint_name=LLAMA2_ENDPOINT,
        region_name=AWS_REGION,
        model_kwargs={"max_new_tokens": 1500, "top_p": 0.9, "temperature": 0.1},
        content_handler=content_handler,
        endpoint_kwargs={"CustomAttributes": "accept_eula=true"},
    )
    print(llm)

    print("Langchain chain for Conversation")
    retriever = PGVector.as_retriever(vector_db, search_type="similarity", search_kwargs={"k": 2})
    qa = RetrievalQA.from_chain_type(
        llm=llm,
        retriever=retriever,
        chain_type="stuff",
        return_source_documents=True,
        verbose=True,
        chain_type_kwargs={"prompt": PROMPT}
    )
    print(qa)

    print("invoking run chain")
    query = event.get('prompt')
    result = qa(query)
    print(result)

    return {
        'statusCode': 200,
        'response': result
    }

except Exception as e:
    print(f"Error during Langchain chain initialization or execution: {e}")
    return {
        'statusCode': 500,
        'response': f'Error: {str(e)}'
    }

`

dosubot[bot] commented 7 months ago

Hi, @Vignesh1489,

I'm helping the LangChain team manage their backlog and am marking this issue as stale. From what I understand, the issue involves a dict validation error for the RetrievalQA component when using a Sagemaker endpoint with the LLAMA2 llm model. It seems that the issue was resolved by removing the trailing comma in the code and ensuring that the LlamaChatContentHandler is a subclass of BaseRetriever and that an instance of it is passed to the RetrievalQA initializer.

Could you please confirm if this issue is still relevant to the latest version of the LangChain repository? If it is, please let the LangChain team know by commenting on the issue. Otherwise, feel free to close the issue yourself, or the issue will be automatically closed in 7 days. Thank you!

Vignesh1489 commented 4 months ago

@dosu-bot Streaming is not supported with streamlit run.chain

Here is the code

import os

import streamlit as st
from dotenv import load_dotenv

import getLLMResponse1 as llama2

# Get Env Variables

load_dotenv() # load the values for environment variables from the .env file

MAX_HISTORY_LENGTH=os.environ.get('MAX_HISTORY_LENGTH', '5') # Determine how many conversation to be stored in the chat history

###Set Streamlit Session State Variables:###
st.session_state["llm_app"] = llama2
st.session_state["llm_chain"] = llama2.build_chain()

###Initial UI configuration:###
st.set_page_config(page_title="CSE-ChatBot", page_icon="๐Ÿš€")

def render_app():
    # Reduce font sizes for input text boxes. Reduce button sizes too.
    custom_css = """
        <style>
            .stTextArea textarea {font-size: 13px;}
            div[data-baseweb="select"] > div {font-size: 13px !important;}
        </style>
        <style>
        button {
            height: 30px !important;
            width: 150px !important;
            padding-top: 10px !important;
            padding-bottom: 10px !important;
        }
        </style>
    """
    st.markdown(custom_css, unsafe_allow_html=True)

    # Set config for a cleaner menu, footer & background:
    hide_streamlit_style = """
                <style>
                #MainMenu {visibility: hidden;}
                footer {visibility: hidden;}
                </style>
                """
    st.markdown(hide_streamlit_style, unsafe_allow_html=True)

    st.subheader("Hello ๐Ÿ‘‹ I'm your CSE ChatBot๐Ÿ˜€")

    # Accept user input
    # container for the chat history
    st.container()
    # container for the user input
    st.container()
    # Set up/Initialize Session State variables:
    if "chat_dialogue" not in st.session_state:
        st.session_state["chat_dialogue"] = []
    if "llm" not in st.session_state:
        st.session_state["llm"] = llama2
        st.session_state["llm_chain"] = llama2.build_chain()
    # Add the "Clear Chat History" button to the sidebar

    def clear_history():
        st.session_state["chat_dialogue"] = []

    # Display chat messages from history on app rerun
    for message in st.session_state.chat_dialogue:
        with st.chat_message(message["role"]):
            st.markdown(message["content"])

    if len(st.session_state.chat_dialogue) == int(MAX_HISTORY_LENGTH):
        st.session_state.chat_dialogue = st.session_state.chat_dialogue[:-1]
        clear_history()

    if prompt := st.chat_input("Type your question here..."):
        # Add user message to chat history
        st.session_state.chat_dialogue.append({"role": "user", "content": prompt})
        # Display user message in chat message container
        with st.chat_message("user"):
            st.markdown(prompt)
        # Display message from LLM / assistant
        with st.chat_message("assistant"):
            answer_placeholder = st.empty()
            answer = ""
            for dict_message in st.session_state.chat_dialogue:
                if dict_message["role"] == "user":
                    string_dialogue = "User: " + dict_message["content"] + "\n\n"
                else:
                    string_dialogue = "Assistant: " + dict_message["content"] + "\n\n"
            llm_chain = st.session_state["llm_chain"]
            chain = st.session_state["llm_app"]
            try:
                output = chain.run_chain(llm_chain, prompt)
                if isinstance(output, tuple) and len(output) > 0 and isinstance(output[0], dict):
                     answer = output[0].get("answer")
                else:
    # Handle the case where output is not as expected
                     answer = "Unexpected output format"
            except Exception as e:
                answer = f"An error occurred: {str(e)}"
                output = {}
                output["answer"] = "I'm sorry I'm not unable to respond to your question ๐Ÿ˜”"
            if 'source_documents' in output:
                with st.expander("Sources"):
                    for _sd in output.get('source_documents'):
                        _sd_metadata = _sd.metadata
                        source = _sd_metadata.get('source').replace('./aws_docs/sagemaker/', 'https://docs.aws.amazon.com/sagemaker/latest/dg/')
                        title = _sd_metadata.get('title')
                        st.write(f"{title} --> {source}")
            answer_placeholder.markdown(answer + "โ–Œ")    
            # Add assistant response to chat history
            st.session_state.chat_dialogue.append({"role": "assistant", "content": answer})
        col1, col2 = st.columns([10, 4])
        with col1:
            pass
        with col2:
            st.button("Clear History", use_container_width=True, on_click=clear_history)

render_app()