@happyvertical/smrt-core
The foundational AI agent framework providing ORM, code generation, AI operations, and standardized collections for building intelligent TypeScript applications.
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
npm install @happyvertical/smrt-coreDependencies
smrt-core builds on the HappyVertical SDK:
npm install @happyvertical/ai @happyvertical/sql @happyvertical/filesQuick Start
Create your first SMRT object in under 5 minutes:
1. Define Your Object
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
// 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
// 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
// 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:
┌─────────────────────────────────────────┐
│ @smrt Decorated Classes │
│ (auto-register on instantiation) │
└────────────┬────────────────────────────┘
│
↓
┌─────────────────────────────────────────┐
│ ObjectRegistry (Global Singleton) │
│ • Class constructors │
│ • Field definitions │
│ • Decorator configurations │
│ • Collection instances (cached) │
└────────────┬────────────────────────────┘
│
┌────────┼────────┐
↓ ↓ ↓
REST API MCP Tools CLIThree 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:
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:
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:
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:
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:
const description = await product.describe();
// Returns professional description suitable for displayAI Tools & Function Calling
Objects expose methods as AI tools automatically:
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 neededAdvanced Querying
Query Operators
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)
// 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
// 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:
@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
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 - DeleteMCP Server Generator
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_productCLI Commands
# 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:
// 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
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
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
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
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:
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:
// 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
// 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
// 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
- Use TypeScript types for simple properties - let the framework infer the schema
- Use field helpers only when you need constraints or validation
- Always define static _itemClass on collection classes
- Use factory pattern for collection creation (
create()method) - Leverage eager loading to prevent N+1 query problems
- Set confidence scores in context memory for pattern reliability
- Use hierarchical scopes for context organization
- Cache AI responses in object properties to avoid redundant calls
- Use direct SQL for complex queries when the ORM is insufficient
- Organize by concerns - one object class per business entity