# Typesense

Vector store that utilizes the Typesense search engine.

### Basic Usage

import IntegrationInstallTooltip from "@mdx_components/integration_install_tooltip.mdx";

<IntegrationInstallTooltip></IntegrationInstallTooltip>

```bash npm2yarn
npm install @langchain/openai @langchain/community @langchain/core
```

```typescript
import {
  Typesense,
  TypesenseConfig,
} from "@lanchain/community/vectorstores/typesense";
import { OpenAIEmbeddings } from "@langchain/openai";
import { Client } from "typesense";
import { Document } from "@langchain/core/documents";

const vectorTypesenseClient = new Client({
  nodes: [
    {
      // Ideally should come from your .env file
      host: "...",
      port: 123,
      protocol: "https",
    },
  ],
  // Ideally should come from your .env file
  apiKey: "...",
  numRetries: 3,
  connectionTimeoutSeconds: 60,
});

const typesenseVectorStoreConfig = {
  // Typesense client
  typesenseClient: vectorTypesenseClient,
  // Name of the collection to store the vectors in
  schemaName: "your_schema_name",
  // Optional column names to be used in Typesense
  columnNames: {
    // "vec" is the default name for the vector column in Typesense but you can change it to whatever you want
    vector: "vec",
    // "text" is the default name for the text column in Typesense but you can change it to whatever you want
    pageContent: "text",
    // Names of the columns that you will save in your typesense schema and need to be retrieved as metadata when searching
    metadataColumnNames: ["foo", "bar", "baz"],
  },
  // Optional search parameters to be passed to Typesense when searching
  searchParams: {
    q: "*",
    filter_by: "foo:[fooo]",
    query_by: "",
  },
  // You can override the default Typesense import function if you want to do something more complex
  // Default import function:
  // async importToTypesense<
  //   T extends Record<string, unknown> = Record<string, unknown>
  // >(data: T[], collectionName: string) {
  //   const chunkSize = 2000;
  //   for (let i = 0; i < data.length; i += chunkSize) {
  //     const chunk = data.slice(i, i + chunkSize);

  //     await this.caller.call(async () => {
  //       await this.client
  //         .collections<T>(collectionName)
  //         .documents()
  //         .import(chunk, { action: "emplace", dirty_values: "drop" });
  //     });
  //   }
  // }
  import: async (data, collectionName) => {
    await vectorTypesenseClient
      .collections(collectionName)
      .documents()
      .import(data, { action: "emplace", dirty_values: "drop" });
  },
} satisfies TypesenseConfig;

/**
 * Creates a Typesense vector store from a list of documents.
 * Will update documents if there is a document with the same id, at least with the default import function.
 * @param documents list of documents to create the vector store from
 * @returns Typesense vector store
 */
const createVectorStoreWithTypesense = async (documents: Document[] = []) =>
  Typesense.fromDocuments(
    documents,
    new OpenAIEmbeddings(),
    typesenseVectorStoreConfig
  );

/**
 * Returns a Typesense vector store from an existing index.
 * @returns Typesense vector store
 */
const getVectorStoreWithTypesense = async () =>
  new Typesense(new OpenAIEmbeddings(), typesenseVectorStoreConfig);

// Do a similarity search
const vectorStore = await getVectorStoreWithTypesense();
const documents = await vectorStore.similaritySearch("hello world");

// Add filters based on metadata with the search parameters of Typesense
// will exclude documents with author:JK Rowling, so if Joe Rowling & JK Rowling exists, only Joe Rowling will be returned
vectorStore.similaritySearch("Rowling", undefined, {
  filter_by: "author:!=JK Rowling",
});

// Delete a document
vectorStore.deleteDocuments(["document_id_1", "document_id_2"]);
```

### Constructor

Before starting, create a schema in Typesense with an id, a field for the vector and a field for the text. Add as many other fields as needed for the metadata.

- `constructor(embeddings: Embeddings, config: TypesenseConfig)`: Constructs a new instance of the `Typesense` class.
  - `embeddings`: An instance of the `Embeddings` class used for embedding documents.
  - `config`: Configuration object for the Typesense vector store.
    - `typesenseClient`: Typesense client instance.
    - `schemaName`: Name of the Typesense schema in which documents will be stored and searched.
    - `searchParams` (optional): Typesense search parameters. Default is `{ q: '*', per_page: 5, query_by: '' }`.
    - `columnNames` (optional): Column names configuration.
      - `vector` (optional): Vector column name. Default is `'vec'`.
      - `pageContent` (optional): Page content column name. Default is `'text'`.
      - `metadataColumnNames` (optional): Metadata column names. Default is an empty array `[]`.
    - `import` (optional): Replace the default import function for importing data to Typesense. This can affect the functionality of updating documents.

### Methods

- `async addDocuments(documents: Document[]): Promise<void>`: Adds documents to the vector store. The documents will be updated if there is a document with the same ID.
- `static async fromDocuments(docs: Document[], embeddings: Embeddings, config: TypesenseConfig): Promise<Typesense>`: Creates a Typesense vector store from a list of documents. Documents are added to the vector store during construction.
- `static async fromTexts(texts: string[], metadatas: object[], embeddings: Embeddings, config: TypesenseConfig): Promise<Typesense>`: Creates a Typesense vector store from a list of texts and associated metadata. Texts are converted to documents and added to the vector store during construction.
- `async similaritySearch(query: string, k?: number, filter?: Record<string, unknown>): Promise<Document[]>`: Searches for similar documents based on a query. Returns an array of similar documents.
- `async deleteDocuments(documentIds: string[]): Promise<void>`: Deletes documents from the vector store based on their IDs.

## Related

- Vector store [conceptual guide](/docs/concepts/#vectorstores)
- Vector store [how-to guides](/docs/how_to/#vectorstores)


---
sidebar_class_name: node-only
---

import CodeBlock from "@theme/CodeBlock";

# Tigris

Tigris makes it easy to build AI applications with vector embeddings.
It is a fully managed cloud-native database that allows you store and
index documents and vector embeddings for fast and scalable vector search.

:::tip Compatibility
Only available on Node.js.
:::

## Setup

### 1. Install the Tigris SDK

Install the SDK as follows

```bash npm2yarn
npm install -S @tigrisdata/vector
```

### 2. Fetch Tigris API credentials

You can sign up for a free Tigris account [here](https://www.tigrisdata.com/).

Once you have signed up for the Tigris account, create a new project called `vectordemo`.
Next, make a note of the `clientId` and `clientSecret`, which you can get from the
Application Keys section of the project.

## Index docs

import IntegrationInstallTooltip from "@mdx_components/integration_install_tooltip.mdx";

<IntegrationInstallTooltip></IntegrationInstallTooltip>

```bash npm2yarn
npm install -S @langchain/openai
```

```typescript
import { VectorDocumentStore } from "@tigrisdata/vector";
import { Document } from "langchain/document";
import { OpenAIEmbeddings } from "@langchain/openai";
import { TigrisVectorStore } from "langchain/vectorstores/tigris";

const index = new VectorDocumentStore({
  connection: {
    serverUrl: "api.preview.tigrisdata.cloud",
    projectName: process.env.TIGRIS_PROJECT,
    clientId: process.env.TIGRIS_CLIENT_ID,
    clientSecret: process.env.TIGRIS_CLIENT_SECRET,
  },
  indexName: "examples_index",
  numDimensions: 1536, // match the OpenAI embedding size
});

const docs = [
  new Document({
    metadata: { foo: "bar" },
    pageContent: "tigris is a cloud-native vector db",
  }),
  new Document({
    metadata: { foo: "bar" },
    pageContent: "the quick brown fox jumped over the lazy dog",
  }),
  new Document({
    metadata: { baz: "qux" },
    pageContent: "lorem ipsum dolor sit amet",
  }),
  new Document({
    metadata: { baz: "qux" },
    pageContent: "tigris is a river",
  }),
];

await TigrisVectorStore.fromDocuments(docs, new OpenAIEmbeddings(), { index });
```

## Query docs

import Search from "@examples/indexes/vector_stores/tigris/search.ts";

```typescript
import { VectorDocumentStore } from "@tigrisdata/vector";
import { OpenAIEmbeddings } from "@langchain/openai";
import { TigrisVectorStore } from "langchain/vectorstores/tigris";

const index = new VectorDocumentStore({
  connection: {
    serverUrl: "api.preview.tigrisdata.cloud",
    projectName: process.env.TIGRIS_PROJECT,
    clientId: process.env.TIGRIS_CLIENT_ID,
    clientSecret: process.env.TIGRIS_CLIENT_SECRET,
  },
  indexName: "examples_index",
  numDimensions: 1536, // match the OpenAI embedding size
});

const vectorStore = await TigrisVectorStore.fromExistingIndex(
  new OpenAIEmbeddings(),
  { index }
);

/* Search the vector DB independently with metadata filters */
const results = await vectorStore.similaritySearch("tigris", 1, {
  "metadata.foo": "bar",
});
console.log(JSON.stringify(results, null, 2));
/*
[
  Document {
    pageContent: 'tigris is a cloud-native vector db',
    metadata: { foo: 'bar' }
  }
]
*/
```

## Related

- Vector store [conceptual guide](/docs/concepts/#vectorstores)
- Vector store [how-to guides](/docs/how_to/#vectorstores)


# libSQL

[Turso](https://turso.tech) is a SQLite-compatible database built on [libSQL](https://docs.turso.tech/libsql), the Open Contribution fork of SQLite. Vector Similiarity Search is built into Turso and libSQL as a native datatype, enabling you to store and query vectors directly in the database.

LangChain.js supports using a local libSQL, or remote Turso database as a vector store, and provides a simple API to interact with it.

This guide provides a quick overview for getting started with libSQL vector stores. For detailed documentation of all libSQL features and configurations head to the API reference.

## Overview

## Integration details

| Class               | Package                | JS support | Package latest                                                    |
| ------------------- | ---------------------- | ---------- | ----------------------------------------------------------------- |
| `LibSQLVectorStore` | `@langchain/community` | ✅         | ![npm version](https://img.shields.io/npm/v/@langchain/community) |

## Setup

To use libSQL vector stores, you'll need to create a Turso account or set up a local SQLite database, and install the `@langchain/community` integration package.

This guide will also use OpenAI embeddings, which require you to install the `@langchain/openai` integration package. You can also use other supported embeddings models if you wish.

You can use local SQLite when working with the libSQL vector store, or use a hosted Turso Database.

import IntegrationInstallTooltip from "@mdx_components/integration_install_tooltip.mdx";

<IntegrationInstallTooltip></IntegrationInstallTooltip>

```bash npm2yarn
npm install @libsql/client @langchain/openai @langchain/community
```

Now it's time to create a database. You can create one locally, or use a hosted Turso database.

### Local libSQL

Create a new local SQLite file and connect to the shell:

```bash
sqlite3 file.db
```

### Hosted Turso

Visit [sqlite.new](https://sqlite.new) to create a new database, give it a name, and create a database auth token.

Make sure to copy the database auth token, and the database URL, it should look something like:

```text
libsql://[database-name]-[your-username].turso.io
```

### Setup the table and index

Execute the following SQL command to create a new table or add the embedding column to an existing table.

Make sure to mopdify the following parts of the SQL:

- `TABLE_NAME` is the name of the table you want to create.
- `content` is used to store the `Document.pageContent` values.
- `metadata` is used to store the `Document.metadata` object.
- `EMBEDDING_COLUMN` is used to store the vector values, use the dimensions size used by the model you plan to use (1536 for OpenAI).

```sql
CREATE TABLE IF NOT EXISTS TABLE_NAME (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    content TEXT,
    metadata TEXT,
    EMBEDDING_COLUMN F32_BLOB(1536) -- 1536-dimensional f32 vector for OpenAI
);
```

Now create an index on the `EMBEDDING_COLUMN` column:

```sql
CREATE INDEX IF NOT EXISTS idx_TABLE_NAME_EMBEDDING_COLUMN ON TABLE_NAME(libsql_vector_idx(EMBEDDING_COLUMN));
```

Make sure to replace the `TABLE_NAME` and `EMBEDDING_COLUMN` with the values you used in the previous step.

## Instantiation

To initialize a new `LibSQL` vector store, you need to provide the database URL and Auth Token when working remotely, or by passing the filename for a local SQLite.

```typescript
import { LibSQLVectorStore } from "@langchain/community/vectorstores/libsql";
import { OpenAIEmbeddings } from "@langchain/openai";
import { createClient } from "@libsql/client";

const embeddings = new OpenAIEmbeddings({
  model: "text-embedding-3-small",
});

const libsqlClient = createClient({
  url: "libsql://[database-name]-[your-username].turso.io",
  authToken: "...",
});

// Local instantiation
// const libsqlClient = createClient({
//  url: "file:./dev.db",
// });

const vectorStore = new LibSQLVectorStore(embeddings, {
  db: libsqlClient,
  tableName: "TABLE_NAME",
  embeddingColumn: "EMBEDDING_COLUMN",
  dimensions: 1536,
});
```

## Manage vector store

### Add items to vector store

```typescript
import type { Document } from "@langchain/core/documents";

const documents: Document[] = [
  { pageContent: "Hello", metadata: { topic: "greeting" } },
  { pageContent: "Bye bye", metadata: { topic: "greeting" } },
];

await vectorStore.addDocuments(documents);
```

### Delete items from vector store

```typescript
await vectorStore.deleteDocuments({ ids: [1, 2] });
```

## Query vector store

Once you have inserted the documents, you can query the vector store.

### Query directly

Performing a simple similarity search can be done as follows:

```typescript
const resultOne = await vectorStore.similaritySearch("hola", 1);

for (const doc of similaritySearchResults) {
  console.log(`${doc.pageContent} [${JSON.stringify(doc.metadata, null)}]`);
}
```

For similarity search with scores:

```typescript
const similaritySearchWithScoreResults =
  await vectorStore.similaritySearchWithScore("hola", 1);

for (const [doc, score] of similaritySearchWithScoreResults) {
  console.log(
    `${score.toFixed(3)} ${doc.pageContent} [${JSON.stringify(doc.metadata)}`
  );
}
```

## API reference

For detailed documentation of all `LibSQLVectorStore` features and configurations head to the API reference.

## Related

- Vector store [conceptual guide](/docs/concepts/#vectorstores)
- Vector store [how-to guides](/docs/how_to/#vectorstores)


"  pageContent: \"The powerhouse of the cell is the mitochondria\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "};\n",
    "\n",
    "const document2: Document = {\n",
    "  pageContent: \"Buildings are made out of brick\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "};\n",
    "\n",
    "const document3: Document = {\n",
    "  pageContent: \"Mitochondria are made out of lipids\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "};\n",
    "\n",
    "const document4: Document = {\n",
    "  pageContent: \"The 2024 Olympics are in Paris\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "}\n",
    "\n",
    "const documents = [document1, document2, document3, document4];\n",
    "\n",
    "await vectorStore.addDocuments(documents, { ids: [\"1\", \"2\", \"3\", \"4\"] });"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dcf1b905",
   "metadata": {},
   "source": [
    "### Delete items from vector store"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "ef61e188",
   "metadata": {},
   "outputs": [],
   "source": [
    "await vectorStore.delete({ ids: [\"4\"] });"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3620501",
   "metadata": {},
   "source": [
    "## Query vector store\n",
    "\n",
    "Once your vector store has been created and the relevant documents have been added you will most likely wish to query it during the running of your chain or agent. \n",
    "\n",
    "### Query directly\n",
    "\n",
    "Performing a simple similarity search can be done as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "aa0a16fa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "* The powerhouse of the cell is the mitochondria [{\"source\":\"https://example.com\"}]\n",
      "* Mitochondria are made out of lipids [{\"source\":\"https://example.com\"}]\n"
     ]
    }
   ],
   "source": [
    "const filter = { source: \"https://example.com\" };\n",
    "\n",
    "const similaritySearchResults = await vectorStore.similaritySearch(\"biology\", 2, filter);\n",
    "\n",
    "for (const doc of similaritySearchResults) {\n",
    "  console.log(`* ${doc.pageContent} [${JSON.stringify(doc.metadata, null)}]`);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ed9d733",
   "metadata": {},
   "source": [
    "If you want to execute a similarity search and receive the corresponding scores you can run:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "5efd2eaa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "* [SIM=0.165] The powerhouse of the cell is the mitochondria [{\"source\":\"https://example.com\"}]\n",
      "* [SIM=0.148] Mitochondria are made out of lipids [{\"source\":\"https://example.com\"}]\n"
     ]
    }
   ],
   "source": [
    "const similaritySearchWithScoreResults = await vectorStore.similaritySearchWithScore(\"biology\", 2, filter)\n",
    "\n",
    "for (const [doc, score] of similaritySearchWithScoreResults) {\n",
    "  console.log(`* [SIM=${score.toFixed(3)}] ${doc.pageContent} [${JSON.stringify(doc.metadata)}]`);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "180b0e66",
   "metadata": {},
   "source": [
    "### Metadata Query Builder Filtering\n",
    "\n",
    "You can also use query builder-style filtering similar to how the [Supabase JavaScript library](https://supabase.com/docs/reference/javascript/using-filters) works instead of passing an object. Note that since most of the filter properties are in the metadata column, you need to use arrow operators (-> for integer or ->> for text) as defined in [Postgrest API documentation](https://postgrest.org/en/stable/references/api/tables_views.html#json-columns) and specify the data type of the property (e.g. the column should look something like `metadata->some_int_prop_name::int`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "e3287768",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "* The powerhouse of the cell is the mitochondria [{\"source\":\"https://example.com\"}]\n",
      "* Mitochondria are made out of lipids [{\"source\":\"https://example.com\"}]\n"
     ]
    }
   ],
   "source": [
    "import { SupabaseFilterRPCCall } from \"@langchain/community/vectorstores/supabase\";\n",
    "\n",
    "const funcFilter: SupabaseFilterRPCCall = (rpc) =>\n",
    "  rpc.filter(\"metadata->>source\", \"eq\", \"https://example.com\");\n",
    "\n",
    "const funcFilterSearchResults = await vectorStore.similaritySearch(\"biology\", 2, funcFilter);\n",
    "\n",
    "for (const doc of funcFilterSearchResults) {\n",
    "  console.log(`* ${doc.pageContent} [${JSON.stringify(doc.metadata, null)}]`);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c235cdc",
   "metadata": {},
   "source": [
    "### Query by turning into retriever\n",
    "\n",
    "You can also transform the vector store into a [retriever](/docs/concepts/#retrievers) for easier usage in your chains. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "f3460093",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[\n",
      "  Document {\n",
      "    pageContent: 'The powerhouse of the cell is the mitochondria',\n",
      "    metadata: { source: 'https://example.com' },\n",
      "    id: undefined\n",
      "  },\n",
      "  Document {\n",
      "    pageContent: 'Mitochondria are made out of lipids',\n",
      "    metadata: { source: 'https://example.com' },\n",
      "    id: undefined\n",
      "  }\n",
      "]\n"
     ]
    }
   ],
   "source": [
    "const retriever = vectorStore.asRetriever({\n",
    "  // Optional filter\n",
    "  filter: filter,\n",
    "  k: 2,\n",
    "});\n",
    "await retriever.invoke(\"biology\");"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2e0a211",
   "metadata": {},
   "source": [
    "### Usage for retrieval-augmented generation\n",
    "\n",
    "For guides on how to use this vector store for retrieval-augmented generation (RAG), see the following sections:\n",
    "\n",
    "- [Tutorials: working with external knowledge](/docs/tutorials/#working-with-external-knowledge).\n",
    "- [How-to: Question and answer with RAG](/docs/how_to/#qa-with-rag)\n",
    "- [Retrieval conceptual docs](/docs/concepts#retrieval)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a27244f",
   "metadata": {},
   "source": [
    "## API reference\n",
    "\n",
    "For detailed documentation of all `SupabaseVectorStore` features and configurations head to the [API reference](https://api.js.langchain.com/classes/langchain_community_vectorstores_supabase.SupabaseVectorStore.html)."


You can specify the fields to return from the document using `fields` parameter in the filter during searches.
These fields are returned as part of the `metadata` object. You can fetch any field that is stored in the index.
The `textKey` of the document is returned as part of the document's `pageContent`.

If you do not specify any fields to be fetched, all the fields stored in the index are returned.

If you want to fetch one of the fields in the metadata, you need to specify it using `.`
For example, to fetch the `source` field in the metadata, you need to use `metadata.source`.

```typescript
const result = await store.similaritySearch(query, 1, {
  fields: ["metadata.source"],
});
console.log(result[0]);
```

## Hybrid Search

Couchbase allows you to do hybrid searches by combining vector search results with searches on non-vector fields of the document like the `metadata` object.

The results will be based on the combination of the results from both vector search and the searches supported by full text search service.
The scores of each of the component searches are added up to get the total score of the result.

To perform hybrid searches, there is an optional key, `searchOptions` in `fields` parameter that can be passed to all the similarity searches.
The different search/query possibilities for the `searchOptions` can be found [here](https://docs.couchbase.com/server/current/search/search-request-params.html#query-object).

### Create Diverse Metadata for Hybrid Search

In order to simulate hybrid search, let us create some random metadata from the existing documents.
We uniformly add three fields to the metadata, `date` between 2010 & 2020, `rating` between 1 & 5 and `author` set to either John Doe or Jane Doe.
We will also declare few sample queries.

```typescript
for (let i = 0; i < docs.length; i += 1) {
  docs[i].metadata.date = `${2010 + (i % 10)}-01-01`;
  docs[i].metadata.rating = 1 + (i % 5);
  docs[i].metadata.author = ["John Doe", "Jane Doe"][i % 2];
}

const store = await CouchbaseVectorStore.fromDocuments(
  docs,
  embeddings,
  couchbaseConfig
);

const query = "What did the president say about Ketanji Brown Jackson";
const independenceQuery = "Any mention about independence?";
```

### Example: Search by Exact Value

We can search for exact matches on a textual field like the author in the `metadata` object.

```typescript
const exactValueResult = await store.similaritySearch(query, 4, {
  fields: ["metadata.author"],
  searchOptions: {
    query: { field: "metadata.author", match: "John Doe" },
  },
});
console.log(exactValueResult[0]);
```

### Example: Search by Partial Match

We can search for partial matches by specifying a fuzziness for the search. This is useful when you want to search for slight variations or misspellings of a search query.

Here, "Johny" is close (fuzziness of 1) to "John Doe".

```typescript
const partialMatchResult = await store.similaritySearch(query, 4, {
  fields: ["metadata.author"],
  searchOptions: {
    query: { field: "metadata.author", match: "Johny", fuzziness: 1 },
  },
});
console.log(partialMatchResult[0]);
```

### Example: Search by Date Range Query

We can search for documents that are within a date range query on a date field like `metadata.date`.

```typescript
const dateRangeResult = await store.similaritySearch(independenceQuery, 4, {
  fields: ["metadata.date", "metadata.author"],
  searchOptions: {
    query: {
      start: "2016-12-31",
      end: "2017-01-02",
      inclusiveStart: true,
      inclusiveEnd: false,
      field: "metadata.date",
    },
  },
});
console.log(dateRangeResult[0]);
```

### Example: Search by Numeric Range Query

We can search for documents that are within a range for a numeric field like `metadata.rating`.

```typescript
const ratingRangeResult = await store.similaritySearch(independenceQuery, 4, {
  fields: ["metadata.rating"],
  searchOptions: {
    query: {
      min: 3,
      max: 5,
      inclusiveMin: false,
      inclusiveMax: true,
      field: "metadata.rating",
    },
  },
});
console.log(ratingRangeResult[0]);
```

### Example: Combining Multiple Search Conditions

Different queries can by combined using AND (conjuncts) or OR (disjuncts) operators.

In this example, we are checking for documents with a rating between 3 & 4 and dated between 2015 & 2018.

```typescript
const multipleConditionsResult = await store.similaritySearch(texts[0], 4, {
  fields: ["metadata.rating", "metadata.date"],
  searchOptions: {
    query: {
      conjuncts: [
        { min: 3, max: 4, inclusive_max: true, field: "metadata.rating" },
        { start: "2016-12-31", end: "2017-01-02", field: "metadata.date" },
      ],
    },
  },
});
console.log(multipleConditionsResult[0]);
```

### Other Queries

Similarly, you can use any of the supported Query methods like Geo Distance, Polygon Search, Wildcard, Regular Expressions, etc in the `searchOptions` Key of `filter` parameter.
Please refer to the documentation for more details on the available query methods and their syntax.

- [Couchbase Capella](https://docs.couchbase.com/cloud/search/search-request-params.html#query-object)
- [Couchbase Server](https://docs.couchbase.com/server/current/search/search-request-params.html#query-object)

# Frequently Asked Questions

## Question: Should I create the Search index before creating the CouchbaseVectorStore object?

Yes, currently you need to create the Search index before creating the `CouchbaseVectorStore` object.

## Question: I am not seeing all the fields that I specified in my search results.

In Couchbase, we can only return the fields stored in the Search index. Please ensure that the field that you are trying to access in the search results is part of the Search index.

One way to handle this is to index and store a document's fields dynamically in the index.

- In Capella, you need to go to "Advanced Mode" then under the chevron "General Settings" you can check "[X] Store Dynamic Fields" or "[X] Index Dynamic Fields"
- In Couchbase Server, in the Index Editor (not Quick Editor) under the chevron "Advanced" you can check "[X] Store Dynamic Fields" or "[X] Index Dynamic Fields"

Note that these options will increase the size of the index.

For more details on dynamic mappings, please refer to the [documentation](https://docs.couchbase.com/cloud/search/customize-index.html).

## Question: I am unable to see the metadata object in my search results.

This is most likely due to the `metadata` field in the document not being indexed and/or stored by the Couchbase Search index. In order to index the `metadata` field in the document, you need to add it to the index as a child mapping.

If you select to map all the fields in the mapping, you will be able to search by all metadata fields. Alternatively, to optimize the index, you can select the specific fields inside `metadata` object to be indexed.
You can refer to the [docs](https://docs.couchbase.com/cloud/search/customize-index.html) to learn more about indexing child mappings.

To create Child Mappings, you can refer to the following docs -

- [Couchbase Capella](https://docs.couchbase.com/cloud/search/create-child-mapping.html)
- [Couchbase Server](https://docs.couchbase.com/server/current/fts/fts-creating-index-from-UI-classic-editor-dynamic.html)

## Related

- Vector store [conceptual guide](/docs/concepts/#vectorstores)
- Vector store [how-to guides](/docs/how_to/#vectorstores)


{
 "cells": [
  {
   "cell_type": "raw",
   "id": "1957f5cb",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "---\n",
    "sidebar_label: MongoDB Atlas\n",
    "sidebar_class_name: node-only\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef1f0986",
   "metadata": {},
   "source": [
    "# MongoDB Atlas\n",
    "\n",
    "```{=mdx}\n",
    ":::tip Compatibility\n",
    "Only available on Node.js.\n",
    "\n",
    "You can still create API routes that use MongoDB with Next.js by setting the `runtime` variable to `nodejs` like so:\n",
    "\n",
    "`export const runtime = \"nodejs\";`\n",
    "\n",
    "You can read more about Edge runtimes in the Next.js documentation [here](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes).\n",
    ":::\n",
    "```\n",
    "\n",
    "This guide provides a quick overview for getting started with MongoDB Atlas [vector stores](/docs/concepts/#vectorstores). For detailed documentation of all `MongoDBAtlasVectorSearch` features and configurations head to the [API reference](https://api.js.langchain.com/classes/langchain_mongodb.MongoDBAtlasVectorSearch.html)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c824838d",
   "metadata": {},
   "source": [
    "## Overview\n",
    "\n",
    "### Integration details\n",
    "\n",
    "| Class | Package | [PY support](https://python.langchain.com/docs/integrations/vectorstores/mongodb_atlas/) |  Package latest |\n",
    "| :--- | :--- | :---: | :---: |\n",
    "| [`MongoDBAtlasVectorSearch`](https://api.js.langchain.com/classes/langchain_mongodb.MongoDBAtlasVectorSearch.html) | [`@langchain/mongodb`](https://www.npmjs.com/package/@langchain/mongodb) | ✅ | ![NPM - Version](https://img.shields.io/npm/v/@langchain/mongodb?style=flat-square&label=%20&) |"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36fdc060",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "To use MongoDB Atlas vector stores, you'll need to configure a MongoDB Atlas cluster and install the `@langchain/mongodb` integration package.\n",
    "\n",
    "### Initial Cluster Configuration\n",
    "\n",
    "To create a MongoDB Atlas cluster, navigate to the [MongoDB Atlas website](https://www.mongodb.com/products/platform/atlas-database) and create an account if you don't already have one.\n",
    "\n",
    "Create and name a cluster when prompted, then find it under `Database`. Select `Browse Collections` and create either a blank collection or one from the provided sample data.\n",
    "\n",
    "**Note:** The cluster created must be MongoDB 7.0 or higher.\n",
    "\n",
    "### Creating an Index\n",
    "\n",
    "After configuring your cluster, you'll need to create an index on the collection field you want to search over.\n",
    "\n",
    "Switch to the `Atlas Search` tab and click `Create Search Index`. From there, make sure you select `Atlas Vector Search - JSON Editor`, then select the appropriate database and collection and paste the following into the textbox:\n",
    "\n",
    "```json\n",
    "{\n",
    "  \"fields\": [\n",
    "    {\n",
    "      \"numDimensions\": 1536,\n",
    "      \"path\": \"embedding\",\n",
    "      \"similarity\": \"euclidean\",\n",
    "      \"type\": \"vector\"\n",
    "    }\n",
    "  ]\n",
    "}\n",
    "```\n",
    "\n",
    "Note that the dimensions property should match the dimensionality of the embeddings you are using. For example, Cohere embeddings have 1024 dimensions, and by default OpenAI embeddings have 1536:\n",
    "\n",
    "Note: By default the vector store expects an index name of default, an indexed collection field name of embedding, and a raw text field name of text. You should initialize the vector store with field names matching your index name collection schema as shown below.\n",
    "\n",
    "Finally, proceed to build the index.\n",
    "\n",
    "### Embeddings\n",
    "\n",
    "This guide will also use [OpenAI embeddings](/docs/integrations/text_embedding/openai), which require you to install the `@langchain/openai` integration package. You can also use [other supported embeddings models](/docs/integrations/text_embedding) if you wish.\n",
    "\n",
    "### Installation\n",
    "\n",
    "Install the following packages:\n",
    "\n",
    "```{=mdx}\n",
    "import IntegrationInstallTooltip from \"@mdx_components/integration_install_tooltip.mdx\";\n",
    "import Npm2Yarn from \"@theme/Npm2Yarn\";\n",
    "\n",
    "<IntegrationInstallTooltip></IntegrationInstallTooltip>\n",
    "\n",
    "<Npm2Yarn>\n",
    "  @langchain/mongodb mongodb @langchain/openai @langchain/core\n",
    "</Npm2Yarn>\n",
    "```\n",
    "\n",
    "### Credentials\n",
    "\n",
    "Once you've done the above, set the `MONGODB_ATLAS_URI` environment variable from the `Connect` button in Mongo's dashboard. You'll also need your DB name and collection name:\n",
    "\n",
    "```typescript\n",
    "process.env.MONGODB_ATLAS_URI = \"your-atlas-url\";\n",
    "process.env.MONGODB_ATLAS_COLLECTION_NAME = \"your-atlas-db-name\";\n",
    "process.env.MONGODB_ATLAS_DB_NAME = \"your-atlas-db-name\";\n",
    "```\n",
    "\n",
    "If you are using OpenAI embeddings for this guide, you'll need to set your OpenAI key as well:\n",
    "\n",
    "```typescript\n",
    "process.env.OPENAI_API_KEY = \"YOUR_API_KEY\";\n",
    "```\n",
    "\n",
    "If you want to get automated tracing of your model calls you can also set your [LangSmith](https://docs.smith.langchain.com/) API key by uncommenting below:\n",
    "\n",
    "```typescript\n",
    "// process.env.LANGCHAIN_TRACING_V2=\"true\"\n",
    "// process.env.LANGCHAIN_API_KEY=\"your-api-key\"\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93df377e",
   "metadata": {},
   "source": [
    "## Instantiation\n",
    "\n",
    "Once you've set up your cluster as shown above, you can initialize your vector store as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "dc37144c-208d-4ab3-9f3a-0407a69fe052",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import { MongoDBAtlasVectorSearch } from \"@langchain/mongodb\";\n",
    "import { OpenAIEmbeddings } from \"@langchain/openai\";\n",
    "import { MongoClient } from \"mongodb\";\n",
    "\n",
    "const client = new MongoClient(process.env.MONGODB_ATLAS_URI || \"\");\n",
    "const collection = client.db(process.env.MONGODB_ATLAS_DB_NAME)\n",
    "  .collection(process.env.MONGODB_ATLAS_COLLECTION_NAME);\n",
    "\n",
    "const embeddings = new OpenAIEmbeddings({\n",
    "  model: \"text-embedding-3-small\",\n",
    "});\n",
    "\n",
    "const vectorStore = new MongoDBAtlasVectorSearch(embeddings, {\n",
    "  collection: collection,\n",
    "  indexName: \"vector_index\", // The name of the Atlas search index. Defaults to \"default\"\n",
    "  textKey: \"text\", // The name of the collection field containing the raw content. Defaults to \"text\"\n",
    "  embeddingKey: \"embedding\", // The name of the collection field containing the embedded text. Defaults to \"embedding\"\n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac6071d4",
   "metadata": {},
   "source": [
    "## Manage vector store\n",
    "\n",
    "### Add items to vector store\n",


"\n",
    "You can now add documents to your vector store:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "17f5efc0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ '1', '2', '3', '4' ]\n"
     ]
    }
   ],
   "source": [
    "import type { Document } from \"@langchain/core/documents\";\n",
    "\n",
    "const document1: Document = {\n",
    "  pageContent: \"The powerhouse of the cell is the mitochondria\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "};\n",
    "\n",
    "const document2: Document = {\n",
    "  pageContent: \"Buildings are made out of brick\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "};\n",
    "\n",
    "const document3: Document = {\n",
    "  pageContent: \"Mitochondria are made out of lipids\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "};\n",
    "\n",
    "const document4: Document = {\n",
    "  pageContent: \"The 2024 Olympics are in Paris\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "}\n",
    "\n",
    "const documents = [document1, document2, document3, document4];\n",
    "\n",
    "await vectorStore.addDocuments(documents, { ids: [\"1\", \"2\", \"3\", \"4\"] });"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dcf1b905",
   "metadata": {},
   "source": [
    "**Note:** After adding documents, there is a slight delay before they become queryable.\n",
    "\n",
    "Adding a document with the same `id` as an existing document will update the existing one.\n",
    "\n",
    "### Delete items from vector store"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "ef61e188",
   "metadata": {},
   "outputs": [],
   "source": [
    "await vectorStore.delete({ ids: [\"4\"] });"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3620501",
   "metadata": {},
   "source": [
    "## Query vector store\n",
    "\n",
    "Once your vector store has been created and the relevant documents have been added you will most likely wish to query it during the running of your chain or agent. \n",
    "\n",
    "### Query directly\n",
    "\n",
    "Performing a simple similarity search can be done as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "aa0a16fa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "* The powerhouse of the cell is the mitochondria [{\"_id\":\"1\",\"source\":\"https://example.com\"}]\n",
      "* Mitochondria are made out of lipids [{\"_id\":\"3\",\"source\":\"https://example.com\"}]\n"
     ]
    }
   ],
   "source": [
    "const similaritySearchResults = await vectorStore.similaritySearch(\"biology\", 2);\n",
    "\n",
    "for (const doc of similaritySearchResults) {\n",
    "  console.log(`* ${doc.pageContent} [${JSON.stringify(doc.metadata, null)}]`);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ed9d733",
   "metadata": {},
   "source": [
    "### Filtering\n",
    "\n",
    "MongoDB Atlas supports pre-filtering of results on other fields. They require you to define which metadata fields you plan to filter on by updating the index you created initially. Here's an example:\n",
    "\n",
    "```json\n",
    "{\n",
    "  \"fields\": [\n",
    "    {\n",
    "      \"numDimensions\": 1024,\n",
    "      \"path\": \"embedding\",\n",
    "      \"similarity\": \"euclidean\",\n",
    "      \"type\": \"vector\"\n",
    "    },\n",
    "    {\n",
    "      \"path\": \"source\",\n",
    "      \"type\": \"filter\"\n",
    "    }\n",
    "  ]\n",
    "}\n",
    "```\n",
    "\n",
    "Above, the first item in `fields` is the vector index, and the second item is the metadata property you want to filter on. The name of the property is the value of the `path` key. So the above index would allow us to search on a metadata field named `source`.\n",
    "\n",
    "Then, in your code you can use [MQL Query Operators](https://www.mongodb.com/docs/manual/reference/operator/query/) for filtering.\n",
    "\n",
    "The below example illustrates this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "bc8f242e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "* The powerhouse of the cell is the mitochondria [{\"_id\":\"1\",\"source\":\"https://example.com\"}]\n",
      "* Mitochondria are made out of lipids [{\"_id\":\"3\",\"source\":\"https://example.com\"}]\n"
     ]
    }
   ],
   "source": [
    "const filter = {\n",
    "  preFilter: {\n",
    "    source: {\n",
    "      $eq: \"https://example.com\",\n",
    "    },\n",
    "  },\n",
    "}\n",
    "\n",
    "const filteredResults = await vectorStore.similaritySearch(\"biology\", 2, filter);\n",
    "\n",
    "for (const doc of filteredResults) {\n",
    "  console.log(`* ${doc.pageContent} [${JSON.stringify(doc.metadata, null)}]`);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69326bba",
   "metadata": {},
   "source": [
    "### Returning scores\n",
    "\n",
    "If you want to execute a similarity search and receive the corresponding scores you can run:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "5efd2eaa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "* [SIM=0.374] The powerhouse of the cell is the mitochondria [{\"_id\":\"1\",\"source\":\"https://example.com\"}]\n",
      "* [SIM=0.370] Mitochondria are made out of lipids [{\"_id\":\"3\",\"source\":\"https://example.com\"}]\n"
     ]
    }
   ],
   "source": [
    "const similaritySearchWithScoreResults = await vectorStore.similaritySearchWithScore(\"biology\", 2, filter)\n",
    "\n",
    "for (const [doc, score] of similaritySearchWithScoreResults) {\n",
    "  console.log(`* [SIM=${score.toFixed(3)}] ${doc.pageContent} [${JSON.stringify(doc.metadata)}]`);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c235cdc",
   "metadata": {},
   "source": [
    "### Query by turning into retriever\n",
    "\n",
    "You can also transform the vector store into a [retriever](/docs/concepts/#retrievers) for easier usage in your chains. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "f3460093",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[\n",
      "  Document {\n",
      "    pageContent: 'The powerhouse of the cell is the mitochondria',\n",
      "    metadata: { _id: '1', source: 'https://example.com' },\n",
      "    id: undefined\n",
      "  },\n",
      "  Document {\n",


{
 "cells": [
  {
   "cell_type": "raw",
   "id": "1957f5cb",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "---\n",
    "sidebar_label: Faiss\n",
    "sidebar_class_name: node-only\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef1f0986",
   "metadata": {},
   "source": [
    "# FaissStore\n",
    "\n",
    "```{=mdx}\n",
    "\n",
    ":::tip Compatibility\n",
    "Only available on Node.js.\n",
    ":::\n",
    "\n",
    "```\n",
    "\n",
    "[Faiss](https://github.com/facebookresearch/faiss) is a library for efficient similarity search and clustering of dense vectors.\n",
    "\n",
    "LangChain.js supports using Faiss as a locally-running vectorstore that can be saved to a file. It also provides the ability to read the saved file from the [LangChain Python implementation](https://python.langchain.com/docs/integrations/vectorstores/faiss#saving-and-loading).\n",
    "\n",
    "This guide provides a quick overview for getting started with Faiss [vector stores](/docs/concepts/#vectorstores). For detailed documentation of all `FaissStore` features and configurations head to the [API reference](https://api.js.langchain.com/classes/langchain_community_vectorstores_faiss.FaissStore.html)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c824838d",
   "metadata": {},
   "source": [
    "## Overview\n",
    "\n",
    "### Integration details\n",
    "\n",
    "| Class | Package | [PY support](https://python.langchain.com/docs/integrations/vectorstores/faiss) |  Package latest |\n",
    "| :--- | :--- | :---: | :---: |\n",
    "| [`FaissStore`](https://api.js.langchain.com/classes/langchain_community_vectorstores_faiss.FaissStore.html) | [`@langchain/community`](https://npmjs.com/@langchain/community) | ✅ |  ![NPM - Version](https://img.shields.io/npm/v/@langchain/community?style=flat-square&label=%20&) |"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36fdc060",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "To use Faiss vector stores, you'll need to install the `@langchain/community` integration package and the [`faiss-node`](https://github.com/ewfian/faiss-node) package as a peer dependency.\n",
    "\n",
    "This guide will also use [OpenAI embeddings](/docs/integrations/text_embedding/openai), which require you to install the `@langchain/openai` integration package. You can also use [other supported embeddings models](/docs/integrations/text_embedding) if you wish.\n",
    "\n",
    "```{=mdx}\n",
    "import IntegrationInstallTooltip from \"@mdx_components/integration_install_tooltip.mdx\";\n",
    "import Npm2Yarn from \"@theme/Npm2Yarn\";\n",
    "\n",
    "<IntegrationInstallTooltip></IntegrationInstallTooltip>\n",
    "\n",
    "<Npm2Yarn>\n",
    "  @langchain/community faiss-node @langchain/openai @langchain/core\n",
    "</Npm2Yarn>\n",
    "```\n",
    "\n",
    "### Credentials\n",
    "\n",
    "Because Faiss runs locally, you do not need any credentials to use it.\n",
    "\n",
    "If you are using OpenAI embeddings for this guide, you'll need to set your OpenAI key as well:\n",
    "\n",
    "```typescript\n",
    "process.env.OPENAI_API_KEY = \"YOUR_API_KEY\";\n",
    "```\n",
    "\n",
    "If you want to get automated tracing of your model calls you can also set your [LangSmith](https://docs.smith.langchain.com/) API key by uncommenting below:\n",
    "\n",
    "```typescript\n",
    "// process.env.LANGCHAIN_TRACING_V2=\"true\"\n",
    "// process.env.LANGCHAIN_API_KEY=\"your-api-key\"\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93df377e",
   "metadata": {},
   "source": [
    "## Instantiation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "dc37144c-208d-4ab3-9f3a-0407a69fe052",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import { FaissStore } from \"@langchain/community/vectorstores/faiss\";\n",
    "import { OpenAIEmbeddings } from \"@langchain/openai\";\n",
    "\n",
    "const embeddings = new OpenAIEmbeddings({\n",
    "  model: \"text-embedding-3-small\",\n",
    "});\n",
    "\n",
    "const vectorStore = new FaissStore(embeddings, {});"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac6071d4",
   "metadata": {},
   "source": [
    "## Manage vector store\n",
    "\n",
    "### Add items to vector store"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "17f5efc0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ '1', '2', '3', '4' ]\n"
     ]
    }
   ],
   "source": [
    "import type { Document } from \"@langchain/core/documents\";\n",
    "\n",
    "const document1: Document = {\n",
    "  pageContent: \"The powerhouse of the cell is the mitochondria\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "};\n",
    "\n",
    "const document2: Document = {\n",
    "  pageContent: \"Buildings are made out of brick\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "};\n",
    "\n",
    "const document3: Document = {\n",
    "  pageContent: \"Mitochondria are made out of lipids\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "};\n",
    "\n",
    "const document4: Document = {\n",
    "  pageContent: \"The 2024 Olympics are in Paris\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "}\n",
    "\n",
    "const documents = [document1, document2, document3, document4];\n",
    "\n",
    "await vectorStore.addDocuments(documents, { ids: [\"1\", \"2\", \"3\", \"4\"] });"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dcf1b905",
   "metadata": {},
   "source": [
    "### Delete items from vector store"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ef61e188",
   "metadata": {},
   "outputs": [],
   "source": [
    "await vectorStore.delete({ ids: [\"4\"] });"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3620501",
   "metadata": {},
   "source": [
    "## Query vector store\n",
    "\n",
    "Once your vector store has been created and the relevant documents have been added you will most likely wish to query it during the running of your chain or agent. \n",
    "\n",
    "### Query directly\n",
    "\n",
    "Performing a simple similarity search can be done as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "aa0a16fa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "* The powerhouse of the cell is the mitochondria [{\"source\":\"https://example.com\"}]\n",
      "* Mitochondria are made out of lipids [{\"source\":\"https://example.com\"}]\n"
     ]
    }
   ],
   "source": [
    "const similaritySearchResults = await vectorStore.similaritySearch(\"biology\", 2);\n",


{
 "cells": [
  {
   "cell_type": "raw",
   "id": "1957f5cb",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "---\n",
    "sidebar_label: Pinecone\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef1f0986",
   "metadata": {},
   "source": [
    "# PineconeStore\n",
    "\n",
    "[Pinecone](https://www.pinecone.io/) is a vector database that helps power AI for some of the world’s best companies.\n",
    "\n",
    "This guide provides a quick overview for getting started with Pinecone [vector stores](/docs/concepts/#vectorstores). For detailed documentation of all `PineconeStore` features and configurations head to the [API reference](https://api.js.langchain.com/classes/langchain_pinecone.PineconeStore.html)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c824838d",
   "metadata": {},
   "source": [
    "## Overview\n",
    "\n",
    "### Integration details\n",
    "\n",
    "| Class | Package | [PY support](https://python.langchain.com/docs/integrations/vectorstores/pinecone/) | Package latest |\n",
    "| :--- | :--- | :---: | :---: |\n",
    "| [`PineconeStore`](https://api.js.langchain.com/classes/langchain_pinecone.PineconeStore.html) | [`@langchain/pinecone`](https://npmjs.com/@langchain/pinecone) | ✅ |  ![NPM - Version](https://img.shields.io/npm/v/@langchain/pinecone?style=flat-square&label=%20&) |"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36fdc060",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "To use Pinecone vector stores, you'll need to create a Pinecone account, initialize an index, and install the `@langchain/pinecone` integration package. You'll also want to install the [official Pinecone SDK](https://www.npmjs.com/package/@pinecone-database/pinecone) to initialize a client to pass into the `PineconeStore` instance.\n",
    "\n",
    "This guide will also use [OpenAI embeddings](/docs/integrations/text_embedding/openai), which require you to install the `@langchain/openai` integration package. You can also use [other supported embeddings models](/docs/integrations/text_embedding) if you wish.\n",
    "\n",
    "```{=mdx}\n",
    "import IntegrationInstallTooltip from \"@mdx_components/integration_install_tooltip.mdx\";\n",
    "import Npm2Yarn from \"@theme/Npm2Yarn\";\n",
    "\n",
    "<IntegrationInstallTooltip></IntegrationInstallTooltip>\n",
    "\n",
    "<Npm2Yarn>\n",
    "  @langchain/pinecone @langchain/openai @langchain/core @pinecone-database/pinecone \n",
    "</Npm2Yarn>\n",
    "```\n",
    "\n",
    "### Credentials\n",
    "\n",
    "Sign up for a [Pinecone](https://www.pinecone.io/) account and create an index. Make sure the dimensions match those of the embeddings you want to use (the default is 1536 for OpenAI's `text-embedding-3-small`). Once you've done this set the `PINECONE_INDEX`, `PINECONE_API_KEY`, and (optionally) `PINECONE_ENVIRONMENT` environment variables:\n",
    "\n",
    "```typescript\n",
    "process.env.PINECONE_API_KEY = \"your-pinecone-api-key\";\n",
    "process.env.PINECONE_INDEX = \"your-pinecone-index\";\n",
    "\n",
    "// Optional\n",
    "process.env.PINECONE_ENVIRONMENT = \"your-pinecone-environment\";\n",
    "```\n",
    "\n",
    "If you are using OpenAI embeddings for this guide, you'll need to set your OpenAI key as well:\n",
    "\n",
    "```typescript\n",
    "process.env.OPENAI_API_KEY = \"YOUR_API_KEY\";\n",
    "```\n",
    "\n",
    "If you want to get automated tracing of your model calls you can also set your [LangSmith](https://docs.smith.langchain.com/) API key by uncommenting below:\n",
    "\n",
    "```typescript\n",
    "// process.env.LANGCHAIN_TRACING_V2=\"true\"\n",
    "// process.env.LANGCHAIN_API_KEY=\"your-api-key\"\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93df377e",
   "metadata": {},
   "source": [
    "## Instantiation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "dc37144c-208d-4ab3-9f3a-0407a69fe052",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import { PineconeStore } from \"@langchain/pinecone\";\n",
    "import { OpenAIEmbeddings } from \"@langchain/openai\";\n",
    "\n",
    "import { Pinecone as PineconeClient } from \"@pinecone-database/pinecone\";\n",
    "\n",
    "const embeddings = new OpenAIEmbeddings({\n",
    "  model: \"text-embedding-3-small\",\n",
    "});\n",
    "\n",
    "const pinecone = new PineconeClient();\n",
    "// Will automatically read the PINECONE_API_KEY and PINECONE_ENVIRONMENT env vars\n",
    "const pineconeIndex = pinecone.Index(process.env.PINECONE_INDEX!);\n",
    "\n",
    "const vectorStore = await PineconeStore.fromExistingIndex(\n",
    "  embeddings,\n",
    "  {\n",
    "    pineconeIndex,\n",
    "    // Maximum number of batch requests to allow at once. Each batch is 1000 vectors.\n",
    "    maxConcurrency: 5,\n",
    "    // You can pass a namespace here too\n",
    "    // namespace: \"foo\",\n",
    "  }\n",
    ");"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac6071d4",
   "metadata": {},
   "source": [
    "## Manage vector store\n",
    "\n",
    "### Add items to vector store"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "17f5efc0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ '1', '2', '3', '4' ]\n"
     ]
    }
   ],
   "source": [
    "import type { Document } from \"@langchain/core/documents\";\n",
    "\n",
    "const document1: Document = {\n",
    "  pageContent: \"The powerhouse of the cell is the mitochondria\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "};\n",
    "\n",
    "const document2: Document = {\n",
    "  pageContent: \"Buildings are made out of brick\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "};\n",
    "\n",
    "const document3: Document = {\n",
    "  pageContent: \"Mitochondria are made out of lipids\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "};\n",
    "\n",
    "const document4: Document = {\n",
    "  pageContent: \"The 2024 Olympics are in Paris\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "}\n",
    "\n",
    "const documents = [document1, document2, document3, document4];\n",
    "\n",
    "await vectorStore.addDocuments(documents, { ids: [\"1\", \"2\", \"3\", \"4\"] });"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dcf1b905",
   "metadata": {},
   "source": [
    "**Note:** After adding documents, there is a slight delay before they become queryable.\n",
    "\n",
    "### Delete items from vector store"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,


"  pageContent: \"Buildings are made out of brick\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "};\n",
    "\n",
    "const document3: Document = {\n",
    "  pageContent: \"Mitochondria are made out of lipids\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "};\n",
    "\n",
    "const document4: Document = {\n",
    "  pageContent: \"The 2024 Olympics are in Paris\",\n",
    "  metadata: { source: \"https://example.com\" }\n",
    "}\n",
    "\n",
    "const documents = [document1, document2, document3, document4];\n",
    "\n",
    "await vectorStore.addDocuments(documents, { ids: [\"1\", \"2\", \"3\", \"4\"] });"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dcf1b905",
   "metadata": {},
   "source": [
    "### Delete items from vector store\n",
    "\n",
    "You can delete values from the store by passing the same id you passed in:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "ef61e188",
   "metadata": {},
   "outputs": [],
   "source": [
    "await vectorStore.delete({ ids: [\"4\"] });"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3620501",
   "metadata": {},
   "source": [
    "## Query vector store\n",
    "\n",
    "Once your vector store has been created and the relevant documents have been added you will most likely wish to query it during the running of your chain or agent.\n",
    "\n",
    "### Query directly\n",
    "\n",
    "Performing a simple similarity search can be done as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "aa0a16fa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "* The powerhouse of the cell is the mitochondria [{\"source\":\"https://example.com\"}]\n",
      "* Mitochondria are made out of lipids [{\"source\":\"https://example.com\"}]\n"
     ]
    }
   ],
   "source": [
    "const filter = [{\n",
    "  operator: \"match\",\n",
    "  field: \"source\",\n",
    "  value: \"https://example.com\",\n",
    "}];\n",
    "\n",
    "const similaritySearchResults = await vectorStore.similaritySearch(\"biology\", 2, filter);\n",
    "\n",
    "for (const doc of similaritySearchResults) {\n",
    "  console.log(`* ${doc.pageContent} [${JSON.stringify(doc.metadata, null)}]`);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ed9d733",
   "metadata": {},
   "source": [
    "The vector store supports [Elasticsearch filter syntax](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html) operators.\n",
    "\n",
    "If you want to execute a similarity search and receive the corresponding scores you can run:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "5efd2eaa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "* [SIM=0.374] The powerhouse of the cell is the mitochondria [{\"source\":\"https://example.com\"}]\n",
      "* [SIM=0.370] Mitochondria are made out of lipids [{\"source\":\"https://example.com\"}]\n"
     ]
    }
   ],
   "source": [
    "const similaritySearchWithScoreResults = await vectorStore.similaritySearchWithScore(\"biology\", 2, filter)\n",
    "\n",
    "for (const [doc, score] of similaritySearchWithScoreResults) {\n",
    "  console.log(`* [SIM=${score.toFixed(3)}] ${doc.pageContent} [${JSON.stringify(doc.metadata)}]`);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c235cdc",
   "metadata": {},
   "source": [
    "### Query by turning into retriever\n",
    "\n",
    "You can also transform the vector store into a [retriever](/docs/concepts/#retrievers) for easier usage in your chains. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "f3460093",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[\n",
      "  Document {\n",
      "    pageContent: 'The powerhouse of the cell is the mitochondria',\n",
      "    metadata: { source: 'https://example.com' },\n",
      "    id: undefined\n",
      "  },\n",
      "  Document {\n",
      "    pageContent: 'Mitochondria are made out of lipids',\n",
      "    metadata: { source: 'https://example.com' },\n",
      "    id: undefined\n",
      "  }\n",
      "]\n"
     ]
    }
   ],
   "source": [
    "const retriever = vectorStore.asRetriever({\n",
    "  // Optional filter\n",
    "  filter: filter,\n",
    "  k: 2,\n",
    "});\n",
    "await retriever.invoke(\"biology\");"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2e0a211",
   "metadata": {},
   "source": [
    "### Usage for retrieval-augmented generation\n",
    "\n",
    "For guides on how to use this vector store for retrieval-augmented generation (RAG), see the following sections:\n",
    "\n",
    "- [Tutorials: working with external knowledge](/docs/tutorials/#working-with-external-knowledge).\n",
    "- [How-to: Question and answer with RAG](/docs/how_to/#qa-with-rag)\n",
    "- [Retrieval conceptual docs](/docs/concepts#retrieval)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a27244f",
   "metadata": {},
   "source": [
    "## API reference\n",
    "\n",
    "For detailed documentation of all `ElasticVectorSearch` features and configurations head to the [API reference](https://api.js.langchain.com/classes/langchain_community_vectorstores_elasticsearch.ElasticVectorSearch.html)."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "TypeScript",
   "language": "typescript",
   "name": "tslab"
  },
  "language_info": {
   "codemirror_mode": {
    "mode": "typescript",
    "name": "javascript",
    "typescript": true
   },
   "file_extension": ".ts",
   "mimetype": "text/typescript",
   "name": "typescript",
   "version": "3.7.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}


---
hide_table_of_contents: true
---

# JSONLines files

This example goes over how to load data from JSONLines or JSONL files. The second argument is a JSONPointer to the property to extract from each JSON object in the file. One document will be created for each JSON object in the file.

Example JSONLines file:

```json
{"html": "This is a sentence."}
{"html": "This is another sentence."}
```

Example code:

```typescript
import { JSONLinesLoader } from "langchain/document_loaders/fs/json";

const loader = new JSONLinesLoader(
  "src/document_loaders/example_data/example.jsonl",
  "/html"
);

const docs = await loader.load();
/*
[
  Document {
    "metadata": {
      "blobType": "application/jsonl+json",
      "line": 1,
      "source": "blob",
    },
    "pageContent": "This is a sentence.",
  },
  Document {
    "metadata": {
      "blobType": "application/jsonl+json",
      "line": 2,
      "source": "blob",
    },
    "pageContent": "This is another sentence.",
  },
]
*/
```


# JSON files

The JSON loader use [JSON pointer](https://github.com/janl/node-jsonpointer) to target keys in your JSON files you want to target.

### No JSON pointer example

The most simple way of using it, is to specify no JSON pointer.
The loader will load all strings it finds in the JSON object.

Example JSON file:

```json
{
  "texts": ["This is a sentence.", "This is another sentence."]
}
```

Example code:

```typescript
import { JSONLoader } from "langchain/document_loaders/fs/json";

const loader = new JSONLoader("src/document_loaders/example_data/example.json");

const docs = await loader.load();
/*
[
  Document {
    "metadata": {
      "blobType": "application/json",
      "line": 1,
      "source": "blob",
    },
    "pageContent": "This is a sentence.",
  },
  Document {
    "metadata": {
      "blobType": "application/json",
      "line": 2,
      "source": "blob",
    },
    "pageContent": "This is another sentence.",
  },
]
*/
```

### Using JSON pointer example

You can do a more advanced scenario by choosing which keys in your JSON object you want to extract string from.

In this example, we want to only extract information from "from" and "surname" entries.

```json
{
  "1": {
    "body": "BD 2023 SUMMER",
    "from": "LinkedIn Job",
    "labels": ["IMPORTANT", "CATEGORY_UPDATES", "INBOX"]
  },
  "2": {
    "body": "Intern, Treasury and other roles are available",
    "from": "LinkedIn Job2",
    "labels": ["IMPORTANT"],
    "other": {
      "name": "plop",
      "surname": "bob"
    }
  }
}
```

Example code:

```typescript
import { JSONLoader } from "langchain/document_loaders/fs/json";

const loader = new JSONLoader(
  "src/document_loaders/example_data/example.json",
  ["/from", "/surname"]
);

const docs = await loader.load();
/*
[
  Document {
    "metadata": {
      "blobType": "application/json",
      "line": 1,
      "source": "blob",
    },
    "pageContent": "BD 2023 SUMMER",
  },
  Document {
    "metadata": {
      "blobType": "application/json",
      "line": 2,
      "source": "blob",
    },
    "pageContent": "LinkedIn Job",
  },
  ...
]
```


"  {\n",
    "    \".pdf\": (path: string) => new PDFLoader(path),\n",
    "  }\n",
    ");\n",
    "\n",
    "const directoryDocs = await directoryLoader.load();\n",
    "\n",
    "console.log(directoryDocs[0]);\n",
    "\n",
    "/* Additional steps : Split text into chunks with any TextSplitter. You can then use it as context or save it to memory afterwards. */\n",
    "const textSplitter = new RecursiveCharacterTextSplitter({\n",
    "  chunkSize: 1000,\n",
    "  chunkOverlap: 200,\n",
    "});\n",
    "\n",
    "const splitDocs = await textSplitter.splitDocuments(directoryDocs);\n",
    "console.log(splitDocs[0]);\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## API reference\n",
    "\n",
    "For detailed documentation of all PDFLoader features and configurations head to the API reference: https://api.js.langchain.com/classes/langchain_community_document_loaders_fs_pdf.PDFLoader.html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "TypeScript",
   "language": "typescript",
   "name": "tslab"
  },
  "language_info": {
   "codemirror_mode": {
    "mode": "typescript",
    "name": "javascript",
    "typescript": true
   },
   "file_extension": ".ts",
   "mimetype": "text/typescript",
   "name": "typescript",
   "version": "3.7.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}


---
sidebar_class_name: hidden
---

# PromptLayer OpenAI

:::warning
This module has been deprecated and is no longer supported. The documentation below will not work in versions 0.2.0 or later.
:::

LangChain integrates with PromptLayer for logging and debugging prompts and responses. To add support for PromptLayer:

1. Create a PromptLayer account here: [https://promptlayer.com](https://promptlayer.com).
2. Create an API token and pass it either as `promptLayerApiKey` argument in the `PromptLayerOpenAI` constructor or in the `PROMPTLAYER_API_KEY` environment variable.

```typescript
import { PromptLayerOpenAI } from "langchain/llms/openai";

const model = new PromptLayerOpenAI({
  temperature: 0.9,
  apiKey: "YOUR-API-KEY", // In Node.js defaults to process.env.OPENAI_API_KEY
  promptLayerApiKey: "YOUR-API-KEY", // In Node.js defaults to process.env.PROMPTLAYER_API_KEY
});
const res = await model.invoke(
  "What would be a good company name a company that makes colorful socks?"
);
```

# Azure PromptLayerOpenAI

LangChain also integrates with PromptLayer for Azure-hosted OpenAI instances:

```typescript
import { PromptLayerOpenAI } from "langchain/llms/openai";

const model = new PromptLayerOpenAI({
  temperature: 0.9,
  azureOpenAIApiKey: "YOUR-AOAI-API-KEY", // In Node.js defaults to process.env.AZURE_OPENAI_API_KEY
  azureOpenAIApiInstanceName: "YOUR-AOAI-INSTANCE-NAME", // In Node.js defaults to process.env.AZURE_OPENAI_API_INSTANCE_NAME
  azureOpenAIApiDeploymentName: "YOUR-AOAI-DEPLOYMENT-NAME", // In Node.js defaults to process.env.AZURE_OPENAI_API_DEPLOYMENT_NAME
  azureOpenAIApiCompletionsDeploymentName:
    "YOUR-AOAI-COMPLETIONS-DEPLOYMENT-NAME", // In Node.js defaults to process.env.AZURE_OPENAI_API_COMPLETIONS_DEPLOYMENT_NAME
  azureOpenAIApiEmbeddingsDeploymentName:
    "YOUR-AOAI-EMBEDDINGS-DEPLOYMENT-NAME", // In Node.js defaults to process.env.AZURE_OPENAI_API_EMBEDDINGS_DEPLOYMENT_NAME
  azureOpenAIApiVersion: "YOUR-AOAI-API-VERSION", // In Node.js defaults to process.env.AZURE_OPENAI_API_VERSION
  azureOpenAIBasePath: "YOUR-AZURE-OPENAI-BASE-PATH", // In Node.js defaults to process.env.AZURE_OPENAI_BASE_PATH
  promptLayerApiKey: "YOUR-API-KEY", // In Node.js defaults to process.env.PROMPTLAYER_API_KEY
});
const res = await model.invoke(
  "What would be a good company name a company that makes colorful socks?"
);
```

The request and the response will be logged in the [PromptLayer dashboard](https://promptlayer.com/home).

> **_Note:_** In streaming mode PromptLayer will not log the response.

## Related

- LLM [conceptual guide](/docs/concepts/#llms)
- LLM [how-to guides](/docs/how_to/#llms)


{
 "cells": [
  {
   "cell_type": "raw",
   "id": "67db2992",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "---\n",
    "sidebar_label: Azure OpenAI\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9597802c",
   "metadata": {},
   "source": [
    "# AzureOpenAI\n",
    "\n",
    "```{=mdx}\n",
    "\n",
    ":::caution\n",
    "You are currently on a page documenting the use of Azure OpenAI [text completion models](/docs/concepts/#llms). The latest and most popular Azure OpenAI models are [chat completion models](/docs/concepts/#chat-models).\n",
    "\n",
    "Unless you are specifically using `gpt-3.5-turbo-instruct`, you are probably looking for [this page instead](/docs/integrations/chat/azure/).\n",
    ":::\n",
    "\n",
    ":::info\n",
    "\n",
    "Previously, LangChain.js supported integration with Azure OpenAI using the dedicated [Azure OpenAI SDK](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/openai/openai). This SDK is now deprecated in favor of the new Azure integration in the OpenAI SDK, which allows to access the latest OpenAI models and features the same day they are released, and allows seemless transition between the OpenAI API and Azure OpenAI.\n",
    "\n",
    "If you are using Azure OpenAI with the deprecated SDK, see the [migration guide](#migration-from-azure-openai-sdk) to update to the new API.\n",
    "\n",
    ":::\n",
    "\n",
    "```\n",
    "\n",
    "[Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/) is a Microsoft Azure service that provides powerful language models from OpenAI.\n",
    "\n",
    "This will help you get started with AzureOpenAI completion models (LLMs) using LangChain. For detailed documentation on `AzureOpenAI` features and configuration options, please refer to the [API reference](https://api.js.langchain.com/classes/langchain_openai.AzureOpenAI.html).\n",
    "\n",
    "## Overview\n",
    "### Integration details\n",
    "\n",
    "| Class | Package | Local | Serializable | [PY support](https://python.langchain.com/docs/integrations/llms/azure_openai) | Package downloads | Package latest |\n",
    "| :--- | :--- | :---: | :---: |  :---: | :---: | :---: |\n",
    "| [AzureOpenAI](https://api.js.langchain.com/classes/langchain_openai.AzureOpenAI.html) | [@langchain/openai](https://api.js.langchain.com/modules/langchain_openai.html) | ❌ | ✅ | ✅ | ![NPM - Downloads](https://img.shields.io/npm/dm/@langchain/openai?style=flat-square&label=%20&) | ![NPM - Version](https://img.shields.io/npm/v/@langchain/openai?style=flat-square&label=%20&) |\n",
    "\n",
    "## Setup\n",
    "\n",
    "To access AzureOpenAI models you'll need to create an Azure account, get an API key, and install the `@langchain/openai` integration package.\n",
    "\n",
    "### Credentials\n",
    "\n",
    "Head to [azure.microsoft.com](https://azure.microsoft.com/) to sign up to AzureOpenAI and generate an API key. \n",
    "\n",
    "You'll also need to have an Azure OpenAI instance deployed. You can deploy a version on Azure Portal following [this guide](https://learn.microsoft.com/azure/ai-services/openai/how-to/create-resource?pivots=web-portal).\n",
    "\n",
    "Once you have your instance running, make sure you have the name of your instance and key. You can find the key in the Azure Portal, under the \"Keys and Endpoint\" section of your instance.\n",
    "\n",
    "If you're using Node.js, you can define the following environment variables to use the service:\n",
    "\n",
    "```bash\n",
    "AZURE_OPENAI_API_INSTANCE_NAME=<YOUR_INSTANCE_NAME>\n",
    "AZURE_OPENAI_API_DEPLOYMENT_NAME=<YOUR_DEPLOYMENT_NAME>\n",
    "AZURE_OPENAI_API_KEY=<YOUR_KEY>\n",
    "AZURE_OPENAI_API_VERSION=\"2024-02-01\"\n",
    "```\n",
    "\n",
    "Alternatively, you can pass the values directly to the `AzureOpenAI` constructor.\n",
    "\n",
    "If you want to get automated tracing of your model calls you can also set your [LangSmith](https://docs.smith.langchain.com/) API key by uncommenting below:\n",
    "\n",
    "```bash\n",
    "# export LANGCHAIN_TRACING_V2=\"true\"\n",
    "# export LANGCHAIN_API_KEY=\"your-api-key\"\n",
    "```\n",
    "\n",
    "### Installation\n",
    "\n",
    "The LangChain AzureOpenAI integration lives in the `@langchain/openai` package:\n",
    "\n",
    "```{=mdx}\n",
    "import IntegrationInstallTooltip from \"@mdx_components/integration_install_tooltip.mdx\";\n",
    "import Npm2Yarn from \"@theme/Npm2Yarn\";\n",
    "\n",
    "<IntegrationInstallTooltip></IntegrationInstallTooltip>\n",
    "\n",
    "<Npm2Yarn>\n",
    "  @langchain/openai @langchain/core\n",
    "</Npm2Yarn>\n",
    "\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0a760037",
   "metadata": {},
   "source": [
    "## Instantiation\n",
    "\n",
    "Now we can instantiate our model object and generate chat completions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "a0562a13",
   "metadata": {},
   "outputs": [],
   "source": [
    "import { AzureOpenAI } from \"@langchain/openai\"\n",
    "\n",
    "const llm = new AzureOpenAI({\n",
    "  model: \"gpt-3.5-turbo-instruct\",\n",
    "  azureOpenAIApiKey: \"<your_key>\", // In Node.js defaults to process.env.AZURE_OPENAI_API_KEY\n",
    "  azureOpenAIApiInstanceName: \"<your_instance_name>\", // In Node.js defaults to process.env.AZURE_OPENAI_API_INSTANCE_NAME\n",
    "  azureOpenAIApiDeploymentName: \"<your_deployment_name>\", // In Node.js defaults to process.env.AZURE_OPENAI_API_DEPLOYMENT_NAME\n",
    "  azureOpenAIApiVersion: \"<api_version>\", // In Node.js defaults to process.env.AZURE_OPENAI_API_VERSION\n",
    "  temperature: 0,\n",
    "  maxTokens: undefined,\n",
    "  timeout: undefined,\n",
    "  maxRetries: 2,\n",
    "  // other params...\n",
    "})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0ee90032",
   "metadata": {},
   "source": [
    "## Invocation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "035dea0f",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "provides AI solutions to businesses. They offer a range of services including natural language processing, computer vision, and machine learning. Their solutions are designed to help businesses automate processes, gain insights from data, and improve decision-making. AzureOpenAI also offers consulting services to help businesses identify and implement the best AI solutions for their specific needs. They work with a variety of industries, including healthcare, finance, and retail. With their expertise in AI and their partnership with Microsoft Azure, AzureOpenAI is a trusted provider of AI solutions for businesses looking to stay ahead in the rapidly evolving world of technology.\n"
     ]
    }
   ],
   "source": [
    "const inputText = \"AzureOpenAI is an AI company that \"\n",
    "\n",
    "const completion = await llm.invoke(inputText)\n",
    "completion"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "add38532",
   "metadata": {},
   "source": [
    "## Chaining\n",
    "\n",
    "We can [chain](/docs/how_to/sequence/) our completion model with a prompt template like so:"


---
sidebar_class_name: node-only
---

# Llama CPP

:::tip Compatibility
Only available on Node.js.
:::

This module is based on the [node-llama-cpp](https://github.com/withcatai/node-llama-cpp) Node.js bindings for [llama.cpp](https://github.com/ggerganov/llama.cpp), allowing you to work with a locally running LLM. This allows you to work with a much smaller quantized model capable of running on a laptop environment, ideal for testing and scratch padding ideas without running up a bill!

## Setup

You'll need to install major version `2` of the [node-llama-cpp](https://github.com/withcatai/node-llama-cpp) module to communicate with your local model.

```bash npm2yarn
npm install -S node-llama-cpp@2
```

import IntegrationInstallTooltip from "@mdx_components/integration_install_tooltip.mdx";

<IntegrationInstallTooltip></IntegrationInstallTooltip>

```bash npm2yarn
npm install @langchain/community @langchain/core
```

You will also need a local Llama 2 model (or a model supported by [node-llama-cpp](https://github.com/withcatai/node-llama-cpp)). You will need to pass the path to this model to the LlamaCpp module as a part of the parameters (see example).

Out-of-the-box `node-llama-cpp` is tuned for running on a MacOS platform with support for the Metal GPU of Apple M-series of processors. If you need to turn this off or need support for the CUDA architecture then refer to the documentation at [node-llama-cpp](https://withcatai.github.io/node-llama-cpp/).

A note to LangChain.js contributors: if you want to run the tests associated with this module you will need to put the path to your local model in the environment variable `LLAMA_PATH`.

## Guide to installing Llama2

Getting a local Llama2 model running on your machine is a pre-req so this is a quick guide to getting and building Llama 7B (the smallest) and then quantizing it so that it will run comfortably on a laptop. To do this you will need `python3` on your machine (3.11 is recommended), also `gcc` and `make` so that `llama.cpp` can be built.

### Getting the Llama2 models

To get a copy of Llama2 you need to visit [Meta AI](https://ai.meta.com/resources/models-and-libraries/llama-downloads/) and request access to their models. Once Meta AI grant you access, you will receive an email containing a unique URL to access the files, this will be needed in the next steps.
Now create a directory to work in, for example:

```
mkdir llama2
cd llama2
```

Now we need to get the Meta AI `llama` repo in place so we can download the model.

```
git clone https://github.com/facebookresearch/llama.git
```

Once we have this in place we can change into this directory and run the downloader script to get the model we will be working with. Note: From here on its assumed that the model in use is `llama-2–7b`, if you select a different model don't forget to change the references to the model accordingly.

```
cd llama
/bin/bash ./download.sh
```

This script will ask you for the URL that Meta AI sent to you (see above), you will also select the model to download, in this case we used `llama-2–7b`. Once this step has completed successfully (this can take some time, the `llama-2–7b` model is around 13.5Gb) there should be a new `llama-2–7b` directory containing the model and other files.

### Converting and quantizing the model

In this step we need to use `llama.cpp` so we need to download that repo.

```
cd ..
git clone https://github.com/ggerganov/llama.cpp.git
cd llama.cpp
```

Now we need to build the `llama.cpp` tools and set up our `python` environment. In these steps it's assumed that your install of python can be run using `python3` and that the virtual environment can be called `llama2`, adjust accordingly for your own situation.

```
make
python3 -m venv llama2
source llama2/bin/activate
```

After activating your llama2 environment you should see `(llama2)` prefixing your command prompt to let you know this is the active environment. Note: if you need to come back to build another model or re-quantize the model don't forget to activate the environment again also if you update `llama.cpp` you will need to rebuild the tools and possibly install new or updated dependencies! Now that we have an active python environment, we need to install the python dependencies.

```
python3 -m pip install -r requirements.txt
```

Having done this, we can start converting and quantizing the Llama2 model ready for use locally via `llama.cpp`.
First, we need to convert the model, prior to the conversion let's create a directory to store it in.

```
mkdir models/7B
python3 convert.py --outfile models/7B/gguf-llama2-f16.bin --outtype f16 ../../llama2/llama/llama-2-7b --vocab-dir ../../llama2/llama/llama-2-7b
```

This should create a converted model called `gguf-llama2-f16.bin` in the directory we just created. Note that this is just a converted model so it is also around 13.5Gb in size, in the next step we will quantize it down to around 4Gb.

```
./quantize ./models/7B/gguf-llama2-f16.bin ./models/7B/gguf-llama2-q4_0.bin q4_0
```

Running this should result in a new model being created in the `models\7B` directory, this one called `gguf-llama2-q4_0.bin`, this is the model we can use with langchain. You can validate this model is working by testing it using the `llama.cpp` tools.

```
./main -m ./models/7B/gguf-llama2-q4_0.bin -n 1024 --repeat_penalty 1.0 --color -i -r "User:" -f ./prompts/chat-with-bob.txt
```

Running this command fires up the model for a chat session. BTW if you are running out of disk space this small model is the only one we need, so you can backup and/or delete the original and converted 13.5Gb models.

## Usage

import CodeBlock from "@theme/CodeBlock";
import LlamaCppExample from "@examples/models/llm/llama_cpp.ts";

<CodeBlock language="typescript">{LlamaCppExample}</CodeBlock>

## Streaming

import LlamaCppStreamExample from "@examples/models/llm/llama_cpp_stream.ts";

<CodeBlock language="typescript">{LlamaCppStreamExample}</CodeBlock>;

## Related

- LLM [conceptual guide](/docs/concepts/#llms)
- LLM [how-to guides](/docs/how_to/#llms)


{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "---\n",
    "keywords: [pdf, document loader]\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Build a PDF ingestion and Question/Answering system\n",
    "\n",
    ":::info Prerequisites\n",
    "\n",
    "This guide assumes familiarity with the following concepts:\n",
    "\n",
    "- [Document loaders](/docs/concepts/#document-loaders)\n",
    "- [Chat models](/docs/concepts/#chat-models)\n",
    "- [Embeddings](/docs/concepts/#embedding-models)\n",
    "- [Vector stores](/docs/concepts/#vector-stores)\n",
    "- [Retrieval-augmented generation](/docs/tutorials/rag/)\n",
    "\n",
    ":::\n",
    "\n",
    "PDF files often hold crucial unstructured data unavailable from other sources. They can be quite lengthy, and unlike plain text files, cannot generally be fed directly into the prompt of a language model.\n",
    "\n",
    "In this tutorial, you'll create a system that can answer questions about PDF files. More specifically, you'll use a [Document Loader](/docs/concepts/#document-loaders) to load text in a format usable by an LLM, then build a retrieval-augmented generation (RAG) pipeline to answer questions, including citations from the source material.\n",
    "\n",
    "This tutorial will gloss over some concepts more deeply covered in our [RAG](/docs/tutorials/rag/) tutorial, so you may want to go through those first if you haven't already.\n",
    "\n",
    "Let's dive in!\n",
    "\n",
    "## Loading documents\n",
    "\n",
    "First, you'll need to choose a PDF to load. We'll use a document from [Nike's annual public SEC report](https://s1.q4cdn.com/806093406/files/doc_downloads/2023/414759-1-_5_Nike-NPS-Combo_Form-10-K_WR.pdf). It's over 100 pages long, and contains some crucial data mixed with longer explanatory text. However, you can feel free to use a PDF of your choosing.\n",
    "\n",
    "Once you've chosen your PDF, the next step is to load it into a format that an LLM can more easily handle, since LLMs generally require text inputs. LangChain has a few different [built-in document loaders](/docs/how_to/document_loader_pdf/) for this purpose which you can experiment with. Below, we'll use one powered by the [`pdf-parse`](https://www.npmjs.com/package/pdf-parse) package that reads from a filepath:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "107\n"
     ]
    }
   ],
   "source": [
    "import \"pdf-parse\"; // Peer dep\n",
    "import { PDFLoader } from \"@langchain/community/document_loaders/fs/pdf\";\n",
    "\n",
    "const loader = new PDFLoader(\"../../data/nke-10k-2023.pdf\");\n",
    "\n",
    "const docs = await loader.load();\n",
    "\n",
    "console.log(docs.length);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Table of Contents\n",
      "UNITED STATES\n",
      "SECURITIES AND EXCHANGE COMMISSION\n",
      "Washington, D.C. 20549\n",
      "FORM 10-K\n",
      "\n",
      "{\n",
      "  source: '../../data/nke-10k-2023.pdf',\n",
      "  pdf: {\n",
      "    version: '1.10.100',\n",
      "    info: {\n",
      "      PDFFormatVersion: '1.4',\n",
      "      IsAcroFormPresent: false,\n",
      "      IsXFAPresent: false,\n",
      "      Title: '0000320187-23-000039',\n",
      "      Author: 'EDGAR Online, a division of Donnelley Financial Solutions',\n",
      "      Subject: 'Form 10-K filed on 2023-07-20 for the period ending 2023-05-31',\n",
      "      Keywords: '0000320187-23-000039; ; 10-K',\n",
      "      Creator: 'EDGAR Filing HTML Converter',\n",
      "      Producer: 'EDGRpdf Service w/ EO.Pdf 22.0.40.0',\n",
      "      CreationDate: \"D:20230720162200-04'00'\",\n",
      "      ModDate: \"D:20230720162208-04'00'\"\n",
      "    },\n",
      "    metadata: null,\n",
      "    totalPages: 107\n",
      "  },\n",
      "  loc: { pageNumber: 1 }\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "console.log(docs[0].pageContent.slice(0, 100));\n",
    "console.log(docs[0].metadata)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So what just happened?\n",
    "\n",
    "- The loader reads the PDF at the specified path into memory.\n",
    "- It then extracts text data using the `pdf-parse` package.\n",
    "- Finally, it creates a LangChain [Document](/docs/concepts/#documents) for each page of the PDF with the page's content and some metadata about where in the document the text came from.\n",
    "\n",
    "LangChain has [many other document loaders](/docs/integrations/document_loaders/) for other data sources, or you can create a [custom document loader](/docs/how_to/document_loader_custom/).\n",
    "\n",
    "## Question answering with RAG\n",
    "\n",
    "Next, you'll prepare the loaded documents for later retrieval. Using a [text splitter](/docs/concepts/#text-splitters), you'll split your loaded documents into smaller documents that can more easily fit into an LLM's context window, then load them into a [vector store](/docs/concepts/#vectorstores). You can then create a [retriever](/docs/concepts/#retrievers) from the vector store for use in our RAG chain:\n",
    "\n",
    "```{=mdx}\n",
    "import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
    "\n",
    "<ChatModelTabs openaiParams={`{ model: \"gpt-4o\" }`} />\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import { MemoryVectorStore } from \"langchain/vectorstores/memory\";\n",
    "import { OpenAIEmbeddings } from \"@langchain/openai\";\n",
    "import { RecursiveCharacterTextSplitter } from \"@langchain/textsplitters\";\n",
    "\n",
    "const textSplitter = new RecursiveCharacterTextSplitter({\n",
    "  chunkSize: 1000,\n",
    "  chunkOverlap: 200,\n",
    "});\n",
    "\n",
    "const splits = await textSplitter.splitDocuments(docs);\n",
    "\n",
    "const vectorstore = await MemoryVectorStore.fromDocuments(splits, new OpenAIEmbeddings());\n",
    "\n",
    "const retriever = vectorstore.asRetriever();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, you'll use some built-in helpers to construct the final `ragChain`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "  input: \"What was Nike's revenue in 2023?\",\n",
      "  chat_history: [],\n",
      "  context: [\n",
      "    Document {\n",


"import { createRetrievalChain } from \"langchain/chains/retrieval\";\n",
    "import { createStuffDocumentsChain } from \"langchain/chains/combine_documents\";\n",
    "import { ChatPromptTemplate } from \"@langchain/core/prompts\";\n",
    "\n",
    "const systemTemplate = [\n",
    "  `You are an assistant for question-answering tasks. `,\n",
    "  `Use the following pieces of retrieved context to answer `,\n",
    "  `the question. If you don't know the answer, say that you `,\n",
    "  `don't know. Use three sentences maximum and keep the `,\n",
    "  `answer concise.`,\n",
    "  `\\n\\n`,\n",
    "  `{context}`,\n",
    "].join(\"\");\n",
    "\n",
    "const prompt = ChatPromptTemplate.fromMessages([\n",
    "  [\"system\", systemTemplate],\n",
    "  [\"human\", \"{input}\"],\n",
    "]);\n",
    "\n",
    "const questionAnswerChain = await createStuffDocumentsChain({ llm, prompt });\n",
    "const ragChain = await createRetrievalChain({ retriever, combineDocsChain: questionAnswerChain });\n",
    "\n",
    "const results = await ragChain.invoke({\n",
    "  input: \"What was Nike's revenue in 2023?\",\n",
    "});\n",
    "\n",
    "console.log(results);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can see that you get both a final answer in the `answer` key of the results object, and the `context` the LLM used to generate an answer.\n",
    "\n",
    "Examining the values under the `context` further, you can see that they are documents that each contain a chunk of the ingested page content. Usefully, these documents also preserve the original metadata from way back when you first loaded them:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Enterprise Resource Planning Platform, data and analytics, demand sensing, insight gathering, and other areas to create an end-to-end technology foundation, which we\n",
      "believe will further accelerate our digital transformation. We believe this unified approach will accelerate growth and unlock more efficiency for our business, while driving\n",
      "speed and responsiveness as we serve consumers globally.\n",
      "FINANCIAL HIGHLIGHTS\n",
      "•In fiscal 2023, NIKE, Inc. achieved record Revenues of $51.2 billion, which increased 10% and 16% on a reported and currency-neutral basis, respectively\n",
      "•NIKE Direct revenues grew 14% from $18.7 billion in fiscal 2022 to $21.3 billion in fiscal 2023, and represented approximately 44% of total NIKE Brand revenues for\n",
      "fiscal 2023\n",
      "•Gross margin for the fiscal year decreased 250 basis points to 43.5% primarily driven by higher product costs, higher markdowns and unfavorable changes in foreign\n",
      "currency exchange rates, partially offset by strategic pricing actions\n"
     ]
    }
   ],
   "source": [
    "console.log(results.context[0].pageContent);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "  source: '../../data/nke-10k-2023.pdf',\n",
      "  pdf: {\n",
      "    version: '1.10.100',\n",
      "    info: {\n",
      "      PDFFormatVersion: '1.4',\n",
      "      IsAcroFormPresent: false,\n",
      "      IsXFAPresent: false,\n",
      "      Title: '0000320187-23-000039',\n",
      "      Author: 'EDGAR Online, a division of Donnelley Financial Solutions',\n",
      "      Subject: 'Form 10-K filed on 2023-07-20 for the period ending 2023-05-31',\n",
      "      Keywords: '0000320187-23-000039; ; 10-K',\n",
      "      Creator: 'EDGAR Filing HTML Converter',\n",
      "      Producer: 'EDGRpdf Service w/ EO.Pdf 22.0.40.0',\n",
      "      CreationDate: \"D:20230720162200-04'00'\",\n",
      "      ModDate: \"D:20230720162208-04'00'\"\n",
      "    },\n",
      "    metadata: null,\n",
      "    totalPages: 107\n",
      "  },\n",
      "  loc: { pageNumber: 31, lines: { from: 14, to: 22 } }\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "console.log(results.context[0].metadata);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This particular chunk came from page 31 in the original PDF. You can use this data to show which page in the PDF the answer came from, allowing users to quickly verify that answers are based on the source material.\n",
    "\n",
    ":::info\n",
    "For a deeper dive into RAG, see [this more focused tutorial](/docs/tutorials/rag/) or [our how-to guides](/docs/how_to/#qa-with-rag).\n",
    ":::\n",
    "\n",
    "## Next steps\n",
    "\n",
    "You've now seen how to load documents from a PDF file with a Document Loader and some techniques you can use to prepare that loaded data for RAG.\n",
    "\n",
    "For more on document loaders, you can check out:\n",
    "\n",
    "- [The entry in the conceptual guide](/docs/concepts/#document-loaders)\n",
    "- [Related how-to guides](/docs/how_to/#document-loaders)\n",
    "- [Available integrations](/docs/integrations/document_loaders/)\n",
    "- [How to create a custom document loader](/docs/how_to/document_loader_custom/)\n",
    "\n",
    "For more on RAG, see:\n",
    "\n",
    "- [Build a Retrieval Augmented Generation (RAG) App](/docs/tutorials/rag/)\n",
    "- [Related how-to guides](/docs/how_to/#qa-with-rag)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "TypeScript",
   "language": "typescript",
   "name": "tslab"
  },
  "language_info": {
   "codemirror_mode": {
    "mode": "typescript",
    "name": "javascript",
    "typescript": true
   },
   "file_extension": ".ts",
   "mimetype": "text/typescript",
   "name": "typescript",
   "version": "3.7.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}