# LangChain

Imagine SDK can be used with LangChain in the same way as other language models like the
ones offered by OpenAI, Anthropic, etc. This documentation does not pretend to be a
tutorial about LangChain, but to demonstrate how to use Imagine SDK with LangChain.
Please refer to the [LangChain documentation](https://python.langchain.com/v0.2/docs/introduction/) for any questions about it.

## Using the language model

If you are familiar with LangChain, you will know that it offers a standard interface
to use with language models from different vendors, as seen
on [their list language models](https://python.langchain.com/v0.2/docs/tutorials/llm_chain/#using-language-models).

Imagine SDK can be used exactly in the same way.

The examples of this page are mostly focused on the synchronous client, as the async
client offers a very similar interface. Check
the [API documentation](../api/imagine_clients) for more details about their
differences.

```{include} /snippets/imagine_api_help.md
```

```python
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
from imagine.langchain import ImagineChat

model = ImagineChat(model="Llama-3-8B")
response = model.invoke(
        [
            SystemMessage(content="Translate the following from English into Italian"),
            HumanMessage(content="hello"),
        ]
    )

print(response.content)

for chunk in model.stream(
    [
        HumanMessage(content="hello!"),
        AIMessage(content="Hi there human!"),
        HumanMessage(
            content="Write a program to sort a list of numbers in python!"
        ),
    ], max_tokens=512):

    print(chunk.content, end="", flush=True)

```

## Chat

Chat models are a variation on language models. While chat models use language models
under the hood, the interface they use is a bit different. Rather than using a "text in,
text out" API, they use an interface where "chat messages" are the inputs and outputs.

This is the most basic example that instantiates the client `ChatClient` and starts a
new conversation by asking a question.

```{literalinclude} ../../examples/langchain/basics/00_sync_chat.py
```

This will print something similar to:

```text
The purpose of model regularization is to prevent overfitting in machine learning
models. Overfitting occurs when a model becomes too complex and starts to fit the noise
in the training data, leading to poor generalization on unseen data. Regularization
techniques introduce additional constraints or penalties to the model's objective
function, discouraging it from becoming overly complex and promoting simpler and more
generalizable models. Regularization helps to strike a balance between fitting the
training data well and avoiding overfitting, leading to better performance on new,
unseen data.
```

### Streaming response

The example above returns the response all at once. But on your application you might
want to get the result in small chunks, so that you can start providing some feedback
to the user as soon as possible. This is particularly useful for long responses that
might take a long time to complete.

```{literalinclude} ../../examples/langchain/basics/00_sync_chat_stream.py
```

This will provide an output similar to the example above, but the text will be printed
progressively instead of all at once.

### Asynchronous client

If you are interested in the async client, this is the non-streaming example for it:

```{literalinclude} ../../examples/langchain/basics/00_async_chat.py
```

And with streaming enabled:

```{literalinclude} ../../examples/langchain/basics/00_async_chat_stream.py
```

Notice how on both cases the methods and the input arguments are very similar, making it
very easy to transition from synchronous code to async code.

### Prompt templates

Prompt templates can be used to make formatting a bit easier.

```{literalinclude} ../../examples/langchain/basics/00_prompt_template.py
```

### Chains

Chains refer to sequences of calls - whether to an LLM, a tool, or a data preprocessing step. The primary supported way to do this is with [LCEL](https://python.langchain.com/v0.1/docs/expression_language/).

See the following example that creates a chain with the steps:
1. Defines a prompt template.
2. Passes it to a model.
3. Parses the output of the model to extract the text of the most likely output.
4. Uses a custom function to uppercase the text.
5. Counts then number of words on that text.


```{literalinclude} ../../examples/langchain/basics/02_chain_extended.py
```


## LLM

This interface is one that takes as input a string and returns a string. The following example showcases both the regular case and the stream case:


```{literalinclude} ../../examples/langchain/basics/00_llm.py
```


## Embeddings

The Embeddings class is a class designed for interfacing with text embedding models.

Embeddings create a vector representation of a piece of text. This is useful because it
means we can think about text in the vector space, and do things like semantic search
where we look for pieces of text that are most similar in the vector space.

The base Embeddings class in LangChain provides two methods: one for embedding documents
and one for embedding a query. The former takes as input multiple texts, while the
latter takes a single text.

Again, this can be done synchronously:

```{literalinclude} ../../examples/langchain/basics/00_embeddings.py
```

Or asynchronously:

```{literalinclude} ../../examples/langchain/basics/00_async_embeddings.py
```

## RAG

For RAG functionality check out the [RAG tutorials](rag_with_chromadb).

## Agents

For agents functionality check out the [agents tutorials](crewai).

## Table of contents

```{toctree}
3_1_langchain_tools
3_2_langchain_custom_tools
```
