ORM-native capability runtime for safe, typed, auditable agent database access
Project description
OrmAI
Give your AI agents database access without the risk.
OrmAI wraps your existing ORM models in a policy-enforced runtime. Your agents get typed tools for querying and writing data — while you keep control over what they can see and do. No raw SQL. No prompt injection into your database. Just safe, auditable, tenant-scoped database tools.
Available for Python and TypeScript/Node.js.
Why OrmAI?
Building AI agents that interact with your database? You have probably thought about:
- "What if the agent reads sensitive data?" → Field-level policies hide or mask PII automatically.
- "What if it runs wild queries?" → Query budgets and row limits prevent runaway costs.
- "How do I audit what it did?" → Every operation is logged with full context.
- "What about multi-tenant isolation?" → Tenant scoping is built-in, not bolted on.
- "Which ORM do we use?" → Works with SQLAlchemy, Prisma, Drizzle, TypeORM, Tortoise, Django, SQLModel, and Peewee.
OrmAI solves these at the ORM layer — not the prompt layer.
Pick Your Stack
| Python | TypeScript / Node.js | |
|---|---|---|
| Package | pip install ormai |
npm install @ormai/core |
| ORMs | SQLAlchemy, Tortoise, Django, SQLModel, Peewee | Prisma, Drizzle, TypeORM |
| Integrations | OpenAI, LangChain, LlamaIndex, MCP, FastAPI | Vercel AI SDK, LangChain.js, OpenAI, Anthropic, LlamaIndex.ts, Mastra, MCP |
| Quickstart | ormai.quickstart |
@ormai/utils |
| Docs | Python Guide | TS Guide |
Python Quick Start
# With your ORM of choice
pip install ormai[sqlalchemy]
# or
pip install ormai[prisma]
from ormai.quickstart import mount_sqlalchemy
from ormai.utils import DEFAULT_DEV
# Your existing SQLAlchemy models + session
toolset = mount_sqlalchemy(
engine=engine,
session_factory=Session,
policy=DEFAULT_DEV
)
# Done. Your agent now has: db.query, db.get, db.aggregate, db.describe_schema
TypeScript Quick Start
# Core (required)
npm install @ormai/core
# Choose your ORM adapter
npm install @ormai/prisma
import { PrismaClient } from '@prisma/client';
import { PrismaAdapter } from '@ormai/prisma';
import { PolicyBuilder, createContext } from '@ormai/core';
import { createGenericTools } from '@ormai/tools';
const prisma = new PrismaClient();
const adapter = new PrismaAdapter({ prisma });
const schema = await adapter.introspect();
const policy = new PolicyBuilder('prod')
.registerModels(['Customer', 'Order'])
.tenantScope('tenantId')
.denyFields('*password*')
.maskFields('*email*')
.build();
const tools = createGenericTools({ adapter, policy, schema });
const ctx = createContext({
tenantId: 'tenant-123',
userId: 'user-456',
db: prisma,
roles: ['admin'],
});
// Your agent now has safe database tools
const result = await tools[0].execute({
model: 'Order',
where: [{ field: 'status', op: 'eq', value: 'pending' }],
take: 10,
}, ctx);
What You Get Out of the Box
| Feature | What It Does |
|---|---|
| Read-safe tools | db.query, db.get, db.aggregate, db.describe_schema — no raw SQL |
| Write-safe tools | db.create, db.update, db.delete, db.bulk_update — gated by policy |
| Field-level policies | Hide passwords, mask emails, deny sensitive columns automatically |
| Tenant scoping | .tenantScope('tenant_id') auto-filters every query per user |
| Query budgets | Max rows, max includes depth, statement timeouts per model |
| Audit logging | Every call logged with principal, tenant, trace ID, input, output |
| Human approval gates | Require reason or approval for writes on sensitive models |
| Schema introspection | Auto-discovers models, fields, relations, primary keys |
| Multi-framework | LangChain, OpenAI, Vercel AI SDK, LlamaIndex, Mastra, FastAPI, MCP |
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Your Agent │
└──────────────────────────┬──────────────────────────────────┘
│ calls tools
┌──────────────────────────▼──────────────────────────────────┐
│ OrmAI Runtime │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐│
│ │ Policy │ │ Audit │ │ Tenant Scope ││
│ │ Enforcer │ │ Logger │ │ Filter ││
│ └─────────────┘ └─────────────┘ └─────────────────────┘│
└──────────────────────────┬──────────────────────────────────┘
│ parameterized queries only
┌──────────────────────────▼──────────────────────────────────┐
│ Your ORM (SQLAlchemy / Prisma / Drizzle / ...) │
└─────────────────────────────────────────────────────────────┘
OrmAI sits between your agent and your ORM. It compiles agent requests into type-safe ORM queries, enforces policies, logs everything, and returns structured results. Your database never sees raw SQL from the agent.
Documentation
docs.neullabs.com/ormai — Full guides, API reference, and examples.
Installation
Python
pip install ormai[sqlalchemy]
pip install ormai[tortoise]
pip install ormai[peewee]
pip install ormai[django]
pip install ormai[sqlmodel]
# Or all adapters
pip install ormai[all]
TypeScript / Node.js
# Core (required)
npm install @ormai/core
# ORM adapters
npm install @ormai/prisma
npm install @ormai/drizzle
npm install @ormai/typeorm
# Optional packages
npm install @ormai/tools # Generic database tools
npm install @ormai/store # Audit logging
npm install @ormai/mcp # MCP server
npm install @ormai/integrations # Framework adapters
npm install @ormai/utils # PolicyBuilder and helpers
Policy Configuration
Python
from ormai.utils import PolicyBuilder, DEFAULT_PROD
policy = (
PolicyBuilder(DEFAULT_PROD)
.register_models([Customer, Order])
.deny_fields("*password*", "*secret*", "*token*")
.mask_fields(["email", "phone"])
.tenant_scope("tenant_id")
.enable_writes(models=["Order"], require_reason=True)
.build()
)
TypeScript
import { PolicyBuilder } from '@ormai/core';
const policy = new PolicyBuilder('prod')
.registerModels(['Customer', 'Order', 'Product'])
.tenantScope('tenantId')
.denyFields('*password*')
.maskFields('*email*')
.allowRelations('Order', ['customer', 'items'])
.enableWrites(['Order'], {
allowCreate: true,
allowUpdate: true,
allowDelete: false,
maxAffectedRows: 10,
})
.defaultBudgetConfig({
maxRows: 100,
maxIncludesDepth: 2,
statementTimeoutMs: 5000,
})
.build();
Presets: DEFAULT_DEV (permissive), DEFAULT_INTERNAL (moderate), DEFAULT_PROD (strict)
Agent Framework Integrations
| Framework | Python Package | TypeScript Package |
|---|---|---|
| OpenAI | ormai |
@ormai/integrations |
| LangChain | ormai |
@ormai/integrations |
| Vercel AI SDK | — | @ormai/integrations |
| LlamaIndex | ormai |
@ormai/integrations |
| Mastra | — | @ormai/integrations |
| Anthropic | — | @ormai/integrations |
| FastAPI | ormai |
— |
| MCP | ormai |
@ormai/mcp |
Supported ORMs
| ORM | Python | TypeScript |
|---|---|---|
| SQLAlchemy | ✅ | — |
| Prisma | — | ✅ |
| Drizzle | — | ✅ |
| TypeORM | — | ✅ |
| SQLModel | ✅ | — |
| Django ORM | ✅ | — |
| Tortoise ORM | ✅ | — |
| Peewee | ✅ | — |
Benchmark: OrmAI vs Text-to-SQL
We benchmarked against the Spider dataset — 1034 natural language queries:
| Metric | OrmAI | Text-to-SQL |
|---|---|---|
| SQL Injection possible | No | Yes |
| Unsafe ops executed | 0 | 23 |
| Full audit trail | Yes | No |
# Try it yourself
pip install ormai[benchmark]
python examples/spider_demo.py run --limit 20
Examples
examples/spider_demo.py— Benchmark demoexamples/fastapi-sqlalchemy/— FastAPI + SQLAlchemy integrationormai-ts/— TypeScript monorepo with Prisma, Drizzle, and TypeORM examples
More examples at docs.neullabs.com/ormai/examples.
Contributing
git clone https://github.com/neul-labs/ormai.git
cd ormai
# Python
uv sync --dev
uv run pytest
# TypeScript
cd ormai-ts
npm install
npm run build
npm run test
See contributing guide for development setup and guidelines.
Documentation · GitHub · PyPI · npm
MIT License · Built by Neul Labs
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ormai-0.2.1.tar.gz.
File metadata
- Download URL: ormai-0.2.1.tar.gz
- Upload date:
- Size: 640.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.11 {"installer":{"name":"uv","version":"0.11.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
52465389c0a232a02a810cfac580ca0ee68acc0fdbc334f58e219775dbf91255
|
|
| MD5 |
66c4c01312d3d33104b741e15143f1f8
|
|
| BLAKE2b-256 |
b3680c7d5254db7854877d2f69d40e5f9b41a92b283bddbfe4333bd6fdcf65c7
|
File details
Details for the file ormai-0.2.1-py3-none-any.whl.
File metadata
- Download URL: ormai-0.2.1-py3-none-any.whl
- Upload date:
- Size: 189.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.11 {"installer":{"name":"uv","version":"0.11.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d5290be57ed9c42e1ad9d806a46dd11b85e59e23fc5567d1853623cd00f0809c
|
|
| MD5 |
7429d4d6b14d2acd8e17e473d4a5e5ae
|
|
| BLAKE2b-256 |
083efe4f12fda37a51bfff1129ff1ba4cfefe274829e1e9ee1871dfd17a9fb8d
|