@yesvara/svara 0.1.1 → 0.1.2

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/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  <div align="center">
2
+ <img src="SvaraJS.png" alt="SvaraJS" width="400">
2
3
 
3
- # @yesvara/svara
4
+ <!-- # @yesvara/svara -->
4
5
 
5
6
  **Build AI agents in 15 lines. Ship to production.**
6
7
 
@@ -53,7 +54,7 @@ That's it. No pipeline setup. No embedding boilerplate. No webhook configuration
53
54
  | **Express-compatible** | `agent.handler()` drops into any existing app |
54
55
  | **Built-in database** | Persistent SQLite for users, sessions, RAG chunks, and state |
55
56
  | **RAG per agent** | Each agent has isolated knowledge base, no cross-contamination |
56
- | **RAG persistence** | Vector embeddings stored in SQLite, survive restarts, auto-dedup |
57
+ | **RAG persistence** | Vector embeddings stored in SQLite, auto-dedup |
57
58
  | **User tracking** | Auto-tracks users and sessions with timestamps |
58
59
  | **CLI included** | `svara new`, `svara dev`, `svara build` |
59
60
 
@@ -262,6 +263,43 @@ await salesBot.start();
262
263
  - Deduplication happens per agent (same content in different agents is OK)
263
264
  - Perfect for multi-agent systems with different domains
264
265
 
266
+ **Accessing Retrieved Documents:**
267
+
268
+ The `/chat` endpoint returns `retrievedDocuments` showing which knowledge was used:
269
+
270
+ ```ts
271
+ const response = await fetch('http://localhost:3000/chat', {
272
+ method: 'POST',
273
+ headers: { 'Content-Type': 'application/json' },
274
+ body: JSON.stringify({
275
+ message: 'What is the pricing?',
276
+ sessionId: 'user-123'
277
+ })
278
+ });
279
+
280
+ const result = await response.json();
281
+ // {
282
+ // response: "Our pricing starts at...",
283
+ // retrievedDocuments: [
284
+ // {
285
+ // source: "./docs/pricing.md",
286
+ // score: 0.89, // relevance (0-1)
287
+ // excerpt: "# Pricing\n\nOur plans start at..."
288
+ // },
289
+ // {
290
+ // source: "./docs/faq.md",
291
+ // score: 0.76,
292
+ // excerpt: "## Is there a free trial?\n\nYes, 14 days..."
293
+ // }
294
+ // ]
295
+ // }
296
+ ```
297
+
298
+ The `retrievedDocuments` field shows:
299
+ - **source**: File path of the matching document
300
+ - **score**: Cosine similarity (0-1, higher = more relevant)
301
+ - **excerpt**: First 150 characters of the matched chunk
302
+
265
303
  ### User & Session Tracking
266
304
 
267
305
  Every message automatically tracks the user and their session.
package/SvaraJS.png ADDED
Binary file
@@ -1,3 +1,6 @@
1
+ import {
2
+ SvaraDB
3
+ } from "./chunk-GA7LHPOF.mjs";
1
4
  import {
2
5
  __require
3
6
  } from "./chunk-CIESM3BP.mjs";
@@ -256,256 +259,6 @@ var Chunker = class {
256
259
  }
257
260
  };
258
261
 
259
- // src/database/sqlite.ts
260
- import path2 from "path";
261
- import fs2 from "fs";
262
-
263
- // src/database/schema.ts
264
- var SCHEMA_VERSION = 1;
265
- var CREATE_TABLES_SQL = `
266
- -- Schema version tracking
267
- CREATE TABLE IF NOT EXISTS svara_meta (
268
- key TEXT PRIMARY KEY,
269
- value TEXT NOT NULL
270
- );
271
-
272
- -- Conversation history persistence
273
- CREATE TABLE IF NOT EXISTS svara_messages (
274
- id TEXT PRIMARY KEY,
275
- session_id TEXT NOT NULL,
276
- role TEXT NOT NULL CHECK(role IN ('user', 'assistant', 'system', 'tool')),
277
- content TEXT NOT NULL,
278
- tool_call_id TEXT,
279
- created_at INTEGER NOT NULL DEFAULT (unixepoch())
280
- );
281
-
282
- CREATE INDEX IF NOT EXISTS idx_messages_session
283
- ON svara_messages (session_id, created_at);
284
-
285
- -- User registry
286
- CREATE TABLE IF NOT EXISTS svara_users (
287
- id TEXT PRIMARY KEY,
288
- email TEXT,
289
- display_name TEXT,
290
- first_seen INTEGER NOT NULL DEFAULT (unixepoch()),
291
- last_seen INTEGER NOT NULL DEFAULT (unixepoch()),
292
- metadata TEXT DEFAULT '{}'
293
- );
294
-
295
- CREATE INDEX IF NOT EXISTS idx_users_email
296
- ON svara_users (email);
297
-
298
- -- Session metadata
299
- CREATE TABLE IF NOT EXISTS svara_sessions (
300
- id TEXT PRIMARY KEY,
301
- user_id TEXT NOT NULL,
302
- channel TEXT NOT NULL,
303
- created_at INTEGER NOT NULL DEFAULT (unixepoch()),
304
- updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
305
- metadata TEXT DEFAULT '{}',
306
- FOREIGN KEY (user_id) REFERENCES svara_users(id)
307
- );
308
-
309
- CREATE INDEX IF NOT EXISTS idx_sessions_user
310
- ON svara_sessions (user_id);
311
-
312
- -- Vector store chunks for RAG (per agent)
313
- CREATE TABLE IF NOT EXISTS svara_chunks (
314
- id TEXT PRIMARY KEY,
315
- agent_name TEXT NOT NULL, -- Separate RAG per agent
316
- document_id TEXT NOT NULL,
317
- content TEXT NOT NULL,
318
- content_hash TEXT NOT NULL, -- MD5 hash of content for deduplication
319
- chunk_index INTEGER NOT NULL,
320
- embedding TEXT, -- stored as JSON string of float array
321
- source TEXT NOT NULL,
322
- metadata TEXT DEFAULT '{}',
323
- created_at INTEGER NOT NULL DEFAULT (unixepoch())
324
- );
325
-
326
- CREATE INDEX IF NOT EXISTS idx_chunks_agent
327
- ON svara_chunks (agent_name);
328
-
329
- CREATE INDEX IF NOT EXISTS idx_chunks_agent_document
330
- ON svara_chunks (agent_name, document_id);
331
-
332
- CREATE INDEX IF NOT EXISTS idx_chunks_content_hash
333
- ON svara_chunks (content_hash);
334
-
335
- -- Document registry
336
- CREATE TABLE IF NOT EXISTS svara_documents (
337
- id TEXT PRIMARY KEY,
338
- source TEXT NOT NULL UNIQUE,
339
- type TEXT NOT NULL,
340
- size INTEGER,
341
- hash TEXT,
342
- indexed_at INTEGER NOT NULL DEFAULT (unixepoch()),
343
- metadata TEXT DEFAULT '{}'
344
- );
345
-
346
- -- Key-value store for arbitrary agent state
347
- CREATE TABLE IF NOT EXISTS svara_kv (
348
- key TEXT PRIMARY KEY,
349
- value TEXT NOT NULL,
350
- expires_at INTEGER, -- unix timestamp, NULL = no expiry
351
- updated_at INTEGER NOT NULL DEFAULT (unixepoch())
352
- );
353
- `;
354
- var INSERT_META_SQL = `
355
- INSERT OR REPLACE INTO svara_meta (key, value)
356
- VALUES ('schema_version', ?), ('created_at', ?);
357
- `;
358
-
359
- // src/database/sqlite.ts
360
- var KVStore = class {
361
- constructor(db) {
362
- this.db = db;
363
- }
364
- db;
365
- /** Set a key-value pair, with optional TTL in seconds. */
366
- set(key, value, ttlSeconds) {
367
- const expiresAt = ttlSeconds ? Math.floor(Date.now() / 1e3) + ttlSeconds : null;
368
- this.db.prepare(`
369
- INSERT OR REPLACE INTO svara_kv (key, value, expires_at, updated_at)
370
- VALUES (?, ?, ?, unixepoch())
371
- `).run(key, JSON.stringify(value), expiresAt);
372
- }
373
- /** Get a value by key. Returns undefined if not found or expired. */
374
- get(key) {
375
- const row = this.db.prepare(`
376
- SELECT value, expires_at FROM svara_kv
377
- WHERE key = ? AND (expires_at IS NULL OR expires_at > unixepoch())
378
- `).get(key);
379
- if (!row) return void 0;
380
- return JSON.parse(row.value);
381
- }
382
- /** Delete a key. */
383
- delete(key) {
384
- this.db.prepare("DELETE FROM svara_kv WHERE key = ?").run(key);
385
- }
386
- /** Check if a key exists and is not expired. */
387
- has(key) {
388
- return this.get(key) !== void 0;
389
- }
390
- /** Get all keys matching a prefix. */
391
- keys(prefix = "") {
392
- const rows = this.db.prepare(`
393
- SELECT key FROM svara_kv
394
- WHERE key LIKE ? AND (expires_at IS NULL OR expires_at > unixepoch())
395
- `).all(`${prefix}%`);
396
- return rows.map((r) => r.key);
397
- }
398
- };
399
- var SvaraDB = class {
400
- db;
401
- kv;
402
- constructor(dbPath = ":memory:") {
403
- if (dbPath !== ":memory:") {
404
- fs2.mkdirSync(path2.dirname(path2.resolve(dbPath)), { recursive: true });
405
- }
406
- this.db = this.openDatabase(dbPath);
407
- this.configure();
408
- this.migrate();
409
- this.kv = new KVStore(this.db);
410
- }
411
- // ─── Query Helpers ────────────────────────────────────────────────────────
412
- /**
413
- * Run a SELECT and return all matching rows.
414
- */
415
- query(sql, params = []) {
416
- return this.db.prepare(sql).all(...params);
417
- }
418
- /**
419
- * Run a SELECT and return the first matching row.
420
- */
421
- queryOne(sql, params = []) {
422
- return this.db.prepare(sql).get(...params);
423
- }
424
- /**
425
- * Run an INSERT/UPDATE/DELETE. Returns affected row count.
426
- */
427
- run(sql, params = []) {
428
- return this.db.prepare(sql).run(...params).changes;
429
- }
430
- /**
431
- * Execute raw SQL (for DDL, migrations, etc.).
432
- */
433
- exec(sql) {
434
- this.db.exec(sql);
435
- }
436
- /**
437
- * Run multiple operations in a single transaction.
438
- *
439
- * @example
440
- * db.transaction(() => {
441
- * db.run('INSERT INTO orders ...', [...]);
442
- * db.run('UPDATE inventory ...', [...]);
443
- * });
444
- */
445
- transaction(fn) {
446
- return this.db.transaction(fn)();
447
- }
448
- /**
449
- * Close the database connection.
450
- */
451
- close() {
452
- this.db.close();
453
- }
454
- // ─── Internal Message Storage ─────────────────────────────────────────────
455
- saveMessage(params) {
456
- this.db.prepare(`
457
- INSERT OR REPLACE INTO svara_messages (id, session_id, role, content, tool_call_id)
458
- VALUES (?, ?, ?, ?, ?)
459
- `).run(
460
- params.id,
461
- params.sessionId,
462
- params.role,
463
- params.content,
464
- params.toolCallId ?? null
465
- );
466
- }
467
- getMessages(sessionId, limit = 50) {
468
- return this.db.prepare(`
469
- SELECT id, role, content, tool_call_id, created_at
470
- FROM svara_messages
471
- WHERE session_id = ?
472
- ORDER BY created_at ASC
473
- LIMIT ?
474
- `).all(sessionId, limit);
475
- }
476
- clearSession(sessionId) {
477
- this.db.prepare("DELETE FROM svara_messages WHERE session_id = ?").run(sessionId);
478
- }
479
- // ─── Private Setup ────────────────────────────────────────────────────────
480
- openDatabase(dbPath) {
481
- try {
482
- const Database = __require("better-sqlite3");
483
- return new Database(dbPath);
484
- } catch {
485
- throw new Error(
486
- '[SvaraJS] Database requires the "better-sqlite3" package.\nRun: npm install better-sqlite3'
487
- );
488
- }
489
- }
490
- configure() {
491
- this.db.pragma("journal_mode = WAL");
492
- this.db.pragma("synchronous = NORMAL");
493
- this.db.pragma("foreign_keys = ON");
494
- }
495
- migrate() {
496
- this.db.exec(CREATE_TABLES_SQL);
497
- const meta = this.db.prepare(
498
- "SELECT value FROM svara_meta WHERE key = 'schema_version'"
499
- ).get();
500
- if (!meta) {
501
- this.db.prepare(INSERT_META_SQL).run(
502
- String(SCHEMA_VERSION),
503
- (/* @__PURE__ */ new Date()).toISOString()
504
- );
505
- }
506
- }
507
- };
508
-
509
262
  // src/rag/retriever.ts
510
263
  import crypto3 from "crypto";
511
264
  var OpenAIEmbeddings = class {
@@ -740,7 +493,6 @@ function cosineSimilarity(a, b) {
740
493
  }
741
494
 
742
495
  export {
743
- SvaraDB,
744
496
  DocumentLoader,
745
497
  Chunker,
746
498
  VectorRetriever
@@ -0,0 +1,257 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-CIESM3BP.mjs";
4
+
5
+ // src/database/sqlite.ts
6
+ import path from "path";
7
+ import fs from "fs";
8
+
9
+ // src/database/schema.ts
10
+ var SCHEMA_VERSION = 1;
11
+ var CREATE_TABLES_SQL = `
12
+ -- Schema version tracking
13
+ CREATE TABLE IF NOT EXISTS svara_meta (
14
+ key TEXT PRIMARY KEY,
15
+ value TEXT NOT NULL
16
+ );
17
+
18
+ -- Conversation history persistence
19
+ CREATE TABLE IF NOT EXISTS svara_messages (
20
+ id TEXT PRIMARY KEY,
21
+ session_id TEXT NOT NULL,
22
+ role TEXT NOT NULL CHECK(role IN ('user', 'assistant', 'system', 'tool')),
23
+ content TEXT NOT NULL,
24
+ tool_call_id TEXT,
25
+ created_at INTEGER NOT NULL DEFAULT (unixepoch())
26
+ );
27
+
28
+ CREATE INDEX IF NOT EXISTS idx_messages_session
29
+ ON svara_messages (session_id, created_at);
30
+
31
+ -- User registry
32
+ CREATE TABLE IF NOT EXISTS svara_users (
33
+ id TEXT PRIMARY KEY,
34
+ email TEXT,
35
+ display_name TEXT,
36
+ first_seen INTEGER NOT NULL DEFAULT (unixepoch()),
37
+ last_seen INTEGER NOT NULL DEFAULT (unixepoch()),
38
+ metadata TEXT DEFAULT '{}'
39
+ );
40
+
41
+ CREATE INDEX IF NOT EXISTS idx_users_email
42
+ ON svara_users (email);
43
+
44
+ -- Session metadata
45
+ CREATE TABLE IF NOT EXISTS svara_sessions (
46
+ id TEXT PRIMARY KEY,
47
+ user_id TEXT NOT NULL,
48
+ channel TEXT NOT NULL,
49
+ created_at INTEGER NOT NULL DEFAULT (unixepoch()),
50
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
51
+ metadata TEXT DEFAULT '{}',
52
+ FOREIGN KEY (user_id) REFERENCES svara_users(id)
53
+ );
54
+
55
+ CREATE INDEX IF NOT EXISTS idx_sessions_user
56
+ ON svara_sessions (user_id);
57
+
58
+ -- Vector store chunks for RAG (per agent)
59
+ CREATE TABLE IF NOT EXISTS svara_chunks (
60
+ id TEXT PRIMARY KEY,
61
+ agent_name TEXT NOT NULL, -- Separate RAG per agent
62
+ document_id TEXT NOT NULL,
63
+ content TEXT NOT NULL,
64
+ content_hash TEXT NOT NULL, -- MD5 hash of content for deduplication
65
+ chunk_index INTEGER NOT NULL,
66
+ embedding TEXT, -- stored as JSON string of float array
67
+ source TEXT NOT NULL,
68
+ metadata TEXT DEFAULT '{}',
69
+ created_at INTEGER NOT NULL DEFAULT (unixepoch())
70
+ );
71
+
72
+ CREATE INDEX IF NOT EXISTS idx_chunks_agent
73
+ ON svara_chunks (agent_name);
74
+
75
+ CREATE INDEX IF NOT EXISTS idx_chunks_agent_document
76
+ ON svara_chunks (agent_name, document_id);
77
+
78
+ CREATE INDEX IF NOT EXISTS idx_chunks_content_hash
79
+ ON svara_chunks (content_hash);
80
+
81
+ -- Document registry
82
+ CREATE TABLE IF NOT EXISTS svara_documents (
83
+ id TEXT PRIMARY KEY,
84
+ source TEXT NOT NULL UNIQUE,
85
+ type TEXT NOT NULL,
86
+ size INTEGER,
87
+ hash TEXT,
88
+ indexed_at INTEGER NOT NULL DEFAULT (unixepoch()),
89
+ metadata TEXT DEFAULT '{}'
90
+ );
91
+
92
+ -- Key-value store for arbitrary agent state
93
+ CREATE TABLE IF NOT EXISTS svara_kv (
94
+ key TEXT PRIMARY KEY,
95
+ value TEXT NOT NULL,
96
+ expires_at INTEGER, -- unix timestamp, NULL = no expiry
97
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch())
98
+ );
99
+ `;
100
+ var INSERT_META_SQL = `
101
+ INSERT OR REPLACE INTO svara_meta (key, value)
102
+ VALUES ('schema_version', ?), ('created_at', ?);
103
+ `;
104
+
105
+ // src/database/sqlite.ts
106
+ var KVStore = class {
107
+ constructor(db) {
108
+ this.db = db;
109
+ }
110
+ db;
111
+ /** Set a key-value pair, with optional TTL in seconds. */
112
+ set(key, value, ttlSeconds) {
113
+ const expiresAt = ttlSeconds ? Math.floor(Date.now() / 1e3) + ttlSeconds : null;
114
+ this.db.prepare(`
115
+ INSERT OR REPLACE INTO svara_kv (key, value, expires_at, updated_at)
116
+ VALUES (?, ?, ?, unixepoch())
117
+ `).run(key, JSON.stringify(value), expiresAt);
118
+ }
119
+ /** Get a value by key. Returns undefined if not found or expired. */
120
+ get(key) {
121
+ const row = this.db.prepare(`
122
+ SELECT value, expires_at FROM svara_kv
123
+ WHERE key = ? AND (expires_at IS NULL OR expires_at > unixepoch())
124
+ `).get(key);
125
+ if (!row) return void 0;
126
+ return JSON.parse(row.value);
127
+ }
128
+ /** Delete a key. */
129
+ delete(key) {
130
+ this.db.prepare("DELETE FROM svara_kv WHERE key = ?").run(key);
131
+ }
132
+ /** Check if a key exists and is not expired. */
133
+ has(key) {
134
+ return this.get(key) !== void 0;
135
+ }
136
+ /** Get all keys matching a prefix. */
137
+ keys(prefix = "") {
138
+ const rows = this.db.prepare(`
139
+ SELECT key FROM svara_kv
140
+ WHERE key LIKE ? AND (expires_at IS NULL OR expires_at > unixepoch())
141
+ `).all(`${prefix}%`);
142
+ return rows.map((r) => r.key);
143
+ }
144
+ };
145
+ var SvaraDB = class {
146
+ db;
147
+ kv;
148
+ constructor(dbPath = ":memory:") {
149
+ if (dbPath !== ":memory:") {
150
+ fs.mkdirSync(path.dirname(path.resolve(dbPath)), { recursive: true });
151
+ }
152
+ this.db = this.openDatabase(dbPath);
153
+ this.configure();
154
+ this.migrate();
155
+ this.kv = new KVStore(this.db);
156
+ }
157
+ // ─── Query Helpers ────────────────────────────────────────────────────────
158
+ /**
159
+ * Run a SELECT and return all matching rows.
160
+ */
161
+ query(sql, params = []) {
162
+ return this.db.prepare(sql).all(...params);
163
+ }
164
+ /**
165
+ * Run a SELECT and return the first matching row.
166
+ */
167
+ queryOne(sql, params = []) {
168
+ return this.db.prepare(sql).get(...params);
169
+ }
170
+ /**
171
+ * Run an INSERT/UPDATE/DELETE. Returns affected row count.
172
+ */
173
+ run(sql, params = []) {
174
+ return this.db.prepare(sql).run(...params).changes;
175
+ }
176
+ /**
177
+ * Execute raw SQL (for DDL, migrations, etc.).
178
+ */
179
+ exec(sql) {
180
+ this.db.exec(sql);
181
+ }
182
+ /**
183
+ * Run multiple operations in a single transaction.
184
+ *
185
+ * @example
186
+ * db.transaction(() => {
187
+ * db.run('INSERT INTO orders ...', [...]);
188
+ * db.run('UPDATE inventory ...', [...]);
189
+ * });
190
+ */
191
+ transaction(fn) {
192
+ return this.db.transaction(fn)();
193
+ }
194
+ /**
195
+ * Close the database connection.
196
+ */
197
+ close() {
198
+ this.db.close();
199
+ }
200
+ // ─── Internal Message Storage ─────────────────────────────────────────────
201
+ saveMessage(params) {
202
+ this.db.prepare(`
203
+ INSERT OR REPLACE INTO svara_messages (id, session_id, role, content, tool_call_id)
204
+ VALUES (?, ?, ?, ?, ?)
205
+ `).run(
206
+ params.id,
207
+ params.sessionId,
208
+ params.role,
209
+ params.content,
210
+ params.toolCallId ?? null
211
+ );
212
+ }
213
+ getMessages(sessionId, limit = 50) {
214
+ return this.db.prepare(`
215
+ SELECT id, role, content, tool_call_id, created_at
216
+ FROM svara_messages
217
+ WHERE session_id = ?
218
+ ORDER BY created_at ASC
219
+ LIMIT ?
220
+ `).all(sessionId, limit);
221
+ }
222
+ clearSession(sessionId) {
223
+ this.db.prepare("DELETE FROM svara_messages WHERE session_id = ?").run(sessionId);
224
+ }
225
+ // ─── Private Setup ────────────────────────────────────────────────────────
226
+ openDatabase(dbPath) {
227
+ try {
228
+ const Database = __require("better-sqlite3");
229
+ return new Database(dbPath);
230
+ } catch {
231
+ throw new Error(
232
+ '[SvaraJS] Database requires the "better-sqlite3" package.\nRun: npm install better-sqlite3'
233
+ );
234
+ }
235
+ }
236
+ configure() {
237
+ this.db.pragma("journal_mode = WAL");
238
+ this.db.pragma("synchronous = NORMAL");
239
+ this.db.pragma("foreign_keys = ON");
240
+ }
241
+ migrate() {
242
+ this.db.exec(CREATE_TABLES_SQL);
243
+ const meta = this.db.prepare(
244
+ "SELECT value FROM svara_meta WHERE key = 'schema_version'"
245
+ ).get();
246
+ if (!meta) {
247
+ this.db.prepare(INSERT_META_SQL).run(
248
+ String(SCHEMA_VERSION),
249
+ (/* @__PURE__ */ new Date()).toISOString()
250
+ );
251
+ }
252
+ }
253
+ };
254
+
255
+ export {
256
+ SvaraDB
257
+ };