> ## Documentation Index
> Fetch the complete documentation index at: https://genai.codewithsiva.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# 1. Chains Basics

> Build chains using LCEL, understand how they work under the hood, and explore LangChain's Runnable interface

In this section, you will learn the fundamentals of LangChain Expression Language (LCEL), demystify how chains work under the hood using explicit Runnable objects, and explore the global `Runnable` protocol hierarchy.

## Objectives

1. Build a basic chain connecting prompts, models, and string output parsers using the pipe (`|`) operator.
2. Peek under the hood of LCEL by implementing chains using explicit `RunnableLambda` and `RunnableSequence` constructs.
3. Understand the core **Runnable Hierarchy** and the common methods shared across all LangChain components.

***

## 1. LCEL Chain Basics

LangChain Expression Language (LCEL) allows you to chain together multiple components. The pipe (`|`) operator streams inputs through components sequentially:

```python theme={null}
from dotenv import load_dotenv
from langchain.prompts import ChatPromptTemplate
from langchain.schema.output_parser import StrOutputParser
from langchain_openai import ChatOpenAI

# Load environment variables from .env
load_dotenv()

# Create a ChatOpenAI model
model = ChatOpenAI(model="gpt-4o")

# Define prompt templates
prompt_template = ChatPromptTemplate.from_messages(
    [
        ("system", "You are a comedian who tells jokes about {topic}."),
        ("human", "Tell me {joke_count} jokes."),
    ]
)

# Create the combined chain using LangChain Expression Language (LCEL)
chain = prompt_template | model | StrOutputParser()

# Run the chain
result = chain.invoke({"topic": "lawyers", "joke_count": 3})

# Output
print(result)
```

***

## 2. Under the Hood: Explicit Sequences

Under the hood, LCEL overloads the pipe operator (`|`) to implicitly create sequences. We can achieve the exact same behavior by wrapping code steps in `RunnableLambda` functions and combining them using a `RunnableSequence`.

```python theme={null}
from dotenv import load_dotenv
from langchain.prompts import ChatPromptTemplate
from langchain.schema.runnable import RunnableLambda, RunnableSequence
from langchain_openai import ChatOpenAI

# Load environment variables from .env
load_dotenv()

# Create a ChatOpenAI model
model = ChatOpenAI(model="gpt-4")

# Define prompt templates
prompt_template = ChatPromptTemplate.from_messages(
    [
        ("system", "You are a comedian who tells jokes about {topic}."),
        ("human", "Tell me {joke_count} jokes."),
    ]
)

# Create individual runnables (steps in the chain)
# RunnableLambda turns custom python functions into LCEL-ready components
format_prompt = RunnableLambda(lambda x: prompt_template.format_prompt(**x))
invoke_model = RunnableLambda(lambda x: model.invoke(x.to_messages()))
parse_output = RunnableLambda(lambda x: x.content)

# Create the RunnableSequence manually
chain = RunnableSequence(first=format_prompt, middle=[invoke_model], last=parse_output)

# Run the chain
response = chain.invoke({"topic": "lawyers", "joke_count": 3})

# Output
print(response)
```

***

## 3. The LangChain Runnable Hierarchy

Almost every component in LangChain—including Prompts, Chat Models, LLMs, Output Parsers, and helper classes—implements the **Runnable** protocol. This shared base class structure allows components to be seamlessly piped together.

```mermaid theme={null}
graph TD
    Runnable["Runnable (Base Protocol Class)"]
    RunnableSerializable["RunnableSerializable (Supports Serialization)"]
    RunnableSequence["RunnableSequence (Created by '|')"]
    RunnableParallel["RunnableParallel (Executes concurrently)"]
    RunnableLambda["RunnableLambda (Wraps Python functions)"]
    RunnableBranch["RunnableBranch (Conditional routing)"]
    RunnablePassthrough["RunnablePassthrough (Passes inputs unchanged)"]
    
    BasePromptTemplate["BasePromptTemplate (ChatPromptTemplate, etc.)"]
    BaseChatModel["BaseChatModel / LLM (ChatOpenAI, etc.)"]
    BaseOutputParser["BaseOutputParser (StrOutputParser, etc.)"]

    Runnable --> RunnableSerializable
    Runnable --> RunnableLambda
    Runnable --> RunnablePassthrough
    
    RunnableSerializable --> RunnableSequence
    RunnableSerializable --> RunnableParallel
    RunnableSerializable --> RunnableBranch
    RunnableSerializable --> BasePromptTemplate
    RunnableSerializable --> BaseChatModel
    RunnableSerializable --> BaseOutputParser
```

### Core Hierarchy Classes

* **`Runnable`**: The base protocol class defining the interface contract (`invoke`, `stream`, `batch`).
* **`RunnableSerializable`**: Inherits from `Runnable`. It represents components that can be serialized or saved to disk (e.g. prompt templates, chat models, output parsers).
* **`RunnableLambda`**: Wraps a standard Python callable function or lambda so it behaves like a standard LangChain Runnable.
* **`RunnableSequence`**: Formed when chaining components using the pipe operator (`|`). Represents a pipeline where step $N$ feeds into step $N+1$.
* **`RunnableParallel`**: Executes multiple branches concurrently on the same input, yielding a dictionary mapped to each branch's output.
* **`RunnablePassthrough`**: Passes the input keys through unchanged or adds new keys dynamically. Commonly used with `RunnableParallel` to build RAG chains where you want to pass both the original question and retrieved context forward.

### Regularly Used Component Runnables

* **`BasePromptTemplate` (e.g. `ChatPromptTemplate`)**: Takes a dictionary of arguments and formats it into prompts.
* **`BaseChatModel` / `LLM` (e.g. `ChatOpenAI`)**: Takes prompts/messages and returns message outputs.
* **`BaseOutputParser` (e.g. `StrOutputParser`, `JsonOutputParser`)**: Takes message outputs and parses them into strings or structured formats.

***

## Practice & Exercises

To reinforce what you've learned in this section, practice with the interactive notebook:

<CardGroup cols={1}>
  <Card title="Practice & Exercises" icon="laptop-code">
    Practice composing simple chains, using different invocation methods (batch, stream), and manually building sequences.

    [💻 VS Code](vscode://file/Users/sivaprasad/Downloads/GenAI%20With%20Python/public/notebooks/langchain/1_chains_basics.ipynb) | [🚀 Colab](https://colab.research.google.com/github/prasad230776/genai-course/blob/master/public/notebooks/langchain/1_chains_basics.ipynb) | <a href="/public/notebooks/langchain/1_chains_basics.ipynb" download>📥 Download</a>
  </Card>
</CardGroup>
