Skip to content

Commit

Permalink
Merge pull request #29 from getzep/feat/users
Browse files Browse the repository at this point in the history
feat/users
  • Loading branch information
danielchalef authored Aug 29, 2023
2 parents e7d485c + 128292b commit 30692ab
Show file tree
Hide file tree
Showing 18 changed files with 840 additions and 90 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.idea
# Logs
logs
*.log
Expand Down
35 changes: 24 additions & 11 deletions examples/memory/memory_example.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { v4 as uuidv4 } from "uuid";
import {
ICreateUserRequest,
ISession,
Memory,
MemorySearchPayload,
Expand All @@ -10,28 +12,39 @@ import {

import { history } from "./history";

import { v4 as uuidv4 } from "uuid";
function sleep(ms: number) {
const date = Date.now();
let currentDate = 0;
do {
currentDate = Date.now();
} while (currentDate - date < ms);
}

async function main() {
const baseURL = "http://localhost:8000"; // Replace with Zep API URL
const client = await ZepClient.init(baseURL);

// Create a user
const userId = uuidv4();
const userRequest: ICreateUserRequest = {
user_id: userId,
metadata: { foo: "bar" },
email: "[email protected]",
first_name: "A",
last_name: "User",
};
const user = await client.user.add(userRequest);
console.debug("Created user ", user.toDict());

// Example session ID
let sessionID = uuidv4();

function sleep(ms: number) {
const date = Date.now();
let currentDate = 0;
do {
currentDate = Date.now();
} while (currentDate - date < ms);
}
const sessionID = uuidv4();

// Add session
// Add session associated with the above user
try {
const sessionData: ISession = {
session_id: sessionID,
metadata: { foo: "bar" },
user_id: user.user_id,
};
const session = new Session(sessionData);

Expand Down
72 changes: 72 additions & 0 deletions examples/users/users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { v4 as uuidv4 } from "uuid";
import {
ICreateUserRequest,
IUpdateUserRequest,
ZepClient,
} from "../../src/index";

async function main() {
const BASE_URL = "http://localhost:8000"; // TODO: Replace with Zep API URL
// const API_KEY = "YOUR_API_KEY"; // TODO: Replace with your API key

const client = await ZepClient.init(BASE_URL, API_KEY);

// Create multiple users
for (let i = 0; i < 3; i++) {
const userId = uuidv4();
const userRequest: ICreateUserRequest = {
user_id: userId,
email: `user${i}@example.com`,
first_name: `John${i}`,
last_name: `Doe${i}`,
metadata: { foo: "bar" },
};

try {
const user = await client.user.add(userRequest);
console.log(`Created user ${i + 1}: ${user.user_id}`);
} catch (e) {
console.log(`Failed to create user ${i + 1}: ${e}`);
}
}

// Update the first user
const { user_id } = (await client.user.list())[0];
const userRequest: IUpdateUserRequest = {
user_id,
email: "[email protected]",
first_name: "UpdatedJohn",
last_name: "UpdatedDoe",
metadata: { foo: "updated_bar" },
};

try {
const updatedUser = await client.user.update(userRequest);
console.log(`Updated user: ${updatedUser.user_id}`);
} catch (e) {
console.log(`Failed to update user: ${e}`);
}

// Delete the second user
const userIdToDelete = (await client.user.list())[1].user_id;
try {
await client.user.delete(userIdToDelete);
console.log(`Deleted user: ${userIdToDelete}`);
} catch (e) {
console.log(`Failed to delete user: ${e}`);
}

// List all users
try {
console.log("All users:");
for await (const users of client.user.listChunked()) {
for (const user of users) {
console.log(user.user_id);
}
}
} catch (e) {
console.log(`Failed to list users: ${e}`);
}
}

main();
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"start": "node dist/index.js",
"dev": "ts-node src/main.ts",
"clean": "rm -rf dist/*",
"format": "eslint --fix 'src/**/*.{js,ts}' && prettier --write 'src/**/*.{js,ts,json}'",
"prepublishOnly": "npm run clean && npm run build && npm run test && npm run docbuild",
"prepare": "npm run build",
"test": "jest",
Expand Down
45 changes: 28 additions & 17 deletions src/document_collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
isGetIDocument,
} from "./document_models";
import { ISearchQuery, IUpdateDocumentParams, IZepClient } from "./interfaces";
import { API_BASEURL, handleRequest, isFloat } from "./utils";
import { handleRequest, isFloat } from "./utils";
import { APIError } from "./errors";

const MIN_DOCS_TO_INDEX = 10_000;
Expand Down Expand Up @@ -52,6 +52,7 @@ export default class DocumentCollection extends DocumentCollectionModel {
* @param {IDocument[]} documents - The documents to add.
* @returns {Promise<string[]>} A promise that resolves to an array of document UUIDs.
* @throws {Error} If the collection name is not provided or no documents are provided.
* @throws {APIError} If the request fails.
*/
async addDocuments(documents: IDocument[]): Promise<string[]> {
if (this.name.length === 0) {
Expand All @@ -63,8 +64,8 @@ export default class DocumentCollection extends DocumentCollectionModel {
if (documents.length > LARGE_BATCH_WARNING_LIMIT) {
console.warn(LARGE_BATCH_WARNING);
}
const url = this.getFullUrl(`/collection/${this.name}/document`);
const body = JSON.stringify(docsWithFloatArrayToDocs(documents));
const url = this.client.getFullUrl(`/collection/${this.name}/document`);
const response = await handleRequest(
fetch(url, {
method: "POST",
Expand All @@ -84,6 +85,8 @@ export default class DocumentCollection extends DocumentCollectionModel {
* @param {IUpdateDocumentParams} params - The parameters to update the document.
* @returns {Promise<void>} A promise that resolves when the document is updated.
* @throws {Error} If the collection name is not provided or the document does not have a uuid.
* @throws {APIError} If the request fails.
* @throws {NotFoundError} If the request no document is found for the given uuid.
*/
async updateDocument({
uuid,
Expand All @@ -96,7 +99,9 @@ export default class DocumentCollection extends DocumentCollectionModel {
if (!uuid) {
throw new Error("Document must have a uuid");
}
const url = this.getFullUrl(`/collection/${this.name}/document/${uuid}`);
const url = this.client.getFullUrl(
`/collection/${this.name}/document/${uuid}`
);
await handleRequest(
fetch(url, {
method: "PATCH",
Expand All @@ -118,6 +123,8 @@ export default class DocumentCollection extends DocumentCollectionModel {
* @param {string} uuid - The uuid of the document to delete.
* @returns {Promise<void>} A promise that resolves when the document is deleted.
* @throws {Error} If the collection name is not provided or the document does not have a uuid.
* @throws {APIError} If the request fails.
* @throws {NotFoundError} If the request no document is found for the given uuid.
*/
async deleteDocument(uuid: string): Promise<void> {
if (this.name.length === 0) {
Expand All @@ -126,7 +133,7 @@ export default class DocumentCollection extends DocumentCollectionModel {
if (uuid.length === 0) {
throw new Error("Document must have a uuid");
}
const url = this.getFullUrl(
const url = this.client.getFullUrl(
`/collection/${this.name}/document/uuid/${uuid}`
);
await handleRequest(
Expand All @@ -142,6 +149,8 @@ export default class DocumentCollection extends DocumentCollectionModel {
* @param {string} uuid - The uuid of the document to get.
* @returns {Promise<IDocument>} A promise that resolves to the document.
* @throws {Error} If the collection name is not provided or the document does not have a uuid.
* @throws {APIError} If the request fails.
* @throws {NotFoundError} If the request no document is found for the given uuid.
*/
async getDocument(uuid: string): Promise<IDocument> {
if (this.name.length === 0) {
Expand All @@ -150,7 +159,9 @@ export default class DocumentCollection extends DocumentCollectionModel {
if (uuid.length === 0) {
throw new Error("Document must have a uuid");
}
const url = this.getFullUrl(`/collection/${this.name}/document/${uuid}`);
const url = this.client.getFullUrl(
`/collection/${this.name}/document/${uuid}`
);
const response = await handleRequest(
fetch(url, {
headers: this.client.headers,
Expand All @@ -175,6 +186,8 @@ export default class DocumentCollection extends DocumentCollectionModel {
* @param {string[]} uuids - The uuids of the documents to get.
* @returns {Promise<IDocument[]>} A promise that resolves to an array of documents.
* @throws {Error} If any of the documents do not match the expected format.
* @throws {Error} If the collection name is not provided or no uuids are provided.
* @throws {APIError} If the request fails.
*/
async getDocuments(uuids: string[]): Promise<IDocument[]> {
if (uuids.length === 0) {
Expand All @@ -187,7 +200,9 @@ export default class DocumentCollection extends DocumentCollectionModel {
console.warn(LARGE_BATCH_WARNING);
}

const url = this.getFullUrl(`/collection/${this.name}/document/list/get`);
const url = this.client.getFullUrl(
`/collection/${this.name}/document/list/get`
);
const response = await handleRequest(
fetch(url, {
method: "POST",
Expand Down Expand Up @@ -219,6 +234,7 @@ export default class DocumentCollection extends DocumentCollectionModel {
* A promise that resolves to an array of documents and the query vector.
* @throws {Error} If the collection name is not provided or
* the search query does not have at least one of text, embedding, or metadata.
* @throws {APIError} If the request fails.
*/
async searchReturnQueryVector(
query: ISearchQuery,
Expand All @@ -242,7 +258,7 @@ export default class DocumentCollection extends DocumentCollectionModel {
: query;

const limitParam = limit ? `?limit=${limit}` : "";
const url = this.getFullUrl(
const url = this.client.getFullUrl(
`/collection/${this.name}/search${limitParam}`
);
const response = await handleRequest(
Expand Down Expand Up @@ -282,6 +298,9 @@ export default class DocumentCollection extends DocumentCollectionModel {
* @param {ISearchQuery} query - The search query.
* @param {number} [limit] - The maximum number of results to return.
* @returns {Promise<IDocument[]>} A promise that resolves to an array of documents.
* @throws {Error} If the collection name is not provided or
* the search query does not have at least one of text, embedding, or metadata.
* @throws {APIError} If the request fails.
*/
async search(query: ISearchQuery, limit?: number): Promise<IDocument[]> {
const [results] = await this.searchReturnQueryVector(query, limit);
Expand All @@ -295,6 +314,7 @@ export default class DocumentCollection extends DocumentCollectionModel {
* @returns {Promise<void>} A promise that resolves when the index is created.
* @throws {Error} If the collection name is not provided or the collection
* has less than MIN_DOCS_TO_INDEX documents and force is not true.
* @throws {APIError} If the request fails.
*/
async createIndex(force?: boolean): Promise<void> {
const forceParam = force ? `?force=${force}` : "";
Expand All @@ -311,7 +331,7 @@ export default class DocumentCollection extends DocumentCollectionModel {
`Collection must have at least ${MIN_DOCS_TO_INDEX} documents to index. Use force=true to override.`
);
}
const url = this.getFullUrl(
const url = this.client.getFullUrl(
`/collection/${this.name}/index/create${forceParam}`
);
await handleRequest(
Expand All @@ -324,13 +344,4 @@ export default class DocumentCollection extends DocumentCollectionModel {
})
);
}

/**
* Constructs the full URL for an API endpoint.
* @param {string} endpoint - The endpoint of the API.
* @returns {string} The full URL.
*/
getFullUrl(endpoint: string): string {
return `${this.client.baseURL}${API_BASEURL}${endpoint}`;
}
}
Loading

0 comments on commit 30692ab

Please sign in to comment.