@retrivora-ai/rag-engine 0.1.0

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.
Files changed (61) hide show
  1. package/.env.example +77 -0
  2. package/README.md +149 -0
  3. package/dist/DocumentChunker-BUrIrcPk.d.mts +43 -0
  4. package/dist/DocumentChunker-BUrIrcPk.d.ts +43 -0
  5. package/dist/RAGPipeline-BmkIv1HD.d.mts +298 -0
  6. package/dist/RAGPipeline-BmkIv1HD.d.ts +298 -0
  7. package/dist/chunk-NCG2JKXB.mjs +1254 -0
  8. package/dist/chunk-ZPXLQR5Q.mjs +67 -0
  9. package/dist/handlers/index.d.mts +68 -0
  10. package/dist/handlers/index.d.ts +68 -0
  11. package/dist/handlers/index.js +1319 -0
  12. package/dist/handlers/index.mjs +13 -0
  13. package/dist/index.d.mts +93 -0
  14. package/dist/index.d.ts +93 -0
  15. package/dist/index.js +612 -0
  16. package/dist/index.mjs +551 -0
  17. package/dist/server.d.mts +211 -0
  18. package/dist/server.d.ts +211 -0
  19. package/dist/server.js +1457 -0
  20. package/dist/server.mjs +148 -0
  21. package/package.json +90 -0
  22. package/src/app/api/chat/route.ts +4 -0
  23. package/src/app/api/health/route.ts +4 -0
  24. package/src/app/api/ingest/route.ts +26 -0
  25. package/src/app/favicon.ico +0 -0
  26. package/src/app/globals.css +163 -0
  27. package/src/app/layout.tsx +35 -0
  28. package/src/app/page.tsx +506 -0
  29. package/src/components/ChatWidget.tsx +91 -0
  30. package/src/components/ChatWindow.tsx +248 -0
  31. package/src/components/ConfigProvider.tsx +56 -0
  32. package/src/components/MessageBubble.tsx +99 -0
  33. package/src/components/SourceCard.tsx +66 -0
  34. package/src/components/ThemeProvider.tsx +8 -0
  35. package/src/components/ThemeToggle.tsx +29 -0
  36. package/src/config/RagConfig.ts +159 -0
  37. package/src/config/UniversalProfiles.ts +83 -0
  38. package/src/config/serverConfig.ts +142 -0
  39. package/src/handlers/index.ts +202 -0
  40. package/src/hooks/useHydrated.ts +13 -0
  41. package/src/hooks/useRagChat.ts +167 -0
  42. package/src/hooks/useStoredMessages.ts +53 -0
  43. package/src/index.ts +27 -0
  44. package/src/llm/ILLMProvider.ts +60 -0
  45. package/src/llm/LLMFactory.ts +54 -0
  46. package/src/llm/providers/AnthropicProvider.ts +87 -0
  47. package/src/llm/providers/OllamaProvider.ts +102 -0
  48. package/src/llm/providers/OpenAIProvider.ts +92 -0
  49. package/src/llm/providers/UniversalLLMAdapter.ts +154 -0
  50. package/src/rag/DocumentChunker.ts +105 -0
  51. package/src/rag/RAGPipeline.ts +196 -0
  52. package/src/server.ts +30 -0
  53. package/src/types/pdf-parse.d.ts +13 -0
  54. package/src/utils/DocumentParser.ts +75 -0
  55. package/src/utils/templateUtils.ts +78 -0
  56. package/src/vectordb/IVectorDB.ts +75 -0
  57. package/src/vectordb/VectorDBFactory.ts +41 -0
  58. package/src/vectordb/adapters/MongoDbAdapter.ts +175 -0
  59. package/src/vectordb/adapters/PgVectorAdapter.ts +159 -0
  60. package/src/vectordb/adapters/PineconeAdapter.ts +115 -0
  61. package/src/vectordb/adapters/UniversalVectorDBAdapter.ts +177 -0
@@ -0,0 +1,148 @@
1
+ import {
2
+ AnthropicProvider,
3
+ DocumentChunker,
4
+ LLMFactory,
5
+ MongoDbAdapter,
6
+ OllamaProvider,
7
+ OpenAIProvider,
8
+ PgVectorAdapter,
9
+ PineconeAdapter,
10
+ RAGPipeline,
11
+ VectorDBFactory,
12
+ createChatHandler,
13
+ createHealthHandler,
14
+ createIngestHandler,
15
+ createUploadHandler
16
+ } from "./chunk-NCG2JKXB.mjs";
17
+ import "./chunk-ZPXLQR5Q.mjs";
18
+
19
+ // src/config/serverConfig.ts
20
+ var VECTOR_DB_PROVIDERS = ["pinecone", "pgvector", "mongodb", "rest", "universal_rest", "custom"];
21
+ var LLM_PROVIDERS = ["openai", "anthropic", "ollama", "rest", "universal_rest", "custom"];
22
+ var EMBEDDING_PROVIDERS = ["openai", "ollama", "rest", "universal_rest", "custom"];
23
+ function readString(env, name) {
24
+ var _a;
25
+ const value = (_a = env[name]) == null ? void 0 : _a.trim();
26
+ return value ? value : void 0;
27
+ }
28
+ function readNumber(env, name, fallback) {
29
+ const value = env[name];
30
+ if (!value) return fallback;
31
+ const parsed = Number(value);
32
+ if (!Number.isFinite(parsed)) {
33
+ throw new Error(`[getRagConfig] ${name} must be a valid number`);
34
+ }
35
+ return parsed;
36
+ }
37
+ function readEnum(env, name, fallback, allowed) {
38
+ var _a;
39
+ const value = (_a = readString(env, name)) != null ? _a : fallback;
40
+ if (allowed.includes(value)) {
41
+ return value;
42
+ }
43
+ throw new Error(`[getRagConfig] ${name} must be one of: ${allowed.join(", ")}`);
44
+ }
45
+ function getRagConfig(env = process.env) {
46
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J;
47
+ const projectId = (_b = (_a = readString(env, "RAG_PROJECT_ID")) != null ? _a : readString(env, "NEXT_PUBLIC_PROJECT_ID")) != null ? _b : "default";
48
+ const vectorProvider = readEnum(env, "VECTOR_DB_PROVIDER", "pinecone", VECTOR_DB_PROVIDERS);
49
+ const llmProvider = readEnum(env, "LLM_PROVIDER", "openai", LLM_PROVIDERS);
50
+ const embeddingProvider = readEnum(env, "EMBEDDING_PROVIDER", "openai", EMBEDDING_PROVIDERS);
51
+ const embeddingDimensions = readNumber(env, "EMBEDDING_DIMENSIONS", 1536);
52
+ const vectorDbOptions = {};
53
+ if (vectorProvider === "pinecone") {
54
+ vectorDbOptions.apiKey = (_c = readString(env, "PINECONE_API_KEY")) != null ? _c : "";
55
+ vectorDbOptions.environment = (_d = readString(env, "PINECONE_ENVIRONMENT")) != null ? _d : "";
56
+ } else if (vectorProvider === "pgvector") {
57
+ vectorDbOptions.connectionString = (_e = readString(env, "PGVECTOR_CONNECTION_STRING")) != null ? _e : "";
58
+ vectorDbOptions.dimensions = embeddingDimensions;
59
+ } else if (vectorProvider === "rest") {
60
+ vectorDbOptions.baseUrl = (_f = readString(env, "VECTOR_DB_REST_URL")) != null ? _f : "";
61
+ vectorDbOptions.headers = readString(env, "VECTOR_DB_REST_API_KEY") ? { "api-key": readString(env, "VECTOR_DB_REST_API_KEY") } : {};
62
+ } else if (vectorProvider === "universal_rest") {
63
+ vectorDbOptions.baseUrl = (_h = (_g = readString(env, "VECTOR_BASE_URL")) != null ? _g : readString(env, "VECTOR_DB_REST_URL")) != null ? _h : "";
64
+ vectorDbOptions.profile = readString(env, "VECTOR_UNIVERSAL_PROFILE");
65
+ vectorDbOptions.headers = readString(env, "VECTOR_DB_REST_API_KEY") ? { Authorization: `Bearer ${readString(env, "VECTOR_DB_REST_API_KEY")}` } : {};
66
+ } else if (vectorProvider === "mongodb") {
67
+ vectorDbOptions.uri = (_i = readString(env, "MONGODB_URI")) != null ? _i : "";
68
+ vectorDbOptions.database = (_j = readString(env, "MONGODB_DB")) != null ? _j : "";
69
+ vectorDbOptions.collection = (_k = readString(env, "MONGODB_COLLECTION")) != null ? _k : "";
70
+ vectorDbOptions.indexName = (_l = readString(env, "MONGODB_INDEX_NAME")) != null ? _l : "vector_index";
71
+ }
72
+ const llmApiKeyByProvider = {
73
+ openai: readString(env, "OPENAI_API_KEY"),
74
+ anthropic: readString(env, "ANTHROPIC_API_KEY"),
75
+ ollama: void 0,
76
+ universal_rest: readString(env, "LLM_API_KEY")
77
+ };
78
+ const embeddingApiKeyByProvider = {
79
+ openai: readString(env, "OPENAI_API_KEY"),
80
+ ollama: void 0,
81
+ custom: (_m = readString(env, "EMBEDDING_API_KEY")) != null ? _m : readString(env, "OPENAI_API_KEY")
82
+ };
83
+ return {
84
+ projectId,
85
+ vectorDb: {
86
+ provider: vectorProvider,
87
+ indexName: (_n = readString(env, "VECTOR_DB_INDEX")) != null ? _n : "rag-index",
88
+ options: vectorDbOptions
89
+ },
90
+ llm: {
91
+ provider: llmProvider,
92
+ model: (_o = readString(env, "LLM_MODEL")) != null ? _o : "gpt-4o",
93
+ apiKey: (_p = llmApiKeyByProvider[llmProvider]) != null ? _p : "",
94
+ baseUrl: readString(env, "LLM_BASE_URL"),
95
+ systemPrompt: readString(env, "LLM_SYSTEM_PROMPT"),
96
+ maxTokens: readNumber(env, "LLM_MAX_TOKENS", 1024),
97
+ temperature: readNumber(env, "LLM_TEMPERATURE", 0.7),
98
+ options: {
99
+ profile: readString(env, "LLM_UNIVERSAL_PROFILE")
100
+ }
101
+ },
102
+ embedding: {
103
+ provider: embeddingProvider,
104
+ model: (_q = readString(env, "EMBEDDING_MODEL")) != null ? _q : "text-embedding-3-small",
105
+ apiKey: embeddingApiKeyByProvider[embeddingProvider],
106
+ baseUrl: readString(env, "EMBEDDING_BASE_URL"),
107
+ dimensions: embeddingDimensions,
108
+ options: {
109
+ profile: readString(env, "EMBEDDING_UNIVERSAL_PROFILE")
110
+ }
111
+ },
112
+ ui: {
113
+ title: (_s = (_r = readString(env, "NEXT_PUBLIC_UI_TITLE")) != null ? _r : readString(env, "UI_TITLE")) != null ? _s : "AI Assistant",
114
+ subtitle: (_u = (_t = readString(env, "NEXT_PUBLIC_UI_SUBTITLE")) != null ? _t : readString(env, "UI_SUBTITLE")) != null ? _u : "Powered by RAG",
115
+ primaryColor: (_w = (_v = readString(env, "NEXT_PUBLIC_PRIMARY_COLOR")) != null ? _v : readString(env, "UI_PRIMARY_COLOR")) != null ? _w : "#10b981",
116
+ accentColor: (_y = (_x = readString(env, "NEXT_PUBLIC_ACCENT_COLOR")) != null ? _x : readString(env, "UI_ACCENT_COLOR")) != null ? _y : "#3b82f6",
117
+ logoUrl: (_z = readString(env, "NEXT_PUBLIC_LOGO_URL")) != null ? _z : readString(env, "UI_LOGO_URL"),
118
+ placeholder: (_B = (_A = readString(env, "NEXT_PUBLIC_PLACEHOLDER")) != null ? _A : readString(env, "UI_PLACEHOLDER")) != null ? _B : "Ask me anything\u2026",
119
+ showSources: ((_D = (_C = readString(env, "NEXT_PUBLIC_SHOW_SOURCES")) != null ? _C : readString(env, "UI_SHOW_SOURCES")) != null ? _D : "true") !== "false",
120
+ welcomeMessage: (_F = (_E = readString(env, "NEXT_PUBLIC_WELCOME_MESSAGE")) != null ? _E : readString(env, "UI_WELCOME_MESSAGE")) != null ? _F : "Hello! I'm your AI assistant. Ask me anything about your documents.",
121
+ visualStyle: (_H = (_G = readString(env, "NEXT_PUBLIC_UI_VISUAL_STYLE")) != null ? _G : readString(env, "UI_VISUAL_STYLE")) != null ? _H : "glass",
122
+ borderRadius: (_J = (_I = readString(env, "NEXT_PUBLIC_UI_BORDER_RADIUS")) != null ? _I : readString(env, "UI_BORDER_RADIUS")) != null ? _J : "xl"
123
+ },
124
+ rag: {
125
+ topK: readNumber(env, "RAG_TOP_K", 5),
126
+ scoreThreshold: readNumber(env, "RAG_SCORE_THRESHOLD", 0),
127
+ chunkSize: readNumber(env, "RAG_CHUNK_SIZE", 1e3),
128
+ chunkOverlap: readNumber(env, "RAG_CHUNK_OVERLAP", 200)
129
+ }
130
+ };
131
+ }
132
+ export {
133
+ AnthropicProvider,
134
+ DocumentChunker,
135
+ LLMFactory,
136
+ MongoDbAdapter,
137
+ OllamaProvider,
138
+ OpenAIProvider,
139
+ PgVectorAdapter,
140
+ PineconeAdapter,
141
+ RAGPipeline,
142
+ VectorDBFactory,
143
+ createChatHandler,
144
+ createHealthHandler,
145
+ createIngestHandler,
146
+ createUploadHandler,
147
+ getRagConfig
148
+ };
package/package.json ADDED
@@ -0,0 +1,90 @@
1
+ {
2
+ "name": "@retrivora-ai/rag-engine",
3
+ "version": "0.1.0",
4
+ "description": "Retrivora AI is a plug-and-play AI engine for RAG chat experiences — generic vector DB + LLM provider, embeddable or standalone.",
5
+ "author": "Abhinav Alkuchi",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "rag",
9
+ "chatbot",
10
+ "vector-database",
11
+ "pinecone",
12
+ "pgvector",
13
+ "openai",
14
+ "anthropic",
15
+ "ollama",
16
+ "nextjs",
17
+ "react",
18
+ "ai",
19
+ "llm"
20
+ ],
21
+ "main": "dist/index.js",
22
+ "module": "dist/index.mjs",
23
+ "types": "dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "require": "./dist/index.js",
28
+ "import": "./dist/index.mjs"
29
+ },
30
+ "./handlers": {
31
+ "types": "./dist/handlers/index.d.ts",
32
+ "require": "./dist/handlers/index.js",
33
+ "import": "./dist/handlers/index.mjs"
34
+ },
35
+ "./server": {
36
+ "types": "./dist/server.d.ts",
37
+ "require": "./dist/server.js",
38
+ "import": "./dist/server.mjs"
39
+ }
40
+ },
41
+ "files": [
42
+ "dist",
43
+ "src",
44
+ ".env.example",
45
+ "README.md"
46
+ ],
47
+ "scripts": {
48
+ "dev": "next dev",
49
+ "build": "next build",
50
+ "build:pkg": "tsup src/index.ts src/handlers/index.ts src/server.ts --format cjs,esm --dts --clean --tsconfig tsconfig.build.json --external react,react-dom,next",
51
+ "start": "next start",
52
+ "lint": "eslint",
53
+ "prepublishOnly": "npm run build:pkg"
54
+ },
55
+ "peerDependencies": {
56
+ "next": ">=14.0.0",
57
+ "react": ">=18.0.0",
58
+ "react-dom": ">=18.0.0"
59
+ },
60
+ "dependencies": {
61
+ "@anthropic-ai/sdk": "^0.90.0",
62
+ "@pinecone-database/pinecone": "^7.2.0",
63
+ "axios": "^1.15.0",
64
+ "lucide-react": "^1.8.0",
65
+ "mammoth": "^1.8.0",
66
+ "mongodb": "^7.1.1",
67
+ "next": ">=14.0.0",
68
+ "next-themes": "^0.4.6",
69
+ "openai": "^6.34.0",
70
+ "pdf-parse": "^1.1.1",
71
+ "pg": "^8.20.0",
72
+ "react": ">=18.0.0",
73
+ "react-dom": ">=18.0.0",
74
+ "react-markdown": "^10.1.0",
75
+ "remark-gfm": "^4.0.1"
76
+ },
77
+ "devDependencies": {
78
+ "@tailwindcss/postcss": "^4",
79
+ "@types/node": "^20",
80
+ "@types/pdf-parse": "^1.1.5",
81
+ "@types/pg": "^8.20.0",
82
+ "@types/react": "^19.2.14",
83
+ "@types/react-dom": "^19.2.3",
84
+ "eslint": "^9",
85
+ "eslint-config-next": "16.2.4",
86
+ "tailwindcss": "^4",
87
+ "tsup": "^8.5.1",
88
+ "typescript": "^5"
89
+ }
90
+ }
@@ -0,0 +1,4 @@
1
+ import { createChatHandler } from '@/handlers';
2
+ import { getRagConfig } from '@/config/serverConfig';
3
+
4
+ export const POST = createChatHandler(getRagConfig());
@@ -0,0 +1,4 @@
1
+ import { createHealthHandler } from '@/handlers';
2
+ import { getRagConfig } from '@/config/serverConfig';
3
+
4
+ export const GET = createHealthHandler(getRagConfig());
@@ -0,0 +1,26 @@
1
+ /**
2
+ * POST /api/ingest
3
+ *
4
+ * Ingests one or more documents into the vector DB for a project.
5
+ *
6
+ * Request body:
7
+ * {
8
+ * documents: Array<{
9
+ * docId: string
10
+ * content: string
11
+ * metadata?: Record<string, unknown>
12
+ * }>
13
+ * namespace?: string – optional project namespace override
14
+ * }
15
+ *
16
+ * Response:
17
+ * {
18
+ * results: Array<{ docId: string; chunksIngested: number }>
19
+ * totalChunks: number
20
+ * }
21
+ */
22
+
23
+ import { createIngestHandler } from '@/handlers';
24
+ import { getRagConfig } from '@/config/serverConfig';
25
+
26
+ export const POST = createIngestHandler(getRagConfig());
Binary file
@@ -0,0 +1,163 @@
1
+ @import "tailwindcss";
2
+
3
+ @custom-variant dark (&:where(.dark, .dark *));
4
+
5
+
6
+
7
+ /* ─── Design Tokens ─────────────────────────────────────────── */
8
+ :root {
9
+ --font-inter: 'Inter', sans-serif;
10
+
11
+ /* Light Mode (Default) */
12
+ --bg-base: #f8fafc; /* slate-50 */
13
+ --bg-surface: #ffffff;
14
+ --bg-elevated: #f1f5f9; /* slate-100 */
15
+ --border: rgba(0, 0, 0, 0.08);
16
+ --text-primary: #0f172a; /* slate-900 */
17
+ --text-secondary: #475569; /* slate-600 */
18
+ --text-muted: #94a3b8; /* slate-400 */
19
+ }
20
+
21
+ .dark {
22
+ /* Dark Mode */
23
+ --bg-base: #080811;
24
+ --bg-surface: #0f0f1a;
25
+ --bg-elevated: #161625;
26
+ --border: rgba(255, 255, 255, 0.08);
27
+ --text-primary: rgba(255, 255, 255, 0.92);
28
+ --text-secondary: rgba(255, 255, 255, 0.55);
29
+ --text-muted: rgba(255, 255, 255, 0.25);
30
+ }
31
+
32
+ /* ─── Base Reset ────────────────────────────────────────────── */
33
+ *,
34
+ *::before,
35
+ *::after {
36
+ box-sizing: border-box;
37
+ }
38
+
39
+ html {
40
+ font-family: var(--font-inter), system-ui, sans-serif;
41
+ -webkit-font-smoothing: antialiased;
42
+ -moz-osx-font-smoothing: grayscale;
43
+ scroll-behavior: smooth;
44
+ }
45
+
46
+ body {
47
+ background-color: var(--bg-base);
48
+ color: var(--text-primary);
49
+ margin: 0;
50
+ }
51
+
52
+ /* ─── Scrollbar ─────────────────────────────────────────────── */
53
+ ::-webkit-scrollbar {
54
+ width: 4px;
55
+ height: 4px;
56
+ }
57
+ ::-webkit-scrollbar-track {
58
+ background: transparent;
59
+ }
60
+ ::-webkit-scrollbar-thumb {
61
+ background: rgba(255, 255, 255, 0.12);
62
+ border-radius: 999px;
63
+ }
64
+ ::-webkit-scrollbar-thumb:hover {
65
+ background: rgba(255, 255, 255, 0.2);
66
+ }
67
+
68
+ /* ─── Prose Overrides (markdown in chat) ───────────────────── */
69
+ .prose-invert {
70
+ color: inherit;
71
+ }
72
+
73
+ .prose-invert p {
74
+ margin: 0.25em 0;
75
+ }
76
+
77
+ .prose-invert pre {
78
+ background: rgba(0, 0, 0, 0.4);
79
+ border: 1px solid rgba(255, 255, 255, 0.08);
80
+ border-radius: 0.5rem;
81
+ padding: 0.75rem 1rem;
82
+ overflow-x: auto;
83
+ }
84
+
85
+ .prose-invert code:not(pre code) {
86
+ background: rgba(255, 255, 255, 0.08);
87
+ border-radius: 0.25rem;
88
+ padding: 0.1em 0.35em;
89
+ font-size: 0.85em;
90
+ }
91
+
92
+ .prose-invert a {
93
+ color: #818cf8;
94
+ text-decoration: underline;
95
+ }
96
+
97
+ .prose-invert ul,
98
+ .prose-invert ol {
99
+ padding-left: 1.25em;
100
+ margin: 0.35em 0;
101
+ }
102
+
103
+ .prose-invert li {
104
+ margin: 0.15em 0;
105
+ }
106
+
107
+ .prose-invert blockquote {
108
+ border-left: 3px solid rgba(99, 102, 241, 0.5);
109
+ padding-left: 0.75em;
110
+ color: rgba(255, 255, 255, 0.6);
111
+ margin: 0.5em 0;
112
+ }
113
+
114
+ /* ─── Animations ────────────────────────────────────────────── */
115
+ @keyframes fadeInUp {
116
+ from {
117
+ opacity: 0;
118
+ transform: translateY(8px);
119
+ }
120
+ to {
121
+ opacity: 1;
122
+ transform: translateY(0);
123
+ }
124
+ }
125
+
126
+ @keyframes slideUp {
127
+ from {
128
+ opacity: 0;
129
+ transform: translateY(16px) scale(0.98);
130
+ }
131
+ to {
132
+ opacity: 1;
133
+ transform: translateY(0) scale(1);
134
+ }
135
+ }
136
+
137
+ @keyframes shimmer {
138
+ 0% { background-position: -200% 0; }
139
+ 100% { background-position: 200% 0; }
140
+ }
141
+
142
+ .animate-fade-in-up {
143
+ animation: fadeInUp 0.3s ease-out both;
144
+ }
145
+
146
+ .animate-slide-up {
147
+ animation: slideUp 0.35s cubic-bezier(0.34, 1.56, 0.64, 1) both;
148
+ }
149
+
150
+ /* ─── Glass Surface ─────────────────────────────────────────── */
151
+ .glass {
152
+ background: rgba(255, 255, 255, 0.04);
153
+ backdrop-filter: blur(16px);
154
+ border: 1px solid var(--border);
155
+ }
156
+
157
+ /* ─── Gradient Text Utility ─────────────────────────────────── */
158
+ .gradient-text {
159
+ background: linear-gradient(135deg, #6366f1, #8b5cf6);
160
+ -webkit-background-clip: text;
161
+ -webkit-text-fill-color: transparent;
162
+ background-clip: text;
163
+ }
@@ -0,0 +1,35 @@
1
+ import type { Metadata } from 'next';
2
+ import { Inter } from 'next/font/google';
3
+ import './globals.css';
4
+ import { ConfigProvider } from '@/components/ConfigProvider';
5
+ import { ThemeProvider } from '@/components/ThemeProvider';
6
+ import { getRagConfig } from '@/config/serverConfig';
7
+
8
+ const inter = Inter({ subsets: ['latin'], variable: '--font-inter' });
9
+
10
+ export const metadata: Metadata = {
11
+ title: 'Retrivora AI',
12
+ description:
13
+ 'A configurable Retrieval-Augmented Generation chatbot — generic across any vector database and LLM provider.',
14
+ };
15
+
16
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
17
+ const clientConfig = getRagConfig();
18
+
19
+ return (
20
+ <html lang="en" className={inter.variable} suppressHydrationWarning>
21
+ <body className="bg-slate-50 dark:bg-[#080811] text-slate-900 dark:text-white antialiased transition-colors duration-300">
22
+ <ThemeProvider attribute="class" defaultTheme="dark" enableSystem disableTransitionOnChange>
23
+ <ConfigProvider
24
+ config={{
25
+ projectId: clientConfig.projectId,
26
+ ui: clientConfig.ui,
27
+ }}
28
+ >
29
+ {children}
30
+ </ConfigProvider>
31
+ </ThemeProvider>
32
+ </body>
33
+ </html>
34
+ );
35
+ }