Restructure template (#1)

This commit is contained in:
William FH
2024-09-16 08:26:39 -07:00
committed by William Fu-Hinthorn
parent adfbf16871
commit 869ef22381
17 changed files with 2731 additions and 263 deletions
+808 -20
View File
@@ -1,24 +1,812 @@
# LangGraph Starter Template
# LangGraph Retrieval Agent Template
This repo offers the basic code structure to get started building LangGraph workflows in JavaScript within LangGraph studio.
<!-- TODO: Add CI shields -->
<!-- [![CI](https://github.com/langchain-ai/retrieval-agent-template-js/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/langchain-ai/retrieval-agent-template-js/actions/workflows/unit-tests.yml)
[![Integration Tests](https://github.com/langchain-ai/retrieval-agent-template-js/actions/workflows/integration-tests.yml/badge.svg)](https://github.com/langchain-ai/retrieval-agent-template-js/actions/workflows/integration-tests.yml) -->
[![Open in - LangGraph Studio](https://img.shields.io/badge/Open_in-LangGraph_Studio-00324d.svg?logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NS4zMzMiIGhlaWdodD0iODUuMzMzIiB2ZXJzaW9uPSIxLjAiIHZpZXdCb3g9IjAgMCA2NCA2NCI+PHBhdGggZD0iTTEzIDcuOGMtNi4zIDMuMS03LjEgNi4zLTYuOCAyNS43LjQgMjQuNi4zIDI0LjUgMjUuOSAyNC41QzU3LjUgNTggNTggNTcuNSA1OCAzMi4zIDU4IDcuMyA1Ni43IDYgMzIgNmMtMTIuOCAwLTE2LjEuMy0xOSAxLjhtMzcuNiAxNi42YzIuOCAyLjggMy40IDQuMiAzLjQgNy42cy0uNiA0LjgtMy40IDcuNkw0Ny4yIDQzSDE2LjhsLTMuNC0zLjRjLTQuOC00LjgtNC44LTEwLjQgMC0xNS4ybDMuNC0zLjRoMzAuNHoiLz48cGF0aCBkPSJNMTguOSAyNS42Yy0xLjEgMS4zLTEgMS43LjQgMi41LjkuNiAxLjcgMS44IDEuNyAyLjcgMCAxIC43IDIuOCAxLjYgNC4xIDEuNCAxLjkgMS40IDIuNS4zIDMuMi0xIC42LS42LjkgMS40LjkgMS41IDAgMi43LS41IDIuNy0xIDAtLjYgMS4xLS44IDIuNi0uNGwyLjYuNy0xLjgtMi45Yy01LjktOS4zLTkuNC0xMi4zLTExLjUtOS44TTM5IDI2YzAgMS4xLS45IDIuNS0yIDMuMi0yLjQgMS41LTIuNiAzLjQtLjUgNC4yLjguMyAyIDEuNyAyLjUgMy4xLjYgMS41IDEuNCAyLjMgMiAyIDEuNS0uOSAxLjItMy41LS40LTMuNS0yLjEgMC0yLjgtMi44LS44LTMuMyAxLjYtLjQgMS42LS41IDAtLjYtMS4xLS4xLTEuNS0uNi0xLjItMS42LjctMS43IDMuMy0yLjEgMy41LS41LjEuNS4yIDEuNi4zIDIuMiAwIC43LjkgMS40IDEuOSAxLjYgMi4xLjQgMi4zLTIuMy4yLTMuMi0uOC0uMy0yLTEuNy0yLjUtMy4xLTEuMS0zLTMtMy4zLTMtLjUiLz48L3N2Zz4=)](https://langgraph-studio.vercel.app/templates/open?githubUrl=https://github.com/langchain-ai/retrieval-agent-template-js)
## Repo Structure
This is a starter project to help you get started with developing a retrieval agent using [LangGraph.js](https://github.com/langchain-ai/langgraphjs) in [LangGraph Studio](https://github.com/langchain-ai/langgraph-studio).
![Graph view in LangGraph studio UI](./static/studio_ui.png)
It contains example graphs exported from `src/retrieval_agent/graph.ts` that implement a retrieval-based question answering system.
## What it does
The retrieval agent:
1. Takes a user **query** as input
2. Generates a search query based on the conversation history (if not the first turn)
3. Retrieves relevant documents from an indexed knowledge base
4. Formulates a response using the retrieved information and conversation context
5. Returns the generated answer to the user
By default, it's set up to answer questions based on the user's indexed documents, which are filtered by the user's ID for personalized responses.
## Getting Started
Assuming you have already [installed LangGraph Studio](https://github.com/langchain-ai/langgraph-studio?tab=readme-ov-file#download), to set up:
1. Create a `.env` file.
```bash
cp .env.example .env
```txt
├── LICENSE
├── README.md
├── jest.config.js # Test configuration
├── .env # Define environment variables. Can copy over from .env.example
├── langgraph.json # LangGraph studio configuration
├── my-app
│   ├── graph.ts # Graph / workflow definition
│   └── index.ts
├── package.json # Define the project dependencies
├── tests # Add any tests you'd like here
│   ├── integration
│   │   └── graph.int.test.ts
│   └── unit
│   └── graph.test.ts
├── tsconfig.json # Typescript-specific configuration
└── yarn.lock
```
2. Select your retriever & index, and save the access instructions to your `.env` file.
<!--
Setup instruction auto-generated by `langgraph template lock`. DO NOT EDIT MANUALLY.
-->
<details>
<summary>Setup for `retrieverProvider`</summary>
The `retriever` configuration defaults are shown below:
```yaml
retrieverProvider: elastic
```
Follow the instructions below to get set up, or pick one of the additional options.
### Setup Elasticsearch
#### Elastic Cloud
1. Signup for a free trial with [Elastic Cloud](https://cloud.elastic.co/registration?onboarding_token=search&cta=cloud-registration&tech=trial&plcmt=article%20content&pg=langchain).
2. Get the Elasticsearch URL, found under Applications of your deployment.
3. Create an API key. See the [official elastic documentation](https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#creating-an-api-key) for more information.
4. Copy the URL and API key to your `.env` file created above:
```
ELASTICSEARCH_URL=<ES_URL>
ELASTICSEARCH_API_KEY=<API_KEY>
```
#### Local Elasticsearch (Docker)
```
docker run -p 127.0.0.1:9200:9200 -d --name elasticsearch --network elastic-net -e ELASTIC_PASSWORD=changeme -e "discovery.type=single-node" -e "xpack.security.http.ssl.enabled=false" -e "xpack.license.self_generated.type=trial" docker.elastic.co/elasticsearch/elasticsearch:8.15.1
```
See the [official Elastic documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html) for more information on running it locally.
Then populate the following in your `.env` file:
```
# As both Elasticsearch and LangGraph Studio runs in Docker, we need to use host.docker.internal to access.
ELASTICSEARCH_URL=http://host.docker.internal:9200
ELASTICSEARCH_USER=elastic
ELASTICSEARCH_PASSWORD=changeme
```
### MongoDB Atlas
MongoDB Atlas is a fully-managed cloud database that includes vector search capabilities for AI-powered applications.
1. Create a free Atlas cluster:
- Go to the [MongoDB Atlas website](https://www.mongodb.com/cloud/atlas/register) and sign up for a free account.
- After logging in, create a free cluster by following the on-screen instructions.
2. Set up your environment:
- In the Atlas dashboard, click on "Connect" for your cluster.
- Choose "Connect your application" and copy the provided connection string.
- Create a `.env` file in your project root if you haven't already.
- Add your MongoDB Atlas connection string to the `.env` file:
```
MONGODB_URI="mongodb+srv://username:password@your-cluster-url.mongodb.net/?retryWrites=true&w=majority&appName=your-cluster-name"
```
Replace `username`, `password`, `your-cluster-url`, and `your-cluster-name` with your actual credentials and cluster information.
### Pinecone Serverless
Pinecone is a managed, cloud-native vector database that provides long-term memory for high-performance AI applications.
1. Sign up for a Pinecone account at [https://login.pinecone.io/login](https://login.pinecone.io/login) if you haven't already.
2. After logging in, generate an API key from the Pinecone console.
3. Create a serverless index:
- Choose a name for your index (e.g., "example-index")
- Set the dimension based on your embedding model (e.g., 1536 for OpenAI embeddings)
- Select "cosine" as the metric
- Choose "Serverless" as the index type
- Select your preferred cloud provider and region (e.g., AWS us-east-1)
4. Once you have created your index and obtained your API key, add them to your `.env` file:
```
PINECONE_API_KEY=your-api-key
PINECONE_INDEX_NAME=your-index-name
```
</details>
<details>
<summary>Setup for `responseModel` and `queryModel`</summary>
The `llm` configuration defaults are shown below:
```yaml
responseModel: anthropic/claude-3-5-sonnet-20240620
queryModel: openai/gpt-4o-mini
```
Follow the instructions below to get set up, or pick one of the additional options.
### Anthropic Chat Models
To use Anthropic's chat models:
1. Sign up for an [Anthropic API key](https://console.anthropic.com/) if you haven't already.
2. Once you have your API key, add it to your `.env` file:
```
ANTHROPIC_API_KEY=your-api-key
```
### Fireworks Chat Models
To use Fireworks AI's chat models:
1. Sign up for a [Fireworks AI account](https://app.fireworks.ai/signup) and obtain an API key.
2. Add your Fireworks AI API key to your `.env` file:
```
FIREWORKS_API_KEY=your-api-key
```
#### OpenAI Chat Models
To use OpenAI's chat models:
1. Sign up for an [OpenAI API key](https://platform.openai.com/signup).
2. Once you have your API key, add it to your `.env` file:
```
OPENAI_API_KEY=your-api-key
```
</details>
<details>
<summary>Setup for `embeddingModel`</summary>
The `embeddings` configuration defaults are shown below:
```yaml
embeddingModel: openai/text-embedding-3-small
```
Follow the instructions below to get set up, or pick one of the additional options.
#### OpenAI Embeddings
To use OpenAI's embeddings:
1. Sign up for an [OpenAI API key](https://platform.openai.com/signup).
2. Once you have your API key, add it to your `.env` file:
```
OPENAI_API_KEY=your-api-key
```
#### Cohere Embeddings
To use Cohere's embeddings:
1. Sign up for a [Cohere API key](https://dashboard.cohere.com/welcome/register).
2. Once you have your API key, add it to your `.env` file:
```bash
COHERE_API_KEY=your-api-key
```
</details>
<!--
End setup instructions
-->
## How to customize
You can customize this retrieval agent template in several ways:
1. **Change the retriever**: You can switch between different vector stores (Elasticsearch, MongoDB, Pinecone) by modifying the `retrieverProvider` in the configuration. Each provider has its own setup instructions in the "Getting Started" section above.
2. **Modify the embedding model**: You can change the embedding model used for document indexing and query embedding by updating the `embeddingModel` in the configuration. Options include various OpenAI and Cohere models.
3. **Adjust search parameters**: Fine-tune the retrieval process by modifying the `searchKwargs` in the configuration. This allows you to control aspects like the number of documents retrieved or similarity thresholds.
4. **Customize the response generation**: You can modify the `responseSystemPrompt` to change how the agent formulates its responses. This allows you to adjust the agent's personality or add specific instructions for answer generation.
5. **Change the language model**: Update the `responseModel` in the configuration to use different language models for response generation. Options include various Claude models from Anthropic, as well as models from other providers like Fireworks AI.
6. **Extend the graph**: You can add new nodes or modify existing ones in the `src/retrieval_agent/graph.ts` file to introduce additional processing steps or decision points in the agent's workflow.
7. **Add new tools**: Implement new tools or API integrations in `src/retrieval_agent/tools.ts` to expand the agent's capabilities beyond simple retrieval and response generation.
8. **Modify prompts**: Update the prompts used for query generation and response formulation in `src/retrieval_agent/prompts.ts` to better suit your specific use case or to improve the agent's performance.
Remember to test your changes thoroughly to ensure they improve the agent's performance for your specific use case.
## Development
While iterating on your graph, you can edit past state and rerun your app from past states to debug specific nodes. Local changes will be automatically applied via hot reload. Try adding an interrupt before the agent calls tools, updating the default system message in `src/retrieval_agent/utils.ts` to take on a persona, or adding additional nodes and edges!
Follow up requests will be appended to the same thread. You can create an entirely new thread, clearing previous history, using the `+` button in the top right.
You can find the latest (under construction) docs on [LangGraph](https://github.com/langchain-ai/langgraphjs) here, including examples and other references. Using those guides can help you pick the right patterns to adapt here for your use case.
LangGraph Studio also integrates with [LangSmith](https://smith.langchain.com/) for more in-depth tracing and collaboration with teammates.
<!--
Configuration auto-generated by `langgraph template lock`. DO NOT EDIT MANUALLY.
{
"config_schemas": {
"indexer": {
"type": "object",
"properties": {
"embeddingModel": {
"type": "string",
"default": "openai/text-embedding-3-small",
"description": "Name of the embedding model to use. Must be a valid embedding model name.",
"environment": [
{
"value": "cohere/embed-english-light-v2.0",
"variables": "COHERE_API_KEY"
},
{
"value": "cohere/embed-english-light-v3.0",
"variables": "COHERE_API_KEY"
},
{
"value": "cohere/embed-english-v2.0",
"variables": "COHERE_API_KEY"
},
{
"value": "cohere/embed-english-v3.0",
"variables": "COHERE_API_KEY"
},
{
"value": "cohere/embed-multilingual-light-v3.0",
"variables": "COHERE_API_KEY"
},
{
"value": "cohere/embed-multilingual-v2.0",
"variables": "COHERE_API_KEY"
},
{
"value": "cohere/embed-multilingual-v3.0",
"variables": "COHERE_API_KEY"
},
{
"value": "openai/text-embedding-3-large",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/text-embedding-3-small",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/text-embedding-ada-002",
"variables": "OPENAI_API_KEY"
}
]
},
"retrieverProvider": {
"enum": [
"elastic",
"elastic-local",
"mongodb",
"pinecone"
],
"default": "elastic",
"description": "The vector store provider to use for retrieval. Options are 'elastic', 'pinecone', or 'mongodb'.",
"environment": [
{
"value": "elastic",
"variables": [
"ELASTICSEARCH_URL",
"ELASTICSEARCH_API_KEY"
]
},
{
"value": "elastic-local",
"variables": [
"ELASTICSEARCH_URL",
"ELASTICSEARCH_USER",
"ELASTICSEARCH_PASSWORD"
]
},
{
"value": "mongodb",
"variables": [
"MONGODB_URI"
]
},
{
"value": "pinecone",
"variables": [
"PINECONE_API_KEY",
"PINECONE_INDEX_NAME"
]
}
]
}
}
},
"retrieval_graph": {
"type": "object",
"properties": {
"embeddingModel": {
"type": "string",
"default": "openai/text-embedding-3-small",
"description": "Name of the embedding model to use. Must be a valid embedding model name.",
"environment": [
{
"value": "cohere/embed-english-light-v2.0",
"variables": "COHERE_API_KEY"
},
{
"value": "cohere/embed-english-light-v3.0",
"variables": "COHERE_API_KEY"
},
{
"value": "cohere/embed-english-v2.0",
"variables": "COHERE_API_KEY"
},
{
"value": "cohere/embed-english-v3.0",
"variables": "COHERE_API_KEY"
},
{
"value": "cohere/embed-multilingual-light-v3.0",
"variables": "COHERE_API_KEY"
},
{
"value": "cohere/embed-multilingual-v2.0",
"variables": "COHERE_API_KEY"
},
{
"value": "cohere/embed-multilingual-v3.0",
"variables": "COHERE_API_KEY"
},
{
"value": "openai/text-embedding-3-large",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/text-embedding-3-small",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/text-embedding-ada-002",
"variables": "OPENAI_API_KEY"
}
]
},
"retrieverProvider": {
"enum": [
"elastic",
"elastic-local",
"mongodb",
"pinecone"
],
"default": "elastic",
"description": "The vector store provider to use for retrieval. Options are 'elastic', 'pinecone', or 'mongodb'.",
"environment": [
{
"value": "elastic",
"variables": [
"ELASTICSEARCH_URL",
"ELASTICSEARCH_API_KEY"
]
},
{
"value": "elastic-local",
"variables": [
"ELASTICSEARCH_URL",
"ELASTICSEARCH_USER",
"ELASTICSEARCH_PASSWORD"
]
},
{
"value": "mongodb",
"variables": [
"MONGODB_URI"
]
},
{
"value": "pinecone",
"variables": [
"PINECONE_API_KEY",
"PINECONE_INDEX_NAME"
]
}
]
},
"responseModel": {
"type": "string",
"default": "anthropic/claude-3-5-sonnet-20240620",
"description": "The language model used for generating responses. Should be in the form: provider/model-name.",
"environment": [
{
"value": "anthropic/claude-1.2",
"variables": "ANTHROPIC_API_KEY"
},
{
"value": "anthropic/claude-2.0",
"variables": "ANTHROPIC_API_KEY"
},
{
"value": "anthropic/claude-2.1",
"variables": "ANTHROPIC_API_KEY"
},
{
"value": "anthropic/claude-3-5-sonnet-20240620",
"variables": "ANTHROPIC_API_KEY"
},
{
"value": "anthropic/claude-3-haiku-20240307",
"variables": "ANTHROPIC_API_KEY"
},
{
"value": "anthropic/claude-3-opus-20240229",
"variables": "ANTHROPIC_API_KEY"
},
{
"value": "anthropic/claude-3-sonnet-20240229",
"variables": "ANTHROPIC_API_KEY"
},
{
"value": "anthropic/claude-instant-1.2",
"variables": "ANTHROPIC_API_KEY"
},
{
"value": "fireworks/gemma2-9b-it",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/llama-v3-70b-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/llama-v3-70b-instruct-hf",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/llama-v3-8b-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/llama-v3-8b-instruct-hf",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/llama-v3p1-405b-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/llama-v3p1-405b-instruct-long",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/llama-v3p1-70b-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/llama-v3p1-8b-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/mixtral-8x22b-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/mixtral-8x7b-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/mixtral-8x7b-instruct-hf",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/mythomax-l2-13b",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/phi-3-vision-128k-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/phi-3p5-vision-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/starcoder-16b",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/yi-large",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "openai/gpt-3.5-turbo",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-3.5-turbo-0125",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-3.5-turbo-0301",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-3.5-turbo-0613",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-3.5-turbo-1106",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-3.5-turbo-16k",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-3.5-turbo-16k-0613",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-0125-preview",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-0314",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-0613",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-1106-preview",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-32k",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-32k-0314",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-32k-0613",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-turbo",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-turbo-preview",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-vision-preview",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4o",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4o-mini",
"variables": "OPENAI_API_KEY"
}
]
},
"queryModel": {
"type": "string",
"default": "openai/gpt-4o-mini",
"description": "The language model used for processing and refining queries. Should be in the form: provider/model-name.",
"environment": [
{
"value": "anthropic/claude-1.2",
"variables": "ANTHROPIC_API_KEY"
},
{
"value": "anthropic/claude-2.0",
"variables": "ANTHROPIC_API_KEY"
},
{
"value": "anthropic/claude-2.1",
"variables": "ANTHROPIC_API_KEY"
},
{
"value": "anthropic/claude-3-5-sonnet-20240620",
"variables": "ANTHROPIC_API_KEY"
},
{
"value": "anthropic/claude-3-haiku-20240307",
"variables": "ANTHROPIC_API_KEY"
},
{
"value": "anthropic/claude-3-opus-20240229",
"variables": "ANTHROPIC_API_KEY"
},
{
"value": "anthropic/claude-3-sonnet-20240229",
"variables": "ANTHROPIC_API_KEY"
},
{
"value": "anthropic/claude-instant-1.2",
"variables": "ANTHROPIC_API_KEY"
},
{
"value": "fireworks/gemma2-9b-it",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/llama-v3-70b-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/llama-v3-70b-instruct-hf",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/llama-v3-8b-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/llama-v3-8b-instruct-hf",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/llama-v3p1-405b-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/llama-v3p1-405b-instruct-long",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/llama-v3p1-70b-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/llama-v3p1-8b-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/mixtral-8x22b-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/mixtral-8x7b-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/mixtral-8x7b-instruct-hf",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/mythomax-l2-13b",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/phi-3-vision-128k-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/phi-3p5-vision-instruct",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/starcoder-16b",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "fireworks/yi-large",
"variables": "FIREWORKS_API_KEY"
},
{
"value": "openai/gpt-3.5-turbo",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-3.5-turbo-0125",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-3.5-turbo-0301",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-3.5-turbo-0613",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-3.5-turbo-1106",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-3.5-turbo-16k",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-3.5-turbo-16k-0613",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-0125-preview",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-0314",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-0613",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-1106-preview",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-32k",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-32k-0314",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-32k-0613",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-turbo",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-turbo-preview",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4-vision-preview",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4o",
"variables": "OPENAI_API_KEY"
},
{
"value": "openai/gpt-4o-mini",
"variables": "OPENAI_API_KEY"
}
]
}
}
}
}
}
-->
+2 -2
View File
@@ -1,8 +1,8 @@
{
"node_version": "20",
"graphs": {
"retrieval_graph": "./retrieval-graph/graph.ts:graph",
"indexer": "./retrieval-graph/index_graph.ts:graph"
"retrieval_graph": "./src/retrieval_graph/graph.ts:graph",
"indexer": "./src/retrieval_graph/index_graph.ts:graph"
},
"env": ".env"
}
+9 -5
View File
@@ -1,8 +1,8 @@
{
"name": "example-graph",
"name": "retrieval-graph",
"version": "0.0.1",
"description": "A starter template for creating a LangGraph workflow.",
"main": "graph.ts",
"main": "src/retrieval_graph/graph.ts",
"author": "Your Name",
"license": "MIT",
"private": true,
@@ -16,9 +16,13 @@
},
"dependencies": {
"@elastic/elasticsearch": "^8.15.0",
"@langchain/community": "^0.2.31",
"@langchain/langgraph": "^0.1.2",
"langchain": "^0.2.17",
"@langchain/anthropic": "^0.3.1",
"@langchain/cohere": "^0.3.0",
"@langchain/community": "^0.3.0",
"@langchain/langgraph": "^0.2.3",
"@langchain/mongodb": "^0.1.0",
"@langchain/pinecone": "^0.1.0",
"langchain": "^0.3.2",
"ts-node": "^10.9.2"
},
"resolutions": {
View File
View File
-63
View File
@@ -1,63 +0,0 @@
/**
* Define the configurable parameters for the agent.
*/
import { RunnableConfig } from "@langchain/core/runnables";
export interface IndexConfiguration {
embeddingModelName: string;
searchKwargs: Record<string, any>;
userId: string;
}
export function ensureIndexConfigurable(
config: RunnableConfig | undefined = undefined,
): IndexConfiguration {
// Ensure the defaults are populated.
const configurable = (config?.configurable || {}) as Record<string, any>;
return {
userId: configurable.userId,
embeddingModelName:
configurable.embeddingModelName || "text-embedding-3-small",
searchKwargs: configurable.searchKwargs || {},
};
}
interface Configuration extends IndexConfiguration {
responseSystemPrompt: string;
responseModelName: string;
querySystemPrompt: string;
queryModelName: string;
threadId: string;
}
export function ensureConfigurable(
config: RunnableConfig | undefined = undefined,
): Configuration {
// Ensure the defaults are populated.
const configurable = (config?.configurable || {}) as Record<string, any>;
return {
userId: configurable.userId,
threadId: configurable.threadId,
responseSystemPrompt:
configurable.responseSystemPrompt ||
"You are a helpful AI assistant. Answer the user's questions based on the retrieved documents." +
"\n\n{retrievedDocs}" +
"\n\nSystem time: {systemTime}",
responseModelName:
configurable.responseModelName || "claude-3-5-sonnet-20240620",
querySystemPrompt:
configurable.querySystemPrompt ||
"Generate search queries to retrieve documents that may help answer the user's question." +
"\n\nPreviously, you made the following queries:<previousQueries/>" +
"\n{queries}\n<\\previousQueries/>\n\nSystem time: {systemTime}",
queryModelName:
configurable.queryModelName ||
"accounts/fireworks/models/firefunction-v2",
// In general, you could make these things nested configs and load from a file to get the
// defaults. But for now, we'll keep it simple.
embeddingModelName:
configurable.embeddingModelName || "text-embedding-3-small",
searchKwargs: configurable.searchKwargs || {},
};
}
-103
View File
@@ -1,103 +0,0 @@
import { BaseMessage } from "@langchain/core/messages";
import { RunnableConfig } from "@langchain/core/runnables";
import { Annotation } from "@langchain/langgraph";
import { messagesStateReducer } from "@langchain/langgraph";
import { v4 as uuidv4 } from "uuid";
import { Client } from "@elastic/elasticsearch";
import { OpenAIEmbeddings } from "@langchain/openai";
import { ElasticVectorSearch } from "@langchain/community/vectorstores/elasticsearch";
import {
VectorStoreRetriever,
} from "@langchain/core/vectorstores";
import { Document } from "@langchain/core/documents";
import { ensureConfigurable } from "./configuration.js";
export function reduceDocs(
existing?: Document[],
newDocs?:
| Document[]
| { [key: string]: any }[]
| string[]
| string
| "delete",
) {
if (newDocs === "delete") {
return [];
}
if (typeof newDocs === "string") {
return [{ pageContent: newDocs, metadata: { id: uuidv4() } }];
}
if (Array.isArray(newDocs)) {
const coerced: Document[] = [];
for (const item of newDocs) {
if (typeof item === "string") {
coerced.push({ pageContent: item, metadata: { id: uuidv4() } });
} else if (typeof item === "object") {
coerced.push(item as Document);
}
}
return coerced;
}
return existing || [];
}
export async function makeRetriever(
config: RunnableConfig,
): Promise<VectorStoreRetriever> {
const configuration = ensureConfigurable(config);
const embeddingModel = new OpenAIEmbeddings({
model: configuration.embeddingModelName,
});
const userId = configuration.userId;
if (!userId) {
throw new Error("Please provide a valid user_id in the configuration.");
}
const client = new Client({
node: process.env.ELASTICSEARCH_URL,
auth: {
apiKey: process.env.ELASTICSEARCH_API_KEY || "",
},
});
const vectorStore = new ElasticVectorSearch(embeddingModel, {
client,
indexName: "langchain_index",
});
const searchKwargs = configuration.searchKwargs || {};
searchKwargs.filter = searchKwargs.filter || [];
searchKwargs.filter.push({ term: { "metadata.user_id": userId } });
return vectorStore.asRetriever({ searchKwargs });
}
export const IndexState = Annotation.Root({
docs: Annotation<Document[], Document[] | string | string[]>({
reducer: reduceDocs,
default: () => [],
}),
});
export type IndexStateT = typeof IndexState.State;
export function addQueries(existing: string[], newQueries: string[]): string[] {
return [...existing, ...newQueries];
}
/**
* The State defines three things:
* 1. The structure of the graph's state (which "channels" are available to read/write)
* 2. The default values for the state's channels
* 3. The reducers for the state's channels. Reducers are functions that determine how to apply updates to the state.
* See [Reducers](https://langchain-ai.github.io/langgraphjs/concepts/low_level/#reducers) for more information.
*/
export const State = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: messagesStateReducer,
default: () => [],
}),
queries: Annotation<string[]>({ reducer: addQueries, default: () => [] }),
retrievedDocs: Annotation<Document[]>,
});
export type StateT = typeof State.State;
+104
View File
@@ -0,0 +1,104 @@
/**
* Define the configurable parameters for the agent.
*/
import { RunnableConfig } from "@langchain/core/runnables";
import { RESPONSE_SYSTEM_PROMPT, QUERY_SYSTEM_PROMPT } from "./prompts.js";
/**
* Configuration class for indexing and retrieval operations.
*
* This interface defines the parameters needed for configuring the indexing and
* retrieval processes, including user identification, embedding model selection,
* retriever provider choice, and search parameters.
*/
export interface IndexConfiguration {
/**
* Unique identifier for the user.
*/
userId: string;
/**
* Name of the embedding model to use. Must be a valid embedding model name.
*/
embeddingModel: string;
/**
* The vector store provider to use for retrieval.
* Options are 'elastic', 'elastic-local', 'pinecone', or 'mongodb'.
*/
retrieverProvider: "elastic" | "elastic-local" | "pinecone" | "mongodb";
/**
* Additional keyword arguments to pass to the search function of the retriever.
*/
searchKwargs: Record<string, any>;
}
/**
* Create an IndexConfiguration instance from a RunnableConfig object.
*
* @param config - The configuration object to use.
* @returns An instance of IndexConfiguration with the specified configuration.
*/
export function ensureIndexConfiguration(
config: RunnableConfig | undefined = undefined,
): IndexConfiguration {
const configurable = (config?.configurable ||
{}) as Partial<IndexConfiguration>;
return {
userId: configurable.userId || "default", // Give a default user for shared docs
embeddingModel:
configurable.embeddingModel || "openai/text-embedding-3-small",
retrieverProvider: configurable.retrieverProvider || "elastic",
searchKwargs: configurable.searchKwargs || {},
};
}
/**
* The complete configuration for the agent.
*/
export interface Configuration extends IndexConfiguration {
/**
* The system prompt used for generating responses.
*/
responseSystemPrompt: string;
/**
* The language model used for generating responses. Should be in the form: provider/model-name.
*/
responseModel: string;
/**
* The system prompt used for processing and refining queries.
*/
querySystemPrompt: string;
/**
* The language model used for processing and refining queries. Should be in the form: provider/model-name.
*/
queryModel: string;
}
/**
* Create a Configuration instance from a RunnableConfig object.
*
* @param config - The configuration object to use.
* @returns An instance of Configuration with the specified configuration.
*/
export function ensureConfiguration(
config: RunnableConfig | undefined = undefined,
): Configuration {
const indexConfig = ensureIndexConfiguration(config);
const configurable = (config?.configurable || {}) as Partial<Configuration>;
return {
...indexConfig,
responseSystemPrompt:
configurable.responseSystemPrompt || RESPONSE_SYSTEM_PROMPT,
responseModel:
configurable.responseModel || "anthropic/claude-3-5-sonnet-20240620",
querySystemPrompt: configurable.querySystemPrompt || QUERY_SYSTEM_PROMPT,
queryModel: configurable.queryModel || "openai/gpt-4o-mini",
};
}
@@ -1,11 +1,11 @@
import { initChatModel } from "langchain/chat_models/universal";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { RunnableConfig } from "@langchain/core/runnables";
import { StateGraph } from "@langchain/langgraph";
import { ensureConfigurable } from "./utils/configuration.js";
import { makeRetriever, State, StateT } from "./utils/state.js";
import { formatDocs, getMessageText } from "./utils/utils.js";
import { ensureConfiguration } from "./configuration.js";
import { StateAnnotation, State, InputStateAnnotation } from "./state.js";
import { formatDocs, getMessageText, loadChatModel } from "./utils.js";
import { z } from "zod";
import { makeRetriever } from "./retrieval.js";
// Define the function that calls the model
const SearchQuery = z.object({
@@ -13,7 +13,7 @@ const SearchQuery = z.object({
});
async function generateQuery(
state: StateT,
state: State,
config?: RunnableConfig,
): Promise<{ queries: string[] }> {
const messages = state.messages;
@@ -22,14 +22,14 @@ async function generateQuery(
const humanInput = getMessageText(messages[messages.length - 1]);
return { queries: [humanInput] };
} else {
const configuration = ensureConfigurable(config);
const configuration = ensureConfiguration(config);
// Feel free to customize the prompt, model, and other logic!
const prompt = ChatPromptTemplate.fromMessages([
["system", configuration.querySystemPrompt],
["placeholder", "{messages}"],
]);
const model = (
await initChatModel(configuration.responseModelName)
await loadChatModel(configuration.responseModel)
).withStructuredOutput(SearchQuery);
const messageValue = await prompt.invoke(
@@ -48,7 +48,7 @@ async function generateQuery(
}
async function retrieve(
state: StateT,
state: State,
config: RunnableConfig,
): Promise<{ retrievedDocs: any[] }> {
const query = state.queries[state.queries.length - 1];
@@ -57,17 +57,17 @@ async function retrieve(
return { retrievedDocs: response };
}
async function respond(state: StateT, config: RunnableConfig) {
async function respond(state: State, config: RunnableConfig) {
/**
* Call the LLM powering our "agent".
*/
const configuration = ensureConfigurable(config);
const configuration = ensureConfiguration(config);
// Feel free to customize the prompt, model, and other logic!
const prompt = ChatPromptTemplate.fromMessages([
["system", configuration.responseSystemPrompt],
["placeholder", "{messages}"],
]);
const model = await initChatModel(configuration.responseModelName);
const model = await loadChatModel(configuration.responseModel);
const retrievedDocs = formatDocs(state.retrievedDocs);
const messageValue = await prompt.invoke(
@@ -85,7 +85,10 @@ async function respond(state: StateT, config: RunnableConfig) {
// Define a new graph (It's just a pipe)
const builder = new StateGraph(State)
const builder = new StateGraph({
stateSchema: StateAnnotation,
input: InputStateAnnotation,
})
.addNode("generateQuery", generateQuery)
.addNode("retrieve", retrieve)
.addNode("respond", respond)
+1
View File
@@ -0,0 +1 @@
export { graph } from "./graph.js";
@@ -6,7 +6,8 @@ import { Document } from "@langchain/core/documents";
import { RunnableConfig } from "@langchain/core/runnables";
import { StateGraph } from "@langchain/langgraph";
import { IndexState, IndexStateT, makeRetriever } from "./utils/state.js";
import { IndexStateAnnotation, IndexState } from "./state.js";
import { makeRetriever } from "./retrieval.js";
function ensureDocsHaveUserId(
docs: Document[],
@@ -22,13 +23,13 @@ function ensureDocsHaveUserId(
}
async function indexDocs(
state: IndexStateT,
state: IndexState,
config?: RunnableConfig,
): Promise<{ docs: string }> {
if (!config) {
throw new Error("Configuration required to run index_docs.");
}
const docs = state["docs"];
const docs = state.docs;
const retriever = await makeRetriever(config);
const stampedDocs = ensureDocsHaveUserId(docs, config);
@@ -38,7 +39,7 @@ async function indexDocs(
// Define a new graph
const builder = new StateGraph(IndexState)
const builder = new StateGraph(IndexStateAnnotation)
.addNode("indexDocs", indexDocs)
.addEdge("__start__", "indexDocs");
+17
View File
@@ -0,0 +1,17 @@
/**
* Default prompts.
*/
export const RESPONSE_SYSTEM_PROMPT: string = `You are a helpful AI assistant. Answer the user's questions based on the retrieved documents.
{retrievedDocs}
System time: {systemTime}`;
export const QUERY_SYSTEM_PROMPT: string = `Generate search queries to retrieve documents that may help answer the user's question. Previously, you made the following queries:
<previous_queries/>
{queries}
</previous_queries>
System time: {systemTime}`;
+131
View File
@@ -0,0 +1,131 @@
import { Client } from "@elastic/elasticsearch";
import { ElasticVectorSearch } from "@langchain/community/vectorstores/elasticsearch";
import { RunnableConfig } from "@langchain/core/runnables";
import { VectorStoreRetriever } from "@langchain/core/vectorstores";
import { MongoDBAtlasVectorSearch } from "@langchain/mongodb";
import { PineconeStore } from "@langchain/pinecone";
import { MongoClient } from "mongodb";
import { ensureConfiguration } from "./configuration.js";
import { Pinecone as PineconeClient } from "@pinecone-database/pinecone";
import { Embeddings } from "@langchain/core/embeddings";
import { CohereEmbeddings } from "@langchain/cohere";
import { OpenAIEmbeddings } from "@langchain/openai";
async function makeElasticRetriever(
configuration: ReturnType<typeof ensureConfiguration>,
embeddingModel: Embeddings,
): Promise<VectorStoreRetriever> {
const client = new Client({
node: process.env.ELASTICSEARCH_URL,
auth:
configuration.retrieverProvider === "elastic-local"
? {
username: process.env.ELASTICSEARCH_USER || "",
password: process.env.ELASTICSEARCH_PASSWORD || "",
}
: {
apiKey: process.env.ELASTICSEARCH_API_KEY || "",
},
});
const vectorStore = new ElasticVectorSearch(embeddingModel, {
client,
indexName: "langchain_index",
});
const searchKwargs = configuration.searchKwargs || {};
searchKwargs.filter = searchKwargs.filter || [];
searchKwargs.filter.push({
term: { "metadata.user_id": configuration.userId },
});
return vectorStore.asRetriever({ searchKwargs });
}
async function makePineconeRetriever(
configuration: ReturnType<typeof ensureConfiguration>,
embeddingModel: Embeddings,
): Promise<VectorStoreRetriever> {
const indexName = process.env.PINECONE_INDEX_NAME;
if (!indexName) {
throw new Error("PINECONE_INDEX_NAME environment variable is not defined");
}
const pinecone = new PineconeClient();
const pineconeIndex = pinecone.Index(process.env.PINECONE_INDEX!);
const vectorStore = await PineconeStore.fromExistingIndex(embeddingModel, {
pineconeIndex,
});
const searchKwargs = configuration.searchKwargs || {};
searchKwargs.filter = searchKwargs.filter || {};
searchKwargs.filter.user_id = configuration.userId;
return vectorStore.asRetriever({ searchKwargs });
}
async function makeMongoDBRetriever(
configuration: ReturnType<typeof ensureConfiguration>,
embeddingModel: Embeddings,
): Promise<VectorStoreRetriever> {
const client = new MongoClient(process.env.MONGODB_ATLAS_URI || "");
const namespace = `langgraph_retrieval_agent.${configuration.userId}`;
const [dbName, collectionName] = namespace.split(".");
const collection = client.db(dbName).collection(collectionName);
const vectorStore = new MongoDBAtlasVectorSearch(embeddingModel, {
collection: collection,
indexName: process.env.MONGODB_INDEX_NAME || "",
textKey: "text",
embeddingKey: "embedding",
});
return vectorStore.asRetriever();
}
function makeTextEncoder(modelName: string): Embeddings {
/**
* Connect to the configured text encoder.
*/
const index = modelName.indexOf("/");
let provider, model;
if (index === -1) {
model = modelName;
provider = "openai"; // Assume openai if no provider included
} else {
provider = modelName.slice(0, index);
model = modelName.slice(index + 1);
}
switch (provider) {
case "openai":
return new OpenAIEmbeddings({ model });
case "cohere":
return new CohereEmbeddings({ model });
default:
throw new Error(`Unsupported embedding provider: ${provider}`);
}
}
export async function makeRetriever(
config: RunnableConfig,
): Promise<VectorStoreRetriever> {
const configuration = ensureConfiguration(config);
const embeddingModel = makeTextEncoder(configuration.embeddingModel);
const userId = configuration.userId;
if (!userId) {
throw new Error("Please provide a valid user_id in the configuration.");
}
switch (configuration.retrieverProvider) {
case "elastic":
case "elastic-local":
return makeElasticRetriever(configuration, embeddingModel);
case "pinecone":
return makePineconeRetriever(configuration, embeddingModel);
case "mongodb":
return makeMongoDBRetriever(configuration, embeddingModel);
default:
throw new Error(
`Unrecognized retrieverProvider in configuration: ${configuration.retrieverProvider}`,
);
}
}
+112
View File
@@ -0,0 +1,112 @@
import { Document } from "@langchain/core/documents";
import { BaseMessage } from "@langchain/core/messages";
import { Annotation, messagesStateReducer } from "@langchain/langgraph";
import { v4 as uuidv4 } from "uuid";
/**
* Reduces the document array based on the provided new documents or actions.
*
* @param existing - The existing array of documents.
* @param newDocs - The new documents or actions to apply.
* @returns The updated array of documents.
*/
export function reduceDocs(
existing?: Document[],
newDocs?:
| Document[]
| { [key: string]: any }[]
| string[]
| string
| "delete",
) {
// Supports deletion by returning an empty array when "delete" is specified
if (newDocs === "delete") {
return [];
}
// Supports adding a single string document
if (typeof newDocs === "string") {
return [{ pageContent: newDocs, metadata: { id: uuidv4() } }];
}
// User can provide "docs" content in a few different ways
if (Array.isArray(newDocs)) {
const coerced: Document[] = [];
for (const item of newDocs) {
if (typeof item === "string") {
coerced.push({ pageContent: item, metadata: { id: uuidv4() } });
} else if (typeof item === "object") {
const doc = item as Document;
if (!doc.metadata || !doc.metadata.id) {
doc.metadata = doc.metadata || {};
doc.metadata.id = uuidv4();
}
coerced.push(doc);
}
}
return coerced;
}
// Returns existing documents if no valid update is provided
return existing || [];
}
/**
* Defines the structure and behavior of the index state.
* This state is used to manage the documents in the index.
*/
export const IndexStateAnnotation = Annotation.Root({
/**
* Stores the documents in the index.
* @type {Document[]}
* @reducer reduceDocs - Handles updates to the documents array, including adding new documents.
* @default An empty array.
*/
docs: Annotation<Document[], Document[] | string | string[]>({
reducer: reduceDocs,
default: () => [],
}),
});
export type IndexState = typeof IndexStateAnnotation.State;
export function addQueries(existing: string[], newQueries: string[]): string[] {
return [...existing, ...newQueries];
}
/**
* This narrows the interface with the user.
*/
export const InputStateAnnotation = Annotation.Root({
messages: Annotation<BaseMessage[]>,
});
/**
* The State defines three things:
* 1. The structure of the graph's state (which "channels" are available to read/write)
* 2. The default values for the state's channels
* 3. The reducers for the state's channels. Reducers are functions that determine how to apply updates to the state.
* See [Reducers](https://langchain-ai.github.io/langgraphjs/concepts/low_level/#reducers) for more information.
*/
export const StateAnnotation = Annotation.Root({
/**
* Stores the conversation messages.
* @type {BaseMessage[]}
* @reducer messagesStateReducer - Handles updates to the messages array.
* @default An empty array.
*/
messages: Annotation<BaseMessage[]>({
reducer: messagesStateReducer,
default: () => [],
}),
/**
* Stores the user queries.
* @type {string[]}
* @reducer addQueries - Handles adding new queries to the existing ones.
* @default An empty array.
*/
queries: Annotation<string[]>({ reducer: addQueries, default: () => [] }),
/**
* Stores the retrieved documents.
* @type {Document[]}
*/
retrievedDocs: Annotation<Document[]>,
});
export type State = typeof StateAnnotation.State;
@@ -1,5 +1,8 @@
import { Document } from "langchain/document";
import { BaseMessage } from "@langchain/core/messages";
import { Document } from "langchain/document";
import { BaseChatModel } from "@langchain/core/language_models/chat_models";
import { initChatModel } from "langchain/chat_models/universal";
export function getMessageText(msg: BaseMessage): string {
/**Get the text content of a message. */
@@ -32,3 +35,21 @@ export function formatDocs(docs?: Document[]): string {
const formatted = docs.map(formatDoc).join("\n");
return `<documents>\n${formatted}\n</documents>`;
}
/**
* Load a chat model from a fully specified name.
* @param fullySpecifiedName - String in the format 'provider/model' or 'provider/account/provider/model'.
* @returns A Promise that resolves to a BaseChatModel instance.
*/
export async function loadChatModel(
fullySpecifiedName: string,
): Promise<BaseChatModel> {
const index = fullySpecifiedName.indexOf("/");
if (index === -1) {
// If there's no "/", assume it's just the model
return await initChatModel(fullySpecifiedName);
} else {
const provider = fullySpecifiedName.slice(0, index);
const model = fullySpecifiedName.slice(index + 1);
return await initChatModel(model, { modelProvider: provider });
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 624 KiB

+1505 -53
View File
File diff suppressed because it is too large Load Diff