create-rag-app 0.1.2 → 0.2.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +4 -2
- package/package.json +2 -2
- package/src/index.js +57 -4
- package/src/prompts.js +51 -46
- package/src/utils.js +14 -0
- package/templates/nextjs-rag/package.json +12 -12
- package/templates/nextjs-rag/src/app/api/chat/route.js +49 -19
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kundan Kumar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
# Create RAG App
|
|
1
|
+
# Create RAG App (Beta)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **Note:** This project is currently in beta. Features and APIs are subject to change.
|
|
4
|
+
|
|
5
|
+
Scaffold a RAG (Retrieval Augmented Generation) application in seconds.
|
|
4
6
|
|
|
5
7
|
[](https://www.npmjs.com/package/create-rag-app)
|
|
6
8
|
[](https://www.npmjs.com/package/create-rag-app)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-rag-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.2.1-beta.1",
|
|
4
4
|
"description": "Scaffold a new RAG application",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
},
|
|
35
35
|
"license": "MIT",
|
|
36
36
|
"dependencies": {
|
|
37
|
+
"@inquirer/prompts": "^8.3.0",
|
|
37
38
|
"chalk": "^5.6.2",
|
|
38
39
|
"commander": "^14.0.3",
|
|
39
40
|
"fs-extra": "^11.3.3",
|
|
40
|
-
"inquirer": "^13.2.2",
|
|
41
41
|
"ora": "^9.3.0"
|
|
42
42
|
}
|
|
43
43
|
}
|
package/src/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import fs from "fs-extra";
|
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { getOptions } from "./prompts.js";
|
|
6
|
-
import { checkDir, copyTemplate, installDependencies } from "./utils.js";
|
|
6
|
+
import { checkDir, copyTemplate, installDependencies, initGit } from "./utils.js";
|
|
7
7
|
|
|
8
8
|
const packageJson = JSON.parse(
|
|
9
9
|
await fs.readFile(new URL("../package.json", import.meta.url))
|
|
@@ -18,12 +18,18 @@ export async function main() {
|
|
|
18
18
|
.version(packageJson.version)
|
|
19
19
|
.arguments('[project-directory]')
|
|
20
20
|
.usage(`${chalk.green('[project-directory]')} [options]`)
|
|
21
|
+
.option('-t, --template <template>', 'Specify the template (nextjs-rag, express-rag, fastapi-rag)')
|
|
22
|
+
.option('-p, --provider <provider>', 'Specify the LLM provider (openai, ollama, gemini)')
|
|
23
|
+
.option('-v, --vector-db <vectorDb>', 'Specify the Vector Database (chroma, pinecone, weaviate)')
|
|
24
|
+
.option('--skip-install', 'Skip dependency installation')
|
|
25
|
+
.option('--no-git', 'Skip git initialization')
|
|
21
26
|
.action((name) => {
|
|
22
27
|
projectName = name;
|
|
23
28
|
})
|
|
24
29
|
.parse(process.argv);
|
|
25
30
|
|
|
26
|
-
const
|
|
31
|
+
const cliOptions = program.opts();
|
|
32
|
+
const options = await getOptions(projectName, cliOptions);
|
|
27
33
|
const projectPath = path.resolve(process.cwd(), options.projectName);
|
|
28
34
|
|
|
29
35
|
console.log(`\nCreating a new RAG app in: ${chalk.green(projectPath)}\n`);
|
|
@@ -53,11 +59,58 @@ export async function main() {
|
|
|
53
59
|
await fs.writeFile(envPath, envContent);
|
|
54
60
|
}
|
|
55
61
|
|
|
62
|
+
// Update package.json based on selections
|
|
63
|
+
const pkgPath = path.join(projectPath, "package.json");
|
|
64
|
+
if (await fs.pathExists(pkgPath)) {
|
|
65
|
+
const pkg = await fs.readJson(pkgPath);
|
|
66
|
+
|
|
67
|
+
// Inject Pinecone dependencies if selected
|
|
68
|
+
if (options.vectorDb === "pinecone") {
|
|
69
|
+
pkg.dependencies = {
|
|
70
|
+
...pkg.dependencies,
|
|
71
|
+
"@pinecone-database/pinecone": "^4.1.0",
|
|
72
|
+
"@langchain/pinecone": "^0.1.3"
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Inject Weaviate dependencies if selected
|
|
77
|
+
if (options.vectorDb === "weaviate") {
|
|
78
|
+
pkg.dependencies = {
|
|
79
|
+
...pkg.dependencies,
|
|
80
|
+
"weaviate-ts-client": "^2.2.0",
|
|
81
|
+
"@langchain/weaviate": "^0.0.3"
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Inject Gemini dependencies if selected
|
|
86
|
+
if (options.provider === "gemini") {
|
|
87
|
+
pkg.dependencies["@langchain/google-genai"] = "^0.1.3";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
91
|
+
}
|
|
92
|
+
|
|
56
93
|
// Install Dependencies (user requested automatic installation)
|
|
57
|
-
|
|
94
|
+
let installed = false;
|
|
95
|
+
if (!options.skipInstall) {
|
|
96
|
+
installed = await installDependencies(projectPath);
|
|
97
|
+
} else {
|
|
98
|
+
console.log(chalk.yellow("\n⚠ Skipping dependency installation."));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Initialize Git
|
|
102
|
+
if (options.git) {
|
|
103
|
+
await initGit(projectPath);
|
|
104
|
+
}
|
|
58
105
|
|
|
59
106
|
// Final Success Message
|
|
60
|
-
|
|
107
|
+
if (installed) {
|
|
108
|
+
console.log(chalk.bold.green("\n🎉 Success! Your RAG app is ready."));
|
|
109
|
+
} else if (options.skipInstall) {
|
|
110
|
+
console.log(chalk.bold.green("\n🎉 Project created successfully."));
|
|
111
|
+
} else {
|
|
112
|
+
console.log(chalk.bold.yellow("\n⚠️ Project created, but dependencies failed to install."));
|
|
113
|
+
}
|
|
61
114
|
console.log(chalk.yellow("\nNext steps:"));
|
|
62
115
|
console.log(chalk.cyan(` cd ${options.projectName}`));
|
|
63
116
|
console.log(chalk.cyan(` npm run dev`));
|
package/src/prompts.js
CHANGED
|
@@ -1,66 +1,71 @@
|
|
|
1
1
|
|
|
2
|
-
import
|
|
2
|
+
import { input, select } from "@inquirer/prompts";
|
|
3
3
|
|
|
4
|
-
export async function getOptions(initialProjectName) {
|
|
5
|
-
let
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
export async function getOptions(initialProjectName, cliOptions = {}) {
|
|
5
|
+
let projectName = initialProjectName;
|
|
6
|
+
|
|
7
|
+
if (projectName) {
|
|
8
|
+
if (!/^([a-z0-9\-\_\.]+)$/.test(projectName)) {
|
|
9
|
+
console.log("⚠ Invalid project name provided via CLI.");
|
|
10
|
+
projectName = null; // Force prompt
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (!projectName) {
|
|
15
|
+
projectName = await input({
|
|
9
16
|
message: "What is your project named?",
|
|
10
17
|
default: "my-rag-app",
|
|
11
|
-
validate: (
|
|
12
|
-
if (/^([a-z0-9\-\_\.]+)$/.test(
|
|
18
|
+
validate: (value) => {
|
|
19
|
+
if (/^([a-z0-9\-\_\.]+)$/.test(value)) return true;
|
|
13
20
|
return "Project name may only include letters, numbers, dashes, and underscores.";
|
|
14
21
|
},
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let template = cliOptions.template;
|
|
26
|
+
if (!template) {
|
|
27
|
+
template = await select({
|
|
28
|
+
message: "Choose template",
|
|
20
29
|
choices: [
|
|
21
|
-
{ name: "Next.js
|
|
22
|
-
{ name: "Express
|
|
30
|
+
{ name: "Next.js", value: "nextjs-rag" },
|
|
31
|
+
{ name: "Express", value: "express-rag" },
|
|
32
|
+
{ name: "FastAPI", value: "fastapi-rag" },
|
|
23
33
|
],
|
|
24
34
|
default: "nextjs-rag",
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let provider = cliOptions.provider;
|
|
39
|
+
if (!provider) {
|
|
40
|
+
provider = await select({
|
|
41
|
+
message: "Choose LLM",
|
|
30
42
|
choices: [
|
|
31
|
-
{ name: "OpenAI
|
|
32
|
-
{ name: "
|
|
33
|
-
{ name: "Ollama
|
|
34
|
-
{ name: "Gemini (Google DeepMind)", value: "gemini" },
|
|
43
|
+
{ name: "OpenAI", value: "openai" },
|
|
44
|
+
{ name: "Gemini", value: "gemini" },
|
|
45
|
+
{ name: "Ollama", value: "ollama" },
|
|
35
46
|
],
|
|
36
47
|
default: "openai",
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let vectorDb = cliOptions.vectorDb;
|
|
52
|
+
if (!vectorDb) {
|
|
53
|
+
vectorDb = await select({
|
|
54
|
+
message: "Choose Vector DB",
|
|
42
55
|
choices: [
|
|
43
|
-
{ name: "
|
|
44
|
-
{ name: "
|
|
56
|
+
{ name: "Chroma", value: "chroma" },
|
|
57
|
+
{ name: "Pinecone", value: "pinecone" },
|
|
58
|
+
{ name: "Weaviate", value: "weaviate" },
|
|
45
59
|
],
|
|
46
60
|
default: "chroma",
|
|
47
|
-
}
|
|
48
|
-
];
|
|
49
|
-
|
|
50
|
-
if (initialProjectName) {
|
|
51
|
-
if (/^([a-z0-9\-\_\.]+)$/.test(initialProjectName)) {
|
|
52
|
-
// Valid project name provided via CLI, skip the prompt
|
|
53
|
-
questions = questions.filter(q => q.name !== "projectName");
|
|
54
|
-
} else {
|
|
55
|
-
console.log("⚠ Invalid project name provided via CLI.");
|
|
56
|
-
initialProjectName = null; // Force prompt
|
|
57
|
-
}
|
|
61
|
+
});
|
|
58
62
|
}
|
|
59
63
|
|
|
60
|
-
const answers = await inquirer.prompt(questions);
|
|
61
|
-
|
|
62
64
|
return {
|
|
63
|
-
projectName
|
|
64
|
-
|
|
65
|
+
projectName,
|
|
66
|
+
template,
|
|
67
|
+
provider,
|
|
68
|
+
vectorDb,
|
|
69
|
+
...cliOptions
|
|
65
70
|
};
|
|
66
71
|
}
|
package/src/utils.js
CHANGED
|
@@ -46,17 +46,31 @@ export async function installDependencies(targetPath) {
|
|
|
46
46
|
// Run npm install in the new directory
|
|
47
47
|
await execAsync("npm install", { cwd: targetPath });
|
|
48
48
|
spinner.succeed(chalk.green("Dependencies installed via npm."));
|
|
49
|
+
return true;
|
|
49
50
|
} catch (err) {
|
|
50
51
|
// Fallback for peer dependency conflicts common in AI libraries
|
|
51
52
|
try {
|
|
52
53
|
spinner.text = "Retrying with --legacy-peer-deps...";
|
|
53
54
|
await execAsync("npm install --legacy-peer-deps", { cwd: targetPath });
|
|
54
55
|
spinner.succeed(chalk.green("Dependencies installed with legacy peer deps."));
|
|
56
|
+
return true;
|
|
55
57
|
} catch (retryErr) {
|
|
56
58
|
spinner.fail(chalk.red("Failed to install dependencies."));
|
|
59
|
+
if (retryErr.stderr) console.error(chalk.red(retryErr.stderr));
|
|
57
60
|
console.log(chalk.yellow("You can try installing manually:"));
|
|
58
61
|
console.log(chalk.cyan(` cd ${path.basename(targetPath)}`));
|
|
59
62
|
console.log(chalk.cyan(" npm install --legacy-peer-deps"));
|
|
63
|
+
return false;
|
|
60
64
|
}
|
|
61
65
|
}
|
|
62
66
|
}
|
|
67
|
+
|
|
68
|
+
export async function initGit(targetPath) {
|
|
69
|
+
const spinner = ora("Initializing git repository...").start();
|
|
70
|
+
try {
|
|
71
|
+
await execAsync("git init", { cwd: targetPath });
|
|
72
|
+
spinner.succeed(chalk.green("Git repository initialized."));
|
|
73
|
+
} catch (err) {
|
|
74
|
+
spinner.warn(chalk.yellow("Failed to initialize git repository."));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-rag-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "next dev",
|
|
@@ -10,24 +10,24 @@
|
|
|
10
10
|
"ingest": "node scripts/ingest.js"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"next": "15.5.10",
|
|
14
|
-
"react": "19.0.0-rc-66855b96-20241106",
|
|
15
|
-
"react-dom": "19.0.0-rc-66855b96-20241106",
|
|
16
|
-
"langchain": "^0.3.5",
|
|
17
|
-
"@langchain/openai": "^0.3.11",
|
|
18
|
-
"@langchain/ollama": "^0.1.2",
|
|
19
|
-
"@langchain/google-genai": "^0.1.3",
|
|
20
13
|
"@langchain/community": "^0.3.11",
|
|
21
14
|
"@langchain/core": "^0.3.15",
|
|
15
|
+
"@langchain/google-genai": "^0.1.3",
|
|
16
|
+
"@langchain/ollama": "^0.1.2",
|
|
17
|
+
"@langchain/openai": "^0.3.11",
|
|
22
18
|
"@langchain/textsplitters": "^0.1.0",
|
|
23
19
|
"chromadb": "^1.9.4",
|
|
24
|
-
"
|
|
20
|
+
"dotenv": "^16.4.5",
|
|
21
|
+
"langchain": "^0.3.5",
|
|
22
|
+
"next": "^16.1.6",
|
|
25
23
|
"openai": "^4.71.1",
|
|
26
|
-
"
|
|
24
|
+
"pdf-parse": "1.1.1",
|
|
25
|
+
"react": "^19.2.4",
|
|
26
|
+
"react-dom": "^19.2.4"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
+
"autoprefixer": "^10.4.20",
|
|
29
30
|
"postcss": "^8.4.47",
|
|
30
|
-
"tailwindcss": "^3.4.14"
|
|
31
|
-
"autoprefixer": "^10.4.20"
|
|
31
|
+
"tailwindcss": "^3.4.14"
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getLLM, getEmbeddings } from "@/lib/llm";
|
|
2
|
-
import { Chroma } from "@langchain/community/vectorstores/chroma";
|
|
3
2
|
import { PromptTemplate } from "@langchain/core/prompts";
|
|
3
|
+
import { StringOutputParser } from "@langchain/core/output_parsers";
|
|
4
|
+
import { RunnableSequence, RunnablePassthrough } from "@langchain/core/runnables";
|
|
4
5
|
|
|
5
6
|
export async function POST(req) {
|
|
6
7
|
try {
|
|
@@ -14,36 +15,65 @@ export async function POST(req) {
|
|
|
14
15
|
);
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
url: process.env.CHROMA_URL || "http://localhost:8000"
|
|
20
|
-
});
|
|
18
|
+
let vectorStore;
|
|
19
|
+
const embeddings = getEmbeddings();
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
if (process.env.VECTOR_DB === "supabase") {
|
|
22
|
+
const { SupabaseVectorStore } = await import("@langchain/community/vectorstores/supabase");
|
|
23
|
+
const { createClient } = await import("@supabase/supabase-js");
|
|
24
|
+
const client = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_PRIVATE_KEY);
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
vectorStore = await SupabaseVectorStore.fromExistingIndex(embeddings, {
|
|
27
|
+
client,
|
|
28
|
+
tableName: "documents",
|
|
29
|
+
queryName: "match_documents",
|
|
30
|
+
});
|
|
31
|
+
} else {
|
|
32
|
+
const { Chroma } = await import("@langchain/community/vectorstores/chroma");
|
|
33
|
+
vectorStore = await Chroma.fromExistingCollection(embeddings, {
|
|
34
|
+
collectionName: process.env.COLLECTION_NAME || "rag-docs",
|
|
35
|
+
url: process.env.CHROMA_URL || "http://localhost:8000"
|
|
36
|
+
});
|
|
37
|
+
}
|
|
27
38
|
|
|
28
|
-
const
|
|
39
|
+
const retriever = vectorStore.asRetriever();
|
|
40
|
+
const llm = getLLM();
|
|
29
41
|
|
|
30
|
-
const
|
|
42
|
+
const template = `
|
|
31
43
|
Answer the question based only on the following context:
|
|
32
44
|
|
|
33
45
|
{context}
|
|
34
46
|
|
|
35
47
|
Question: {question}
|
|
36
|
-
|
|
48
|
+
`;
|
|
37
49
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
50
|
+
const prompt = PromptTemplate.fromTemplate(template);
|
|
51
|
+
|
|
52
|
+
// Create an LCEL chain: Retriever -> Prompt -> LLM -> OutputParser
|
|
53
|
+
const chain = RunnableSequence.from([
|
|
54
|
+
{
|
|
55
|
+
context: retriever.pipe((docs) => docs.map((d) => d.pageContent).join("\n\n")),
|
|
56
|
+
question: new RunnablePassthrough()
|
|
57
|
+
},
|
|
58
|
+
prompt,
|
|
59
|
+
llm,
|
|
60
|
+
new StringOutputParser()
|
|
61
|
+
]);
|
|
42
62
|
|
|
43
|
-
const
|
|
63
|
+
const stream = await chain.stream(message);
|
|
64
|
+
|
|
65
|
+
const textEncoder = new TextEncoder();
|
|
66
|
+
const readable = new ReadableStream({
|
|
67
|
+
async start(controller) {
|
|
68
|
+
for await (const chunk of stream) {
|
|
69
|
+
controller.enqueue(textEncoder.encode(chunk));
|
|
70
|
+
}
|
|
71
|
+
controller.close();
|
|
72
|
+
}
|
|
73
|
+
});
|
|
44
74
|
|
|
45
|
-
return Response
|
|
46
|
-
|
|
75
|
+
return new Response(readable, {
|
|
76
|
+
headers: { "Content-Type": "text/plain" },
|
|
47
77
|
});
|
|
48
78
|
} catch (error) {
|
|
49
79
|
console.error(error);
|