├── .dockerignore ├── .env.example ├── examples ├── chat-js │ ├── .npmprc │ ├── .env.example │ ├── README.md │ ├── package.json │ ├── index.js │ └── package-lock.json ├── chat-py │ ├── .env.sample │ ├── .gitignore │ ├── README.md │ ├── requirements.txt │ └── main.py ├── chat-vanilla-js │ ├── .env.example │ ├── .npmrc │ ├── README.md │ ├── package.json │ ├── helpers.js │ ├── index.js │ └── package-lock.json └── chat-vanilla-js-hosted │ ├── .npmrc │ ├── .env.example │ ├── package.json │ ├── README.md │ ├── helpers.js │ ├── index.js │ └── package-lock.json ├── .github ├── workflows │ ├── build_test.yml │ └── publish_docker.yml └── ISSUE_TEMPLATE │ ├── sweep-template.yml │ ├── sweep-fast-template.yml │ └── sweep-slow-template.yml ├── src ├── healthcheck.rs ├── retrieval.rs ├── redis_utils.rs ├── long_term_memory.rs ├── main.rs ├── reducer.rs ├── memory.rs └── models.rs ├── docker-compose.yml ├── Cargo.toml ├── Dockerfile ├── .gitignore ├── sweep.yaml ├── README.md └── LICENSE /.dockerignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .git/ 3 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY= 2 | REDIS_URL= 3 | -------------------------------------------------------------------------------- /examples/chat-js/.npmprc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /examples/chat-py/.env.sample: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY= 2 | -------------------------------------------------------------------------------- /examples/chat-js/.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY= 2 | -------------------------------------------------------------------------------- /examples/chat-py/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | venv 3 | .idea 4 | -------------------------------------------------------------------------------- /examples/chat-vanilla-js/.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY= 2 | -------------------------------------------------------------------------------- /examples/chat-vanilla-js/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /examples/chat-vanilla-js-hosted/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /examples/chat-vanilla-js-hosted/.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY= 2 | METAL_API_KEY= 3 | METAL_CLIENT_ID= 4 | -------------------------------------------------------------------------------- /examples/chat-js/README.md: -------------------------------------------------------------------------------- 1 | ## Chat example in JS 2 | 3 | ### Requirements 4 | - Node 18 5 | - An OPENAI_API_KEY 6 | - Set it in the `.env` file for the server and this project (where the `.env.example` are) 7 | 8 | ### How to run 9 | 10 | In the server root, run: 11 | ```bash 12 | docker-compose build 13 | docker-compose up 14 | ``` 15 | 16 | In this folder run: 17 | ```bash 18 | npm i 19 | npm start 20 | ``` 21 | 22 | Start chatting! 23 | -------------------------------------------------------------------------------- /.github/workflows/build_test.yml: -------------------------------------------------------------------------------- 1 | name: Build and test 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /src/healthcheck.rs: -------------------------------------------------------------------------------- 1 | use crate::models::HealthCheckResponse; 2 | use actix_web::{get, web, Responder}; 3 | use std::time::{SystemTime, UNIX_EPOCH}; 4 | 5 | #[get("/")] 6 | pub async fn get_health() -> actix_web::Result { 7 | let ms = SystemTime::now() 8 | .duration_since(UNIX_EPOCH) 9 | .unwrap() 10 | .as_millis(); 11 | 12 | let res = HealthCheckResponse { now: ms }; 13 | 14 | Ok(web::Json(res)) 15 | } 16 | -------------------------------------------------------------------------------- /examples/chat-vanilla-js/README.md: -------------------------------------------------------------------------------- 1 | ## Chat example: Vanilla JS (no langchain) 2 | 3 | ### Requirements 4 | - Node 18 5 | - An OPENAI_API_KEY 6 | - Set it in the `.env` file for the server and this project (where the `.env.example` are) 7 | 8 | ### How to run 9 | 10 | In the server root, run: 11 | ```bash 12 | docker-compose build 13 | docker-compose up 14 | ``` 15 | 16 | In this folder run: 17 | ```bash 18 | npm i 19 | npm start 20 | ``` 21 | 22 | Start chatting! 23 | -------------------------------------------------------------------------------- /examples/chat-py/README.md: -------------------------------------------------------------------------------- 1 | ## Chat example in Python 2 | 3 | ### Steps 4 | - Create a .env file with the openai api key. Look into .env.sample for reference. 5 | - Run the below commands to execute the chat app for the first time. 6 | 7 | In the server root, run: 8 | ```bash 9 | docker-compose build 10 | docker-compose up 11 | ``` 12 | Then in this folder: 13 | ```bash 14 | python3.11 -m venv venv 15 | . ./venv/bin/activate 16 | pip install -r requirements.txt 17 | python main.py 18 | ``` 19 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | motorhead: 4 | build: ./ 5 | ports: 6 | - '8080:8080' 7 | links: 8 | - redis 9 | environment: 10 | PORT: 8080 11 | MOTORHEAD_MAX_WINDOW_SIZE: 25 12 | MOTORHEAD_LONG_TERM_MEMORY: 'true' 13 | MOTORHEAD_MODEL: 'gpt-3.5-turbo' 14 | REDIS_URL: 'redis://redis:6379' 15 | env_file: 16 | - .env 17 | 18 | redis: 19 | image: redis/redis-stack-server:latest 20 | ports: 21 | - '6379:6379' 22 | -------------------------------------------------------------------------------- /examples/chat-vanilla-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat-vanilla", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "engines": { 8 | "node": ">=18.0.0" 9 | }, 10 | "scripts": { 11 | "start": "node index.js", 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "dependencies": { 18 | "chalk": "^5.2.0", 19 | "dotenv": "^16.0.3", 20 | "openai": "^4.17.4" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/chat-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat-motorhead-example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "engines": { 8 | "node": ">=18.0.0" 9 | }, 10 | "scripts": { 11 | "start": "node index.js", 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "dependencies": { 18 | "chalk": "^5.2.0", 19 | "dotenv": "^16.0.3", 20 | "langchain": "^0.2.19" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/chat-vanilla-js-hosted/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat-vanilla-js-hosted", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "engines": { 8 | "node": ">=18.0.0" 9 | }, 10 | "scripts": { 11 | "start": "node index.js", 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "dependencies": { 18 | "chalk": "^5.2.0", 19 | "dotenv": "^16.0.3", 20 | "openai": "^4.17.4" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/chat-vanilla-js-hosted/README.md: -------------------------------------------------------------------------------- 1 | ## Chat example JS Hosted motorhead (in Metal) 2 | 3 | ### Requirements 4 | - Node 18 5 | - An OPENAI_API_KEY 6 | - Set it in the `.env` file for the server and this project (where the `.env.example` are) 7 | - A metal account, a api key and client id. 8 | - Sign up [here](https://getmetal.io/) 9 | - Obtain an Api key and client id [here](https://app.getmetal.io/settings/organization) 10 | 11 | ### How to run 12 | 13 | In this folder run: 14 | ```bash 15 | npm i 16 | npm start 17 | ``` 18 | 19 | Start chatting! 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/sweep-template.yml: -------------------------------------------------------------------------------- 1 | name: Sweep Issue 2 | title: 'Sweep: ' 3 | description: For small bugs, features, refactors, and tests to be handled by Sweep, an AI-powered junior developer. 4 | labels: sweep 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Details 10 | description: Tell Sweep where and what to edit and provide enough context for a new developer to the codebase 11 | placeholder: | 12 | Bugs: The bug might be in ... file. Here are the logs: ... 13 | Features: the new endpoint should use the ... class from ... file because it contains ... logic. 14 | Refactors: We are migrating this function to ... version because ... 15 | -------------------------------------------------------------------------------- /examples/chat-py/requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.10.11 2 | aiosignal==1.3.1 3 | async-timeout==4.0.2 4 | attrs==23.1.0 5 | certifi==2024.7.4 6 | charset-normalizer==3.1.0 7 | dataclasses-json==0.5.7 8 | frozenlist==1.3.3 9 | idna==3.7 10 | langchain==0.3.7 11 | marshmallow==3.19.0 12 | marshmallow-enum==1.5.1 13 | multidict==6.0.4 14 | mypy-extensions==1.0.0 15 | numexpr==2.8.5 16 | numpy==1.24.3 17 | openai==0.27.5 18 | openapi-schema-pydantic==1.2.4 19 | packaging==23.1 20 | pydantic==1.10.13 21 | python-dotenv==1.0.0 22 | PyYAML==6.0 23 | requests==2.32.4 24 | SQLAlchemy==2.0.11 25 | tenacity==8.2.2 26 | termcolor==2.3.0 27 | tqdm==4.66.3 28 | typing-inspect==0.8.0 29 | typing_extensions==4.5.0 30 | urllib3==1.26.19 31 | yarl==1.9.2 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/sweep-fast-template.yml: -------------------------------------------------------------------------------- 1 | name: Sweep Fast Issue 2 | title: 'Sweep (fast): ' 3 | description: For few-line fixes to be handled by Sweep, an AI-powered junior developer. Sweep will use GPT-3.5 to quickly create a PR for very small changes. 4 | labels: sweep 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Details 10 | description: Tell Sweep where and what to edit and provide enough context for a new developer to the codebase 11 | placeholder: | 12 | Bugs: The bug might be in ... file. Here are the logs: ... 13 | Features: the new endpoint should use the ... class from ... file because it contains ... logic. 14 | Refactors: We are migrating this function to ... version because ... 15 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "motorhead" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Sergio Prada "] 6 | 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | actix-web = "4.3" 12 | async-openai = "0.11.0" 13 | async-trait = "0.1.68" 14 | byteorder = "1.4.3" 15 | chrono = "0.4.24" 16 | deadpool = "0.9.5" 17 | env_logger = "0.10" 18 | futures-util = { version = "0.3.17", default-features = false, features = ["std"] } 19 | log = "0.4" 20 | nanoid = "0.4.0" 21 | redis = { version = "0.22", default-features = false, features = ["tokio-comp", "connection-manager"] } 22 | serde = { version = "1", features = ["derive"] } 23 | tiktoken-rs = "0.4.1" 24 | tokio = { version = "1", features = ["full"] } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/sweep-slow-template.yml: -------------------------------------------------------------------------------- 1 | name: Sweep Slow Issue 2 | title: 'Sweep (slow): ' 3 | description: For larger bugs, features, refactors, and tests to be handled by Sweep, an AI-powered junior developer. Sweep will perform a deeper search and more self-reviews but will take longer. 4 | labels: sweep 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Details 10 | description: Tell Sweep where and what to edit and provide enough context for a new developer to the codebase 11 | placeholder: | 12 | Bugs: The bug might be in ... file. Here are the logs: ... 13 | Features: the new endpoint should use the ... class from ... file because it contains ... logic. 14 | Refactors: We are migrating this function to ... version because ... 15 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.68-bullseye as build 2 | 3 | # had to add this for open-ssl 4 | RUN apt-get update -y && \ 5 | apt-get install -y pkg-config make g++ libssl-dev ca-certificates && \ 6 | rustup target add x86_64-unknown-linux-gnu 7 | 8 | RUN USER=root cargo new --bin motorhead 9 | WORKDIR /motorhead 10 | 11 | COPY ./Cargo.lock ./Cargo.lock 12 | COPY ./Cargo.toml ./Cargo.toml 13 | 14 | # cache dependencies 15 | ENV CARGO_NET_GIT_FETCH_WITH_CLI=true 16 | RUN cargo build --release 17 | RUN rm src/*.rs 18 | 19 | # copy your source tree 20 | COPY ./src ./src 21 | 22 | # build for release 23 | RUN rm ./target/release/deps/motorhead* 24 | RUN cargo build --release 25 | 26 | FROM debian:bullseye 27 | 28 | RUN apt-get update && apt install -y openssl ca-certificates 29 | 30 | COPY --from=build /motorhead/target/release/motorhead . 31 | 32 | CMD ["./motorhead"] 33 | -------------------------------------------------------------------------------- /src/retrieval.rs: -------------------------------------------------------------------------------- 1 | use crate::long_term_memory::search_messages; 2 | use crate::models::{AppState, SearchPayload}; 3 | use actix_web::{error, post, web, HttpResponse, Responder}; 4 | use std::ops::Deref; 5 | use std::sync::Arc; 6 | 7 | #[post("/sessions/{session_id}/retrieval")] 8 | pub async fn run_retrieval( 9 | session_id: web::Path, 10 | web::Json(payload): web::Json, 11 | data: web::Data>, 12 | redis: web::Data, 13 | ) -> actix_web::Result { 14 | if !data.long_term_memory { 15 | return Ok(HttpResponse::BadRequest().body("Long term memory is disabled")); 16 | } 17 | 18 | let conn = redis 19 | .get_tokio_connection_manager() 20 | .await 21 | .map_err(error::ErrorInternalServerError)?; 22 | 23 | let client_wrapper = data.openai_pool.get().await.unwrap(); 24 | let openai_client = client_wrapper.deref(); 25 | 26 | match search_messages(payload.text, session_id.clone(), openai_client, conn).await { 27 | Ok(results) => Ok(HttpResponse::Ok().json(results)), 28 | Err(e) => { 29 | log::error!("Error Retrieval API: {:?}", e); 30 | Ok(HttpResponse::InternalServerError().body("Internal server error")) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/publish_docker.yml: -------------------------------------------------------------------------------- 1 | name: Publish a Docker image 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | env: 8 | REGISTRY: ghcr.io 9 | IMAGE_NAME: ${{ github.repository }} 10 | 11 | jobs: 12 | build-and-push-image: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | packages: write 17 | 18 | steps: 19 | - name: Checkout repo 20 | uses: actions/checkout@v3 21 | 22 | - name: Set up Depot CLI 23 | uses: depot/setup-action@v1 24 | 25 | - name: Log in to the Container registry 26 | uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 27 | with: 28 | registry: ${{ env.REGISTRY }} 29 | username: ${{ github.actor }} 30 | password: ${{ secrets.GITHUB_TOKEN }} 31 | 32 | - name: Extract metadata (tags, labels) for Docker 33 | id: meta 34 | uses: docker/metadata-action@v5 35 | with: 36 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 37 | 38 | - name: Build and push 39 | uses: depot/build-push-action@v1 40 | with: 41 | project: wb79dhkl5q 42 | token: ${{ secrets.DEPOT_PROJECT_TOKEN }} 43 | platforms: linux/amd64,linux/arm64 44 | push: true 45 | tags: ${{ steps.meta.outputs.tags }} 46 | labels: ${{ steps.meta.outputs.labels }} 47 | -------------------------------------------------------------------------------- /src/redis_utils.rs: -------------------------------------------------------------------------------- 1 | use redis::{self, RedisResult}; 2 | 3 | pub fn ensure_redisearch_index( 4 | redis: &redis::Client, 5 | vector_dimensions: usize, 6 | distance_metric: &str, 7 | ) -> RedisResult<()> { 8 | let mut con = redis.get_connection()?; 9 | let index_name = "motorhead"; 10 | 11 | let index_info: Result = redis::cmd("FT.INFO").arg(index_name).query(&mut con); 12 | 13 | if let Err(err) = index_info { 14 | if err 15 | .to_string() 16 | .to_lowercase() 17 | .contains("unknown: index name") 18 | { 19 | redis::cmd("FT.CREATE") 20 | .arg(index_name) 21 | .arg("ON") 22 | .arg("HASH") 23 | .arg("PREFIX") 24 | .arg("1") 25 | .arg("motorhead:") 26 | .arg("SCHEMA") 27 | .arg("session") 28 | .arg("TAG") 29 | .arg("content") 30 | .arg("TEXT") 31 | .arg("role") 32 | .arg("TEXT") 33 | .arg("vector") 34 | .arg("VECTOR") 35 | .arg("HNSW") 36 | .arg("6") 37 | .arg("TYPE") 38 | .arg("FLOAT32") 39 | .arg("DIM") 40 | .arg(vector_dimensions.to_string()) 41 | .arg("DISTANCE_METRIC") 42 | .arg(distance_metric) 43 | .query(&mut con)?; 44 | } else { 45 | return Err(err); 46 | } 47 | } 48 | 49 | Ok(()) 50 | } 51 | -------------------------------------------------------------------------------- /examples/chat-vanilla-js/helpers.js: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | const MOTORHEAD_URL = "http://localhost:8080" 3 | 4 | export const fetchMessages = () => fetch(`${MOTORHEAD_URL}/sessions/ozzy/memory`, { 5 | method: "GET", 6 | headers: { 'Content-Type': 'application/json' }, 7 | }).then(res => res.json()) 8 | 9 | export const postMessages = (messages) => fetch(`${MOTORHEAD_URL}/sessions/ozzy/memory`, { 10 | method: "POST", 11 | headers: { 'Content-Type': 'application/json' }, 12 | body: JSON.stringify({ messages }) 13 | }).then(res => res.json()) 14 | 15 | export const retrieval = async (query) => { 16 | if (!query) return Promise.resolve([]) 17 | 18 | return fetch(`${MOTORHEAD_URL}/sessions/ozzy/retrieval`, { 19 | method: "POST", 20 | headers: { 'Content-Type': 'application/json' }, 21 | body: JSON.stringify({ text: query }) 22 | }).then(res => res.json()) 23 | } 24 | 25 | export function streamResponse(completion) { 26 | return new Promise((resolve) => { 27 | let result = ""; 28 | completion.data.on("data", (data) => { 29 | const lines = data 30 | ?.toString() 31 | ?.split("\n") 32 | .filter((line) => line.trim() !== ""); 33 | for (const line of lines) { 34 | const message = line.replace(/^data: /, ""); 35 | if (message == "[DONE]") { 36 | process.stdout.write(chalk.green('\n')); 37 | resolve(result); 38 | } else { 39 | let token; 40 | try { 41 | token = JSON.parse(message)?.choices?.[0]?.delta.content; 42 | } catch (err) { 43 | // console.log("ERROR", err); 44 | } 45 | 46 | if (token) { 47 | result += token; 48 | process.stdout.write(chalk.green(token)); 49 | } 50 | } 51 | } 52 | }); 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | .cert 107 | .vscode 108 | 109 | 110 | # Added by cargo 111 | 112 | /target 113 | -------------------------------------------------------------------------------- /examples/chat-vanilla-js-hosted/helpers.js: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import * as dotenv from "dotenv" 3 | dotenv.config() 4 | 5 | const MOTORHEAD_URL = "https://api.getmetal.io/v1/motorhead" 6 | const API_KEY = process.env.METAL_API_KEY 7 | const CLIENT_ID = process.env.METAL_CLIENT_ID 8 | 9 | export const fetchMessages = () => fetch(`${MOTORHEAD_URL}/sessions/ozzy/memory`, { 10 | method: "GET", 11 | headers: { 12 | 'Content-Type': 'application/json', 13 | 'x-metal-api-key': API_KEY, 14 | 'x-metal-client-id': CLIENT_ID, 15 | }, 16 | }).then(res => res.json()) 17 | .then(res => res.data) 18 | 19 | export const postMessages = (messages) => fetch(`${MOTORHEAD_URL}/sessions/ozzy/memory`, { 20 | method: "POST", 21 | headers: { 22 | 'Content-Type': 'application/json', 23 | 'x-metal-api-key': API_KEY, 24 | 'x-metal-client-id': CLIENT_ID, 25 | }, 26 | body: JSON.stringify({ messages }) 27 | }).then(res => res.json()) 28 | .then(res => res.data) 29 | 30 | export const retrieval = async (query) => { 31 | if (!query) return Promise.resolve([]) 32 | 33 | return fetch(`${MOTORHEAD_URL}/sessions/ozzy/retrieval`, { 34 | method: "POST", 35 | headers: { 36 | 'Content-Type': 'application/json', 37 | 'x-metal-api-key': API_KEY, 38 | 'x-metal-client-id': CLIENT_ID, 39 | }, 40 | body: JSON.stringify({ text: query }) 41 | }).then(res => res.json()) 42 | .then(res => res.data) 43 | } 44 | 45 | export function streamResponse(completion) { 46 | return new Promise((resolve) => { 47 | let result = ""; 48 | completion.data.on("data", (data) => { 49 | const lines = data 50 | ?.toString() 51 | ?.split("\n") 52 | .filter((line) => line.trim() !== ""); 53 | for (const line of lines) { 54 | const message = line.replace(/^data: /, ""); 55 | if (message == "[DONE]") { 56 | process.stdout.write(chalk.green('\n')); 57 | resolve(result); 58 | } else { 59 | let token; 60 | try { 61 | token = JSON.parse(message)?.choices?.[0]?.delta.content; 62 | } catch (err) { 63 | // console.log("ERROR", err); 64 | } 65 | 66 | if (token) { 67 | result += token; 68 | process.stdout.write(chalk.green(token)); 69 | } 70 | } 71 | } 72 | }); 73 | }); 74 | } 75 | -------------------------------------------------------------------------------- /sweep.yaml: -------------------------------------------------------------------------------- 1 | # Sweep AI turns bugs & feature requests into code changes (https://sweep.dev) 2 | # For details on our config file, check out our docs at https://docs.sweep.dev/usage/config 3 | 4 | # This setting contains a list of rules that Sweep will check for. If any of these rules are broken in a new commit, Sweep will create an pull request to fix the broken rule. 5 | rules: 6 | - "All docstrings and comments should be up to date." 7 | - "Do not include large chunks of commented out code." 8 | - "Ensure that all error logs use traceback during exceptions." 9 | - "Avoid using `import pdb; pdb.set_trace()` in production code." 10 | - "Avoid using debug log or print statements in production code." 11 | - "Ensure that code is properly formatted and follows consistent styling guidelines." 12 | 13 | # This is the branch that Sweep will develop from and make pull requests to. Most people use 'main' or 'master' but some users also use 'dev' or 'staging'. 14 | branch: 'main' 15 | 16 | # By default Sweep will read the logs and outputs from your existing Github Actions. To disable this, set this to false. 17 | gha_enabled: True 18 | 19 | # This is the description of your project. It will be used by sweep when creating PRs. You can tell Sweep what's unique about your project, what frameworks you use, or anything else you want. 20 | # 21 | # Example: 22 | # 23 | # description: sweepai/sweep is a python project. The main api endpoints are in sweepai/api.py. Write code that adheres to PEP8. 24 | description: '' 25 | 26 | # This sets whether to create pull requests as drafts. If this is set to True, then all pull requests will be created as drafts and GitHub Actions will not be triggered. 27 | draft: False 28 | 29 | # This is a list of directories that Sweep will not be able to edit. 30 | blocked_dirs: [] 31 | 32 | # This is a list of documentation links that Sweep will use to help it understand your code. You can add links to documentation for any packages you use here. 33 | # 34 | # Example: 35 | # 36 | # docs: 37 | # - PyGitHub: ["https://pygithub.readthedocs.io/en/latest/", "We use pygithub to interact with the GitHub API"] 38 | docs: [] 39 | 40 | # Sandbox executes commands in a sandboxed environment to validate code changes after every edit to guarantee pristine code. For more details, see the [Sandbox](./sandbox) page. 41 | sandbox: 42 | install: 43 | - trunk init 44 | check: 45 | - trunk fmt {file_path} 46 | - trunk check --fix --print-failures {file_path} 47 | -------------------------------------------------------------------------------- /examples/chat-js/index.js: -------------------------------------------------------------------------------- 1 | import readline from "readline"; 2 | import * as dotenv from "dotenv" 3 | dotenv.config() 4 | 5 | import chalk from "chalk"; 6 | import { CallbackManager } from "langchain/callbacks"; 7 | import { ConversationChain } from "langchain/chains"; 8 | import { ChatOpenAI } from "langchain/chat_models/openai"; 9 | import { 10 | ChatPromptTemplate, 11 | HumanMessagePromptTemplate, 12 | SystemMessagePromptTemplate, 13 | MessagesPlaceholder, 14 | } from "langchain/prompts"; 15 | import { MotorheadMemory } from "langchain/memory"; 16 | 17 | const rl = readline.createInterface({ 18 | input: process.stdin, 19 | output: process.stdout, 20 | }); 21 | 22 | export const run = async () => { 23 | const chat = new ChatOpenAI({ 24 | temperature: 0, 25 | streaming: true, 26 | callbackManager: CallbackManager.fromHandlers({ 27 | async handleLLMNewToken(token) { 28 | process.stdout.write(chalk.green(token)); 29 | }, 30 | }), 31 | }); 32 | 33 | const memory = new MotorheadMemory({ 34 | returnMessages: true, 35 | memoryKey: "history", 36 | sessionId: "ozzy6666", 37 | motorheadURL: "http://localhost:8080" 38 | }); 39 | await memory.init(); // loads previous state from Motorhead 🤘 40 | let context = ""; 41 | 42 | if (memory.context) { 43 | context = ` 44 | Here's previous context: ${memory.context}`; 45 | } 46 | 47 | const chatPrompt = ChatPromptTemplate.fromPromptMessages([ 48 | SystemMessagePromptTemplate.fromTemplate( 49 | `The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.${context}` 50 | ), 51 | new MessagesPlaceholder("history"), 52 | HumanMessagePromptTemplate.fromTemplate("{input}"), 53 | ]); 54 | 55 | const chain = new ConversationChain({ 56 | memory, 57 | prompt: chatPrompt, 58 | llm: chat, 59 | }); 60 | 61 | const postToBash = async () => { 62 | console.log('\n') 63 | rl.question(chalk.green(`\n`), async function(answer) { 64 | const res = await chain.call({ input: answer }); 65 | await postToBash(res.response); 66 | }); 67 | }; 68 | 69 | rl.question( 70 | chalk.blue(`\nMotorhead 🤘chat start\n`), 71 | async function(answer) { 72 | const res = await chain.call({ input: answer }); 73 | await postToBash(res.response); 74 | } 75 | ); 76 | }; 77 | 78 | run(); 79 | -------------------------------------------------------------------------------- /examples/chat-vanilla-js/index.js: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | import { Configuration, OpenAIApi } from "openai"; 3 | import readline from "readline"; 4 | import * as dotenv from "dotenv" 5 | import { fetchMessages, postMessages, retrieval, streamResponse } from "./helpers.js"; 6 | dotenv.config() 7 | 8 | const MODEL = 'gpt-3.5-turbo' 9 | const ASSISTANT = 'assistant' 10 | const USER = 'user' 11 | 12 | const configuration = new Configuration({ apiKey: process.env.OPENAI_API_KEY }); 13 | const openai = new OpenAIApi(configuration); 14 | 15 | const generateSystemMessage = (context, longTermMemory = []) => { 16 | const longTermMemoryContext = longTermMemory.filter(({ role }) => role === ASSISTANT).map(({ content }) => content)[0]; 17 | 18 | return `The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\n${context}\n${longTermMemoryContext}` 19 | } 20 | 21 | const promptModel = async (query, messages, context) => { 22 | const systemMessageContent = generateSystemMessage(context) 23 | const systemMessageObj = { role: 'system', content: systemMessageContent } 24 | const completion = await openai.createChatCompletion({ 25 | model: MODEL, 26 | messages: [systemMessageObj, ...messages, { role: 'user', content: query }], 27 | stream: true, 28 | }, { responseType: 'stream' }) 29 | .catch(err => { 30 | console.log('ERROR', err.toJSON()) 31 | throw new Error('openai failed'); 32 | }); 33 | 34 | return streamResponse(completion); 35 | } 36 | 37 | 38 | const rl = readline.createInterface({ 39 | input: process.stdin, 40 | output: process.stdout, 41 | }); 42 | 43 | 44 | const chat = async (context, messages) => { 45 | rl.question( 46 | "\n", 47 | async function(query) { 48 | const response = await promptModel(query, messages, context); 49 | const newMessages = [{ role: USER, content: query }, { role: ASSISTANT, content: response }]; 50 | 51 | await postMessages(newMessages); 52 | 53 | messages.push(...newMessages); 54 | 55 | await chat(context, messages); 56 | } 57 | ); 58 | }; 59 | 60 | export const run = async () => { 61 | const { context, messages } = await fetchMessages() 62 | .catch(err => { 63 | console.log(`Error starting up:`, err.message) 64 | console.log(chalk.bgCyanBright(`\nIs Motorhead Running?\n`)) 65 | process.exit(1) 66 | }); 67 | 68 | console.log(chalk.blue(`\nMotorhead 🤘chat start\n`)) 69 | chat(context, messages.reverse()); 70 | }; 71 | 72 | 73 | run(); 74 | -------------------------------------------------------------------------------- /examples/chat-vanilla-js-hosted/index.js: -------------------------------------------------------------------------------- 1 | import * as dotenv from "dotenv" 2 | dotenv.config() 3 | 4 | import chalk from "chalk"; 5 | import { Configuration, OpenAIApi } from "openai"; 6 | import readline from "readline"; 7 | import { fetchMessages, postMessages, streamResponse } from "./helpers.js"; 8 | 9 | 10 | const MODEL = 'gpt-3.5-turbo' 11 | const ASSISTANT = 'assistant' 12 | const USER = 'user' 13 | 14 | const configuration = new Configuration({ apiKey: process.env.OPENAI_API_KEY }); 15 | const openai = new OpenAIApi(configuration); 16 | 17 | const generateSystemMessage = (context, longTermMemory = []) => { 18 | const longTermMemoryContext = longTermMemory.filter(({ role }) => role === ASSISTANT).map(({ content }) => content)[0]; 19 | 20 | return `The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.\n${context}\n${longTermMemoryContext}` 21 | } 22 | 23 | const promptModel = async (query, messages, context) => { 24 | const systemMessageContent = generateSystemMessage(context) 25 | const systemMessageObj = { role: 'system', content: systemMessageContent } 26 | const completion = await openai.createChatCompletion({ 27 | model: MODEL, 28 | messages: [systemMessageObj, ...messages, { role: 'user', content: query }], 29 | stream: true, 30 | }, { responseType: 'stream' }) 31 | .catch(err => { 32 | console.log('ERROR', err.toJSON()) 33 | throw new Error('openai failed'); 34 | }); 35 | 36 | return streamResponse(completion); 37 | } 38 | 39 | 40 | const rl = readline.createInterface({ 41 | input: process.stdin, 42 | output: process.stdout, 43 | }); 44 | 45 | 46 | const chat = async (context, messages) => { 47 | rl.question( 48 | "\n", 49 | async function(query) { 50 | const response = await promptModel(query, messages, context); 51 | const newMessages = [{ role: USER, content: query }, { role: ASSISTANT, content: response }]; 52 | 53 | await postMessages(newMessages); 54 | 55 | messages.push(...newMessages); 56 | 57 | await chat(context, messages); 58 | } 59 | ); 60 | }; 61 | 62 | export const run = async () => { 63 | const { context, messages } = await fetchMessages() 64 | .catch(err => { 65 | console.log(`Error starting up:`, err.message) 66 | console.log(chalk.bgCyanBright(`\nIs Motorhead Running?\n`)) 67 | process.exit(1) 68 | }); 69 | 70 | console.log(chalk.blue(`\nMotorhead 🤘chat start\n`)) 71 | chat(context, messages.reverse()); 72 | }; 73 | 74 | 75 | run(); 76 | -------------------------------------------------------------------------------- /src/long_term_memory.rs: -------------------------------------------------------------------------------- 1 | use crate::models::{parse_redisearch_response, AnyOpenAIClient, MemoryMessage, RedisearchResult}; 2 | use byteorder::{LittleEndian, WriteBytesExt}; 3 | use nanoid::nanoid; 4 | use redis::Value; 5 | use std::io::Cursor; 6 | 7 | fn encode(fs: Vec) -> Vec { 8 | let mut buf = Cursor::new(Vec::new()); 9 | for f in fs { 10 | buf.write_f32::(f).unwrap(); 11 | } 12 | buf.into_inner() 13 | } 14 | 15 | pub async fn index_messages( 16 | messages: Vec, 17 | session_id: String, 18 | openai_client: &AnyOpenAIClient, 19 | mut redis_conn: redis::aio::ConnectionManager, 20 | ) -> Result<(), Box> { 21 | let contents: Vec = messages.iter().map(|msg| msg.content.clone()).collect(); 22 | let embeddings = openai_client.create_embedding(contents.clone()).await?; 23 | 24 | // TODO add used tokens let tokens_used = response.usage.total_tokens; 25 | for (index, embedding) in embeddings.iter().enumerate() { 26 | let id = nanoid!(); 27 | let key = format!("motorhead:{}", id); 28 | let vector = encode(embedding.to_vec()); 29 | 30 | redis::cmd("HSET") 31 | .arg(key) 32 | .arg("session") 33 | .arg(&session_id) 34 | .arg("vector") 35 | .arg(vector) 36 | .arg("content") 37 | .arg(&contents[index]) 38 | .arg("role") 39 | .arg(&messages[index].role) 40 | .query_async::<_, ()>(&mut redis_conn) 41 | .await?; 42 | } 43 | 44 | Ok(()) 45 | } 46 | 47 | pub async fn search_messages( 48 | query: String, 49 | session_id: String, 50 | openai_client: &AnyOpenAIClient, 51 | mut redis_conn: redis::aio::ConnectionManager, 52 | ) -> Result, Box> { 53 | let response = openai_client.create_embedding(vec![query]).await?; 54 | let embeddings = response[0].clone(); 55 | let vector = encode(embeddings); 56 | let query = format!("@session:{{{}}}=>[KNN 10 @vector $V AS dist]", session_id); 57 | 58 | let values: Vec = redis::cmd("FT.SEARCH") 59 | .arg("motorhead") 60 | .arg(query) 61 | .arg("PARAMS") 62 | .arg("2") 63 | .arg("V") 64 | .arg(vector) 65 | .arg("RETURN") 66 | .arg("3") 67 | .arg("role") 68 | .arg("content") 69 | .arg("dist") 70 | .arg("SORTBY") 71 | .arg("dist") 72 | .arg("DIALECT") 73 | .arg("2") 74 | .query_async(&mut redis_conn) 75 | .await?; 76 | 77 | let array_value = redis::Value::Bulk(values); 78 | let results = parse_redisearch_response(&array_value); 79 | 80 | Ok(results) 81 | } 82 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod healthcheck; 2 | mod long_term_memory; 3 | mod memory; 4 | mod models; 5 | mod redis_utils; 6 | mod reducer; 7 | mod retrieval; 8 | 9 | use actix_web::{error, middleware, web, App, HttpResponse, HttpServer}; 10 | use healthcheck::get_health; 11 | use memory::{delete_memory, get_memory, get_sessions, post_memory}; 12 | use models::{AppState, OpenAIClientManager}; 13 | use redis_utils::ensure_redisearch_index; 14 | use retrieval::run_retrieval; 15 | use std::collections::HashMap; 16 | use std::env; 17 | use std::io; 18 | use std::sync::Arc; 19 | use tokio::sync::Mutex; 20 | 21 | #[actix_web::main] 22 | async fn main() -> io::Result<()> { 23 | env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); 24 | 25 | log::info!("Starting Motorhead 🤘"); 26 | 27 | let manager = OpenAIClientManager {}; 28 | let max_size = 8; 29 | let openai_pool = deadpool::managed::Pool::builder(manager) 30 | .max_size(max_size) 31 | .build() 32 | .unwrap(); 33 | 34 | let redis_url = env::var("REDIS_URL").expect("$REDIS_URL is not set"); 35 | let redis = redis::Client::open(redis_url).unwrap(); 36 | 37 | let long_term_memory = env::var("MOTORHEAD_LONG_TERM_MEMORY") 38 | .map(|value| value.to_lowercase() == "true") 39 | .unwrap_or(false); 40 | 41 | if long_term_memory { 42 | // TODO: Make these configurable - for now just ADA support 43 | let vector_dimensions = 1536; 44 | let distance_metric = "COSINE"; 45 | 46 | ensure_redisearch_index(&redis, vector_dimensions, distance_metric).unwrap_or_else(|err| { 47 | eprintln!("RediSearch index error: {}", err); 48 | std::process::exit(1); 49 | }); 50 | } 51 | 52 | let port = env::var("PORT") 53 | .ok() 54 | .and_then(|s| s.parse::().ok()) 55 | .unwrap_or(8000); 56 | 57 | let window_size = env::var("MOTORHEAD_MAX_WINDOW_SIZE") 58 | .ok() 59 | .and_then(|s| s.parse::().ok()) 60 | .unwrap_or(12); 61 | let model = env::var("MOTORHEAD_MODEL").unwrap_or_else(|_| "gpt-3.5-turbo".to_string()); 62 | 63 | let session_cleanup = Arc::new(Mutex::new(HashMap::new())); 64 | let session_state = Arc::new(AppState { 65 | window_size, 66 | session_cleanup, 67 | openai_pool, 68 | long_term_memory, 69 | model, 70 | }); 71 | 72 | async fn on_start_logger(port: u16) -> io::Result<()> { 73 | println!(); 74 | println!("-----------------------------------"); 75 | println!("🧠 Motorhead running on port: {}", port); 76 | println!("-----------------------------------"); 77 | println!(); 78 | 79 | Ok(()) 80 | } 81 | 82 | let server = HttpServer::new(move || { 83 | App::new() 84 | .app_data(web::Data::new(redis.clone())) 85 | .app_data(web::Data::new(session_state.clone())) 86 | .wrap(middleware::Logger::default()) 87 | .service(get_health) 88 | .service(get_memory) 89 | .service(post_memory) 90 | .service(delete_memory) 91 | .service(get_sessions) 92 | .service(run_retrieval) 93 | .app_data(web::JsonConfig::default().error_handler(|err, _req| { 94 | error::InternalError::from_response( 95 | "", 96 | HttpResponse::BadRequest() 97 | .content_type("application/json") 98 | .body(format!(r#"{{"error":"{}"}}"#, err)), 99 | ) 100 | .into() 101 | })) 102 | }) 103 | .bind(("0.0.0.0", port))? 104 | .run(); 105 | 106 | let server_future = server; 107 | let logger_future = on_start_logger(port); 108 | 109 | let (server_result, logger_result) = tokio::join!(server_future, logger_future); 110 | 111 | // Handle the Result from server 112 | if let Err(e) = server_result { 113 | eprintln!("Server error: {}", e); 114 | } 115 | 116 | // Handle the Result from logger 117 | if let Err(e) = logger_result { 118 | eprintln!("Logger error: {}", e); 119 | } 120 | 121 | Ok(()) 122 | } 123 | -------------------------------------------------------------------------------- /examples/chat-py/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import asyncio 3 | from typing import Any, Union, Dict, List 4 | 5 | from langchain.callbacks import CallbackManager, BaseCallbackManager, BaseCallbackHandler 6 | from langchain.chains import ConversationChain 7 | from langchain.chat_models.openai import ChatOpenAI 8 | from langchain.prompts import ( 9 | ChatPromptTemplate, 10 | HumanMessagePromptTemplate, 11 | SystemMessagePromptTemplate, 12 | MessagesPlaceholder, 13 | ) 14 | from dotenv import load_dotenv 15 | from langchain.memory.motorhead_memory import MotorheadMemory 16 | from langchain.schema import AgentFinish, AgentAction, LLMResult 17 | from termcolor import colored 18 | 19 | load_dotenv() 20 | 21 | 22 | class CustomCallbackManager(BaseCallbackManager): 23 | 24 | def add_handler(self, callback: BaseCallbackHandler) -> None: 25 | pass 26 | 27 | def remove_handler(self, handler: BaseCallbackHandler) -> None: 28 | pass 29 | 30 | def set_handlers(self, handlers: List[BaseCallbackHandler]) -> None: 31 | pass 32 | 33 | def on_llm_start(self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any) -> Any: 34 | pass 35 | 36 | def on_llm_new_token(self, token: str, **kwargs: Any) -> Any: 37 | return sys.stdout.write(colored(token, "green")) 38 | 39 | def on_llm_end(self, response: LLMResult, **kwargs: Any) -> Any: 40 | pass 41 | 42 | def on_llm_error(self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any) -> Any: 43 | pass 44 | 45 | def on_chain_start(self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs: Any) -> Any: 46 | pass 47 | 48 | def on_chain_end(self, outputs: Dict[str, Any], **kwargs: Any) -> Any: 49 | pass 50 | 51 | def on_chain_error(self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any) -> Any: 52 | pass 53 | 54 | def on_tool_start(self, serialized: Dict[str, Any], input_str: str, **kwargs: Any) -> Any: 55 | pass 56 | 57 | def on_tool_end(self, output: str, **kwargs: Any) -> Any: 58 | pass 59 | 60 | def on_tool_error(self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any) -> Any: 61 | pass 62 | 63 | def on_text(self, text: str, **kwargs: Any) -> Any: 64 | pass 65 | 66 | def on_agent_action(self, action: AgentAction, **kwargs: Any) -> Any: 67 | pass 68 | 69 | def on_agent_finish(self, finish: AgentFinish, **kwargs: Any) -> Any: 70 | pass 71 | 72 | 73 | async def run(): 74 | cb_manager = CallbackManager(handlers=[CustomCallbackManager()]) 75 | chat = ChatOpenAI( 76 | temperature=0, 77 | streaming=True, 78 | callback_manager=cb_manager 79 | ) 80 | 81 | memory = MotorheadMemory( 82 | return_messages=True, 83 | memory_key="history", 84 | session_id="davemustaine666", 85 | url="http://localhost:8080", 86 | ) 87 | await memory.init() 88 | 89 | context = "" 90 | if memory.context: 91 | context = f"\nHere's previous context: {memory.context}" 92 | 93 | chat_prompt = ChatPromptTemplate.from_messages( 94 | [ 95 | SystemMessagePromptTemplate.from_template( 96 | f"The following is a friendly conversation between a human and an AI. The AI is talkative and " 97 | f"provides lots of specific details from its context. If the AI does not know the answer to a " 98 | f"question, it truthfully says it does not know. {context}" 99 | ), 100 | MessagesPlaceholder(variable_name="history"), 101 | HumanMessagePromptTemplate.from_template("{input}"), 102 | ] 103 | ) 104 | chain = ConversationChain(memory=memory, prompt=chat_prompt, llm=chat) 105 | 106 | def post_to_bash(): 107 | while True: 108 | answer_i = input(colored("", "green")) 109 | if not answer_i: 110 | continue 111 | response_i = chain.run(answer_i) 112 | print(colored(response_i, "green")) 113 | 114 | print(colored("\nMotorhead 🤘chat start\n", "blue")) 115 | answer = input(colored("", "green")) 116 | response = chain.run(answer) 117 | print(colored(response, "green")) 118 | post_to_bash() 119 | 120 | 121 | if __name__ == "__main__": 122 | try: 123 | asyncio.run(run()) 124 | except KeyboardInterrupt as kie: 125 | print(colored("\nI see you have chosen to end the conversation with me 💔. Good bye!", "yellow")) 126 | -------------------------------------------------------------------------------- /src/reducer.rs: -------------------------------------------------------------------------------- 1 | use crate::models::{AnyOpenAIClient, MotorheadError}; 2 | use std::error::Error; 3 | use tiktoken_rs::p50k_base; 4 | 5 | pub async fn incremental_summarization( 6 | model: String, 7 | openai_client: &AnyOpenAIClient, 8 | context: Option, 9 | mut messages: Vec, 10 | ) -> Result<(String, u32), Box> { 11 | messages.reverse(); 12 | let messages_joined = messages.join("\n"); 13 | let prev_summary = context.as_deref().unwrap_or_default(); 14 | // Taken from langchain 15 | let progresive_prompt = format!( 16 | r#" 17 | Progressively summarize the lines of conversation provided, adding onto the previous summary returning a new summary. If the lines are meaningless just return NONE 18 | 19 | EXAMPLE 20 | Current summary: 21 | The human asks who is the lead singer of Motorhead. The AI responds Lemmy Kilmister. 22 | New lines of conversation: 23 | Human: What are the other members of Motorhead? 24 | AI: The original members included Lemmy Kilmister (vocals, bass), Larry Wallis (guitar), and Lucas Fox (drums), with notable members throughout the years including \"Fast\" Eddie Clarke (guitar), Phil \"Philthy Animal\" Taylor (drums), and Mikkey Dee (drums). 25 | New summary: 26 | The human asks who is the lead singer and other members of Motorhead. The AI responds Lemmy Kilmister is the lead singer and other original members include Larry Wallis, and Lucas Fox, with notable past members including \"Fast\" Eddie Clarke, Phil \"Philthy Animal\" Taylor, and Mikkey Dee. 27 | END OF EXAMPLE 28 | 29 | Current summary: 30 | {prev_summary} 31 | New lines of conversation: 32 | {messages_joined} 33 | New summary: 34 | "# 35 | ); 36 | 37 | let response = openai_client 38 | .create_chat_completion(&model, &progresive_prompt) 39 | .await?; 40 | 41 | let completion = response 42 | .choices 43 | .first() 44 | .ok_or("No completion found")? 45 | .message 46 | .content 47 | .clone(); 48 | 49 | let usage = response.usage.ok_or("No Usage found")?; 50 | let tokens_used = usage.total_tokens; 51 | 52 | Ok((completion, tokens_used)) 53 | } 54 | 55 | pub async fn handle_compaction( 56 | session_id: String, 57 | model: String, 58 | window_size: i64, 59 | openai_client: &AnyOpenAIClient, 60 | mut redis_conn: redis::aio::ConnectionManager, 61 | ) -> Result<(), MotorheadError> { 62 | let half = window_size / 2; 63 | let session_key = format!("session:{}", &*session_id); 64 | let context_key = format!("context:{}", &*session_id); 65 | let (messages, mut context): (Vec, Option) = redis::pipe() 66 | .cmd("LRANGE") 67 | .arg(session_key.clone()) 68 | .arg(half) 69 | .arg(window_size) 70 | .cmd("GET") 71 | .arg(context_key.clone()) 72 | .query_async(&mut redis_conn) 73 | .await?; 74 | 75 | let max_tokens = 4096usize; 76 | let summary_max_tokens = 512usize; 77 | let buffer_tokens = 230usize; 78 | let max_message_tokens = max_tokens - summary_max_tokens - buffer_tokens; 79 | 80 | let mut total_tokens = 0; 81 | let mut temp_messages = Vec::new(); 82 | let mut total_tokens_temp = 0; 83 | 84 | for message in messages { 85 | let bpe = p50k_base().unwrap(); 86 | let message_tokens = bpe.encode_with_special_tokens(&message); 87 | let message_tokens_used = message_tokens.len(); 88 | 89 | if total_tokens_temp + message_tokens_used <= max_message_tokens { 90 | temp_messages.push(message); 91 | total_tokens_temp += message_tokens_used; 92 | } else { 93 | let (summary, summary_tokens_used) = incremental_summarization( 94 | model.to_string(), 95 | openai_client, 96 | context.clone(), 97 | temp_messages, 98 | ) 99 | .await?; 100 | 101 | total_tokens += summary_tokens_used; 102 | 103 | context = Some(summary); 104 | temp_messages = vec![message]; 105 | total_tokens_temp = message_tokens_used; 106 | } 107 | } 108 | 109 | if !temp_messages.is_empty() { 110 | let (summary, summary_tokens_used) = 111 | incremental_summarization(model, openai_client, context.clone(), temp_messages).await?; 112 | total_tokens += summary_tokens_used; 113 | context = Some(summary); 114 | } 115 | 116 | if let Some(new_context) = context { 117 | let token_count_key = format!("tokens:{}", &*session_id); 118 | let redis_pipe_response_result: Result<((), (), i64), redis::RedisError> = redis::pipe() 119 | .cmd("LTRIM") 120 | .arg(session_key) 121 | .arg(0) 122 | .arg(half) 123 | .cmd("SET") 124 | .arg(context_key) 125 | .arg(new_context) 126 | .cmd("INCRBY") 127 | .arg(token_count_key) 128 | .arg(total_tokens) 129 | .query_async(&mut redis_conn) 130 | .await; 131 | 132 | match redis_pipe_response_result { 133 | Ok(_) => Ok(()), 134 | Err(e) => { 135 | log::error!("Error executing the redis pipeline: {:?}", e); 136 | Err(MotorheadError::RedisError(e)) 137 | } 138 | } 139 | } else { 140 | log::error!("No context found after summarization"); 141 | Err(MotorheadError::IncrementalSummarizationError( 142 | "No context found after summarization".to_string(), 143 | )) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 🧠 Motorhead (DEPRECATED) 3 |

4 |

Support is no longer maintained for this project.

5 |

6 | 7 | License 8 | 9 | 10 | Metal 11 | 12 | 13 | License 14 | 15 |

16 | 17 | Motorhead is a memory and information retrieval server for LLMs. 18 | 19 | [![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/bmO_uf?referralCode=5NirXf) 20 | 21 | ## Why use Motorhead? 22 | 23 | When building chat applications using LLMs, memory handling is something that has to be built every time. Motorhead is a server to assist with that process. It provides 3 simple APIs: 24 | 25 | - GET `/sessions/:id/memory` returns messages up to `MAX_WINDOW_SIZE`. 26 | ```json 27 | { 28 | "messages": [ 29 | { 30 | "role": "AI", 31 | "content": "Electronic music and salsa are two very different genres of music, and the way people dance to them is also quite different." 32 | }, 33 | { 34 | "role": "Human", 35 | "content": "how does it compare to salsa?" 36 | }, 37 | { 38 | "role": "AI", 39 | "content": "Electronic music is a broad genre that encompasses many different styles, so there is no one \"right\" way to dance to it." 40 | }, 41 | { 42 | "role": "Human", 43 | "content": "how do you dance electronic music?" 44 | }, 45 | { 46 | "role": "AI", 47 | "content": "Colombia has a vibrant electronic music scene, and there are many talented DJs and producers who have gained international recognition." 48 | }, 49 | { 50 | "role": "Human", 51 | "content": "What are some famous djs from Colombia?" 52 | }, 53 | { 54 | "role": "AI", 55 | "content": "Baum opened its doors in 2014 and has quickly become one of the most popular clubs for electronic music in Bogotá." 56 | } 57 | ], 58 | "context": "The conversation covers topics such as clubs for electronic music in Bogotá, popular tourist attractions in the city, and general information about Colombia. The AI provides information about popular electronic music clubs such as Baum and Video Club, as well as electronic music festivals that take place in Bogotá. The AI also recommends tourist attractions such as La Candelaria, Monserrate and the Salt Cathedral of Zipaquirá, and provides general information about Colombia's diverse culture, landscape and wildlife.", 59 | "tokens": 744 // tokens used for incremental summarization 60 | } 61 | ``` 62 | 63 | - POST `/sessions/:id/memory` - Send an array of messages to Motorhead to store. 64 | 65 | ```bash 66 | curl --location 'localhost:8080/sessions/${SESSION_ID}/memory' \ 67 | --header 'Content-Type: application/json' \ 68 | --data '{ 69 | "messages": [{ "role": "Human", "content": "ping" }, { "role": "AI", "content": "pong" }] 70 | }' 71 | ``` 72 | 73 | Either an existing or new `SESSION_ID` can be used when storing messages, and the session is automatically created if it did not previously exist. 74 | 75 | Optionally, `context` can be send in if it needs to get loaded from another datastore. 76 | 77 | - DELETE `/sessions/:id/memory` - deletes the session's message list. 78 | 79 | A max `window_size` is set for the LLM to keep track of the conversation. Once that max is hit, Motorhead will process (`window_size / 2` messages) and summarize them. Subsequent summaries, as the messages grow, are incremental. 80 | 81 | - POST `/sessions/:id/retrieval` - searches by text query using VSS. 82 | 83 | ```bash 84 | curl --location 'localhost:8080/sessions/${SESSION_ID}/retrieval' \ 85 | --header 'Content-Type: application/json' \ 86 | --data '{ 87 | "text": "Generals gathered in their masses, just like witches in black masses" 88 | }' 89 | 90 | ``` 91 | 92 | Searches are segmented (filtered) by the session id provided automatically. 93 | 94 | ## Config 95 | 96 | - `MOTORHEAD_MAX_WINDOW_SIZE` (default:12) - Number of max messages returned by the server. When this number is reached, a job is triggered to halve it. 97 | - `MOTORHEAD_LONG_TERM_MEMORY` (default:false) - Enables long term memory using Redisearch VSS. 98 | - `MOTORHEAD_MODEL` (default:gpt-3.5-turbo) - Model used to run the incremental summarization. Use `gpt-3.5-turbo` or `gpt-4` - otherwise some weird things might happen. 99 | - `PORT` (default:8000) - Motorhead Server Port 100 | - `OPENAI_API_KEY`- [Your api key](https://platform.openai.com/account/api-keys) to connect to OpenAI. 101 | - `REDIS_URL` (required)- URL used to connect to `redis`. 102 | - `OPENAI_API_BASE` (default:https://api.openai.com/v1) - OpenAI API Base URL 103 | 104 | ### Azure deployment 105 | 106 | Additional Environment Variables are required for Azure deployments: 107 | 108 | - `AZURE_DEPLOYMENT_ID` 109 | - `AZURE_DEPLOYMENT_ID_ADA` 110 | - `AZURE_API_BASE` 111 | - `AZURE_API_KEY` 112 | 113 | ## How to run 114 | 115 | With docker-compose: 116 | ```bash 117 | docker-compose build && docker-compose up 118 | ``` 119 | 120 | Or you can use the image `docker pull ghcr.io/getmetal/motorhead:latest` directly: 121 | ```bash 122 | docker run --name motorhead -p 8080:8080 -e PORT=8080 -e REDIS_URL='redis://redis:6379' -d ghcr.io/getmetal/motorhead:latest 123 | ``` 124 | 125 | ## Examples 126 | 127 | - [Chat JS Example](examples/chat-js/) 128 | - [Chat JS Vanilla Example](examples/chat-vanilla-js/) 129 | - [Chat JS Vanilla Hosted Example](examples/chat-vanilla-js-hosted/) 130 | - [Chat Python Example](examples/chat-py/) 131 | -------------------------------------------------------------------------------- /src/memory.rs: -------------------------------------------------------------------------------- 1 | use crate::long_term_memory::index_messages; 2 | use crate::models::{ 3 | AckResponse, AppState, GetSessionsQuery, MemoryMessage, MemoryMessagesAndContext, 4 | MemoryResponse, NamespaceQuery, 5 | }; 6 | use crate::reducer::handle_compaction; 7 | use actix_web::{delete, error, get, post, web, HttpResponse, Responder}; 8 | use std::ops::Deref; 9 | use std::sync::Arc; 10 | 11 | #[get("/sessions")] 12 | pub async fn get_sessions( 13 | web::Query(pagination): web::Query, 14 | _data: web::Data>, 15 | redis: web::Data, 16 | ) -> actix_web::Result { 17 | let GetSessionsQuery { 18 | page, 19 | size, 20 | namespace, 21 | } = pagination; 22 | 23 | if page > 100 { 24 | return Err(actix_web::error::ErrorBadRequest( 25 | "Page size must not exceed 100", 26 | )); 27 | } 28 | 29 | let start: isize = ((page - 1) * size) as isize; // 0-indexed 30 | let end: isize = (page * size - 1) as isize; // inclusive 31 | 32 | let mut conn = redis 33 | .get_tokio_connection_manager() 34 | .await 35 | .map_err(error::ErrorInternalServerError)?; 36 | 37 | let sessions_key = match &namespace { 38 | Some(namespace) => format!("sessions:{}", namespace), 39 | None => String::from("sessions"), 40 | }; 41 | 42 | let session_ids: Vec = redis::cmd("ZRANGE") 43 | .arg(sessions_key) 44 | .arg(start) 45 | .arg(end) 46 | .query_async(&mut conn) 47 | .await 48 | .map_err(error::ErrorInternalServerError)?; 49 | 50 | Ok(HttpResponse::Ok() 51 | .content_type("application/json") 52 | .json(session_ids)) 53 | } 54 | 55 | #[get("/sessions/{session_id}/memory")] 56 | pub async fn get_memory( 57 | session_id: web::Path, 58 | data: web::Data>, 59 | redis: web::Data, 60 | ) -> actix_web::Result { 61 | let mut conn = redis 62 | .get_tokio_connection_manager() 63 | .await 64 | .map_err(error::ErrorInternalServerError)?; 65 | 66 | let lrange_key = format!("session:{}", &*session_id); 67 | let context_key = format!("context:{}", &*session_id); 68 | let token_count_key = format!("tokens:{}", &*session_id); 69 | let keys = vec![context_key, token_count_key]; 70 | 71 | let (messages, values): (Vec, Vec>) = redis::pipe() 72 | .cmd("LRANGE") 73 | .arg(lrange_key) 74 | .arg(0) 75 | .arg(data.window_size as isize) 76 | .cmd("MGET") 77 | .arg(keys) 78 | .query_async(&mut conn) 79 | .await 80 | .map_err(error::ErrorInternalServerError)?; 81 | 82 | let context = values.get(0).cloned().flatten(); 83 | let tokens = values 84 | .get(1) 85 | .cloned() 86 | .flatten() 87 | .and_then(|tokens_string| tokens_string.parse::().ok()) 88 | .unwrap_or(0); 89 | 90 | let messages: Vec = messages 91 | .into_iter() 92 | .filter_map(|message| { 93 | let mut parts = message.splitn(2, ": "); 94 | match (parts.next(), parts.next()) { 95 | (Some(role), Some(content)) => Some(MemoryMessage { 96 | role: role.to_string(), 97 | content: content.to_string(), 98 | }), 99 | _ => None, 100 | } 101 | }) 102 | .collect(); 103 | 104 | let response = MemoryResponse { 105 | messages, 106 | context, 107 | tokens: Some(tokens), 108 | }; 109 | 110 | Ok(HttpResponse::Ok() 111 | .content_type("application/json") 112 | .json(response)) 113 | } 114 | 115 | #[post("/sessions/{session_id}/memory")] 116 | pub async fn post_memory( 117 | session_id: web::Path, 118 | web::Json(memory_messages): web::Json, 119 | data: web::Data>, 120 | redis: web::Data, 121 | web::Query(namespace_query): web::Query, 122 | ) -> actix_web::Result { 123 | let mut conn = redis 124 | .get_tokio_connection_manager() 125 | .await 126 | .map_err(error::ErrorInternalServerError)?; 127 | 128 | let memory_messages_clone: Vec = memory_messages.messages.to_vec(); 129 | 130 | let messages: Vec = memory_messages 131 | .messages 132 | .into_iter() 133 | .map(|memory_message| format!("{}: {}", memory_message.role, memory_message.content)) 134 | .collect(); 135 | 136 | // If new context is passed in we overwrite the existing one 137 | if let Some(context) = memory_messages.context { 138 | redis::Cmd::set(format!("context:{}", &*session_id), context) 139 | .query_async::<_, ()>(&mut conn) 140 | .await 141 | .map_err(error::ErrorInternalServerError)?; 142 | } 143 | 144 | let sessions_key = match namespace_query.namespace { 145 | Some(namespace) => format!("sessions:{}", namespace), 146 | None => String::from("sessions"), 147 | }; 148 | 149 | // add to sorted set of sessions 150 | redis::cmd("ZADD") 151 | .arg(sessions_key) 152 | .arg(chrono::Utc::now().timestamp()) 153 | .arg(&*session_id) 154 | .query_async(&mut conn) 155 | .await 156 | .map_err(error::ErrorInternalServerError)?; 157 | 158 | let res: i64 = redis::Cmd::lpush(format!("session:{}", &*session_id), messages.clone()) 159 | .query_async::<_, i64>(&mut conn) 160 | .await 161 | .map_err(error::ErrorInternalServerError)?; 162 | 163 | if data.long_term_memory { 164 | let session = session_id.clone(); 165 | let conn_clone = conn.clone(); 166 | let pool = data.openai_pool.clone(); 167 | 168 | tokio::spawn(async move { 169 | let client_wrapper = pool.get().await.unwrap(); 170 | let client = client_wrapper.deref(); 171 | if let Err(e) = index_messages(memory_messages_clone, session, client, conn_clone).await 172 | { 173 | log::error!("Error in index_messages: {:?}", e); 174 | } 175 | }); 176 | } 177 | 178 | if res > data.window_size { 179 | let state = data.into_inner(); 180 | let mut session_cleanup = state.session_cleanup.lock().await; 181 | 182 | if !session_cleanup.get(&*session_id).unwrap_or(&false) { 183 | session_cleanup.insert((&*session_id.to_string()).into(), true); 184 | let session_cleanup = Arc::clone(&state.session_cleanup); 185 | let session_id = session_id.clone(); 186 | let window_size = state.window_size; 187 | let model = state.model.to_string(); 188 | let pool = state.openai_pool.clone(); 189 | 190 | tokio::spawn(async move { 191 | log::info!("running compact"); 192 | let client_wrapper = pool.get().await.unwrap(); 193 | let client = client_wrapper.deref(); 194 | 195 | let _compaction_result = 196 | handle_compaction(session_id.to_string(), model, window_size, client, conn) 197 | .await; 198 | 199 | let mut lock = session_cleanup.lock().await; 200 | lock.remove(&session_id); 201 | }); 202 | } 203 | } 204 | 205 | let response = AckResponse { status: "Ok" }; 206 | Ok(HttpResponse::Ok() 207 | .content_type("application/json") 208 | .json(response)) 209 | } 210 | 211 | #[delete("/sessions/{session_id}/memory")] 212 | pub async fn delete_memory( 213 | session_id: web::Path, 214 | redis: web::Data, 215 | web::Query(namespace_query): web::Query, 216 | ) -> actix_web::Result { 217 | let mut conn = redis 218 | .get_tokio_connection_manager() 219 | .await 220 | .map_err(error::ErrorInternalServerError)?; 221 | 222 | let context_key = format!("context:{}", &*session_id); 223 | let token_count_key = format!("tokens:{}", &*session_id); 224 | let session_key = format!("session:{}", &*session_id); 225 | let keys = vec![context_key, session_key, token_count_key]; 226 | 227 | let sessions_key = match namespace_query.namespace { 228 | Some(namespace) => format!("sessions:{}", namespace), 229 | None => String::from("sessions"), 230 | }; 231 | 232 | redis::cmd("ZREM") 233 | .arg(sessions_key) 234 | .arg(&*session_id) 235 | .query_async(&mut conn) 236 | .await 237 | .map_err(error::ErrorInternalServerError)?; 238 | 239 | redis::Cmd::del(keys) 240 | .query_async(&mut conn) 241 | .await 242 | .map_err(error::ErrorInternalServerError)?; 243 | 244 | let response = AckResponse { status: "Ok" }; 245 | Ok(HttpResponse::Ok() 246 | .content_type("application/json") 247 | .json(response)) 248 | } 249 | -------------------------------------------------------------------------------- /src/models.rs: -------------------------------------------------------------------------------- 1 | use async_openai::{ 2 | config::{AzureConfig, OpenAIConfig}, 3 | error::OpenAIError, 4 | types::{ 5 | ChatCompletionRequestMessageArgs, CreateChatCompletionRequestArgs, 6 | CreateChatCompletionResponse, CreateEmbeddingRequestArgs, Role, 7 | }, 8 | Client, 9 | }; 10 | use async_trait::async_trait; 11 | use deadpool::managed::{Manager, RecycleResult}; 12 | use futures_util::future::try_join_all; 13 | use redis::{FromRedisValue, RedisError, Value}; 14 | use serde::{Deserialize, Serialize}; 15 | use std::collections::HashMap; 16 | use std::env; 17 | use std::error::Error; 18 | use std::sync::Arc; 19 | use tokio::sync::Mutex; 20 | 21 | pub struct OpenAIClientManager {} 22 | 23 | #[async_trait] 24 | impl Manager for OpenAIClientManager { 25 | type Type = AnyOpenAIClient; 26 | type Error = MotorheadError; 27 | 28 | async fn create(&self) -> Result { 29 | let openai_client = match ( 30 | env::var("AZURE_API_KEY"), 31 | env::var("AZURE_DEPLOYMENT_ID"), 32 | env::var("AZURE_DEPLOYMENT_ID_ADA"), 33 | env::var("AZURE_API_BASE"), 34 | ) { 35 | ( 36 | Ok(azure_api_key), 37 | Ok(azure_deployment_id), 38 | Ok(azure_deployment_id_ada), 39 | Ok(azure_api_base), 40 | ) => { 41 | let config = AzureConfig::new() 42 | .with_api_base(&azure_api_base) 43 | .with_api_key(&azure_api_key) 44 | .with_deployment_id(azure_deployment_id) 45 | .with_api_version("2023-05-15"); 46 | 47 | let config_ada = AzureConfig::new() 48 | .with_api_base(&azure_api_base) 49 | .with_api_key(&azure_api_key) 50 | .with_deployment_id(azure_deployment_id_ada) 51 | .with_api_version("2023-05-15"); 52 | 53 | AnyOpenAIClient::Azure { 54 | embedding_client: Client::with_config(config_ada), 55 | completion_client: Client::with_config(config), 56 | } 57 | } 58 | _ => { 59 | let openai_api_base = env::var("OPENAI_API_BASE"); 60 | 61 | if let Ok(openai_api_base) = openai_api_base { 62 | let embedding_config = OpenAIConfig::default().with_api_base(&openai_api_base); 63 | let completion_config = OpenAIConfig::default().with_api_base(&openai_api_base); 64 | 65 | AnyOpenAIClient::OpenAI { 66 | embedding_client: Client::with_config(embedding_config), 67 | completion_client: Client::with_config(completion_config), 68 | } 69 | } else { 70 | AnyOpenAIClient::OpenAI { 71 | embedding_client: Client::new(), 72 | completion_client: Client::new(), 73 | } 74 | } 75 | } 76 | }; 77 | Ok(openai_client) 78 | } 79 | 80 | async fn recycle(&self, _: &mut AnyOpenAIClient) -> RecycleResult { 81 | Ok(()) 82 | } 83 | } 84 | 85 | pub enum AnyOpenAIClient { 86 | Azure { 87 | embedding_client: Client, 88 | completion_client: Client, 89 | }, 90 | OpenAI { 91 | embedding_client: Client, 92 | completion_client: Client, 93 | }, 94 | } 95 | 96 | impl AnyOpenAIClient { 97 | pub async fn create_chat_completion( 98 | &self, 99 | model: &str, 100 | progresive_prompt: &str, 101 | ) -> Result { 102 | let request = CreateChatCompletionRequestArgs::default() 103 | .max_tokens(512u16) 104 | .model(model) 105 | .messages([ChatCompletionRequestMessageArgs::default() 106 | .role(Role::User) 107 | .content(progresive_prompt) 108 | .build()?]) 109 | .build()?; 110 | 111 | match self { 112 | AnyOpenAIClient::Azure { 113 | completion_client, .. 114 | } => completion_client.chat().create(request).await, 115 | AnyOpenAIClient::OpenAI { 116 | completion_client, .. 117 | } => completion_client.chat().create(request).await, 118 | } 119 | } 120 | 121 | pub async fn create_embedding( 122 | &self, 123 | query_vec: Vec, 124 | ) -> Result>, OpenAIError> { 125 | match self { 126 | AnyOpenAIClient::OpenAI { 127 | embedding_client, .. 128 | } => { 129 | let request = CreateEmbeddingRequestArgs::default() 130 | .model("text-embedding-ada-002") 131 | .input(query_vec) 132 | .build()?; 133 | 134 | let response = embedding_client.embeddings().create(request).await?; 135 | let embeddings: Vec<_> = response 136 | .data 137 | .iter() 138 | .map(|data| data.embedding.clone()) 139 | .collect(); 140 | 141 | Ok(embeddings) 142 | } 143 | AnyOpenAIClient::Azure { 144 | embedding_client, .. 145 | } => { 146 | let tasks: Vec<_> = query_vec 147 | .into_iter() 148 | .map(|query| async { 149 | let request = CreateEmbeddingRequestArgs::default() 150 | .model("text-embedding-ada-002") 151 | .input(vec![query]) 152 | .build()?; 153 | 154 | embedding_client.embeddings().create(request).await 155 | }) 156 | .collect(); 157 | 158 | let responses: Result, _> = try_join_all(tasks).await; 159 | 160 | match responses { 161 | Ok(successful_responses) => { 162 | let embeddings: Vec<_> = successful_responses 163 | .into_iter() 164 | .flat_map(|response| response.data.into_iter()) 165 | .map(|data| data.embedding) 166 | .collect(); 167 | 168 | Ok(embeddings) 169 | } 170 | Err(err) => Err(err), 171 | } 172 | } 173 | } 174 | } 175 | } 176 | 177 | pub struct AppState { 178 | pub window_size: i64, 179 | pub session_cleanup: Arc>>, 180 | pub openai_pool: deadpool::managed::Pool, 181 | pub long_term_memory: bool, 182 | pub model: String, 183 | } 184 | 185 | #[derive(Serialize, Deserialize)] 186 | pub struct SearchPayload { 187 | pub text: String, 188 | } 189 | 190 | #[derive(Serialize, Deserialize, Clone)] 191 | pub struct MemoryMessage { 192 | pub role: String, 193 | pub content: String, 194 | } 195 | 196 | #[derive(Deserialize)] 197 | pub struct MemoryMessagesAndContext { 198 | pub messages: Vec, 199 | pub context: Option, 200 | } 201 | 202 | #[derive(Serialize)] 203 | pub struct MemoryResponse { 204 | pub messages: Vec, 205 | pub context: Option, 206 | pub tokens: Option, 207 | } 208 | 209 | #[derive(Serialize)] 210 | pub struct HealthCheckResponse { 211 | pub now: u128, 212 | } 213 | 214 | #[derive(Serialize)] 215 | pub struct AckResponse { 216 | pub status: &'static str, 217 | } 218 | 219 | #[derive(Debug)] 220 | pub enum MotorheadError { 221 | RedisError(RedisError), 222 | IncrementalSummarizationError(String), 223 | } 224 | 225 | impl std::fmt::Display for MotorheadError { 226 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 227 | match self { 228 | MotorheadError::RedisError(e) => write!(f, "Redis error: {}", e), 229 | MotorheadError::IncrementalSummarizationError(e) => { 230 | write!(f, "Incremental summarization error: {}", e) 231 | } 232 | } 233 | } 234 | } 235 | 236 | impl From> for MotorheadError { 237 | fn from(error: Box) -> Self { 238 | MotorheadError::IncrementalSummarizationError(error.to_string()) 239 | } 240 | } 241 | 242 | impl From for MotorheadError { 243 | fn from(err: RedisError) -> Self { 244 | MotorheadError::RedisError(err) 245 | } 246 | } 247 | 248 | impl std::error::Error for MotorheadError {} 249 | 250 | #[derive(Serialize, Deserialize, Debug)] 251 | pub struct RedisearchResult { 252 | pub role: String, 253 | pub content: String, 254 | pub dist: f64, 255 | } 256 | 257 | impl FromRedisValue for RedisearchResult { 258 | fn from_redis_value(v: &Value) -> redis::RedisResult { 259 | let values: Vec = redis::from_redis_value(v)?; 260 | let mut content = String::new(); 261 | let mut role = String::new(); 262 | let mut dist = 0.0; 263 | 264 | for i in 0..values.len() { 265 | match values[i].as_str() { 266 | "content" => content = values[i + 1].clone(), 267 | "role" => role = values[i + 1].clone(), 268 | "dist" => dist = values[i + 1].parse::().unwrap_or(0.0), 269 | _ => continue, 270 | } 271 | } 272 | 273 | Ok(RedisearchResult { 274 | role, 275 | content, 276 | dist, 277 | }) 278 | } 279 | } 280 | 281 | pub fn parse_redisearch_response(response: &Value) -> Vec { 282 | match response { 283 | Value::Bulk(array) => { 284 | let mut results = Vec::new(); 285 | let n = array.len(); 286 | 287 | for item in array.iter().take(n).skip(1) { 288 | if let Value::Bulk(ref bulk) = item { 289 | if let Ok(result) = 290 | RedisearchResult::from_redis_value(&Value::Bulk(bulk.clone())) 291 | { 292 | results.push(result); 293 | } 294 | } 295 | } 296 | 297 | results 298 | } 299 | _ => vec![], 300 | } 301 | } 302 | 303 | #[derive(serde::Deserialize)] 304 | pub struct NamespaceQuery { 305 | pub namespace: Option, 306 | } 307 | 308 | #[derive(serde::Deserialize)] 309 | pub struct GetSessionsQuery { 310 | #[serde(default = "default_page")] 311 | pub page: usize, 312 | #[serde(default = "default_size")] 313 | pub size: usize, 314 | pub namespace: Option, 315 | } 316 | 317 | fn default_page() -> usize { 318 | 1 319 | } 320 | 321 | fn default_size() -> usize { 322 | 10 323 | } 324 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2023 Metal Technologies Inc 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /examples/chat-vanilla-js/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat-vanilla", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "chat-vanilla", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "chalk": "^5.2.0", 13 | "dotenv": "^16.0.3", 14 | "openai": "^4.17.4" 15 | }, 16 | "engines": { 17 | "node": ">=18.0.0" 18 | } 19 | }, 20 | "node_modules/@types/node": { 21 | "version": "18.18.9", 22 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.9.tgz", 23 | "integrity": "sha512-0f5klcuImLnG4Qreu9hPj/rEfFq6YRc5n2mAjSsH+ec/mJL+3voBH0+8T7o8RpFjH7ovc+TRsL/c7OYIQsPTfQ==", 24 | "dependencies": { 25 | "undici-types": "~5.26.4" 26 | } 27 | }, 28 | "node_modules/@types/node-fetch": { 29 | "version": "2.6.9", 30 | "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.9.tgz", 31 | "integrity": "sha512-bQVlnMLFJ2d35DkPNjEPmd9ueO/rh5EiaZt2bhqiSarPjZIuIV6bPQVqcrEyvNo+AfTrRGVazle1tl597w3gfA==", 32 | "dependencies": { 33 | "@types/node": "*", 34 | "form-data": "^4.0.0" 35 | } 36 | }, 37 | "node_modules/abort-controller": { 38 | "version": "3.0.0", 39 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 40 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 41 | "dependencies": { 42 | "event-target-shim": "^5.0.0" 43 | }, 44 | "engines": { 45 | "node": ">=6.5" 46 | } 47 | }, 48 | "node_modules/agentkeepalive": { 49 | "version": "4.5.0", 50 | "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", 51 | "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", 52 | "dependencies": { 53 | "humanize-ms": "^1.2.1" 54 | }, 55 | "engines": { 56 | "node": ">= 8.0.0" 57 | } 58 | }, 59 | "node_modules/asynckit": { 60 | "version": "0.4.0", 61 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 62 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 63 | }, 64 | "node_modules/base-64": { 65 | "version": "0.1.0", 66 | "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", 67 | "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" 68 | }, 69 | "node_modules/chalk": { 70 | "version": "5.2.0", 71 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", 72 | "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", 73 | "engines": { 74 | "node": "^12.17.0 || ^14.13 || >=16.0.0" 75 | }, 76 | "funding": { 77 | "url": "https://github.com/chalk/chalk?sponsor=1" 78 | } 79 | }, 80 | "node_modules/charenc": { 81 | "version": "0.0.2", 82 | "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", 83 | "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", 84 | "engines": { 85 | "node": "*" 86 | } 87 | }, 88 | "node_modules/combined-stream": { 89 | "version": "1.0.8", 90 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 91 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 92 | "dependencies": { 93 | "delayed-stream": "~1.0.0" 94 | }, 95 | "engines": { 96 | "node": ">= 0.8" 97 | } 98 | }, 99 | "node_modules/crypt": { 100 | "version": "0.0.2", 101 | "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", 102 | "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", 103 | "engines": { 104 | "node": "*" 105 | } 106 | }, 107 | "node_modules/delayed-stream": { 108 | "version": "1.0.0", 109 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 110 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 111 | "engines": { 112 | "node": ">=0.4.0" 113 | } 114 | }, 115 | "node_modules/digest-fetch": { 116 | "version": "1.3.0", 117 | "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-1.3.0.tgz", 118 | "integrity": "sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==", 119 | "dependencies": { 120 | "base-64": "^0.1.0", 121 | "md5": "^2.3.0" 122 | } 123 | }, 124 | "node_modules/dotenv": { 125 | "version": "16.0.3", 126 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 127 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", 128 | "engines": { 129 | "node": ">=12" 130 | } 131 | }, 132 | "node_modules/event-target-shim": { 133 | "version": "5.0.1", 134 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 135 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 136 | "engines": { 137 | "node": ">=6" 138 | } 139 | }, 140 | "node_modules/form-data": { 141 | "version": "4.0.0", 142 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 143 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 144 | "dependencies": { 145 | "asynckit": "^0.4.0", 146 | "combined-stream": "^1.0.8", 147 | "mime-types": "^2.1.12" 148 | }, 149 | "engines": { 150 | "node": ">= 6" 151 | } 152 | }, 153 | "node_modules/form-data-encoder": { 154 | "version": "1.7.2", 155 | "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", 156 | "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" 157 | }, 158 | "node_modules/formdata-node": { 159 | "version": "4.4.1", 160 | "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", 161 | "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", 162 | "dependencies": { 163 | "node-domexception": "1.0.0", 164 | "web-streams-polyfill": "4.0.0-beta.3" 165 | }, 166 | "engines": { 167 | "node": ">= 12.20" 168 | } 169 | }, 170 | "node_modules/formdata-node/node_modules/web-streams-polyfill": { 171 | "version": "4.0.0-beta.3", 172 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", 173 | "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", 174 | "engines": { 175 | "node": ">= 14" 176 | } 177 | }, 178 | "node_modules/humanize-ms": { 179 | "version": "1.2.1", 180 | "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", 181 | "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", 182 | "dependencies": { 183 | "ms": "^2.0.0" 184 | } 185 | }, 186 | "node_modules/is-buffer": { 187 | "version": "1.1.6", 188 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 189 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" 190 | }, 191 | "node_modules/md5": { 192 | "version": "2.3.0", 193 | "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", 194 | "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", 195 | "dependencies": { 196 | "charenc": "0.0.2", 197 | "crypt": "0.0.2", 198 | "is-buffer": "~1.1.6" 199 | } 200 | }, 201 | "node_modules/mime-db": { 202 | "version": "1.52.0", 203 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 204 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 205 | "engines": { 206 | "node": ">= 0.6" 207 | } 208 | }, 209 | "node_modules/mime-types": { 210 | "version": "2.1.35", 211 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 212 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 213 | "dependencies": { 214 | "mime-db": "1.52.0" 215 | }, 216 | "engines": { 217 | "node": ">= 0.6" 218 | } 219 | }, 220 | "node_modules/ms": { 221 | "version": "2.1.3", 222 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 223 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 224 | }, 225 | "node_modules/node-domexception": { 226 | "version": "1.0.0", 227 | "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", 228 | "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", 229 | "funding": [ 230 | { 231 | "type": "github", 232 | "url": "https://github.com/sponsors/jimmywarting" 233 | }, 234 | { 235 | "type": "github", 236 | "url": "https://paypal.me/jimmywarting" 237 | } 238 | ], 239 | "engines": { 240 | "node": ">=10.5.0" 241 | } 242 | }, 243 | "node_modules/node-fetch": { 244 | "version": "2.7.0", 245 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 246 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 247 | "dependencies": { 248 | "whatwg-url": "^5.0.0" 249 | }, 250 | "engines": { 251 | "node": "4.x || >=6.0.0" 252 | }, 253 | "peerDependencies": { 254 | "encoding": "^0.1.0" 255 | }, 256 | "peerDependenciesMeta": { 257 | "encoding": { 258 | "optional": true 259 | } 260 | } 261 | }, 262 | "node_modules/openai": { 263 | "version": "4.17.4", 264 | "resolved": "https://registry.npmjs.org/openai/-/openai-4.17.4.tgz", 265 | "integrity": "sha512-ThRFkl6snLbcAKS58St7N3CaKuI5WdYUvIjPvf4s+8SdymgNtOfzmZcZXVcCefx04oKFnvZJvIcTh3eAFUUhAQ==", 266 | "dependencies": { 267 | "@types/node": "^18.11.18", 268 | "@types/node-fetch": "^2.6.4", 269 | "abort-controller": "^3.0.0", 270 | "agentkeepalive": "^4.2.1", 271 | "digest-fetch": "^1.3.0", 272 | "form-data-encoder": "1.7.2", 273 | "formdata-node": "^4.3.2", 274 | "node-fetch": "^2.6.7", 275 | "web-streams-polyfill": "^3.2.1" 276 | }, 277 | "bin": { 278 | "openai": "bin/cli" 279 | } 280 | }, 281 | "node_modules/tr46": { 282 | "version": "0.0.3", 283 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 284 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 285 | }, 286 | "node_modules/undici-types": { 287 | "version": "5.26.5", 288 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 289 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 290 | }, 291 | "node_modules/web-streams-polyfill": { 292 | "version": "3.2.1", 293 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", 294 | "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", 295 | "engines": { 296 | "node": ">= 8" 297 | } 298 | }, 299 | "node_modules/webidl-conversions": { 300 | "version": "3.0.1", 301 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 302 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 303 | }, 304 | "node_modules/whatwg-url": { 305 | "version": "5.0.0", 306 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 307 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 308 | "dependencies": { 309 | "tr46": "~0.0.3", 310 | "webidl-conversions": "^3.0.0" 311 | } 312 | } 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /examples/chat-vanilla-js-hosted/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat-vanilla-js-hosted", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "chat-vanilla-js-hosted", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "chalk": "^5.2.0", 13 | "dotenv": "^16.0.3", 14 | "openai": "^4.17.4" 15 | }, 16 | "engines": { 17 | "node": ">=18.0.0" 18 | } 19 | }, 20 | "node_modules/@types/node": { 21 | "version": "18.18.9", 22 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.9.tgz", 23 | "integrity": "sha512-0f5klcuImLnG4Qreu9hPj/rEfFq6YRc5n2mAjSsH+ec/mJL+3voBH0+8T7o8RpFjH7ovc+TRsL/c7OYIQsPTfQ==", 24 | "dependencies": { 25 | "undici-types": "~5.26.4" 26 | } 27 | }, 28 | "node_modules/@types/node-fetch": { 29 | "version": "2.6.9", 30 | "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.9.tgz", 31 | "integrity": "sha512-bQVlnMLFJ2d35DkPNjEPmd9ueO/rh5EiaZt2bhqiSarPjZIuIV6bPQVqcrEyvNo+AfTrRGVazle1tl597w3gfA==", 32 | "dependencies": { 33 | "@types/node": "*", 34 | "form-data": "^4.0.0" 35 | } 36 | }, 37 | "node_modules/abort-controller": { 38 | "version": "3.0.0", 39 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 40 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 41 | "dependencies": { 42 | "event-target-shim": "^5.0.0" 43 | }, 44 | "engines": { 45 | "node": ">=6.5" 46 | } 47 | }, 48 | "node_modules/agentkeepalive": { 49 | "version": "4.5.0", 50 | "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", 51 | "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", 52 | "dependencies": { 53 | "humanize-ms": "^1.2.1" 54 | }, 55 | "engines": { 56 | "node": ">= 8.0.0" 57 | } 58 | }, 59 | "node_modules/asynckit": { 60 | "version": "0.4.0", 61 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 62 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 63 | }, 64 | "node_modules/base-64": { 65 | "version": "0.1.0", 66 | "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", 67 | "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" 68 | }, 69 | "node_modules/chalk": { 70 | "version": "5.2.0", 71 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", 72 | "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", 73 | "engines": { 74 | "node": "^12.17.0 || ^14.13 || >=16.0.0" 75 | }, 76 | "funding": { 77 | "url": "https://github.com/chalk/chalk?sponsor=1" 78 | } 79 | }, 80 | "node_modules/charenc": { 81 | "version": "0.0.2", 82 | "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", 83 | "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", 84 | "engines": { 85 | "node": "*" 86 | } 87 | }, 88 | "node_modules/combined-stream": { 89 | "version": "1.0.8", 90 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 91 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 92 | "dependencies": { 93 | "delayed-stream": "~1.0.0" 94 | }, 95 | "engines": { 96 | "node": ">= 0.8" 97 | } 98 | }, 99 | "node_modules/crypt": { 100 | "version": "0.0.2", 101 | "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", 102 | "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", 103 | "engines": { 104 | "node": "*" 105 | } 106 | }, 107 | "node_modules/delayed-stream": { 108 | "version": "1.0.0", 109 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 110 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 111 | "engines": { 112 | "node": ">=0.4.0" 113 | } 114 | }, 115 | "node_modules/digest-fetch": { 116 | "version": "1.3.0", 117 | "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-1.3.0.tgz", 118 | "integrity": "sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==", 119 | "dependencies": { 120 | "base-64": "^0.1.0", 121 | "md5": "^2.3.0" 122 | } 123 | }, 124 | "node_modules/dotenv": { 125 | "version": "16.0.3", 126 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 127 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", 128 | "engines": { 129 | "node": ">=12" 130 | } 131 | }, 132 | "node_modules/event-target-shim": { 133 | "version": "5.0.1", 134 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 135 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 136 | "engines": { 137 | "node": ">=6" 138 | } 139 | }, 140 | "node_modules/form-data": { 141 | "version": "4.0.0", 142 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 143 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 144 | "dependencies": { 145 | "asynckit": "^0.4.0", 146 | "combined-stream": "^1.0.8", 147 | "mime-types": "^2.1.12" 148 | }, 149 | "engines": { 150 | "node": ">= 6" 151 | } 152 | }, 153 | "node_modules/form-data-encoder": { 154 | "version": "1.7.2", 155 | "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", 156 | "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" 157 | }, 158 | "node_modules/formdata-node": { 159 | "version": "4.4.1", 160 | "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", 161 | "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", 162 | "dependencies": { 163 | "node-domexception": "1.0.0", 164 | "web-streams-polyfill": "4.0.0-beta.3" 165 | }, 166 | "engines": { 167 | "node": ">= 12.20" 168 | } 169 | }, 170 | "node_modules/formdata-node/node_modules/web-streams-polyfill": { 171 | "version": "4.0.0-beta.3", 172 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", 173 | "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", 174 | "engines": { 175 | "node": ">= 14" 176 | } 177 | }, 178 | "node_modules/humanize-ms": { 179 | "version": "1.2.1", 180 | "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", 181 | "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", 182 | "dependencies": { 183 | "ms": "^2.0.0" 184 | } 185 | }, 186 | "node_modules/is-buffer": { 187 | "version": "1.1.6", 188 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 189 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" 190 | }, 191 | "node_modules/md5": { 192 | "version": "2.3.0", 193 | "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", 194 | "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", 195 | "dependencies": { 196 | "charenc": "0.0.2", 197 | "crypt": "0.0.2", 198 | "is-buffer": "~1.1.6" 199 | } 200 | }, 201 | "node_modules/mime-db": { 202 | "version": "1.52.0", 203 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 204 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 205 | "engines": { 206 | "node": ">= 0.6" 207 | } 208 | }, 209 | "node_modules/mime-types": { 210 | "version": "2.1.35", 211 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 212 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 213 | "dependencies": { 214 | "mime-db": "1.52.0" 215 | }, 216 | "engines": { 217 | "node": ">= 0.6" 218 | } 219 | }, 220 | "node_modules/ms": { 221 | "version": "2.1.3", 222 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 223 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 224 | }, 225 | "node_modules/node-domexception": { 226 | "version": "1.0.0", 227 | "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", 228 | "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", 229 | "funding": [ 230 | { 231 | "type": "github", 232 | "url": "https://github.com/sponsors/jimmywarting" 233 | }, 234 | { 235 | "type": "github", 236 | "url": "https://paypal.me/jimmywarting" 237 | } 238 | ], 239 | "engines": { 240 | "node": ">=10.5.0" 241 | } 242 | }, 243 | "node_modules/node-fetch": { 244 | "version": "2.7.0", 245 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 246 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 247 | "dependencies": { 248 | "whatwg-url": "^5.0.0" 249 | }, 250 | "engines": { 251 | "node": "4.x || >=6.0.0" 252 | }, 253 | "peerDependencies": { 254 | "encoding": "^0.1.0" 255 | }, 256 | "peerDependenciesMeta": { 257 | "encoding": { 258 | "optional": true 259 | } 260 | } 261 | }, 262 | "node_modules/openai": { 263 | "version": "4.17.4", 264 | "resolved": "https://registry.npmjs.org/openai/-/openai-4.17.4.tgz", 265 | "integrity": "sha512-ThRFkl6snLbcAKS58St7N3CaKuI5WdYUvIjPvf4s+8SdymgNtOfzmZcZXVcCefx04oKFnvZJvIcTh3eAFUUhAQ==", 266 | "dependencies": { 267 | "@types/node": "^18.11.18", 268 | "@types/node-fetch": "^2.6.4", 269 | "abort-controller": "^3.0.0", 270 | "agentkeepalive": "^4.2.1", 271 | "digest-fetch": "^1.3.0", 272 | "form-data-encoder": "1.7.2", 273 | "formdata-node": "^4.3.2", 274 | "node-fetch": "^2.6.7", 275 | "web-streams-polyfill": "^3.2.1" 276 | }, 277 | "bin": { 278 | "openai": "bin/cli" 279 | } 280 | }, 281 | "node_modules/tr46": { 282 | "version": "0.0.3", 283 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 284 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 285 | }, 286 | "node_modules/undici-types": { 287 | "version": "5.26.5", 288 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 289 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 290 | }, 291 | "node_modules/web-streams-polyfill": { 292 | "version": "3.2.1", 293 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", 294 | "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", 295 | "engines": { 296 | "node": ">= 8" 297 | } 298 | }, 299 | "node_modules/webidl-conversions": { 300 | "version": "3.0.1", 301 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 302 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 303 | }, 304 | "node_modules/whatwg-url": { 305 | "version": "5.0.0", 306 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 307 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 308 | "dependencies": { 309 | "tr46": "~0.0.3", 310 | "webidl-conversions": "^3.0.0" 311 | } 312 | } 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /examples/chat-js/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat-motorhead-example", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "chat-motorhead-example", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "chalk": "^5.2.0", 13 | "dotenv": "^16.0.3", 14 | "langchain": "^0.2.19" 15 | }, 16 | "engines": { 17 | "node": ">=18.0.0" 18 | } 19 | }, 20 | "node_modules/@langchain/core": { 21 | "version": "0.2.36", 22 | "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.2.36.tgz", 23 | "integrity": "sha512-qHLvScqERDeH7y2cLuJaSAlMwg3f/3Oc9nayRSXRU2UuaK/SOhI42cxiPLj1FnuHJSmN0rBQFkrLx02gI4mcVg==", 24 | "dependencies": { 25 | "ansi-styles": "^5.0.0", 26 | "camelcase": "6", 27 | "decamelize": "1.2.0", 28 | "js-tiktoken": "^1.0.12", 29 | "langsmith": "^0.1.56-rc.1", 30 | "mustache": "^4.2.0", 31 | "p-queue": "^6.6.2", 32 | "p-retry": "4", 33 | "uuid": "^10.0.0", 34 | "zod": "^3.22.4", 35 | "zod-to-json-schema": "^3.22.3" 36 | }, 37 | "engines": { 38 | "node": ">=18" 39 | } 40 | }, 41 | "node_modules/@langchain/openai": { 42 | "version": "0.2.11", 43 | "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.2.11.tgz", 44 | "integrity": "sha512-Pu8+WfJojCgSf0bAsXb4AjqvcDyAWyoEB1AoCRNACgEnBWZuitz3hLwCo9I+6hAbeg3QJ37g82yKcmvKAg1feg==", 45 | "dependencies": { 46 | "@langchain/core": ">=0.2.26 <0.3.0", 47 | "js-tiktoken": "^1.0.12", 48 | "openai": "^4.57.3", 49 | "zod": "^3.22.4", 50 | "zod-to-json-schema": "^3.22.3" 51 | }, 52 | "engines": { 53 | "node": ">=18" 54 | } 55 | }, 56 | "node_modules/@langchain/textsplitters": { 57 | "version": "0.0.3", 58 | "resolved": "https://registry.npmjs.org/@langchain/textsplitters/-/textsplitters-0.0.3.tgz", 59 | "integrity": "sha512-cXWgKE3sdWLSqAa8ykbCcUsUF1Kyr5J3HOWYGuobhPEycXW4WI++d5DhzdpL238mzoEXTi90VqfSCra37l5YqA==", 60 | "dependencies": { 61 | "@langchain/core": ">0.2.0 <0.3.0", 62 | "js-tiktoken": "^1.0.12" 63 | }, 64 | "engines": { 65 | "node": ">=18" 66 | } 67 | }, 68 | "node_modules/@types/node": { 69 | "version": "18.19.61", 70 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.61.tgz", 71 | "integrity": "sha512-z8fH66NcVkDzBItOao+Nyh0fiy7CYdxIyxnNCcZ60aY0I+EA/y4TSi/S/W9i8DIQvwVo7a0pgzAxmDeNnqrpkw==", 72 | "dependencies": { 73 | "undici-types": "~5.26.4" 74 | } 75 | }, 76 | "node_modules/@types/node-fetch": { 77 | "version": "2.6.11", 78 | "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", 79 | "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", 80 | "dependencies": { 81 | "@types/node": "*", 82 | "form-data": "^4.0.0" 83 | } 84 | }, 85 | "node_modules/@types/retry": { 86 | "version": "0.12.0", 87 | "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", 88 | "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" 89 | }, 90 | "node_modules/@types/uuid": { 91 | "version": "10.0.0", 92 | "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", 93 | "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==" 94 | }, 95 | "node_modules/abort-controller": { 96 | "version": "3.0.0", 97 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 98 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 99 | "dependencies": { 100 | "event-target-shim": "^5.0.0" 101 | }, 102 | "engines": { 103 | "node": ">=6.5" 104 | } 105 | }, 106 | "node_modules/agentkeepalive": { 107 | "version": "4.5.0", 108 | "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", 109 | "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", 110 | "dependencies": { 111 | "humanize-ms": "^1.2.1" 112 | }, 113 | "engines": { 114 | "node": ">= 8.0.0" 115 | } 116 | }, 117 | "node_modules/ansi-styles": { 118 | "version": "5.2.0", 119 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", 120 | "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", 121 | "engines": { 122 | "node": ">=10" 123 | }, 124 | "funding": { 125 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 126 | } 127 | }, 128 | "node_modules/argparse": { 129 | "version": "2.0.1", 130 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 131 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" 132 | }, 133 | "node_modules/asynckit": { 134 | "version": "0.4.0", 135 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 136 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 137 | }, 138 | "node_modules/base64-js": { 139 | "version": "1.5.1", 140 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 141 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 142 | "funding": [ 143 | { 144 | "type": "github", 145 | "url": "https://github.com/sponsors/feross" 146 | }, 147 | { 148 | "type": "patreon", 149 | "url": "https://www.patreon.com/feross" 150 | }, 151 | { 152 | "type": "consulting", 153 | "url": "https://feross.org/support" 154 | } 155 | ] 156 | }, 157 | "node_modules/binary-extensions": { 158 | "version": "2.2.0", 159 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 160 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 161 | "engines": { 162 | "node": ">=8" 163 | } 164 | }, 165 | "node_modules/camelcase": { 166 | "version": "6.3.0", 167 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 168 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 169 | "engines": { 170 | "node": ">=10" 171 | }, 172 | "funding": { 173 | "url": "https://github.com/sponsors/sindresorhus" 174 | } 175 | }, 176 | "node_modules/chalk": { 177 | "version": "5.2.0", 178 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", 179 | "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", 180 | "engines": { 181 | "node": "^12.17.0 || ^14.13 || >=16.0.0" 182 | }, 183 | "funding": { 184 | "url": "https://github.com/chalk/chalk?sponsor=1" 185 | } 186 | }, 187 | "node_modules/combined-stream": { 188 | "version": "1.0.8", 189 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 190 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 191 | "dependencies": { 192 | "delayed-stream": "~1.0.0" 193 | }, 194 | "engines": { 195 | "node": ">= 0.8" 196 | } 197 | }, 198 | "node_modules/commander": { 199 | "version": "10.0.1", 200 | "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", 201 | "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", 202 | "engines": { 203 | "node": ">=14" 204 | } 205 | }, 206 | "node_modules/decamelize": { 207 | "version": "1.2.0", 208 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 209 | "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", 210 | "engines": { 211 | "node": ">=0.10.0" 212 | } 213 | }, 214 | "node_modules/delayed-stream": { 215 | "version": "1.0.0", 216 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 217 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 218 | "engines": { 219 | "node": ">=0.4.0" 220 | } 221 | }, 222 | "node_modules/dotenv": { 223 | "version": "16.0.3", 224 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 225 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", 226 | "engines": { 227 | "node": ">=12" 228 | } 229 | }, 230 | "node_modules/event-target-shim": { 231 | "version": "5.0.1", 232 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 233 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 234 | "engines": { 235 | "node": ">=6" 236 | } 237 | }, 238 | "node_modules/eventemitter3": { 239 | "version": "4.0.7", 240 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", 241 | "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" 242 | }, 243 | "node_modules/form-data": { 244 | "version": "4.0.1", 245 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", 246 | "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", 247 | "dependencies": { 248 | "asynckit": "^0.4.0", 249 | "combined-stream": "^1.0.8", 250 | "mime-types": "^2.1.12" 251 | }, 252 | "engines": { 253 | "node": ">= 6" 254 | } 255 | }, 256 | "node_modules/form-data-encoder": { 257 | "version": "1.7.2", 258 | "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", 259 | "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" 260 | }, 261 | "node_modules/formdata-node": { 262 | "version": "4.4.1", 263 | "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", 264 | "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", 265 | "dependencies": { 266 | "node-domexception": "1.0.0", 267 | "web-streams-polyfill": "4.0.0-beta.3" 268 | }, 269 | "engines": { 270 | "node": ">= 12.20" 271 | } 272 | }, 273 | "node_modules/humanize-ms": { 274 | "version": "1.2.1", 275 | "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", 276 | "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", 277 | "dependencies": { 278 | "ms": "^2.0.0" 279 | } 280 | }, 281 | "node_modules/js-tiktoken": { 282 | "version": "1.0.15", 283 | "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.15.tgz", 284 | "integrity": "sha512-65ruOWWXDEZHHbAo7EjOcNxOGasQKbL4Fq3jEr2xsCqSsoOo6VVSqzWQb6PRIqypFSDcma4jO90YP0w5X8qVXQ==", 285 | "dependencies": { 286 | "base64-js": "^1.5.1" 287 | } 288 | }, 289 | "node_modules/js-yaml": { 290 | "version": "4.1.0", 291 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 292 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 293 | "dependencies": { 294 | "argparse": "^2.0.1" 295 | }, 296 | "bin": { 297 | "js-yaml": "bin/js-yaml.js" 298 | } 299 | }, 300 | "node_modules/jsonpointer": { 301 | "version": "5.0.1", 302 | "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", 303 | "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", 304 | "engines": { 305 | "node": ">=0.10.0" 306 | } 307 | }, 308 | "node_modules/langchain": { 309 | "version": "0.2.19", 310 | "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.2.19.tgz", 311 | "integrity": "sha512-2NhxNz5hE6eRrklMEidihzHq9J0Lzbyo1HxxZe4CuUIJf5UVSiPj9sqiJ2Ovx7BfgD/SJOursnx/yw/jCjVE1A==", 312 | "dependencies": { 313 | "@langchain/core": ">=0.2.21 <0.3.0", 314 | "@langchain/openai": ">=0.1.0 <0.3.0", 315 | "@langchain/textsplitters": "~0.0.0", 316 | "binary-extensions": "^2.2.0", 317 | "js-tiktoken": "^1.0.12", 318 | "js-yaml": "^4.1.0", 319 | "jsonpointer": "^5.0.1", 320 | "langsmith": "~0.1.40", 321 | "openapi-types": "^12.1.3", 322 | "p-retry": "4", 323 | "uuid": "^10.0.0", 324 | "yaml": "^2.2.1", 325 | "zod": "^3.22.4", 326 | "zod-to-json-schema": "^3.22.3" 327 | }, 328 | "engines": { 329 | "node": ">=18" 330 | }, 331 | "peerDependencies": { 332 | "@aws-sdk/client-s3": "*", 333 | "@aws-sdk/client-sagemaker-runtime": "*", 334 | "@aws-sdk/client-sfn": "*", 335 | "@aws-sdk/credential-provider-node": "*", 336 | "@azure/storage-blob": "*", 337 | "@browserbasehq/sdk": "*", 338 | "@gomomento/sdk": "*", 339 | "@gomomento/sdk-core": "*", 340 | "@gomomento/sdk-web": "^1.51.1", 341 | "@langchain/anthropic": "*", 342 | "@langchain/aws": "*", 343 | "@langchain/cohere": "*", 344 | "@langchain/community": "*", 345 | "@langchain/google-genai": "*", 346 | "@langchain/google-vertexai": "*", 347 | "@langchain/groq": "*", 348 | "@langchain/mistralai": "*", 349 | "@langchain/ollama": "*", 350 | "@mendable/firecrawl-js": "*", 351 | "@notionhq/client": "*", 352 | "@pinecone-database/pinecone": "*", 353 | "@supabase/supabase-js": "*", 354 | "@vercel/kv": "*", 355 | "@xata.io/client": "*", 356 | "apify-client": "*", 357 | "assemblyai": "*", 358 | "axios": "*", 359 | "cheerio": "*", 360 | "chromadb": "*", 361 | "convex": "*", 362 | "couchbase": "*", 363 | "d3-dsv": "*", 364 | "epub2": "*", 365 | "fast-xml-parser": "*", 366 | "handlebars": "^4.7.8", 367 | "html-to-text": "*", 368 | "ignore": "*", 369 | "ioredis": "*", 370 | "jsdom": "*", 371 | "mammoth": "*", 372 | "mongodb": "*", 373 | "node-llama-cpp": "*", 374 | "notion-to-md": "*", 375 | "officeparser": "*", 376 | "pdf-parse": "*", 377 | "peggy": "^3.0.2", 378 | "playwright": "*", 379 | "puppeteer": "*", 380 | "pyodide": ">=0.24.1 <0.27.0", 381 | "redis": "*", 382 | "sonix-speech-recognition": "*", 383 | "srt-parser-2": "*", 384 | "typeorm": "*", 385 | "weaviate-ts-client": "*", 386 | "web-auth-library": "*", 387 | "ws": "*", 388 | "youtube-transcript": "*", 389 | "youtubei.js": "*" 390 | }, 391 | "peerDependenciesMeta": { 392 | "@aws-sdk/client-s3": { 393 | "optional": true 394 | }, 395 | "@aws-sdk/client-sagemaker-runtime": { 396 | "optional": true 397 | }, 398 | "@aws-sdk/client-sfn": { 399 | "optional": true 400 | }, 401 | "@aws-sdk/credential-provider-node": { 402 | "optional": true 403 | }, 404 | "@azure/storage-blob": { 405 | "optional": true 406 | }, 407 | "@browserbasehq/sdk": { 408 | "optional": true 409 | }, 410 | "@gomomento/sdk": { 411 | "optional": true 412 | }, 413 | "@gomomento/sdk-core": { 414 | "optional": true 415 | }, 416 | "@gomomento/sdk-web": { 417 | "optional": true 418 | }, 419 | "@langchain/anthropic": { 420 | "optional": true 421 | }, 422 | "@langchain/aws": { 423 | "optional": true 424 | }, 425 | "@langchain/cohere": { 426 | "optional": true 427 | }, 428 | "@langchain/community": { 429 | "optional": true 430 | }, 431 | "@langchain/google-genai": { 432 | "optional": true 433 | }, 434 | "@langchain/google-vertexai": { 435 | "optional": true 436 | }, 437 | "@langchain/groq": { 438 | "optional": true 439 | }, 440 | "@langchain/mistralai": { 441 | "optional": true 442 | }, 443 | "@langchain/ollama": { 444 | "optional": true 445 | }, 446 | "@mendable/firecrawl-js": { 447 | "optional": true 448 | }, 449 | "@notionhq/client": { 450 | "optional": true 451 | }, 452 | "@pinecone-database/pinecone": { 453 | "optional": true 454 | }, 455 | "@supabase/supabase-js": { 456 | "optional": true 457 | }, 458 | "@vercel/kv": { 459 | "optional": true 460 | }, 461 | "@xata.io/client": { 462 | "optional": true 463 | }, 464 | "apify-client": { 465 | "optional": true 466 | }, 467 | "assemblyai": { 468 | "optional": true 469 | }, 470 | "axios": { 471 | "optional": true 472 | }, 473 | "cheerio": { 474 | "optional": true 475 | }, 476 | "chromadb": { 477 | "optional": true 478 | }, 479 | "convex": { 480 | "optional": true 481 | }, 482 | "couchbase": { 483 | "optional": true 484 | }, 485 | "d3-dsv": { 486 | "optional": true 487 | }, 488 | "epub2": { 489 | "optional": true 490 | }, 491 | "faiss-node": { 492 | "optional": true 493 | }, 494 | "fast-xml-parser": { 495 | "optional": true 496 | }, 497 | "handlebars": { 498 | "optional": true 499 | }, 500 | "html-to-text": { 501 | "optional": true 502 | }, 503 | "ignore": { 504 | "optional": true 505 | }, 506 | "ioredis": { 507 | "optional": true 508 | }, 509 | "jsdom": { 510 | "optional": true 511 | }, 512 | "mammoth": { 513 | "optional": true 514 | }, 515 | "mongodb": { 516 | "optional": true 517 | }, 518 | "node-llama-cpp": { 519 | "optional": true 520 | }, 521 | "notion-to-md": { 522 | "optional": true 523 | }, 524 | "officeparser": { 525 | "optional": true 526 | }, 527 | "pdf-parse": { 528 | "optional": true 529 | }, 530 | "peggy": { 531 | "optional": true 532 | }, 533 | "playwright": { 534 | "optional": true 535 | }, 536 | "puppeteer": { 537 | "optional": true 538 | }, 539 | "pyodide": { 540 | "optional": true 541 | }, 542 | "redis": { 543 | "optional": true 544 | }, 545 | "sonix-speech-recognition": { 546 | "optional": true 547 | }, 548 | "srt-parser-2": { 549 | "optional": true 550 | }, 551 | "typeorm": { 552 | "optional": true 553 | }, 554 | "weaviate-ts-client": { 555 | "optional": true 556 | }, 557 | "web-auth-library": { 558 | "optional": true 559 | }, 560 | "ws": { 561 | "optional": true 562 | }, 563 | "youtube-transcript": { 564 | "optional": true 565 | }, 566 | "youtubei.js": { 567 | "optional": true 568 | } 569 | } 570 | }, 571 | "node_modules/langsmith": { 572 | "version": "0.1.68", 573 | "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.1.68.tgz", 574 | "integrity": "sha512-otmiysWtVAqzMx3CJ4PrtUBhWRG5Co8Z4o7hSZENPjlit9/j3/vm3TSvbaxpDYakZxtMjhkcJTqrdYFipISEiQ==", 575 | "dependencies": { 576 | "@types/uuid": "^10.0.0", 577 | "commander": "^10.0.1", 578 | "p-queue": "^6.6.2", 579 | "p-retry": "4", 580 | "semver": "^7.6.3", 581 | "uuid": "^10.0.0" 582 | }, 583 | "peerDependencies": { 584 | "openai": "*" 585 | }, 586 | "peerDependenciesMeta": { 587 | "openai": { 588 | "optional": true 589 | } 590 | } 591 | }, 592 | "node_modules/mime-db": { 593 | "version": "1.52.0", 594 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 595 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 596 | "engines": { 597 | "node": ">= 0.6" 598 | } 599 | }, 600 | "node_modules/mime-types": { 601 | "version": "2.1.35", 602 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 603 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 604 | "dependencies": { 605 | "mime-db": "1.52.0" 606 | }, 607 | "engines": { 608 | "node": ">= 0.6" 609 | } 610 | }, 611 | "node_modules/ms": { 612 | "version": "2.1.3", 613 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 614 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 615 | }, 616 | "node_modules/mustache": { 617 | "version": "4.2.0", 618 | "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", 619 | "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", 620 | "bin": { 621 | "mustache": "bin/mustache" 622 | } 623 | }, 624 | "node_modules/node-domexception": { 625 | "version": "1.0.0", 626 | "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", 627 | "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", 628 | "funding": [ 629 | { 630 | "type": "github", 631 | "url": "https://github.com/sponsors/jimmywarting" 632 | }, 633 | { 634 | "type": "github", 635 | "url": "https://paypal.me/jimmywarting" 636 | } 637 | ], 638 | "engines": { 639 | "node": ">=10.5.0" 640 | } 641 | }, 642 | "node_modules/node-fetch": { 643 | "version": "2.7.0", 644 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 645 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 646 | "dependencies": { 647 | "whatwg-url": "^5.0.0" 648 | }, 649 | "engines": { 650 | "node": "4.x || >=6.0.0" 651 | }, 652 | "peerDependencies": { 653 | "encoding": "^0.1.0" 654 | }, 655 | "peerDependenciesMeta": { 656 | "encoding": { 657 | "optional": true 658 | } 659 | } 660 | }, 661 | "node_modules/openai": { 662 | "version": "4.68.4", 663 | "resolved": "https://registry.npmjs.org/openai/-/openai-4.68.4.tgz", 664 | "integrity": "sha512-LRinV8iU9VQplkr25oZlyrsYGPGasIwYN8KFMAAFTHHLHjHhejtJ5BALuLFrkGzY4wfbKhOhuT+7lcHZ+F3iEA==", 665 | "dependencies": { 666 | "@types/node": "^18.11.18", 667 | "@types/node-fetch": "^2.6.4", 668 | "abort-controller": "^3.0.0", 669 | "agentkeepalive": "^4.2.1", 670 | "form-data-encoder": "1.7.2", 671 | "formdata-node": "^4.3.2", 672 | "node-fetch": "^2.6.7" 673 | }, 674 | "bin": { 675 | "openai": "bin/cli" 676 | }, 677 | "peerDependencies": { 678 | "zod": "^3.23.8" 679 | }, 680 | "peerDependenciesMeta": { 681 | "zod": { 682 | "optional": true 683 | } 684 | } 685 | }, 686 | "node_modules/openapi-types": { 687 | "version": "12.1.3", 688 | "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", 689 | "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==" 690 | }, 691 | "node_modules/p-finally": { 692 | "version": "1.0.0", 693 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 694 | "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", 695 | "engines": { 696 | "node": ">=4" 697 | } 698 | }, 699 | "node_modules/p-queue": { 700 | "version": "6.6.2", 701 | "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", 702 | "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", 703 | "dependencies": { 704 | "eventemitter3": "^4.0.4", 705 | "p-timeout": "^3.2.0" 706 | }, 707 | "engines": { 708 | "node": ">=8" 709 | }, 710 | "funding": { 711 | "url": "https://github.com/sponsors/sindresorhus" 712 | } 713 | }, 714 | "node_modules/p-retry": { 715 | "version": "4.6.2", 716 | "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", 717 | "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", 718 | "dependencies": { 719 | "@types/retry": "0.12.0", 720 | "retry": "^0.13.1" 721 | }, 722 | "engines": { 723 | "node": ">=8" 724 | } 725 | }, 726 | "node_modules/p-timeout": { 727 | "version": "3.2.0", 728 | "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", 729 | "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", 730 | "dependencies": { 731 | "p-finally": "^1.0.0" 732 | }, 733 | "engines": { 734 | "node": ">=8" 735 | } 736 | }, 737 | "node_modules/retry": { 738 | "version": "0.13.1", 739 | "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", 740 | "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", 741 | "engines": { 742 | "node": ">= 4" 743 | } 744 | }, 745 | "node_modules/semver": { 746 | "version": "7.6.3", 747 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", 748 | "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", 749 | "bin": { 750 | "semver": "bin/semver.js" 751 | }, 752 | "engines": { 753 | "node": ">=10" 754 | } 755 | }, 756 | "node_modules/tr46": { 757 | "version": "0.0.3", 758 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 759 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 760 | }, 761 | "node_modules/undici-types": { 762 | "version": "5.26.5", 763 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 764 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 765 | }, 766 | "node_modules/uuid": { 767 | "version": "10.0.0", 768 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", 769 | "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", 770 | "funding": [ 771 | "https://github.com/sponsors/broofa", 772 | "https://github.com/sponsors/ctavan" 773 | ], 774 | "bin": { 775 | "uuid": "dist/bin/uuid" 776 | } 777 | }, 778 | "node_modules/web-streams-polyfill": { 779 | "version": "4.0.0-beta.3", 780 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", 781 | "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", 782 | "engines": { 783 | "node": ">= 14" 784 | } 785 | }, 786 | "node_modules/webidl-conversions": { 787 | "version": "3.0.1", 788 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 789 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 790 | }, 791 | "node_modules/whatwg-url": { 792 | "version": "5.0.0", 793 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 794 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 795 | "dependencies": { 796 | "tr46": "~0.0.3", 797 | "webidl-conversions": "^3.0.0" 798 | } 799 | }, 800 | "node_modules/yaml": { 801 | "version": "2.2.2", 802 | "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", 803 | "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", 804 | "engines": { 805 | "node": ">= 14" 806 | } 807 | }, 808 | "node_modules/zod": { 809 | "version": "3.23.8", 810 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", 811 | "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", 812 | "funding": { 813 | "url": "https://github.com/sponsors/colinhacks" 814 | } 815 | }, 816 | "node_modules/zod-to-json-schema": { 817 | "version": "3.23.5", 818 | "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.23.5.tgz", 819 | "integrity": "sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA==", 820 | "peerDependencies": { 821 | "zod": "^3.23.3" 822 | } 823 | } 824 | }, 825 | "dependencies": { 826 | "@langchain/core": { 827 | "version": "0.2.36", 828 | "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.2.36.tgz", 829 | "integrity": "sha512-qHLvScqERDeH7y2cLuJaSAlMwg3f/3Oc9nayRSXRU2UuaK/SOhI42cxiPLj1FnuHJSmN0rBQFkrLx02gI4mcVg==", 830 | "requires": { 831 | "ansi-styles": "^5.0.0", 832 | "camelcase": "6", 833 | "decamelize": "1.2.0", 834 | "js-tiktoken": "^1.0.12", 835 | "langsmith": "^0.1.56-rc.1", 836 | "mustache": "^4.2.0", 837 | "p-queue": "^6.6.2", 838 | "p-retry": "4", 839 | "uuid": "^10.0.0", 840 | "zod": "^3.22.4", 841 | "zod-to-json-schema": "^3.22.3" 842 | } 843 | }, 844 | "@langchain/openai": { 845 | "version": "0.2.11", 846 | "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.2.11.tgz", 847 | "integrity": "sha512-Pu8+WfJojCgSf0bAsXb4AjqvcDyAWyoEB1AoCRNACgEnBWZuitz3hLwCo9I+6hAbeg3QJ37g82yKcmvKAg1feg==", 848 | "requires": { 849 | "@langchain/core": ">=0.2.26 <0.3.0", 850 | "js-tiktoken": "^1.0.12", 851 | "openai": "^4.57.3", 852 | "zod": "^3.22.4", 853 | "zod-to-json-schema": "^3.22.3" 854 | } 855 | }, 856 | "@langchain/textsplitters": { 857 | "version": "0.0.3", 858 | "resolved": "https://registry.npmjs.org/@langchain/textsplitters/-/textsplitters-0.0.3.tgz", 859 | "integrity": "sha512-cXWgKE3sdWLSqAa8ykbCcUsUF1Kyr5J3HOWYGuobhPEycXW4WI++d5DhzdpL238mzoEXTi90VqfSCra37l5YqA==", 860 | "requires": { 861 | "@langchain/core": ">0.2.0 <0.3.0", 862 | "js-tiktoken": "^1.0.12" 863 | } 864 | }, 865 | "@types/node": { 866 | "version": "18.19.61", 867 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.61.tgz", 868 | "integrity": "sha512-z8fH66NcVkDzBItOao+Nyh0fiy7CYdxIyxnNCcZ60aY0I+EA/y4TSi/S/W9i8DIQvwVo7a0pgzAxmDeNnqrpkw==", 869 | "requires": { 870 | "undici-types": "~5.26.4" 871 | } 872 | }, 873 | "@types/node-fetch": { 874 | "version": "2.6.11", 875 | "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", 876 | "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", 877 | "requires": { 878 | "@types/node": "*", 879 | "form-data": "^4.0.0" 880 | } 881 | }, 882 | "@types/retry": { 883 | "version": "0.12.0", 884 | "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", 885 | "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" 886 | }, 887 | "@types/uuid": { 888 | "version": "10.0.0", 889 | "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", 890 | "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==" 891 | }, 892 | "abort-controller": { 893 | "version": "3.0.0", 894 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 895 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 896 | "requires": { 897 | "event-target-shim": "^5.0.0" 898 | } 899 | }, 900 | "agentkeepalive": { 901 | "version": "4.5.0", 902 | "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", 903 | "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", 904 | "requires": { 905 | "humanize-ms": "^1.2.1" 906 | } 907 | }, 908 | "ansi-styles": { 909 | "version": "5.2.0", 910 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", 911 | "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" 912 | }, 913 | "argparse": { 914 | "version": "2.0.1", 915 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 916 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" 917 | }, 918 | "asynckit": { 919 | "version": "0.4.0", 920 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 921 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 922 | }, 923 | "base64-js": { 924 | "version": "1.5.1", 925 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 926 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 927 | }, 928 | "binary-extensions": { 929 | "version": "2.2.0", 930 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 931 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" 932 | }, 933 | "camelcase": { 934 | "version": "6.3.0", 935 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 936 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" 937 | }, 938 | "chalk": { 939 | "version": "5.2.0", 940 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", 941 | "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==" 942 | }, 943 | "combined-stream": { 944 | "version": "1.0.8", 945 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 946 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 947 | "requires": { 948 | "delayed-stream": "~1.0.0" 949 | } 950 | }, 951 | "commander": { 952 | "version": "10.0.1", 953 | "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", 954 | "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==" 955 | }, 956 | "decamelize": { 957 | "version": "1.2.0", 958 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 959 | "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" 960 | }, 961 | "delayed-stream": { 962 | "version": "1.0.0", 963 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 964 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" 965 | }, 966 | "dotenv": { 967 | "version": "16.0.3", 968 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 969 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" 970 | }, 971 | "event-target-shim": { 972 | "version": "5.0.1", 973 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 974 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" 975 | }, 976 | "eventemitter3": { 977 | "version": "4.0.7", 978 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", 979 | "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" 980 | }, 981 | "form-data": { 982 | "version": "4.0.1", 983 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", 984 | "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", 985 | "requires": { 986 | "asynckit": "^0.4.0", 987 | "combined-stream": "^1.0.8", 988 | "mime-types": "^2.1.12" 989 | } 990 | }, 991 | "form-data-encoder": { 992 | "version": "1.7.2", 993 | "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", 994 | "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" 995 | }, 996 | "formdata-node": { 997 | "version": "4.4.1", 998 | "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", 999 | "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", 1000 | "requires": { 1001 | "node-domexception": "1.0.0", 1002 | "web-streams-polyfill": "4.0.0-beta.3" 1003 | } 1004 | }, 1005 | "humanize-ms": { 1006 | "version": "1.2.1", 1007 | "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", 1008 | "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", 1009 | "requires": { 1010 | "ms": "^2.0.0" 1011 | } 1012 | }, 1013 | "js-tiktoken": { 1014 | "version": "1.0.15", 1015 | "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.15.tgz", 1016 | "integrity": "sha512-65ruOWWXDEZHHbAo7EjOcNxOGasQKbL4Fq3jEr2xsCqSsoOo6VVSqzWQb6PRIqypFSDcma4jO90YP0w5X8qVXQ==", 1017 | "requires": { 1018 | "base64-js": "^1.5.1" 1019 | } 1020 | }, 1021 | "js-yaml": { 1022 | "version": "4.1.0", 1023 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 1024 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1025 | "requires": { 1026 | "argparse": "^2.0.1" 1027 | } 1028 | }, 1029 | "jsonpointer": { 1030 | "version": "5.0.1", 1031 | "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", 1032 | "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==" 1033 | }, 1034 | "langchain": { 1035 | "version": "0.2.19", 1036 | "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.2.19.tgz", 1037 | "integrity": "sha512-2NhxNz5hE6eRrklMEidihzHq9J0Lzbyo1HxxZe4CuUIJf5UVSiPj9sqiJ2Ovx7BfgD/SJOursnx/yw/jCjVE1A==", 1038 | "requires": { 1039 | "@langchain/core": ">=0.2.21 <0.3.0", 1040 | "@langchain/openai": ">=0.1.0 <0.3.0", 1041 | "@langchain/textsplitters": "~0.0.0", 1042 | "binary-extensions": "^2.2.0", 1043 | "js-tiktoken": "^1.0.12", 1044 | "js-yaml": "^4.1.0", 1045 | "jsonpointer": "^5.0.1", 1046 | "langsmith": "~0.1.40", 1047 | "openapi-types": "^12.1.3", 1048 | "p-retry": "4", 1049 | "uuid": "^10.0.0", 1050 | "yaml": "^2.2.1", 1051 | "zod": "^3.22.4", 1052 | "zod-to-json-schema": "^3.22.3" 1053 | } 1054 | }, 1055 | "langsmith": { 1056 | "version": "0.1.68", 1057 | "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.1.68.tgz", 1058 | "integrity": "sha512-otmiysWtVAqzMx3CJ4PrtUBhWRG5Co8Z4o7hSZENPjlit9/j3/vm3TSvbaxpDYakZxtMjhkcJTqrdYFipISEiQ==", 1059 | "requires": { 1060 | "@types/uuid": "^10.0.0", 1061 | "commander": "^10.0.1", 1062 | "p-queue": "^6.6.2", 1063 | "p-retry": "4", 1064 | "semver": "^7.6.3", 1065 | "uuid": "^10.0.0" 1066 | } 1067 | }, 1068 | "mime-db": { 1069 | "version": "1.52.0", 1070 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 1071 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" 1072 | }, 1073 | "mime-types": { 1074 | "version": "2.1.35", 1075 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 1076 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 1077 | "requires": { 1078 | "mime-db": "1.52.0" 1079 | } 1080 | }, 1081 | "ms": { 1082 | "version": "2.1.3", 1083 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1084 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1085 | }, 1086 | "mustache": { 1087 | "version": "4.2.0", 1088 | "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", 1089 | "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==" 1090 | }, 1091 | "node-domexception": { 1092 | "version": "1.0.0", 1093 | "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", 1094 | "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" 1095 | }, 1096 | "node-fetch": { 1097 | "version": "2.7.0", 1098 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 1099 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 1100 | "requires": { 1101 | "whatwg-url": "^5.0.0" 1102 | } 1103 | }, 1104 | "openai": { 1105 | "version": "4.68.4", 1106 | "resolved": "https://registry.npmjs.org/openai/-/openai-4.68.4.tgz", 1107 | "integrity": "sha512-LRinV8iU9VQplkr25oZlyrsYGPGasIwYN8KFMAAFTHHLHjHhejtJ5BALuLFrkGzY4wfbKhOhuT+7lcHZ+F3iEA==", 1108 | "requires": { 1109 | "@types/node": "^18.11.18", 1110 | "@types/node-fetch": "^2.6.4", 1111 | "abort-controller": "^3.0.0", 1112 | "agentkeepalive": "^4.2.1", 1113 | "form-data-encoder": "1.7.2", 1114 | "formdata-node": "^4.3.2", 1115 | "node-fetch": "^2.6.7" 1116 | } 1117 | }, 1118 | "openapi-types": { 1119 | "version": "12.1.3", 1120 | "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", 1121 | "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==" 1122 | }, 1123 | "p-finally": { 1124 | "version": "1.0.0", 1125 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 1126 | "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==" 1127 | }, 1128 | "p-queue": { 1129 | "version": "6.6.2", 1130 | "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", 1131 | "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", 1132 | "requires": { 1133 | "eventemitter3": "^4.0.4", 1134 | "p-timeout": "^3.2.0" 1135 | } 1136 | }, 1137 | "p-retry": { 1138 | "version": "4.6.2", 1139 | "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", 1140 | "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", 1141 | "requires": { 1142 | "@types/retry": "0.12.0", 1143 | "retry": "^0.13.1" 1144 | } 1145 | }, 1146 | "p-timeout": { 1147 | "version": "3.2.0", 1148 | "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", 1149 | "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", 1150 | "requires": { 1151 | "p-finally": "^1.0.0" 1152 | } 1153 | }, 1154 | "retry": { 1155 | "version": "0.13.1", 1156 | "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", 1157 | "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" 1158 | }, 1159 | "semver": { 1160 | "version": "7.6.3", 1161 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", 1162 | "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==" 1163 | }, 1164 | "tr46": { 1165 | "version": "0.0.3", 1166 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 1167 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 1168 | }, 1169 | "undici-types": { 1170 | "version": "5.26.5", 1171 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 1172 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 1173 | }, 1174 | "uuid": { 1175 | "version": "10.0.0", 1176 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", 1177 | "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==" 1178 | }, 1179 | "web-streams-polyfill": { 1180 | "version": "4.0.0-beta.3", 1181 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", 1182 | "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==" 1183 | }, 1184 | "webidl-conversions": { 1185 | "version": "3.0.1", 1186 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 1187 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 1188 | }, 1189 | "whatwg-url": { 1190 | "version": "5.0.0", 1191 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 1192 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 1193 | "requires": { 1194 | "tr46": "~0.0.3", 1195 | "webidl-conversions": "^3.0.0" 1196 | } 1197 | }, 1198 | "yaml": { 1199 | "version": "2.2.2", 1200 | "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", 1201 | "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==" 1202 | }, 1203 | "zod": { 1204 | "version": "3.23.8", 1205 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", 1206 | "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==" 1207 | }, 1208 | "zod-to-json-schema": { 1209 | "version": "3.23.5", 1210 | "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.23.5.tgz", 1211 | "integrity": "sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA==", 1212 | "requires": {} 1213 | } 1214 | } 1215 | } 1216 | --------------------------------------------------------------------------------