master 89301c62ac34 cached
169 files
1.5 MB
431.4k tokens
175 symbols
1 requests
Download .txt
Showing preview only (1,613K chars total). Download the full file or copy to clipboard to get everything.
Repository: langchain-ai/learning-langchain
Branch: master
Commit: 89301c62ac34
Files: 169
Total size: 1.5 MB

Directory structure:
gitextract__8u75gon/

├── .gitignore
├── README.md
├── ch1/
│   ├── js/
│   │   ├── a-llm.js
│   │   ├── b-chat.js
│   │   ├── c-system.js
│   │   ├── d-prompt.js
│   │   ├── e-prompt-model.js
│   │   ├── f-chat-prompt.js
│   │   ├── g-chat-prompt-model.js
│   │   ├── h-structured.js
│   │   ├── i-csv.js
│   │   ├── j-methods.js
│   │   ├── k-imperative.js
│   │   ├── ka-stream.js
│   │   └── l-declarative.js
│   └── py/
│       ├── a-llm.py
│       ├── b-chat.py
│       ├── c-system.py
│       ├── d-prompt.py
│       ├── e-prompt-model.py
│       ├── f-chat-prompt.py
│       ├── g-chat-prompt-model.py
│       ├── h-structured.py
│       ├── i-csv.py
│       ├── j-methods.py
│       ├── k-imperative.py
│       ├── ka-stream.py
│       ├── kb-async.py
│       └── l-declarative.py
├── ch10/
│   ├── js/
│   │   ├── agent-evaluation-rag.js
│   │   ├── agent-evaluation-sql.js
│   │   ├── agent-sql-graph.js
│   │   ├── create-rag-dataset.js
│   │   ├── create-sql-dataset.js
│   │   ├── rag-graph.js
│   │   ├── retrieve-and-grade.js
│   │   └── search-graph.js
│   └── py/
│       ├── agent_evaluation_rag.py
│       ├── agent_evaluation_sql.py
│       ├── agent_sql_graph.py
│       ├── create_rag_dataset.py
│       ├── create_sql_dataset.py
│       ├── rag_graph.py
│       ├── retrieve_and_grade.py
│       └── search_graph.py
├── ch2/
│   ├── js/
│   │   ├── a-text-loader.js
│   │   ├── b-web-loader.js
│   │   ├── c-pdf-loader.js
│   │   ├── d-rec-text-splitter.js
│   │   ├── e-rec-text-splitter-code.js
│   │   ├── f-markdown-splitter.js
│   │   ├── g-embeddings.js
│   │   ├── h-load-split-embed.js
│   │   ├── i-pg-vector.js
│   │   ├── j-record-manager.js
│   │   └── k-multi-vector-retriever.js
│   └── py/
│       ├── a-text-loader.py
│       ├── b-web-loader.py
│       ├── c-pdf-loader.py
│       ├── d-rec-text-splitter.py
│       ├── e-rec-text-splitter-code.py
│       ├── f-markdown-splitter.py
│       ├── g-embeddings.py
│       ├── h-load-split-embed.py
│       ├── i-pg-vector.py
│       ├── j-record-manager.py
│       ├── k-multi-vector-retriever.py
│       └── l-rag-colbert.py
├── ch3/
│   ├── js/
│   │   ├── a-basic-rag.js
│   │   ├── b-rewrite.js
│   │   ├── c-multi-query.js
│   │   ├── d-rag-fusion.js
│   │   ├── e-hyde.js
│   │   ├── f-router.js
│   │   ├── g-semantic-router.js
│   │   ├── h-self-query.js
│   │   └── i-sql-example.js
│   └── py/
│       ├── a-basic-rag.py
│       ├── b-rewrite.py
│       ├── c-multi-query.py
│       ├── d-rag-fusion.py
│       ├── e-hyde.py
│       ├── f-router.py
│       ├── g-semantic-router.py
│       ├── h-self-query.py
│       └── i-sql-example.py
├── ch4/
│   ├── js/
│   │   ├── a-simple-memory.js
│   │   ├── b-state-graph.js
│   │   ├── c-persistent-memory.js
│   │   ├── d-trim-messages.js
│   │   ├── e-filter-messages.js
│   │   └── f-merge-messages.js
│   └── py/
│       ├── a-simple-memory.py
│       ├── b-state-graph.py
│       ├── c-persistent-memory.py
│       ├── d-trim-messages.py
│       ├── e-filter-messages.py
│       └── f-merge-messages.py
├── ch5/
│   ├── js/
│   │   ├── a-chatbot.js
│   │   ├── b-sql-generator.js
│   │   └── c-multi-rag.js
│   └── py/
│       ├── a-chatbot.py
│       ├── b-sql-generator.py
│       └── c-multi-rag.py
├── ch6/
│   ├── js/
│   │   ├── a-basic-agent.js
│   │   ├── b-force-first-tool.js
│   │   └── c-many-tools.js
│   └── py/
│       ├── a-basic-agent.py
│       ├── b-force-first-tool.py
│       └── c-many-tools.py
├── ch7/
│   ├── js/
│   │   ├── a-reflection.js
│   │   ├── b-subgraph-direct.js
│   │   ├── c-subgraph-function.js
│   │   └── d-supervisor.js
│   └── py/
│       ├── a-reflection.py
│       ├── b-subgraph-direct.py
│       ├── c-subgraph-function.py
│       └── d-supervisor.py
├── ch8/
│   ├── js/
│   │   ├── a-structured-output.js
│   │   ├── b-streaming-output.js
│   │   ├── c-interrupt.js
│   │   ├── d-authorize.js
│   │   ├── e-resume.js
│   │   ├── f-edit-state.js
│   │   └── g-fork.js
│   └── py/
│       ├── a-structured-output.py
│       ├── b-streaming-output.py
│       ├── c-interrupt.py
│       ├── d-authorize.py
│       ├── e-resume.py
│       ├── f-edit-state.py
│       └── g-fork.py
├── ch9/
│   ├── README.md
│   ├── js/
│   │   ├── .gitignore
│   │   ├── demo.ts
│   │   ├── langgraph.json
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── ingestion_graph/
│   │   │   │   ├── configuration.ts
│   │   │   │   ├── graph.ts
│   │   │   │   └── state.ts
│   │   │   ├── retrieval_graph/
│   │   │   │   ├── configuration.ts
│   │   │   │   ├── graph.ts
│   │   │   │   ├── state.ts
│   │   │   │   └── utils.ts
│   │   │   └── shared/
│   │   │       ├── configuration.ts
│   │   │       ├── retrieval.ts
│   │   │       ├── state.ts
│   │   │       └── utils.ts
│   │   └── tsconfig.json
│   └── py/
│       ├── demo.py
│       ├── langgraph.json
│       ├── pyproject.toml
│       └── src/
│           ├── docSplits.json
│           ├── ingestion_graph/
│           │   ├── __init__.py
│           │   ├── configuration.py
│           │   ├── graph.py
│           │   └── state.py
│           ├── retrieval_graph/
│           │   ├── __init__.py
│           │   ├── configuration.py
│           │   ├── graph.py
│           │   ├── state.py
│           │   └── utils.py
│           └── shared/
│               ├── __init__.py
│               ├── configuration.py
│               ├── retrieval.py
│               └── state.py
├── package.json
├── pyproject.toml
└── test.txt

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# ===== Python =====

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# Virtual Environment
venv/
env/
ENV/
.env
.venv/

# IDEs and Editors
.idea/
.vscode/
*.swp
*.swo
*~

# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# Logs and databases
*.log
*.sqlite
*.db

# Coverage and testing
.coverage
htmlcov/
.tox/
.pytest_cache/
coverage.xml
*.cover

# Documentation
docs/_build/


# ===== JavaScript =====

# Node modules
node_modules/

# Optional npm cache
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn
.yarn/
.yarn-integrity

# dotenv environment variables
.env
.env.test
.env.production

# Build output
dist/
build/

# Logs
logs/

# VS Code
.vscode/

# IntelliJ/WebStorm
.idea/

# Jest coverage
coverage/


================================================
FILE: README.md
================================================
# Learning LangChain Code Examples

This repository contains code examples (in python and javascript) from each chapter of the book ["Learning LangChain: Building AI and LLM Applications with LangChain and LangGraph"](https://www.oreilly.com/library/view/learning-langchain/9781098167271/) published by O'Reilly Media.

To run the examples, you can clone the repository and run the examples in your preferred language folders.

## Table of Contents

- [Quick Start](#quick-start)
  - [Environment variables setup](#environment-variables-setup)
  - [Running the chapter examples](#running-the-chapter-examples)
- [Repository Structure](#repository-structure)
- [Chapter-wise Examples](#chapter-wise-examples)
- [Docker Setup and Usage](#docker-setup-and-usage)
    - [Installing Docker](#installing-docker)
    - [Running the PostgreSQL Container](#running-the-postgresql-container)
    - [Troubleshooting Docker](#troubleshooting-docker)
- [Setting up Chinook.db with SQLite](#setting-up-chinookdb-with-sqlite)
- [General Troubleshooting](#general-troubleshooting)

## Quick Start

### Environment variables setup

First, we need the environment variables required to run the examples in this repository.

You can find the full list in the `.env.example` file. Copy this file to a `.env` and fill in the values:

```bash
cp .env.example .env
```

- **OPENAI_API_KEY**: You can get your key [here](https://platform.openai.com/account/api-keys). This will enable you to run the examples that require an OpenAI model.

- **LANGCHAIN_API_KEY**: You can get your key by creating an account [here](https://smith.langchain.com/). This will enable you to interact with the LangSmith tracing and debugging tools.

- **LANGCHAIN_TRACING_V2=true**: This is required to enable visual tracing and debugging in LangSmith for the examples.

If you want to run the production example in chapter 9, you need a Supabase account and a Supabase API key:

1. To register for a Supabase account, go to [supabase.com](https://supabase.com) and sign up.
2. Once you have an account, create a new project then navigate to the settings section.
3. In the settings section, navigate to the API section to see your keys.
4. Copy the project URL and service_role key and add them to the `.env` file as values for `SUPABASE_URL` and `SUPABASE_SERVICE_ROLE_KEY`.

### Running the chapter examples

#### For Python examples:

If you haven't installed Python on your system, install it first as per the instructions [here](https://www.python.org/downloads/).

1. Create a virtual environment:
```bash
python -m venv .venv
```

2. Activate the virtual environment:

For MacOS/Linux:
```bash
source .venv/bin/activate
```

For Windows:
```bash
.venv\Scripts\activate
```

After activation, your terminal prompt should prefix with (venv), indicating that the virtual environment is active.

3. Install the dependencies in the pyproject.toml file:
```bash
pip install -e .
```

4. Verify the installation:
```bash
pip list
```

5. Run an example to see the output:
```bash
python ch2/py/a-text-loader.py
```

#### For JavaScript examples:

If you haven't installed Node.js on your system, install it first as per the instructions [here](https://nodejs.org/).

1. Install the dependencies in the package.json file:
```bash
npm install
```

2. Run the example to see the output:
```bash
node ch2/js/a-text-loader.js
```

## Repository Structure

The repository is structured as follows:

```
├── .env.example          # Example environment variables
├── learning-langchain    # Root directory
│   ├── ch1               # Chapter 1 examples
│   │   ├── js            # JavaScript examples
│   │   │   ├── a-llm.js  # Example file
│   │   │   └── ...
│   │   └── py            # Python examples
│   │       ├── a-llm.py  # Example file
│   │       └── ...
│   ├── ch2               # Chapter 2 examples
│   │   ├── js            # JavaScript examples
│   │   │   └── ...
│   │   └── py            # Python examples
│   │       └── ...
│   ├── ...               # Remaining chapters
│   ├── test.pdf          # Test PDF file
│   ├── test.txt          # Test text file
│   ├── package.json      # JavaScript dependencies
│   ├── pyproject.toml    # Python dependencies
│   └── README.md         # This file
└── ...
```

Each chapter (ch1, ch2, etc.) contains subdirectories `js` and `py` for JavaScript and Python examples, respectively.

## Chapter-wise Examples

Here's a brief overview of the code examples available for each chapter:

### Chapter 1: Introduction to LangChain

Demonstrates basic usage of LLMs, Chat models, prompts, and output parsers.

Files:
- `ch1/js/*.js`: JavaScript examples
- `ch1/py/*.py`: Python examples

### Chapter 2: Document Loading and Data Transformation

Covers loading data from various sources (text files, web pages, PDFs), splitting text into chunks, and creating embeddings.

Files:
- `ch2/js/*.js`: JavaScript examples
- `ch2/py/*.py`: Python examples

### Chapter 3: Retrieval

Explores different retrieval strategies, including basic RAG, query rewriting, multi-query, RAG fusion, and self-query.

Files:
- `ch3/js/*.js`: JavaScript examples
- `ch3/py/*.py`: Python examples

### Chapter 4: Memory

Demonstrates how to add memory to your chains and agents, including simple memory, state graphs, persistent memory, and message trimming/filtering/merging.

Files:
- `ch4/js/*.js`: JavaScript examples
- `ch4/py/*.py`: Python examples

### Chapter 5: Chatbots

Shows how to build chatbots using LangGraph, including basic chatbots, SQL generators, and multi-RAG chatbots.

Files:
- `ch5/js/*.js`: JavaScript examples
- `ch5/py/*.py`: Python examples

### Chapter 6: Agents

Covers building agents with tools, including basic agents, forcing the first tool, and using many tools.

Files:
- `ch6/js/*.js`: JavaScript examples
- `ch6/py/*.py`: Python examples

### Chapter 7: Subgraphs

Explores how to use subgraphs to create more complex agents, including reflection, direct subgraphs, function subgraphs, and supervisors.

Files:
- `ch7/js/*.js`: JavaScript examples
- `ch7/py/*.py`: Python examples

### Chapter 8: Productionizing LangGraph

Demonstrates how to productionize LangGraph applications, including structured output, streaming output, interruption, authorization, resuming, editing state, and forking.

Files:
- `ch8/js/*.js`: JavaScript examples
- `ch8/py/*.py`: Python examples

### Chapter 9: Deployment

Provides examples of deploying LangGraph applications.

Files:
- `ch9/js/*`: JavaScript examples
- `ch9/py/*`: Python examples

#### Running the Local Development Server

You can run the local development server for either JavaScript or Python implementations from the root directory:

##### For JavaScript:
```bash
# Using npm script
npm run langgraph:dev
```

##### For Python:
You have two options:

1. Using the CLI directly:
```bash
langgraph dev -c ch9/py/langgraph.json --verbose
```

2. Using the installed script command:
```bash
langgraph-dev
```

Note: To use the script command, make sure you have installed the package in development mode (`pip install -e .`).

### Chapter 10: Evaluation

Shows how to evaluate LangChain applications, including agent evaluation for RAG and SQL, and creating datasets.

Files:
- `ch10/js/*.js`: JavaScript examples
- `ch10/py/*.py`: Python examples

## Docker Setup and Usage

Several examples in this repository require Docker to be installed and running to set up a PostgreSQL database with the pgvector extension. This section provides guidance on setting up Docker and running the PostgreSQL container.


### Installing Docker
1. Download Docker Desktop:

Go to the Docker [website](https://www.docker.com/get-started/) and download the appropriate version for your operating system (Windows, macOS, or Linux).

2. Install Docker Desktop:

- Windows: Double-click the downloaded installer and follow the on-screen instructions. You may need to enable virtualization in your BIOS settings.

- macOS: Drag the Docker icon to the Applications folder and double-click to start.

- Linux: Follow the instructions provided on the Docker website for your specific distribution.

3. Start Docker Desktop:

After installation, start Docker Desktop. On Windows and macOS, it will run in the system tray. On Linux, you may need to start it manually.

4. Verify the installation:

To check if Docker is installed, run:
```bash
docker --version
```


### Running the PostgreSQL Container

Run the Docker command:

Open a terminal or command prompt and run the following command to start the PostgreSQL container with the pgvector extension:

```bash
docker run \
    --name pgvector-container \
    -e POSTGRES_USER=langchain \
    -e POSTGRES_PASSWORD=langchain \
    -e POSTGRES_DB=langchain \
    -p 6024:5432 \
    -d pgvector/pgvector:pg16
```

Explanation of the command:

- `docker run`: Starts a new container.
- `--name pgvector-container`: Assigns the name "pgvector-container" to the container.
- `-e POSTGRES_USER=langchain`: Sets the PostgreSQL user to "langchain".
- `-e POSTGRES_PASSWORD=langchain`: Sets the PostgreSQL password to "langchain".
- `-e POSTGRES_DB=langchain`: Sets the default database name to "langchain".
- `-p 6024:5432`: Maps port 6024 on your host machine to port 5432 in the container (PostgreSQL's default port).
- `-d pgvector/pgvector:pg16`: Specifies the image to use (pgvector/pgvector:pg16), which includes PostgreSQL 16 and the pgvector extension.

#### Verify the Container is Running:

Run the following command to list running containers:

```bash
docker ps
```

You should see the "pgvector-container" listed with a status of "Up".

#### Accessing the PostgreSQL Database:

You can now access the PostgreSQL database from your code using the following connection string:

```
postgresql://langchain:langchain@localhost:6024/langchain
```

### Troubleshooting Docker

#### Docker Desktop Not Starting:

- **Windows**: Ensure that virtualization is enabled in your BIOS settings. Check the Docker Desktop logs for any specific error messages.
- **macOS**: Make sure you have granted Docker Desktop the necessary permissions in System Preferences.
- **Linux**: Ensure that the Docker daemon is running and that your user has the necessary permissions to run Docker commands.

#### Container Not Running:

Check the container logs for any errors:

```bash
docker logs pgvector-container
```

Look for error messages related to PostgreSQL startup or pgvector initialization.

#### Port Conflict:

If port 6024 is already in use, you can change the port mapping in the docker run command to an available port. For example, `-p 6025:5432`.

#### Image Not Found:

Ensure that you have an internet connection and that the pgvector/pgvector:pg16 image is available on Docker Hub.

#### Permissions Issues:

On Linux, you may encounter permissions issues when running Docker commands. Ensure that your user is part of the docker group:

```bash
sudo usermod -aG docker $USER
newgrp docker
```

#### General Docker Troubleshooting:

- Restart Docker Desktop
- Update Docker Desktop to the latest version
- Check the Docker documentation for troubleshooting tips specific to your operating system

### Setting up Chinook.db with SQLite

Some examples in Chapter 3 and Chapter 10 use the Chinook database, a sample database for a digital media store, with SQLite. Here's how to set it up:

#### Download the Chinook Database Schema:

Open a terminal or command prompt and use curl to download the Chinook databas:

```bash
curl -s https://raw.githubusercontent.com/lerocha/chinook-database/master/ChinookDatabase/DataSources/Chinook_Sqlite.sql | sqlite3 Chinook.db
```

This will create a file called `Chinook.db` in the current directory.

#### Verify the Setup:

You can verify that the database is set up correctly by connecting to it using the sqlite3 tool and running a simple query:

```bash
sqlite3 Chinook.db
```

Then, inside the sqlite3 prompt, run:

```sql
SELECT * FROM Artist LIMIT 10;
```

If the setup is successful, you should see the first 10 rows from the Artist table.

#### Place Chinook.db in the Correct Directory:

Ensure that the Chinook.db file is located in the same directory as the code examples that use it.

## General Troubleshooting

### Dependency Conflicts:

If you encounter errors related to conflicting dependencies, try creating a fresh virtual environment and reinstalling the dependencies.

Ensure that your package.json or pyproject.toml files specify compatible versions of the libraries.

### PgVector Vector Store Installation or Connection Errors:

#### Errors for Python:

If you can't find `psycopg` or `psycopg_binary`: Try to reinstall psycopg with the `[binary]` extra, which includes pre-compiled binaries and necessary dependencies:

```bash
pip install psycopg[binary]
```

Then run the file again.

If you're having issues connecting to Postgres via Docker, you can try some alternative vector stores:

1. Use the memory vector store instead: This is a simple vector store that stores vectors in memory. It is not persistent and will be lost when the program is terminated. Here's the [API for Python](https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/memory) and [docs for Javascript](https://js.langchain.com/docs/modules/data_connection/vectorstores/integrations/memory).

2. You can also use Chroma-- an AI-native open-source vector database. You can install Chroma as per the instructions for [Python](https://python.langchain.com/docs/integrations/vectorstores/chroma) or [Javascript](https://js.langchain.com/docs/modules/data_connection/vectorstores/integrations/chroma).
















================================================
FILE: ch1/js/a-llm.js
================================================
import { ChatOpenAI } from '@langchain/openai';

const model = new ChatOpenAI({ model: 'gpt-3.5-turbo' });

const response = await model.invoke('The sky is');
console.log(response);


================================================
FILE: ch1/js/b-chat.js
================================================
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage } from '@langchain/core/messages';

const model = new ChatOpenAI();
const prompt = [new HumanMessage('What is the capital of France?')];

const response = await model.invoke(prompt);
console.log(response);


================================================
FILE: ch1/js/c-system.js
================================================
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage, SystemMessage } from '@langchain/core/messages';

const model = new ChatOpenAI();
const prompt = [
  new SystemMessage(
    'You are a helpful assistant that responds to questions with three exclamation marks.'
  ),
  new HumanMessage('What is the capital of France?'),
];

const response = await model.invoke(prompt);
console.log(response);


================================================
FILE: ch1/js/d-prompt.js
================================================
import { PromptTemplate } from '@langchain/core/prompts';

const template =
  PromptTemplate.fromTemplate(`Answer the question based on the context below. If the question cannot be answered using the information provided, answer with "I don't know".

Context: {context}

Question: {question}

Answer: `);

const response = await template.invoke({
  context:
    "The most recent advancements in NLP are being driven by Large Language Models (LLMs). These models outperform their smaller counterparts and have become invaluable for developers who are creating applications with NLP capabilities. Developers can tap into these models through Hugging Face's `transformers` library, or by utilizing OpenAI and Cohere's offerings through the `openai` and `cohere` libraries, respectively.",
  question: 'Which model providers offer LLMs?',
});

console.log(response);


================================================
FILE: ch1/js/e-prompt-model.js
================================================
import { PromptTemplate } from '@langchain/core/prompts';
import { OpenAI } from '@langchain/openai';

const model = new OpenAI({
  model: 'gpt-3.5-turbo',
});
const template =
  PromptTemplate.fromTemplate(`Answer the question based on the context below. If the question cannot be answered using the information provided, answer with "I don't know".

Context: {context}

Question: {question}

Answer: `);

const prompt = await template.invoke({
  context:
    "The most recent advancements in NLP are being driven by Large Language Models (LLMs). These models outperform their smaller counterparts and have become invaluable for developers who are creating applications with NLP capabilities. Developers can tap into these models through Hugging Face's `transformers` library, or by utilizing OpenAI and Cohere's offerings through the `openai` and `cohere` libraries, respectively.",
  question: 'Which model providers offer LLMs?',
});

const response = await model.invoke(prompt);
console.log(response);


================================================
FILE: ch1/js/f-chat-prompt.js
================================================
import { ChatPromptTemplate } from '@langchain/core/prompts';

const template = ChatPromptTemplate.fromMessages([
  [
    'system',
    'Answer the question based on the context below. If the question cannot be answered using the information provided, answer with "I don\'t know".',
  ],
  ['human', 'Context: {context}'],
  ['human', 'Question: {question}'],
]);

const response = await template.invoke({
  context:
    "The most recent advancements in NLP are being driven by Large Language Models (LLMs). These models outperform their smaller counterparts and have become invaluable for developers who are creating applications with NLP capabilities. Developers can tap into these models through Hugging Face's `transformers` library, or by utilizing OpenAI and Cohere's offerings through the `openai` and `cohere` libraries, respectively.",
  question: 'Which model providers offer LLMs?',
});
console.log(response);


================================================
FILE: ch1/js/g-chat-prompt-model.js
================================================
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { ChatOpenAI } from '@langchain/openai';

const model = new ChatOpenAI();
const template = ChatPromptTemplate.fromMessages([
  [
    'system',
    'Answer the question based on the context below. If the question cannot be answered using the information provided, answer with "I don\'t know".',
  ],
  ['human', 'Context: {context}'],
  ['human', 'Question: {question}'],
]);

const prompt = await template.invoke({
  context:
    "The most recent advancements in NLP are being driven by Large Language Models (LLMs). These models outperform their smaller counterparts and have become invaluable for developers who are creating applications with NLP capabilities. Developers can tap into these models through Hugging Face's `transformers` library, or by utilizing OpenAI and Cohere's offerings through the `openai` and `cohere` libraries, respectively.",
  question: 'Which model providers offer LLMs?',
});

const response = await model.invoke(prompt);
console.log(response);


================================================
FILE: ch1/js/h-structured.js
================================================
import { ChatOpenAI } from '@langchain/openai';
import { z } from 'zod';

const answerSchema = z
  .object({
    answer: z.string().describe("The answer to the user's question"),
    justification: z.string().describe('Justification for the answer'),
  })
  .describe(
    "An answer to the user's question along with justification for the answer."
  );

const model = new ChatOpenAI({
  model: 'gpt-3.5-turbo',
  temperature: 0,
}).withStructuredOutput(answerSchema);

const response = await model.invoke(
  'What weighs more, a pound of bricks or a pound of feathers'
);
console.log(response);


================================================
FILE: ch1/js/i-csv.js
================================================
import { CommaSeparatedListOutputParser } from '@langchain/core/output_parsers';

const parser = new CommaSeparatedListOutputParser();

const response = await parser.invoke('apple, banana, cherry');
console.log(response);


================================================
FILE: ch1/js/j-methods.js
================================================
import { ChatOpenAI } from '@langchain/openai';

const model = new ChatOpenAI();

const response = await model.invoke('Hi there!');
console.log(response);
// Hi!

const completions = await model.batch(['Hi there!', 'Bye!']);
// ['Hi!', 'See you!']

for await (const token of await model.stream('Bye!')) {
  console.log(token);
  // Good
  // bye
  // !
}


================================================
FILE: ch1/js/k-imperative.js
================================================
import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { RunnableLambda } from '@langchain/core/runnables';

// the building blocks

const template = ChatPromptTemplate.fromMessages([
  ['system', 'You are a helpful assistant.'],
  ['human', '{question}'],
]);

const model = new ChatOpenAI({
  model: 'gpt-3.5-turbo',
});

// combine them in a function
// RunnableLambda adds the same Runnable interface for any function you write

const chatbot = RunnableLambda.from(async (values) => {
  const prompt = await template.invoke(values);
  return await model.invoke(prompt);
});

// use it

const response = await chatbot.invoke({
  question: 'Which model providers offer LLMs?',
});
console.log(response);


================================================
FILE: ch1/js/ka-stream.js
================================================
import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { RunnableLambda } from '@langchain/core/runnables';

const template = ChatPromptTemplate.fromMessages([
  ['system', 'You are a helpful assistant.'],
  ['human', '{question}'],
]);

const model = new ChatOpenAI({
  model: 'gpt-3.5-turbo',
});

const chatbot = RunnableLambda.from(async function* (values) {
  const prompt = await template.invoke(values);
  for await (const token of await model.stream(prompt)) {
    yield token;
  }
});

for await (const token of await chatbot.stream({
  question: 'Which model providers offer LLMs?',
})) {
  console.log(token);
}


================================================
FILE: ch1/js/l-declarative.js
================================================
import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { RunnableLambda } from '@langchain/core/runnables';

// the building blocks

const template = ChatPromptTemplate.fromMessages([
  ['system', 'You are a helpful assistant.'],
  ['human', '{question}'],
]);

const model = new ChatOpenAI({
  model: 'gpt-3.5-turbo',
});

// combine them in a function

const chatbot = template.pipe(model);

// use it

const response = await chatbot.invoke({
  question: 'Which model providers offer LLMs?',
});

console.log(response);

//streaming

for await (const part of chatbot.stream({
  question: 'Which model providers offer LLMs?',
})) {
  console.log(part);
}


================================================
FILE: ch1/py/a-llm.py
================================================
from langchain_openai.chat_models import ChatOpenAI

model = ChatOpenAI(model="gpt-3.5-turbo")

response = model.invoke("The sky is")
print(response.content)


================================================
FILE: ch1/py/b-chat.py
================================================
from langchain_openai.chat_models import ChatOpenAI
from langchain_core.messages import HumanMessage

model = ChatOpenAI()
prompt = [HumanMessage("What is the capital of France?")]

response = model.invoke(prompt)
print(response.content)


================================================
FILE: ch1/py/c-system.py
================================================
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_openai.chat_models import ChatOpenAI

model = ChatOpenAI()
system_msg = SystemMessage(
    "You are a helpful assistant that responds to questions with three exclamation marks."
)
human_msg = HumanMessage("What is the capital of France?")

response = model.invoke([system_msg, human_msg])
print(response.content)


================================================
FILE: ch1/py/d-prompt.py
================================================
from langchain_core.prompts import PromptTemplate

template = PromptTemplate.from_template("""Answer the question based on the context below. If the question cannot be answered using the information provided, answer with "I don't know".

Context: {context}

Question: {question}

Answer: """)

response = template.invoke(
    {
        "context": "The most recent advancements in NLP are being driven by Large Language Models (LLMs). These models outperform their smaller counterparts and have become invaluable for developers who are creating applications with NLP capabilities. Developers can tap into these models through Hugging Face's `transformers` library, or by utilizing OpenAI and Cohere's offerings through the `openai` and `cohere` libraries, respectively.",
        "question": "Which model providers offer LLMs?",
    }
)

print(response)


================================================
FILE: ch1/py/e-prompt-model.py
================================================
from langchain_openai.chat_models import ChatOpenAI
from langchain_core.prompts import PromptTemplate

# both `template` and `model` can be reused many times

template = PromptTemplate.from_template("""Answer the question based on the context below. If the question cannot be answered using the information provided, answer with "I don't know".

Context: {context}

Question: {question}

Answer: """)

model = ChatOpenAI(model="gpt-3.5-turbo")

# `prompt` and `completion` are the results of using template and model once

prompt = template.invoke(
    {
        "context": "The most recent advancements in NLP are being driven by Large Language Models (LLMs). These models outperform their smaller counterparts and have become invaluable for developers who are creating applications with NLP capabilities. Developers can tap into these models through Hugging Face's `transformers` library, or by utilizing OpenAI and Cohere's offerings through the `openai` and `cohere` libraries, respectively.",
        "question": "Which model providers offer LLMs?",
    }
)

response = model.invoke(prompt)
print(response)


================================================
FILE: ch1/py/f-chat-prompt.py
================================================
from langchain_core.prompts import ChatPromptTemplate

template = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            'Answer the question based on the context below. If the question cannot be answered using the information provided, answer with "I don\'t know".',
        ),
        ("human", "Context: {context}"),
        ("human", "Question: {question}"),
    ]
)

response = template.invoke(
    {
        "context": "The most recent advancements in NLP are being driven by Large Language Models (LLMs). These models outperform their smaller counterparts and have become invaluable for developers who are creating applications with NLP capabilities. Developers can tap into these models through Hugging Face's `transformers` library, or by utilizing OpenAI and Cohere's offerings through the `openai` and `cohere` libraries, respectively.",
        "question": "Which model providers offer LLMs?",
    }
)

print(response)


================================================
FILE: ch1/py/g-chat-prompt-model.py
================================================
from langchain_openai.chat_models import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

# both `template` and `model` can be reused many times

template = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            'Answer the question based on the context below. If the question cannot be answered using the information provided, answer with "I don\'t know".',
        ),
        ("human", "Context: {context}"),
        ("human", "Question: {question}"),
    ]
)

model = ChatOpenAI()

# `prompt` and `completion` are the results of using template and model once

prompt = template.invoke(
    {
        "context": "The most recent advancements in NLP are being driven by Large Language Models (LLMs). These models outperform their smaller counterparts and have become invaluable for developers who are creating applications with NLP capabilities. Developers can tap into these models through Hugging Face's `transformers` library, or by utilizing OpenAI and Cohere's offerings through the `openai` and `cohere` libraries, respectively.",
        "question": "Which model providers offer LLMs?",
    }
)

print(model.invoke(prompt))


================================================
FILE: ch1/py/h-structured.py
================================================
from langchain_openai import ChatOpenAI
from pydantic import BaseModel


class AnswerWithJustification(BaseModel):
    """An answer to the user's question along with justification for the answer."""

    answer: str
    """The answer to the user's question"""
    justification: str
    """Justification for the answer"""


llm = ChatOpenAI(model="gpt-3.5", temperature=0)
structured_llm = llm.with_structured_output(AnswerWithJustification)

response = structured_llm.invoke(
    "What weighs more, a pound of bricks or a pound of feathers")
print(response)


================================================
FILE: ch1/py/i-csv.py
================================================
from langchain_core.output_parsers import CommaSeparatedListOutputParser

parser = CommaSeparatedListOutputParser()

response = parser.invoke("apple, banana, cherry")
print(response)


================================================
FILE: ch1/py/j-methods.py
================================================
from langchain_openai.chat_models import ChatOpenAI

model = ChatOpenAI(model="gpt-3.5-turbo")

completion = model.invoke("Hi there!")
# Hi!

completions = model.batch(["Hi there!", "Bye!"])
# ['Hi!', 'See you!']

for token in model.stream("Bye!"):
    print(token)
    # Good
    # bye
    # !


================================================
FILE: ch1/py/k-imperative.py
================================================
from langchain_openai.chat_models import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import chain

# the building blocks

template = ChatPromptTemplate.from_messages(
    [
        ("system", "You are a helpful assistant."),
        ("human", "{question}"),
    ]
)

model = ChatOpenAI(model="gpt-3.5-turbo")

# combine them in a function
# @chain decorator adds the same Runnable interface for any function you write


@chain
def chatbot(values):
    prompt = template.invoke(values)
    return model.invoke(prompt)


# use it

response = chatbot.invoke({"question": "Which model providers offer LLMs?"})
print(response.content)


================================================
FILE: ch1/py/ka-stream.py
================================================
from langchain_core.runnables import chain
from langchain_openai.chat_models import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate


model = ChatOpenAI(model="gpt-3.5-turbo")


template = ChatPromptTemplate.from_messages(
    [
        ("system", "You are a helpful assistant."),
        ("human", "{question}"),
    ]
)


@chain
def chatbot(values):
    prompt = template.invoke(values)
    for token in model.stream(prompt):
        yield token


for part in chatbot.stream({"question": "Which model providers offer LLMs?"}):
    print(part)


================================================
FILE: ch1/py/kb-async.py
================================================
from langchain_core.runnables import chain
from langchain_openai.chat_models import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

template = ChatPromptTemplate.from_messages(
    [
        ("system", "You are a helpful assistant."),
        ("human", "{question}"),
    ]
)

model = ChatOpenAI(model="gpt-3.5-turbo")


@chain
async def chatbot(values):
    prompt = await template.ainvoke(values)
    return await model.ainvoke(prompt)


async def main():
    return await chatbot.ainvoke({"question": "Which model providers offer LLMs?"})

if __name__ == "__main__":
    import asyncio
    print(asyncio.run(main()))


================================================
FILE: ch1/py/l-declarative.py
================================================
from langchain_openai.chat_models import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

# the building blocks

template = ChatPromptTemplate.from_messages(
    [
        ("system", "You are a helpful assistant."),
        ("human", "{question}"),
    ]
)

model = ChatOpenAI()

# combine them with the | operator

chatbot = template | model

# use it

response = chatbot.invoke({"question": "Which model providers offer LLMs?"})
print(response.content)

# streaming

for part in chatbot.stream({"question": "Which model providers offer LLMs?"}):
    print(part)


================================================
FILE: ch10/js/agent-evaluation-rag.js
================================================
import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { evaluate } from 'langsmith/evaluation';
import { traceable } from 'langsmith/traceable';
import { graph } from './rag-graph.js';
import { z } from 'zod';

const defaultDataset = 'langchain-blogs-qa';

const experimentPrefix = 'langchain-blogs-qa-evals';

const llm = new ChatOpenAI({ model: 'gpt-4o', temperature: 0 });

const EVALUATION_PROMPT = `You are a teacher grading a quiz.

You will be given a QUESTION, the GROUND TRUTH (correct) RESPONSE, and the STUDENT RESPONSE.

Here is the grade criteria to follow:
(1) Grade the student responses based ONLY on their factual accuracy relative to the ground truth answer.
(2) Ensure that the student response does not contain any conflicting statements.
(3) It is OK if the student response contains more information than the ground truth response, as long as it is factually accurate relative to the  ground truth response.

Correctness:
True means that the student's response meets all of the criteria.
False means that the student's response does not meet all of the criteria.

Explain your reasoning in a step-by-step manner to ensure your reasoning and conclusion are correct.`;

const userPrompt = `QUESTION: {question}
GROUND TRUTH RESPONSE: {reference}
STUDENT RESPONSE: {answer}`;

const prompt = ChatPromptTemplate.fromMessages([
  ['system', EVALUATION_PROMPT],
  ['user', userPrompt],
]);

// LLM-as-judge output schema

const grade = z
  .object({
    reasoning: z
      .string()
      .describe(
        'Explain your reasoning for whether the actual response is correct or not.'
      ),
    isCorrect: z
      .boolean()
      .describe(
        'True if the student response is mostly or exactly correct, otherwise False.'
      ),
  })
  .describe(
    'Compare the expected and actual answers and grade the actual answer.'
  );

const graderLlm = prompt.pipe(llm.withStructuredOutput(grade));

const evaluateAgent = async (run, example) => {
  const question = run.inputs.question;
  const answer = run.outputs.answer;
  const reference = example.outputs.answer;

  const grade = await graderLlm.invoke({ question, reference, answer });
  const isCorrect = grade.isCorrect;

  return { key: 'correct', score: Number(isCorrect) };
};

const runGraph = traceable(async (inputs) => {
  const answer = await graph.invoke({ question: inputs.question });
  return { answer: answer.answer };
});

await evaluate((inputs) => runGraph(inputs), {
  data: defaultDataset,
  evaluators: [evaluateAgent],
  experimentPrefix,
  maxConcurrency: 4,
});


================================================
FILE: ch10/js/agent-evaluation-sql.js
================================================
import { ChatOpenAI } from '@langchain/openai';
import { graph } from './agent-sql-graph.js';
import crypto from 'crypto';
import * as hub from 'langchain/hub';
import { evaluate } from 'langsmith/evaluation';
import { traceable } from 'langsmith/traceable';
import { z } from 'zod';

const thread_id = crypto.randomUUID();
const config = {
  configurable: {
    thread_id: thread_id,
  },
};

const llm = new ChatOpenAI({ model: 'gpt-4o-mini', temperature: 0 });

const predictSQLAgentAnswer = traceable(async (example) => {
  const messages = await graph.invoke(
    { messages: ['user', example.input] },
    config
  );
  return { response: messages.messages[messages.messages.length - 1].content };
});

const gradePromptAnswerAccuracy = await hub.pull(
  'langchain-ai/rag-answer-vs-reference'
);

const grade = z.object({
  score: z.number(),
});

const answerEvaluator = async (run, example) => {
  const input_question = example.inputs['input'];
  const reference = example.outputs['output'];
  const prediction = run.outputs['response'];

  const grader = gradePromptAnswerAccuracy.pipe(llm);
  const score = await grader.invoke({
    question: input_question,
    correct_answer: reference,
    student_answer: prediction,
  });
  return { key: 'answer_v_reference_score', score: score.Score };
};

const datasetName = 'sql-agent-response';
const experimentPrefix = 'sql-agent-gpt4o';

const experimentResults = await evaluate(
  (inputs) => predictSQLAgentAnswer(inputs),
  {
    data: datasetName,
    evaluators: [answerEvaluator],
    experimentPrefix,
    maxConcurrency: 4,
  }
);

// Single tool evaluation
const predictAssistant = traceable(async (example) => {
  const result = await graph.invoke(
    { messages: [['user', example.input]] },
    config
  );
  return { response: result };
});

const checkSpecificToolCall = async (run, example) => {
  const response = run.outputs['response'];
  const messages = response.messages;

  let firstToolCall = null;
  for (const message of messages) {
    if (message.tool_calls?.length > 0) {
      // Get the name of the first tool call in the message
      firstToolCall = message.tool_calls[0].name;
      break;
    }
  }

  const expected_tool_call = 'list-tables-sql';
  const score = firstToolCall === expected_tool_call ? 1 : 0;

  return {
    key: 'single_tool_call',
    score: score,
  };
};

const singleToolCallResults = await evaluate(
  (inputs) => predictAssistant(inputs),
  {
    data: datasetName,
    evaluators: [checkSpecificToolCall],
    experimentPrefix: `${experimentPrefix}-single-tool`,
    maxConcurrency: 4,
  }
);

const EXPECTED_TOOLS = {
  LIST_TABLES: 'list-tables-sql',
  SCHEMA: 'info-sql',
  QUERY_CHECK: 'query-checker',
  QUERY_EXEC: 'query-sql',
  RESULT_CHECK: 'checkResult',
};

const containsAllToolCallsAnyOrder = async ({ run, example }) => {
  const expected = [
    EXPECTED_TOOLS.LIST_TABLES,
    EXPECTED_TOOLS.SCHEMA,
    EXPECTED_TOOLS.QUERY_CHECK,
    EXPECTED_TOOLS.QUERY_EXEC,
  ];

  const messages = run.outputs?.response?.messages || [];
  const toolCalls = Array.from(
    new Set(
      messages.flatMap(
        (m) => m.tool_calls?.map((tc) => tc.name) || m.name || []
      )
    )
  );

  const score = expected.every((tool) => toolCalls.includes(tool)) ? 1 : 0;
  return { key: 'multi_tool_call_any_order', score };
};

const containsAllToolCallsInOrder = async ({ run, example }) => {
  const expectedSequence = [
    EXPECTED_TOOLS.LIST_TABLES,
    EXPECTED_TOOLS.SCHEMA,
    EXPECTED_TOOLS.QUERY_CHECK,
    EXPECTED_TOOLS.QUERY_EXEC,
  ];

  const messages = run.outputs?.response?.messages || [];
  const toolCalls = messages.flatMap(
    (m) => m.tool_calls?.map((tc) => tc.name) || m.name || []
  );

  let seqIndex = 0;
  for (const call of toolCalls) {
    if (call === expectedSequence[seqIndex]) {
      seqIndex++;
      if (seqIndex === expectedSequence.length) break;
    }
  }

  return {
    key: 'multi_tool_call_in_order',
    score: seqIndex === expectedSequence.length ? 1 : 0,
  };
};

const containsAllToolCallsExactOrder = async ({ run, example }) => {
  const expectedSequence = [
    EXPECTED_TOOLS.LIST_TABLES,
    EXPECTED_TOOLS.SCHEMA,
    EXPECTED_TOOLS.QUERY_CHECK,
    EXPECTED_TOOLS.QUERY_EXEC,
  ];

  const messages = run.outputs?.response?.messages || [];
  const toolCalls = messages.flatMap(
    (m) => m.tool_calls?.map((tc) => tc.name) || m.name || []
  );

  // Find the first occurrence sequence
  const firstOccurrences = [];
  for (const call of toolCalls) {
    if (call === expectedSequence[firstOccurrences.length]) {
      firstOccurrences.push(call);
      if (firstOccurrences.length === expectedSequence.length) break;
    }
  }

  const score =
    JSON.stringify(firstOccurrences) === JSON.stringify(expectedSequence)
      ? 1
      : 0;
  return { key: 'multi_tool_call_exact_order', score };
};

// Prediction functions for different evaluation types
const predictSqlAgentMessages = traceable(async (example) => {
  const result = await graph.invoke(
    { messages: [['user', example.input]] },
    config
  );
  return { response: result };
});

// Trajectory Evaluation Execution
const trajectoryResults = await evaluate(
  (inputs) => predictSqlAgentMessages(inputs),
  {
    data: datasetName,
    evaluators: [
      containsAllToolCallsAnyOrder,
      containsAllToolCallsInOrder,
      containsAllToolCallsExactOrder,
    ],
    experimentPrefix: `${experimentPrefix}-full-trajectory`,
    maxConcurrency: 4,
  }
);


================================================
FILE: ch10/js/agent-sql-graph.js
================================================
import { ChatOpenAI } from '@langchain/openai';
import { SqlDatabase } from 'langchain/sql_db';
import { DataSource } from 'typeorm';
import { SqlToolkit } from 'langchain/agents/toolkits/sql';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { z } from 'zod';
import { tool } from '@langchain/core/tools';
import { ToolNode } from '@langchain/langgraph/prebuilt';
import { SqliteSaver } from '@langchain/langgraph-checkpoint-sqlite';
import {
  StateGraph,
  MessagesAnnotation,
  END,
  START,
} from '@langchain/langgraph';
import Database from 'better-sqlite3';

// LLM
const llm = new ChatOpenAI({ model: 'gpt-4o', temperature: 0 });

// SQL toolkit
const datasource = new DataSource({
  type: 'sqlite',
  database: 'Chinook_Sqlite.sqlite',
});

const db = await SqlDatabase.fromDataSourceParams({
  appDataSource: datasource,
});

console.log(db.allTables.map((t) => t.tableName));

const toolkit = new SqlToolkit(db, llm);
const tools = toolkit.getTools();

// Query checking
const queryCheckSystemPrompt = `You are a SQL expert with a strong attention to detail.
Double check the SQLite query for common mistakes, including:
- Using NOT IN with NULL values
- Using UNION when UNION ALL should have been used
- Using BETWEEN for exclusive ranges
- Data type mismatch in predicates
- Properly quoting identifiers
- Using the correct number of arguments for functions
- Casting to the correct data type
- Using the proper columns for joins

If there are any of the above mistakes, rewrite the query. If there are no mistakes, just reproduce the original query.

Execute the correct query with the appropriate tool.`;

const queryCheckPrompt = ChatPromptTemplate.fromMessages([
  ['system', queryCheckSystemPrompt],
  ['user', '{query}'],
]);

const queryChain = queryCheckPrompt.pipe(llm);

const checkQueryTool = tool(
  async (input) => {
    const res = await queryChain.invoke(input.query);
    return res.content;
  },
  {
    name: 'checkQuery',
    description:
      'Use this tool to double check if your query is correct before executing it.',
    schema: z.object({
      query: z.string(),
    }),
  }
);

// Query result checking
const queryResultCheckSystemPrompt = `You are grading the result of a SQL query from a DB. 
- Check that the result is not empty.
- If it is empty, instruct the system to re-try!`;

const queryResultCheckPrompt = ChatPromptTemplate.fromMessages([
  ['system', queryResultCheckSystemPrompt],
  ['user', '{query_result}'],
]);

const queryResultChain = queryResultCheckPrompt.pipe(llm);

const checkResultTool = tool(
  async (input) => {
    const res = await queryResultChain.invoke(input.query);
    return res.content;
  },
  {
    name: 'checkResult',
    description:
      'Use this tool to check the query result from the database to confirm it is not empty and is relevant.',
    schema: z.object({
      query_result: z.string(),
    }),
  }
);

tools.push(checkQueryTool, checkResultTool);

// Assistant runnable
const queryGenSystem = `
ROLE:
You are an agent designed to interact with a SQL database. You have access to tools for interacting with the database.
GOAL:
Given an input question, create a syntactically correct SQLite query to run, then look at the results of the query and return the answer.
INSTRUCTIONS:
- Only use the below tools for the following operations.
- Only use the information returned by the below tools to construct your final answer.
- To start you should ALWAYS look at the tables in the database to see what you can query. Do NOT skip this step.
- Then you should query the schema of the most relevant tables.
- Write your query based upon the schema of the tables. You MUST double check your query before executing it. 
- Unless the user specifies a specific number of examples they wish to obtain, always limit your query to at most 5 results.
- You can order the results by a relevant column to return the most interesting examples in the database.
- Never query for all the columns from a specific table, only ask for the relevant columns given the question.
- If you get an error while executing a query, rewrite the query and try again.
- If the query returns a result, use check_result tool to check the query result.
- If the query result result is empty, think about the table schema, rewrite the query, and try again.
- DO NOT make any DML statements (INSERT, UPDATE, DELETE, DROP etc.) to the database.
`;

const queryGenPrompt = ChatPromptTemplate.fromMessages([
  ['system', queryGenSystem],
  ['placeholder', '{messages}'],
]);

const modelWithTools = queryGenPrompt.pipe(llm.bindTools(tools));

const handleToolError = async (state) => {
  const { messages } = state;
  const toolsByName = {
    checkQuery: checkQueryTool,
    checkResult: checkResultTool,
  };
  const lastMessage = messages[messages.length - 1];
  const outputMessages = [];
  for (const toolCall of lastMessage.tool_calls) {
    try {
      const toolResult = await toolsByName[toolCall.name].invoke(toolCall);
      outputMessages.push(toolResult);
    } catch (error) {
      // Return the error if the tool call fails
      outputMessages.push(
        new ToolMessage({
          content: error.message,
          name: toolCall.name,
          tool_call_id: toolCall.id,
          additional_kwargs: { error },
        })
      );
    }
  }
  return { messages: outputMessages };
};

const toolNodeForGraph = new ToolNode(tools).withFallbacks([handleToolError]);

const shouldContinue = (state) => {
  const { messages } = state;
  const lastMessage = messages[messages.length - 1];
  if (
    'tool_calls' in lastMessage &&
    Array.isArray(lastMessage.tool_calls) &&
    lastMessage.tool_calls?.length
  ) {
    return 'tools';
  }
  return END;
};

export const callModel = async (state) => {
  const { messages } = state;
  const response = await modelWithTools.invoke({ messages });
  return { messages: response };
};

const builder = new StateGraph(MessagesAnnotation)
  // Define the two nodes we will cycle between
  .addNode('agent', callModel)
  .addNode('tools', toolNodeForGraph)
  .addEdge(START, 'agent')
  .addConditionalEdges('agent', shouldContinue, ['tools', END])
  .addEdge('tools', 'agent');

const memory = new SqliteSaver(new Database(':memory:'));
export const graph = builder.compile({ checkpointer: memory });


================================================
FILE: ch10/js/create-rag-dataset.js
================================================
import { Client } from 'langsmith';
const client = new Client();

const exampleInputs = [
  [
    'Which companies are highlighted as top LangGraph agent adopters in 2024?',
    'The top adopters include Uber (code migration tools), AppFolio (property management copilot), LinkedIn (SQL Bot), Elastic (AI assistant), and Replit (multi-agent development platform) :cite[3].',
  ],
  [
    "How did AppFolio's AI copilot impact property managers?",
    "AppFolio's Realm-X AI copilot saved property managers over 10 hours per week by automating queries, bulk actions, and scheduling :cite[3].",
  ],
  [
    'What infrastructure trends dominated LLM usage in 2024?',
    'OpenAI remained the top LLM provider (6x more usage than Ollama), while open-source models via Ollama and Groq surged. Chroma and FAISS led vector stores, with MongoDB and Elastic gaining traction :cite[2]:cite[5].',
  ],
  [
    'How did LangGraph improve agent workflows compared to 2023?',
    'LangGraph usage grew to 43% of LangSmith organizations, with 21.9% of traces involving tool calls (up from 0.5% in 2023), enabling complex multi-step tasks like database writes :cite[2]:cite[7].',
  ],
  [
    "What distinguishes Replit's LangGraph implementation?",
    "Replit's agent emphasizes human-in-the-loop validation and a multi-agent architecture for code generation, combining autonomy with controlled outputs :cite[3].",
  ],
];

const datasetName = 'langchain-blogs-qa';

// Create dataset
const dataset = await client.createDataset(datasetName, {
  description: 'Langchain blogs QA.',
});

// Prepare inputs, outputs, and metadata for bulk creation
const inputs = exampleInputs.map(([inputPrompt]) => ({
  question: inputPrompt,
}));

const outputs = exampleInputs.map(([, outputAnswer]) => ({
  answer: outputAnswer,
}));

const metadata = exampleInputs.map(() => ({ source: 'LangChain Blog' }));

// Use the bulk createExamples method
await client.createExamples({
  inputs,
  outputs,
  metadata,
  datasetId: dataset.id,
});

console.log(
  `Dataset created in langsmith with ID: ${dataset.id}\n Navigate to ${dataset.url}.`
);


================================================
FILE: ch10/js/create-sql-dataset.js
================================================
import { Client } from 'langsmith';
const client = new Client();

const exampleInputs = [
  [
    "Which country's customers spent the most? And how much did they spend?",
    'The country whose customers spent the most is the USA, with a total expenditure of $523.06',
  ],
  [
    'What was the most purchased track of 2013?',
    'The most purchased track of 2013 was Hot Girl.',
  ],
  [
    'How many albums does the artist Led Zeppelin have?',
    'Led Zeppelin has 14 albums',
  ],
  [
    "What is the total price for the album 'Big Ones'?",
    'The total price for the album "Big Ones" is 14.85',
  ],
  [
    'Which sales agent made the most in sales in 2009?',
    'Steve Johnson made the most sales in 2009',
  ],
];

const datasetName = 'sql-agent-response';

if (!(await client.hasDataset({ datasetName }))) {
  client.createDataset(datasetName);

  // Prepare inputs, outputs, and metadata for bulk creation
  const inputs = exampleInputs.map(([inputPrompt]) => ({
    question: inputPrompt,
  }));

  const outputs = exampleInputs.map(([, outputAnswer]) => ({
    answer: outputAnswer,
  }));

  await client.createExamples({
    inputs,
    outputs,
    datasetId: dataset.id,
  });
}


================================================
FILE: ch10/js/rag-graph.js
================================================
import { Annotation, StateGraph } from '@langchain/langgraph';
import { CheerioWebBaseLoader } from '@langchain/community/document_loaders/web/cheerio';
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
import { ChatOpenAI, OpenAIEmbeddings } from '@langchain/openai';
import * as hub from 'langchain/hub';
import { StringOutputParser } from '@langchain/core/output_parsers';

const GraphState = Annotation.Root({
  question: Annotation(),
  scrapedDocuments: Annotation(),
  vectorstore: Annotation(),
  answer: Annotation(),
});

const scrapeBlogPosts = async (state) => {
  const urls = [
    'https://blog.langchain.dev/top-5-langgraph-agents-in-production-2024/',
    'https://blog.langchain.dev/langchain-state-of-ai-2024/',
    'https://blog.langchain.dev/introducing-ambient-agents/',
  ];

  const loadDocs = async (urls) => {
    const docs = [];
    for (const url of urls) {
      const loader = new CheerioWebBaseLoader(url);
      const loadedDocs = await loader.load();
      docs.push(...loadedDocs);
    }
    return docs;
  };

  const scrapedDocuments = await loadDocs(urls);

  return { scrapedDocuments };
};

const indexing = async (state) => {
  const textSplitter = new RecursiveCharacterTextSplitter({
    chunkSize: 1000,
    chunkOverlap: 0,
  });

  const docSplits = await textSplitter.splitDocuments(state.scrapedDocuments);

  const vectorstore = new MemoryVectorStore(new OpenAIEmbeddings());

  await vectorstore.addDocuments(docSplits);

  console.log('vectorstore: ', vectorstore);

  return { vectorstore };
};

const retrieveAndGenerate = async (state) => {
  const { question, vectorstore } = state;

  const retriever = vectorstore.asRetriever();

  const prompt = await hub.pull('rlm/rag-prompt');

  const llm = new ChatOpenAI({ model: 'gpt-3.5-turbo', temperature: 0 });

  const docs = await retriever.invoke(question);

  const chain = prompt.pipe(llm).pipe(new StringOutputParser());

  const answer = await chain.invoke({ context: docs, question });

  console.log('answer: ', answer);

  return { answer };
};

const workflow = new StateGraph(GraphState)
  .addNode('retrieve_and_generate', retrieveAndGenerate)
  .addNode('scrape_blog_posts', scrapeBlogPosts)
  .addNode('indexing', indexing)
  .addEdge('__start__', 'scrape_blog_posts')
  .addEdge('scrape_blog_posts', 'indexing')
  .addEdge('indexing', 'retrieve_and_generate')
  .addEdge('retrieve_and_generate', '__end__');

const graph = workflow.compile();

await graph.invoke({ question: 'What are ambient agents?' });


================================================
FILE: ch10/js/retrieve-and-grade.js
================================================
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';
import { CheerioWebBaseLoader } from '@langchain/community/document_loaders/web/cheerio';
import { InMemoryVectorStore } from '@langchain/community/vectorstores/in_memory';
import { OpenAIEmbeddings } from '@langchain/openai';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { z } from 'zod';
import { ChatOpenAI } from '@langchain/openai';

const urls = [
  'https://blog.langchain.dev/top-5-langgraph-agents-in-production-2024/',
  'https://blog.langchain.dev/langchain-state-of-ai-2024/',
  'https://blog.langchain.dev/introducing-ambient-agents/',
];

// Load documents from URLs
const loadDocs = async (urls) => {
  const docs = [];
  for (const url of urls) {
    const loader = new CheerioWebBaseLoader(url);
    const loadedDocs = await loader.load();
    docs.push(...loadedDocs);
  }
  return docs;
};

const docsList = await loadDocs(urls);

// Initialize the text splitter
const textSplitter = new RecursiveCharacterTextSplitter({
  chunkSize: 250,
  chunkOverlap: 0,
});

// Split the documents into smaller chunks
const docSplits = textSplitter.splitDocuments(docsList);

// Add to vector database
const vectorstore = await InMemoryVectorStore.fromDocuments(
  docSplits,
  new OpenAIEmbeddings()
);

const retriever = vectorstore.asRetriever(); // The `retriever` object can now be used for querying

const question = 'What are 2 LangGraph agents used in production in 2024?';

const docs = retriever.invoke(question);

console.log('Retrieved documents: \n', docs[0].page_content);

// Define the schema using Zod
const GradeDocumentsSchema = z.object({
  binary_score: z
    .string()
    .describe("Documents are relevant to the question, 'yes' or 'no'"),
});

// Initialize LLM with structured output using Zod schema
const llm = new ChatOpenAI({ model: 'gpt-3.5-turbo', temperature: 0 });
const structuredLLMGrader = llm.withStructuredOutput(GradeDocumentsSchema);

// System and prompt template
const systemMessage = `You are a grader assessing relevance of a retrieved document to a user question. If the document contains keyword(s) or semantic meaning related to the question, grade it as relevant. Give a binary score 'yes' or 'no' to indicate whether the document is relevant to the question.`;
const gradePrompt = ChatPromptTemplate.fromMessages([
  { role: 'system', content: systemMessage },
  {
    role: 'human',
    content:
      'Retrieved document: \n\n {document} \n\n User question: {question}',
  },
]);

// Combine prompt with the structured output
const retrievalGrader = gradePrompt.pipe(structuredLLMGrader);

// Grade retrieved documents
const results = await retrievalGrader.invoke({
  question,
  document: docs[0].page_content,
});

console.log('\n\nGrading results: \n', results);


================================================
FILE: ch10/js/search-graph.js
================================================
import { Annotation, StateGraph, START, END } from '@langchain/langgraph';
import { StringOutputParser } from '@langchain/core/output_parsers';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { ChatOpenAI } from '@langchain/openai';
import { DuckDuckGoSearch } from '@langchain/community/tools/duckduckgo_search';
import * as hub from 'langchain/hub';
import { retriever, retrievalGrader } from './retrieve-and-grade.js';
import { Document } from '@langchain/core/documents';

// LLM setup
const prompt = await hub.pull('rlm/rag-prompt');
const llm = new ChatOpenAI({ modelName: 'gpt-4', temperature: 0 }); // Fixed model name
const ragChain = prompt.pipe(llm).pipe(new StringOutputParser());
const webSearchTool = new DuckDuckGoSearch();
// Question rewriting prompt
const rewritePrompt = ChatPromptTemplate.fromMessages([
  [
    'system',
    `You are a question re-writer that converts an input question to a better version that is optimized 
     for web search. Look at the input and try to reason about the underlying semantic intent / meaning.`,
  ],
  [
    'human',
    'Here is the initial question: \n\n {question} \n Formulate an improved question.',
  ],
]);

const questionRewriter = rewritePrompt.pipe(llm).pipe(new StringOutputParser());

// Create the graph state
const GraphState = Annotation.Root({
  question: Annotation(),
  generation: Annotation(),
  webSearch: Annotation(),
  documents: Annotation({
    reducer: (currentState, updateValue) => currentState.concat(updateValue),
    default: () => [],
  }),
});

// Node functions
const retrieve = async (state) => {
  const documents = await retriever.invoke(state.question);
  return { question: state.question, documents };
};

const generate = async (state) => {
  const { question, documents } = state;
  const context = documents.map((doc) => doc.pageContent).join('\n');
  const generation = await ragChain.invoke({ context, question });
  console.log('Final answer:', generation);
  return { documents, question, generation };
};

const gradeDocuments = async (state) => {
  const { question, documents } = state;
  const filteredDocs = [];
  let webSearch = 'No';

  for (const doc of documents) {
    try {
      const score = await retrievalGrader.invoke({
        question: question,
        document: doc.pageContent,
      });

      if (score.binary_score === 'yes') {
        console.log('---GRADE: DOCUMENT RELEVANT---');
        filteredDocs.push(doc);
      } else {
        console.log('---GRADE: DOCUMENT NOT RELEVANT---');
        webSearch = 'Yes';
      }
    } catch (error) {
      console.error('Error grading document:', error);
      webSearch = 'Yes';
    }
  }

  return {
    documents: filteredDocs,
    question,
    webSearch,
  };
};

const transformQuery = async (state) => {
  const betterQuestion = await questionRewriter.invoke({
    question: state.question,
  });
  console.log('Transformed question:', betterQuestion);
  return { documents: state.documents, question: betterQuestion };
};

const webSearch = async (state) => {
  console.log('---WEB SEARCH---');
  const webResult = await webSearchTool.invoke(state.question);
  const webResultsDocument = new Document({ pageContent: webResult });

  // Fixed document concatenation
  const updatedDocuments = [...state.documents, webResultsDocument];

  return { documents: updatedDocuments, question: state.question };
};

// Routing function
const generateRoute = (state) => {
  return state.webSearch === 'Yes' ? 'transform_query' : 'generate';
};

// Create and compile the graph
const workflow = new StateGraph(GraphState)
  .addNode('retrieve', retrieve)
  .addNode('grade_documents', gradeDocuments)
  .addNode('transform_query', transformQuery)
  .addNode('generate', generate)
  .addNode('web_search_node', webSearch)
  .addEdge(START, 'retrieve')
  .addEdge('retrieve', 'grade_documents')
  .addConditionalEdges('grade_documents', generateRoute)
  .addEdge('transform_query', 'web_search_node')
  .addEdge('web_search_node', 'generate')
  .addEdge('generate', END);

const graph = workflow.compile();

const result = await graph.invoke({
  question: 'What are the Top 5 LangGraph Agents in Production 2024?',
});

console.log(result);


================================================
FILE: ch10/py/agent_evaluation_rag.py
================================================
from typing import Optional

from langchain_openai import ChatOpenAI
from langsmith import Client, evaluate, aevaluate
from langsmith.evaluation import EvaluationResults
from pydantic import BaseModel, Field
from typing_extensions import Annotated, TypedDict
from rag_graph import graph

client = Client()

DEFAULT_DATASET_NAME = "langchain-blogs-qa"

llm = ChatOpenAI(model="gpt-4o", temperature=0)

EVALUATION_PROMPT = f"""You are a teacher grading a quiz.

You will be given a QUESTION, the GROUND TRUTH (correct) RESPONSE, and the STUDENT RESPONSE.

Here is the grade criteria to follow:
(1) Grade the student responses based ONLY on their factual accuracy relative to the ground truth answer.
(2) Ensure that the student response does not contain any conflicting statements.
(3) It is OK if the student response contains more information than the ground truth response, as long as it is factually accurate relative to the  ground truth response.

Correctness:
True means that the student's response meets all of the criteria.
False means that the student's response does not meet all of the criteria.

Explain your reasoning in a step-by-step manner to ensure your reasoning and conclusion are correct."""

# LLM-as-judge output schema


class Grade(TypedDict):
    """Compare the expected and actual answers and grade the actual answer."""
    reasoning: Annotated[str, ...,
                         "Explain your reasoning for whether the actual response is correct or not."]
    is_correct: Annotated[bool, ...,
                          "True if the student response is mostly or exactly correct, otherwise False."]


grader_llm = llm.with_structured_output(Grade)
# PUBLIC API


def transform_dataset_inputs(inputs: dict) -> dict:
    """Transform LangSmith dataset inputs to match the agent's input schema before invoking the agent."""
    # see the `Example input` in the README for reference on what `inputs` dict should look like
    # the dataset inputs already match the agent's input schema, but you can add any additional processing here
    return inputs


def transform_agent_outputs(outputs: dict) -> dict:
    """Transform agent outputs to match the LangSmith dataset output schema."""
    # see the `Example output` in the README for reference on what the output should look like
    return {"info": outputs["info"]}

# Evaluator function


async def evaluate_agent(inputs: dict, outputs: dict, reference_outputs: dict) -> bool:
    """Evaluate if the final response is equivalent to reference response."""

    # Note that we assume the outputs has a 'response' dictionary. We'll need to make sure
    # that the target function we define includes this key.
    user = f"""QUESTION: {inputs['question']}
    GROUND TRUTH RESPONSE: {reference_outputs['answer']}
    STUDENT RESPONSE: {outputs['answer']}"""

    grade = await grader_llm.ainvoke([{"role": "system", "content": EVALUATION_PROMPT}, {"role": "user", "content": user}])
    is_correct = grade["is_correct"]
    return is_correct


# Target function
async def run_graph(inputs: dict) -> dict:
    """Run graph and track the trajectory it takes along with the final response."""
    result = await graph.ainvoke({
        "question": inputs["question"]
    })
    return {"answer": result["answer"].content}

# run evaluation


async def run_eval(
    dataset_name: str,
    experiment_prefix: Optional[str] = None,
) -> EvaluationResults:
    dataset = client.read_dataset(dataset_name=dataset_name)
    results = await aevaluate(
        run_graph,
        data=dataset,
        evaluators=[evaluate_agent],
        experiment_prefix=experiment_prefix,
    )
    return results


async def main():
    experiment_results = await run_eval(dataset_name=DEFAULT_DATASET_NAME,
                                        experiment_prefix="langchain-blogs-qa-evals")

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())


================================================
FILE: ch10/py/agent_evaluation_sql.py
================================================
from agent_sql_graph import builder
from langchain import hub
from langchain_openai import ChatOpenAI
from langsmith.evaluation import evaluate
from langsmith.schemas import Example, Run
from langchain_core.runnables import Runnable
from agent_sql_graph import assistant_runnable
import uuid
_printed = set()
thread_id = str(uuid.uuid4())
experiment_prefix = "sql-agent-gpt4o"
metadata = "chinook-gpt-4o-base-case-agent"
config = {
    "configurable": {
        # Checkpoints are accessed by thread_id
        "thread_id": thread_id,
    }
}


def predict_sql_agent_answer(example: dict):
    """Use this for answer evaluation"""
    msg = {"messages": ("user", example["input"])}
    messages = graph.invoke(msg, config)
    return {"response": messages['messages'][-1].content}


# Grade prompt
grade_prompt_answer_accuracy = hub.pull(
    "langchain-ai/rag-answer-vs-reference")


def answer_evaluator(run, example) -> dict:
    """
    A simple evaluator for RAG answer accuracy
    """

    # Get question, ground truth answer, chain answer
    input_question = example.inputs["input"]
    reference = example.outputs["output"]
    prediction = run.outputs["response"]

    # LLM grader
    llm = ChatOpenAI(model="gpt-4o", temperature=0)

    # Structured prompt
    answer_grader = grade_prompt_answer_accuracy | llm

    # Run evaluator
    score = answer_grader.invoke({"question": input_question,
                                  "correct_answer": reference,
                                  "student_answer": prediction})
    score = score["Score"]

    return {"key": "answer_v_reference_score", "score": score}


dataset_name = "sql-agent-response"
experiment_results = evaluate(
    predict_sql_agent_answer,
    data=dataset_name,
    evaluators=[answer_evaluator],
    num_repetitions=3,
    experiment_prefix=experiment_prefix,
    metadata={"version": metadata},
)


"""
Single tool evaluation

"""


def predict_assistant(example: dict):
    """Invoke assistant for single tool call evaluation"""
    msg = [("user", example["input"])]
    result = assistant_runnable.invoke({"messages": msg})
    return {"response": result}


def check_specific_tool_call(root_run: Run, example: Example) -> dict:
    """
    Check if the first tool call in the response matches the expected tool call.
    """

    # Exepected tool call
    expected_tool_call = 'sql_db_list_tables'

    # Run
    response = root_run.outputs["response"]

    # Get tool call
    try:
        tool_call = getattr(response, 'tool_calls', [])[0]['name']

    except (IndexError, KeyError):
        tool_call = None

    score = 1 if tool_call == expected_tool_call else 0
    return {"score": score, "key": "single_tool_call"}


experiment_results = evaluate(
    predict_assistant,
    data=dataset_name,
    evaluators=[check_specific_tool_call],
    experiment_prefix=experiment_prefix + "-single-tool",
    num_repetitions=3,
    metadata={"version": metadata},
)


"""
Agent trajectory evaluation
"""


def predict_sql_agent_messages(example: dict):
    """Use this for answer evaluation"""
    msg = {"messages": ("user", example["input"])}
    graph = builder.compile()
    messages = graph.invoke(msg, config)
    return {"response": messages}


def find_tool_calls(messages):
    """  
    Find all tool calls in the messages returned 
    """
    tool_calls = [tc['name'] for m in messages['messages']
                  for tc in getattr(m, 'tool_calls', [])]
    return tool_calls


def contains_all_tool_calls_any_order(root_run: Run, example: Example) -> dict:
    """
    Check if all expected tools are called in any order.
    """
    expected = ['sql_db_list_tables', 'sql_db_schema',
                'sql_db_query_checker', 'sql_db_query', 'check_result']
    messages = root_run.outputs["response"]
    tool_calls = find_tool_calls(messages)
    # Optionally, log the tool calls -
    # print("Here are my tool calls:")
    # print(tool_calls)
    if set(expected) <= set(tool_calls):
        score = 1
    else:
        score = 0
    return {"score": int(score), "key": "multi_tool_call_any_order"}


def contains_all_tool_calls_in_order(root_run: Run, example: Example) -> dict:
    """
    Check if all expected tools are called in exact order.
    """
    messages = root_run.outputs["response"]
    tool_calls = find_tool_calls(messages)
    # Optionally, log the tool calls -
    # print("Here are my tool calls:")
    # print(tool_calls)
    it = iter(tool_calls)
    expected = ['sql_db_list_tables', 'sql_db_schema',
                'sql_db_query_checker', 'sql_db_query', 'check_result']
    if all(elem in it for elem in expected):
        score = 1
    else:
        score = 0
    return {"score": int(score), "key": "multi_tool_call_in_order"}


def contains_all_tool_calls_in_order_exact_match(root_run: Run, example: Example) -> dict:
    """
    Check if all expected tools are called in exact order and without any additional tool calls.
    """
    expected = ['sql_db_list_tables', 'sql_db_schema',
                'sql_db_query_checker', 'sql_db_query', 'check_result']
    messages = root_run.outputs["response"]
    tool_calls = find_tool_calls(messages)
    # Optionally, log the tool calls -
    # print("Here are my tool calls:")
    # print(tool_calls)
    if tool_calls == expected:
        score = 1
    else:
        score = 0

    return {"score": int(score), "key": "multi_tool_call_in_exact_order"}


experiment_results = evaluate(
    predict_sql_agent_messages,
    data=dataset_name,
    evaluators=[contains_all_tool_calls_any_order, contains_all_tool_calls_in_order,
                contains_all_tool_calls_in_order_exact_match],
    experiment_prefix=experiment_prefix + "-trajectory",
    num_repetitions=3,
    metadata={"version": metadata},
)


================================================
FILE: ch10/py/agent_sql_graph.py
================================================
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.graph import END, StateGraph
from langgraph.prebuilt import ToolNode, tools_condition
from langchain_core.messages import ToolMessage
from langchain_core.runnables import RunnableLambda, Runnable, RunnableConfig
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph.message import AnyMessage, add_messages
import json
from langchain_community.agent_toolkits import SQLDatabaseToolkit
from langchain_core.messages import AIMessage
from langchain_core.prompts import ChatPromptTemplate
from langchain.agents import tool
from langchain_openai import ChatOpenAI
from langchain_community.utilities import SQLDatabase

db = SQLDatabase.from_uri("sqlite:///Chinook.db")
print(db.dialect)
print(db.get_usable_table_names())
db.run("SELECT * FROM Artist LIMIT 10;")
# gpt4o
llm = ChatOpenAI(model="gpt-4o", temperature=0)
experiment_prefix = "sql-agent-gpt4o"
metadata = "Chinook, gpt-4o agent"
# SQL toolkit
toolkit = SQLDatabaseToolkit(db=db, llm=llm)
tools = toolkit.get_tools()

# Query checking
query_check_system = """You are a SQL expert with a strong attention to detail.
Double check the SQLite query for common mistakes, including:
- Using NOT IN with NULL values
- Using UNION when UNION ALL should have been used
- Using BETWEEN for exclusive ranges
- Data type mismatch in predicates
- Properly quoting identifiers
- Using the correct number of arguments for functions
- Casting to the correct data type
- Using the proper columns for joins

If there are any of the above mistakes, rewrite the query. If there are no mistakes, just reproduce the original query.

Execute the correct query with the appropriate tool."""
query_check_prompt = ChatPromptTemplate.from_messages(
    [("system", query_check_system), ("user", "{query}")])
query_check = query_check_prompt | llm


@tool
def check_query_tool(query: str) -> str:
    """
    Use this tool to double check if your query is correct before executing it.
    """
    return query_check.invoke({"query": query}).content


# Query result checking
query_result_check_system = """You are grading the result of a SQL query from a DB. 
- Check that the result is not empty.
- If it is empty, instruct the system to re-try!"""
query_result_check_prompt = ChatPromptTemplate.from_messages(
    [("system", query_result_check_system), ("user", "{query_result}")])
query_result_check = query_result_check_prompt | llm


@tool
def check_result(query_result: str) -> str:
    """
    Use this tool to check the query result from the database to confirm it is not empty and is relevant.
    """
    return query_result_check.invoke({"query_result": query_result}).content


tools.append(check_query_tool)
tools.append(check_result)


class State(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]


def create_tool_node_with_fallback(tools: list) -> dict:
    return ToolNode(tools).with_fallbacks(
        [RunnableLambda(handle_tool_error)], exception_key="error"
    )


def _print_event(event: dict, _printed: set, max_length=1500):
    current_state = event.get("dialog_state")
    if current_state:
        print(f"Currently in: ", current_state[-1])
    message = event.get("messages")
    if message:
        if isinstance(message, list):
            message = message[-1]
        if message.id not in _printed:
            msg_repr = message.pretty_repr(html=True)
            if len(msg_repr) > max_length:
                msg_repr = msg_repr[:max_length] + " ... (truncated)"
            print(msg_repr)
            _printed.add(message.id)


def handle_tool_error(state) -> dict:
    error = state.get("error")
    tool_calls = state["messages"][-1].tool_calls
    return {
        "messages": [
            ToolMessage(
                content=f"Error: {repr(error)}\n please fix your mistakes.",
                tool_call_id=tc["id"],
            )
            for tc in tool_calls
        ]
    }


# Assistant
class Assistant:

    def __init__(self, runnable: Runnable):
        self.runnable = runnable

    def __call__(self, state: State, config: RunnableConfig):
        while True:
            # Append to state
            state = {**state}
            # Invoke the tool-calling LLM
            result = self.runnable.invoke(state)
            # If it is a tool call -> response is valid
            # If it has meaninful text -> response is valid
            # Otherwise, we re-prompt it b/c response is not meaninful
            if not result.tool_calls and (
                not result.content
                or isinstance(result.content, list)
                and not result.content[0].get("text")
            ):
                messages = state["messages"] + \
                    [("user", "Respond with a real output.")]
                state = {**state, "messages": messages}
            else:
                break
        return {"messages": result}


# Assistant runnable
query_gen_system = """
ROLE:
You are an agent designed to interact with a SQL database. You have access to tools for interacting with the database.
GOAL:
Given an input question, create a syntactically correct SQLite query to run, then look at the results of the query and return the answer.
INSTRUCTIONS:
- Only use the below tools for the following operations.
- Only use the information returned by the below tools to construct your final answer.
- To start you should ALWAYS look at the tables in the database to see what you can query. Do NOT skip this step.
- Then you should query the schema of the most relevant tables.
- Write your query based upon the schema of the tables. You MUST double check your query before executing it. 
- Unless the user specifies a specific number of examples they wish to obtain, always limit your query to at most 5 results.
- You can order the results by a relevant column to return the most interesting examples in the database.
- Never query for all the columns from a specific table, only ask for the relevant columns given the question.
- If you get an error while executing a query, rewrite the query and try again.
- If the query returns a result, use check_result tool to check the query result.
- If the query result result is empty, think about the table schema, rewrite the query, and try again.
- DO NOT make any DML statements (INSERT, UPDATE, DELETE, DROP etc.) to the database."""

query_gen_prompt = ChatPromptTemplate.from_messages(
    [("system", query_gen_system), ("placeholder", "{messages}")])
assistant_runnable = query_gen_prompt | llm.bind_tools(tools)


# Graph
builder = StateGraph(State)

# Define nodes: these do the work
builder.add_node("assistant", Assistant(assistant_runnable))
builder.add_node("tools", create_tool_node_with_fallback(tools))

# Define edges: these determine how the control flow moves
builder.set_entry_point("assistant")
builder.add_conditional_edges(
    "assistant",
    # If the latest message (result) from assistant is a tool call -> tools_condition routes to tools
    # If the latest message (result) from assistant is a not a tool call -> tools_condition routes to END
    tools_condition,
    # "tools" calls one of our tools. END causes the graph to terminate (and respond to the user)
    {"tools": "tools", END: END},
)
builder.add_edge("tools", "assistant")

# The checkpointer lets the graph persist its state
memory = SqliteSaver.from_conn_string(":memory:")
graph = builder.compile(checkpointer=memory)


================================================
FILE: ch10/py/create_rag_dataset.py
================================================
from langsmith import wrappers, Client
from pydantic import BaseModel, Field
from openai import OpenAI

client = Client()
openai_client = wrappers.wrap_openai(OpenAI())

examples = [
    {
        "question": "Which companies are highlighted as top LangGraph agent adopters in 2024?",
        "answer": "The top adopters include Uber (code migration tools), AppFolio (property management copilot), LinkedIn (SQL Bot), Elastic (AI assistant), and Replit (multi-agent development platform) :cite[3]."
    },
    {
        "question": "How did AppFolio's AI copilot impact property managers?",
        "answer": "AppFolio's Realm-X AI copilot saved property managers over 10 hours per week by automating queries, bulk actions, and scheduling :cite[3]."
    },
    {
        "question": "What infrastructure trends dominated LLM usage in 2024?",
        "answer": "OpenAI remained the top LLM provider (6x more usage than Ollama), while open-source models via Ollama and Groq surged. Chroma and FAISS led vector stores, with MongoDB and Elastic gaining traction :cite[2]:cite[5]."
    },
    {
        "question": "How did LangGraph improve agent workflows compared to 2023?",
        "answer": "LangGraph usage grew to 43% of LangSmith organizations, with 21.9% of traces involving tool calls (up from 0.5% in 2023), enabling complex multi-step tasks like database writes :cite[2]:cite[7]."
    },
    {
        "question": "What distinguishes Replit's LangGraph implementation?",
        "answer": "Replit's agent emphasizes human-in-the-loop validation and a multi-agent architecture for code generation, combining autonomy with controlled outputs :cite[3]."
    }
]

inputs = [{"question": example["question"]} for example in examples]
outputs = [{"answer": example["answer"]} for example in examples]

# Programmatically create a dataset in LangSmith
dataset = client.create_dataset(
    dataset_name="langchain-blogs-qa", description="Langchain blogs QA."
)

# Add examples to the dataset
client.create_examples(inputs=inputs, outputs=outputs, dataset_id=dataset.id)

print(
    f"Dataset created in langsmith with ID: {dataset.id}\n Navigate to {dataset.url}.")


================================================
FILE: ch10/py/create_sql_dataset.py
================================================
from langsmith import Client

client = Client()

# Create a dataset
examples = [
    ("Which country's customers spent the most? And how much did they spend?",
     "The country whose customers spent the most is the USA, with a total expenditure of $523.06"),
    ("What was the most purchased track of 2013?",
     "The most purchased track of 2013 was Hot Girl."),
    ("How many albums does the artist Led Zeppelin have?",
     "Led Zeppelin has 14 albums"),
    ("What is the total price for the album “Big Ones”?",
     "The total price for the album 'Big Ones' is 14.85"),
    ("Which sales agent made the most in sales in 2009?",
     "Steve Johnson made the most sales in 2009"),
]

dataset_name = "sql-agent-response"
if not client.has_dataset(dataset_name=dataset_name):
    dataset = client.create_dataset(dataset_name=dataset_name)
    inputs, outputs = zip(
        *[({"input": text}, {"output": label}) for text, label in examples]
    )
    client.create_examples(
        inputs=inputs, outputs=outputs, dataset_id=dataset.id)


================================================
FILE: ch10/py/rag_graph.py
================================================
from typing import List, TypedDict
from langchain_community.document_loaders import WebBaseLoader
from langchain.schema import Document
from langgraph.graph import END, StateGraph, START
from langchain_community.vectorstores import InMemoryVectorStore
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain import hub
from langchain_openai import ChatOpenAI


class GraphState(TypedDict):
    """
    Represents the state of our graph.

    Attributes:
        question: question
        scraped_documents: list of documents
        vectorstore: vectorstore
    """

    question: str
    scraped_documents: List[str]
    vectorstore: InMemoryVectorStore
    answer: str


def scrape_blog_posts(state) -> List[Document]:
    """
    Scrape the blog posts and create a list of documents
    """

    urls = [
        "https://blog.langchain.dev/top-5-langgraph-agents-in-production-2024/",
        "https://blog.langchain.dev/langchain-state-of-ai-2024/",
        "https://blog.langchain.dev/introducing-ambient-agents/",
    ]

    docs = [WebBaseLoader(url).load() for url in urls]
    docs_list = [item for sublist in docs for item in sublist]

    return {"scraped_documents": docs_list}


def indexing(state):
    """
    Index the documents
    """
    text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
        chunk_size=250, chunk_overlap=0
    )
    doc_splits = text_splitter.split_documents(state["scraped_documents"])

# Add to vectorDB
    vectorstore = InMemoryVectorStore.from_documents(
        documents=doc_splits,
        embedding=OpenAIEmbeddings(),
    )
    return {"vectorstore": vectorstore}


def retrieve_and_generate(state):
    """
    Retrieve documents from vectorstore and generate answer
    """
    question = state["question"]
    vectorstore = state["vectorstore"]

    retriever = vectorstore.as_retriever()

    prompt = hub.pull("rlm/rag-prompt")
    llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)

    # fetch relevant documents
    docs = retriever.invoke(question)  # format prompt
    formatted = prompt.invoke(
        {"context": docs, "question": question})  # generate answer
    answer = llm.invoke(formatted)
    return {"answer": answer}


# Graph
workflow = StateGraph(GraphState)

# Define the nodes
workflow.add_node("retrieve_and_generate", retrieve_and_generate)  # retrieve
workflow.add_node("scrape_blog_posts", scrape_blog_posts)  # scrape web
workflow.add_node("indexing", indexing)  # index

# Build graph
workflow.add_edge(START, "scrape_blog_posts")
workflow.add_edge("scrape_blog_posts", "indexing")
workflow.add_edge("indexing", "retrieve_and_generate")

workflow.add_edge("retrieve_and_generate", END)

# Compile
graph = workflow.compile()


================================================
FILE: ch10/py/retrieve_and_grade.py
================================================
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import InMemoryVectorStore
from langchain_openai import OpenAIEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field
from langchain_openai import ChatOpenAI


# --- Create an index of documents ---

urls = [
    "https://blog.langchain.dev/top-5-langgraph-agents-in-production-2024/",
    "https://blog.langchain.dev/langchain-state-of-ai-2024/",
    "https://blog.langchain.dev/introducing-ambient-agents/",
]

docs = [WebBaseLoader(url).load() for url in urls]
docs_list = [item for sublist in docs for item in sublist]

text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
    chunk_size=250, chunk_overlap=0
)
doc_splits = text_splitter.split_documents(docs_list)

# Add to vectorDB
vectorstore = InMemoryVectorStore.from_documents(
    documents=doc_splits,
    embedding=OpenAIEmbeddings(),
)
retriever = vectorstore.as_retriever()

# Retrieve the relevant documents
results = retriever.invoke(
    "What are 2 LangGraph agents used in production in 2024?")

print("Results: \n", results)


# --- Create a grader for retrieved documents ---

# Data model
class GradeDocuments(BaseModel):
    """Binary score for relevance check on retrieved documents."""

    binary_score: str = Field(
        description="Documents are relevant to the question, 'yes' or 'no'"
    )


# LLM with structured output
llm = ChatOpenAI(temperature=0)
structured_llm_grader = llm.with_structured_output(GradeDocuments)

# Prompt
system = """You are a grader assessing relevance of a retrieved document to a user question.
If the document contains keyword(s) or semantic meaning related to the question, grade it as relevant.
Give a binary score 'yes' or 'no' score to indicate whether the document is relevant to the question."""

grade_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system),
        ("human",
         "Retrieved document: \n\n {document} \n\n User question: {question}"),
    ]
)

retrieval_grader = grade_prompt | structured_llm_grader

# --- Grade retrieved documents ---

question = "What are 2 LangGraph agents used in production in 2024?"

# as an example retrieval_grader.invoke({"question": question, "document": doc_txt})
docs = retriever.invoke(question)

doc_txt = docs[0].page_content

result = retrieval_grader.invoke({"question": question, "document": doc_txt})

print("\n\nGrade Result: \n", result)


================================================
FILE: ch10/py/search_graph.py
================================================
from typing import List, TypedDict

from langchain_core.documents import Document
from langchain_community.tools import DuckDuckGoSearchRun
from langgraph.graph import END, StateGraph, START

from retrieve_and_grade import retrieval_grader
from retrieve_and_grade import retriever
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain import hub  # Prompt

prompt = hub.pull("rlm/rag-prompt")

# LLM
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)

rag_chain = prompt | llm | StrOutputParser()

# Prompt
system = """You a question re-writer that converts an input question to a better version that is optimized \n 
     for web search. Look at the input and try to reason about the underlying semantic intent / meaning."""
re_write_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", system),
        (
            "human",
            "Here is the initial question: \n\n {question} \n Formulate an improved question.",
        ),
    ]
)

question_rewriter = re_write_prompt | llm | StrOutputParser()

# --- Create the graph ---

web_search_tool = DuckDuckGoSearchRun()


class GraphState(TypedDict):
    """
    Represents the state of our graph.

    Attributes:
        question: question
        generation: LLM generation
        web_search: whether to add search
        documents: list of documents
    """

    question: str
    generation: str
    web_search: str
    documents: List[str]


def retrieve(state):
    """
    Retrieve documents

    Args:
        state (dict): The current graph state

    Returns:
        state (dict): New key added to state, documents, that contains retrieved documents
    """
    question = state["question"]

    # Retrieval
    documents = retriever.invoke(question)
    return {"documents": documents, "question": question}


def generate(state):
    """
    Generate answer

    Args:
        state (dict): The current graph state

    Returns:
        state (dict): New key added to state, generation, that contains LLM generation
    """
    question = state["question"]
    documents = state["documents"]

    # RAG generation
    generation = rag_chain.invoke({"context": documents, "question": question})
    return {"documents": documents, "question": question, "generation": generation}


def grade_documents(state):
    """
    Determines whether the retrieved documents are relevant to the question.

    Args:
        state (dict): The current graph state

    Returns:
        state (dict): Updates documents key with only filtered relevant documents
    """

    question = state["question"]
    documents = state["documents"]

    # Score each doc
    filtered_docs = []
    web_search = "No"
    for d in documents:
        score = retrieval_grader.invoke(
            {"question": question, "document": d.page_content}
        )
        grade = score.binary_score
        if grade == "yes":
            print("---GRADE: DOCUMENT RELEVANT---")
            filtered_docs.append(d)
        else:
            print("---GRADE: DOCUMENT NOT RELEVANT---")
            web_search = "Yes"
            continue
    return {"documents": filtered_docs, "question": question, "web_search": web_search}


def transform_query(state):
    """
    Transform the query to produce a better question.

    Args:
        state (dict): The current graph state

    Returns:
        state (dict): Updates question key with a re-phrased question
    """

    question = state["question"]
    documents = state["documents"]

    # Re-write question
    better_question = question_rewriter.invoke({"question": question})
    return {"documents": documents, "question": better_question}


def web_search(state):
    """
    Web search based on the re-phrased question.

    Args:
        state (dict): The current graph state

    Returns:
        state (dict): Updates documents key with appended web results
    """

    print("---WEB SEARCH---")
    question = state["question"]
    documents = state["documents"]

    # Web search
    docs = web_search_tool.invoke({"query": question})
    web_results = "\n".join([d["content"] for d in docs])
    web_results = Document(page_content=web_results)
    documents.append(web_results)

    return {"documents": documents, "question": question}


# Edges


def decide_to_generate(state):
    """
    Determines whether to generate an answer, or re-generate a question.

    Args:
        state (dict): The current graph state

    Returns:
        str: Binary decision for next node to call
    """

    state["question"]
    web_search = state["web_search"]
    state["documents"]

    if web_search == "Yes":
        # All documents have been filtered check_relevance
        # We will re-generate a new query
        return "transform_query"
    else:
        # We have relevant documents, so generate answer
        return "generate"


# Graph

workflow = StateGraph(GraphState)

# Define the nodes
workflow.add_node("retrieve", retrieve)  # retrieve
workflow.add_node("grade_documents", grade_documents)  # grade documents
workflow.add_node("generate", generate)  # generatae
workflow.add_node("transform_query", transform_query)  # transform_query
workflow.add_node("web_search_node", web_search)  # web search

# Build graph
workflow.add_edge(START, "retrieve")
workflow.add_edge("retrieve", "grade_documents")
workflow.add_conditional_edges(
    "grade_documents",
    decide_to_generate,
    {
        "transform_query": "transform_query",
        "generate": "generate",
    },
)
workflow.add_edge("transform_query", "web_search_node")
workflow.add_edge("web_search_node", "generate")
workflow.add_edge("generate", END)

# Compile
app = workflow.compile()


================================================
FILE: ch2/js/a-text-loader.js
================================================
import { TextLoader } from 'langchain/document_loaders/fs/text';

const loader = new TextLoader('./test.txt');
const docs = await loader.load();

console.log(docs);


================================================
FILE: ch2/js/b-web-loader.js
================================================
import { CheerioWebBaseLoader } from '@langchain/community/document_loaders/web/cheerio';

const loader = new CheerioWebBaseLoader('https://www.langchain.com/');
const docs = await loader.load();

console.log(docs);


================================================
FILE: ch2/js/c-pdf-loader.js
================================================
// install the pdf parsing library: npm install pdf-parse
import { PDFLoader } from '@langchain/community/document_loaders/fs/pdf';

const loader = new PDFLoader('./test.pdf');
const docs = await loader.load();

console.log(docs);


================================================
FILE: ch2/js/d-rec-text-splitter.js
================================================
import { TextLoader } from 'langchain/document_loaders/fs/text';
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';

const loader = new TextLoader('./test.txt'); // or any other loader
const docs = await loader.load();

const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 1000,
  chunkOverlap: 200,
});

const splittedDocs = await splitter.splitDocuments(docs);

console.log(splittedDocs);


================================================
FILE: ch2/js/e-rec-text-splitter-code.js
================================================
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';

const PYTHON_CODE = ` def hello_world(): print("Hello, World!") # Call the function hello_world() `;

const pythonSplitter = RecursiveCharacterTextSplitter.fromLanguage('python', {
  chunkSize: 50,
  chunkOverlap: 0,
});

const pythonDocs = await pythonSplitter.createDocuments([PYTHON_CODE]);

console.log(pythonDocs);


================================================
FILE: ch2/js/f-markdown-splitter.js
================================================
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';

const markdownText = ` # 🦜🔗 LangChain ⚡ Building applications with LLMs through composability ⚡ ## Quick Install \`\`\`bash pip install langchain \`\`\` As an open source project in a rapidly developing field, we are extremely open to contributions. `;

const mdSplitter = RecursiveCharacterTextSplitter.fromLanguage('markdown', {
  chunkSize: 60,
  chunkOverlap: 0,
});

const mdDocs = await mdSplitter.createDocuments(
  [markdownText],
  [{ source: 'https://www.langchain.com' }]
);

console.log(mdDocs);


================================================
FILE: ch2/js/g-embeddings.js
================================================
import { OpenAIEmbeddings } from '@langchain/openai';

const model = new OpenAIEmbeddings();
const embeddings = await model.embedDocuments([
  'Hi there!',
  'Oh, hello!',
  "What's your name?",
  'My friends call me World',
  'Hello World!',
]);

console.log(embeddings);


================================================
FILE: ch2/js/h-load-split-embed.js
================================================
import { TextLoader } from 'langchain/document_loaders/fs/text';
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';
import { OpenAIEmbeddings } from '@langchain/openai';

const loader = new TextLoader('./test.txt');
const docs = await loader.load();

// Split the document
const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 1000,
  chunkOverlap: 200,
});
const chunks = await splitter.splitDocuments(docs);

console.log(chunks);

// Generate embeddings
const model = new OpenAIEmbeddings();
const embeddings = await model.embedDocuments(chunks.map((c) => c.pageContent));

console.log(embeddings);


================================================
FILE: ch2/js/i-pg-vector.js
================================================
/** 
1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)
2. Run the following command to start the postgres container:
   
docker run \
    --name pgvector-container \
    -e POSTGRES_USER=langchain \
    -e POSTGRES_PASSWORD=langchain \
    -e POSTGRES_DB=langchain \
    -p 6024:5432 \
    -d pgvector/pgvector:pg16
3. Use the connection string below for the postgres container
*/

import { TextLoader } from 'langchain/document_loaders/fs/text';
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';
import { OpenAIEmbeddings } from '@langchain/openai';
import { PGVectorStore } from '@langchain/community/vectorstores/pgvector';
import { v4 as uuidv4 } from 'uuid';

const connectionString =
  'postgresql://langchain:langchain@localhost:6024/langchain';
// Load the document, split it into chunks
const loader = new TextLoader('./test.txt');
const raw_docs = await loader.load();
const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 1000,
  chunkOverlap: 200,
});
const docs = await splitter.splitDocuments(raw_docs);

// embed each chunk and insert it into the vector store
const model = new OpenAIEmbeddings();
const db = await PGVectorStore.fromDocuments(docs, model, {
  postgresConnectionOptions: {
    connectionString,
  },
});

console.log('Vector store created successfully');

const results = await db.similaritySearch('query', 4);

console.log(`Similarity search results: ${JSON.stringify(results)}`);

console.log('Adding documents to the vector store');

const ids = [uuidv4(), uuidv4()];

await db.addDocuments(
  [
    {
      pageContent: 'there are cats in the pond',
      metadata: { location: 'pond', topic: 'animals' },
    },
    {
      pageContent: 'ducks are also found in the pond',
      metadata: { location: 'pond', topic: 'animals' },
    },
  ],
  { ids }
);

console.log('Documents added successfully');

await db.delete({ ids: [ids[1]] });

console.log('second document deleted successfully');


================================================
FILE: ch2/js/j-record-manager.js
================================================
/** 
1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)
2. Run the following command to start the postgres container:
   
docker run \
    --name pgvector-container \
    -e POSTGRES_USER=langchain \
    -e POSTGRES_PASSWORD=langchain \
    -e POSTGRES_DB=langchain \
    -p 6024:5432 \
    -d pgvector/pgvector:pg16
3. Use the connection string below for the postgres container
*/

import { PostgresRecordManager } from '@langchain/community/indexes/postgres';
import { index } from 'langchain/indexes';
import { OpenAIEmbeddings } from '@langchain/openai';
import { PGVectorStore } from '@langchain/community/vectorstores/pgvector';
import { v4 as uuidv4 } from 'uuid';

const tableName = 'test_langchain';
const connectionString =
  'postgresql://langchain:langchain@localhost:6024/langchain';
// Load the document, split it into chunks

const config = {
  postgresConnectionOptions: {
    connectionString,
  },
  tableName: tableName,
  columns: {
    idColumnName: 'id',
    vectorColumnName: 'vector',
    contentColumnName: 'content',
    metadataColumnName: 'metadata',
  },
};

const vectorStore = await PGVectorStore.initialize(
  new OpenAIEmbeddings(),
  config
);

// Create a new record manager
const recordManagerConfig = {
  postgresConnectionOptions: {
    connectionString,
  },
  tableName: 'upsertion_records',
};
const recordManager = new PostgresRecordManager(
  'test_namespace',
  recordManagerConfig
);

// Create the schema if it doesn't exist
await recordManager.createSchema();

const docs = [
  {
    pageContent: 'there are cats in the pond',
    metadata: { id: uuidv4(), source: 'cats.txt' },
  },
  {
    pageContent: 'ducks are also found in the pond',
    metadata: { id: uuidv4(), source: 'ducks.txt' },
  },
];

// the first attempt will index both documents
const index_attempt_1 = await index({
  docsSource: docs,
  recordManager,
  vectorStore,
  options: {
    cleanup: 'incremental', // prevent duplicate documents by id from being indexed
    sourceIdKey: 'source', // the key in the metadata that will be used to identify the document
  },
});

console.log(index_attempt_1);

// the second attempt will skip indexing because the identical documents already exist
const index_attempt_2 = await index({
  docsSource: docs,
  recordManager,
  vectorStore,
  options: {
    cleanup: 'incremental',
    sourceIdKey: 'source',
  },
});

console.log(index_attempt_2);

// If we mutate a document, the new version will be written and all old versions sharing the same source will be deleted.
docs[0].pageContent = 'I modified the first document content';
const index_attempt_3 = await index({
  docsSource: docs,
  recordManager,
  vectorStore,
  options: {
    cleanup: 'incremental',
    sourceIdKey: 'source',
  },
});

console.log(index_attempt_3);


================================================
FILE: ch2/js/k-multi-vector-retriever.js
================================================
import * as uuid from 'uuid';
import { MultiVectorRetriever } from 'langchain/retrievers/multi_vector';
import { OpenAIEmbeddings } from '@langchain/openai';
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';
import { InMemoryStore } from '@langchain/core/stores';
import { TextLoader } from 'langchain/document_loaders/fs/text';
import { Document } from '@langchain/core/documents';
import { PGVectorStore } from '@langchain/community/vectorstores/pgvector';
import { ChatOpenAI } from '@langchain/openai';
import { PromptTemplate } from '@langchain/core/prompts';
import { RunnableSequence } from '@langchain/core/runnables';
import { StringOutputParser } from '@langchain/core/output_parsers';

const connectionString =
  'postgresql://langchain:langchain@localhost:6024/langchain';
const collectionName = 'summaries';

const textLoader = new TextLoader('./test.txt');
const parentDocuments = await textLoader.load();
const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 10000,
  chunkOverlap: 20,
});
const docs = await splitter.splitDocuments(parentDocuments);

const prompt = PromptTemplate.fromTemplate(
  `Summarize the following document:\n\n{doc}`
);

const llm = new ChatOpenAI({ modelName: 'gpt-3.5-turbo' });

const chain = RunnableSequence.from([
  { doc: (doc) => doc.pageContent },
  prompt,
  llm,
  new StringOutputParser(),
]);

// batch summarization chain across the chunks
const summaries = await chain.batch(docs, {
  maxConcurrency: 5,
});

const idKey = 'doc_id';
const docIds = docs.map((_) => uuid.v4());
// create summary docs with metadata linking to the original docs
const summaryDocs = summaries.map((summary, i) => {
  const summaryDoc = new Document({
    pageContent: summary,
    metadata: {
      [idKey]: docIds[i],
    },
  });
  return summaryDoc;
});

// The byteStore to use to store the original chunks
const byteStore = new InMemoryStore();

// vector store for the summaries
const vectorStore = await PGVectorStore.fromDocuments(
  docs,
  new OpenAIEmbeddings(),
  {
    postgresConnectionOptions: {
      connectionString,
    },
  }
);

const retriever = new MultiVectorRetriever({
  vectorstore: vectorStore,
  byteStore,
  idKey,
});

const keyValuePairs = docs.map((originalDoc, i) => [docIds[i], originalDoc]);

// Use the retriever to add the original chunks to the document store
await retriever.docstore.mset(keyValuePairs);

// Vectorstore alone retrieves the small chunks
const vectorstoreResult = await retriever.vectorstore.similaritySearch(
  'chapter on philosophy',
  2
);
console.log(`summary: ${vectorstoreResult[0].pageContent}`);
console.log(
  `summary retrieved length: ${vectorstoreResult[0].pageContent.length}`
);

// Retriever returns larger chunk result
const retrieverResult = await retriever.invoke('chapter on philosophy');
console.log(
  `multi-vector retrieved chunk length: ${retrieverResult[0].pageContent.length}`
);


================================================
FILE: ch2/py/a-text-loader.py
================================================
from langchain_community.document_loaders import TextLoader

loader = TextLoader('./test.txt', encoding="utf-8")
docs = loader.load()

print(docs)


================================================
FILE: ch2/py/b-web-loader.py
================================================
"""
Install the beautifulsoup4 package:

```bash
pip install beautifulsoup4
```
"""

from langchain_community.document_loaders import WebBaseLoader

loader = WebBaseLoader('https://www.langchain.com/')
docs = loader.load()

print(docs)


================================================
FILE: ch2/py/c-pdf-loader.py
================================================
# install the pdf parsing library !pip install pypdf

from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader('./test.pdf')
pages = loader.load()

print(pages)


================================================
FILE: ch2/py/d-rec-text-splitter.py
================================================
from langchain_text_splitters import RecursiveCharacterTextSplitter

from langchain_community.document_loaders import TextLoader

loader = TextLoader('./test.txt', encoding="utf-8")
docs = loader.load()

splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splitted_docs = splitter.split_documents(docs)

print(splitted_docs)


================================================
FILE: ch2/py/e-rec-text-splitter-code.py
================================================
from langchain_text_splitters import (
    Language,
    RecursiveCharacterTextSplitter,
)

PYTHON_CODE = """ def hello_world(): print("Hello, World!") # Call the function hello_world() """

python_splitter = RecursiveCharacterTextSplitter.from_language(
    language=Language.PYTHON, chunk_size=50, chunk_overlap=0
)

python_docs = python_splitter.create_documents([PYTHON_CODE])

print(python_docs)


================================================
FILE: ch2/py/f-markdown-splitter.py
================================================
from langchain_text_splitters import (
    Language,
    RecursiveCharacterTextSplitter,
)
markdown_text = """ # 🦜🔗 LangChain ⚡ Building applications with LLMs through composability ⚡ ## Quick Install ```bash pip install langchain ``` As an open source project in a rapidly developing field, we are extremely open     to contributions. """

md_splitter = RecursiveCharacterTextSplitter.from_language(
    language=Language.MARKDOWN, chunk_size=60, chunk_overlap=0
)

md_docs = md_splitter.create_documents(
    [markdown_text], [{"source": "https://www.langchain.com"}])

print(md_docs)


================================================
FILE: ch2/py/g-embeddings.py
================================================
from langchain_openai import OpenAIEmbeddings

model = OpenAIEmbeddings(model="text-embedding-3-small")
embeddings = model.embed_documents([
    "Hi there!",
    "Oh, hello!",
    "What's your name?",
    "My friends call me World",
    "Hello World!"
])

print(embeddings)


================================================
FILE: ch2/py/h-load-split-embed.py
================================================
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings

# Load the document
loader = TextLoader("./test.txt", encoding="utf-8")
doc = loader.load()

# Split the document
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
chunks = splitter.split_documents(doc)

# Generate embeddings
embeddings_model = OpenAIEmbeddings(model="text-embedding-3-small")
embeddings = embeddings_model.embed_documents(
    [chunk.page_content for chunk in chunks]
)

print(embeddings)


================================================
FILE: ch2/py/i-pg-vector.py
================================================
"""
1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)
2. pip install -qU langchain_postgres
3. Run the following command to start the postgres container:
   
docker run \
    --name pgvector-container \
    -e POSTGRES_USER=langchain \
    -e POSTGRES_PASSWORD=langchain \
    -e POSTGRES_DB=langchain \
    -p 6024:5432 \
    -d pgvector/pgvector:pg16
4. Use the connection string below for the postgres container

"""

from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_postgres.vectorstores import PGVector
from langchain_core.documents import Document
import uuid


# See docker command above to launch a postgres instance with pgvector enabled.
connection = "postgresql+psycopg://langchain:langchain@localhost:6024/langchain"

# Load the document, split it into chunks
raw_documents = TextLoader('./test.txt', encoding="utf-8").load()
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000, chunk_overlap=200)
documents = text_splitter.split_documents(raw_documents)

# Create embeddings for the documents
embeddings_model = OpenAIEmbeddings()

db = PGVector.from_documents(
    documents, embeddings_model, connection=connection)

results = db.similarity_search("query", k=4)

print(results)

print("Adding documents to the vector store")
ids = [str(uuid.uuid4()), str(uuid.uuid4())]
db.add_documents(
    [
        Document(
            page_content="there are cats in the pond",
            metadata={"location": "pond", "topic": "animals"},
        ),
        Document(
            page_content="ducks are also found in the pond",
            metadata={"location": "pond", "topic": "animals"},
        ),
    ],
    ids=ids,
)

print("Documents added successfully.\n Fetched documents count:",
      len(db.get_by_ids(ids)))

print("Deleting document with id", ids[1])
db.delete({"ids": ids})

print("Document deleted successfully.\n Fetched documents count:",
      len(db.get_by_ids(ids)))


================================================
FILE: ch2/py/j-record-manager.py
================================================
from langchain.indexes import SQLRecordManager, index
from langchain_postgres.vectorstores import PGVector
from langchain_openai import OpenAIEmbeddings
from langchain.docstore.document import Document

connection = "postgresql+psycopg://langchain:langchain@localhost:6024/langchain"
collection_name = "my_docs"
embeddings_model = OpenAIEmbeddings(model="text-embedding-3-small")
namespace = "my_docs_namespace"

vectorstore = PGVector(
    embeddings=embeddings_model,
    collection_name=collection_name,
    connection=connection,
    use_jsonb=True,
)

record_manager = SQLRecordManager(
    namespace,
    db_url="postgresql+psycopg://langchain:langchain@localhost:6024/langchain",
)

# Create the schema if it doesn't exist
record_manager.create_schema()

# Create documents
docs = [
    Document(page_content='there are cats in the pond', metadata={
             "id": 1, "source": "cats.txt"}),
    Document(page_content='ducks are also found in the pond', metadata={
             "id": 2, "source": "ducks.txt"}),
]

# Index the documents
index_1 = index(
    docs,
    record_manager,
    vectorstore,
    cleanup="incremental",  # prevent duplicate documents
    source_id_key="source",  # use the source field as the source_id
)

print("Index attempt 1:", index_1)

# second time you attempt to index, it will not add the documents again
index_2 = index(
    docs,
    record_manager,
    vectorstore,
    cleanup="incremental",
    source_id_key="source",
)

print("Index attempt 2:", index_2)

# If we mutate a document, the new version will be written and all old versions sharing the same source will be deleted.

docs[0].page_content = "I just modified this document!"

index_3 = index(
    docs,
    record_manager,
    vectorstore,
    cleanup="incremental",
    source_id_key="source",
)

print("Index attempt 3:", index_3)


================================================
FILE: ch2/py/k-multi-vector-retriever.py
================================================
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_postgres.vectorstores import PGVector
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI
from langchain_core.documents import Document
from langchain.retrievers.multi_vector import MultiVectorRetriever
from langchain.storage import InMemoryStore
import uuid

connection = "postgresql+psycopg://langchain:langchain@localhost:6024/langchain"
collection_name = "summaries"
embeddings_model = OpenAIEmbeddings()
# Load the document
loader = TextLoader("./test.txt", encoding="utf-8")
docs = loader.load()

print("length of loaded docs: ", len(docs[0].page_content))
# Split the document
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
chunks = splitter.split_documents(docs)

# The rest of your code remains the same, starting from:
prompt_text = "Summarize the following document:\n\n{doc}"

prompt = ChatPromptTemplate.from_template(prompt_text)
llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo")
summarize_chain = {
    "doc": lambda x: x.page_content} | prompt | llm | StrOutputParser()

# batch the chain across the chunks
summaries = summarize_chain.batch(chunks, {"max_concurrency": 5})

# The vectorstore to use to index the child chunks
vectorstore = PGVector(
    embeddings=embeddings_model,
    collection_name=collection_name,
    connection=connection,
    use_jsonb=True,
)
# The storage layer for the parent documents
store = InMemoryStore()
id_key = "doc_id"

# indexing the summaries in our vector store, whilst retaining the original documents in our document store:
retriever = MultiVectorRetriever(
    vectorstore=vectorstore,
    docstore=store,
    id_key=id_key,
)

# Changed from summaries to chunks since we need same length as docs
doc_ids = [str(uuid.uuid4()) for _ in chunks]

# Each summary is linked to the original document by the doc_id
summary_docs = [
    Document(page_content=s, metadata={id_key: doc_ids[i]})
    for i, s in enumerate(summaries)
]

# Add the document summaries to the vector store for similarity search
retriever.vectorstore.add_documents(summary_docs)

# Store the original documents in the document store, linked to their summaries via doc_ids
# This allows us to first search summaries efficiently, then fetch the full docs when needed
retriever.docstore.mset(list(zip(doc_ids, chunks)))

# vector store retrieves the summaries
sub_docs = retriever.vectorstore.similarity_search(
    "chapter on philosophy", k=2)

print("sub docs: ", sub_docs[0].page_content)

print("length of sub docs:\n", len(sub_docs[0].page_content))

# Whereas the retriever will return the larger source document chunks:
retrieved_docs = retriever.invoke("chapter on philosophy")

print("length of retrieved docs: ", len(retrieved_docs[0].page_content))


================================================
FILE: ch2/py/l-rag-colbert.py
================================================
"""
- Windows is not supported. RAGatouille doesn't appear to work outside WSL and has issues with WSL1. Some users have had success running RAGatouille in WSL2.
- Only on python.
- Read full docs here: https://github.com/AnswerDotAI/RAGatouille/blob/8183aad64a9a6ba805d4066dcab489d97615d316/README.md

- To install run:

```bash
pip install -U ragatouille transformers
```
"""
from ragatouille import RAGPretrainedModel
import requests

RAG = RAGPretrainedModel.from_pretrained("colbert-ir/colbertv2.0")


def get_wikipedia_page(title: str):
    """
    Retrieve the full text content of a Wikipedia page.
    :param title: str - Title of the Wikipedia page.
    :return: str - Full text content of the page as raw string.
    """
    # Wikipedia API endpoint
    URL = "https://en.wikipedia.org/w/api.php"
    # Parameters for the API request
    params = {
        "action": "query",
        "format": "json",
        "titles": title,
        "prop": "extracts",
        "explaintext": True,
    }
    # Custom User-Agent header to comply with Wikipedia's best practices
    headers = {"User-Agent": "RAGatouille_tutorial/0.0.1"}
    response = requests.get(URL, params=params, headers=headers)
    data = response.json()
    # Extracting page content
    page = next(iter(data["query"]["pages"].values()))
    return page["extract"] if "extract" in page else None


full_document = get_wikipedia_page("Hayao_Miyazaki")
# Create an index
RAG.index(
    collection=[full_document],
    index_name="Miyazaki-123",
    max_document_length=180,
    split_documents=True,
)
# query
results = RAG.search(query="What animation studio did Miyazaki found?", k=3)

print(results)

# Alternative: Utilize langchain retriever
retriever = RAG.as_langchain_retriever(k=3)
retriever.invoke("What animation studio did Miyazaki found?")


================================================
FILE: ch3/js/a-basic-rag.js
================================================
/** 
1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)
2. Run the following command to start the postgres container:
   
docker run \
    --name pgvector-container \
    -e POSTGRES_USER=langchain \
    -e POSTGRES_PASSWORD=langchain \
    -e POSTGRES_DB=langchain \
    -p 6024:5432 \
    -d pgvector/pgvector:pg16
3. Use the connection string below for the postgres container
*/

import { TextLoader } from 'langchain/document_loaders/fs/text';
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';
import { OpenAIEmbeddings } from '@langchain/openai';
import { PGVectorStore } from '@langchain/community/vectorstores/pgvector';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { ChatOpenAI } from '@langchain/openai';
import { RunnableLambda } from '@langchain/core/runnables';
const connectionString =
  'postgresql://langchain:langchain@localhost:6024/langchain';
// Load the document, split it into chunks
const loader = new TextLoader('./test.txt');
const raw_docs = await loader.load();
const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 1000,
  chunkOverlap: 200,
});
const splitDocs = await splitter.splitDocuments(raw_docs);

// embed each chunk and insert it into the vector store
const model = new OpenAIEmbeddings();

const db = await PGVectorStore.fromDocuments(splitDocs, model, {
  postgresConnectionOptions: {
    connectionString,
  },
});

// retrieve 2 relevant documents from the vector store
const retriever = db.asRetriever({ k: 2 });

const query =
  'Who are the key figures in the ancient greek history of philosophy?';

// fetch relevant documents
const docs = await retriever.invoke(query);

console.log(
  `fetched document based on similarity search query:\n ${docs[0].pageContent}\n\n`
);

/**
 * Provide retrieved docs as context to the LLM to answer a user's question
 */
const prompt = ChatPromptTemplate.fromTemplate(
  'Answer the question based only on the following context:\n {context}\n\nQuestion: {question}'
);

const llm = new ChatOpenAI({ temperature: 0, modelName: 'gpt-3.5-turbo' });
const chain = prompt.pipe(llm);

const result = await chain.invoke({
  context: docs,
  question: query,
});

console.log(result);
console.log('\n\n');

// run again but this time encapsulate the logic for efficiency

console.log(
  'Running again but this time encapsulate the logic for efficiency\n'
);
const qa = RunnableLambda.from(async (input) => {
  // fetch relevant documents
  const docs = await retriever.invoke(input);
  // format prompt
  const formatted = await prompt.invoke({ context: docs, question: input });
  // generate answer
  const answer = await llm.invoke(formatted);
  return answer;
});

const finalResult = await qa.invoke(query);
console.log(finalResult);


================================================
FILE: ch3/js/b-rewrite.js
================================================
/** 
1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)
2. Run the following command to start the postgres container:
   
docker run \
    --name pgvector-container \
    -e POSTGRES_USER=langchain \
    -e POSTGRES_PASSWORD=langchain \
    -e POSTGRES_DB=langchain \
    -p 6024:5432 \
    -d pgvector/pgvector:pg16
3. Use the connection string below for the postgres container
*/

import { TextLoader } from 'langchain/document_loaders/fs/text';
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';
import { OpenAIEmbeddings } from '@langchain/openai';
import { PGVectorStore } from '@langchain/community/vectorstores/pgvector';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { ChatOpenAI } from '@langchain/openai';
import { RunnableLambda } from '@langchain/core/runnables';
const connectionString =
  'postgresql://langchain:langchain@localhost:6024/langchain';
// Load the document, split it into chunks
const loader = new TextLoader('./test.txt');
const raw_docs = await loader.load();
const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 1000,
  chunkOverlap: 200,
});
const splitDocs = await splitter.splitDocuments(raw_docs);

// embed each chunk and insert it into the vector store
const model = new OpenAIEmbeddings();

const db = await PGVectorStore.fromDocuments(splitDocs, model, {
  postgresConnectionOptions: {
    connectionString,
  },
});

// retrieve 2 relevant documents from the vector store
const retriever = db.asRetriever({ k: 2 });

/**
 * Query starts with irrelevant information before asking the relevant question
 */
const query =
  'Today I woke up and brushed my teeth, then I sat down to read the news. But then I forgot the food on the cooker. Who are some key figures in the ancient greek history of philosophy?';
/**
 * Provide retrieved docs as context to the LLM to answer a user's question
 */
const prompt = ChatPromptTemplate.fromTemplate(
  'Answer the question based only on the following context:\n {context}\n\nQuestion: {question}'
);

const llm = new ChatOpenAI({ temperature: 0, modelName: 'gpt-3.5-turbo' });

const qa = RunnableLambda.from(async (input) => {
  // fetch relevant documents
  const docs = await retriever.invoke(input);
  // format prompt
  const formatted = await prompt.invoke({ context: docs, question: input });
  // generate answer
  const answer = await llm.invoke(formatted);
  return { answer, docs };
});

const result = await qa.invoke(query);
console.log(result);
console.log('\n\nCall model again with rewritten query\n\n');

const rewritePrompt = ChatPromptTemplate.fromTemplate(
  `Provide a better search query for web search engine to answer the given question, end the queries with '**'. Question: {question} Answer:`
);
const rewriter = rewritePrompt.pipe(llm).pipe((message) => {
  return message.content.replaceAll('"', '').replaceAll('**');
});
const rewriterQA = RunnableLambda.from(async (input) => {
  const newQuery = await rewriter.invoke({ question: input }); // fetch relevant documents  console.log('New query: ', newQuery);
  const docs = await retriever.invoke(newQuery); // format prompt
  const formatted = await prompt.invoke({ context: docs, question: input }); // generate answer
  const answer = await llm.invoke(formatted);
  return answer;
});

const finalResult = await rewriterQA.invoke(query);
console.log(finalResult);


================================================
FILE: ch3/js/c-multi-query.js
================================================
/** 
1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)
2. Run the following command to start the postgres container:
   
docker run \
    --name pgvector-container \
    -e POSTGRES_USER=langchain \
    -e POSTGRES_PASSWORD=langchain \
    -e POSTGRES_DB=langchain \
    -p 6024:5432 \
    -d pgvector/pgvector:pg16
3. Use the connection string below for the postgres container
*/

import { TextLoader } from 'langchain/document_loaders/fs/text';
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';
import { OpenAIEmbeddings } from '@langchain/openai';
import { PGVectorStore } from '@langchain/community/vectorstores/pgvector';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { ChatOpenAI } from '@langchain/openai';
import { RunnableLambda } from '@langchain/core/runnables';

const connectionString =
  'postgresql://langchain:langchain@localhost:6024/langchain';
// Load the document, split it into chunks
const loader = new TextLoader('./test.txt');
const raw_docs = await loader.load();
const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 1000,
  chunkOverlap: 200,
});
const splitDocs = await splitter.splitDocuments(raw_docs);

// embed each chunk and insert it into the vector store
const model = new OpenAIEmbeddings();

const db = await PGVectorStore.fromDocuments(splitDocs, model, {
  postgresConnectionOptions: {
    connectionString,
  },
});

// retrieve 2 relevant documents from the vector store
const retriever = db.asRetriever({ k: 2 });
/**
 * Provide retrieved docs as context to the LLM to answer a user's question
 */
const llm = new ChatOpenAI({ temperature: 0, modelName: 'gpt-3.5-turbo' });

const perspectivesPrompt = ChatPromptTemplate.fromTemplate(
  `You are an AI language model assistant. Your task is to generate five different versions of the given user question to retrieve relevant documents from a vector database. By generating multiple perspectives on the user question, your goal is to help the user overcome some of the limitations of the distance-based similarity search. Provide these alternative questions separated by newlines. Original question: {question}`
);

const queryGen = perspectivesPrompt.pipe(llm).pipe((message) => {
  return message.content.split('\n');
});

/**
 * This chain retrieves and combines the documents from the vector store for each query
 */
const retrievalChain = queryGen
  .pipe(retriever.batch.bind(retriever))
  .pipe((documentLists) => {
    const dedupedDocs = {};
    documentLists.flat().forEach((doc) => {
      dedupedDocs[doc.pageContent] = doc;
    });
    return Object.values(dedupedDocs);
  });

const prompt = ChatPromptTemplate.fromTemplate(
  'Answer the question based only on the following context:\n {context}\n\nQuestion: {question}'
);

console.log('Running multi query qa\n');
const multiQueryQa = RunnableLambda.from(async (input) => {
  // fetch relevant documents
  const docs = await retrievalChain.invoke({ question: input });
  // format prompt
  const formatted = await prompt.invoke({ context: docs, question: input });
  // generate answer
  const answer = await llm.invoke(formatted);
  return answer;
});

const result = await multiQueryQa.invoke(
  'Who are the key figures in the ancient greek history of philosophy?'
);

console.log(result);


================================================
FILE: ch3/js/d-rag-fusion.js
================================================
/** 
1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)
2. Run the following command to start the postgres container:
   
docker run \
    --name pgvector-container \
    -e POSTGRES_USER=langchain \
    -e POSTGRES_PASSWORD=langchain \
    -e POSTGRES_DB=langchain \
    -p 6024:5432 \
    -d pgvector/pgvector:pg16
3. Use the connection string below for the postgres container
*/

import { TextLoader } from 'langchain/document_loaders/fs/text';
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';
import { OpenAIEmbeddings } from '@langchain/openai';
import { PGVectorStore } from '@langchain/community/vectorstores/pgvector';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { ChatOpenAI } from '@langchain/openai';
import { RunnableLambda } from '@langchain/core/runnables';

const connectionString =
  'postgresql://langchain:langchain@localhost:6024/langchain';
// Load the document, split it into chunks
const loader = new TextLoader('./test.txt');
const raw_docs = await loader.load();
const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 1000,
  chunkOverlap: 200,
});
const splitDocs = await splitter.splitDocuments(raw_docs);

// embed each chunk and insert it into the vector store
const model = new OpenAIEmbeddings();

const db = await PGVectorStore.fromDocuments(splitDocs, model, {
  postgresConnectionOptions: {
    connectionString,
  },
});

// retrieve 2 relevant documents from the vector store
const retriever = db.asRetriever({ k: 2 });
/**
 * Provide retrieved docs as context to the LLM to answer a user's question
 */
const llm = new ChatOpenAI({ temperature: 0, modelName: 'gpt-3.5-turbo' });

const perspectivesPrompt = ChatPromptTemplate.fromTemplate(
  `You are a helpful assistant that generates multiple search queries based on a single input query. \n Generate multiple search queries related to: {question} \n Output (4 queries):`
);
const queryGen = perspectivesPrompt.pipe(llm).pipe((message) => {
  return message.content.split('\n');
});

function reciprocalRankFusion(results, k = 60) {
  // Initialize a dictionary to hold fused scores for each document
  // Documents will be keyed by their contents to ensure uniqueness
  const fusedScores = {};
  const documents = {};
  results.forEach((docs) => {
    docs.forEach((doc, rank) => {
      // Use the document contents as the key for uniqueness
      const key = doc.pageContent;
      // If the document hasn't been seen yet,
      // - initialize score to 0
      // - save it for later
      if (!(key in fusedScores)) {
        fusedScores[key] = 0;
        documents[key] = 0;
      }
      // Update the score of the document using the RRF formula:
      // 1 / (rank + k)
      fusedScores[key] += 1 / (rank + k);
    });
  });
  // Sort the documents based on their fused scores in descending order to get the final reranked results
  const sorted = Object.entries(fusedScores).sort((a, b) => b[1] - a[1]);
  // retrieve the corresponding doc for each key
  return sorted.map(([key]) => documents[key]);
}

const prompt = ChatPromptTemplate.fromTemplate(
  'Answer the question based only on the following context:\n {context}\n\nQuestion: {question}'
);

const retrievalChain = queryGen
  .pipe(retriever.batch.bind(retriever))
  .pipe(reciprocalRankFusion);

console.log('Running rag fusion\n');
const ragFusion = RunnableLambda.from(async (input) => {
  // fetch relevant documents
  const docs = await retrievalChain.invoke({ question: input });
  // format prompt
  const formatted = await prompt.invoke({ context: docs, question: input });
  // generate answer
  const answer = await llm.invoke(formatted);
  return answer;
});

const result = await ragFusion.invoke(
  'Who are the key figures in the ancient greek history of philosophy?'
);

console.log(result);


================================================
FILE: ch3/js/e-hyde.js
================================================
/** 
1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)
2. Run the following command to start the postgres container:
   
docker run \
    --name pgvector-container \
    -e POSTGRES_USER=langchain \
    -e POSTGRES_PASSWORD=langchain \
    -e POSTGRES_DB=langchain \
    -p 6024:5432 \
    -d pgvector/pgvector:pg16
3. Use the connection string below for the postgres container
*/

import { TextLoader } from 'langchain/document_loaders/fs/text';
import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';
import { OpenAIEmbeddings } from '@langchain/openai';
import { PGVectorStore } from '@langchain/community/vectorstores/pgvector';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { ChatOpenAI } from '@langchain/openai';
import { RunnableLambda } from '@langchain/core/runnables';

const connectionString =
  'postgresql://langchain:langchain@localhost:6024/langchain';
// Load the document, split it into chunks
const loader = new TextLoader('./test.txt');
const raw_docs = await loader.load();
const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 1000,
  chunkOverlap: 200,
});
const splitDocs = await splitter.splitDocuments(raw_docs);

// embed each chunk and insert it into the vector store
const model = new OpenAIEmbeddings();

const db = await PGVectorStore.fromDocuments(splitDocs, model, {
  postgresConnectionOptions: {
    connectionString,
  },
});

// retrieve 2 relevant documents from the vector store
const retriever = db.asRetriever({ k: 2 });
/**
 * Provide retrieved docs as context to the LLM to answer a user's question
 */
const llm = new ChatOpenAI({ temperature: 0, modelName: 'gpt-3.5-turbo' });

const hydePrompt = ChatPromptTemplate.fromTemplate(
  `Please write a passage to answer the question.\n Question: {question} \n Passage:`
);

const generatedDoc = hydePrompt.pipe(llm).pipe((msg) => msg.content);

/**
 * This chain retrieves and combines the documents from the vector store for each query
 */
const retrievalChain = generatedDoc.pipe(retriever);

const prompt = ChatPromptTemplate.fromTemplate(
  'Answer the question based only on the following context:\n {context}\n\nQuestion: {question}'
);

console.log('Running hyde\n');
const hydeQa = RunnableLambda.from(async (input) => {
  // fetch relevant documents
  const docs = await retrievalChain.invoke(input);
  // format prompt
  const formatted = await prompt.invoke({ context: docs, question: input });
  // generate answer
  const answer = await llm.invoke(formatted);
  return answer;
});

const result = await hydeQa.invoke(
  'Who are some lesser known philosophers in the ancient greek history of philosophy?'
);

console.log(result);


================================================
FILE: ch3/js/f-router.js
================================================
import { ChatOpenAI } from '@langchain/openai';
import { z } from 'zod';
import { ChatPromptTemplate } from '@langchain/core/prompts';

const routeQuery = z
  .object({
    datasource: z
      .enum(['python_docs', 'js_docs'])
      .describe(
        'Given a user question, choose which datasource would be most relevant for answering their question'
      ),
  })
  .describe('Route a user query to the most relevant datasource.');

const llm = new ChatOpenAI({ model: 'gpt-3.5-turbo', temperature: 0 });
// withStructuredOutput is a method that allows us to use the structured output of the model
const structuredLlm = llm.withStructuredOutput(routeQuery, {
  name: 'RouteQuery',
});

const prompt = ChatPromptTemplate.fromMessages([
  [
    'system',
    `You are an expert at routing a user question to the appropriate data source. Based on the programming language the question is referring to, route it to the relevant data source.`,
  ],
  ['human', '{question}'],
]);

const router = prompt.pipe(structuredLlm);

const question = `Why doesn't the following code work: 
from langchain_core.prompts 
import ChatPromptTemplate 
prompt = ChatPromptTemplate.from_messages(["human", "speak in {language}"]) 
prompt.invoke("french") `;

const result = await router.invoke({ question });

console.log('Routing to: ', result);

/** Once we’ve extracted the relevant data source, we can pass the value into another function to execute additional logic as required: */

const chooseRoute = (result) => {
  if (result.datasource.toLowerCase().includes('python_docs')) {
    return 'chain for python_docs';
  } else {
    return 'chain for js_docs';
  }
};

const fullChain = router.pipe(chooseRoute);

const finalResult = await fullChain.invoke({ question });

console.log('Choose route: ', finalResult);


================================================
FILE: ch3/js/g-semantic-router.js
================================================
import { cosineSimilarity } from '@langchain/core/utils/math';
import { ChatOpenAI, OpenAIEmbeddings } from '@langchain/openai';
import { PromptTemplate } from '@langchain/core/prompts';
import { RunnableLambda } from '@langchain/core/runnables';

const physicsTemplate = `You are a very smart physics professor. You are great     at answering questions about physics in a concise and easy-to-understand     manner. When you don't know the answer to a question, you admit that you don't know. Here is a question: {query}`;

const mathTemplate = `You are a very good mathematician. You are great at answering     math questions. You are so good because you are able to break down hard     problems into their component parts, answer the component parts, and then     put them together to answer the broader question. Here is a question: {query}`;

const embeddings = new OpenAIEmbeddings();

const promptTemplates = [physicsTemplate, mathTemplate];

const promptEmbeddings = await embeddings.embedDocuments(promptTemplates);

const promptRouter = RunnableLambda.from(async (query) => {
  // Embed question
  const queryEmbedding = await embeddings.embedQuery(query);
  // Compute similarity
  const similarities = cosineSimilarity([queryEmbedding], promptEmbeddings)[0];
  // Pick the prompt most similar to the input question
  const mostSimilar =
    similarities[0] > similarities[1] ? promptTemplates[0] : promptTemplates[1];
  console.log(
    `Using ${mostSimilar === promptTemplates[0] ? 'PHYSICS' : 'MATH'}`
  );
  return PromptTemplate.fromTemplate(mostSimilar).invoke({ query });
});

const semanticRouter = promptRouter.pipe(
  new ChatOpenAI({ modelName: 'gpt-3.5-turbo', temperature: 0 })
);

const result = await semanticRouter.invoke('What is a black hole');
console.log('\nSemantic router result: ', result);


================================================
FILE: ch3/js/h-self-query.js
================================================
import { ChatOpenAI } from '@langchain/openai';
import { SelfQueryRetriever } from 'langchain/retrievers/self_query';
import { FunctionalTranslator } from '@langchain/core/structured_query';
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
import { Document } from 'langchain/document';
import { AttributeInfo } from 'langchain/chains/query_constructor';
import { OpenAIEmbeddings } from '@langchain/openai';
/**
 * First, we create a bunch of documents. You can load your own documents here instead.
 * Each document has a pageContent and a metadata field. Make sure your metadata matches the AttributeInfo below.
 */
const docs = [
  new Document({
    pageContent:
      'A bunch of scientists bring back dinosaurs and mayhem breaks loose',
    metadata: {
      year: 1993,
      rating: 7.7,
      genre: 'science fiction',
      length: 122,
    },
  }),
  new Document({
    pageContent:
      'Leo DiCaprio gets lost in a dream within a dream within a dream within a ...',
    metadata: {
      year: 2010,
      director: 'Christopher Nolan',
      rating: 8.2,
      length: 148,
    },
  }),
  new Document({
    pageContent:
      'A psychologist / detective gets lost in a series of dreams within dreams within dreams and Inception reused the idea',
    metadata: { year: 2006, director: 'Satoshi Kon', rating: 8.6 },
  }),
  new Document({
    pageContent:
      'A bunch of normal-sized women are supremely wholesome and some men pine after them',
    metadata: {
      year: 2019,
      director: 'Greta Gerwig',
      rating: 8.3,
      length: 135,
    },
  }),
  new Document({
    pageContent: 'Toys come alive and have a blast doing so',
    metadata: { year: 1995, genre: 'animated', length: 77 },
  }),
  new Document({
    pageContent: 'Three men walk into the Zone, three men walk out of the Zone',
    metadata: {
      year: 1979,
      director: 'Andrei Tarkovsky',
      genre: 'science fiction',
      rating: 9.9,
    },
  }),
];

const llm = new ChatOpenAI({ modelName: 'gpt-3.5-turbo', temperature: 0 });

const embeddings = new OpenAIEmbeddings();

const vectorStore = await MemoryVectorStore.fromDocuments(docs, embeddings);

/** * First, we define the attributes we want to be able to query on. * in this case, we want to be able to query on the genre, year, director,     rating, and length of the movie. * We also provide a description of each attribute and the type of the attribute. * This is used to generate the query prompts. */

const fields = [
  {
    name: 'genre',
    description: 'The genre of the movie',
    type: 'string or array of strings',
  },
  {
    name: 'year',
    description: 'The year the movie was released',
    type: 'number',
  },
  {
    name: 'director',
    description: 'The director of the movie',
    type: 'string',
  },
  {
    name: 'rating',
    description: 'The rating of the movie (1-10)',
    type: 'number',
  },
  {
    name: 'length',
    description: 'The length of the movie in minutes',
    type: 'number',
  },
];

const attributeInfos = fields.map(
  (field) => new AttributeInfo(field.name, field.description, field.type)
);
const description = 'Brief summary of a movie';
const selfQueryRetriever = SelfQueryRetriever.fromLLM({
  llm,
  vectorStore,
  description,
  attributeInfo: attributeInfos,
  /**
   * We need to use a translator that translates the queries into a
   * filter format that the vector store can understand. LangChain provides one
   * here.
   */
  structuredQueryTranslator: new FunctionalTranslator(),
});

const result = await selfQueryRetriever.invoke(
  'Which movies are less than 90 minutes?'
);
console.log(result);


================================================
FILE: ch3/js/i-sql-example.js
================================================
/*
The below example will use a SQLite connection with the Chinook database, which is a sample database that represents a digital media store. Follow these installation steps to create Chinook.db in the same directory as this notebook. You can also download and build the database via the command line:

```bash
curl -s https://raw.githubusercontent.com/lerocha/chinook-database/master/ChinookDatabase/DataSources/Chinook_Sqlite.sql | sqlite3 Chinook.db

```

Afterwards, place `Chinook.db` in the same directory where this code is running.

*/

import { ChatOpenAI } from '@langchain/openai';
import { createSqlQueryChain } from 'langchain/chains/sql_db';
import { SqlDatabase } from 'langchain/sql_db';
import { DataSource } from 'typeorm';
import { QuerySqlTool } from 'langchain/tools/sql';

const datasource = new DataSource({
  type: 'sqlite',
  database: 'Chinook.db', //this should be the path to the db
});
const db = await SqlDatabase.fromDataSourceParams({
  appDataSource: datasource,
});
//test that the db is working
await db.run('SELECT * FROM Artist LIMIT 10;');

const llm = new ChatOpenAI({ modelName: 'gpt-4o', temperature: 0 });
// convert question to sql query
const writeQuery = await createSqlQueryChain({ llm, db, dialect: 'sqlite' });
// execute query
const executeQuery = new QuerySqlTool(db);
// combined
const chain = writeQuery.pipe(executeQuery);

const result = await chain.invoke({
  question: 'How many employees are there?',
});
console.log(result);


================================================
FILE: ch3/py/a-basic-rag.py
================================================
"""
1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)
2. pip install -qU langchain_postgres
3. Run the following command to start the postgres container:
   
docker run \
    --name pgvector-container \
    -e POSTGRES_USER=langchain \
    -e POSTGRES_PASSWORD=langchain \
    -e POSTGRES_DB=langchain \
    -p 6024:5432 \
    -d pgvector/pgvector:pg16
4. Use the connection string below for the postgres container

"""

from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_postgres.vectorstores import PGVector
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import chain


# See docker command above to launch a postgres instance with pgvector enabled.
connection = "postgresql+psycopg://langchain:langchain@localhost:6024/langchain"

# Load the document, split it into chunks
raw_documents = TextLoader('./test.txt', encoding='utf-8').load()
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000, chunk_overlap=200)
documents = text_splitter.split_documents(raw_documents)

# Create embeddings for the documents
embeddings_model = OpenAIEmbeddings()

db = PGVector.from_documents(
    documents, embeddings_model, connection=connection)

# create retriever to retrieve 2 relevant documents
retriever = db.as_retriever(search_kwargs={"k": 2})

query = 'Who are the key figures in the ancient greek history of philosophy?'

# fetch relevant documents
docs = retriever.invoke(query)

print(docs[0].page_content)

prompt = ChatPromptTemplate.from_template(
    """Answer the question based only on the following context: {context} Question: {question} """
)
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
llm_chain = prompt | llm

# answer the question based on relevant documents
result = llm_chain.invoke({"context": docs, "question": query})

print(result)
print("\n\n")

# Run again but this time encapsulate the logic for efficiency

# @chain decorator transforms this function into a LangChain runnable,
# making it compatible with LangChain's chain operations and pipeline

print("Running again but this time encapsulate the logic for efficiency\n")


@chain
def qa(input):
    # fetch relevant documents
    docs = retriever.invoke(input)
    # format prompt
    formatted = prompt.invoke({"context": docs, "question": input})
    # generate answer
    answer = llm.invoke(formatted)
    return answer


# run it
result = qa.invoke(query)
print(result.content)


================================================
FILE: ch3/py/b-rewrite.py
================================================
"""
1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)
2. pip install -qU langchain_postgres
3. Run the following command to start the postgres container:
   
docker run \
    --name pgvector-container \
    -e POSTGRES_USER=langchain \
    -e POSTGRES_PASSWORD=langchain \
    -e POSTGRES_DB=langchain \
    -p 6024:5432 \
    -d pgvector/pgvector:pg16
4. Use the connection string below for the postgres container

"""

from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_postgres.vectorstores import PGVector
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import chain


# See docker command above to launch a postgres instance with pgvector enabled.
connection = "postgresql+psycopg://langchain:langchain@localhost:6024/langchain"

# Load the document, split it into chunks
raw_documents = TextLoader('./test.txt', encoding='utf-8').load()
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000, chunk_overlap=200)
documents = text_splitter.split_documents(raw_documents)

# Create embeddings for the documents
embeddings_model = OpenAIEmbeddings()

db = PGVector.from_documents(
    documents, embeddings_model, connection=connection)

# create retriever to retrieve 2 relevant documents
retriever = db.as_retriever(search_kwargs={"k": 2})

# Query starts with irrelevant information before asking the relevant question
query = 'Today I woke up and brushed my teeth, then I sat down to read the news. But then I forgot the food on the cooker. Who are some key figures in the ancient greek history of philosophy?'

# fetch relevant documents
docs = retriever.invoke(query)

print(docs[0].page_content)
print("\n\n")

prompt = ChatPromptTemplate.from_template(
    """Answer the question based only on the following context: {context} Question: {question} """
)
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)


# Run again but this time encapsulate the logic for efficiency

# @chain decorator transforms this function into a LangChain runnable,
# making it compatible with LangChain's chain operations and pipeline

@chain
def qa(input):
    # fetch relevant documents
    docs = retriever.invoke(input)
    # format prompt
    formatted = prompt.invoke({"context": docs, "question": input})
    # generate answer
    answer = llm.invoke(formatted)
    return answer


# run it
result = qa.invoke(query)
print(result.content)

print("\nRewrite the query to improve accuracy\n")

rewrite_prompt = ChatPromptTemplate.from_template(
    """Provide a better search query for web search engine to answer the given question, end the queries with ’**’. Question: {x} Answer:""")


def parse_rewriter_output(message):
    return message.content.strip('"').strip("**")


rewriter = rewrite_prompt | llm | parse_rewriter_output


@chain
def qa_rrr(input):
    # rewrite the query
    new_query = rewriter.invoke(input)
    print("Rewritten query: ", new_query)
    # fetch relevant documents
    docs = retriever.invoke(new_query)
    # format prompt
    formatted = prompt.invoke({"context": docs, "question": input})
    # generate answer
    answer = llm.invoke(formatted)
    return answer


print("\nCall model again with rewritten query\n")

# call model again with rewritten query
result = qa_rrr.invoke(query)
print(result.content)


================================================
FILE: ch3/py/c-multi-query.py
================================================
from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_postgres.vectorstores import PGVector
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import chain


# See docker command above to launch a postgres instance with pgvector enabled.
connection = "postgresql+psycopg://langchain:langchain@localhost:6024/langchain"

# Load the document, split it into chunks
raw_documents = TextLoader('./test.txt', encoding='utf-8').load()
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000, chunk_overlap=200)
documents = text_splitter.split_documents(raw_documents)

# Create embeddings for the documents
embeddings_model = OpenAIEmbeddings()

db = PGVector.from_documents(
    documents, embeddings_model, connection=connection)

# create retriever to retrieve 2 relevant documents
retriever = db.as_retriever(search_kwargs={"k": 5})

# instruction to generate multiple queries
perspectives_prompt = ChatPromptTemplate.from_template(
    """You are an AI language model assistant. Your task is to generate five different versions of the given user question to retrieve relevant documents from a vector database. 
    By generating multiple perspectives on the user question, your goal is to help the user overcome some of the limitations of the distance-based  similarity search. 
    Provide these alternative questions separated by newlines. 
    Original question: {question}""")

llm = ChatOpenAI(model="gpt-3.5-turbo")


def parse_queries_output(message):
    return message.content.split('\n')


query_gen = perspectives_prompt | llm | parse_queries_output


def get_unique_union(document_lists):
    # Flatten list of lists, and dedupe them
    deduped_docs = {
        doc.page_content: doc for sublist in document_lists for doc in sublist}
    # return a flat list of unique docs
    return list(deduped_docs.values())


retrieval_chain = query_gen | retriever.batch | get_unique_union

prompt = ChatPromptTemplate.from_template(
    """Answer the question based only on the following context: {context} Question: {question} """
)

query = "Who are the key figures in the ancient greek history of philosophy?"


@chain
def multi_query_qa(input):
    # fetch relevant documents
    docs = retrieval_chain.invoke(input)  # format prompt
    formatted = prompt.invoke(
        {"context": docs, "question": input})  # generate answer
    answer = llm.invoke(formatted)
    return answer


# run
print("Running multi query qa\n")
result = multi_query_qa.invoke(query)
print(result.content)


================================================
FILE: ch3/py/d-rag-fusion.py
================================================
from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_postgres.vectorstores import PGVector
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import chain

# See docker command above to launch a postgres instance with pgvector enabled.
connection = "postgresql+psycopg://langchain:langchain@localhost:6024/langchain"

# Load the document, split it into chunks
raw_documents = TextLoader('./test.txt', encoding='utf-8').load()
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000, chunk_overlap=200)
documents = text_splitter.split_documents(raw_documents)

# Create embeddings for the documents
embeddings_model = OpenAIEmbeddings()

db = PGVector.from_documents(
    documents, embeddings_model, connection=connection)

# create retriever to retrieve 2 relevant documents
retriever = db.as_retriever(search_kwargs={"k": 5})

prompt_rag_fusion = ChatPromptTemplate.from_template(
    """You are a helpful assistant that generates multiple search queries based on a single input query. \n Generate multiple search queries related to: {question} \n Output (4 queries):""")


def parse_queries_output(message):
    return message.content.split('\n')


llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
query_gen = prompt_rag_fusion | llm | parse_queries_output

query = "Who are the key figures in the ancient greek history of philosophy?"

generated_queries = query_gen.invoke(query)

print("generated queries: ", generated_queries)


"""
we fetch relevant documents for each query and pass them into a function to rerank (that is, reorder according to relevancy) the final list of relevant documents.
"""


def reciprocal_rank_fusion(results: list[list], k=60):
    """reciprocal rank fusion on multiple lists of ranked documents and an optional parameter k used in the RRF formula"""
    # Initialize a dictionary to hold fused scores for each document
    # Documents will be keyed by their contents to ensure uniqueness
    fused_scores = {}
    documents = {}
    for docs in results:
        # Iterate through each document in the list, with its rank (position in the list)
        for rank, doc in enumerate(docs):
            doc_str = doc.page_content
            if doc_str not in fused_scores:
                fused_scores[doc_str] = 0
                documents[doc_str] = doc
            fused_scores[doc_str] += 1 / (rank + k)
    # sort the documents based on their fused scores in descending order to get the final reranked results
    reranked_doc_strs = sorted(
        fused_scores, key=lambda d: fused_scores[d], reverse=True)
    return [documents[doc_str] for doc_str in reranked_doc_strs]


retrieval_chain = query_gen | retriever.batch | reciprocal_rank_fusion

result = retrieval_chain.invoke(query)

print("retrieved context using rank fusion: ", result[0].page_content)
print("\n\n")

print("Use model to answer question based on retrieved docs\n")


prompt = ChatPromptTemplate.from_template(
    """Answer the question based only on the following context: {context} Question: {question} """
)

query = "Who are the some important yet not well known philosophers in the ancient greek history of philosophy?"


@chain
def rag_fusion(input):
    # fetch relevant documents
    docs = retrieval_chain.invoke(input)  # format prompt
    formatted = prompt.invoke(
        {"context": docs, "question": input})  # generate answer
    answer = llm.invoke(formatted)
    return answer


# run
print("Running rag fusion\n")
result = rag_fusion.invoke(query)
print(result.content)


================================================
FILE: ch3/py/e-hyde.py
================================================
from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_postgres.vectorstores import PGVector
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import chain
from langchain_core.output_parsers import StrOutputParser

# See docker command above to launch a postgres instance with pgvector enabled.
connection = "postgresql+psycopg://langchain:langchain@localhost:6024/langchain"

# Load the document, split it into chunks
raw_documents = TextLoader('./test.txt', encoding='utf-8').load()
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000, chunk_overlap=200)
documents = text_splitter.split_documents(raw_documents)

# Create embeddings for the documents
embeddings_model = OpenAIEmbeddings()

db = PGVector.from_documents(
    documents, embeddings_model, connection=connection)

# create retriever to retrieve 2 relevant documents
retriever = db.as_retriever(search_kwargs={"k": 5})

prompt_hyde = ChatPromptTemplate.from_template(
    """Please write a passage to answer the question.\n Question: {question} \n Passage:""")

generate_doc = (prompt_hyde | ChatOpenAI(temperature=0) | StrOutputParser())

"""
Next, we take the hypothetical document generated above and use it as input to the retriever, 
which will generate its embedding and search for similar documents in the vector store:
"""
retrieval_chain = generate_doc | retriever

query = "Who are some lesser known philosophers in the ancient greek history of philosophy?"

prompt = ChatPromptTemplate.from_template(
    """Answer the question based only on the following context: {context} Question: {question} """
)

llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)


@chain
def qa(input):
    # fetch relevant documents from the hyde retrieval chain defined earlier
    docs = retrieval_chain.invoke(input)
    # format prompt
    formatted = prompt.invoke({"context": docs, "question": input})
    # generate answer
    answer = llm.invoke(formatted)
    return answer


print("Running hyde\n")
result = qa.invoke(query)
print("\n\n")
print(result.content)


================================================
FILE: ch3/py/f-router.py
================================================

from typing import Literal
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field
from langchain_openai import ChatOpenAI
from langchain_core.runnables import RunnableLambda


# Data model class
class RouteQuery(BaseModel):
    """Route a user query to the most relevant datasource."""
    datasource: Literal["python_docs", "js_docs"] = Field(
        ...,
        description="Given a user question, choose which datasource would be most relevant for answering their question",
    )


# Prompt template
# LLM with function call
llm = ChatOpenAI(model="gpt-4o", temperature=0)

"""
with_structured_output: Model wrapper that returns outputs formatted to match the given schema.

"""
structured_llm = llm.with_structured_output(RouteQuery)

# Prompt
system = """You are an expert at routing a user question to the appropriate data source. Based on the programming language the question is referring to, route it to the relevant data source."""
prompt = ChatPromptTemplate.from_messages(
    [("system", system), ("human", "{question}")]
)

# Define router
router = prompt | structured_llm

# Run
question = """Why doesn't the following code work: 
from langchain_core.prompts 
import ChatPromptTemplate 
prompt = ChatPromptTemplate.from_messages(["human", "speak in {language}"]) 
prompt.invoke("french") """

result = router.invoke({"question": question})
print("\nRouting to: ", result)

"""
Once we extracted the relevant data source, we can pass the value into another function to execute additional logic as required:
"""


def choose_route(result):
    if "python_docs" in result.datasource.lower():
        return "chain for python_docs"
    else:
        return "chain for js_docs"


full_chain = router | RunnableLambda(choose_route)

result = full_chain.invoke({"question": question})
print("\nChoose route: ", result)


================================================
FILE: ch3/py/g-semantic-router.py
================================================
from langchain.utils.math import cosine_similarity
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import chain
from langchain_openai import ChatOpenAI, OpenAIEmbeddings

physics_template = """You are a very smart physics professor. You are great at     answering questions about physics in a concise and easy-to-understand manner.     When you don't know the answer to a question, you admit that you don't know. Here is a question: {query}"""
math_template = """You are a very good mathematician. You are great at answering     math questions. You are so good because you are able to break down hard     problems into their component parts, answer the component parts, and then     put them together to answer the broader question. Here is a question: {query}"""

# Embed prompts
embeddings = OpenAIEmbeddings()
prompt_templates = [physics_template, math_template]
prompt_embeddings = embeddings.embed_documents(prompt_templates)

# Route question to prompt


@chain
def prompt_router(query):
    query_embedding = embeddings.embed_query(query)
    similarity = cosine_similarity([query_embedding], prompt_embeddings)[0]
    most_similar = prompt_templates[similarity.argmax()]
    print("Using MATH" if most_similar == math_template else "Using PHYSICS")
    return PromptTemplate.from_template(most_similar)


semantic_router = (prompt_router | ChatOpenAI() | StrOutputParser())

result = semantic_router.invoke("What's a black hole")
print("\nSemantic router result: ", result)


================================================
FILE: ch3/py/h-self-query.py
================================================
# pip install lark

from langchain.chains.query_constructor.base import AttributeInfo
from langchain.retrievers.self_query.base import SelfQueryRetriever
from langchain_openai import ChatOpenAI
from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_postgres.vectorstores import PGVector
from langchain_core.documents import Document

# See docker command above to launch a postgres instance with pgvector enabled.
connection = "postgresql+psycopg://langchain:langchain@localhost:6024/langchain"

docs = [
    Document(
        page_content="A bunch of scientists bring back dinosaurs and mayhem breaks loose",
        metadata={"year": 1993, "rating": 7.7, "genre": "science fiction"},
    ),
    Document(
        page_content="Leo DiCaprio gets lost in a dream within a dream within a dream within a ...",
        metadata={"year": 2010, "director": "Christopher Nolan", "rating": 8.2},
    ),
    Document(
        page_content="A psychologist / detective gets lost in a series of dreams within dreams within dreams and Inception reused the idea",
        metadata={"year": 2006, "director": "Satoshi Kon", "rating": 8.6},
    ),
    Document(
        page_content="A bunch of normal-sized women are supremely wholesome and some men pine after them",
        metadata={"year": 2019, "director": "Greta Gerwig", "rating": 8.3},
    ),
    Document(
        page_content="Toys come alive and have a blast doing so",
        metadata={"year": 1995, "genre": "animated"},
    ),
    Document(
        page_content="Three men walk into the Zone, three men walk out of the Zone",
        metadata={
            "year": 1979,
            "director": "Andrei Tarkovsky",
            "genre": "thriller",
            "rating": 9.9,
        },
    ),
]

# Create embeddings for the documents
embeddings_model = OpenAIEmbeddings()

vectorstore = PGVector.from_documents(
    docs, embeddings_model, connection=connection)

# Define the fields for the query
fields = [
    AttributeInfo(
        name="genre",
        description="The genre of the movie",
        type="string or list[string]",
    ),
    AttributeInfo(
        name="year",
        description="The year the movie was released",
        type="integer",
    ),
    AttributeInfo(
        name="director",
        description="The name of the movie director",
        type="string",
    ),
    AttributeInfo(
        name="rating",
        description="A 1-10 rating for the movie",
        type="float",
    ),
]

description = "Brief summary of a movie"
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
retriever = SelfQueryRetriever.from_llm(llm, vectorstore, description, fields)

# This example only specifies a filter
print(retriever.invoke("I want to watch a movie rated higher than 8.5"))

print('\n')

# This example specifies multiple filters
print(retriever.invoke(
    "What's a highly rated (above 8.5) science fiction film?"))


================================================
FILE: ch3/py/i-sql-example.py
================================================
"""
The below example will use a SQLite connection with the Chinook database, which is a sample database that represents a digital media store. Follow these installation steps to create Chinook.db in the same directory as this notebook. You can also download and build the database via the command line:

```bash
curl -s https://raw.githubusercontent.com/lerocha/chinook-database/master/ChinookDatabase/DataSources/Chinook_Sqlite.sql | sqlite3 Chinook.db

```

Afterwards, place `Chinook.db` in the same directory where this code is running.

"""

from langchain_community.tools import QuerySQLDatabaseTool
from langchain_community.utilities import SQLDatabase
from langchain.chains import create_sql_query_chain
# replace this with the connection details of your db
from langchain_openai import ChatOpenAI

db = SQLDatabase.from_uri("sqlite:///Chinook.db")
print(db.get_usable_table_names())
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

# convert question to sql query
write_query = create_sql_query_chain(llm, db)

# Execute SQL query
execute_query = QuerySQLDatabaseTool(db=db)

# combined chain = write_query | execute_query
combined_chain = write_query | execute_query

# run the chain
result = combined_chain.invoke({"question": "How many employees are there?"})

print(result)


================================================
FILE: ch4/js/a-simple-memory.js
================================================
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { ChatOpenAI } from '@langchain/openai';

const prompt = ChatPromptTemplate.fromMessages([
  [
    'system',
    'You are a helpful assistant. Answer all questions to the best of your ability.',
  ],
  ['placeholder', '{messages}'],
]);
const model = new ChatOpenAI();
const chain = prompt.pipe(model);

const response = await chain.invoke({
  messages: [
    [
      'human',
      'Translate this sentence from English to French: I love programming.',
    ],
    ['ai', "J'adore programmer."],
    ['human', 'What did you just say?'],
  ],
});

console.log(response.content);


================================================
FILE: ch4/js/b-state-graph.js
================================================
import {
  StateGraph,
  Annotation,
  messagesStateReducer,
  START,
  END,
} from '@langchain/langgraph';
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage } from '@langchain/core/messages';

const State = {
  messages: Annotation({
    reducer: messagesStateReducer,
    default: () => [],
  }),
};

let builder = new StateGraph(State);

const model = new ChatOpenAI();

async function chatbot(state) {
  const answer = await model.invoke(state.messages);
  return { messages: answer };
}

builder = builder.addNode('chatbot', chatbot);

builder = builder.addEdge(START, 'chatbot').addEdge('chatbot', END);

let graph = builder.compile();

// Run the graph
const input = { messages: [new HumanMessage('hi!')] };
for await (const chunk of await graph.stream(input)) {
  console.log(chunk);
}


================================================
FILE: ch4/js/c-persistent-memory.js
================================================
import {
  StateGraph,
  Annotation,
  messagesStateReducer,
  START,
} from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
import { MemorySaver } from "@langchain/langgraph";
import { HumanMessage } from "@langchain/core/messages";

const State = {
  messages: Annotation({
    reducer: messagesStateReducer,
    default: () => [],
  }),
};

let builder = new StateGraph(State);

const model = new ChatOpenAI();

async function chatbot(state) {
  const answer = await model.invoke(state.messages);
  return { messages: answer };
}

builder = builder.addNode("chatbot", chatbot);

builder = builder.addEdge(START, "chatbot").addEdge("chatbot", END);

// Add persistence
const graph = builder.compile({ checkpointer: new MemorySaver() });

// Configure thread
const thread1 = { configurable: { thread_id: "1" } };

// Run with persistence
const result_1 = await graph.invoke(
  {
    messages: [new HumanMessage("hi, my name is Jack!")],
  },
  thread1,
);
console.log(result_1);

const result_2 = await graph.invoke(
  {
    messages: [new HumanMessage("what is my name?")],
  },
  thread1,
);
console.log(result_2);

// Get state
await graph.getState(thread1);


================================================
FILE: ch4/js/d-trim-messages.js
================================================
import {
  AIMessage,
  HumanMessage,
  SystemMessage,
  trimMessages,
} from "@langchain/core/messages";
import { ChatOpenAI } from "@langchain/openai";

const messages = [
  new SystemMessage("you're a good assistant"),
  new HumanMessage("hi! I'm bob"),
  new AIMessage("hi!"),
  new HumanMessage("I like vanilla ice cream"),
  new AIMessage("nice"),
  new HumanMessage("whats 2 + 2"),
  new AIMessage("4"),
  new HumanMessage("thanks"),
  new AIMessage("no problem!"),
  new HumanMessage("having fun?"),
  new AIMessage("yes!"),
];

const trimmer = trimMessages({
  maxTokens: 65,
  strategy: "last",
  tokenCounter: new ChatOpenAI({ modelName: "gpt-4o" }),
  includeSystem: true,
  allowPartial: false,
  startOn: "human",
});

const trimmed = await trimmer.invoke(messages);
console.log(trimmed);


================================================
FILE: ch4/js/e-filter-messages.js
================================================
import {
  HumanMessage,
  SystemMessage,
  AIMessage,
  filterMessages,
} from '@langchain/core/messages';

const messages = [
  new SystemMessage({ content: 'you are a good assistant', id: '1' }),
  new HumanMessage({ content: 'example input', id: '2', name: 'example_user' }),
  new AIMessage({
    content: 'example output',
    id: '3',
    name: 'example_assistant',
  }),
  new HumanMessage({ content: 'real input', id: '4', name: 'bob' }),
  new AIMessage({ content: 'real output', id: '5', name: 'alice' }),
];

// Filter for human messages
const filterByHumanMessages = filterMessages(messages, {
  includeTypes: ['human'],
});
console.log(`Human messages: ${JSON.stringify(filterByHumanMessages)}`);

// Filter to exclude names
const filterByExcludedNames = filterMessages(messages, {
  excludeNames: ['example_user', 'example_assistant'],
});
console.log(
  `\nExcluding example names: ${JSON.stringify(filterByExcludedNames)}`
);

// Filter by types and IDs
const filterByTypesAndIDs = filterMessages(messages, {
  includeTypes: ['human', 'ai'],
  excludeIds: ['3'],
});
console.log(
  `\nFiltered by types and IDs: ${JSON.stringify(filterByTypesAndIDs)}`
);


================================================
FILE: ch4/js/f-merge-messages.js
================================================
import {
  HumanMessage,
  SystemMessage,
  AIMessage,
  mergeMessageRuns,
} from '@langchain/core/messages';

const messages = [
  new SystemMessage("you're a good assistant."),
  new SystemMessage('you always respond with a joke.'),
  new HumanMessage({
    content: [{ type: 'text', text: "i wonder why it's called langchain" }],
  }),
  new HumanMessage('and who is harrison chasing anyways'),
  new AIMessage(
    'Well, I guess they thought "WordRope" and "SentenceString" just didn\'t have the same ring to it!'
  ),
  new AIMessage(
    "Why, he's probably chasing after the last cup of coffee in the office!"
  ),
];

// Merge consecutive messages
const mergedMessages = mergeMessageRuns(messages);
console.log(mergedMessages);


================================================
FILE: ch4/py/a-simple-memory.py
================================================
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant. Answer all questions to the best of         your ability."),
    ("placeholder", "{messages}"),
])

model = ChatOpenAI()

chain = prompt | model

response = chain.invoke({
    "messages": [
        ("human", "Translate this sentence from English to French: I love programming."),
        ("ai", "J'adore programmer."),
        ("human", "What did you just say?"),
    ],
})

print(response.content)


================================================
FILE: ch4/py/b-state-graph.py
================================================
from typing import Annotated, TypedDict

from langchain_core.messages import HumanMessage
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START, END, add_messages
from langgraph.checkpoint.memory import MemorySaver


class State(TypedDict):
    messages: Annotated[list, add_messages]


builder = StateGraph(State)

model = ChatOpenAI()


def chatbot(state: State):
    answer = model.invoke(state["messages"])
    return {"messages": [answer]}


# Add the chatbot node
builder.add_node("chatbot", chatbot)

# Add edges
builder.add_edge(START, "chatbot")
builder.add_edge("chatbot", END)

graph = builder.compile()

# Run the graph
input = {"messages": [HumanMessage("hi!")]}
for chunk in graph.stream(input):
    print(chunk)


================================================
FILE: ch4/py/c-persistent-memory.py
================================================
from typing import Annotated, TypedDict

from langchain_core.messages import HumanMessage
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START, END, add_messages
from langgraph.checkpoint.memory import MemorySaver


class State(TypedDict):
    messages: Annotated[list, add_messages]


builder = StateGraph(State)

model = ChatOpenAI()


def chatbot(state: State):
    answer = model.invoke(state["messages"])
    return {"messages": [answer]}


builder.add_node("chatbot", chatbot)
builder.add_edge(START, "chatbot")
builder.add_edge("chatbot", END)

# Add persistence with MemorySaver
graph = builder.compile(checkpointer=MemorySaver())

# Configure thread
thread1 = {"configurable": {"thread_id": "1"}}

# Run with persistence
result_1 = graph.invoke({"messages": [HumanMessage("hi, my name is Jack!")]}, thread1)
print(result_1)

result_2 = graph.invoke({"messages": [HumanMessage("what is my name?")]}, thread1)
print(result_2)

# Get state
print(graph.get_state(thread1))


================================================
FILE: ch4/py/d-trim-messages.py
================================================
from langchain_core.messages import (
    SystemMessage,
    HumanMessage,
    AIMessage,
    trim_messages,
)
from langchain_openai import ChatOpenAI

# Define sample messages
messages = [
    SystemMessage(content="you're a good assistant"),
    HumanMessage(content="hi! I'm bob"),
    AIMessage(content="hi!"),
    HumanMessage(content="I like vanilla ice cream"),
    AIMessage(content="nice"),
    HumanMessage(content="whats 2 + 2"),
    AIMessage(content="4"),
    HumanMessage(content="thanks"),
    AIMessage(content="no problem!"),
    HumanMessage(content="having fun?"),
    AIMessage(content="yes!"),
]

# Create trimmer
trimmer = trim_messages(
    max_tokens=65,
    strategy="last",
    token_counter=ChatOpenAI(model="gpt-4o"),
    include_system=True,
    allow_partial=False,
    start_on="human",
)

# Apply trimming
trimmed = trimmer.invoke(messages)
print(trimmed)


================================================
FILE: ch4/py/e-filter-messages.py
================================================
from langchain_core.messages import (
    AIMessage,
    HumanMessage,
    SystemMessage,
    filter_messages,
)

# Sample messages
messages = [
    SystemMessage(content="you are a good assistant", id="1"),
    HumanMessage(content="example input", id="2", name="example_user"),
    AIMessage(content="example output", id="3", name="example_assistant"),
    HumanMessage(content="real input", id="4", name="bob"),
    AIMessage(content="real output", id="5", name="alice"),
]

# Filter for human messages
human_messages = filter_messages(messages, include_types="human")
print("Human messages:", human_messages)

# Filter to exclude certain names
excluded_names = filter_messages(
    messages, exclude_names=["example_user", "example_assistant"]
)
print("\nExcluding example names:", excluded_names)

# Filter by types and IDs
filtered_messages = filter_messages(
    messages, include_types=["human", "ai"], exclude_ids=["3"]
)
print("\nFiltered by types and IDs:", filtered_messages)


================================================
FILE: ch4/py/f-merge-messages.py
================================================
from langchain_core.messages import (
    AIMessage,
    HumanMessage,
    SystemMessage,
    merge_message_runs,
)

# Sample messages with consecutive messages of same type
messages = [
    SystemMessage(content="you're a good assistant."),
    SystemMessage(content="you always respond with a joke."),
    HumanMessage(
        content=[{"type": "text", "text": "i wonder why it's called langchain"}]
    ),
    HumanMessage(content="and who is harrison chasing anyways"),
    AIMessage(
        content='Well, I guess they thought "WordRope" and "SentenceString" just didn\'t have the same ring to it!'
    ),
    AIMessage(
        content="Why, he's probably chasing after the last cup of coffee in the office!"
    ),
]

# Merge consecutive messages
merged = merge_message_runs(messages)
print(merged)


================================================
FILE: ch5/js/a-chatbot.js
================================================
import {
  StateGraph,
  Annotation,
  messagesStateReducer,
  START,
  END,
} from '@langchain/langgraph';

import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage } from '@langchain/core/messages';

const model = new ChatOpenAI();

const State = {
  // Messages have the type "list". The `add_messages`
  // function in the annotation defines how this state should
  // be updated (in this case, it appends new messages to the
  // list, rather than replacing the previous messages)
  messages: Annotation({
    reducer: messagesStateReducer,
    default: () => [],
  }),
};

async function chatbot(state) {
  const answer = await model.invoke(state.messages);
  return { messages: answer };
}

const builder = new StateGraph(State)
  .addNode('chatbot', chatbot)
  .addEdge(START, 'chatbot')
  .addEdge('chatbot', END);

const graph = builder.compile();

// Example usage
const input = { messages: [new HumanMessage('hi!')] };
for await (const chunk of await graph.stream(input)) {
  console.log(chunk);
}


================================================
FILE: ch5/js/b-sql-generator.js
================================================
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
import { ChatOpenAI } from "@langchain/openai";
import {
  StateGraph,
  Annotation,
  messagesStateReducer,
  START,
  END,
} from "@langchain/langgraph";

// useful to generate SQL query
const modelLowTemp = new ChatOpenAI({ temperature: 0.1 });
// useful to generate natural language outputs
const modelHighTemp = new ChatOpenAI({ temperature: 0.7 });

const annotation = Annotation.Root({
  messages: Annotation({ reducer: messagesStateReducer, default: () => [] }),
  user_query: Annotation(),
  sql_query: Annotation(),
  sql_explanation: Annotation(),
});

const generatePrompt = new SystemMessage(
  "You are a helpful data analyst, who generates SQL queries for users based on their questions.",
);

async function generateSql(state) {
  const userMessage = new HumanMessage(state.user_query);
  const messages = [generatePrompt, ...state.messages, userMessage];
  const res = await modelLowTemp.invoke(messages);
  return {
    sql_query: res.content,
    // update conversation history
    messages: [userMessage, res],
  };
}

const explainPrompt = new SystemMessage(
  "You are a helpful data analyst, who explains SQL queries to users.",
);

async function explainSql(state) {
  const messages = [explainPrompt, ...state.messages];
  const res = await modelHighTemp.invoke(messages);
  return {
    sql_explanation: res.content,
    // update conversation history
    messages: res,
  };
}

const builder = new StateGraph(annotation)
  .addNode("generate_sql", generateSql)
  .addNode("explain_sql", explainSql)
  .addEdge(START, "generate_sql")
  .addEdge("generate_sql", "explain_sql")
  .addEdge("explain_sql", END);

const graph = builder.compile();

// Example usage
const result = await graph.invoke({
  user_query: "What is the total sales for each product?",
});
console.log(result);


================================================
FILE: ch5/js/c-multi-rag.js
================================================
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai";
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import {
  StateGraph,
  Annotation,
  messagesStateReducer,
  START,
  END,
} from "@langchain/langgraph";

const embeddings = new OpenAIEmbeddings();
// useful to generate SQL query
const modelLowTemp = new ChatOpenAI({ temperature: 0.1 });
// useful to generate natural language outputs
const modelHighTemp = new ChatOpenAI({ temperature: 0.7 });

const annotation = Annotation.Root({
  messages: Annotation({ reducer: messagesStateReducer, default: () => [] }),
  user_query: Annotation(),
  domain: Annotation(),
  documents: Annotation(),
  answer: Annotation(),
});

// Sample documents for testing
const sampleDocs = [
  { pageContent: "Patient medical record...", metadata: { domain: "records" } },
  {
    pageContent: "Insurance policy details...",
    metadata: { domain: "insurance" },
  },
];

// Initialize vector stores
const medicalRecordsStore = await MemoryVectorStore.fromDocuments(
  sampleDocs,
  embeddings,
);
const medicalRecordsRetriever = medicalRecordsStore.asRetriever();

const insuranceFaqsStore = await MemoryVectorStore.fromDocuments(
  sampleDocs,
  embeddings,
);
const insuranceFaqsRetriever = insuranceFaqsStore.asRetriever();

const routerPrompt = new SystemMessage(
  `You need to decide which domain to route the user query to. You have two domains to choose from:
- records: contains medical records of the patient, such as diagnosis, treatment, and prescriptions.
- insurance: contains frequently asked questions about insurance policies, claims, and coverage.

Output only the domain name.`,
);

async function routerNode(state) {
  const userMessage = new HumanMessage(state.user_query);
  const messages = [routerPrompt, ...state.messages, userMessage];
  const res = await modelLowTemp.invoke(messages);
  return {
    domain: res.content,
    // update conversation history
    messages: [userMessage, res],
  };
}

function pickRetriever(state) {
  if (state.domain === "records") {
    return "retrieve_medical_records";
  } else {
    return "retrieve_insurance_faqs";
  }
}

async function retrieveMedicalRecords(state) {
  const documents = await medicalRecordsRetriever.invoke(state.user_query);
  return {
    documents,
  };
}

async function retrieveInsuranceFaqs(state) {
  const documents = await insuranceFaqsRetriever.invoke(state.user_query);
  return {
    documents,
  };
}

const medicalRecordsPrompt = new SystemMessage(
  "You are a helpful medical chatbot, who answers questions based on the patient's medical records, such as diagnosis, treatment, and prescriptions.",
);

const insuranceFaqsPrompt = new SystemMessage(
  "You are a helpful medical insurance chatbot, who answers frequently asked questions about insurance policies, claims, and coverage.",
);

async function generateAnswer(state) {
  const prompt =
    state.domain === "records" ? medicalRecordsPrompt : insuranceFaqsPrompt;
  const messages = [
    prompt,
    ...state.messages,
    new HumanMessage(`Documents: ${state.documents}`),
  ];
  const res = await modelHighTemp.invoke(messages);
  return {
    answer: res.content,
    // update conversation history
    messages: res,
  };
}

const builder = new StateGraph(annotation)
  .addNode("router", routerNode)
  .addNode("retrieve_medical_records", retrieveMedicalRecords)
  .addNode("retrieve_insurance_faqs", retrieveInsuranceFaqs)
  .addNode("generate_answer", generateAnswer)
  .addEdge(START, "router")
  .addConditionalEdges("router", pickRetriever)
  .addEdge("retrieve_medical_records", "generate_answer")
  .addEdge("retrieve_insurance_faqs", "generate_answer")
  .addEdge("generate_answer", END);

const graph = builder.compile();

// Example usage
const input = {
  user_query: "Am I covered for COVID-19 treatment?",
};

for await (const chunk of await graph.stream(input)) {
  console.log(chunk);
}


================================================
FILE: ch5/py/a-chatbot.py
================================================
from typing import Annotated, TypedDict

from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage

model = ChatOpenAI()


class State(TypedDict):
    # Messages have the type "list". The `add_messages`
    # function in the annotation defines how this state should
    # be updated (in this case, it appends new messages to the
    # list, rather than replacing the previous messages)
    messages: Annotated[list, add_messages]


def chatbot(state: State):
    answer = model.invoke(state["messages"])
    return {"messages": [answer]}


builder = StateGraph(State)

builder.add_node("chatbot", chatbot)

builder.add_edge(START, "chatbot")
builder.add_edge("chatbot", END)

graph = builder.compile()

# Example usage

input = {"messages": [HumanMessage("hi!")]}
for chunk in graph.stream(input):
    print(chunk)


================================================
FILE: ch5/py/b-sql-generator.py
================================================
from typing import Annotated, TypedDict

from langchain_core.messages import HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
from langgraph.graph import END, START, StateGraph
from langgraph.graph.message import add_messages

# useful to generate SQL query
model_low_temp = ChatOpenAI(temperature=0.1)
# useful to generate natural language outputs
model_high_temp = ChatOpenAI(temperature=0.7)


class State(TypedDict):
    # to track conversation history
    messages: Annotated[list, add_messages]
    # input
    user_query: str
    # output
    sql_query: str
    sql_explanation: str


class Input(TypedDict):
    user_query: str


class Output(TypedDict):
    sql_query: str
    sql_explanation: str


generate_prompt = SystemMessage(
    "You are a helpful data analyst, who generates SQL queries for users based on their questions."
)


def generate_sql(state: State) -> State:
    user_message = HumanMessage(state["user_query"])
    messages = [generate_prompt, *state["messages"], user_message]
    res = model_low_temp.invoke(messages)
    return {
        "sql_query": res.content,
        # update conversation history
        "messages": [user_message, res],
    }


explain_prompt = SystemMessage(
    "You are a helpful data analyst, who explains SQL queries to users."
)


def explain_sql(state: State) -> State:
    messages = [
        explain_prompt,
        # contains user's query and SQL query from prev step
        *state["messages"],
    ]
    res = model_high_temp.invoke(messages)
    return {
        "sql_explanation": res.content,
        # update conversation history
        "messages": res,
    }


builder = StateGraph(State, input=Input, output=Output)
builder.add_node("generate_sql", generate_sql)
builder.add_node("explain_sql", explain_sql)
builder.add_edge(START, "generate_sql")
builder.add_edge("generate_sql", "explain_sql")
builder.add_edge("explain_sql", END)

graph = builder.compile()

# Example usage
result = graph.invoke({"user_query": "What is the total sales for each product?"})
print(result)


================================================
FILE: ch5/py/c-multi-rag.py
================================================
from typing import Annotated, Literal, TypedDict

from langchain_core.documents import Document
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.vectorstores.in_memory import InMemoryVectorStore
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langgraph.graph import END, START, StateGraph
from langgraph.graph.message import add_messages

embeddings = OpenAIEmbeddings()
# useful to generate SQL query
model_low_temp = ChatOpenAI(temperature=0.1)
# useful to generate natural language outputs
model_high_temp = ChatOpenAI(temperature=0.7)


class State(TypedDict):
    # to track conversation history
    messages: Annotated[list, add_messages]
    # input
    user_query: str
    # output
    domain: Literal["records", "insurance"]
    documents: list[Document]
    answer: str


class Input(TypedDict):
    user_query: str


class Output(TypedDict):
    documents: list[Document]
    answer: str


# Sample documents for testing
sample_docs = [
    Document(page_content="Patient medical record...", metadata={"domain": "records"}),
    Document(
        page_content="Insurance policy details...", metadata={"domain": "insurance"}
    ),
]

# Initialize vector stores
medical_records_store = InMemoryVectorStore.from_documents(sample_docs, embeddings)
medical_records_retriever = medical_records_store.as_retriever()

insurance_faqs_store = InMemoryVectorStore.from_documents(sample_docs, embeddings)
insurance_faqs_retriever = insurance_faqs_store.as_retriever()

router_prompt = SystemMessage(
    """You need to decide which domain to route the user query to. You have two domains to choose from:
- records: contains medical records of the patient, such as diagnosis, treatment, and prescriptions.
- insurance: contains frequently asked questions about insurance policies, claims, and coverage.

Output only the domain name."""
)


def router_node(state: State) -> State:
    user_message = HumanMessage(state["user_query"])
    messages = [router_prompt, *state["messages"], user_message]
    res = model_low_temp.invoke(messages)
    return {
        "domain": res.content,
        # update conversation history
        "messages": [user_message, res],
    }


def pick_retriever(
    state: State,
) -> Literal["retrieve_medical_records", "retrieve_insurance_faqs"]:
    if state["domain"] == "records":
        return "retrieve_medical_records"
    else:
        return "retrieve_insurance_faqs"


def retrieve_medical_records(state: State) -> State:
    documents = medical_records_retriever.invoke(state["user_query"])
    return {
        "documents": documents,
    }


def retrieve_insurance_faqs(state: State) -> State:
    documents = insurance_faqs_retriever.invoke(state["user_query"])
    return {
        "documents": documents,
    }


medical_records_prompt = SystemMessage(
    "You are a helpful medical chatbot, who answers questions based on the patient's medical records, such as diagnosis, treatment, and prescriptions."
)

insurance_faqs_prompt = SystemMessage(
    "You are a helpful medical insurance chatbot, who answers frequently asked questions about insurance policies, claims, and coverage."
)


def generate_answer(state: State) -> State:
    if state["domain"] == "records":
        prompt = medical_records_prompt
    else:
        prompt = insurance_faqs_prompt
    messages = [
        prompt,
        *state["messages"],
        HumanMessage(f"Documents: {state['documents']}"),
    ]
    res = model_high_temp.invoke(messages)
    return {
        "answer": res.content,
        # update conversation history
        "messages": res,
    }


builder = StateGraph(State, input=Input, output=Output)
builder.add_node("router", router_node)
builder.add_node("retrieve_medical_records", retrieve_medical_records)
builder.add_node("retrieve_insurance_faqs", retrieve_insurance_faqs)
builder.add_node("generate_answer", generate_answer)
builder.add_edge(START, "router")
builder.add_conditional_edges("router", pick_retriever)
builder.add_edge("retrieve_medical_records", "generate_answer")
builder.add_edge("retrieve_insurance_faqs", "generate_answer")
builder.add_edge("generate_answer", END)

graph = builder.compile()

# Example usage
input = {"user_query": "Am I covered for COVID-19 treatment?"}
for chunk in graph.stream(input):
    print(chunk)


================================================
FILE: ch6/js/a-basic-agent.js
================================================
import { DuckDuckGoSearch } from "@langchain/community/tools/duckduckgo_search";
import { Calculator } from "@langchain/community/tools/calculator";
import {
  StateGraph,
  Annotation,
  messagesStateReducer,
  START,
} from "@langchain/langgraph";
import { ToolNode, toolsCondition } from "@langchain/langgraph/prebuilt";
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";

const search = new DuckDuckGoSearch();
const calculator = new Calculator();
const tools = [search, calculator];
const model = new ChatOpenAI({
  temperature: 0.1,
}).bindTools(tools);

const annotation = Annotation.Root({
  messages: Annotation({
    reducer: messagesStateReducer,
    default: () => [],
  }),
});

async function modelNode(state) {
  const res = await model.invoke(state.messages);
  return { messages: res };
}

const builder = new StateGraph(annotation)
  .addNode("model", modelNode)
  .addNode("tools", new ToolNode(tools))
  .addEdge(START, "model")
  .addConditionalEdges("model", toolsCondition)
  .addEdge("tools", "model");

const graph = builder.compile();

// Example usage
const input = {
  messages: [
    new HumanMessage(
      "How old was the 30th president of the United States when he died?",
    ),
  ],
};

for await (const c of await graph.stream(input)) {
  console.log(c);
}


================================================
FILE: ch6/js/b-force-first-tool.js
================================================
import { DuckDuckGoSearch } from "@langchain/community/tools/duckduckgo_search";
import { Calculator } from "@langchain/community/tools/calculator";
import { AIMessa
Download .txt
gitextract__8u75gon/

├── .gitignore
├── README.md
├── ch1/
│   ├── js/
│   │   ├── a-llm.js
│   │   ├── b-chat.js
│   │   ├── c-system.js
│   │   ├── d-prompt.js
│   │   ├── e-prompt-model.js
│   │   ├── f-chat-prompt.js
│   │   ├── g-chat-prompt-model.js
│   │   ├── h-structured.js
│   │   ├── i-csv.js
│   │   ├── j-methods.js
│   │   ├── k-imperative.js
│   │   ├── ka-stream.js
│   │   └── l-declarative.js
│   └── py/
│       ├── a-llm.py
│       ├── b-chat.py
│       ├── c-system.py
│       ├── d-prompt.py
│       ├── e-prompt-model.py
│       ├── f-chat-prompt.py
│       ├── g-chat-prompt-model.py
│       ├── h-structured.py
│       ├── i-csv.py
│       ├── j-methods.py
│       ├── k-imperative.py
│       ├── ka-stream.py
│       ├── kb-async.py
│       └── l-declarative.py
├── ch10/
│   ├── js/
│   │   ├── agent-evaluation-rag.js
│   │   ├── agent-evaluation-sql.js
│   │   ├── agent-sql-graph.js
│   │   ├── create-rag-dataset.js
│   │   ├── create-sql-dataset.js
│   │   ├── rag-graph.js
│   │   ├── retrieve-and-grade.js
│   │   └── search-graph.js
│   └── py/
│       ├── agent_evaluation_rag.py
│       ├── agent_evaluation_sql.py
│       ├── agent_sql_graph.py
│       ├── create_rag_dataset.py
│       ├── create_sql_dataset.py
│       ├── rag_graph.py
│       ├── retrieve_and_grade.py
│       └── search_graph.py
├── ch2/
│   ├── js/
│   │   ├── a-text-loader.js
│   │   ├── b-web-loader.js
│   │   ├── c-pdf-loader.js
│   │   ├── d-rec-text-splitter.js
│   │   ├── e-rec-text-splitter-code.js
│   │   ├── f-markdown-splitter.js
│   │   ├── g-embeddings.js
│   │   ├── h-load-split-embed.js
│   │   ├── i-pg-vector.js
│   │   ├── j-record-manager.js
│   │   └── k-multi-vector-retriever.js
│   └── py/
│       ├── a-text-loader.py
│       ├── b-web-loader.py
│       ├── c-pdf-loader.py
│       ├── d-rec-text-splitter.py
│       ├── e-rec-text-splitter-code.py
│       ├── f-markdown-splitter.py
│       ├── g-embeddings.py
│       ├── h-load-split-embed.py
│       ├── i-pg-vector.py
│       ├── j-record-manager.py
│       ├── k-multi-vector-retriever.py
│       └── l-rag-colbert.py
├── ch3/
│   ├── js/
│   │   ├── a-basic-rag.js
│   │   ├── b-rewrite.js
│   │   ├── c-multi-query.js
│   │   ├── d-rag-fusion.js
│   │   ├── e-hyde.js
│   │   ├── f-router.js
│   │   ├── g-semantic-router.js
│   │   ├── h-self-query.js
│   │   └── i-sql-example.js
│   └── py/
│       ├── a-basic-rag.py
│       ├── b-rewrite.py
│       ├── c-multi-query.py
│       ├── d-rag-fusion.py
│       ├── e-hyde.py
│       ├── f-router.py
│       ├── g-semantic-router.py
│       ├── h-self-query.py
│       └── i-sql-example.py
├── ch4/
│   ├── js/
│   │   ├── a-simple-memory.js
│   │   ├── b-state-graph.js
│   │   ├── c-persistent-memory.js
│   │   ├── d-trim-messages.js
│   │   ├── e-filter-messages.js
│   │   └── f-merge-messages.js
│   └── py/
│       ├── a-simple-memory.py
│       ├── b-state-graph.py
│       ├── c-persistent-memory.py
│       ├── d-trim-messages.py
│       ├── e-filter-messages.py
│       └── f-merge-messages.py
├── ch5/
│   ├── js/
│   │   ├── a-chatbot.js
│   │   ├── b-sql-generator.js
│   │   └── c-multi-rag.js
│   └── py/
│       ├── a-chatbot.py
│       ├── b-sql-generator.py
│       └── c-multi-rag.py
├── ch6/
│   ├── js/
│   │   ├── a-basic-agent.js
│   │   ├── b-force-first-tool.js
│   │   └── c-many-tools.js
│   └── py/
│       ├── a-basic-agent.py
│       ├── b-force-first-tool.py
│       └── c-many-tools.py
├── ch7/
│   ├── js/
│   │   ├── a-reflection.js
│   │   ├── b-subgraph-direct.js
│   │   ├── c-subgraph-function.js
│   │   └── d-supervisor.js
│   └── py/
│       ├── a-reflection.py
│       ├── b-subgraph-direct.py
│       ├── c-subgraph-function.py
│       └── d-supervisor.py
├── ch8/
│   ├── js/
│   │   ├── a-structured-output.js
│   │   ├── b-streaming-output.js
│   │   ├── c-interrupt.js
│   │   ├── d-authorize.js
│   │   ├── e-resume.js
│   │   ├── f-edit-state.js
│   │   └── g-fork.js
│   └── py/
│       ├── a-structured-output.py
│       ├── b-streaming-output.py
│       ├── c-interrupt.py
│       ├── d-authorize.py
│       ├── e-resume.py
│       ├── f-edit-state.py
│       └── g-fork.py
├── ch9/
│   ├── README.md
│   ├── js/
│   │   ├── .gitignore
│   │   ├── demo.ts
│   │   ├── langgraph.json
│   │   ├── package.json
│   │   ├── src/
│   │   │   ├── ingestion_graph/
│   │   │   │   ├── configuration.ts
│   │   │   │   ├── graph.ts
│   │   │   │   └── state.ts
│   │   │   ├── retrieval_graph/
│   │   │   │   ├── configuration.ts
│   │   │   │   ├── graph.ts
│   │   │   │   ├── state.ts
│   │   │   │   └── utils.ts
│   │   │   └── shared/
│   │   │       ├── configuration.ts
│   │   │       ├── retrieval.ts
│   │   │       ├── state.ts
│   │   │       └── utils.ts
│   │   └── tsconfig.json
│   └── py/
│       ├── demo.py
│       ├── langgraph.json
│       ├── pyproject.toml
│       └── src/
│           ├── docSplits.json
│           ├── ingestion_graph/
│           │   ├── __init__.py
│           │   ├── configuration.py
│           │   ├── graph.py
│           │   └── state.py
│           ├── retrieval_graph/
│           │   ├── __init__.py
│           │   ├── configuration.py
│           │   ├── graph.py
│           │   ├── state.py
│           │   └── utils.py
│           └── shared/
│               ├── __init__.py
│               ├── configuration.py
│               ├── retrieval.py
│               └── state.py
├── package.json
├── pyproject.toml
└── test.txt
Download .txt
SYMBOL INDEX (175 symbols across 72 files)

FILE: ch1/py/h-structured.py
  class AnswerWithJustification (line 5) | class AnswerWithJustification(BaseModel):

FILE: ch1/py/k-imperative.py
  function chatbot (line 21) | def chatbot(values):

FILE: ch1/py/ka-stream.py
  function chatbot (line 18) | def chatbot(values):

FILE: ch1/py/kb-async.py
  function chatbot (line 16) | async def chatbot(values):
  function main (line 21) | async def main():

FILE: ch10/js/agent-evaluation-rag.js
  constant EVALUATION_PROMPT (line 14) | const EVALUATION_PROMPT = `You are a teacher grading a quiz.

FILE: ch10/js/agent-evaluation-sql.js
  constant EXPECTED_TOOLS (line 102) | const EXPECTED_TOOLS = {

FILE: ch10/py/agent_evaluation_rag.py
  class Grade (line 34) | class Grade(TypedDict):
  function transform_dataset_inputs (line 46) | def transform_dataset_inputs(inputs: dict) -> dict:
  function transform_agent_outputs (line 53) | def transform_agent_outputs(outputs: dict) -> dict:
  function evaluate_agent (line 61) | async def evaluate_agent(inputs: dict, outputs: dict, reference_outputs:...
  function run_graph (line 76) | async def run_graph(inputs: dict) -> dict:
  function run_eval (line 86) | async def run_eval(
  function main (line 100) | async def main():

FILE: ch10/py/agent_evaluation_sql.py
  function predict_sql_agent_answer (line 21) | def predict_sql_agent_answer(example: dict):
  function answer_evaluator (line 33) | def answer_evaluator(run, example) -> dict:
  function predict_assistant (line 75) | def predict_assistant(example: dict):
  function check_specific_tool_call (line 82) | def check_specific_tool_call(root_run: Run, example: Example) -> dict:
  function predict_sql_agent_messages (line 119) | def predict_sql_agent_messages(example: dict):
  function find_tool_calls (line 127) | def find_tool_calls(messages):
  function contains_all_tool_calls_any_order (line 136) | def contains_all_tool_calls_any_order(root_run: Run, example: Example) -...
  function contains_all_tool_calls_in_order (line 154) | def contains_all_tool_calls_in_order(root_run: Run, example: Example) ->...
  function contains_all_tool_calls_in_order_exact_match (line 173) | def contains_all_tool_calls_in_order_exact_match(root_run: Run, example:...

FILE: ch10/py/agent_sql_graph.py
  function check_query_tool (line 50) | def check_query_tool(query: str) -> str:
  function check_result (line 67) | def check_result(query_result: str) -> str:
  class State (line 78) | class State(TypedDict):
  function create_tool_node_with_fallback (line 82) | def create_tool_node_with_fallback(tools: list) -> dict:
  function _print_event (line 88) | def _print_event(event: dict, _printed: set, max_length=1500):
  function handle_tool_error (line 104) | def handle_tool_error(state) -> dict:
  class Assistant (line 119) | class Assistant:
    method __init__ (line 121) | def __init__(self, runnable: Runnable):
    method __call__ (line 124) | def __call__(self, state: State, config: RunnableConfig):

FILE: ch10/py/rag_graph.py
  class GraphState (line 12) | class GraphState(TypedDict):
  function scrape_blog_posts (line 28) | def scrape_blog_posts(state) -> List[Document]:
  function indexing (line 45) | def indexing(state):
  function retrieve_and_generate (line 62) | def retrieve_and_generate(state):

FILE: ch10/py/retrieve_and_grade.py
  class GradeDocuments (line 43) | class GradeDocuments(BaseModel):

FILE: ch10/py/search_graph.py
  class GraphState (line 42) | class GraphState(TypedDict):
  function retrieve (line 59) | def retrieve(state):
  function generate (line 76) | def generate(state):
  function grade_documents (line 94) | def grade_documents(state):
  function transform_query (line 126) | def transform_query(state):
  function web_search (line 145) | def web_search(state):
  function decide_to_generate (line 172) | def decide_to_generate(state):

FILE: ch2/js/e-rec-text-splitter-code.js
  constant PYTHON_CODE (line 3) | const PYTHON_CODE = ` def hello_world(): print("Hello, World!") # Call t...

FILE: ch2/py/l-rag-colbert.py
  function get_wikipedia_page (line 18) | def get_wikipedia_page(title: str):

FILE: ch3/js/d-rag-fusion.js
  function reciprocalRankFusion (line 57) | function reciprocalRankFusion(results, k = 60) {

FILE: ch3/py/a-basic-rag.py
  function qa (line 72) | def qa(input):

FILE: ch3/py/b-rewrite.py
  function qa (line 65) | def qa(input):
  function parse_rewriter_output (line 85) | def parse_rewriter_output(message):
  function qa_rrr (line 93) | def qa_rrr(input):

FILE: ch3/py/c-multi-query.py
  function parse_queries_output (line 38) | def parse_queries_output(message):
  function get_unique_union (line 45) | def get_unique_union(document_lists):
  function multi_query_qa (line 63) | def multi_query_qa(input):

FILE: ch3/py/d-rag-fusion.py
  function parse_queries_output (line 31) | def parse_queries_output(message):
  function reciprocal_rank_fusion (line 50) | def reciprocal_rank_fusion(results: list[list], k=60):
  function rag_fusion (line 88) | def rag_fusion(input):

FILE: ch3/py/e-hyde.py
  function qa (line 49) | def qa(input):

FILE: ch3/py/f-router.py
  class RouteQuery (line 10) | class RouteQuery(BaseModel):
  function choose_route (line 52) | def choose_route(result):

FILE: ch3/py/g-semantic-router.py
  function prompt_router (line 19) | def prompt_router(query):

FILE: ch4/js/b-state-graph.js
  function chatbot (line 22) | async function chatbot(state) {

FILE: ch4/js/c-persistent-memory.js
  function chatbot (line 22) | async function chatbot(state) {

FILE: ch4/py/b-state-graph.py
  class State (line 9) | class State(TypedDict):
  function chatbot (line 18) | def chatbot(state: State):

FILE: ch4/py/c-persistent-memory.py
  class State (line 9) | class State(TypedDict):
  function chatbot (line 18) | def chatbot(state: State):

FILE: ch5/js/a-chatbot.js
  function chatbot (line 25) | async function chatbot(state) {

FILE: ch5/js/b-sql-generator.js
  function generateSql (line 27) | async function generateSql(state) {
  function explainSql (line 42) | async function explainSql(state) {

FILE: ch5/js/c-multi-rag.js
  function routerNode (line 56) | async function routerNode(state) {
  function pickRetriever (line 67) | function pickRetriever(state) {
  function retrieveMedicalRecords (line 75) | async function retrieveMedicalRecords(state) {
  function retrieveInsuranceFaqs (line 82) | async function retrieveInsuranceFaqs(state) {
  function generateAnswer (line 97) | async function generateAnswer(state) {

FILE: ch5/py/a-chatbot.py
  class State (line 11) | class State(TypedDict):
  function chatbot (line 19) | def chatbot(state: State):

FILE: ch5/py/b-sql-generator.py
  class State (line 14) | class State(TypedDict):
  class Input (line 24) | class Input(TypedDict):
  class Output (line 28) | class Output(TypedDict):
  function generate_sql (line 38) | def generate_sql(state: State) -> State:
  function explain_sql (line 54) | def explain_sql(state: State) -> State:

FILE: ch5/py/c-multi-rag.py
  class State (line 17) | class State(TypedDict):
  class Input (line 28) | class Input(TypedDict):
  class Output (line 32) | class Output(TypedDict):
  function router_node (line 61) | def router_node(state: State) -> State:
  function pick_retriever (line 72) | def pick_retriever(
  function retrieve_medical_records (line 81) | def retrieve_medical_records(state: State) -> State:
  function retrieve_insurance_faqs (line 88) | def retrieve_insurance_faqs(state: State) -> State:
  function generate_answer (line 104) | def generate_answer(state: State) -> State:

FILE: ch6/js/a-basic-agent.js
  function modelNode (line 27) | async function modelNode(state) {

FILE: ch6/js/b-force-first-tool.js
  function firstModelNode (line 22) | async function firstModelNode(state) {
  function modelNode (line 34) | async function modelNode(state) {

FILE: ch6/js/c-many-tools.js
  function modelNode (line 41) | async function modelNode(state) {
  function selectTools (line 49) | async function selectTools(state) {

FILE: ch6/py/a-basic-agent.py
  function calculator (line 14) | def calculator(query: str) -> str:
  class State (line 24) | class State(TypedDict):
  function model_node (line 28) | def model_node(state: State) -> State:

FILE: ch6/py/b-force-first-tool.py
  function calculator (line 16) | def calculator(query: str) -> str:
  class State (line 26) | class State(TypedDict):
  function model_node (line 30) | def model_node(state: State) -> State:
  function first_model (line 35) | def first_model(state: State) -> State:

FILE: ch6/py/c-many-tools.py
  function calculator (line 17) | def calculator(query: str) -> str:
  class State (line 34) | class State(TypedDict):
  function model_node (line 39) | def model_node(state: State) -> State:
  function select_tools (line 45) | def select_tools(state: State) -> State:

FILE: ch7/js/a-reflection.js
  function generate (line 27) | async function generate(state) {
  function reflect (line 37) | async function reflect(state) {
  function shouldContinue (line 56) | function shouldContinue(state) {

FILE: ch7/py/a-reflection.py
  class State (line 18) | class State(TypedDict):
  function generate (line 35) | def generate(state: State) -> State:
  function reflect (line 40) | def reflect(state: State) -> State:
  function should_continue (line 52) | def should_continue(state: State):

FILE: ch7/py/b-subgraph-direct.py
  class State (line 7) | class State(TypedDict):
  class SubgraphState (line 11) | class SubgraphState(TypedDict):
  function subgraph_node (line 17) | def subgraph_node(state: SubgraphState):

FILE: ch7/py/c-subgraph-function.py
  class State (line 5) | class State(TypedDict):
  class SubgraphState (line 9) | class SubgraphState(TypedDict):
  function subgraph_node (line 16) | def subgraph_node(state: SubgraphState):
  function node (line 28) | def node(state: State):

FILE: ch7/py/d-supervisor.py
  class SupervisorDecision (line 8) | class SupervisorDecision(BaseModel):
  function supervisor (line 29) | def supervisor(state):
  class AgentState (line 39) | class AgentState(MessagesState):
  function researcher (line 44) | def researcher(state: AgentState):
  function coder (line 58) | def coder(state: AgentState):

FILE: ch8/py/a-structured-output.py
  class Joke (line 5) | class Joke(BaseModel):

FILE: ch8/py/b-streaming-output.py
  function create_simple_graph (line 5) | def create_simple_graph():

FILE: ch8/py/c-interrupt.py
  function main (line 9) | async def main():

FILE: ch8/py/d-authorize.py
  function main (line 6) | async def main():

FILE: ch8/py/e-resume.py
  function main (line 5) | async def main():

FILE: ch8/py/f-edit-state.py
  function main (line 5) | def main():

FILE: ch8/py/g-fork.py
  function main (line 5) | def main():

FILE: ch9/js/demo.ts
  function runDemo (line 16) | async function runDemo() {

FILE: ch9/js/src/ingestion_graph/configuration.ts
  constant DEFAULT_DOCS_PATH (line 5) | const DEFAULT_DOCS_PATH = 'src/sample_docs.json';
  function ensureIndexConfiguration (line 39) | function ensureIndexConfiguration(

FILE: ch9/js/src/ingestion_graph/graph.ts
  function ingestDocs (line 17) | async function ingestDocs(

FILE: ch9/js/src/ingestion_graph/state.ts
  type IndexStateType (line 25) | type IndexStateType = typeof IndexStateAnnotation.State;

FILE: ch9/js/src/retrieval_graph/configuration.ts
  function ensureAgentConfiguration (line 25) | function ensureAgentConfiguration(

FILE: ch9/js/src/retrieval_graph/graph.ts
  function checkQueryType (line 21) | async function checkQueryType(
  function answerQueryDirectly (line 58) | async function answerQueryDirectly(
  function routeQuery (line 70) | async function routeQuery(
  function retrieveDocuments (line 86) | async function retrieveDocuments(
  function generateResponse (line 95) | async function generateResponse(

FILE: ch9/js/src/retrieval_graph/utils.ts
  function formatDoc (line 3) | function formatDoc(doc: Document): string {
  function formatDocs (line 13) | function formatDocs(docs?: Document[]): string {

FILE: ch9/js/src/shared/configuration.ts
  function ensureBaseConfiguration (line 47) | function ensureBaseConfiguration(

FILE: ch9/js/src/shared/retrieval.ts
  function makeSupabaseRetriever (line 10) | async function makeSupabaseRetriever(
  function makeChromaRetriever (line 34) | async function makeChromaRetriever(
  function makeRetriever (line 47) | async function makeRetriever(

FILE: ch9/js/src/shared/state.ts
  function reduceDocs (line 11) | function reduceDocs(

FILE: ch9/js/src/shared/utils.ts
  constant SUPPORTED_PROVIDERS (line 4) | const SUPPORTED_PROVIDERS = [
  function loadChatModel (line 27) | async function loadChatModel(

FILE: ch9/py/demo.py
  function invoke_retrieval_assistant (line 5) | async def invoke_retrieval_assistant():

FILE: ch9/py/src/ingestion_graph/configuration.py
  class IndexConfiguration (line 14) | class IndexConfiguration:
    method from_runnable_config (line 56) | def from_runnable_config(

FILE: ch9/py/src/ingestion_graph/graph.py
  function ingest_docs (line 12) | async def ingest_docs(state: IndexState, config: Optional[RunnableConfig...

FILE: ch9/py/src/ingestion_graph/state.py
  class IndexState (line 13) | class IndexState:

FILE: ch9/py/src/retrieval_graph/configuration.py
  class Configuration (line 13) | class Configuration(BaseConfiguration):

FILE: ch9/py/src/retrieval_graph/graph.py
  class Schema (line 17) | class Schema(BaseModel):
  function check_query_type (line 22) | async def check_query_type(state: AgentState, *, config: RunnableConfig):
  function route_query (line 43) | async def route_query(state: AgentState, *, config: RunnableConfig):
  function retrieve_documents (line 54) | async def retrieve_documents(state: AgentState, *, config: RunnableConfig):
  function generate_response (line 61) | async def generate_response(state: AgentState, *, config: RunnableConfig):

FILE: ch9/py/src/retrieval_graph/state.py
  class AgentState (line 8) | class AgentState(MessagesState):

FILE: ch9/py/src/retrieval_graph/utils.py
  function _format_doc (line 7) | def _format_doc(doc: Document) -> str:
  function format_docs (line 24) | def format_docs(docs: Optional[list[Document]]) -> str:
  function load_chat_model (line 58) | def load_chat_model(fully_specified_name: str) -> BaseChatModel:

FILE: ch9/py/src/shared/configuration.py
  class BaseConfiguration (line 12) | class BaseConfiguration:
    method from_runnable_config (line 47) | def from_runnable_config(

FILE: ch9/py/src/shared/retrieval.py
  function make_text_encoder (line 16) | def make_text_encoder(model: str) -> Embeddings:
  function make_supabase_retriever (line 27) | def make_supabase_retriever(configuration: RunnableConfig, embedding_mod...
  function make_chroma_retriever (line 43) | def make_chroma_retriever(configuration: IndexConfiguration, embedding_m...
  function make_retriever (line 57) | def make_retriever(

FILE: ch9/py/src/shared/state.py
  function _generate_uuid (line 13) | def _generate_uuid(page_content: str) -> str:
  function reduce_docs (line 19) | def reduce_docs(
Condensed preview — 169 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,704K chars).
[
  {
    "path": ".gitignore",
    "chars": 962,
    "preview": "# ===== Python =====\n\n# Python\n__pycache__/\n*.py[cod]\n*$py.class\n*.so\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs"
  },
  {
    "path": "README.md",
    "chars": 13676,
    "preview": "# Learning LangChain Code Examples\n\nThis repository contains code examples (in python and javascript) from each chapter "
  },
  {
    "path": "ch1/js/a-llm.js",
    "chars": 182,
    "preview": "import { ChatOpenAI } from '@langchain/openai';\n\nconst model = new ChatOpenAI({ model: 'gpt-3.5-turbo' });\n\nconst respon"
  },
  {
    "path": "ch1/js/b-chat.js",
    "chars": 276,
    "preview": "import { ChatOpenAI } from '@langchain/openai';\nimport { HumanMessage } from '@langchain/core/messages';\n\nconst model = "
  },
  {
    "path": "ch1/js/c-system.js",
    "chars": 413,
    "preview": "import { ChatOpenAI } from '@langchain/openai';\nimport { HumanMessage, SystemMessage } from '@langchain/core/messages';\n"
  },
  {
    "path": "ch1/js/d-prompt.js",
    "chars": 863,
    "preview": "import { PromptTemplate } from '@langchain/core/prompts';\n\nconst template =\n  PromptTemplate.fromTemplate(`Answer the qu"
  },
  {
    "path": "ch1/js/e-prompt-model.js",
    "chars": 1007,
    "preview": "import { PromptTemplate } from '@langchain/core/prompts';\nimport { OpenAI } from '@langchain/openai';\n\nconst model = new"
  },
  {
    "path": "ch1/js/f-chat-prompt.js",
    "chars": 921,
    "preview": "import { ChatPromptTemplate } from '@langchain/core/prompts';\n\nconst template = ChatPromptTemplate.fromMessages([\n  [\n  "
  },
  {
    "path": "ch1/js/g-chat-prompt-model.js",
    "chars": 1045,
    "preview": "import { ChatPromptTemplate } from '@langchain/core/prompts';\nimport { ChatOpenAI } from '@langchain/openai';\n\nconst mod"
  },
  {
    "path": "ch1/js/h-structured.js",
    "chars": 596,
    "preview": "import { ChatOpenAI } from '@langchain/openai';\nimport { z } from 'zod';\n\nconst answerSchema = z\n  .object({\n    answer:"
  },
  {
    "path": "ch1/js/i-csv.js",
    "chars": 222,
    "preview": "import { CommaSeparatedListOutputParser } from '@langchain/core/output_parsers';\n\nconst parser = new CommaSeparatedListO"
  },
  {
    "path": "ch1/js/j-methods.js",
    "chars": 355,
    "preview": "import { ChatOpenAI } from '@langchain/openai';\n\nconst model = new ChatOpenAI();\n\nconst response = await model.invoke('H"
  },
  {
    "path": "ch1/js/k-imperative.js",
    "chars": 768,
    "preview": "import { ChatOpenAI } from '@langchain/openai';\nimport { ChatPromptTemplate } from '@langchain/core/prompts';\nimport { R"
  },
  {
    "path": "ch1/js/ka-stream.js",
    "chars": 685,
    "preview": "import { ChatOpenAI } from '@langchain/openai';\nimport { ChatPromptTemplate } from '@langchain/core/prompts';\nimport { R"
  },
  {
    "path": "ch1/js/l-declarative.js",
    "chars": 718,
    "preview": "import { ChatOpenAI } from '@langchain/openai';\nimport { ChatPromptTemplate } from '@langchain/core/prompts';\nimport { R"
  },
  {
    "path": "ch1/py/a-llm.py",
    "chars": 158,
    "preview": "from langchain_openai.chat_models import ChatOpenAI\n\nmodel = ChatOpenAI(model=\"gpt-3.5-turbo\")\n\nresponse = model.invoke("
  },
  {
    "path": "ch1/py/b-chat.py",
    "chars": 238,
    "preview": "from langchain_openai.chat_models import ChatOpenAI\nfrom langchain_core.messages import HumanMessage\n\nmodel = ChatOpenAI"
  },
  {
    "path": "ch1/py/c-system.py",
    "chars": 392,
    "preview": "from langchain_core.messages import HumanMessage, SystemMessage\nfrom langchain_openai.chat_models import ChatOpenAI\n\nmod"
  },
  {
    "path": "ch1/py/d-prompt.py",
    "chars": 853,
    "preview": "from langchain_core.prompts import PromptTemplate\n\ntemplate = PromptTemplate.from_template(\"\"\"Answer the question based "
  },
  {
    "path": "ch1/py/e-prompt-model.py",
    "chars": 1112,
    "preview": "from langchain_openai.chat_models import ChatOpenAI\nfrom langchain_core.prompts import PromptTemplate\n\n# both `template`"
  },
  {
    "path": "ch1/py/f-chat-prompt.py",
    "chars": 960,
    "preview": "from langchain_core.prompts import ChatPromptTemplate\n\ntemplate = ChatPromptTemplate.from_messages(\n    [\n        (\n    "
  },
  {
    "path": "ch1/py/g-chat-prompt-model.py",
    "chars": 1178,
    "preview": "from langchain_openai.chat_models import ChatOpenAI\nfrom langchain_core.prompts import ChatPromptTemplate\n\n# both `templ"
  },
  {
    "path": "ch1/py/h-structured.py",
    "chars": 559,
    "preview": "from langchain_openai import ChatOpenAI\nfrom pydantic import BaseModel\n\n\nclass AnswerWithJustification(BaseModel):\n    \""
  },
  {
    "path": "ch1/py/i-csv.py",
    "chars": 183,
    "preview": "from langchain_core.output_parsers import CommaSeparatedListOutputParser\n\nparser = CommaSeparatedListOutputParser()\n\nres"
  },
  {
    "path": "ch1/py/j-methods.py",
    "chars": 295,
    "preview": "from langchain_openai.chat_models import ChatOpenAI\n\nmodel = ChatOpenAI(model=\"gpt-3.5-turbo\")\n\ncompletion = model.invok"
  },
  {
    "path": "ch1/py/k-imperative.py",
    "chars": 681,
    "preview": "from langchain_openai.chat_models import ChatOpenAI\nfrom langchain_core.prompts import ChatPromptTemplate\nfrom langchain"
  },
  {
    "path": "ch1/py/ka-stream.py",
    "chars": 562,
    "preview": "from langchain_core.runnables import chain\nfrom langchain_openai.chat_models import ChatOpenAI\nfrom langchain_core.promp"
  },
  {
    "path": "ch1/py/kb-async.py",
    "chars": 638,
    "preview": "from langchain_core.runnables import chain\nfrom langchain_openai.chat_models import ChatOpenAI\nfrom langchain_core.promp"
  },
  {
    "path": "ch1/py/l-declarative.py",
    "chars": 581,
    "preview": "from langchain_openai.chat_models import ChatOpenAI\nfrom langchain_core.prompts import ChatPromptTemplate\n\n# the buildin"
  },
  {
    "path": "ch10/js/agent-evaluation-rag.js",
    "chars": 2625,
    "preview": "import { ChatOpenAI } from '@langchain/openai';\nimport { ChatPromptTemplate } from '@langchain/core/prompts';\nimport { e"
  },
  {
    "path": "ch10/js/agent-evaluation-sql.js",
    "chars": 5510,
    "preview": "import { ChatOpenAI } from '@langchain/openai';\nimport { graph } from './agent-sql-graph.js';\nimport crypto from 'crypto"
  },
  {
    "path": "ch10/js/agent-sql-graph.js",
    "chars": 6339,
    "preview": "import { ChatOpenAI } from '@langchain/openai';\nimport { SqlDatabase } from 'langchain/sql_db';\nimport { DataSource } fr"
  },
  {
    "path": "ch10/js/create-rag-dataset.js",
    "chars": 2115,
    "preview": "import { Client } from 'langsmith';\nconst client = new Client();\n\nconst exampleInputs = [\n  [\n    'Which companies are h"
  },
  {
    "path": "ch10/js/create-sql-dataset.js",
    "chars": 1203,
    "preview": "import { Client } from 'langsmith';\nconst client = new Client();\n\nconst exampleInputs = [\n  [\n    \"Which country's custo"
  },
  {
    "path": "ch10/js/rag-graph.js",
    "chars": 2615,
    "preview": "import { Annotation, StateGraph } from '@langchain/langgraph';\nimport { CheerioWebBaseLoader } from '@langchain/communit"
  },
  {
    "path": "ch10/js/retrieve-and-grade.js",
    "chars": 2823,
    "preview": "import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';\nimport { CheerioWebBaseLoader } from '@langch"
  },
  {
    "path": "ch10/js/search-graph.js",
    "chars": 4234,
    "preview": "import { Annotation, StateGraph, START, END } from '@langchain/langgraph';\nimport { StringOutputParser } from '@langchai"
  },
  {
    "path": "ch10/py/agent_evaluation_rag.py",
    "chars": 3915,
    "preview": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom langsmith import Client, evaluate, aevaluate\nf"
  },
  {
    "path": "ch10/py/agent_evaluation_sql.py",
    "chars": 5792,
    "preview": "from agent_sql_graph import builder\nfrom langchain import hub\nfrom langchain_openai import ChatOpenAI\nfrom langsmith.eva"
  },
  {
    "path": "ch10/py/agent_sql_graph.py",
    "chars": 7484,
    "preview": "from langgraph.checkpoint.sqlite import SqliteSaver\nfrom langgraph.graph import END, StateGraph\nfrom langgraph.prebuilt "
  },
  {
    "path": "ch10/py/create_rag_dataset.py",
    "chars": 2165,
    "preview": "from langsmith import wrappers, Client\nfrom pydantic import BaseModel, Field\nfrom openai import OpenAI\n\nclient = Client("
  },
  {
    "path": "ch10/py/create_sql_dataset.py",
    "chars": 1044,
    "preview": "from langsmith import Client\n\nclient = Client()\n\n# Create a dataset\nexamples = [\n    (\"Which country's customers spent t"
  },
  {
    "path": "ch10/py/rag_graph.py",
    "chars": 2811,
    "preview": "from typing import List, TypedDict\nfrom langchain_community.document_loaders import WebBaseLoader\nfrom langchain.schema "
  },
  {
    "path": "ch10/py/retrieve_and_grade.py",
    "chars": 2573,
    "preview": "from langchain.text_splitter import RecursiveCharacterTextSplitter\nfrom langchain_community.document_loaders import WebB"
  },
  {
    "path": "ch10/py/search_graph.py",
    "chars": 5853,
    "preview": "from typing import List, TypedDict\n\nfrom langchain_core.documents import Document\nfrom langchain_community.tools import "
  },
  {
    "path": "ch2/js/a-text-loader.js",
    "chars": 165,
    "preview": "import { TextLoader } from 'langchain/document_loaders/fs/text';\n\nconst loader = new TextLoader('./test.txt');\nconst doc"
  },
  {
    "path": "ch2/js/b-web-loader.js",
    "chars": 216,
    "preview": "import { CheerioWebBaseLoader } from '@langchain/community/document_loaders/web/cheerio';\n\nconst loader = new CheerioWeb"
  },
  {
    "path": "ch2/js/c-pdf-loader.js",
    "chars": 231,
    "preview": "// install the pdf parsing library: npm install pdf-parse\nimport { PDFLoader } from '@langchain/community/document_loade"
  },
  {
    "path": "ch2/js/d-rec-text-splitter.js",
    "chars": 429,
    "preview": "import { TextLoader } from 'langchain/document_loaders/fs/text';\nimport { RecursiveCharacterTextSplitter } from '@langch"
  },
  {
    "path": "ch2/js/e-rec-text-splitter-code.js",
    "chars": 396,
    "preview": "import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';\n\nconst PYTHON_CODE = ` def hello_world(): pri"
  },
  {
    "path": "ch2/js/f-markdown-splitter.js",
    "chars": 584,
    "preview": "import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';\n\nconst markdownText = ` # 🦜🔗 LangChain ⚡ Buil"
  },
  {
    "path": "ch2/js/g-embeddings.js",
    "chars": 273,
    "preview": "import { OpenAIEmbeddings } from '@langchain/openai';\n\nconst model = new OpenAIEmbeddings();\nconst embeddings = await mo"
  },
  {
    "path": "ch2/js/h-load-split-embed.js",
    "chars": 638,
    "preview": "import { TextLoader } from 'langchain/document_loaders/fs/text';\nimport { RecursiveCharacterTextSplitter } from '@langch"
  },
  {
    "path": "ch2/js/i-pg-vector.js",
    "chars": 1995,
    "preview": "/** \n1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)\n2. Run the following command to sta"
  },
  {
    "path": "ch2/js/j-record-manager.js",
    "chars": 2820,
    "preview": "/** \n1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)\n2. Run the following command to sta"
  },
  {
    "path": "ch2/js/k-multi-vector-retriever.js",
    "chars": 2936,
    "preview": "import * as uuid from 'uuid';\nimport { MultiVectorRetriever } from 'langchain/retrievers/multi_vector';\nimport { OpenAIE"
  },
  {
    "path": "ch2/py/a-text-loader.py",
    "chars": 147,
    "preview": "from langchain_community.document_loaders import TextLoader\n\nloader = TextLoader('./test.txt', encoding=\"utf-8\")\ndocs = "
  },
  {
    "path": "ch2/py/b-web-loader.py",
    "chars": 236,
    "preview": "\"\"\"\nInstall the beautifulsoup4 package:\n\n```bash\npip install beautifulsoup4\n```\n\"\"\"\n\nfrom langchain_community.document_l"
  },
  {
    "path": "ch2/py/c-pdf-loader.py",
    "chars": 187,
    "preview": "# install the pdf parsing library !pip install pypdf\n\nfrom langchain_community.document_loaders import PyPDFLoader\n\nload"
  },
  {
    "path": "ch2/py/d-rec-text-splitter.py",
    "chars": 351,
    "preview": "from langchain_text_splitters import RecursiveCharacterTextSplitter\n\nfrom langchain_community.document_loaders import Te"
  },
  {
    "path": "ch2/py/e-rec-text-splitter-code.py",
    "chars": 401,
    "preview": "from langchain_text_splitters import (\n    Language,\n    RecursiveCharacterTextSplitter,\n)\n\nPYTHON_CODE = \"\"\" def hello_"
  },
  {
    "path": "ch2/py/f-markdown-splitter.py",
    "chars": 587,
    "preview": "from langchain_text_splitters import (\n    Language,\n    RecursiveCharacterTextSplitter,\n)\nmarkdown_text = \"\"\" # 🦜🔗 Lang"
  },
  {
    "path": "ch2/py/g-embeddings.py",
    "chars": 274,
    "preview": "from langchain_openai import OpenAIEmbeddings\n\nmodel = OpenAIEmbeddings(model=\"text-embedding-3-small\")\nembeddings = mod"
  },
  {
    "path": "ch2/py/h-load-split-embed.py",
    "chars": 610,
    "preview": "from langchain_community.document_loaders import TextLoader\nfrom langchain_text_splitters import RecursiveCharacterTextS"
  },
  {
    "path": "ch2/py/i-pg-vector.py",
    "chars": 2082,
    "preview": "\"\"\"\n1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)\n2. pip install -qU langchain_postgre"
  },
  {
    "path": "ch2/py/j-record-manager.py",
    "chars": 1844,
    "preview": "from langchain.indexes import SQLRecordManager, index\nfrom langchain_postgres.vectorstores import PGVector\nfrom langchai"
  },
  {
    "path": "ch2/py/k-multi-vector-retriever.py",
    "chars": 3096,
    "preview": "from langchain_community.document_loaders import TextLoader\nfrom langchain_text_splitters import RecursiveCharacterTextS"
  },
  {
    "path": "ch2/py/l-rag-colbert.py",
    "chars": 1823,
    "preview": "\"\"\"\n- Windows is not supported. RAGatouille doesn't appear to work outside WSL and has issues with WSL1. Some users have"
  },
  {
    "path": "ch3/js/a-basic-rag.js",
    "chars": 2804,
    "preview": "/** \n1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)\n2. Run the following command to sta"
  },
  {
    "path": "ch3/js/b-rewrite.js",
    "chars": 3415,
    "preview": "/** \n1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)\n2. Run the following command to sta"
  },
  {
    "path": "ch3/js/c-multi-query.js",
    "chars": 3342,
    "preview": "/** \n1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)\n2. Run the following command to sta"
  },
  {
    "path": "ch3/js/d-rag-fusion.js",
    "chars": 3858,
    "preview": "/** \n1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)\n2. Run the following command to sta"
  },
  {
    "path": "ch3/js/e-hyde.js",
    "chars": 2719,
    "preview": "/** \n1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)\n2. Run the following command to sta"
  },
  {
    "path": "ch3/js/f-router.js",
    "chars": 1803,
    "preview": "import { ChatOpenAI } from '@langchain/openai';\nimport { z } from 'zod';\nimport { ChatPromptTemplate } from '@langchain/"
  },
  {
    "path": "ch3/js/g-semantic-router.js",
    "chars": 1824,
    "preview": "import { cosineSimilarity } from '@langchain/core/utils/math';\nimport { ChatOpenAI, OpenAIEmbeddings } from '@langchain/"
  },
  {
    "path": "ch3/js/h-self-query.js",
    "chars": 3657,
    "preview": "import { ChatOpenAI } from '@langchain/openai';\nimport { SelfQueryRetriever } from 'langchain/retrievers/self_query';\nim"
  },
  {
    "path": "ch3/js/i-sql-example.js",
    "chars": 1484,
    "preview": "/*\nThe below example will use a SQLite connection with the Chinook database, which is a sample database that represents "
  },
  {
    "path": "ch3/py/a-basic-rag.py",
    "chars": 2637,
    "preview": "\"\"\"\n1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)\n2. pip install -qU langchain_postgre"
  },
  {
    "path": "ch3/py/b-rewrite.py",
    "chars": 3503,
    "preview": "\"\"\"\n1. Ensure docker is installed and running (https://docs.docker.com/get-docker/)\n2. pip install -qU langchain_postgre"
  },
  {
    "path": "ch3/py/c-multi-query.py",
    "chars": 2711,
    "preview": "from langchain_community.document_loaders import TextLoader\nfrom langchain_openai import OpenAIEmbeddings\nfrom langchain"
  },
  {
    "path": "ch3/py/d-rag-fusion.py",
    "chars": 3726,
    "preview": "from langchain_community.document_loaders import TextLoader\nfrom langchain_openai import OpenAIEmbeddings\nfrom langchain"
  },
  {
    "path": "ch3/py/e-hyde.py",
    "chars": 2250,
    "preview": "from langchain_community.document_loaders import TextLoader\nfrom langchain_openai import OpenAIEmbeddings\nfrom langchain"
  },
  {
    "path": "ch3/py/f-router.py",
    "chars": 1868,
    "preview": "\nfrom typing import Literal\nfrom langchain_core.prompts import ChatPromptTemplate\nfrom pydantic import BaseModel, Field\n"
  },
  {
    "path": "ch3/py/g-semantic-router.py",
    "chars": 1572,
    "preview": "from langchain.utils.math import cosine_similarity\nfrom langchain_core.output_parsers import StrOutputParser\nfrom langch"
  },
  {
    "path": "ch3/py/h-self-query.py",
    "chars": 3038,
    "preview": "# pip install lark\n\nfrom langchain.chains.query_constructor.base import AttributeInfo\nfrom langchain.retrievers.self_que"
  },
  {
    "path": "ch3/py/i-sql-example.py",
    "chars": 1295,
    "preview": "\"\"\"\nThe below example will use a SQLite connection with the Chinook database, which is a sample database that represents"
  },
  {
    "path": "ch4/js/a-simple-memory.js",
    "chars": 647,
    "preview": "import { ChatPromptTemplate } from '@langchain/core/prompts';\nimport { ChatOpenAI } from '@langchain/openai';\n\nconst pro"
  },
  {
    "path": "ch4/js/b-state-graph.js",
    "chars": 814,
    "preview": "import {\n  StateGraph,\n  Annotation,\n  messagesStateReducer,\n  START,\n  END,\n} from '@langchain/langgraph';\nimport { Cha"
  },
  {
    "path": "ch4/js/c-persistent-memory.js",
    "chars": 1187,
    "preview": "import {\n  StateGraph,\n  Annotation,\n  messagesStateReducer,\n  START,\n} from \"@langchain/langgraph\";\nimport { ChatOpenAI"
  },
  {
    "path": "ch4/js/d-trim-messages.js",
    "chars": 803,
    "preview": "import {\n  AIMessage,\n  HumanMessage,\n  SystemMessage,\n  trimMessages,\n} from \"@langchain/core/messages\";\nimport { ChatO"
  },
  {
    "path": "ch4/js/e-filter-messages.js",
    "chars": 1172,
    "preview": "import {\n  HumanMessage,\n  SystemMessage,\n  AIMessage,\n  filterMessages,\n} from '@langchain/core/messages';\n\nconst messa"
  },
  {
    "path": "ch4/js/f-merge-messages.js",
    "chars": 737,
    "preview": "import {\n  HumanMessage,\n  SystemMessage,\n  AIMessage,\n  mergeMessageRuns,\n} from '@langchain/core/messages';\n\nconst mes"
  },
  {
    "path": "ch4/py/a-simple-memory.py",
    "chars": 583,
    "preview": "from langchain_core.prompts import ChatPromptTemplate\nfrom langchain_openai import ChatOpenAI\n\nprompt = ChatPromptTempla"
  },
  {
    "path": "ch4/py/b-state-graph.py",
    "chars": 759,
    "preview": "from typing import Annotated, TypedDict\n\nfrom langchain_core.messages import HumanMessage\nfrom langchain_openai import C"
  },
  {
    "path": "ch4/py/c-persistent-memory.py",
    "chars": 1011,
    "preview": "from typing import Annotated, TypedDict\n\nfrom langchain_core.messages import HumanMessage\nfrom langchain_openai import C"
  },
  {
    "path": "ch4/py/d-trim-messages.py",
    "chars": 888,
    "preview": "from langchain_core.messages import (\n    SystemMessage,\n    HumanMessage,\n    AIMessage,\n    trim_messages,\n)\nfrom lang"
  },
  {
    "path": "ch4/py/e-filter-messages.py",
    "chars": 988,
    "preview": "from langchain_core.messages import (\n    AIMessage,\n    HumanMessage,\n    SystemMessage,\n    filter_messages,\n)\n\n# Samp"
  },
  {
    "path": "ch4/py/f-merge-messages.py",
    "chars": 808,
    "preview": "from langchain_core.messages import (\n    AIMessage,\n    HumanMessage,\n    SystemMessage,\n    merge_message_runs,\n)\n\n# S"
  },
  {
    "path": "ch5/js/a-chatbot.js",
    "chars": 1023,
    "preview": "import {\n  StateGraph,\n  Annotation,\n  messagesStateReducer,\n  START,\n  END,\n} from '@langchain/langgraph';\n\nimport { Ch"
  },
  {
    "path": "ch5/js/b-sql-generator.js",
    "chars": 1878,
    "preview": "import { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { ChatOpenAI } from \"@langchain/openai\";\n"
  },
  {
    "path": "ch5/js/c-multi-rag.js",
    "chars": 4011,
    "preview": "import { HumanMessage, SystemMessage } from \"@langchain/core/messages\";\nimport { ChatOpenAI, OpenAIEmbeddings } from \"@l"
  },
  {
    "path": "ch5/py/a-chatbot.py",
    "chars": 946,
    "preview": "from typing import Annotated, TypedDict\n\nfrom langgraph.graph import StateGraph, START, END\nfrom langgraph.graph.message"
  },
  {
    "path": "ch5/py/b-sql-generator.py",
    "chars": 2068,
    "preview": "from typing import Annotated, TypedDict\n\nfrom langchain_core.messages import HumanMessage, SystemMessage\nfrom langchain_"
  },
  {
    "path": "ch5/py/c-multi-rag.py",
    "chars": 4343,
    "preview": "from typing import Annotated, Literal, TypedDict\n\nfrom langchain_core.documents import Document\nfrom langchain_core.mess"
  },
  {
    "path": "ch6/js/a-basic-agent.js",
    "chars": 1347,
    "preview": "import { DuckDuckGoSearch } from \"@langchain/community/tools/duckduckgo_search\";\nimport { Calculator } from \"@langchain/"
  },
  {
    "path": "ch6/js/b-force-first-tool.js",
    "chars": 1748,
    "preview": "import { DuckDuckGoSearch } from \"@langchain/community/tools/duckduckgo_search\";\nimport { Calculator } from \"@langchain/"
  },
  {
    "path": "ch6/js/c-many-tools.js",
    "chars": 2335,
    "preview": "import { DuckDuckGoSearch } from '@langchain/community/tools/duckduckgo_search';\nimport { Calculator } from '@langchain/"
  },
  {
    "path": "ch6/py/a-basic-agent.py",
    "chars": 1316,
    "preview": "import ast\nfrom typing import Annotated, TypedDict\n\nfrom langchain_community.tools import DuckDuckGoSearchRun\nfrom langc"
  },
  {
    "path": "ch6/py/b-force-first-tool.py",
    "chars": 1725,
    "preview": "import ast\nfrom typing import Annotated, TypedDict\nfrom uuid import uuid4\n\nfrom langchain_community.tools import DuckDuc"
  },
  {
    "path": "ch6/py/c-many-tools.py",
    "chars": 2080,
    "preview": "import ast\nfrom typing import Annotated, TypedDict\n\nfrom langchain_community.tools import DuckDuckGoSearchRun\nfrom langc"
  },
  {
    "path": "ch7/js/a-reflection.js",
    "chars": 2571,
    "preview": "import {\n  AIMessage,\n  SystemMessage,\n  HumanMessage,\n} from '@langchain/core/messages';\nimport { ChatOpenAI } from '@l"
  },
  {
    "path": "ch7/js/b-subgraph-direct.js",
    "chars": 1074,
    "preview": "import { StateGraph, START, Annotation } from '@langchain/langgraph';\n\nconst StateAnnotation = Annotation.Root({\n  foo: "
  },
  {
    "path": "ch7/js/c-subgraph-function.js",
    "chars": 1291,
    "preview": "import { StateGraph, START, Annotation } from '@langchain/langgraph';\n\nconst StateAnnotation = Annotation.Root({\n  foo: "
  },
  {
    "path": "ch7/js/d-supervisor.js",
    "chars": 3013,
    "preview": "import { ChatOpenAI } from \"langchain-openai\";\nimport {\n  StateGraph,\n  Annotation,\n  MessagesAnnotation,\n  START,\n  END"
  },
  {
    "path": "ch7/py/a-reflection.py",
    "chars": 2578,
    "preview": "from typing import Annotated, TypedDict\n\nfrom langchain_core.messages import (\n    AIMessage,\n    BaseMessage,\n    Human"
  },
  {
    "path": "ch7/py/b-subgraph-direct.py",
    "chars": 1045,
    "preview": "from typing import TypedDict\n\nfrom langgraph.graph import START, StateGraph\n\n\n# Define the state types for parent and su"
  },
  {
    "path": "ch7/py/c-subgraph-function.py",
    "chars": 1263,
    "preview": "from typing import TypedDict\nfrom langgraph.graph import START, StateGraph\n\n\nclass State(TypedDict):\n    foo: str\n\n\nclas"
  },
  {
    "path": "ch7/py/d-supervisor.py",
    "chars": 2993,
    "preview": "from typing import Literal\n\nfrom langchain_openai import ChatOpenAI\nfrom langgraph.graph import StateGraph, MessagesStat"
  },
  {
    "path": "ch8/js/a-structured-output.js",
    "chars": 432,
    "preview": "import { z } from \"zod\";\nimport { ChatOpenAI } from \"@langchain/openai\";\n\nconst joke = z.object({\n  setup: z.string().de"
  },
  {
    "path": "ch8/js/b-streaming-output.js",
    "chars": 513,
    "preview": "import { HumanMessage } from \"@langchain/core/messages\";\n\n// Assuming graph is already created and configured\nconst grap"
  },
  {
    "path": "ch8/js/c-interrupt.js",
    "chars": 809,
    "preview": "import { HumanMessage } from \"@langchain/core/messages\";\nimport { MemorySaver } from \"@langchain/langgraph\";\n\nconst cont"
  },
  {
    "path": "ch8/js/d-authorize.js",
    "chars": 620,
    "preview": "import { HumanMessage } from \"@langchain/core/messages\";\nimport { MemorySaver } from \"@langchain/langgraph\";\n\n// Assumin"
  },
  {
    "path": "ch8/js/e-resume.js",
    "chars": 419,
    "preview": "import { MemorySaver } from \"@langchain/langgraph\";\n\n// Assuming graph is already created and configured\nconst graph = n"
  },
  {
    "path": "ch8/js/f-edit-state.js",
    "chars": 451,
    "preview": "import { MemorySaver } from \"@langchain/langgraph\";\n\n// Assuming graph is already created and configured\nconst graph = n"
  },
  {
    "path": "ch8/js/g-fork.js",
    "chars": 519,
    "preview": "import { MemorySaver } from \"@langchain/langgraph\";\n\n// Assuming graph is already created and configured\nconst graph = n"
  },
  {
    "path": "ch8/py/a-structured-output.py",
    "chars": 392,
    "preview": "from pydantic import BaseModel, Field\nfrom langchain_openai import ChatOpenAI\n\n\nclass Joke(BaseModel):\n    setup: str = "
  },
  {
    "path": "ch8/py/b-streaming-output.py",
    "chars": 503,
    "preview": "from langchain_core.messages import HumanMessage\nfrom langgraph.graph import StateGraph\n\n\ndef create_simple_graph():\n   "
  },
  {
    "path": "ch8/py/c-interrupt.py",
    "chars": 979,
    "preview": "import asyncio\nfrom contextlib import aclosing\n\nfrom langchain.schema import HumanMessage\nfrom langgraph.graph import St"
  },
  {
    "path": "ch8/py/d-authorize.py",
    "chars": 749,
    "preview": "from langchain.schema import HumanMessage\nfrom langgraph.graph import StateGraph\nfrom langgraph.checkpoint.memory import"
  },
  {
    "path": "ch8/py/e-resume.py",
    "chars": 529,
    "preview": "from langgraph.graph import StateGraph\nfrom langgraph.checkpoint.memory import MemorySaver\n\n\nasync def main():\n    # Cre"
  },
  {
    "path": "ch8/py/f-edit-state.py",
    "chars": 542,
    "preview": "from langgraph.graph import StateGraph\nfrom langgraph.checkpoint.memory import MemorySaver\n\n\ndef main():\n    # Create a "
  },
  {
    "path": "ch8/py/g-fork.py",
    "chars": 611,
    "preview": "from langgraph.graph import StateGraph\nfrom langgraph.checkpoint.memory import MemorySaver\n\n\ndef main():\n    # Create a "
  },
  {
    "path": "ch9/README.md",
    "chars": 6467,
    "preview": "# Chapter 9: Deployment - RAG AI Agent Example\n\nThis directory contains the code for deploying a Retrieval-Augmented Gen"
  },
  {
    "path": "ch9/js/.gitignore",
    "chars": 32,
    "preview": "\n# LangGraph API\n.langgraph_api\n"
  },
  {
    "path": "ch9/js/demo.ts",
    "chars": 2404,
    "preview": "// demo of the compiled graph running using the sdk\nimport { Client } from '@langchain/langgraph-sdk';\nimport { graph } "
  },
  {
    "path": "ch9/js/langgraph.json",
    "chars": 234,
    "preview": "{\n    \"node_version\": \"20\",\n    \"graphs\": {\n      \"ingestion_graph\": \"./src/ingestion_graph/graph.ts:graph\",\n      \"retr"
  },
  {
    "path": "ch9/js/package.json",
    "chars": 489,
    "preview": "{\n    \"type\": \"module\",\n    \"dependencies\": {\n        \"@langchain/community\": \"^0.3.26\",\n        \"@langchain/core\": \"^0."
  },
  {
    "path": "ch9/js/src/ingestion_graph/configuration.ts",
    "chars": 1663,
    "preview": "import { Annotation } from '@langchain/langgraph';\nimport { RunnableConfig } from '@langchain/core/runnables';\n\n// This "
  },
  {
    "path": "ch9/js/src/ingestion_graph/graph.ts",
    "chars": 1775,
    "preview": "/**\n * This \"graph\" simply exposes an endpoint for a user to upload docs to be indexed.\n */\nimport path from 'path';\nimp"
  },
  {
    "path": "ch9/js/src/ingestion_graph/state.ts",
    "chars": 745,
    "preview": "import { Annotation } from '@langchain/langgraph';\nimport { Document } from '@langchain/core/documents';\nimport { reduce"
  },
  {
    "path": "ch9/js/src/retrieval_graph/configuration.ts",
    "chars": 1125,
    "preview": "import { Annotation } from '@langchain/langgraph';\nimport {\n  BaseConfigurationAnnotation,\n  ensureBaseConfiguration,\n} "
  },
  {
    "path": "ch9/js/src/retrieval_graph/graph.ts",
    "chars": 4663,
    "preview": "import {\n  StateGraph,\n  START,\n  END,\n  MessagesAnnotation,\n} from '@langchain/langgraph';\nimport { AgentStateAnnotatio"
  },
  {
    "path": "ch9/js/src/retrieval_graph/state.ts",
    "chars": 764,
    "preview": "import { Annotation, MessagesAnnotation } from '@langchain/langgraph';\nimport { reduceDocs } from '../shared/state.js';\n"
  },
  {
    "path": "ch9/js/src/retrieval_graph/utils.ts",
    "chars": 630,
    "preview": "import { Document } from '@langchain/core/documents';\n\nexport function formatDoc(doc: Document): string {\n  const metada"
  },
  {
    "path": "ch9/js/src/shared/configuration.ts",
    "chars": 1997,
    "preview": "/**\n * Define the configurable parameters for the agent.\n */\n\nimport { Annotation } from '@langchain/langgraph';\nimport "
  },
  {
    "path": "ch9/js/src/shared/retrieval.ts",
    "chars": 2203,
    "preview": "import { VectorStoreRetriever } from '@langchain/core/vectorstores';\nimport { OpenAIEmbeddings } from '@langchain/openai"
  },
  {
    "path": "ch9/js/src/shared/state.ts",
    "chars": 1865,
    "preview": "import { Document } from '@langchain/core/documents';\nimport { v4 as uuidv4 } from 'uuid';\n\n/**\n * Reduces the document "
  },
  {
    "path": "ch9/js/src/shared/utils.ts",
    "chars": 1619,
    "preview": "import { BaseChatModel } from '@langchain/core/language_models/chat_models';\nimport { initChatModel } from 'langchain/ch"
  },
  {
    "path": "ch9/js/tsconfig.json",
    "chars": 338,
    "preview": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"module\": \"NodeNext\",\n    \"outDir\": \"./dist\",\n    \"rootDir\": \"./src"
  },
  {
    "path": "ch9/py/demo.py",
    "chars": 1501,
    "preview": "import asyncio\nfrom langgraph_sdk import get_client\n\n\nasync def invoke_retrieval_assistant():\n    # Initialize the LangG"
  },
  {
    "path": "ch9/py/langgraph.json",
    "chars": 195,
    "preview": "{\r\n  \"dependencies\": [\".\"],\r\n  \"graphs\": {\r\n    \"indexer\": \"./src/ingestion_graph/graph.py:graph\",\r\n    \"retrieval_graph"
  },
  {
    "path": "ch9/py/pyproject.toml",
    "chars": 1692,
    "preview": "[project]\r\nname = \"rag-langgraph-template\"\r\nversion = \"0.0.1\"\r\ndescription = \"A RAG agentin LangGraph.\"\r\nauthors = []\r\nl"
  },
  {
    "path": "ch9/py/src/docSplits.json",
    "chars": 681584,
    "preview": "[\n  {\n    \"pageContent\": \"UNITED\\tSTATES\\nSECURITIES\\tAND\\tEXCHANGE\\tCOMMISSION\\nWashington,\\tD.C.\\t20549\\nFORM\\t10-K\\n("
  },
  {
    "path": "ch9/py/src/ingestion_graph/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "ch9/py/src/ingestion_graph/configuration.py",
    "chars": 2485,
    "preview": "\"\"\"Define the configurable parameters for the index graph.\"\"\"\r\n\r\nfrom __future__ import annotations\r\n\r\nfrom dataclasses "
  },
  {
    "path": "ch9/py/src/ingestion_graph/graph.py",
    "chars": 1330,
    "preview": "import json\r\nfrom typing import Optional\r\nfrom langchain_core.runnables import RunnableConfig\r\nfrom langgraph.graph impo"
  },
  {
    "path": "ch9/py/src/ingestion_graph/state.py",
    "chars": 779,
    "preview": "\"\"\"State management for the index graph.\"\"\"\r\n\r\nfrom dataclasses import dataclass, field\r\nfrom typing import Annotated\r\n\r"
  },
  {
    "path": "ch9/py/src/retrieval_graph/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "ch9/py/src/retrieval_graph/configuration.py",
    "chars": 728,
    "preview": "\"\"\"Define the configurable parameters for the agent.\"\"\"\n\nfrom __future__ import annotations\n\nfrom dataclasses import dat"
  },
  {
    "path": "ch9/py/src/retrieval_graph/graph.py",
    "chars": 3340,
    "preview": "from typing import Literal\r\nfrom langchain.hub import pull\r\nfrom langchain_core.prompts import ChatPromptTemplate\r\nfrom "
  },
  {
    "path": "ch9/py/src/retrieval_graph/state.py",
    "chars": 287,
    "preview": "from typing import Annotated\r\nfrom langgraph.graph import MessagesState\r\nfrom langchain_core.documents import Document\r\n"
  },
  {
    "path": "ch9/py/src/retrieval_graph/utils.py",
    "chars": 2030,
    "preview": "from typing import Optional\r\nfrom langchain_core.documents import Document\r\nfrom langchain_core.language_models import B"
  },
  {
    "path": "ch9/py/src/shared/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "ch9/py/src/shared/configuration.py",
    "chars": 2194,
    "preview": "\"\"\"Define the configurable parameters for the agent.\"\"\"\n\nfrom __future__ import annotations\n\nfrom dataclasses import dat"
  },
  {
    "path": "ch9/py/src/shared/retrieval.py",
    "chars": 2938,
    "preview": "from contextlib import contextmanager\r\nimport os\r\nfrom langchain_chroma import Chroma\r\nfrom langchain_core.embeddings im"
  },
  {
    "path": "ch9/py/src/shared/state.py",
    "chars": 3076,
    "preview": "\"\"\"Shared functions for state management.\"\"\"\r\n\r\nimport hashlib\r\nimport uuid\r\nfrom typing import Any, Literal, Optional, "
  },
  {
    "path": "package.json",
    "chars": 784,
    "preview": "{\n\t\"name\": \"learning-langchain-repo\",\n\t\"description\": \"Learning LangChain O'Reilly book code examples\",\n\t\"type\": \"module"
  },
  {
    "path": "pyproject.toml",
    "chars": 988,
    "preview": "[project]\nname = \"learning-langchain\"\nversion = \"0.0.1\"\ndescription = \"Code blocks for the book Learning LangChain.\"\naut"
  },
  {
    "path": "test.txt",
    "chars": 624212,
    "preview": "Chapter 1: Life in Ancient Greece\nLife in ancient Greece was a tapestry woven with rich cultural, social, and political "
  }
]

About this extraction

This page contains the full source code of the langchain-ai/learning-langchain GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 169 files (1.5 MB), approximately 431.4k tokens, and a symbol index with 175 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!