From cd70e5eba2dd0b751468adacb0c4a50a97b6ec36 Mon Sep 17 00:00:00 2001 From: pranav-kural Date: Sun, 7 Jul 2024 00:41:15 -0400 Subject: [PATCH] refactored code for publishing NPM package --- .gitignore | 3 +- .swcrc | 11 +- README.md | 28 ++--- index.js | 3 + jest.config.ts | 2 +- package.json | 111 +++++++++++++++++- scripts/copy-build-files.mjs | 21 ---- src/agents/chat-agent.ts | 2 +- src/auth/api-key-store.ts | 8 ++ src/auth/firestore-api-key-store.ts | 8 +- src/auth/in-memory-api-key-store.ts | 8 +- src/cache/cache-store.ts | 7 ++ src/{ => config}/config.ts | 14 +-- src/{flows/flow.ts => endpoints/endpoints.ts} | 36 +++--- src/{ => genkit}/genkit.ts | 19 ++- src/history/chat-history-store.ts | 7 ++ src/index.ts | 17 --- src/integrations/{ => firebase}/firebase.ts | 0 src/models/{model.ts => models.ts} | 0 src/prompts/prompts.ts | 9 ++ .../embedding-models.ts | 2 +- .../{loaders => data-loaders}/data-loaders.ts | 3 +- .../data-retrievers.ts} | 99 ++++++++++++++-- .../data-splitters.ts | 0 src/rag/rag.ts | 0 src/rag/retrievers/retriever.ts | 93 --------------- src/test.ts | 3 + src/tests/api-keys.tests.ts | 23 ++-- src/tests/cache.tests.ts | 23 ++-- src/tests/chat-history.tests.ts | 29 +++-- ...{flow.tests.ts => endpoint-basic.tests.ts} | 21 ++-- ...low-rag.tests.ts => endpoint-rag.tests.ts} | 25 ++-- src/utils/utils.ts | 19 +++ test-flows.ts | 98 ---------------- tsconfig.json | 10 +- 35 files changed, 401 insertions(+), 361 deletions(-) create mode 100644 index.js delete mode 100644 scripts/copy-build-files.mjs rename src/{ => config}/config.ts (86%) rename src/{flows/flow.ts => endpoints/endpoints.ts} (88%) rename src/{ => genkit}/genkit.ts (83%) delete mode 100644 src/index.ts rename src/integrations/{ => firebase}/firebase.ts (100%) rename src/models/{model.ts => models.ts} (100%) create mode 100644 src/prompts/prompts.ts rename src/rag/{embeddings => data-embeddings}/embedding-models.ts (98%) rename src/rag/{loaders => data-loaders}/data-loaders.ts (95%) rename src/rag/{retrievers/data-retriever.ts => data-retrievers/data-retrievers.ts} (56%) rename src/rag/{splitters => data-splitters}/data-splitters.ts (100%) create mode 100644 src/rag/rag.ts delete mode 100644 src/rag/retrievers/retriever.ts create mode 100644 src/test.ts rename src/tests/{flow.tests.ts => endpoint-basic.tests.ts} (77%) rename src/tests/{flow-rag.tests.ts => endpoint-rag.tests.ts} (77%) delete mode 100644 test-flows.ts diff --git a/.gitignore b/.gitignore index 9966b1a..dcae46f 100644 --- a/.gitignore +++ b/.gitignore @@ -159,4 +159,5 @@ dist *.pem # Test files -test.txt \ No newline at end of file +test.txt +tmp-test-flows.ts \ No newline at end of file diff --git a/.swcrc b/.swcrc index 25a00ee..38e1bbb 100644 --- a/.swcrc +++ b/.swcrc @@ -1,5 +1,5 @@ { - "minify": true, + "minify": false, "jsc": { "parser": { "syntax": "typescript", @@ -9,11 +9,7 @@ }, "target": "es2017", "loose": false, - "transform": { - "legacyDecorator": false, - "decoratorVersion": "2022-03" - }, - "keepClassNames": false, + "keepClassNames": true, "baseUrl": "./src", "paths": { "/*": ["./*"] @@ -23,6 +19,7 @@ "isModule": true, "module": { "type": "commonjs", - "strictMode": true + "strictMode": true, + "noInterop": true } } diff --git a/README.md b/README.md index 11b42a1..cce2db4 100644 --- a/README.md +++ b/README.md @@ -170,10 +170,10 @@ For failed requests, the response will contain: Check `src/index.ts` to see how you can define an open-ended chat flow. ```typescript -import { defineChatFlow } from "./flows/flow"; +import { defineChatEndpoint } from "./flows/flow"; // Open-ended chat flow -defineChatFlow({ +defineChatEndpoint({ chatAgent: new ChatAgent(), endpoint: ENDPOINTS.CHAT.OPEN_ENDED, }); @@ -239,10 +239,10 @@ For failed requests, the response will contain: Check `src/index.ts` to see how you can define an open-ended chat flow that supports chat history. ```typescript -import { defineChatFlow } from "./flows/flow"; +import { defineChatEndpoint } from "./flows/flow"; // Open-ended chat flow with support for chat history -defineChatFlow({ +defineChatEndpoint({ chatAgent: new ChatAgent({ useChatHistory: true, }), @@ -318,10 +318,10 @@ For failed requests, the response will contain: Check `src/index.ts` to see how you can define an open-ended chat flow that supports chat history, response caching, and API key authentication. ```typescript -import { defineChatFlow } from "./flows/flow"; +import { defineChatEndpoint } from "./flows/flow"; // Open-ended chat flow with support for chat history, authentication, and caching -defineChatFlow({ +defineChatEndpoint({ chatAgent: new ChatAgent({ useChatHistory: true, }), @@ -386,10 +386,10 @@ For failed requests, the response will contain: Check `src/index.ts` to see how you can define a close-ended chat flow. ```typescript -import { defineChatFlow } from "./flows/flow"; +import { defineChatEndpoint } from "./flows/flow"; // Close-ended chat flow (will only answer queries related to specified topic, in this case, 'Firebase') -defineChatFlow( +defineChatEndpoint( new ChatAgent({ agentTypeConfig: { agentType: "close-ended", @@ -461,10 +461,10 @@ For failed requests, the response will contain: Check `src/index.ts` to see how you can define a close-ended chat flow that supports chat history. ```typescript -import { defineChatFlow } from "./flows/flow"; +import { defineChatEndpoint } from "./flows/flow"; // Close-ended chat flow with support for chat history -defineChatFlow({ +defineChatEndpoint({ chatAgent: new ChatAgent({ agentTypeConfig: { agentType: "close-ended", @@ -540,10 +540,10 @@ For failed requests, the response will contain: Check `src/index.ts` to see how you can define a close-ended chat flow that supports chat history, response caching, and API key authentication. ```typescript -import { defineChatFlow } from "./flows/flow"; +import { defineChatEndpoint } from "./flows/flow"; // Close-ended chat flow with support for chat history, authentication, and caching -defineChatFlow({ +defineChatEndpoint({ chatAgent: new ChatAgent({ agentTypeConfig: { agentType: "close-ended", @@ -630,13 +630,13 @@ Check `src/index.ts` to see how you can define a RAG chat flow that supports cha Below example uses a RAG agent to answer user queries related to 'Store Inventory Data'. The sample inventory data is stored in `rag/knowledge-bases/test-retail-store-kb/inventory-data.csv` in CSV format. We'll index this data, store it in a vector store, and use a vector store retriever to retrieve the data when answering user queries. ```typescript -import { defineChatFlow } from "./flows/flow"; +import { defineChatEndpoint } from "./flows/flow"; // Index inventory data and get retriever const inventoryDataRetriever = await getInventoryDataRetriever(); // Inventory Data chat flow with support for chat history, authentication, caching and RAG -defineChatFlow({ +defineChatEndpoint({ chatAgent: new ChatAgent({ agentTypeConfig: { agentType: "rag", diff --git a/index.js b/index.js new file mode 100644 index 0000000..0d55f34 --- /dev/null +++ b/index.js @@ -0,0 +1,3 @@ +import { InMemoryChatHistoryStore } from "./lib/history/chat-history-store"; + +new InMemoryChatHistoryStore(); \ No newline at end of file diff --git a/jest.config.ts b/jest.config.ts index d38524e..7f0c2da 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -155,7 +155,7 @@ const config: Config = { // testLocationInResults: false, // The glob patterns Jest uses to detect test files - testMatch: ["**/lib/tests/**/*.[jt]s?(x)"], + testMatch: ["**/src/tests/**/*.[jt]s?(x)"], // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped // testPathIgnorePatterns: [ diff --git a/package.json b/package.json index 990a3d5..ccb0112 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "scripts": { "start": "nodemon -x tsx src/index.js", "dev": "npm run build && npm run start", - "build": "swc --strip-leading-paths --delete-dir-on-start -d lib src && node scripts/copy-build-files.mjs", + "build": "swc --strip-leading-paths --delete-dir-on-start -d lib src && tsc", + "buildtypes": "tsc", "lint": "pnpm eslint .", "format": "pnpm prettier . --write", "test": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --no-watchman", @@ -20,9 +21,113 @@ "langchain", "genkit" ], - "author": "Oconva (https://github.com/oconva)", - "license": "ISC", + "author": "Pranav Kural (https://github.com/pranav-kural)", + "license": "MIT", "description": "Framework to build secure, performant and reliable chat apps and services quickly and efficiently.", + "files": [ + "lib", + "src", + "*" + ], + "types": "lib/index.d.ts", + "exports": { + "./agents": { + "types": "./lib/agents/chat-agents.d.ts", + "import": "./lib/agents/chat-agent.js", + "require": "./lib/agents/chat-agent.js", + "default": "./lib/agents/chat-agent.js" + }, + "./auth": { + "types": "./lib/auth/api-key-store.d.ts", + "import": "./lib/auth/api-key-store.js", + "require": "./lib/auth/api-key-store.js", + "default": "./lib/auth/api-key-store.js" + }, + "./cache": { + "types": "./lib/cache/cache-store.d.ts", + "import": "./lib/cache/cache-store.js", + "require": "./lib/cache/cache-store.js", + "default": "./lib/cache/cache-store.js" + }, + "./config": { + "types": "./lib/config/config.d.ts", + "import": "./lib/config/config.js", + "require": "./lib/config/config.js", + "default": "./lib/config/config.js" + }, + "./endpoints": { + "types": "./lib/endpoints/endpoints.d.ts", + "import": "./lib/endpoints/endpoints.js", + "require": "./lib/endpoints/endpoints.js", + "default": "./lib/endpoints/endpoints.js" + }, + "./genkit": { + "types": "./lib/genkit/genkit.d.ts", + "import": "./lib/genkit/genkit.js", + "require": "./lib/genkit/genkit.js", + "default": "./lib/genkit/genkit.js" + }, + "./history": { + "types": "./lib/history/chat-history-store.d.ts", + "import": "./lib/history/chat-history-store.js", + "require": "./lib/history/chat-history-store.js", + "default": "./lib/history/chat-history-store.js" + }, + "./firebase": { + "types": "./lib/integrations/firebase.d.ts", + "import": "./lib/integrations/firebase.js", + "require": "./lib/integrations/firebase.js", + "default": "./lib/integrations/firebase.js" + }, + "./models": { + "types": "./lib/models/models.d.ts", + "import": "./lib/models/models.js", + "require": "./lib/models/models.js", + "default": "./lib/models/models.js" + }, + "./prompts": { + "types": "./lib/prompts/prompts.d.ts", + "import": "./lib/prompts/prompts.js", + "require": "./lib/prompts/prompts.js", + "default": "./lib/prompts/prompts.js" + }, + "./rag": { + "types": "./lib/rag/rag.d.ts", + "import": "./lib/rag/rag.js", + "require": "./lib/rag/rag.js", + "default": "./lib/rag/rag.js" + }, + "./data-embeddings": { + "types": "./lib/rag/data-embeddings/embedding-models.d.ts", + "import": "./lib/rag/data-embeddings/embedding-models.js", + "require": "./lib/rag/data-embeddings/embedding-models.js", + "default": "./lib/rag/data-embeddings/embedding-models.js" + }, + "./data-loaders": { + "types": "./lib/rag/data-loaders/data-loaders.d.ts", + "import": "./lib/rag/data-loaders/data-loaders.js", + "require": "./lib/rag/data-loaders/data-loaders.js", + "default": "./lib/rag/data-loaders/data-loaders.js" + }, + "./data-retrievers": { + "types": "./lib/rag/data-retrievers/data-retrievers.d.ts", + "import": "./lib/rag/data-retrievers/data-retrievers.js", + "require": "./lib/rag/data-retrievers/data-retrievers.js", + "default": "./lib/rag/data-retrievers/data-retrievers.js" + }, + "./data-splitters": { + "types": "./lib/rag/data-splitters/data-splitters.d.ts", + "import": "./lib/rag/data-splitters/data-splitters.js", + "require": "./lib/rag/data-splitters/data-splitters.js", + "default": "./lib/rag/data-splitters/data-splitters.js" + }, + "./utils": { + "types": "./lib/utils/utils.d.ts", + "import": "./lib/utils/utils.js", + "require": "./lib/utils/utils.js", + "default": "./lib/utils/utils.js" + } + }, "devDependencies": { "@eslint/js": "^9.6.0", "@swc/cli": "^0.4.0", diff --git a/scripts/copy-build-files.mjs b/scripts/copy-build-files.mjs deleted file mode 100644 index 1eff08a..0000000 --- a/scripts/copy-build-files.mjs +++ /dev/null @@ -1,21 +0,0 @@ -import fs from "fs-extra"; - -// source directory -const sourceDir = "src"; -// build directory -const destinationDir = "lib"; - -// Copy the 'prompts' directory -fs.copySync(sourceDir + "/prompts", destinationDir + "/prompts"); - -// Copy the 'knowledge-bases' directory inside 'rag' directory -fs.copySync( - sourceDir + "/rag/knowledge-bases", - destinationDir + "/rag/knowledge-bases" -); - -// Copy the 'tests/test-data' directory -fs.copySync( - sourceDir + "/tests/test-data", - destinationDir + "/tests/test-data" -); diff --git a/src/agents/chat-agent.ts b/src/agents/chat-agent.ts index eaf09d6..562fd0f 100644 --- a/src/agents/chat-agent.ts +++ b/src/agents/chat-agent.ts @@ -5,7 +5,7 @@ import { ModelConfig, SupportedModelNames, SupportedModels, -} from "../models/model"; +} from "../models/models"; import { closeEndedSystemPrompt, openEndedSystemPrompt, diff --git a/src/auth/api-key-store.ts b/src/auth/api-key-store.ts index 1e46ff7..fd75b38 100644 --- a/src/auth/api-key-store.ts +++ b/src/auth/api-key-store.ts @@ -138,3 +138,11 @@ export interface APIKeyStore { */ incrementRequests: (key: APIKey) => Promise | Promise; } + +// Export supported API key stores +export { InMemoryAPIKeyStore } from "./in-memory-api-key-store"; + +export { + FirestoreAPIKeyStore, + type FirestoreAPIKeyStoreConfig, +} from "./firestore-api-key-store"; diff --git a/src/auth/firestore-api-key-store.ts b/src/auth/firestore-api-key-store.ts index d744234..867a59b 100644 --- a/src/auth/firestore-api-key-store.ts +++ b/src/auth/firestore-api-key-store.ts @@ -15,7 +15,7 @@ import { app } from "firebase-admin"; * Configurations for the FirestoreAPIKeyStore. * @property collectionName - The name of the Firestore collection to store API keys. */ -type FirestoreAPIKeyStoreConfig = { +export type FirestoreAPIKeyStoreConfig = { firebaseApp: app.App; collectionName: string; }; @@ -70,18 +70,18 @@ export class FirestoreAPIKeyStore implements APIKeyStore { async updateKey({ key, status, - flows, + endpoints, requests, }: { key: string; status?: APIKeyStatus; - flows?: string[] | "all"; + endpoints?: string[] | "all"; requests?: number; }): Promise { // values to update const valuesToUpdate = { ...(status && { status }), - ...(flows && { endpoints: flows }), + ...(endpoints && { endpoints: endpoints }), ...(requests && { requests }), }; // Update the API key in the Firestore collection diff --git a/src/auth/in-memory-api-key-store.ts b/src/auth/in-memory-api-key-store.ts index 66aa0ae..ba6d345 100644 --- a/src/auth/in-memory-api-key-store.ts +++ b/src/auth/in-memory-api-key-store.ts @@ -54,12 +54,12 @@ export class InMemoryAPIKeyStore implements APIKeyStore { async updateKey({ key, status, - flows, + endpoints, requests, }: { key: string; status?: APIKeyStatus; - flows?: string[] | "all"; + endpoints?: string[] | "all"; requests?: number; }): Promise { const apiKey = this.keys.get(key); @@ -68,8 +68,8 @@ export class InMemoryAPIKeyStore implements APIKeyStore { if (status) { apiKey.status = status; } - if (flows) { - apiKey.endpoints = flows; + if (endpoints) { + apiKey.endpoints = endpoints; } if (requests) { apiKey.requests = requests; diff --git a/src/cache/cache-store.ts b/src/cache/cache-store.ts index 0eefad3..728b649 100644 --- a/src/cache/cache-store.ts +++ b/src/cache/cache-store.ts @@ -110,3 +110,10 @@ export interface CacheStore { */ incrementCacheHits(hash: QueryHash): Promise | Promise; } + +// export supported cache stores +export { InMemoryCacheStore } from "./in-memory-cache-store"; +export { + FirestoreCacheStore, + type FirestoreCacheStoreConfig, +} from "./firestore-cache-store"; diff --git a/src/config.ts b/src/config/config.ts similarity index 86% rename from src/config.ts rename to src/config/config.ts index ca32a7e..8f9b976 100644 --- a/src/config.ts +++ b/src/config/config.ts @@ -1,11 +1,11 @@ import { ConfigOptions } from "@genkit-ai/core"; import { CorsOptions } from "cors"; -import { SupportedModels } from "./models/model"; +import { SupportedModels } from "../models/models"; /** - * Type for the parameters of the startFlowsServer function + * Type for the parameters to start the server */ -export type StartFlowsServerParamsType = { +export type StartServerParamsType = { port?: number; cors?: CorsOptions; pathPrefix?: string; @@ -36,7 +36,7 @@ export const cacheStoreConfig = { * * @property {string} primaryLLM - The primary LLM to use (for example, for chat completion tasks) * @property {ConfigOptions} genkitConfig - The configuration options for the Genkit library - * @property {StartFlowsServerParamsType} startFlowsServerParams - The parameters for the startFlowsServer function + * @property {StartServerParamsType} startServerParams - Parameters for starting the server * @property {boolean} enableAPIKeyAuth - Enable API key authentication * @property {CacheStoreConfig} cacheStoreConfig - Cache store configuration * @@ -47,8 +47,8 @@ export interface GlobalConfigInterface { primaryLLM?: SupportedModels; /** Genkit configuration options */ genkitConfig?: ConfigOptions; - /** Parameters for the startFlowsServer function */ - startFlowsServerParams?: StartFlowsServerParamsType; + /** Parameters for server */ + startServerParams?: StartServerParamsType; /** Enable API key authentication */ enableAPIKeyAuth?: boolean; /** Cache store configuration */ @@ -65,7 +65,7 @@ export const GLOBAL_CONFIG: GlobalConfigInterface = { logLevel: "warn", enableTracingAndMetrics: true, }, - startFlowsServerParams: { + startServerParams: { port: 8884, }, enableAPIKeyAuth: true, diff --git a/src/flows/flow.ts b/src/endpoints/endpoints.ts similarity index 88% rename from src/flows/flow.ts rename to src/endpoints/endpoints.ts index 8522db3..00653af 100644 --- a/src/flows/flow.ts +++ b/src/endpoints/endpoints.ts @@ -4,7 +4,7 @@ import { GenerateResponseHistoryProps, GenerateResponseProps, } from "../agents/chat-agent"; -import { defineFlow } from "@genkit-ai/flow"; +import { defineFlow, runFlow } from "@genkit-ai/flow"; import { APIKeyStore } from "../auth/api-key-store"; import { CacheStore } from "../cache/cache-store"; import { generateHash, getChatHistoryAsString } from "../utils/utils"; @@ -13,8 +13,8 @@ import { apiKeyAuthPolicy } from "../auth/api-key-auth-policy"; import { RetrieverConfig, TextDataRetriever, -} from "../rag/retrievers/retriever"; -import { getDataRetriever } from "../rag/retrievers/data-retriever"; +} from "../rag/data-retrievers/data-retrievers"; +import { getDataRetriever } from "../rag/data-retrievers/data-retrievers"; import { ChatHistoryStore } from "../history/chat-history-store"; type ChatHistoryParams = @@ -71,7 +71,7 @@ type ChatAgentTypeParams = chatAgent: ChatAgent; }; -export type DefineChatFlowConfig = { +export type DefineChatEndpointConfig = { endpoint: string; enableChatHistory?: boolean; } & ChatAgentTypeParams & @@ -81,21 +81,21 @@ export type DefineChatFlowConfig = { RAGParams; /** - * Method to define a chat flow using the provided chat agent and endpoint, with support for chat history. - * @param chatAgent Chat Agent instance to use for this flow. - * @param endpoint Flow endpoint value that will be used to send requests to this flow. - * @param enableChatHistory Enable chat history for this flow. If chat ID is provided, chat history will be fetched and used to generate response. If no chat ID is provided, a new chat ID will be generated to store chat history, and will be returned in the response. - * @param chatHistoryStore Chat History Store instance to use for this flow. - * @param enableAuth Enable authentication for this flow. Must provide an API Key Store instance if set to true. - * @param apiKeyStore API Key Store instance to use for this flow. - * @param enableCache Enable caching for this flow. Must provide a Cache Store instance if set to true. - * @param cacheStore Cache Store instance to use for this flow. - * @param enableRAG Enable RAG (Retrieval Augmented Generation) functionality for this flow. Must provide a retriever method if set to true. + * Method to define a chat endpoint using the provided chat agent and endpoint, with support for chat history. + * @param chatAgent Chat Agent instance to use for this endpoint. + * @param endpoint Server endpoint to which queries should be sent to run this chat flow. + * @param enableChatHistory Enable chat history for this endpoint. If chat ID is provided, chat history will be fetched and used to generate response. If no chat ID is provided, a new chat ID will be generated to store chat history, and will be returned in the response. + * @param chatHistoryStore Chat History Store instance to use for this endpoint. + * @param enableAuth Enable authentication for this endpoint. Must provide an API Key Store instance if set to true. + * @param apiKeyStore API Key Store instance to use for this endpoint. + * @param enableCache Enable caching for this endpoint. Must provide a Cache Store instance if set to true. + * @param cacheStore Cache Store instance to use for this endpoint. + * @param enableRAG Enable RAG (Retrieval Augmented Generation) functionality for this endpoint. Must provide a retriever method if set to true. * @param retriever Method to retrieve documents for RAG. * @param retrieverConfig Configuration for the RAG retriever. * @returns Object containing the response generated by the chat agent and the chat ID (if available), or an error message. */ -export const defineChatFlow = (config: DefineChatFlowConfig) => +export const defineChatEndpoint = (config: DefineChatEndpointConfig) => defineFlow( { name: config.endpoint, @@ -335,3 +335,9 @@ export const defineChatFlow = (config: DefineChatFlowConfig) => } } ); + +/** + * Get method to run the chat endpoint flow. + * @returns Method to run the chat endpoint flow. + */ +export const getChatEndpointRunner = () => runFlow; diff --git a/src/genkit.ts b/src/genkit/genkit.ts similarity index 83% rename from src/genkit.ts rename to src/genkit/genkit.ts index de21747..8242b76 100644 --- a/src/genkit.ts +++ b/src/genkit/genkit.ts @@ -1,4 +1,3 @@ -import dotenv from "dotenv"; import { ConfigOptions, PluginProvider, @@ -9,12 +8,10 @@ import { googleAI } from "@genkit-ai/googleai"; import { dotprompt } from "@genkit-ai/dotprompt"; import { firebase } from "@genkit-ai/firebase"; import { TelemetryConfig } from "@genkit-ai/google-cloud"; -import { GLOBAL_CONFIG, StartFlowsServerParamsType } from "./config"; +import { GLOBAL_CONFIG, StartServerParamsType } from "../config/config"; import { langchain } from "genkitx-langchain"; import { openAI } from "genkitx-openai"; - -// Load environment variables -dotenv.config(); +import { getEnvironmentVariable } from "../utils/utils"; /** * Configuration for Firebase plugin. @@ -44,12 +41,12 @@ export type SetupGenkitConfig = { */ const requiredPlugins: PluginProvider[] = [ googleAI({ - apiKey: process.env.GOOGLE_GENAI_API_KEY, + apiKey: getEnvironmentVariable("GOOGLE_GENAI_API_KEY"), }), dotprompt(), langchain({}), openAI({ - apiKey: process.env.OPENAI_API_KEY, + apiKey: getEnvironmentVariable("OPENAI_API_KEY"), }), ]; @@ -96,10 +93,10 @@ export const setupGenkit = (config: SetupGenkitConfig = {}) => { }; /** - * Method to run the flows server. - * @param params parameters for running the flows server + * Method to run the server that will serve the chat endpoints. + * @param params parameters for running the server */ -export const runFlowsServer = (params: StartFlowsServerParamsType = {}) => { +export const runServer = (params: StartServerParamsType = {}) => { // Start the flows server with global configurations - startFlowsServer(params ?? GLOBAL_CONFIG.startFlowsServerParams); + startFlowsServer(params ?? GLOBAL_CONFIG.startServerParams); }; diff --git a/src/history/chat-history-store.ts b/src/history/chat-history-store.ts index c6830fc..108a325 100644 --- a/src/history/chat-history-store.ts +++ b/src/history/chat-history-store.ts @@ -72,3 +72,10 @@ export interface ChatHistoryStore { */ deleteChatHistory: (chatId: string) => Promise | Promise; } + +// export supported chat history stores +export { InMemoryChatHistoryStore } from "./in-memory-chat-history-store"; +export { + FirestoreChatHistoryStore, + type FirestoreChatHistoryStoreConfig, +} from "./firestore-chat-history-store"; diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 5107e06..0000000 --- a/src/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { runFlowsServer, setupGenkit } from "./genkit"; -import { defineTestFlows } from "/test-flows"; - -// Setup Genkit -setupGenkit(); - -setupGenkit({ - firebaseConfig: { - projectId: "my-project-id", - }, -}); - -// Define flows -defineTestFlows(); - -// Run server -runFlowsServer(); diff --git a/src/integrations/firebase.ts b/src/integrations/firebase/firebase.ts similarity index 100% rename from src/integrations/firebase.ts rename to src/integrations/firebase/firebase.ts diff --git a/src/models/model.ts b/src/models/models.ts similarity index 100% rename from src/models/model.ts rename to src/models/models.ts diff --git a/src/prompts/prompts.ts b/src/prompts/prompts.ts new file mode 100644 index 0000000..e8d461f --- /dev/null +++ b/src/prompts/prompts.ts @@ -0,0 +1,9 @@ +// export prompts + +export { + openEndedSystemPrompt, + closeEndedSystemPrompt, + ragSystemPrompt, +} from "./system-prompts"; + +export { secureChatPrompt, secureRagChatPrompt } from "./chat-prompts"; diff --git a/src/rag/embeddings/embedding-models.ts b/src/rag/data-embeddings/embedding-models.ts similarity index 98% rename from src/rag/embeddings/embedding-models.ts rename to src/rag/data-embeddings/embedding-models.ts index 01b0041..2ff2e31 100644 --- a/src/rag/embeddings/embedding-models.ts +++ b/src/rag/data-embeddings/embedding-models.ts @@ -1,6 +1,6 @@ import { EmbeddingsInterface } from "@langchain/core/embeddings"; import { GoogleGenerativeAIEmbeddings } from "@langchain/google-genai"; -import { TaskType } from "../retrievers/retriever"; +import { TaskType } from "../data-retrievers/data-retrievers"; import { AzureOpenAIInput, OpenAIEmbeddings, diff --git a/src/rag/loaders/data-loaders.ts b/src/rag/data-loaders/data-loaders.ts similarity index 95% rename from src/rag/loaders/data-loaders.ts rename to src/rag/data-loaders/data-loaders.ts index fe83fd5..cdbed68 100644 --- a/src/rag/loaders/data-loaders.ts +++ b/src/rag/data-loaders/data-loaders.ts @@ -2,6 +2,7 @@ import { TextLoader } from "langchain/document_loaders/fs/text"; import { JSONLoader } from "langchain/document_loaders/fs/json"; import { CSVLoader } from "@langchain/community/document_loaders/fs/csv"; import { PDFLoader } from "@langchain/community/document_loaders/fs/pdf"; +import { Document } from "langchain/document"; /** * Supported data loader types. @@ -54,7 +55,7 @@ export const getDocs = async ( jsonLoaderKeysToInclude?: JSONLoaderKeysToInclude, csvLoaderOptions?: CSVLoaderOptions, pdfLoaderOptions?: PDFLoaderOptions -) => { +): Promise>[]> => { // store loader let loader; // infer loader to use based on dataLoaderType diff --git a/src/rag/retrievers/data-retriever.ts b/src/rag/data-retrievers/data-retrievers.ts similarity index 56% rename from src/rag/retrievers/data-retriever.ts rename to src/rag/data-retrievers/data-retrievers.ts index 8700c2e..341ec85 100644 --- a/src/rag/retrievers/data-retriever.ts +++ b/src/rag/data-retrievers/data-retrievers.ts @@ -4,20 +4,101 @@ import { GoogleGenerativeAIEmbeddings } from "@langchain/google-genai"; import { formatDocumentsAsString } from "langchain/util/document"; import { Runnable, RunnableConfig } from "@langchain/core/runnables"; import { EmbeddingsInterface } from "@langchain/core/embeddings"; -import { VectorStore } from "@langchain/core/vectorstores"; -import { SupportedDataLoaderTypes, getDocs } from "../loaders/data-loaders"; +import { + VectorStore, + VectorStoreRetrieverInput, +} from "@langchain/core/vectorstores"; +import { + CSVLoaderOptions, + JSONLoaderKeysToInclude, + PDFLoaderOptions, + SupportedDataLoaderTypes, + getDocs, +} from "../data-loaders/data-loaders"; import { ChunkingConfig, DataSplitterConfig, SupportedDataSplitterTypes, runDataSplitter, -} from "../splitters/data-splitters"; -import { GOOGLE_GENAI_EMBEDDING_MODELS } from "../embeddings/embedding-models"; -import { RetrieverConfig, TaskType } from "./retriever"; -import dotenv from "dotenv"; +} from "../data-splitters/data-splitters"; +import { GOOGLE_GENAI_EMBEDDING_MODELS } from "../data-embeddings/embedding-models"; +import { getEnvironmentVariable } from "../../utils/utils"; + +/** + * Type denoting a retriever that retrieves text data. + */ +export type TextDataRetriever = Runnable; + +/** + * Retrieval options for the retriever. + */ +export type RetrievalOptions = + | number + // eslint-disable-next-line @typescript-eslint/no-explicit-any + | Partial> + | undefined; + +/** + * Represents the configuration for the retriever when generating embeddings. + * @property {SupportedDataLoaderTypes} dataType - The type of data loader to use. + * @property {string} filePath - The path to the file containing the data. + * @property {JSONLoaderKeysToInclude} [jsonLoaderKeysToInclude] - The keys to include when loading JSON data. + * @property {CSVLoaderOptions} [csvLoaderOptions] - The options for loading CSV data. + * @property {PDFLoaderOptions} [pdfLoaderOptions] - The options for loading PDF data. + * @property {SupportedDataSplitterTypes} [dataSplitterType] - The type of data splitter to use. + * @property {ChunkingConfig} [chunkingConfig] - The configuration for chunking the data. + * @property {DataSplitterConfig} [splitterConfig] - The configuration for the data splitter. + * @property {RetrievalOptions} [retrievalOptions] - The retrieval options for the retriever. + * @property {VectorStore} [vectorStore] - The vector store to use. + * @property {EmbeddingsInterface} [embeddingModel] - The embedding model to use. + * @property {boolean} generateEmbeddings - Whether to generate embeddings. + */ +export type RetrieverConfigGeneratingEmbeddings = { + dataType: SupportedDataLoaderTypes; + filePath: string; + jsonLoaderKeysToInclude?: JSONLoaderKeysToInclude; + csvLoaderOptions?: CSVLoaderOptions; + pdfLoaderOptions?: PDFLoaderOptions; + dataSplitterType?: SupportedDataSplitterTypes; + chunkingConfig?: ChunkingConfig; + splitterConfig?: DataSplitterConfig; + retrievalOptions?: RetrievalOptions; + vectorStore?: VectorStore; + embeddingModel?: EmbeddingsInterface; + generateEmbeddings: true; +}; -// Load environment variables needed for API key for the embedding model -dotenv.config(); +/** + * Represents the configuration for the retriever when not generating embeddings. + * @property {VectorStore} vectorStore - The vector store to use. + * @property {RetrievalOptions} [retrievalOptions] - The retrieval options for the retriever. + * @property {boolean} generateEmbeddings - Whether to generate embeddings. + */ +export type RetrieverConfigNotGeneratingEmbeddings = { + generateEmbeddings: false; + vectorStore: VectorStore; + retrievalOptions?: RetrievalOptions; +}; + +/** + * Represents the configuration for the retriever. + */ +export type RetrieverConfig = + | RetrieverConfigGeneratingEmbeddings + | RetrieverConfigNotGeneratingEmbeddings; + +/** + * From @google/generative-ai + * Task type for embedding content. + */ +export enum TaskType { + TASK_TYPE_UNSPECIFIED = "TASK_TYPE_UNSPECIFIED", + RETRIEVAL_QUERY = "RETRIEVAL_QUERY", + RETRIEVAL_DOCUMENT = "RETRIEVAL_DOCUMENT", + SEMANTIC_SIMILARITY = "SEMANTIC_SIMILARITY", + CLASSIFICATION = "CLASSIFICATION", + CLUSTERING = "CLUSTERING", +} /** * Get a default data splitter based on the data type. @@ -112,7 +193,7 @@ export const getDataRetriever = async ( const embeddings: EmbeddingsInterface = config.embeddingModel ?? new GoogleGenerativeAIEmbeddings({ - apiKey: process.env.GOOGLE_GENAI_API_KEY, + apiKey: getEnvironmentVariable("GOOGLE_GENAI_API_KEY"), model: GOOGLE_GENAI_EMBEDDING_MODELS["text-embedding-004"].name, taskType: TaskType.RETRIEVAL_DOCUMENT, }); diff --git a/src/rag/splitters/data-splitters.ts b/src/rag/data-splitters/data-splitters.ts similarity index 100% rename from src/rag/splitters/data-splitters.ts rename to src/rag/data-splitters/data-splitters.ts diff --git a/src/rag/rag.ts b/src/rag/rag.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/rag/retrievers/retriever.ts b/src/rag/retrievers/retriever.ts deleted file mode 100644 index eeee6d9..0000000 --- a/src/rag/retrievers/retriever.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Runnable, RunnableConfig } from "@langchain/core/runnables"; -import { EmbeddingsInterface } from "@langchain/core/embeddings"; -import { - VectorStore, - VectorStoreRetrieverInput, -} from "@langchain/core/vectorstores"; -import { - CSVLoaderOptions, - JSONLoaderKeysToInclude, - PDFLoaderOptions, - SupportedDataLoaderTypes, -} from "../loaders/data-loaders"; -import { - ChunkingConfig, - DataSplitterConfig, - SupportedDataSplitterTypes, -} from "../splitters/data-splitters"; - -/** - * Type denoting a retriever that retrieves text data. - */ -export type TextDataRetriever = Runnable; - -/** - * Retrieval options for the retriever. - */ -export type RetrievalOptions = - | number - // eslint-disable-next-line @typescript-eslint/no-explicit-any - | Partial> - | undefined; - -/** - * Represents the configuration for the retriever when generating embeddings. - * @property {SupportedDataLoaderTypes} dataType - The type of data loader to use. - * @property {string} filePath - The path to the file containing the data. - * @property {JSONLoaderKeysToInclude} [jsonLoaderKeysToInclude] - The keys to include when loading JSON data. - * @property {CSVLoaderOptions} [csvLoaderOptions] - The options for loading CSV data. - * @property {PDFLoaderOptions} [pdfLoaderOptions] - The options for loading PDF data. - * @property {SupportedDataSplitterTypes} [dataSplitterType] - The type of data splitter to use. - * @property {ChunkingConfig} [chunkingConfig] - The configuration for chunking the data. - * @property {DataSplitterConfig} [splitterConfig] - The configuration for the data splitter. - * @property {RetrievalOptions} [retrievalOptions] - The retrieval options for the retriever. - * @property {VectorStore} [vectorStore] - The vector store to use. - * @property {EmbeddingsInterface} [embeddingModel] - The embedding model to use. - * @property {boolean} generateEmbeddings - Whether to generate embeddings. - */ -export type RetrieverConfigGeneratingEmbeddings = { - dataType: SupportedDataLoaderTypes; - filePath: string; - jsonLoaderKeysToInclude?: JSONLoaderKeysToInclude; - csvLoaderOptions?: CSVLoaderOptions; - pdfLoaderOptions?: PDFLoaderOptions; - dataSplitterType?: SupportedDataSplitterTypes; - chunkingConfig?: ChunkingConfig; - splitterConfig?: DataSplitterConfig; - retrievalOptions?: RetrievalOptions; - vectorStore?: VectorStore; - embeddingModel?: EmbeddingsInterface; - generateEmbeddings: true; -}; - -/** - * Represents the configuration for the retriever when not generating embeddings. - * @property {VectorStore} vectorStore - The vector store to use. - * @property {RetrievalOptions} [retrievalOptions] - The retrieval options for the retriever. - * @property {boolean} generateEmbeddings - Whether to generate embeddings. - */ -export type RetrieverConfigNotGeneratingEmbeddings = { - generateEmbeddings: false; - vectorStore: VectorStore; - retrievalOptions?: RetrievalOptions; -}; - -/** - * Represents the configuration for the retriever. - */ -export type RetrieverConfig = - | RetrieverConfigGeneratingEmbeddings - | RetrieverConfigNotGeneratingEmbeddings; - -/** - * From @google/generative-ai - * Task type for embedding content. - */ -export enum TaskType { - TASK_TYPE_UNSPECIFIED = "TASK_TYPE_UNSPECIFIED", - RETRIEVAL_QUERY = "RETRIEVAL_QUERY", - RETRIEVAL_DOCUMENT = "RETRIEVAL_DOCUMENT", - SEMANTIC_SIMILARITY = "SEMANTIC_SIMILARITY", - CLASSIFICATION = "CLASSIFICATION", - CLUSTERING = "CLUSTERING", -} diff --git a/src/test.ts b/src/test.ts new file mode 100644 index 0000000..0fa9bfe --- /dev/null +++ b/src/test.ts @@ -0,0 +1,3 @@ +import { getEnvironmentVariable } from "./utils/utils"; + +console.log(getEnvironmentVariable("GOOGLE_GENAI_API_KEY")); diff --git a/src/tests/api-keys.tests.ts b/src/tests/api-keys.tests.ts index 610ff1f..f8785bd 100644 --- a/src/tests/api-keys.tests.ts +++ b/src/tests/api-keys.tests.ts @@ -1,15 +1,20 @@ -import { runFlow } from "@genkit-ai/flow"; -import { defineChatFlow } from "../flows/flow"; +import { + defineChatEndpoint, + getChatEndpointRunner, +} from "../endpoints/endpoints"; import { InMemoryAPIKeyStore } from "../auth/in-memory-api-key-store"; import { generateAlphaNumericString } from "../utils/utils"; -import { setupGenkit } from "../genkit"; +import { setupGenkit } from "../genkit/genkit"; /** - * Test suite for Chat Flow Core Functionality. + * Test suite for Chat Endpoint - API Keys. * * Some tests include the use of LLM model, defining a chat agent, defining API key store, defining chat history store, and defining cache store. */ -describe("Test - Flow API Keys Tests", () => { +describe("Test - Endpoint API Keys Tests", () => { + // Initialize endpoint runner + const runEndpoint = getChatEndpointRunner(); + beforeAll(() => { setupGenkit(); }); @@ -38,7 +43,7 @@ describe("Test - Flow API Keys Tests", () => { endpoints: "all", // allow access to all endpoints }); // define chat flow - const flow = defineChatFlow({ + const flow = defineChatEndpoint({ endpoint: "test-chat-open-api-key-required", enableAuth: true, apiKeyStore, @@ -48,7 +53,7 @@ describe("Test - Flow API Keys Tests", () => { try { // send test query without API key - response = await runFlow(flow, { + response = await runEndpoint(flow, { query: "How can you help? In one sentence.", uid: "test-user", }); @@ -82,14 +87,14 @@ describe("Test - Flow API Keys Tests", () => { endpoints: "all", // allow access to all endpoints }); // define chat flow - const flow = defineChatFlow({ + const flow = defineChatEndpoint({ endpoint: "test-chat-open-api-key-auth-working", enableAuth: true, apiKeyStore, }); try { // send test query with API key - const response = await runFlow( + const response = await runEndpoint( flow, { query: "How can you help? In one sentence.", diff --git a/src/tests/cache.tests.ts b/src/tests/cache.tests.ts index 32c4944..c0c78ee 100644 --- a/src/tests/cache.tests.ts +++ b/src/tests/cache.tests.ts @@ -1,15 +1,20 @@ -import { runFlow } from "@genkit-ai/flow"; -import { defineChatFlow } from "../flows/flow"; +import { + defineChatEndpoint, + getChatEndpointRunner, +} from "../endpoints/endpoints"; import { InMemoryCacheStore } from "../cache/in-memory-cache-store"; -import { setupGenkit } from "../genkit"; +import { setupGenkit } from "../genkit/genkit"; import { CacheCollection } from "../cache/cache-store"; /** - * Test suite for Chat Flow Core Functionality. + * Test suite for Chat Endpoint - Cache. * * Some tests include the use of LLM model, defining a chat agent, defining API key store, defining chat history store, and defining cache store. */ -describe("Test - Flow Cache Tests", () => { +describe("Test - Endpoint Cache Tests", () => { + // Initialize endpoint runner + const runEndpoint = getChatEndpointRunner(); + beforeAll(() => { setupGenkit(); }); @@ -32,7 +37,7 @@ describe("Test - Flow Cache Tests", () => { cacheQueryAfterThreshold: 2, // cache response after same query is received twice }); // define chat flow - const flow = defineChatFlow({ + const flow = defineChatEndpoint({ endpoint: "test-chat-open-cache", enableCache: true, cacheStore: cacheStore, @@ -40,17 +45,17 @@ describe("Test - Flow Cache Tests", () => { try { // send query the first time - await runFlow(flow, { + await runEndpoint(flow, { query: "Answer in one sentence: what is Firebase?", }); // send query the second time - await runFlow(flow, { + await runEndpoint(flow, { query: "Answer in one sentence: what is Firebase?", }); // send query the third time - const response = await runFlow(flow, { + const response = await runEndpoint(flow, { query: "Answer in one sentence: what is Firebase?", }); diff --git a/src/tests/chat-history.tests.ts b/src/tests/chat-history.tests.ts index c8f6451..27ab589 100644 --- a/src/tests/chat-history.tests.ts +++ b/src/tests/chat-history.tests.ts @@ -1,14 +1,19 @@ -import { runFlow } from "@genkit-ai/flow"; -import { defineChatFlow } from "../flows/flow"; -import { setupGenkit } from "../genkit"; +import { + defineChatEndpoint, + getChatEndpointRunner, +} from "../endpoints/endpoints"; +import { setupGenkit } from "../genkit/genkit"; import { InMemoryChatHistoryStore } from "../history/in-memory-chat-history-store"; /** - * Test suite for Chat Flow Core Functionality. + * Test suite for Chat Endpoint - Chat History. * * Some tests include the use of LLM model, defining a chat agent, defining API key store, defining chat history store, and defining cache store. */ -describe("Test - Flow Chat History Tests", () => { +describe("Test - Endpoint Chat History Tests", () => { + // Initialize endpoint runner + const runEndpoint = getChatEndpointRunner(); + beforeAll(() => { setupGenkit(); }); @@ -28,11 +33,11 @@ describe("Test - Flow Chat History Tests", () => { test( "Sending invalid Chat ID when chat history is disabled", async () => { - const flow = defineChatFlow({ + const flow = defineChatEndpoint({ endpoint: "test-chat-open-chat-id-history-disabled", enableChatHistory: false, }); - const response = await runFlow(flow, { + const response = await runEndpoint(flow, { query: "How can you help? In one sentence.", chatId: "test-chat-id", }); @@ -54,12 +59,12 @@ describe("Test - Flow Chat History Tests", () => { test( "Sending invalid Chat ID when chat history is enabled", async () => { - const flow = defineChatFlow({ + const flow = defineChatEndpoint({ endpoint: "test-chat-open-chat-id-history-enabled", enableChatHistory: true, chatHistoryStore: new InMemoryChatHistoryStore(), }); - const response = await runFlow(flow, { + const response = await runEndpoint(flow, { query: "How can you help? In one sentence.", chatId: "test-chat-id", }); @@ -73,12 +78,12 @@ describe("Test - Flow Chat History Tests", () => { test( "Test chat history works", async () => { - const flow = defineChatFlow({ + const flow = defineChatEndpoint({ endpoint: "test-chat-open-chat-history", enableChatHistory: true, chatHistoryStore: new InMemoryChatHistoryStore(), }); - const response = await runFlow(flow, { + const response = await runEndpoint(flow, { query: "What is Firebase? In one sentence.", }); expect(response).toBeDefined(); @@ -90,7 +95,7 @@ describe("Test - Flow Chat History Tests", () => { // response should not be empty expect(response.response.length).toBeGreaterThan(0); - const secondResponse = await runFlow(flow, { + const secondResponse = await runEndpoint(flow, { query: "Can I use it for authentication? In one sentence.", chatId, }); diff --git a/src/tests/flow.tests.ts b/src/tests/endpoint-basic.tests.ts similarity index 77% rename from src/tests/flow.tests.ts rename to src/tests/endpoint-basic.tests.ts index 72f18eb..77044f3 100644 --- a/src/tests/flow.tests.ts +++ b/src/tests/endpoint-basic.tests.ts @@ -1,13 +1,18 @@ -import { runFlow } from "@genkit-ai/flow"; -import { defineChatFlow } from "../flows/flow"; -import { setupGenkit } from "../genkit"; +import { + defineChatEndpoint, + getChatEndpointRunner, +} from "../endpoints/endpoints"; +import { setupGenkit } from "../genkit/genkit"; /** - * Test suite for Chat Flow Core Functionality. + * Test suite for Chat Endpoint Basic Functionality. * * Some tests include the use of LLM model, defining a chat agent, defining API key store, defining chat history store, and defining cache store. */ -describe("Test - Chat Flow Core Funtionality Tests", () => { +describe("Test - Chat Endpoint Core Funtionality Tests", () => { + // Initialize endpoint runner + const runEndpoint = getChatEndpointRunner(); + beforeAll(() => { setupGenkit(); }); @@ -31,7 +36,7 @@ describe("Test - Chat Flow Core Funtionality Tests", () => { if (Tests.define_chat_flow) test("Define chat flow", () => { - const flow = defineChatFlow({ endpoint: "test-chat" }); + const flow = defineChatEndpoint({ endpoint: "test-chat" }); expect(flow).toBeDefined(); }); @@ -39,10 +44,10 @@ describe("Test - Chat Flow Core Funtionality Tests", () => { test( "Confirm response generation", async () => { - const flow = defineChatFlow({ + const flow = defineChatEndpoint({ endpoint: "test-chat-open-response", }); - const response = await runFlow(flow, { + const response = await runEndpoint(flow, { query: "How can you help? In one sentence.", }); expect(response).toBeDefined(); diff --git a/src/tests/flow-rag.tests.ts b/src/tests/endpoint-rag.tests.ts similarity index 77% rename from src/tests/flow-rag.tests.ts rename to src/tests/endpoint-rag.tests.ts index deb5a1e..62d72cd 100644 --- a/src/tests/flow-rag.tests.ts +++ b/src/tests/endpoint-rag.tests.ts @@ -1,14 +1,19 @@ -import { runFlow } from "@genkit-ai/flow"; -import { defineChatFlow } from "../flows/flow"; -import { setupGenkit } from "../genkit"; -import { getDataRetriever } from "../rag/retrievers/data-retriever"; +import { + defineChatEndpoint, + getChatEndpointRunner, +} from "../endpoints/endpoints"; +import { setupGenkit } from "../genkit/genkit"; +import { getDataRetriever } from "../rag/data-retrievers/data-retrievers"; /** - * Test suite for Chat Flow Core Functionality. + * Test suite for Chat Endpoint RAG Functionality. * * Some tests include the use of LLM model, defining a chat agent, defining API key store, defining chat history store, and defining cache store. */ -describe("Test - Flow RAG Tests", () => { +describe("Test - Endpoint RAG Tests", () => { + // Initialize endpoint runner + const runEndpoint = getChatEndpointRunner(); + beforeAll(() => { setupGenkit(); }); @@ -24,21 +29,21 @@ describe("Test - Flow RAG Tests", () => { if (Tests.test_rag_works) test( - "Test RAG works (w/ Data Retriever, Embedding Generation, RAG-enabled Flow)", + "Test RAG works (w/ Data Retriever, Embedding Generation, RAG-enabled Endpoint)", async () => { // define chat flow - const flow = defineChatFlow({ + const flow = defineChatEndpoint({ endpoint: "test-chat-open-rag", enableRAG: true, retriever: await getDataRetriever({ dataType: "csv", - filePath: "lib/tests/test-data/inventory-data.csv", + filePath: "src/tests/test-data/inventory-data.csv", generateEmbeddings: true, }), }); try { // send test query - const response = await runFlow(flow, { + const response = await runEndpoint(flow, { query: "What is the price of Seagate ST1000DX002?", }); diff --git a/src/utils/utils.ts b/src/utils/utils.ts index bab667c..fe9cb05 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,5 +1,7 @@ import { MessageData } from "@genkit-ai/ai/model"; import * as crypto from "crypto"; +import { config as dotenvConfig } from "dotenv"; +import { resolve as pathResolve } from "path"; export function generateAlphaNumericString( bytes: number = 32, @@ -36,3 +38,20 @@ export function getChatHistoryAsString(messages: MessageData[]): string { chatHistory += ""; return chatHistory; } + +export function getEnvironmentVariable(name: string, envFileName?: string) { + // Certain Deno setups will throw an error if you try to access environment variables + // https://github.com/langchain-ai/langchainjs/issues/1412 + console.log("getEnvironmentVariable: ", pathResolve(envFileName ?? ".env")); + try { + dotenvConfig({ + path: pathResolve(envFileName ?? ".env"), + }); + return typeof process !== "undefined" + ? // eslint-disable-next-line no-process-env + process.env?.[name] + : undefined; + } catch (e) { + throw new Error(`Error getting environment variable. ${e}`); + } +} diff --git a/test-flows.ts b/test-flows.ts deleted file mode 100644 index 566e7c9..0000000 --- a/test-flows.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { ChatAgent } from "./src/agents/chat-agent"; -import { InMemoryAPIKeyStore } from "./src/auth/in-memory-api-key-store"; -import { InMemoryCacheStore } from "./src/cache/in-memory-cache-store"; -import { ENDPOINTS } from "./src/config"; -import { defineChatFlow } from "./src/flows/flow"; -import { InMemoryChatHistoryStore } from "./src/history/in-memory-chat-history-store"; -import { getDataRetriever } from "./src/rag/retrievers/data-retriever"; -import { generateAlphaNumericString } from "./src/utils/utils"; - -// Function to define test flows -export const defineTestFlows = async () => { - // Open-ended chat flow - defineChatFlow({ - chatAgent: new ChatAgent(), - endpoint: ENDPOINTS.CHAT.OPEN_ENDED, - }); - // Open-ended chat flow with support for chat history - defineChatFlow({ - endpoint: ENDPOINTS.CHAT.OPEN_ENDED_WITH_HISTORY, - enableChatHistory: true, - chatHistoryStore: new InMemoryChatHistoryStore(), - }); - - // Close-ended chat flow (will only answer queries related to specified topic, in this case, 'Firebase') - defineChatFlow({ - chatAgent: new ChatAgent({ - agentType: "close-ended", - topic: "Firebase", - }), - endpoint: ENDPOINTS.CHAT.CLOSE_ENDED, - }); - // Close-ended chat flow with support for chat history - defineChatFlow({ - endpoint: ENDPOINTS.CHAT.CLOSE_ENDED_WITH_HISTORY, - agentType: "close-ended", - topic: "Firebase", - enableChatHistory: true, - chatHistoryStore: new InMemoryChatHistoryStore(), - }); - - // Below are examples of flows that support authentication using API keys and support response caching - - // Initialize API key store - const testAPIKeyStore = new InMemoryAPIKeyStore(); - // add a test API key - const key = generateAlphaNumericString(); - testAPIKeyStore.addKey(key, { - uid: "test-user", - status: "active", - endpoints: "all", // allow access to all endpoints - }); - console.log("\nTest API Key"); - console.dir(testAPIKeyStore); - - // Open-ended chat flow with support for chat history, authentication, and caching - defineChatFlow({ - endpoint: ENDPOINTS.CHAT.OPEN_ENDED_HISTORY_AUTH_CACHED, - enableChatHistory: true, - chatHistoryStore: new InMemoryChatHistoryStore(), - enableAuth: true, - apiKeyStore: testAPIKeyStore, - enableCache: true, - cacheStore: new InMemoryCacheStore(), - }); - - // Close-ended chat flow with support for chat history, authentication, and caching - defineChatFlow({ - agentType: "close-ended", - topic: "Firebase", - endpoint: ENDPOINTS.CHAT.CLOSE_ENDED_HISTORY_AUTH_CACHED, - enableChatHistory: true, - chatHistoryStore: new InMemoryChatHistoryStore(), - enableAuth: true, - apiKeyStore: testAPIKeyStore, - enableCache: true, - cacheStore: new InMemoryCacheStore(), - }); - - // Index inventory data and get retriever - const inventoryDataRetriever = await getDataRetriever({ - dataType: "csv", - filePath: "lib/rag/knowledge-base/test-data/inventory-data.csv", - generateEmbeddings: true, - }); - - // Inventory Data chat flow with support for chat history, authentication, caching and RAG - defineChatFlow({ - endpoint: ENDPOINTS.CHAT.RAG_HISTORY_AUTH_CACHED + "-inventory", - enableChatHistory: true, - chatHistoryStore: new InMemoryChatHistoryStore(), - enableAuth: true, - apiKeyStore: testAPIKeyStore, - enableCache: true, - cacheStore: new InMemoryCacheStore(), - enableRAG: true, - retriever: inventoryDataRetriever, - }); -}; diff --git a/tsconfig.json b/tsconfig.json index 9b4a1b6..5e2b783 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,5 @@ { + "include": ["src/**/*"], "compileOnSave": true, "compilerOptions": { "module": "CommonJS", @@ -9,12 +10,12 @@ "target": "ES2017", "skipLibCheck": true, "esModuleInterop": true, - "lib": ["ESNext"], + "lib": ["es2017"], "forceConsistentCasingInFileNames": true, "allowSyntheticDefaultImports": true, "resolveJsonModule": true, "isolatedModules": true, - "noEmit": true, + "noEmit": false, "strictNullChecks": true, "strictFunctionTypes": true, "strictBindCallApply": true, @@ -26,8 +27,7 @@ "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, "incremental": true, - "paths": { - "/*": ["./*"] - } + "declaration": true, + "emitDeclarationOnly": true } }