@yesvara/svara 0.1.1 → 0.1.3

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/CONTRIBUTING.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Contributing to SvaraJS
2
2
 
3
- Thanks for your interest in contributing! We welcome all contributionsbug reports, feature requests, documentation improvements, and pull requests.
3
+ Thanks for your interest in contributing! We welcome all contributions such as bug reports, feature requests, documentation improvements, and pull requests.
4
4
 
5
5
  ## Code of Conduct
6
6
 
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
 
@@ -28,7 +29,7 @@ const app = new SvaraApp();
28
29
  const agent = new SvaraAgent({
29
30
  name: 'Support Bot',
30
31
  model: 'gpt-4o-mini', // provider auto-detected
31
- knowledge: './docs', // PDF, MD, TXT just point to a folder
32
+ knowledge: './docs', // PDF, MD, TXT, just point to a folder
32
33
  });
33
34
 
34
35
  app.route('/chat', agent.handler());
@@ -43,7 +44,7 @@ That's it. No pipeline setup. No embedding boilerplate. No webhook configuration
43
44
 
44
45
  ## Features
45
46
 
46
- | | |
47
+ |||
47
48
  |---|---|
48
49
  | **Zero-config LLM** | Pass a model name, provider is auto-detected |
49
50
  | **Instant RAG** | Point to a folder, documents are indexed automatically |
@@ -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.
@@ -321,7 +359,7 @@ agent
321
359
  verifyToken: process.env.WA_VERIFY_TOKEN,
322
360
  });
323
361
 
324
- await agent.start();
362
+ await agent.start(); // Start all channels
325
363
  ```
326
364
 
327
365
  ### Events
@@ -378,12 +416,15 @@ console.log(reply); // "It's currently 14:32 UTC..."
378
416
  const agent = new SvaraAgent({
379
417
  name: 'Support Bot',
380
418
  model: 'gpt-4o-mini',
381
- knowledge: './docs',
419
+ knowledge: './docs', // Auto-loaded on first request
382
420
  systemPrompt: 'You are a customer support agent. Answer using the documentation.',
383
421
  memory: { window: 20 },
384
422
  });
385
423
 
386
- await agent.start(); // indexes documents
424
+ const app = new SvaraApp({ cors: true });
425
+ app.route('/chat', agent.handler());
426
+ app.listen(3000);
427
+ // Knowledge indexes automatically on first request
387
428
  ```
388
429
 
389
430
  ### Multi-channel (Web + Telegram + WhatsApp)
@@ -424,21 +465,105 @@ app.listen(3000);
424
465
 
425
466
  ---
426
467
 
468
+ ## Adding Knowledge & Tools at Runtime
469
+
470
+ ### Adding knowledge (dynamic indexing)
471
+
472
+ After the agent is created, add more documents **without restarting**:
473
+
474
+ ```ts
475
+ const agent = new SvaraAgent({
476
+ name: 'Support Bot',
477
+ model: 'gpt-4o-mini',
478
+ knowledge: './docs', // Initial knowledge
479
+ });
480
+
481
+ // ── Later (no restart needed) ──────────────────────
482
+ // Add a single document
483
+ await agent.addKnowledge('./policies/new-policy-2024.pdf');
484
+
485
+ // Add multiple documents
486
+ await agent.addKnowledge(['./faq.md', './terms.txt', './pricing.pdf']);
487
+
488
+ // Agent immediately has the new knowledge
489
+ ```
490
+
491
+ **Real-world example** — admin endpoint to upload documents:
492
+
493
+ ```ts
494
+ const app = new SvaraApp({ cors: true });
495
+ app.route('/chat', agent.handler());
496
+
497
+ // Admin: dynamically add knowledge
498
+ app.post('/admin/add-knowledge', async (req, res) => {
499
+ const { path } = req.body;
500
+ try {
501
+ await agent.addKnowledge(path);
502
+ res.json({ status: 'Knowledge added', path });
503
+ } catch (err) {
504
+ res.status(400).json({ error: (err as Error).message });
505
+ }
506
+ });
507
+
508
+ app.listen(3000);
509
+ ```
510
+
511
+ ### Adding tools (dynamic function calling)
512
+
513
+ Add tools at runtime with `addTool()`:
514
+
515
+ ```ts
516
+ import { SvaraAgent, createTool } from '@yesvara/svara';
517
+
518
+ const agent = new SvaraAgent({
519
+ name: 'Assistant',
520
+ model: 'gpt-4o-mini',
521
+ tools: [initialTool], // Start with one tool
522
+ });
523
+
524
+ // ── Later ────────────────────────────────────────
525
+ // Add more tools dynamically
526
+ const newTool = createTool({
527
+ name: 'send_email',
528
+ description: 'Send an email to a user',
529
+ parameters: {
530
+ to: { type: 'string', description: 'Email address', required: true },
531
+ subject: { type: 'string', description: 'Email subject', required: true },
532
+ body: { type: 'string', description: 'Email body', required: true },
533
+ },
534
+ async run({ to, subject, body }) {
535
+ // Call your email service
536
+ return { status: 'sent', to, subject };
537
+ },
538
+ });
539
+
540
+ agent.addTool(newTool);
541
+
542
+ // Agent can now use send_email tool
543
+ ```
544
+
545
+ **Chainable tool registration** (multiple tools):
546
+
547
+ ```ts
548
+ agent
549
+ .addTool(emailTool)
550
+ .addTool(databaseTool)
551
+ .addTool(slackTool)
552
+ .addTool(webhookTool);
553
+ ```
554
+
555
+ ---
556
+
427
557
  ## CLI
428
558
 
559
+ ### Create a new project
560
+
429
561
  ```bash
430
- # Scaffold a new project
431
562
  svara new my-app
432
-
433
- # Start dev server with hot-reload
434
- svara dev
435
-
436
- # Build for production
437
- svara build
438
563
  ```
439
564
 
565
+ Output:
440
566
  ```
441
- $ svara new my-app
442
567
  ✨ Creating SvaraJS project: my-app
443
568
 
444
569
  ✓ package.json
@@ -456,6 +581,89 @@ $ svara new my-app
456
581
  npm run dev
457
582
  ```
458
583
 
584
+ Options:
585
+ - `--template <name>` — Use a specific template (default: `basic`)
586
+ - `basic` — Simple agent with HTTP endpoint
587
+ - `rag` — RAG-powered agent with document loader
588
+ - `multi-channel` — Web + Telegram + WhatsApp setup
589
+ - `tools` — Agent with tool calling examples
590
+
591
+ ### Start development server
592
+
593
+ ```bash
594
+ svara dev
595
+ ```
596
+
597
+ Features:
598
+ - Hot-reload on file changes
599
+ - Auto-restart agent on code updates
600
+ - Debug logging enabled
601
+ - Serves on `http://localhost:3000` (configurable)
602
+
603
+ Options:
604
+ - `--port <number>` — Custom port (default: `3000`)
605
+ - `--watch <glob>` — Watch additional paths (default: `src/**`)
606
+ - `--env <file>` — Load custom .env file
607
+
608
+ ### Build for production
609
+
610
+ ```bash
611
+ svara build
612
+ ```
613
+
614
+ Outputs:
615
+ - `dist/` — Compiled JavaScript
616
+ - `dist/index.js` — Entry point (ready for Node or Docker)
617
+ - Source maps and type declarations included
618
+
619
+ Options:
620
+ - `--minify` — Minify output
621
+ - `--sourcemaps` — Include source maps (default: true)
622
+ - `--outdir <path>` — Custom output directory (default: `dist/`)
623
+
624
+ ### Running built projects
625
+
626
+ ```bash
627
+ # After building
628
+ node dist/index.js
629
+
630
+ # Or with env file
631
+ NODE_ENV=production OPENAI_API_KEY=sk-... node dist/index.js
632
+ ```
633
+
634
+ ### Database management
635
+
636
+ ```bash
637
+ # Initialize database (auto-creates tables)
638
+ svara db init
639
+
640
+ # Show database schema
641
+ svara db schema
642
+
643
+ # Export users and conversation history
644
+ svara db export --format json
645
+ svara db export --format csv --table svara_messages
646
+
647
+ # Query database directly
648
+ svara db query "SELECT COUNT(*) FROM svara_users"
649
+
650
+ # Reset database (⚠️ deletes all data)
651
+ svara db reset
652
+
653
+ # Backup database
654
+ svara db backup --output backup-2026-01-15.db
655
+
656
+ # Restore from backup
657
+ svara db restore --input backup-2026-01-15.db
658
+ ```
659
+
660
+ Options:
661
+ - `--db <path>` — Custom database path (default: `./data/svara.db`)
662
+ - `--format <type>` — Export format: `json`, `csv`, `sql` (default: `json`)
663
+ - `--table <name>` — Specific table to export
664
+ - `--output <path>` — Output file path
665
+ - `--force` — Skip confirmation prompts (use with `reset` carefully!)
666
+
459
667
  ---
460
668
 
461
669
  ## Built-in Database
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