Skip to content

Commit

Permalink
Added r2r-js as regular directory
Browse files Browse the repository at this point in the history
  • Loading branch information
NolanTrem committed Aug 3, 2024
1 parent edf976e commit 2daa273
Show file tree
Hide file tree
Showing 17 changed files with 9,603 additions and 0 deletions.
148 changes: 148 additions & 0 deletions r2r-js/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
<p align="left">
<a href="https://r2r-docs.sciphi.ai"><img src="https://img.shields.io/badge/docs.sciphi.ai-3F16E4" alt="Docs"></a>
<a href="https://discord.gg/p6KqD2kjtB"><img src="https://img.shields.io/discord/1120774652915105934?style=social&logo=discord" alt="Discord"></a>
<a href="https://github.com/SciPhi-AI/R2R"><img src="https://img.shields.io/github/stars/SciPhi-AI/R2R" alt="Github Stars"></a>
<a href="https://github.com/SciPhi-AI/R2R/pulse"><img src="https://img.shields.io/github/commit-activity/w/SciPhi-AI/R2R" alt="Commits-per-week"></a>
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-purple.svg" alt="License: MIT"></a>
<a href="https://www.npmjs.com/package/r2r-js"><img src="https://img.shields.io/npm/v/r2r-js.svg" alt="npm version"></a>
</p>

<img src="https://raw.githubusercontent.com/SciPhi-AI/R2R/main/assets/r2r.png" alt="R2R JavaScript Client">
<h3 align="center">
The ultimate open source RAG answer engine - JavaScript Client
</h3>

# About

The official JavaScript client for R2R (Retrieval-Augmented Generation to Riches). R2R is designed to bridge the gap between local LLM experimentation and scalable, production-ready Retrieval-Augmented Generation (RAG). This JavaScript client provides a seamless interface to interact with the R2R RESTful API.

For a more complete view of R2R, check out the [full documentation](https://r2r-docs.sciphi.ai/).

## Key Features

- **📁 Multimodal Support**: Ingest files ranging from `.txt`, `.pdf`, `.json` to `.png`, `.mp3`, and more.
- **🔍 Hybrid Search**: Combine semantic and keyword search with reciprocal rank fusion for enhanced relevancy.
- **🔗 Graph RAG**: Automatically extract relationships and build knowledge graphs.
- **🗂️ App Management**: Efficiently manage documents and users with rich observability and analytics.
- **🌐 Client-Server**: RESTful API support out of the box.
- **🧩 Configurable**: Provision your application using intuitive configuration files.
- **🔌 Extensible**: Develop your application further with easy builder + factory pattern.
- **🖥️ Dashboard**: Use the [R2R Dashboard](https://github.com/SciPhi-AI/R2R-Dashboard), an open-source React+Next.js app for a user-friendly interaction with R2R.

## Table of Contents

1. [Install](#install)
2. [R2R JavaScript Client Quickstart](#r2r-javascript-client-quickstart)
3. [Community and Support](#community-and-support)
4. [Contributing](#contributing)

# Install

```bash
npm install r2r-js
```

# R2R JavaScript Client Quickstart

## Initialize the R2R client

```javascript
const { r2rClient } = require("r2r-js");

const client = new r2rClient("http://localhost:8000");
```

## Login

```javascript
const EMAIL = "[email protected]";
const PASSWORD = "change_me_immediately";
console.log("Logging in...");
await client.login(EMAIL, PASSWORD);
```

## Ingest files

```javascript
const files = [
{ path: "examples/data/raskolnikov.txt", name: "raskolnikov.txt" },
{ path: "examples/data/karamozov.txt", name: "karamozov.txt" },
];

const ingestResult = await client.ingestFiles(files, {
metadatas: [{ title: "raskolnikov.txt" }, { title: "karamozov.txt" }],
user_ids: [
"123e4567-e89b-12d3-a456-426614174000",
"123e4567-e89b-12d3-a456-426614174000",
],
skip_document_info: false,
});
console.log(ingestResult);
```

## Perform a search

```javascript
const searchResult = await client.search("Who was Raskolnikov?");
console.log(searchResult);
```

## Perform RAG

```javascript
const ragResult = await client.rag({
query: "Who was Raskolnikov?",
use_vector_search: true,
search_filters: {},
search_limit: 10,
do_hybrid_search: false,
use_kg_search: false,
kg_generation_config: {},
rag_generation_config: {
model: "gpt-4o",
temperature: 0.0,
stream: false,
},
});
console.log(ragResult);
```

## Stream a RAG Response

```javascript
const streamingRagResult = await client.rag({
query: "Who was Raskolnikov?",
rag_generation_config: {
stream: true,
},
});

if (streamingRagResult instanceof ReadableStream) {
const reader = streamingRagResult.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log(new TextDecoder().decode(value));
}
}
```

# Community and Support

- [Discord](https://discord.gg/p6KqD2kjtB): Chat live with maintainers and community members
- [Github Issues](https://github.com/SciPhi-AI/R2R-js/issues): Report bugs and request features

**Explore our [R2R Docs](https://r2r-docs.sciphi.ai/) for tutorials and cookbooks on various R2R features and integrations.**

# Contributing

We welcome contributions of all sizes! Here's how you can help:

- Open a PR for new features, improvements, or better documentation.
- Submit a [feature request](https://github.com/SciPhi-AI/R2R-js/issues/new?assignees=&labels=&projects=&template=feature_request.md&title=) or [bug report](https://github.com/SciPhi-AI/R2R-js/issues/new?assignees=&labels=&projects=&template=bug_report.md&title=)

### Our Contributors

<a href="https://github.com/SciPhi-AI/R2R/graphs/contributors">
<img src="https://contrib.rocks/image?repo=SciPhi-AI/R2R" />
</a>
43 changes: 43 additions & 0 deletions r2r-js/__tests__/r2rClient.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { r2rClient } from "../src/r2rClient";
import axios from "axios";

jest.mock("axios");

describe("R2RClient", () => {
let client: r2rClient;
let mockAxiosInstance: any;

beforeEach(() => {
mockAxiosInstance = {
post: jest.fn(),
request: jest.fn(),
defaults: { baseURL: "http://0.0.0.0:8000/v1" },
};

(axios.create as jest.Mock).mockReturnValue(mockAxiosInstance);

client = new r2rClient("http://0.0.0.0:8000");
});

describe("Mocked Tests", () => {
test("should correctly set the baseURL with prefix", () => {
expect((client as any).axiosInstance.defaults.baseURL).toBe(
"http://0.0.0.0:8000/v1",
);
});

test("health should return data from the /health endpoint", async () => {
const mockResponse = { response: "ok" };
mockAxiosInstance.request.mockResolvedValue({ data: mockResponse });

const result = await client.health();
expect(result).toEqual(mockResponse);
expect(mockAxiosInstance.request).toHaveBeenCalledWith({
method: "GET",
url: "health",
headers: {},
responseType: "json",
});
});
});
});
168 changes: 168 additions & 0 deletions r2r-js/__tests__/r2rClientIntegrationSuperUser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import { r2rClient } from "../src/index";
import { FilterCriteria, AnalysisTypes } from "../src/models";
const fs = require("fs");

const baseUrl = "http://localhost:8000";

describe("r2rClient Integration Tests", () => {
let client: r2rClient;

beforeAll(async () => {
client = new r2rClient(baseUrl);
});

test("Health check", async () => {
await expect(client.health()).resolves.not.toThrow();
});

test("Login", async () => {
await expect(
client.login("[email protected]", "change_me_immediately"),
).resolves.not.toThrow();
});

test("Server stats", async () => {
await expect(client.serverStats()).resolves.not.toThrow();
});

test("Ingest file", async () => {
const files = [
{ path: "examples/data/raskolnikov.txt", name: "raskolnikov.txt" },
];

await expect(
client.ingestFiles(files, {
metadatas: [{ title: "raskolnikov.txt" }, { title: "karamozov.txt" }],
user_ids: ["123e4567-e89b-12d3-a456-426614174000"],
skip_document_info: false,
}),
).resolves.not.toThrow();
});

test("Ingest files in folder", async () => {
const files = ["examples/data/folder"];

await expect(client.ingestFiles(files)).resolves.not.toThrow();
});

test("Update files", async () => {
const updated_file = [
{ path: "examples/data/folder/myshkin.txt", name: "super_myshkin.txt" },
];
await expect(
client.updateFiles(updated_file, {
document_ids: ["f3c6afa5-fc58-58b7-b797-f7148e5253c3"],
metadatas: [{ title: "updated_karamozov.txt" }],
}),
).resolves.not.toThrow();
});

test("Search documents", async () => {
await expect(client.search("test")).resolves.not.toThrow();
});

test("Generate RAG response", async () => {
await expect(client.rag({ query: "test" })).resolves.not.toThrow();
}, 30000);

test("Generate RAG Chat response", async () => {
const messages = [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Tell me about Raskolnikov." },
];

await expect(client.agent({ messages })).resolves.not.toThrow();
}, 30000);

test("Generate RAG Chat response with streaming", async () => {
const messages = [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Tell me about Raskolnikov." },
];

const streamingConfig = {
messages,
rag_generation_config: { stream: true },
};

const stream = await client.agent(streamingConfig);

expect(stream).toBeDefined();
expect(stream instanceof ReadableStream).toBe(true);

let fullResponse = "";
const reader = stream.getReader();

while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}

const chunk = new TextDecoder().decode(value);
fullResponse += chunk;
}

expect(fullResponse.length).toBeGreaterThan(0);
}, 30000);

test("Delete document", async () => {
await expect(
client.delete(["document_id"], ["cb6e55f3-cb3e-5646-ad52-42f06eb321f5"]),
).resolves.not.toThrow();
});

test("Get logs", async () => {
await expect(client.logs()).resolves.not.toThrow();
});

test("App settings", async () => {
await expect(client.appSettings()).resolves.not.toThrow();
});

test("Get analytics", async () => {
const filterCriteria: FilterCriteria = {
filters: {
search_latencies: "search_latency",
},
};

const analysisTypes: AnalysisTypes = {
analysis_types: {
search_latencies: ["basic_statistics", "search_latency"],
},
};

await expect(
client.analytics(filterCriteria, analysisTypes),
).resolves.not.toThrow();
});

test("Get users overview", async () => {
await expect(client.usersOverview()).resolves.not.toThrow();
});

test("Get documents overview", async () => {
await expect(client.documentsOverview()).resolves.not.toThrow();
});

test("Get document chunks", async () => {
await expect(
client.documentChunks("43eebf9c-c2b4-59e5-993a-054bf4a5c423"),
).resolves.not.toThrow();
});

test("Clean up remaining documents", async () => {
await expect(
client.delete(["document_id"], ["43eebf9c-c2b4-59e5-993a-054bf4a5c423"]),
).resolves.not.toThrow();

await expect(
client.delete(["document_id"], ["f3c6afa5-fc58-58b7-b797-f7148e5253c3"]),
).resolves.not.toThrow;
});

test("Logout", async () => {
await expect(client.logout()).resolves.not.toThrow();
});
});
Loading

0 comments on commit 2daa273

Please sign in to comment.