s-m-r-t

@happyvertical/smrt-core

The foundational AI agent framework providing ORM, code generation, AI operations, and standardized collections for building intelligent TypeScript applications.

v0.17.100Core FoundationESM

Overview

@happyvertical/smrt-core is the heart of the SMRT framework. It provides:

  • AI-First Object Framework - TypeScript classes with built-in AI operations (is, do, describe)
  • Object-Relational Mapping - Automatic database schema generation from TypeScript definitions
  • Standardized Collections - Advanced CRUD with SQL-like querying
  • Code Generators - Auto-generate REST APIs, MCP servers, CLI commands, and Swagger docs
  • Vite Plugin - Virtual modules for seamless development integration
  • Context Memory - Persistent storage for learned patterns
  • Semantic Search - Built-in embedding support for similarity

Installation

bash
npm install @happyvertical/smrt-core

Dependencies

smrt-core builds on the HappyVertical SDK:

bash
npm install @happyvertical/ai @happyvertical/sql @happyvertical/files

Quick Start

Create your first SMRT object in under 5 minutes:

1. Define Your Object

typescript
import { SmrtObject, SmrtCollection, smrt } from '@happyvertical/smrt-core';

@smrt({ cli: true, api: true })
export class Product extends SmrtObject {
  name: string = '';
  description: string = '';
  price: number = 0.0;    // Decimal point → DECIMAL type
  quantity: number = 0;   // No decimal → INTEGER type
  active: boolean = true;
}

export class ProductCollection extends SmrtCollection<Product> {
  static readonly _itemClass = Product;
}

2. Initialize Collection

typescript
// Create and initialize collection
const products = await ProductCollection.create({
  db: 'products.db',  // SQLite database
  ai: {
    provider: 'openai',
    apiKey: process.env.OPENAI_API_KEY
  }
});

await products.initialize();

3. CRUD Operations

typescript
// Create
const product = await products.create({
  name: 'Widget',
  description: 'A useful widget',
  price: 29.99,
  quantity: 100,
  active: true
});
await product.save();

// Read
const all = await products.list({ limit: 10 });
const one = await products.get(product.id);

// Update
product.price = 24.99;
await product.save();

// Delete
await product.delete();

4. AI Operations

typescript
// Ask yes/no questions
const isExpensive = await product.is(`
  - Price is above $50
  - Premium quality product
`);

// Perform AI actions
const summary = await product.do(`
  Create a compelling 2-sentence product description
  that highlights the key benefits.
`);

// Generate descriptions
const description = await product.describe();
console.log(description);
// "Widget is a useful product priced at $29.99..."

Core Concepts

Architecture

smrt-core uses a registry-driven design:

text
┌─────────────────────────────────────────┐
│  @smrt Decorated Classes                │
│  (auto-register on instantiation)       │
└────────────┬────────────────────────────┘
             │
             ↓
┌─────────────────────────────────────────┐
│  ObjectRegistry (Global Singleton)      │
│  • Class constructors                   │
│  • Field definitions                    │
│  • Decorator configurations             │
│  • Collection instances (cached)        │
└────────────┬────────────────────────────┘
             │
    ┌────────┼────────┐
    ↓        ↓        ↓
 REST API  MCP Tools  CLI

Three Core Classes

  • SmrtClass - Foundation providing database, filesystem, and AI client access
  • SmrtObject - Persistent entities with unique IDs, timestamps, and AI operations
  • SmrtCollection<T> - Manages sets of SmrtObject instances with CRUD operations

Field System

Use TypeScript types for automatic schema inference:

typescript
class Product extends SmrtObject {
  name: string = '';              // → TEXT
  quantity: number = 0;           // → INTEGER (no decimal)
  price: number = 0.0;            // → DECIMAL (has decimal)
  active: boolean = true;         // → BOOLEAN
  tags: string[] = [];            // → JSON
  createdAt: Date = new Date();   // → DATETIME
}

Or use field helpers when constraints are needed:

typescript
import { text, decimal, foreignKey } from '@happyvertical/smrt-core/decorators';

class Product extends SmrtObject {
  name = text({ required: true, maxLength: 100 });
  price = decimal({ min: 0, max: 999999.99, required: true });
  categoryId = foreignKey(Category, { onDelete: 'restrict' });
}

AI Integration

The is() Method

Ask yes/no questions about your objects:

typescript
const isHighQuality = await document.is(`
  - Contains more than 500 words
  - Has clear structure and headings
  - Uses professional language
`);

if (isHighQuality) {
  await document.publish();
}

The do() Method

Perform AI-powered actions:

typescript
const summary = await document.do(`
  Create a 2-sentence summary of this document.
  Focus on the key points and main conclusions.
`);

const translation = await document.do(`
  Translate the title to Spanish.
`);

The describe() Method

Generate human-readable descriptions:

typescript
const description = await product.describe();
// Returns professional description suitable for display

AI Tools & Function Calling

Objects expose methods as AI tools automatically:

typescript
class Document extends SmrtObject {
  async summarize() { /* ... */ }
  async analyze() { /* ... */ }
  async translate(language: string) { /* ... */ }
}

// AI can call these methods during do() operations
const result = await document.do(`
  Analyze this document and translate the summary to Spanish.
`);
// AI will call analyze() and translate() as needed

Advanced Querying

Query Operators

typescript
const products = await collection.list({
  where: {
    'price >': 10,          // Greater than
    'price <=': 100,        // Less than or equal
    'name like': '%widget%', // Pattern matching
    'category in': ['A', 'B', 'C'],
    'inStock': true,         // Equals (default)
    'deletedAt !=': null    // Not equal
  },
  orderBy: ['price DESC', 'name ASC'],
  limit: 20,
  offset: 0
});

Eager Loading (Prevent N+1 Queries)

typescript
// Load relationships efficiently with SQL JOINs
const orders = await orderCollection.list({
  limit: 100,
  include: ['customerId', 'productId']  // Pre-load relationships
});

// Access without additional queries
for (const order of orders) {
  const customer = order.getRelated('customerId');  // Already loaded!
  const product = order.getRelated('productId');
}

Direct SQL Access

typescript
// Template literal safety (SQL injection prevention)
const expensive = await collection.db.many`
  SELECT * FROM products
  WHERE price > ${100}
  ORDER BY price DESC
`;

const count = await collection.db.pluck`
  SELECT COUNT(*) FROM products WHERE category = ${'electronics'}
`;

Code Generation

The @smrt Decorator

Control what gets generated for each object:

typescript
@smrt({
  api: { include: ['list', 'get', 'create', 'update', 'delete'] },
  mcp: { include: ['list', 'get'] },  // Read-only for AI
  cli: true,
  swagger: true
})
export class Product extends SmrtObject { }

REST API Generator

typescript
import { APIGenerator } from '@happyvertical/smrt-core/generators';

const generator = new APIGenerator({
  basePath: '/api/v1',
  enableCors: true,
  port: 3000
});

generator.registerCollection('products', productCollection);
const { server, url } = generator.createServer();

// Generated endpoints:
// GET    /api/v1/products       - List
// POST   /api/v1/products       - Create
// GET    /api/v1/products/:id   - Get
// PUT    /api/v1/products/:id   - Update
// DELETE /api/v1/products/:id   - Delete

MCP Server Generator

typescript
import { MCPGenerator } from '@happyvertical/smrt-core/generators';

const generator = new MCPGenerator({
  name: 'smrt-mcp-server',
  version: '1.0.0'
});

generator.registerCollection('products', productCollection);
const tools = generator.generateTools();

// Generated MCP tools:
// list_products, get_product_by_id, create_product,
// update_product, delete_product

CLI Commands

bash
# Auto-generated from @smrt({ cli: true })
npx smrt products list
npx smrt products get <id>
npx smrt products create --name "Widget" --price 29.99
npx smrt products update <id> --price 24.99
npx smrt products delete <id>

Context Memory System

Store and retrieve learned patterns:

typescript
// Store learned patterns
await object.remember({
  scope: 'parser/html/domain.com',
  key: 'article-selector',
  value: '.main-article',
  confidence: 0.95,
  metadata: { discoveredAt: new Date() }
});

// Retrieve with ancestor fallback
const context = await object.recall({
  scope: 'parser/html/domain.com/news',
  key: 'article-selector',
  includeAncestors: true  // Falls back to parent scopes
});

// Batch retrieval
const allContexts = await object.recallAll({
  scope: 'config/processing',
  includeDescendants: true
});

// Cleanup
await object.forget({ scope, key });
await object.forgetScope({ scope, includeDescendants: true });

Use Case: Web Scraper Learning

typescript
class WebScraper extends SmrtObject {
  async discoverSelector(url: string) {
    const hostname = new URL(url).hostname;

    // Try to recall learned selector
    const remembered = await this.recall({
      scope: `parser/${hostname}`,
      key: 'main-content',
      includeAncestors: true
    });

    if (remembered) return remembered.value;

    // Discover and remember
    const selector = await this.do(`Find CSS selector for main content`);
    await this.remember({
      scope: `parser/${hostname}`,
      key: 'main-content',
      value: selector,
      confidence: 0.9
    });

    return selector;
  }
}

Relationships

Foreign Keys

typescript
import { foreignKey } from '@happyvertical/smrt-core/decorators';

class Order extends SmrtObject {
  customerId = foreignKey(Customer, { onDelete: 'cascade' });
  productId = foreignKey(Product, { onDelete: 'restrict' });
  total: number = 0.0;
}

// Load relationship
await order.loadRelated('customerId');
const customer = order.getRelated('customerId');

One-to-Many

typescript
import { oneToMany } from '@happyvertical/smrt-core/decorators';

class Customer extends SmrtObject {
  orders = oneToMany(Order, { foreignKey: 'customerId' });
}

// Access related records
const orders = await customer.loadRelated('orders');

Many-to-Many

typescript
import { manyToMany } from '@happyvertical/smrt-core/decorators';

class Product extends SmrtObject {
  relatedProducts = manyToMany(Product, {
    through: 'product_relations'
  });
}

// Access related products
const related = await product.loadRelated('relatedProducts');

Single Table Inheritance

Polymorphic object hierarchies in a single database table:

typescript
import { Meta } from '@happyvertical/smrt-core';

@smrt({ tableStrategy: 'sti' })
class Event extends SmrtObject {
  title: string = '';          // Base table column
  startTime: Date = new Date(); // Base table column
}

@smrt()
class Meeting extends Event {
  location: string = '';       // Base table column
  roomNumber: Meta<string> = ''; // Stored in _meta_data JSONB
  attendees: Meta<string[]> = [];
}

@smrt()
class Concert extends Event {
  venue: string = '';          // Base table column
  artist: Meta<string> = '';   // Stored in _meta_data JSONB
  ticketPrice: Meta<number> = 0;
}

// Polymorphic queries
const events = await eventCollection.list({
  where: { '_meta_type': ['Meeting', 'Concert'] }
});

// Returns correct subclass instances
events.forEach(event => {
  if (event instanceof Meeting) {
    console.log(`Meeting at ${event.location}`);
  } else if (event instanceof Concert) {
    console.log(`Concert by ${event.artist}`);
  }
});

Vite Plugin

Auto-generate virtual modules during development:

typescript
// vite.config.ts
import { smrtPlugin } from '@happyvertical/smrt-core/vite-plugin';

export default {
  plugins: [
    smrtPlugin({
      include: ['src/**/*.ts'],
      exclude: ['**/*.test.ts'],
      generateTypes: true,
      hmr: true,
      svelteKit: {
        enabled: true,
        routesDir: 'src/routes/api',
        objectsDir: 'src/lib/objects'
      }
    })
  ]
};

Virtual Modules

typescript
// Auto-generated type-safe imports
import { setupRoutes } from '@smrt/routes';
import { createClient } from '@smrt/client';
import { tools } from '@smrt/mcp';
import { manifest } from '@smrt/manifest';
import type { Product } from '@smrt/types';

Database Support

Supported Databases

  • SQLite - { type: 'sqlite', url: 'app.db' }
  • PostgreSQL - { type: 'postgres', url: 'postgres://...' }
  • DuckDB - { type: 'duckdb', url: 'data.db' }
  • JSON - { type: 'json', url: 'data.json' } (testing only)

Configuration

typescript
// String shortcut (auto-detects type)
const collection = await ProductCollection.create({
  db: 'products.db'
});

// Config object
const collection = await ProductCollection.create({
  db: {
    type: 'sqlite',
    url: 'products.db'
  }
});

// DatabaseInterface instance
import { getDatabase } from '@happyvertical/sql';
const db = await getDatabase({ type: 'postgres', url: '...' });
const collection = await ProductCollection.create({ db });

Best Practices

  1. Use TypeScript types for simple properties - let the framework infer the schema
  2. Use field helpers only when you need constraints or validation
  3. Always define static _itemClass on collection classes
  4. Use factory pattern for collection creation (create() method)
  5. Leverage eager loading to prevent N+1 query problems
  6. Set confidence scores in context memory for pattern reliability
  7. Use hierarchical scopes for context organization
  8. Cache AI responses in object properties to avoid redundant calls
  9. Use direct SQL for complex queries when the ORM is insufficient
  10. Organize by concerns - one object class per business entity

Next Steps