> ## 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.

# LCEL & Runnables

> Compose pipelines using LangChain Expression Language and manage data flow

## 💻 Practice Notebook

Master the concepts from this page with hands-on practice:
[💻 VS Code](vscode://file/Users/sivaprasad/Downloads/GenAI%20With%20Python/public/notebooks/prompt-engg/prompt-engg-lcel-vscode.ipynb) | [🚀 Colab](https://colab.research.google.com/github/prasad230776/genai-course/blob/master/public/notebooks/prompt-engg/prompt-engg-lcel-colab.ipynb) | <a href="/public/notebooks/prompt-engg/prompt-engg-lcel-vscode.ipynb" download>📥 Download Notebook</a>

LangChain Expression Language (LCEL) is a declarative way to build LLM applications, allowing you to compose components using the pipe operator (`|`).

## 1. What is LCEL & Why Use It?

Instead of writing imperative code to link prompts, models, and parsers, you connect them together like a Unix pipeline:

```python theme={null}
# A basic LCEL chain structure
chain = prompt | llm
```

### Why Use LCEL?

* **Simple**: Chain complex components in just a few lines of code.
* **Readable**: Easy to inspect the flow of inputs and outputs.
* **Composable**: Swap prompts, LLMs, retrievers, or parsers effortlessly.
* **Built-in Support**: Handles streaming and parallel operations out of the box.

## 2. LCEL Code Examples

### Example 1: Basic QA Chain

A simple chain that takes a topic, formats a prompt, invokes the model, and extracts the response content.

```python theme={null}
from langchain_core.prompts import PromptTemplate
from langchain.chat_models import init_chat_model

# Initialize components
llm = init_chat_model("llama-3.3-70b-versatile", model_provider="groq")
prompt = PromptTemplate.from_template("Tell me a short joke about \{topic\}.")

# Compose LCEL Chain: prompt | llm
chain = prompt | llm

# Invoke the chain (returns an AIMessage)
response = chain.invoke({"topic": "programming"})
print(response.content)
```

### Example 2: Subject Line Generator

Generate a professional email subject line using dynamic topic and tone variables.

```python theme={null}
from langchain_core.prompts import PromptTemplate
from langchain.chat_models import init_chat_model

# Initialize components
llm = init_chat_model("llama-3.3-70b-versatile", model_provider="groq")
prompt = PromptTemplate.from_template("Write a single email subject line for a request about \{request_topic\} in a \{tone\} tone.")

# Compose LCEL Chain
chain = prompt | llm

# Invoke the chain
response = chain.invoke({"request_topic": "server maintenance delay", "tone": "apologetic"})
print(response.content)
```

## 3. Exercises for LCEL

### Exercise 1: Marketing Pitch Generator

Create a chain that takes a product name and a target audience and generates a catchy marketing slogan.

**Instructions:**

1. Import `PromptTemplate` and `init_chat_model`.
2. Define a string prompt template containing two variables: `{product_name}` and `{target_audience}`.
3. Initialize the Groq model `llama-3.3-70b-versatile`.
4. Compose an LCEL chain linking the prompt template and the chat model.
5. Invoke the chain passing a dictionary with values for `"product_name"` (e.g., `"EcoWater Bottle"`) and `"target_audience"` (e.g., `"fitness enthusiasts"`).
6. Print the model's text response using `.content`.

<Accordion title="Solution">
  ```python theme={null}
  from langchain_core.prompts import PromptTemplate
  from langchain.chat_models import init_chat_model

  llm = init_chat_model("llama-3.3-70b-versatile", model_provider="groq")

  prompt = PromptTemplate.from_template(
      "Create a catchy one-sentence marketing slogan for {product_name} targeting {target_audience}."
  )

  # Compose the chain
  pitch_chain = prompt | llm

  # Run
  result = pitch_chain.invoke({
      "product_name": "EcoWater Bottle",
      "target_audience": "fitness enthusiasts"
  })
  print(result.content)
  ```
</Accordion>

### Exercise 2: Tech Tag Extractor

Create a chain that takes an article excerpt and lists the top 3 technology keywords mentioned.

**Instructions:**

1. Import `PromptTemplate` and `init_chat_model`.
2. Define a string prompt template containing a variable `{text}` that asks the model to list the top 3 technology keywords mentioned in the text.
3. Initialize the Groq model `llama-3.3-70b-versatile`.
4. Compose an LCEL chain linking the prompt template and the chat model.
5. Invoke the chain passing a dictionary containing a sample text paragraph.
6. Print the model's text response using `.content`.

<Accordion title="Solution">
  ```python theme={null}
  from langchain_core.prompts import PromptTemplate
  from langchain.chat_models import init_chat_model

  llm = init_chat_model("llama-3.3-70b-versatile", model_provider="groq")

  prompt = PromptTemplate.from_template(
      "List the top 3 technology keywords mentioned in this text: '{text}'."
  )

  # Compose the chain
  tag_chain = prompt | llm

  # Run
  result = tag_chain.invoke({
      "text": "Kubernetes is an open-source container orchestration system for automating software deployment and scaling. It was originally designed by Google."
  })
  print(result.content)
  ```
</Accordion>

## 4. Invoking vs. Streaming

### 4.1 `invoke()`

Waits for the entire model execution to complete and returns the full response at once.

```python theme={null}
response = chain.invoke({"topic": "Python"})
print(response.content)
```

### 4.2 `stream()`

Yields the response progressively, token-by-token. This is crucial for interactive chat interfaces to improve perceived user latency.

```python theme={null}
for chunk in chain.stream({"topic": "Python generators"}):
    # ChatModel stream yields BaseMessageChunks; print chunk.content
    print(chunk.content, end="", flush=True)
```

### Invocation Input Cheat Sheet

| What are you invoking?            | Input type           | Example                             |
| --------------------------------- | -------------------- | ----------------------------------- |
| `llm.invoke()`                    | String               | `"What is Python?"`                 |
| Prompt with one variable          | Dictionary or String | `{"topic": "Python"}` or `"Python"` |
| Prompt with multiple variables    | Dictionary           | `{"name": "Siva", "age": 25}`       |
| LCEL chain starting with a prompt | Dictionary           | `{"topic": "Generators"}`           |

## 5. LangChain Runnables

A **Runnable** is the fundamental building block in LangChain. Any component that implements `invoke()`, `batch()`, or `stream()` is a Runnable.

### 5.1 RunnableSequence

Chains multiple runnables sequentially so the output of one component becomes the input of the next. The pipe operator (`|`) automatically creates a `RunnableSequence`.

```python theme={null}
# The pipe creates a RunnableSequence under the hood
chain = prompt | llm
```

### 5.2 RunnablePassthrough

Forwards the input value as-is. This is useful for passing unchanged variables down a chain or creating multi-keyed inputs.

```python theme={null}
from langchain_core.runnables import RunnablePassthrough, RunnableLambda

# Combine Passthrough with Lambda mapping
chain = {
    "original": RunnablePassthrough(),
    "upper": RunnableLambda(lambda x: x.upper())
}

result = chain.invoke("hello")
print(result)
```

**Output:**

```text theme={null}
{'original': 'hello', 'upper': 'HELLO'}
```

### 5.3 RunnableParallel

Executes multiple runnables concurrently on the same input, returning their outputs as a unified dictionary.

```python theme={null}
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnableParallel

prompt1 = PromptTemplate.from_template("Summarize \{topic\}")
prompt2 = PromptTemplate.from_template("List advantages of \{topic\}")

parallel_chain = RunnableParallel(
    summary=prompt1 | llm,
    advantages=prompt2 | llm
)

result = parallel_chain.invoke({"topic": "Artificial Intelligence"})
print(result)
```

**Output:**

```text theme={null}
{
  "summary": AIMessage(content="AI is the simulation..."),
  "advantages": AIMessage(content="1. Automation...")
}
```

## 6. Practice Exercises

### Practice 1: Basic LCEL Translation Pipeline

Create a simple LCEL chain combining a prompt template (`"Translate the word '{word}' into German."`) and a chat model. Invoke it with the word `"apple"` and print the response content.

<Accordion title="Solution">
  ```python theme={null}
  from langchain_core.prompts import PromptTemplate
  from langchain.chat_models import init_chat_model

  llm = init_chat_model("groq:llama-3.3-70b-versatile", model_provider="groq")
  prompt = PromptTemplate.from_template("Translate the word '{word}' into German.")

  # Create the LCEL chain
  chain = prompt | llm

  # Invoke the chain
  result = chain.invoke({"word": "apple"})
  print(result.content)
  ```
</Accordion>
